Inserting a JSON Object into a database - json

I am building a DataSnap server in Delphi XE2, and I'm having trouble figuring out the best way to insert the JSON Object that the client sends me into a Database.
Here's the format of the object I'm receiving:
{"PK":0,"FIELD1":"EXAMPLE","FIELD2":5, "DATE":""}
The solution I found was the following code
with qryDBMethods do
begin
SQL.Text := 'SELECT * FROM Table';
Open;
Append;
FieldByName('PK') .AsInteger := StrToInt(newId.ToString)
FieldByName('FIELD1').AsString := Object.Get('FIELD1').JsonValue.Value;
FieldByName('FIELD2').AsInteger := StrToInt(Object.Get('FIELD2').JsonValue.Value);
FieldByName('DATE') .AsDateTime:= Now;
Post;
After that, I would have my query component made into a JSON Object and returned to my client, but problem is, this is going to be a big application with dense tables and so doing a "SELECT * " every time I want to insert something isn't ideal. What is the best way to do this?

Try using a INSERT sentence instead.
with qryDBMethods do
begin
SQL.Text := 'INSERT INTO Table (PK, FIELD1, FIELD2) VALUES (:PK, :FIELD1, :FIELD2)';
ParamByName('PK') .Value:= StrToInt(newId.ToString)
ParamByName('FIELD1').Value := Object.Get('FIELD1').JsonValue.Value;
ParamByName('FIELD2').Value:= StrToInt(Object.Get('FIELD2').JsonValue.Value);
ExecSQL();

If what the problem is the amount of data when you open a Select * From Table, why not do something like Select * From Table Where 1 <> 1?
This way you would be able to insert while not loading any result.
Other option would to make an Insert script instead.

Related

Duplicate problem in MySQL with autoincrement id

I have an issue with my database.
Table structure is:
Table name: sales
sale_id (autoincrement)
date (datetime)
total (decimal)
etc.
I have 2 computers, one is "the server" and the other is "the client", when I Insert in "sales" sometimes the database saves more than 1 record, it's an issue kind of random because one day could be normal just save 1 record as is but other day could save 2 or more duplicates.
My code is:
qry1.SQL.Text := 'SELECT * FROM sales '
+ 'WHERE sale_id = 1';
qry1.Open;
qry1.Insert;
qry1.FieldByName('date').AsDateTime := Date;
qry1.FieldByName('total').AsFloat := total;
qry1.Post;
saleId := qry1.FieldByName('sale_id').AsInteger;
qry1.Close;
// Code to save sale details using saleId.
I'm using Delphi 10.3 + ZeosLib 7.2.6-stable + MySQL 8.0
I opened the ports in the server so I have a direct connection to MySQL, I don't know what could be happening
Hope you can help me
Update----
Thanks for your kind answers,
#nbk Yes, I did it already.
#A Lombardo I used "where" to get just 1 record and then I use the query to insert the new one similar to use TTable but instead of load the hole table I just get one record and I can insert (qry.Insert),
#TheSatinKnight not only I get two records, sometimes I get 3 or more, but makes sense probably the keayboard is not working well and could send "enter" key more than once.
#fpiette, I will do ti right now.
I will keep you posted.
There are better ways to accomplish an insert than to open a TZTable and inserting on that open table.
As another approach, drop 2 TZQuery (NOT TZTable) on your form (which I'll assume is TForm1 - change as appropriate).
Assuming the name is ZQuery1 and ZQuery2.
Set its connection property the same as your TZTable, so it uses the same connector.
Set ZQuery1.SQL property to 'Insert into sales (date, total) values (:pdate, :ptotal)' //(w/o quotes)
Set ZQuery2.SQL property to 'select last_insert_id() as iddb'
now add the Function below to your form's Private delcaration
TForm1 = class(TForm)
ZQuery1: TZQuery; //added when dropped on form
ZQuery2: TZQuery;
private
{ Private declarations }
function AddNewSale(SaleDate: TDateTime; Total: Double): Integer; //add this line
public
{ Public declarations }
end;
and then add the following code to your form's methods
var
Form1: TForm1;
implementation
{$R *.dfm}
function TForm1.AddNewSale(SaleDate: TDateTime; Total: Double): Integer;
begin
ZQuery1.ParamByName('pdate').AsDateTime := SaleDate;
ZQuery1.ParamByName('ptotal').AsFloat := Total;
ZQuery1.ExecSQL; //*Execute* the Insert - Only "open" SQL that returns a result set
//now the record has been added to your DB
if ZQuery1.RowsAffected = 1 then //check to ensure it was inserted
begin
ZQuery2.Open;
try
Result := ZQuery2.FieldByName('iddb').AsInteger;
finally
ZQuery2.Close;
end;
end
else
result := -1;//indicate error by returning negative value
end;
now in the place you want to insert the record, simply call this function:
var
ReturnValue: Integer;
begin
ReturnValue := AddNewSale(Date, total);
if ReturnValue < 0 then
//error happened
else
begin
//Everything worked
end;
end;
Thanks again for all your kind answers.
At the end the problem was keyboard, It had a problem with "Enter" key, so when you pressed it, it send more than one pulsation so #TheSatinKnigh your approach was correct
#fpiette I created the log file and I figured out as you said the request had been executed twice or more.
I know maybe it is a silly thing for a programmer because I was disabling the button to late, sorry for that
#A Lombardo thanks for you code I like it better than mine I will use it

go - How do i use gorp select for an empty interface

Hi i am using gorp and want to use select query for any table without actually knowing its schema
for that i am using the query
db, err := sql.Open("mysql", "root:1234#tcp(localhost:3306)/information_schema")
checkErr(err, "sql.Open failed")
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{}}
var data []interface{}
_, err = dbmap.Select(&data, "select * from collations")
checkErr(err, "select query failed")
fmt.Println(data)
}
However this is resulting in an error because i can only pass a struct as first parameter to select
this returns an error
select query failed gorp: select into non-struct slice requires 1 column, got 6
suggest me some corrections or any other alternative so that i can use select query on any table name dynamically selected by user
If you don't know the schema, don't use GORP ... Why? because GORPs is a mapper, it needs a source and a target field to know how to process data, if you don't pass a target then GORP really doesn't know what to do.
However, you can do this using the standard SQL package. See this answer for more information: sql: scan row(s) with unknown number of columns (select * from ...)

DELPHI Fill a listbox with the results of ADO Query

have been reading through all associated threads but no solution posted to help me with Delphi.
Quite simply, i have a MySQL table called Story and i want to extract specific fields from this - and hence populate list box.
From other posts, i've used the following...
adoqMenu.Close;
adoqMenu.SQL.Text := 'SELECT StoryID, Story Description, Completion Date FROM Story';
try
adoqMenu.Open;
ListBox1.Items.Clear;
while not adoqMenu.Eof do
begin
ListBox1.Items.Add(adoqMenu.Fields[0].AsString);
adoqMenu.Next;
end;
finally
adoqMenu.Close;
end;
This only gives me the first field...grr. Quite simply, how can i change this so that the fields stated in the SELECT clause appear in the listbox as is?
Thanks
You only see one field because you are only reading out one field (adoqMenu.Fields[0]). Simply read out the other fields as well:
adoqMenu.Close;
adoqMenu.SQL.Text := 'SELECT StoryID, Story Description, Completion Date FROM Story';
adoqMenu.Open;
try
ListBox1.Items.Clear;
while not adoqMenu.Eof do
begin
Value1 := adoqMenu.Fields[0].AsString;
Value2 := adoqMenu.Fields[1].AsString;
Value3 := adoqMenu.Fields[2].AsString;
// use Values as needed. Format the ListBox text however
// you want to show all three values...
ListBox1.Items.Add(...);
adoqMenu.Next;
end;
finally
adoqMenu.Close;
end;
Depending on your actual needs (which you did not explain), a multi-column TListView in vsReport mode may be a better choice than a TListBox:
adoqMenu.Close;
adoqMenu.SQL.Text := 'SELECT StoryID, Story Description, Completion Date FROM Story';
adoqMenu.Open;
try
ListView1.Items.Clear;
while not adoqMenu.Eof do
begin
Item := ListView1.Items.Add;
Item.Caption := adoqMenu.Fields[0].AsString;
Item.SubItems.Add(adoqMenu.Fields[1].AsString);
Item.SubItems.Add(adoqMenu.Fields[2].AsString);
adoqMenu.Next;
end;
finally
adoqMenu.Close;
end;

capture values from a simple select query TADOQuery Delphi

I am having issues returning the value gotten from a simple SELECT query using TADOQuery
here is my code below:
dbWizconQuery.SQL.Clear;
dbWizconQuery.SQL.Add('SELECT * FROM test');
tb_wizconValues.Items.Add('' + dbWizconQuery.SQL.GetText);
dbWizconQuery.ExecSQL;
processed := IntToStr(dbWizconQuery.FieldByName ('input' ).Value);
tb_wizconValues.Items.Add('' + processed);
I get the first print out in my textbox ok, with the SQL String
but then i dont get the value coming out.
Can you see why this might be?
Processed is a String and input is an INT(5) which comes out AsString
Kind Regards,
Jordan
in delphi ExecSQL is used when you want to do an
UPDATE,INSERT,DELETE
when you want to do a select call open or just set the query to
active := true;
you can get multiple results back by using this code:
dbWizconQuery.SQL.Clear;
dbWizconQuery.SQL.Text := 'SELECT * FROM test';
dbWizconQuery.Open; // or dbWizconQuery.Active := True;
while not dbWizconQuery.eof do
begin
ShowMessage(dbWizconQuery.FieldByName('FieldName').AsString); // this shows the fields value
dbWizconQuery.Next; //use this line or you will get an infinite loop
end;
dbWizconQuery.Close; //closes the dataset
for executing statements like UPDATE,INSERT,DELETE use it like this:
dbWizconQuery.SQL.Text := 'DELETE FROM test WHERE ID = 1';
dbWizconQuery.ExecSQL;
ExecSQL is used for statements that don't return a rowset, such as INSERT, DELETE and UPDATE. For a SELECT, you need to use Open instead:
dbWizconQuery.SQL.Clear;
dbWizconQuery.SQL.Add('SELECT * FROM test');
tb_wizconValues.Items.Add('' + dbWizconQuery.SQL.GetText);
dbWizconQuery.Open;
processed := IntToStr(dbWizconQuery.FieldByName ('input' ).Value);
tb_wizconValues.Items.Add('' + processed);
For more info, see the documentation for TDataSet

dbExpress how to show result from query?

var
Connection: TSQLConnection;
SqlSet:TSQLDataSet;
begin
Connection := TSQLConnection.Create(nil);
SqlSet := TSQLDataSet.Create(nil);
SqlSet.SQLConnection:=Connection;
Connection.DriverName := 'MySQL';
Connection.GetDriverFunc := 'getSQLDriverMYSQL';
Connection.LibraryName := 'dbxmys.dll';
Connection.VendorLib := 'libmysql.dll';
Connection.LoginPrompt:=False;
Connection.Params.Values['Database']:=('shadowxx1');
Connection.Params.Values['User_Name']:=('shadowxx1');
Connection.Params.Values['Password']:=('shadowxx1');
Connection.Params.Values['HostName']:=('shadowxx1');
Connection.Open;
Connection.Connected:=True;
SqlSet.CommandType:=ctQuery;
SqlSet.CommandText:= 'SELECT VERSION()';
SqlSet.ExecSQL;
Connection.Close;
Connection.Free;
SqlSet.Free;
end;
Code working , but , how to show result of query or extract it to the grid???
I simply dont find this information, in ADO it was smth like this
DataSrc := TDataSource.Create(Self);
DataSrc.DataSet := ADOQuery;
DataSrc.Enabled := true;
DBGrid1.DataSource := DataSrc;
If someone can - give some examples
And like this dont work
You're using the wrong method. In any of the TDataSet descendants that have it, ExecSQL is for executing queries that return no result set, such as INSERT, UPDATE, DELETE, or CREATE TABLE. See, for instance, TSQLQuery.ExecSQL (emphasis mine)
Executes a query that does not return a set of records.
Call ExecSQL to execute an SQL command that does not return a set of records. This command is a query other than a SELECT query, such as an INSERT, UPDATE, DELETE, or CREATE TABLE query.
Use TSQLQuery.Open to return rows from a SELECT. Something like this should work (untested - I don't use MySQL or DBExpress):
var
Qry: TSQLQuery;
VersionString: String;
// Set up your connection as above, and open it
Qry := TSQLQuery.Create(nil);
Qry.SQLConnection := Connection;
Qry.SQL.Text := 'SELECT VERSION() as DBVersion';
Qry.Open;
if not Qry.IsEmpty then
VersionString := Qry.FieldByName('DBVersion').AsString
else
VersionString := 'No results found';
Qry.Close;
For more information (including step-by-step tutorials), see Using DBExpress Components at the Delphi docwiki. (The one I've linked is for the current Delphi version, but the basic steps for DBExpress are the same since it was introduced.)
If you want a basic video tutorial for using DBExpress in Delphi, you can try DBExpress Data Access Components in Delphi - Delphi 101. I haven't watched it, but it was posted by Embarcadero, the makers of Delphi.