想写一个SQL查询及执行器,现在碰到的问题是例如我用ADODataSet(或者用CoreLab的DAC系列控件)当用户输入一条SQL后,这句SQL有可能是有返回结果集的(例如用select语句),也有可能没有返回结果集的(例如用Delete或update),那么应如何使用办呢,究竟用DataSet的Open还是Execute方法呢?
如果用Open的话,对于没有结果集返回时就会报错,如果用Exectue的话,那么又不会返回结果集。
那么应如何解决这个问题?

解决方案 »

  1.   

    很好办!
    放置一个adoQuery,命名为:adoMyQuery,
         try
              adoMyQuery.Open;
         except
              try
                   adoMyQuery.ExecSQL;
              except on E:Exception do
                   begin
                        adoMyQuery.Close;
                        showmessage('命令执行失败!原因:'
                             + #13 + E.Message);
                        EXIT;
                   end;
              end;
         end;请结分。100分噢。
      

  2.   

    如何加啊?
    关键是SQL语句千变万化?很难讲那个一定有返回集,举个例子说,如果用户执行的储存过程的话,那根本无从判断。
      

  3.   

    to jozosoft:
    我一早就是这样做了,原来也是很好的,但如果碰到的是Oracle的DDL(例如Create Table等)语句的话,就会出错。
      

  4.   

    这是我原来的代码,如果按这样做的话,运行DDL时就会报错(例如会报对像已被占用,其实就是因为Open语句执行完了(即执行了Create Table),但还是抛出Exception,而Execute又继续执行一次。  FDacDS := FlexCoreLibDBUnit.CreateDataSet(BaseConf.CnnMgr.GetConnection(cmbConnection.Text));
      try
        FDacDS.SQL.Assign(FslContent);
        FDacDS.Open;
        dspContent.DataSet := FDacDS;
        cdsContent.Active := true;
        mgrContent.DoFieldInit;
        labMsg.Caption := 'Rows:'+ IntToStr(cdsContent.RecordCount);
      except
        on E : Exception do
          begin
            try
              FDacDS.Free;
              FDacDS := FlexCoreLibDBUnit.CreateDataSet(BaseConf.CnnMgr.GetConnection(cmbConnection.Text));
              FDacDS.SQL.Assign(FslContent);
              FDacDS.Execute;
              BaseConf.MyMessageBox(Handle,' 执行成功! ', MB_ICONINFORMATION + IDOK);
            except
              on E : Exception do
                begin
                  BaseConf.MyMessageBox(Handle,E.Message, MB_ICONWARNING + IDOK);
                  exit;
                end;
            end;
          end;
        on E : Exception do
          begin
            BaseConf.MyMessageBox(Handle,E.Message, MB_ICONWARNING + IDOK);
            exit;
          end;
      end;
      

  5.   

    (例如用select语句),也有可能没有返回结果集的(例如用Delete或update),对于这个要,要区别对待,自己要作处理,如果命令是DDL时,则使用OPEN,如果命令是DML时,则使用ExecuteDDL与DML是SQL的定义,应该明白我说的吧.
      

  6.   

    用异常处理吧
    try
      open;
    except
     Exectue;
    end;
      

  7.   

    那就Oracle中全部用Open呗,多好,反正什么SQL都用Open执行了
      

  8.   

    判断SQL里是否有Delete,update,insert关键字
      

  9.   

    执行open或者execsql前,判断一下如:
    adoquery.sql.test:='select .....'
    if LowerCase(copy(sql.text,1,6))='select' then
     adoquery.open
    else
     adoquery.execsql;  
    ...
      

  10.   

    1\能得到SQLTEXT的情况
      sqltext:='select ....';   
      if trim(left(sqltext,6))='select ' then
         ado.open
      else ado.execsql;2、不知道sql串的情况
      try
        ado.open;
      except
        ado.execsql;
      end;
      

  11.   

    虽然很感激大家能回复我的问题,但发现其实大家都没有解决我的问题。唉~~
    我不是用Delphi的新手,所以不要以为我连open与execute什么时候用也搞不清,请不要用以下或类似的方法,因为我已试过,无效:1.用判断的方法去识别语句
    原因:SQL语句千变万化,很难说那个一定有返回集,举个例子说,如果用户执行的储存过程的话,那根本无从判断。2.使用类似以下方式
      try 
              adoMyQuery.Open; 
         except 
              try 
                   adoMyQuery.ExecSQL; 
              except on E:Exception do 
                   begin 
                        adoMyQuery.Close; 
                        showmessage('命令执行失败!原因:' 
                             + #13 + E.Message); 
                        EXIT; 
                   end; 
              end; 
         end; 
    上面的方式,一般情况下可以对付,但如果碰到的是Oracle的DDL(例如Create Table等)语句的话,就会出错。其实就是因为Open语句执行完了(即执行了Create Table),但还是抛出Exception,而Execute又继续执行一次。 
      

  12.   

    这样调用不知道可行不可行var sSQL:string;sSQL:=Trim(adoMyQuery.SQL.Text);if (Pos('SELECT', UpperCase(sSQL)) = 1) and
       (Pos('INTO', UpperCase(sSQL)) = 0) and
       (Pos('UPDATE', UpperCase(sSQL)) = 0) then
    begin
      adoMyQuery.Open;
    end
    else
    begin
      adoMyQuery.Execute;
    end;
      

  13.   

    判断SQL里是否有Delete,update,insert关键字
      

  14.   

    感觉如果对SQL语句里面的东西分下类,应该比较好处理了吧。比如存储过程 有返回的前最用USPS 没有的用USPE,
    其它的就判断SELECT  INSERT这些。
      

  15.   


    CSDN的Delphi论坛真的有点不如DelphiBBS,我已在DelphiBBS中找到了答案,但也谢谢各位的热心。http://www.delphibbs.com/delphibbs/DispQ.asp?LID=3888475
      

  16.   

    CSDN的Delphi论坛真的有点不如DelphiBBS,我已在DelphiBBS中找到了答案,但也谢谢各位的热心。 http://www.delphibbs.com/delphibbs/DispQ.asp?LID=3888475
      

  17.   

    好像存储过程一样的也可以吧?
    如果是有数据返回的,ado数据集也能捕获到数据。
      

  18.   

    aert1986应该是说我不厚道吧~
    确实我是觉得CSDN里面的Delphi论坛比不上DelphiBBS的,这其实也不只是我一个人是这样认为的,你自己也可以去那里看看去比较一下。但这并不代表我认为在CSDN上的同行水平会比别人差。
    另外,我的BLOG在CSDN上而不在DelphiBBS上,是因为我也十分留恋CSDN。之前在它上面学到很多的知识。但这些年来,CSDN对Delphi的支持十分的少,你看看它的版面编排就知道了。这是十分让人失望的事。你再看看《程序员》杂志,它们有多少期会提及Delphi?反正你如果看《程序员》,你就会觉得Delphi简直是没落了(Perl的内容比Delphi还要多得多),而事实真的如此吗?
    Borland或许真的有点没落了,但Delphi远还不至于此!!!
      

  19.   

    执行SQL 
    Exec '客户输入的SQL语句';
      

  20.   

    我的建议也是,如果不明白用什么方式就用open方式。
      

  21.   

    用Exec拉
    try
      strSQL:='客户的SQL语句'
      with 数据集 do
      begin
        Close;
        SQL.Text:='exec('+QuotedStr(strSQL)+')';
        ExecSQL;
      end;
     except
       异常处理
     end;
      

  22.   

    呵呵,几天不来,又有回复了~,来回复的同志可能都没有看我给出的网址,算了,我代为转一下
    ----------------
    来自:levi, 时间:2008-4-24 16:57:45, ID:3888579
    试试我的这个方法,肯定行。
    首先用ADOConnection.execute执行命令,我试过了执行 select、Delete、存储过程都没问题。
    var aaa:_Recordset;aaa:= ADOConnection1.Execute('exec Add_Assigner ''000'', ''002''');
    aaa:= ADOConnection1.Execute('select * From Table1');
    aaa:= ADOConnection1.Execute('Delete From Table1 Where Code=''00''');
    (上面语句一次选一个就行了,我是上述三种类似都试过了,都不会出错)
      if Assigned(aaa) then 
        if aaa.Fields.Count > 0  then
          ADODataSet1.Recordset := aaa;原理:其实无论ADOQUERY还是ADOTABLE,最终都是执行adoconn.execute来执行语句或都存储过程的 
    execute有两种方式,
    一种是不返回结果,是PROCEDURE
    procedure TADOConnection.Execute(const CommandText: WideString;
      var RecordsAffected: Integer; const ExecuteOptions: TExecuteOptions = [eoExecuteNoRecords]);
    var
      VarRecsAffected: OleVariant;
    begin
      CheckActive;
      ConnectionObject.Execute(CommandText, VarRecsAffected,
        adCmdText+ExecuteOptionsToOrd(ExecuteOptions));
      RecordsAffected := VarRecsAffected;
    end;一种是返回结果,是Function
    function TADOConnection.Execute(const CommandText: WideString;
      const CommandType: TCommandType = cmdText;
      const ExecuteOptions: TExecuteOptions = []): _Recordset;
    var
      VarRecsAffected: OleVariant;
    begin
      CheckActive;
      Result := ConnectionObject.Execute(CommandText, VarRecsAffected,
        Integer(CommandTypeValues[CommandType])+ExecuteOptionsToOrd(ExecuteOptions));
    end;如果数据集控件是用execsql的,就调用procedure execute, 相反就调用 function execute, 交叉时就会出错楼上讲的提示。 现在我们把这过程提前到先用function ADOCONN.EXECUTE()执行语句,然后自行判断有无结果返回,如有,才直接将结果赋值给ADODataSet显示,避免DELPHI默认的用ADOQuery.Open就调用funtion execute, 用EXECSQL就调用Procedure execute , 这样就可以解决楼主的问题了。  
      

  23.   

    这个是我在大富翁中的最后回复,其实我不用ADO而是用CoreLib的DAC系列组件,问题的解决方法不一样---------
    来自:flexitime, 时间:2008-4-24 23:25:10, ID:3888679 | 编辑搞了半天,原来自己是个大傻瓜~~~,幸好也学到了其它的知识(再次感谢levi)我的程序其实是用CoreLab的OraDAC,MyDac,MSDAC等组件写的,而不是用ADO来写的,但因为ADO以前用得比较多,所以也十分清楚ADODataSet.Execute是不返回结果集的,所以一直也认为其它的组件也不返回结果集的。(BDE, DBX等也确定是不返回)。
    但今天回到家里试一下levi的方法,对ADO确实是有效,但对CoreLab的东西是无效的,原因是_RecordSet是AdoInt中的东西。DAC是没有的。无奈,自己再一次去查DAC的源码,结果在DBAccess.pas单元中发现procedure TCustomDADataSet.Execute;
    var
      ReExecute: boolean;
    begin
      if UsedConnection <> nil then
        UsedConnection.PushOperation(clExecute);
      try
        if Executing then
          Exit;
        BeginConnection;
        if Active then
           Close;
        repeat
          ReExecute := False;      BeforeExecute;
          if not FNonBlocking then
            StartWait;
          try
            if Options.AutoPrepare then
              Prepare;
            if IsQuery then begin
              Open;            //看到了,就是这里,在Exceute里居然会先判断是否查询,是的话再Open!CoreLab真是太可爱了~~
              EndConnection; //Here we decrement UsedConection.FConnectCount that was incremented in InternalExecute and then
                             //in OpenCursor, also we make disconection in case of all data fetched during Opening (Less or equal to one fetch block)
            end
            else begin
              // get param values from master dataset
              if (FDataLink.DataSource <> nil)
                and (FDataLink.DataSource.DataSet <> nil)
                and (FDataLink.DataSource.DataSet.Active)
              then
                SetMasterParams(Params);
              if FNonBlocking then
                SetCursor(crSQLArrow);
              FCommand.WriteParams;
              InternalExecute;
              if IsQuery then begin
                Open;
                EndConnection; //Here we decrement UsedConection.FConnectCount that was incremented in InternalExecute and then
                               //in OpenCursor, also we make disconection in case of all data fetched during Opening (Less or equal to one fetch block)
              end;
            end;
          except
            on E: TObject do begin
              if FNonBlocking then begin
                StopWait;
              end;
              if (E is EFailOver) and (EFailOver(E).FConnLostCause = clExecute) then begin
                UsedConnection.RestoreAfterFailOver; //Restore all read transactions
                ReExecute := True; //We should pass clConnectionApplyUpdates FailOver
              end
              else begin
                EndConnection;
                raise;
              end;
            end;
          end;
        until (not ReExecute);
      finally
        if UsedConnection <> nil then
          UsedConnection.PopOperation;
      end;
    end;原来DAC的Execute是会返回结果集的!!!!!!晕死~~~~~