2

我面临两个问题...

(1) 当我尝试使用 Delphi XE6 写入数据库 (SQLite) 时,我总是得到数据库被锁定的错误消息。我确定每次使用命令 FDConnection1.Close 访问数据库时都会关闭数据库;

(2) 如何将传入参数插入到表中?我有以下传入参数

procedure TStock_Bookkeeping.Write_To_DB(const Stock_Code, Stock_Name,
Tran_Date, Buy_Sell, Price_Per_Share, Num_Shares, Trans_Fee: string);

并尝试使用以下 SQL 命令写入表:

sSQL := 'INSERT INTO Each_Stock_Owned(Stock_Code, Stock_Name, Tran_Date, Buy_Sell,
         Price_Per_Share, Num_Shares, Trans_Fee) 
         VALUES (Stock_Code, Stock_Name, Tran_Date, Buy_Sell, Price_Per_Share,  
         Num_Shares, Trans_Fee)';

但它似乎不起作用......

以下是我遇到问题的完整程序

procedure TStock_Bookkeeping.Write_To_DB(const Stock_Code, Stock_Name,
  Tran_Date, Buy_Sell, Price_Per_Share, Num_Shares, Trans_Fee: string);
var
  query : TFDQuery;
  sSQL: string;
begin
    query := TFDQuery.Create(nil);
  try
    ConnectToSQLite;
    query.Connection := FDConnection1;
  if Stock_Code.IsEmpty then
    ShowMessage('Stock Code Cannot Be Empty')
    else
      if Stock_Name.IsEmpty then
        ShowMessage('Stock Name Cannot Be Empty')
        else
          if Tran_Date.IsEmpty then
            ShowMessage('Transaction Date Cannot Be Empty')
            else
            begin
//              sSQL := 'INSERT INTO Each_Stock_Owned(Stock_Code, Stock_Name, Tran_Date, Buy_Sell, Price_Per_Share, Num_Shares, Trans_Fee) VALUES (Stock_Code, Stock_Name, Tran_Date, Buy_Sell, Price_Per_Share, Num_Shares, Trans_Fee)';
              sSQL := 'INSERT INTO Each_Stock_Owned(Stock_Code, Stock_Name, Tran_Date, Buy_Sell, Price_Per_Share, Num_Shares, Trans_Fee) VALUES (1,2,3,4,5,6,7)';
              query.sql.Text := sSQL;
              query.ExecSQL;
              query.Open();
        end;
  finally
    query.Close;
    query.DisposeOf;
    DisconnectFromSQLite;
  end;

end;

任何提示将不胜感激。提前致谢。

4

2 回答 2

7

执行动态 SQL 语句有两种技术。但我将使用更短的 SQL 来专注于逻辑:

纯粹的方式(使用参数)

q.SQL.Text:=
  'INSERT INTO Each_Stock_Owned (Stock_Code, Stock_Name) '+
  'VALUES (:Stock_Code, :Stock_Name)';
q.Prepare; //Optional
q.ParamsByName('Stock_Code').AsString := Stock_Code;
q.ParamsByName('Stock_Name').AsString := Stock_Name;
q.ExecSQL;

肮脏的方式(构建 SQL)

q.SQL.Text:=
  'INSERT INTO Each_Stock_Owned (Stock_Code, Stock_Name) VALUES ('+
  QuotedStr(Stock_Code) + ', '+
  QuotedStr(Stock_Name) + ')';
q.ExecSQL;

差异是显着的。肮脏的方式使您面临 SQL 注入问题(就像在大多数其他语言中,当您动态地构建 SQL 但没有参数时)。这对您来说可能是也可能不是问题。如果您知道该过程仅由您自己的代码私下调用,并且这些过程参数值只能包含好的值...或者如果您在构建和执行 SQL 之前进行了一些良好的参数检查...那么您是安全的.

但是,如果您使用参数(纯方式)执行此操作,您将自动免受 SQL 注入,因为 SQL 语句由引擎验证,而无需知道参数值。所以 SQL 语句结构是引擎已知的,不能被实际值改变。

另一个考虑因素是执行该 INSERT 语句的频率。纯方式允许您准备一次查询,并使用不同的参数值多次执行(您不能破坏查询对象,也不能更改 SQL 属性,并且必须调用一次 Prepare 方法)。如果您在循环中频繁运行它,那么它可能比多次构建 SQL 更有效。OTOH,如果您只需要插入一行,则可能会带来更多开销。

==================

顺便说一句...... CL 是对的......这些值不应该是字符串。请记住,Parameter 对象有许多属性来处理不同的数据类型:

  q.ParamsByName('somedate').AsDateTime := Date;
  q.ParamsByName('somenumeric').AsFloat := 3/4;

... 等等。

如果你不使用参数,那么事情就会变得困难。QuoteStr 函数适用于字符串,但如果你想直接在 SQL 中刻录日期和货币以及其他值类型,你必须知道你在做什么。您可能会遇到许多不同的问题...特定于区域设置或格式设置不利于与您的服务器通信,这可能位于世界的另一端,或者可能无法读取以这种方式格式化的值。您可能必须处理引擎特定的格式和转换问题。

如果您确实使用参数,那么 FireDAC 应该为您处理所有这些;)

于 2014-07-31T07:33:38.397 回答
1

要将值获取到查询中,请使用参数(文档对此进行了解释):

query.SQL.Text := 'INSERT INTO Each_Stock_Owned'+
                    '(Stock_Code, Stock_Name, Tran_Date, Buy_Sell, '+
                     'Price_Per_Share, Num_Shares, Trans_Fee) '+
                    'VALUES (:sc, :sn, :td, :bs, :pps, :ns, :tf)';
query.ParamByName('sc').AsString := Stock_Code;
query.ParamByName('sn').AsString := Stock_Name;
query.ParamByName('td').AsString := Tran_Date;
query.ParamByName('bs').AsString := Buy_Sell;
query.ParamByName('pps').AsString := Price_Per_Share;
query.ParamByName('ns').AsString := Num_Shares;
query.ParamByName('tf').AsString := Trans_Fee;
query.ExecSQL;

(而且我怀疑所有这些值真的应该是字符串......)

于 2014-07-31T07:28:08.750 回答