下面的情况,究竟应用DataSet的Open还是Execute,或者其它方案?(100分)

  • 主题发起人 主题发起人 flexitime
  • 开始时间 开始时间
F

flexitime

Unregistered / Unconfirmed
GUEST, unregistred user!
想写一个SQL查询及执行器,现在碰到的问题是例如我用ADODataSet(或者用CoreLab的DAC系列控件)<br>当用户输入一条SQL后,这句SQL有可能是有返回结果集的(例如用select语句),也有可能没有返回结果集的<br>(例如用Delete或update),那么应如何使用办呢,究竟用DataSet的Open还是Execute方法呢?<br>如果用Open的话,对于没有结果集返回时就会报错,如果用Exectue的话,那么又不会返回结果集。<br>那么应如何解决这个问题?
 
select open<br>update delete insert execute
 
不要用以下的方法,因为我已试过,无效:<br><br>1.用判断的方法去识别语句<br>原因:SQL语句千变万化,很难说那个一定有返回集,举个例子说,如果用户执行的储存过程的话,那根本无从判断。<br><br>2.使用类似以下方式<br>&nbsp; try <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; adoMyQuery.Open; <br>&nbsp; &nbsp; &nbsp;except <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;adoMyQuery.ExecSQL; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except on E:Exception do <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;begin <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; adoMyQuery.Close; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; showmessage('命令执行失败!原因:' <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;+ #13 + E.Message); <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EXIT; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end; <br>&nbsp; &nbsp; &nbsp;end; <br>上面的方式,一般情况下可以对付,但如果碰到的是Oracle的DDL(例如Create Table等)语句的话,就会出错。其实就是因为Open语句执行完了(即执行了Create Table。。。),但还是抛出Exception,而Execute又继续执行一次。
 
晕倒,第一次见到还有这么干的,在下佩服,直接无语,啥也不说了
 
真是直接啊~~~~~~~~~<br><br>请学习一下 sql 的 分析程序,<br>直接分析输入的情况,是什么就用什么 方式<br>比如select 开始 就用open ,当然有可能用execute (select * into xxx from xxx)<br><br>总之做得好点就要用语法分析
 
to DIGUA:<br>谢谢你能抽空回复,但我不是Delphi的新手,所以不要以为我连open与execute什么时候用也搞不清。如果你怀疑我的Delphi水平的话,请到我的Blog看看http://blog.csdn.net/flexitime<br><br>to bsense:<br>谢谢你的回复,之前我也想过自己分析SQL语句,但实际上是无效的,因为有些语句是无法判断是否有返回结果集的,例如SQL Server的存储过程,它是有可能返回结果集,也有可能不返回,完全取决于存储过程的编写者。
 
ADO原始接口中,只是一个Execute原型,ADO组件才将它分开:execute, Open,<br>你只要在原始接口判断一下Connection.Execute返回是否有recordset,且recordset.state=adopen应该可行。组件封装也是差不多这样干的。
 
不记得原始接口有没Open函数了,不过你的问题,直接可以用Execute处理就可以了。你查查吧
 
to QQ在线,<br>谢谢你的意见,但我之前已经看过ADO的原生对象了,实际上它也有<br>open及 exectue<br>例如<br>rst.Open(sql, cnn, adOpenStatic, adLockOptimistic, adCmdText);<br>另外,Execute也有<br>&nbsp; Result := not (VarIsEmpty(cnn) or VarIsEmpty(cmd)) and (cnn.State = adStateOpen);<br>&nbsp; if Result then<br>&nbsp; begin<br>&nbsp; &nbsp; cnn.BeginTrans;<br>&nbsp; &nbsp; try<br>&nbsp; &nbsp; &nbsp; cmd.ActiveConnection := cnn;<br>&nbsp; &nbsp; &nbsp; cmd.CommandText := sql;<br>&nbsp; &nbsp; &nbsp; cmd.Prepared := True;<br>&nbsp; &nbsp; &nbsp; cmd.Execute;<br>&nbsp; &nbsp; &nbsp; cnn.CommitTrans;<br>&nbsp; &nbsp; except<br>&nbsp; &nbsp; &nbsp; cnn.RollbackTrans;<br>&nbsp; &nbsp; &nbsp; Result := False;<br>&nbsp; &nbsp; end;<br>&nbsp; end;
 
试试我的这个方法,肯定行。<br>首先用ADOConnection.execute执行命令,我试过了执行 select、Delete、存储过程都没问题。<br>var aaa:_Recordset;<br><br>aaa:= ADOConnection1.Execute('exec Add_Assigner ''000'', ''002''');<br>aaa:= ADOConnection1.Execute('select * From Table1');<br>aaa:= ADOConnection1.Execute('Delete From Table1 Where Code=''00''');<br>(上面语句一次选一个就行了,我是上述三种类似都试过了,都不会出错)<br>&nbsp; if Assigned(aaa) then <br>&nbsp; &nbsp; if aaa.Fields.Count &gt; 0 &nbsp;then<br>&nbsp; &nbsp; &nbsp; ADODataSet1.Recordset := aaa;<br><br>原理:其实无论ADOQUERY还是ADOTABLE,最终都是执行adoconn.execute来执行语句或都存储过程的 <br>execute有两种方式,<br>一种是不返回结果,是PROCEDURE<br>procedure TADOConnection.Execute(const CommandText: WideString;<br>&nbsp; var RecordsAffected: Integer; const ExecuteOptions: TExecuteOptions = [eoExecuteNoRecords]);<br>var<br>&nbsp; VarRecsAffected: OleVariant;<br>begin<br>&nbsp; CheckActive;<br>&nbsp; ConnectionObject.Execute(CommandText, VarRecsAffected,<br>&nbsp; &nbsp; adCmdText+ExecuteOptionsToOrd(ExecuteOptions));<br>&nbsp; RecordsAffected := VarRecsAffected;<br>end;<br><br>一种是返回结果,是Function<br>function TADOConnection.Execute(const CommandText: WideString;<br>&nbsp; const CommandType: TCommandType = cmdText;<br>&nbsp; const ExecuteOptions: TExecuteOptions = []): _Recordset;<br>var<br>&nbsp; VarRecsAffected: OleVariant;<br>begin<br>&nbsp; CheckActive;<br>&nbsp; Result := ConnectionObject.Execute(CommandText, VarRecsAffected,<br>&nbsp; &nbsp; Integer(CommandTypeValues[CommandType])+ExecuteOptionsToOrd(ExecuteOptions));<br>end;<br><br>如果数据集控件是用execsql的,就调用procedure execute, 相反就调用 function execute, 交叉时就会出错楼上讲的提示。 <br><br>现在我们把这过程提前到先用function ADOCONN.EXECUTE()执行语句,然后自行判断有无结果返回,如有,才直接将结果赋值给ADODataSet显示,避免DELPHI默认的用ADOQuery.Open就调用funtion execute, 用EXECSQL就调用Procedure execute , 这样就可以解决楼主的问题了。
 
flexitime,我都告诉过你了,怎么不行么??<br><br>try<br>&nbsp; adoMyQuery.Close;<br>&nbsp; adoMyQuery.SQL.Text:=sql;<br>&nbsp; adoMyQuery.Open;<br>except<br>&nbsp; adoMyQuery.Close;<br>&nbsp; adoMyQuery.SQL.Text:=sql;<br>&nbsp; adoMyQuery.ExecSQL;<br>end;
 
谢谢levi,你的答案是我到目前为止最想看到的。谢谢了<br><br>to 可视化:<br>谢谢你的关注,我是第二次看到你的答案了,但如果你的方法行的话,我根本不用到处去问。我早就是试过类似于你提供的方法了,而且碰到的问题我一早就在前面写清楚了,是你没有看清楚而已。
 
如果用ADO的话, N年前已经解决这个问题了, 实际上ADO只有一种Execute方法. Recordset的Open是在Execute的基础上发挥的.
 
搞了半天,原来自己是个大傻瓜~~~,幸好也学到了其它的知识(再次感谢levi)<br><br>我的程序其实是用CoreLab的OraDAC,MyDac,MSDAC等组件写的,而不是用ADO来写的,但因为ADO以前用得比较多,所以也十分清楚ADODataSet.Execute是不返回结果集的,所以一直也认为其它的组件也不返回结果集的。(BDE, DBX等也确定是不返回)。<br>但今天回到家里试一下levi的方法,对ADO确实是有效,但对CoreLab的东西是无效的,原因是_RecordSet是AdoInt中的东西。DAC是没有的。无奈,自己再一次去查DAC的源码,结果在<br><br>DBAccess.pas单元中发现<br><br>procedure TCustomDADataSet.Execute;<br>var<br>&nbsp; ReExecute: boolean;<br>begin<br>&nbsp; if UsedConnection &lt;&gt; nil then<br>&nbsp; &nbsp; UsedConnection.PushOperation(clExecute);<br>&nbsp; try<br>&nbsp; &nbsp; if Executing then<br>&nbsp; &nbsp; &nbsp; Exit;<br>&nbsp; &nbsp; BeginConnection;<br>&nbsp; &nbsp; if Active then<br>&nbsp; &nbsp; &nbsp; &nbsp;Close;<br>&nbsp; &nbsp; repeat<br>&nbsp; &nbsp; &nbsp; ReExecute := False;<br><br>&nbsp; &nbsp; &nbsp; BeforeExecute;<br>&nbsp; &nbsp; &nbsp; if not FNonBlocking then<br>&nbsp; &nbsp; &nbsp; &nbsp; StartWait;<br>&nbsp; &nbsp; &nbsp; try<br>&nbsp; &nbsp; &nbsp; &nbsp; if Options.AutoPrepare then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Prepare;<br>&nbsp; &nbsp; &nbsp; &nbsp; if IsQuery then begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Open; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//看到了,就是这里,在Exceute里居然会先判断是否查询,是的话再Open!CoreLab真是太可爱了~~<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EndConnection; //Here we decrement UsedConection.FConnectCount that was incremented in InternalExecute and then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//in OpenCursor, also we make disconection in case of all data fetched during Opening (Less or equal to one fetch block)<br>&nbsp; &nbsp; &nbsp; &nbsp; end<br>&nbsp; &nbsp; &nbsp; &nbsp; else begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // get param values from master dataset<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (FDataLink.DataSource &lt;&gt; nil)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; and (FDataLink.DataSource.DataSet &lt;&gt; nil)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; and (FDataLink.DataSource.DataSet.Active)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SetMasterParams(Params);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if FNonBlocking then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SetCursor(crSQLArrow);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FCommand.WriteParams;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; InternalExecute;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if IsQuery then begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Open;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EndConnection; //Here we decrement UsedConection.FConnectCount that was incremented in InternalExecute and then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//in OpenCursor, also we make disconection in case of all data fetched during Opening (Less or equal to one fetch block)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<br>&nbsp; &nbsp; &nbsp; &nbsp; end;<br>&nbsp; &nbsp; &nbsp; except<br>&nbsp; &nbsp; &nbsp; &nbsp; on E: TObject do begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if FNonBlocking then begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; StopWait;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (E is EFailOver) and (EFailOver(E).FConnLostCause = clExecute) then begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; UsedConnection.RestoreAfterFailOver; //Restore all read transactions<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ReExecute := True; //We should pass clConnectionApplyUpdates FailOver<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EndConnection;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; raise;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<br>&nbsp; &nbsp; &nbsp; &nbsp; end;<br>&nbsp; &nbsp; &nbsp; end;<br>&nbsp; &nbsp; until (not ReExecute);<br>&nbsp; finally<br>&nbsp; &nbsp; if UsedConnection &lt;&gt; nil then<br>&nbsp; &nbsp; &nbsp; UsedConnection.PopOperation;<br>&nbsp; end;<br>end;<br><br>原来DAC的Execute是会返回结果集的!!!!!!晕死~~~~~
 
多人接受答案了。
 
TO:flexitime<br>想和你讨论一下TMSQUERYR的问题,问题说起来比较复杂,不知你遇到没有?<br>能把你的邮箱给我吗?我的是52254884@qq.com。<br>简单地说一下吧。<br>1、在TMSQUERYR中同一个字段有很多条记录是同一个值,而只Select了这个字段,只有这一个字段的时候(如:Select A From Table1),这个时候(不要用Update的SQL语句)在DBGrid中修改一个的记录的时候,将会出现同一个值的记录都被修改。<br>2、在TMSQUERYR只能查询一个表,不能使用Join。<br>3、按levi的说法,是不是还是在执行了两次?
 
后退
顶部