Delphi Insert data from StringGrid to a Database Table - delphi

Iam trying to insert data from a StringGrid to the Oracle DB table for that i tried like below.
function TfrmMapping.LoadtoTable: Boolean;
var
I, J: Integer;
lQuery, s: string;
lData: TArray<string>;
begin
for I := 0 to vTableColumns.count - 1 do
begin
if I <> vTableColumns.count - 1 then
begin
s := s + vTableColumns[I] + ',';
end
else
begin
s := s + vTableColumns[I];
end;
end;
for I := 1 to StrGrdLoadCSVData.RowCount - 1 do
begin
vSortedGrid.Add(StrGrdLoadCSVData.Rows[I].CommaText);
end;
for I := 0 to vSortedGrid.count - 1 do
begin
lQuery := 'Insert into ' + cmbBXDBTables.Text + '(' + s + ') values(' +
vSortedGrid[I] + ')';
DataModSample.FDQuery1.SQL.Clear;
DataModSample.FDQuery1.SQL.Add(lQuery);
DataModSample.FDQuery1.ExecSQL;
end;
Result := True;
end;
In the code , Iam adding all the data of StringGrid(StrGrdLoadCSVData) to a StringList(vSortedGrid), and now iam trying to loop through the StringList to add each row to the DB, But Iam not able to insert because my is taking the values like this
Insert into abc(sno,Name)values(1,welcome);
It is because there are no quotes to welcome it is giving an error.
it is error like this : [FireDAC][Phys][Ora]ORA-00984:column not allowed here
How i can modify my code to insert the data successfully to Db.
EDIT
My table Structure is :
Name Type
--------- ------------
SNO NUMBER(38)
NAME VARCHAR2(15)
my desired result in the table should be like this :
SNO NAME
---------- ----------
1 Hello
2 Welcome
The values in the table comming from the string List

It is because there are no quotes to welcome it is giving an error.
So from what you say :
for I := 0 to vSortedGrid.count - 1 do
begin
lQuery := 'Insert into ' + cmbBXDBTables.Text + '(' + s + ') values('+IntToStr(i+1)+',' +
QuotedStr(vSortedGrid[I]) + ')';
DataModSample.FDQuery1.SQL.Clear;
DataModSample.FDQuery1.SQL.Add(lQuery);
DataModSample.FDQuery1.ExecSQL;
end;
Result := True;
end;
Note: Better to use parameters.
Update:
Another option to insert from TStringGrid using TFDTable:
procedure TForm1.Button2Click(Sender: TObject);
Var I : Integer;
begin
for i := 1 to StringGrid1.RowCount-1 do
begin
try
FDTable1.Append;
FDTable1SNO.Value := StrToInt( StringGrid1.Cells[0,i] );
FDTable1SName.Value := StringGrid1.Cells[1,i];
FDTable1.Post;
except on E: Exception do
begin
MessageDlg(E.Message,mtError,[mbOK],0);
MessageBeep(MB_ICONERROR);
end;
end;
end;
Another option to insert from the TStringGrid using TFDQuery (avoid SQL Injection):
procedure TForm1.Button1Click(Sender: TObject);
Var I : Integer; TableName : String;
begin
TableName := 'Table1';
for i := 1 to StringGrid1.RowCount-1 do
begin
try
FDQuery1.SQL.Text := 'Insert Into '+TableName+' Values(:Val1 , :Val2)' ;
FDQuery1.Params.ParamByName('Val1').Value := StrToInt( StringGrid1.Cells[0,i] );
FDQuery1.Params.ParamByName('Val2').Value := StringGrid1.Cells[1,i];
FDQuery1.ExecSQL;
except on E: Exception do
begin
MessageDlg(E.Message,mtError,[mbOK],0);
MessageBeep(MB_ICONERROR);
end;
end;
You can also Create parameters as you need at Runtime for exemple:
FDQuery1.Params.CreateParam(ftString,'ParamName',ptInput) ;
Also you can use GetTableNames() to get all tables in the Database.

I Modified the Code like below
function TfrmMapping.LoadtoTable: Boolean;
var
I, J: Integer;
lQuery, s, lcolvalues: string;
begin
for I := 0 to vTableColumns.count - 1 do
begin
if I <> vTableColumns.count - 1 then
begin
s := s + vTableColumns[I] + ',';
end
else
begin
s := s + vTableColumns[I];
end;
end;
for I := 1 to StrGrdLoadCSVData.RowCount - 1 do
begin
for J := 0 to vTableColumns.count - 1 do
begin
if J <> vTableColumns.count - 1 then
begin
lcolvalues := lcolvalues +
QuotedStr(StrGrdLoadCSVData.Cells[J, I]) + ',';
end
else
begin
lcolvalues := lcolvalues + QuotedStr(StrGrdLoadCSVData.Cells[J, I]);
end;
end;
lQuery := 'Insert into ' + cmbBXDBTables.Text + '(' + s + ') values (' +
lcolvalues + ')';
DataModSample.FDQuery1.SQL.Clear;
DataModSample.FDQuery1.SQL.Add(lQuery);
DataModSample.FDQuery1.ExecSQL;
lcolvalues := '';
end;
Result := True;
end;
This is inserting values to Table from the string grid, I didn't use Parameter passing as of now. I have to try that also for ensuring more security.
Thank You #Sami , Buy using your concept of FDQuery I have got this idea...

Related

How to fill TListView with TJSONIterator.Next?

I have an app with a TListView and I want to populate data from JSON inside its Items by using TJSONIterator.Next(). The code I use displays the results I want, except for the first one.
How can I parse these JSON objects correctly, what am I doing wrong?
Data: Data.json
{
"event":"subscribe-status",
"status":"ok",
"success":[
{
"symbol":"EUR/USD",
"exchange":"PHYSICAL CURRENCY",
"mic_code":"PHYSICAL CURRENCY",
"country":"",
"type":"Physical Currency"
},
{
"symbol":"USD/JPY",
"exchange":"PHYSICAL CURRENCY",
"mic_code":"PHYSICAL CURRENCY",
"country":"",
"type":"Physical Currency"
},
{
"symbol":"BTC/USD",
"exchange":"Coinbase Pro",
"mic_code":"Coinbase Pro",
"country":"",
"type":"Digital Currency"
},
{
"symbol":"ETH/BTC",
"exchange":"Huobi",
"mic_code":"Huobi",
"country":"",
"type":"Digital Currency"
}
],
"fails":null
}
Code app:
LStringReader := TStreamReader.Create('../../Data.json', TEncoding.UTF8, True);
LJsonTextReader := TJsonTextReader.Create(LStringReader);
LIterator := TJSONIterator.Create(LJsonTextReader);
NObjJSON := LIterator.AsInteger;
ListView1.Items.Clear;
ListView1.BeginUpdate;
try
while True do
begin
while LIterator.Next do
begin
if LIterator.&Type in [TJsonToken.StartObject, TJsonToken.StartArray] then
begin
LIterator.Recurse;
LIterator.Next;
oItem := ListView1.Items.Add;
for NObjJSON := 0 to ListView1.ItemCount -1 do
begin
oItem.Text := 'Object #' + NObjJSON.ToString + ' ' + LIterator.AsValue.ToString;
oItem.Detail := 'Key:' +LIterator.Key;
end
end;
end;
if LIterator.InRecurse then
LIterator.Return
else
Break;
end;
finally
ListView1.EndUpdate;
LIterator.Free;
LJsonTextReader.Free;
lStringReader.Free;
Memo1.Lines.Text := NObjJSON.ToString;
end;
Add this recurse / next at beginning of your loop to prepare to enter array :
while LIterator.Next do
begin
if LIterator.&Type = TJsonToken.StartArray then
begin
LIterator.Recurse;
LIterator.Next;
end;
You can check this exemple in the doc : https://docwiki.embarcadero.com/CodeExamples/Sydney/en/RTL.JSONIterator
The code below is easier to read :
procedure TFormX.LoadJSON;
const
cValue = 'symbol';
var
LValue: TJSONValue;
LArray: TJSONArray;
i: integer;
oItem: TListViewItem;
begin
LValue := TJSONObject.ParseJSONValue('{json}');
LArray := LValue.FindValue('success') as TJSONArray;
if Assigned(LArray) then
begin
for i := 0 to LArray.Count - 1 do
begin
oItem := ListView1.Items.Add;
oItem.Text := 'Object #' + i.ToString + ' ' + LArray.Items[i].GetValue<string>(cValue);
oItem.Detail := 'Key:' + cValue;
end;
end;
end;
After all, i found the correct solution:*
var
LIterator: TJSONIterator;
LJsonTextReader: TJsonTextReader;
LStringReader: TStreamReader;
NObjJSON: Integer;
begin
LStringReader := TStreamReader.Create('../../Data.json', TEncoding.UTF8, True);
LJsonTextReader := TJsonTextReader.Create(LStringReader);
LIterator := TJSONIterator.Create(LJsonTextReader);
NObjJSON := LIterator.AsInteger;
ListView1.Items.Clear;
ListView1.BeginUpdate;
try
while True do
begin
while LIterator.Next do
begin
if LIterator.&Type in [TJsonToken.StartObject, TJsonToken.StartArray] then
begin
Memo1.Lines.Add(LIterator.Key);
LIterator.Recurse;
end
else if LIterator.Path = 'success['+NObjJSON.ToString+'].symbol' then
begin
Memo1.Lines.Add(LIterator.AsValue.ToString);
oItem := ListView1.Items.Add;
for NObjJSON := 0 to ListView1.ItemCount -1 do
oItem.Text := 'Object #' + NObjJSON.ToString + ' ' + LIterator.AsValue.ToString;
end
end;
if LIterator.InRecurse then
LIterator.Return
else
Break;
end;
finally
ListView1.EndUpdate;
LIterator.Free;
LJsonTextReader.Free;
LStringReader.Free;
end;
end;
NObjJSON is used to count the number of objects inside array and it returns 4.
You can use a simple integer (I) and replace "for NObjJSON := 0 to ListView1.ItemCount -1 do" by for I := 0 to ListView1.ItemCount -1 do but the number of objects will return 0.

TStringList to TStringGrid

This is my first post and I am still learning a lot about Delphi and general programming. So please feel free to teach.
I am trying to populate a TStringList with column names from Access. Then display them in a TStringGrid. I am currently getting an "Array Type required" error. But I fear there may be more.
procedure TFormDB1DataMapping.FieldNamesToGrid();
var
myFieldnames: TStringList;
I: Integer;
begin
if not Form1.ConnIn1.Connected then begin
try
//Set Connection Parameters and connect
Form1.ConnIn1Parameters;
Form1.ConnIn1.Connected:=True
finally
end;
end;
myFieldnames := TStringList.Create;
Form1.ConnIn1.GetFieldNames('','',Form1.ComboBoxDB1TableName.Text,'',myFieldnames);
StringGridDB1.RowCount := StringGridDB1.RowCount + 1;
for I:= StringGridDB1.RowCount - 1 downto 1 do
StringGridDB1.Rows[I] := StringGridDB1.Rows[I - 1];
StringGridDB1.Cols[0][1] := myFieldnames.Text;
myFieldnames.Free;
End;
Working procedure with answer below
procedure TFormDB1DataMapping.FieldNamesToGrid();
var
myFieldnames: TStringList;
I: Integer;
begin
if not Form1.ConnIn1.Connected then begin
try
//Set Connection Parameters and connect
Form1.ConnIn1Parameters;
Form1.ConnIn1.Connected:=True
finally
end;
end;
myFieldnames := TStringList.Create;
Form1.ConnIn1.GetFieldNames('','',Form1.ComboBoxDB1TableName.Text,'',myFieldnames);
StringGridDB1.RowCount := StringGridDB1.RowCount + 1;
StringGridDB1.RowCount := myFieldnames.Count + 1;
for I := 0 to myFieldnames.Count - 1 do
StringGridDB1.Cells[0, I + 1] := myFieldnames[I];
myFieldnames.Free;
End;
Assuming you want the field names in the first column sparing the top row for the header, you can do that with the following code:
StringGrid1.RowCount := myFieldnames.Count + 1;
for I := 0 to myFieldnames.Count - 1 do
StringGrid1.Cells[0, I + 1] := myFieldnames[I];

Delphi : Char value for newline

I have an AnsiString as follows :
UID,CardNo,Pin,Password,Group,StartTime,EndTime,Name,SuperAuthorize
1,0,1,,0,0,0,Johnny Test,0
2,0,2,,0,0,0,Harry Potter,0
3,0,3,,0,0,0,Scooby Doo,0
4,0,4,,0,0,0,Baby Lonney Toons,0
How do I add the delimiter for new line in a TStringlist named Users so that I get :
Users[1] : 1,0,1,,0,0,0,Johnny Test,0
Users[2] : 2,0,2,,0,0,0,Harry Potter,0
Users[3] : 3,0,3,,0,0,0,Scooby Doo,0
Here's my code :
procedure TFRTest.GetInfo(sbuffer : AnsiString);
var
userIndex : Integer;;
Users : TStringList;
begin
Users := TStringList.Create;
Users.Delimiter := Char(10) ;
try
Users.DelimitedText := sbuffer;
for userIndex := 0 to Users.Count -1 do
begin
ShowMessage(Users[userIndex]);
end;
finally
Users.Free;
end;
You do not need to use DelimitedText for this. You can simply assign directly to Text and that will treat line breaks as delimiters.
{$APPTYPE CONSOLE}
uses
Classes;
const
Str =
'UID,CardNo,Pin,Password,Group,StartTime,EndTime,Name,SuperAuthorize' + sLineBreak +
'1,0,1,,0,0,0,Johnny Test,0' + sLineBreak +
'2,0,2,,0,0,0,Harry Potter,0' + sLineBreak +
'3,0,3,,0,0,0,Scooby Doo,0' + sLineBreak +
'4,0,4,,0,0,0,Baby Lonney Toons,0';
var
i: Integer;
StringList: TStringList;
begin
StringList := TStringList.Create;
try
StringList.Text := Str;
Writeln(StringList.Count);
for i := 0 to StringList.Count-1 do begin
Writeln(StringList[i]);
end;
finally
StringList.Free;
end;
Readln;
end.
Output:
5
UID,CardNo,Pin,Password,Group,StartTime,EndTime,Name,SuperAuthorize
1,0,1,,0,0,0,Johnny Test,0
2,0,2,,0,0,0,Harry Potter,0
3,0,3,,0,0,0,Scooby Doo,0
4,0,4,,0,0,0,Baby Lonney Toons,0

Exporting DBgrid to CSV?

I have a DB grid which is sorted (the user clicked a few radio buttons and checkboxes to influence the display).
I would like to export all of the data (not just what is visible in the grid), sorted identically, to CSV - how do I do so? The data - not the user settings, just to clarify.
Thanks in advance for any help
[Update] I build sqlQuery bit by bit, depending on the user's settings of checkboxes & radio groups, then, when one of them changes, I
ActivityADQuery.SQL.Clear();
ActivityADQuery.SQL.Add(sqlQuery);
ActivityADQuery.Open(sqlQuery);
That is to say that there isn't a hard coded query, it varies and I want to export the current settings.
I don't know enough if I want to export from the grid or the dataset (I am just not a db guy, this is my first DBgrid), but I suspect that I want the grid, because it has a subset of fields of he dataset.
I guess that TJvDBGridCSVExport is a Jedi component(?) I have tried to avoid them so far, great as they sound, because I prefer discreet, stand-alone, components to installing a huge collection. That may not be the cleverest thing to do, but it's how I feel - ymmv (and prolly does)
Another solution, works also with (multi)selected rows:
procedure TReportsForm.ExportToCSV(const aGrid : TDBGrid; const FileName : String);
Var
I, J : Integer;
SavePlace : TBookmark;
Table : TStrings;
HeadTable : String;
LineTable : String;
First : Boolean;
Begin
HeadTable := '';
LineTable := '';
Table := TStringList.Create;
First := True;
Try
For I := 0 To Pred(aGrid.Columns.Count) Do
If aGrid.Columns[I].Visible Then
If First Then
Begin
// Use the text from the grid, in case it has been set programatically
// E.g., we prefer to show "Date/time" than "from_unixtime(activity.time_stamp, "%D %b %Y %l:%i:%S")"
// HeadTable := HeadTable + aGrid.Columns[I].FieldName;
HeadTable := HeadTable + ActivityReportStringGrid.Columns[i].Title.Caption + ','; // Previous separated wth semi-colon, not comma! (global)
First := False;
End
Else
begin
// HeadTable := HeadTable + ';' + aGrid.Columns[I].FieldName;
HeadTable := HeadTable + ActivityReportStringGrid.Columns[i].Title.Caption + ',';
end;
Delete(HeadTable, Length(HeadTable), 1); // Remove the superfluous trailing comma
Table.Add(HeadTable);
First := True;
// with selection of rows
If aGrid.SelectedRows.Count > 0 Then
Begin
For i := 0 To aGrid.SelectedRows.Count - 1 Do
Begin
aGrid.DataSource.Dataset.GotoBookmark(pointer(aGrid.SelectedRows.Items[i]));
For j := 0 To aGrid.Columns.Count - 1 Do
If aGrid.Columns[J].Visible Then
If First Then
Begin
lineTable := lineTable + aGrid.Fields[J].AsString;
First := False;
End
Else
lineTable := lineTable + ',' + aGrid.Fields[J].AsString;
Delete(LineTable, Length(LineTable), 1); // Remove the superfluous trailing comma
Table.Add(LineTable);
LineTable := '';
First := True;
End;
End
Else
//no selection
Begin
SavePlace := aGrid.DataSource.Dataset.GetBookmark;
aGrid.DataSource.Dataset.First;
Try
While Not aGrid.DataSource.Dataset.Eof Do
Begin
For I := 0 To aGrid.Columns.Count - 1 Do
If aGrid.Columns[I].Visible Then
If First Then
Begin
lineTable := lineTable + aGrid.Fields[I].AsString;
First := False;
End
Else
lineTable := lineTable + ',' + aGrid.Fields[I].AsString;
Delete(LineTable, Length(LineTable), 1); // Remove the superfluous trailing comma
Table.Add(LineTable);
LineTable := '';
aGrid.DataSource.Dataset.Next;
First := True;
End;
aGrid.DataSource.Dataset.GotoBookmark(SavePlace);
Finally
aGrid.DataSource.Dataset.FreeBookmark(SavePlace);
End;
End;
Table.SaveToFile(FileName);
Finally
Table.Free;
End;
End; // ExportToCSV()
You could use a own tiny procedure wich could be adapted to your needs
Procedure Dataset2SeparatedFile(ads: TDataset; const fn: String; const Separator: String = ';');
var
sl: TStringList;
s: String;
i: Integer;
bm: TBookmark;
Procedure ClipIt;
begin
s := Copy(s, 1, Length(s) - Length(Separator));
sl.Add(s);
s := '';
end;
Function FixIt(const s: String): String;
begin
// maybe changed
Result := StringReplace(StringReplace(StringReplace(s, Separator, '', [rfReplaceAll]), #13, '', [rfReplaceAll]), #10, '', [rfReplaceAll]);
// additional changes could be Quoting Strings
end;
begin
sl := TStringList.Create;
try
s := '';
For i := 0 to ads.FieldCount - 1 do
begin
if ads.Fields[i].Visible then
s := s + FixIt(ads.Fields[i].DisplayLabel) + Separator;
end;
ClipIt;
bm := ads.GetBookmark;
ads.DisableControls;
try
ads.First;
while not ads.Eof do
begin
For i := 0 to ads.FieldCount - 1 do
begin
if ads.Fields[i].Visible then
s := s + FixIt(ads.Fields[i].DisplayText) + Separator;
end;
ClipIt;
ads.Next;
end;
ads.GotoBookmark(bm);
finally
ads.EnableControls;
ads.FreeBookmark(bm);
end;
sl.SaveToFile(fn);
finally
sl.Free;
end;
end;

'Malformed string' exception when inserting into Firebird (Delphi, UniDAC, UniSQL, INSERT, parameters)

Using Delphi 2010, UniDAC components, Firebird 2.5 SuperServer.
Database character set is ISO_8559_1 (my Windows default).
I am writing a data transfer application to transfer data from an Access database to a Firebird database that has identical table structure. I am using a ADOQuery component to select all rows from source table, and then looping through that recordset, and using UniSQL component with an INSERT statement with parameters, assigning parameter values from the corresponding source dataset field values.
When running the insert command, it throws a 'Malformed string' exception.
I am stuck and need help to resolve the issue.
Code follows:
function TDataTransfer.BeginTransfer(AProgressCallback: TProgressCallback): Boolean;
var
slSQLSelect, slSQLInsert: TStringList;
i, f, z: Integer;
cmdS, cmdI: String;
adods: TADODataSet;
fbcmd: TUniSQL;
fbscript: TUniscript;
q: String;
s : WideString;
begin
FProgressCallback := AProgressCallback;
fbscript := TUniscript.Create(nil);
try
fbscript.Connection := FirebirdConnection;
FirebirdConnection.StartTransaction;
try
fbscript.Delimiter := ';';
fbscript.ExecuteFile(ExtractFilePath(ParamStr(0)) + 'Firebird_Script_0.txt');
FirebirdConnection.CommitRetaining;
slSQLSelect := TStringList.Create;
slSQLInsert := TStringList.Create;
adods := TADODataSet.Create(nil);
fbcmd := TUniSQL.Create(nil);
try
adods.Connection := AccessConnection;
fbcmd.Connection := FirebirdConnection;
slSQLSelect.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Access_Select.txt');
slSQLInsert.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Firebird_Insert.txt');
z := slSQLSelect.Count - 1;
for i := 0 to z do begin
cmdS := slSQLSelect[i];
cmdI := slSQLInsert[i];
adods.CommandText := cmdS;
fbcmd.SQL.Text := cmdI;
adods.Open;
while not adods.Eof do begin
for f := 0 to adods.FieldCount - 1 do
try
if adods.FieldDefs[f].DataType = ftWideString then begin
s := adods.Fields[f].AsAnsiString ;
q := '"';
// if AnsiStrPos(PAnsiChar(#s), PAnsiChar(q)) <> nil then
// s := StringReplace(s, '"', '""', [rfReplaceAll]);
fbcmd.Params[f].Value := s;
end
else
if adods.FieldDefs[f].DataType = ftWideMemo then
fbcmd.Params[f].SetBlobData(adods.CreateBlobStream(adods.Fields[f], bmRead))
else
fbcmd.Params[f].Value := adods.Fields[f].Value;
except
raise;
end;
try
fbcmd.Execute;
// FirebirdConnection.CommitRetaining;
except
raise;
end;
adods.Next;
end;
adods.Close;
FProgressCallback((i + 1) * 100 div (z + 1), 10);
end;
finally
slSQLSelect.Free;
slSQLInsert.Free;
adods.Free;
fbcmd.Free;
end;
fbscript.ExecuteFile(ExtractFilePath(ParamStr(0)) + 'Firebird_Script_1.txt');
FirebirdConnection.Commit;
Result := True;
except
FirebirdConnection.Rollback;
Result := False;
end;
finally
fbscript.Free;
end;
end;
TIA,
SteveL
If you try to replace s := StringReplace(s, '"', '""', [rfReplaceAll]); with s := StringReplace(s, '''''', '''', [rfReplaceAll]); and uncomment the line;

Resources