Delphi:Banco de dados está bloqueado (SQLite)
-
21-12-2019 - |
Pergunta
Estou com dois problemas...
(1) Quando eu tento escrever para um Banco de dados (SQLite) usando Delphi XE6, eu sempre fico com o Banco de dados está bloqueado mensagem de erro.Estou certo de que eu feche o banco de dados toda vez que eu acesso a ele usando o comando FDConnection1.Fechar;
(2) Como faço para INSERIR uma tabela a partir de parâmetros de entrada?Eu tenho os seguintes parâmetros de entrada
procedure TStock_Bookkeeping.Write_To_DB(const Stock_Code, Stock_Name,
Tran_Date, Buy_Sell, Price_Per_Share, Num_Shares, Trans_Fee: string);
e tentou escrever na tabela com o seguinte 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)';
mas ele parece não funcionar...
O seguinte é o procedimento completo eu estou tendo problemas com
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;
Quaisquer sugestões serão muito apreciados.Obrigado antecipadamente.
Solução
Existem duas técnicas para a execução de uma instrução SQL dinâmica.Mas vou usar mais curto, SQL, para concentrar-se na lógica:
A pura forma (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;
O caminho sujo (edifício SQL)
q.SQL.Text:=
'INSERT INTO Each_Stock_Owned (Stock_Code, Stock_Name) VALUES ('+
QuotedStr(Stock_Code) + ', '+
QuotedStr(Stock_Name) + ')';
q.ExecSQL;
As diferenças são significativas.O caminho sujo expõe a injeção de SQL problemas (como na maioria das outras línguas, quando você cria o SQL dinamicamente, mas sem parâmetros).Isso pode ser ou não ser um problema para você.Se você sabe que o procedimento só é chamado em particular pelo seu próprio código, e que aqueles procedimento valores de parâmetro só pode conter bons valores...ou se você fazer um bom parâmetro de verificação antes de criar e executar o SQL...em seguida, você está seguro.
Mas se você fizer isso com parâmetros (o puro maneira), você está automaticamente protegido contra a injeção de SQL, como a instrução SQL é validado pelo motor, sem saber os valores de parâmetro.Portanto, a instrução SQL estrutura é conhecida pelo motor e não pode ser alterada por valores reais.
Outra consideração é a frequência com que você vai executar essa instrução INSERT.A pura forma permite que você para preparar a consulta de uma VEZ, e executá-lo MUITAS VEZES com diferentes valores do parâmetro (você não deve destruir o objeto de consulta, nem alterar a propriedade SQL, e você deve chamar o método de preparação de uma vez).Se você executá-lo freqüentemente dentro de um loop, então ele pode ser mais eficiente do que a construção do SQL muitas vezes o caminho sujo.OTOH, se você só precisa inserir uma única linha, ele pode representar um pouco mais de peso.
=================
Como um aparte...CL é direito...esses valores não devem ser cadeias de caracteres.Tenha em mente que o Parâmetro de objeto tem muitas propriedades para lidar com diferentes tipos de dados:
q.ParamsByName('somedate').AsDateTime := Date;
q.ParamsByName('somenumeric').AsFloat := 3/4;
...e assim por diante.
Se você não usa parâmetros, então as coisas ficam difíceis.O QuoteStr função é boa para cadeias de caracteres, mas se você deseja gravar datas e moedas e outros tipos de valor diretamente em seu SQL você tem que saber o que você está fazendo.Você pode encontrar muitos problemas diferentes...localidade específicos ou configurações de formato que não são boas para a comunicação com o servidor, que pode ser o oposto do fim do mundo, ou pode simplesmente não ser capaz de ler os valores formatados de que maneira.Você pode ter que lidar com motor de formatação específica e problemas de conversão.
Se você usar os parâmetros e, em seguida, FireDAC deve cuidar de tudo isso para você ;)
Outras dicas
Para obter valores para a consulta, use os parâmetros (isto é explicado o documentação):
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;
(E eu dúvida de que todos esses valores realmente deve ser cadeias de caracteres ...)