Pregunta

Estoy enfrentando a dos problemas ...

(1) Cuando intento escribir en una base de datos (SQLite) utilizando Delphi XE6, siempre obtengo que la base de datos es un mensaje de error bloqueado.Estoy seguro de que cierre la base de datos cada vez que lo acce, usando el comando Fdconnection1.close;

(2) ¿Cómo me inserto en una tabla a partir de parámetros entrantes? Tengo los siguientes parámetros entrantes

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

e intenté escribir en la tabla con el siguiente comando 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)';

pero no parece funcionar ...

El siguiente es el procedimiento completo que tengo problemas con

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;

Cualquier sugerencia será muy apreciada.Gracias de antemano.

¿Fue útil?

Solución

Hay dos técnicas para ejecutar una declaración SQL dinámica. Pero usaré un SQL más corto, para concentrarse en la lógica:

El camino puro (usando parámetros)

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;

El camino sucio (Edificio SQL)

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

Las diferencias son significativas. La forma sucia lo expone a problemas de inyección de SQL (como en la mayoría de los otros idiomas, cuando construye SQL dinámicamente, pero sin parámetros). Esto podría ser o no ser un problema para usted. Si sabe que el procedimiento solo se llama en privado por su propio código, y que los valores de los parámetros de procedimiento solo pueden contener buenos valores ... o si realiza una buena comprobación de parámetros antes de crear y ejecutar su SQL ... entonces está seguro .

Pero si lo hace con parámetros (de la manera pura), se protege automáticamente de la inyección de SQL, ya que la declaración SQL es valida por el motor, sin saber los valores de los parámetros. Por lo tanto, la estructura de la declaración SQL es conocida por el motor y no puede ser alterada por los valores reales.

Otra consideración es con la frecuencia con la frecuencia que ejecutará esa declaración de inserción. La forma pura le permite preparar la consulta una vez, y ejecutarlo muchas veces con diferentes valores de parámetros (no debe destruir el objeto de consulta, ni cambiar la propiedad SQL, y debe llamar al método de preparación una vez). Si lo ejecuta con frecuencia dentro de un bucle, puede ser más eficiente que construir el SQL muchas veces el camino sucio. OTOH Si solo necesita insertar una sola fila, puede plantear un poco más de arriba.

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

Como un lado ... CL es correcto ... esos valores no deben ser cadenas. Tenga en cuenta que el objeto de parámetros tiene muchas propiedades para manejar diferentes tipos de datos:

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

... y así sucesivamente.

Si no usa parámetros, entonces las cosas se vuelven difíciles. La función de cuchero es buena para las cadenas, pero si desea quemar fechas y monedas y otros tipos de valor directamente en su SQL, debe saber qué está haciendo. Puede encontrar muchos problemas diferentes ... configuración regional específica o de formato que no son buenos para la comunicación con su servidor, lo que puede estar en el extremo opuesto del mundo, o puede no poder leer los valores formateados de esa manera. Es posible que tenga que manejar los problemas de conversión y formato específico del motor.

Si usa parámetros, entonces Firedac debería cuidar de todo esto para usted;)

Otros consejos

Para obtener valores en la consulta, use parámetros (esto se explica el DOCUMENTACIÓN ):

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;

(y dudo que todos estos valores realmente deberían ser cuerdas ...)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top