Search for field name in access table then update relevant fields names with values delphi 7 - ms-access-2010

I have an Access database table named ReceiptTable with the following field names: item name, buying price, selling price, goods total, cash, change. I am using an Adoquery and datasource to connect to the access database. When I want to update records to receiptTable, I use the following code to locate an item name from the database then update all the records with similar item name in the database with the values from edit box field values:
procedure TReceiptForm.BitBtn1Click(Sender: TObject);
begin
with ADOQuery1 do
ADOQuery1.Open;
ADOQuery1.Locate('item name',Edit1.Text,[]) ;
ADOQuery1.edit;
ADOQuery1.FieldValues['goods total']:=edit3.Text;
ADOQuery1.FieldValues['cash']:=edit4.Text;
ADOQuery1.FieldValues['change']:=edit5.Text;
ADOQuery1.Post;
end;
The problem I have is that only one row with the item name is updated but the other rows with similar item name are not updated. What code should I add above so that all the rows which have similar item names are updated with values from edit boxes?

This simple code answers your question:
procedure TReceiptForm.BitBtn1Click(Sender: TObject);
var
itemname, goodstotal, cash, change: string;
begin
// Execute query
try
ADOQuery1.Open;
except
on E: Exception do begin
ShowMessage(E.Message);
Exit;
end{on};
end{try};
// Values
itemname := Edit1.Text;
goodstotal := Edit3.Text;
cash := Edit4.Text;
change := Edit5.Text;
// Find first matching record, then go to the end of resultset.
try
ADOQuery1.DisableControls;
if ADOQuery1.Locate('item name', itemname, []) then begin
while not ADOQuery1.Eof do begin
if ADOQuery1.FieldByName('item name').AsString = itemname then begin
ADOQuery1.Edit;
ADOQuery1.FieldValues['goods total'] := goodstotal;
ADOQuery1.FieldValues['cash'] := cash;
ADOQuery1.FieldValues['change'] := change;
ADOQuery1.Post;
end{if};
ADOQuery1.Next;
end{while};
end{if};
finally
ADOQuery1.EnableControls;
end{try};
end;
This will work, but you can consider using one SQL statement for updating the table, or
if it is possible order your query by 'item name' and use this:
...
// Find first matching record, then update while next record matches too.
if ADOQuery1.Locate('item name', itemname, []) then begin
while (not ADOQuery1.Eof) and
(ADOQuery1.FieldByName('item name').AsString = itemname) do begin
ADOQuery1.Edit;
ADOQuery1.FieldValues['goods total'] := goodstotal;
ADOQuery1.FieldValues['cash'] := cash;
ADOQuery1.FieldValues['change'] := change;
ADOQuery1.Post;
ADOQuery1.Next;
end{while};
end{if};
...

Related

Getting multiple values from a function

I have my CLIENTS table with the fields: (PK:ID NUMBER), PRICE NUMBER, PAYMENT_TYPE_ID NUMBER, SESSION_AREA NUMBER
I created a dynamic action from my page, in order to take the above values from CLIENTS table according to my :P2007_CLIENTS_ID.
First i created this type:
CREATE OR REPLACE EDITIONABLE TYPE "PATIENT_DETAILS" as object
( payment_type number,
session_area number,
price number
)
/
then i created this function:
create or replace FUNCTION "F_PATIENT_DETAILS"
(patient_id in NUMBER, session_date date)
RETURN patient_details
IS
v_payment_type number;
v_session_area number;
v_price number;
BEGIN
SELECT CLIENTS.PAYMENT_TYPE_ID into v_payment_type
FROM CLIENTS
WHERE CLIENTS.ID = patient_id;
SELECT CLIENTS.SESSION_AREA into v_session_area
FROM CLIENTS
WHERE CLIENTS.ID = patient_id;
SELECT CLIENTS.PRICE into v_price
FROM CLIENTS
WHERE CLIENTS.ID = patient_id;
if v_price is null then
SELECT POLICIES.PRICE into v_price
FROM POLICIES
WHERE POLICIES.ACTIVE = 1
AND to_char(session_date, 'MM-DD-YYYY') BETWEEN POLICIES.START_DATE AND POLICIES.END_DATE;
end if;
return patient_details(v_payment_type, v_session_area, v_price);
END;
How do i get the values from this function in my page, with Dynamic Action?
I tried this: Identification-> Set Value, Set Type -> PL/SQL Function Body:
declare
My_Result PATIENT_DETAILS;
begin
My_Result := F_PATIENT_DETAILS(:P2007_CLIENTS_ID, :P2007_SESSION_DATE);
end;
Items to Submit-> P2007_CLIENTS_ID, :P2007_SESSION_DATE
Affected Elements -> P2007_PAYMENT_TYPE_ID, :P2007_SESSION_AREA, :P2007_PRICE
but nothing happens..!
Those three fields are never assigned the new values after your function is returned, eg:
:P2007_PAYMENT_TYPE_ID := my_result.payment_type;
Also, there is no reason for 3 separate queries on CLIENTS. You could do this in one motion.
SELECT c.PAYMENT_TYPE_ID, c.SESSION_AREA, c.PRICE
into v_payment_type, v_session_area, v_price
FROM CLIENTS c
WHERE c.ID = patient_id;
Taking that a step further, you could coalesce c.price with a subquery on policies.
Which has a questionable filter on a date being represented as a character. I doubt this would return accurate results.

Is there a way to INSERT Null value as a parameter using FireDAC?

I want to leave some fields empty (i.e. Null) when I insert values into table. I don't see why would I want to have a DB full of empty strings in fields.
I use Delphi 10, FireDAC and local SQLite DB.
Edit: Provided code is just an example. In my application values are provided by user input and functions, any many of them are optional. If value is empty, I would like to keep it at Null or default value. Creating multiple variants of ExecSQL and nesting If statements isn't an option too - there are too many optional fields (18, to be exact).
Test table:
CREATE TABLE "Clients" (
"Name" TEXT,
"Notes" TEXT
);
This is how I tried it:
var someName,someNote: string;
begin
{...}
someName:='Vasya';
someNote:='';
FDConnection1.ExecSQL('INSERT OR REPLACE INTO Clients(Name,Notes) VALUES (:nameval,:notesval)',
[someName, IfThen(someNote.isEmpty, Null, somenote)]);
This raises an exception:
could not convert variant of type (Null) into type (OleStr)
I've tried to overload it and specify [ftString,ftString] and it didn't help.
Currently I have to do it like this and I hate this messy code:
FDConnection1.ExecSQL('INSERT OR REPLACE INTO Clients(Name,Notes) VALUES ('+
IfThen(someName.isEmpty,'NULL','"'+Sanitize(someName)+'"')+','+
IfThen(someNote.isEmpty,'NULL','"'+Sanitize(someNote)+'"')+');');
Any recommendations?
Edit2: Currently I see an option of creating new row with "INSERT OR REPLACE" and then use multiple UPDATEs in a row for each non-empty value. But this looks direly ineffective. Like this:
FDConnection1.ExecSQL('INSERT OR REPLACE INTO Clients(Name) VALUES (:nameval)',[SomeName]);
id := FDConnection1.ExecSQLScalar('SELECT FROM Clients VALUES id WHERE Name=:nameval',[SomeName]);
if not SomeString.isEmpty then
FDConnection1.ExecSQL('UPDATE Clients SET Notes=:noteval WHERE id=:idval)',[SomeNote,id]);
According to Embarcadero documentation ( here ):
To set the parameter value to Null, specify the parameter data type,
then call the Clear method:
with FDQuery1.ParamByName('name') do begin
DataType := ftString;
Clear;
end;
FDQuery1.ExecSQL;
So, you have to use FDQuery to insert Null values, I suppose. Something like this:
//Assign FDConnection1 to FDQuery1's Connection property
FDQuery1.SQL.Text := 'INSERT OR REPLACE INTO Clients(Name,Notes) VALUES (:nameval,:notesval)';
with FDQuery1.ParamByName('nameval') do
begin
DataType := ftString;
Value := someName;
end;
with FDQuery1.ParamByName('notesval') do
begin
DataType := ftString;
if someNote.IsEmpty then
Clear;
else
Value := someNote;
end;
if not FDConnection1.Connected then
FDConnection.Open;
FDQuery1.ExecSql;
It's not very good idea to execute query as String without parameters because this code is vulnerable to SQL injections.
Some sources tells that it's not enough and you should do something like this:
with FDQuery1.ParamByName('name') do begin
DataType := ftString;
AsString := '';
Clear;
end;
FDQuery1.ExecSQL;
but I can't confirm it. You can try it if main example won't work.

Mutating table problem, but im using compound triggers and still the same problem

So I want to change the information of my database record that is email information, so I made a compound trigger that saves the information inside a database object then assigns to the: NEW elements
but still have the problem of mutating elements, hope u can help me
create or replace TRIGGER MAIL_OUTBOX
FOR UPDATE OR INSERT ON MAIL_OUTBOX
COMPOUND TRIGGER
TYPE mail IS RECORD
(v_Mail_Id VARCHAR2(200),
v_new_comment varchar2(200),
v_new_email varchar2(200),
v_new_cc varchar2(200),
replaced_cc varchar2(200),
replaced_receiver varchar2(200));
objetoMail mail;
BEFORE EACH ROW IS
BEGIN
objetoMail.v_Mail_Id := :NEW.MAIL_OUTBOX_ID;
objetoMail.v_new_comment := :OLD.COMMENTS;
objetoMail.v_new_cc := :OLD.CC;
objetoMail.replaced_receiver := :OLD.RECEIVER;
DBMS_OUTPUT.PUT_LINE(objetoMail.v_new_comment);
IF objetoMail.v_new_comment LIKE '%ORA-29279%' THEN
SELECT regexp_substr(objetoMail.v_new_comment,'<([^>]+)>',1,1,NULL,1) into objetoMail.v_new_email
objetoMail.v_new_comment := 'Receptor desconocido ' || objetoMail.v_new_email;
:NEW.COMMENTS := objetoMail.v_new_comment;
hselect REGEXP_REPLACE(objetoMail.v_new_cc, objetoMail.v_new_email, 'xxx#xxxxxx.com' )
into objetoMail.replaced_cc
from MAIL_OUTBOX;
:NEW.CC := objetoMail.replaced_cc;
select REGEXP_REPLACE(objetoMail.replaced_receiver, objetoMail.v_new_email, 'xxx#xxxxxx.com' )
into objetoMail.replaced_receiver
from MAIL_OUTBOX;
:NEW.RECEIVER := objetoMail.replaced_receiver;
END IF;
END BEFORE EACH ROW;
AFTER STATEMENT IS
v_Mail_Id VARCHAR2(60);
BEGIN
MAIL_PROCESS(NULL, objetoMail.v_Mail_Id );
END AFTER STATEMENT;
END;

How to get the ID of the last record inserted in SQLite with Delphi 10?

Delphi 10 with Firemonkey and SQLite: After running the code below I want to get the ID of the last record inserted into an SQLite table. How do I get the last ID?
NOTE: The ID field of Table 1 is autoincrement.
var myQr: TFDQuery;
begin
myQr := TFDQuery.Create(Self);
with myQr do begin
SQL.Add('Insert into table1 values (:_id, :_name, :_dthr)');
Params.ParamByName('_id').ParamType := TParamType.ptInput;
Params.ParamByName('_id').DataType := TFieldType.ftInteger;
Params.ParamByName('_id').Value := null;
ParamByName('_name').AsString := 'name test';
ParamByName('_dthr').AsDateTime := Now;
ExecSQL;
end;
// How to get last ID? <<<<<<<<<<<<<=================
myQr.DisposeOf;
You could query last_insert_rowid if your ID column is declared as INTEGER PRIMARY KEY. In such case the column becomes alias for the ROWID. If that is your case, you can query it natively e.g. this way:
uses
FireDAC.Phys.SQLiteWrapper;
function GetLastInsertRowID(Connection: TFDConnection): Int64;
begin
Result := Int64((TObject(Connection.CliObj) as TSQLiteDatabase).LastInsertRowid);
end;
Or in common way by calling GetLastAutoGenValue method:
function GetLastInsertRowID(Connection: TFDConnection): Int64;
begin
Result := Int64(Connection.GetLastAutoGenValue(''));
end;

SQLite insert on android works only once

I'm with a problem on Delphi, i create a simple app to test the mobile power of RadStudio, I created a simple app that put some data into some inputs and then add it to database when button is clicked. I followed this Embarcadero tutorial as starting point
The problem is that I only get one entry added, then no more entries are added or the list is not refreshed. Below some code:
Table creation:
procedure TTabbedForm.logAfterConnect(Sender: TObject);
begin
log.ExecuteDirect('CREATE TABLE IF NOT EXISTS lista (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,idCons INTEGER,nome TEXT,kms INTEGER,kmsAlarme INTEGER,quantidade INTEGER,quantidadeAlarme INTEGER,data INTEGER,dataAlarme INTEGER,alarmeMsg TEXT)');
end;
Add button code:
procedure TTabbedForm.btnGravarClick(Sender: TObject);
begin
try
SQLQueryInsert.ParamByName('idCons').AsInteger := PopupBoxTipo.ItemIndex;
SQLQueryInsert.ParamByName('nome').AsString := PopupBoxTipo.Text;
SQLQueryInsert.ParamByName('kms').AsInteger := StrToInt(kmsEdit.Text);
SQLQueryInsert.ParamByName('quantidade').AsInteger := StrToInt(qtdEdit.Text);
SQLQueryInsert.ParamByName('data').AsInteger := DateTimeToUnix(dtaEvento.Date);
SQLQueryInsert.ExecSQL();
lista.Refresh;
LinkFillControlToField1.BindList.FillList;
except
on e: Exception do
begin
ShowMessage(e.Message);
end;
end;
end;
If you need some more code snippet, please ask!
Thanks in advance for any reply!
Try this method it works for me
procedure TData.InsertItem(someObject: TSomeObjectClass);
var
qry: TFDQuery;
begin
qry := CreateQry( 'insert into SomeObject(id, description, something)'+
' values (:id, :description, :something);', false);
qry.Params.ParamByName('id').AsInteger := someObject.id;
qry.Params.ParamByName('description').asstring := someObject.description;
qry.Params.ParamByName('something').asstring := someObject.something;
qry.Prepare;
qry.execsql;
qry.Free;
end;
I've put and object in the parameter but you can also put the data you want to insert seperatly

Resources