How can I calculate all field and post (SQL/or onCalcFields) - sql

I want to calculate all fields and add a total field. How can I do this? I did this with sql command, but I don't know how to update dbgrid every time.
My code:
procedure TForm1.Button1Click(Sender: TObject);
begin
ADOQuery1.Close;
ADOQuery1.SQL.Text := 'select names,big1,small1,black1,big2,small2,big3,'+
'((big1+small1+black1+big2+small2+big3)*0.35) as total from adlar7v';
ADOQuery1.Open;
end;
when I run this code the first time it works, but the second time the changes don't appear in the grid.
Second method:
I did this with the TAdoQuery.OnCalcField event but there is a problem.
I know how to calculate a field but I don't know how to multiply all field after addition
my code:
ADOQuery1.FieldValues['total'] :=
ADOQuery1.FieldValues['big1'] + ADOQuery1.FieldValues['small1'] +
ADOQuery1.FieldValues['black1'] + ADOQuery1.FieldValues['big2'] +
ADOQuery1.FieldValues['small2'] + ADOQuery1.FieldValues['big3'];
ADOQuery1.FieldValues['cemi']:=((ADOQuery1.FieldValues['boyuk1'] + ADOQuery1.FieldValues['boyuk2'] + ADOQuery1.FieldValues['boyuk3'])*0.35)+((ADOQuery1.FieldValues['kicik1'] + ADOQuery1.FieldValues['kicik2'])*0.25)+(ADOQuery1.FieldValues['qara1']*0.30);
Hi I want to post calc field(cemi) to table (sql). when I calc all field the last field doesn't post on sql table. because last field (cemi) type fkcalc how can I post fkcalc type field Thanks in advance!

when I run this code first time working second time changes doesn't appear db grid
Use the Object Inspector to set up an AfterPost handler on your AdoQuery1:
procedure TForm1.ADOQuery1AfterPost(DataSet: TDataSet);
begin
AdoQuery1.Refresh;
end;
Then, after you save a change to one of the numeric columns in the grid, the AdoQuery1.Refresh will cause the data to be retrieved again from the server, and that will cause the server to recalculate the "Total" column. The fact that it isn't doing that at the moment is why the Total column isn't updating.
Btw, if I've understood what you are asking about that correctly, then your second point
I know how to calculate field but don't know how to multiplication(*) all field after (+)
Use the following code for this
ADOQuery1.FieldValues['total'] := (
ADOQuery1.FieldValues['big1'] + ADOQuery1.FieldValues['small1'] +
ADOQuery1.FieldValues['black1'] + ADOQuery1.FieldValues['big2'] +
ADOQuery1.FieldValues['small2'] + ADOQuery1.FieldValues['big3'] )
* 0.35;

Related

I keep getting "Data type mismatch in criteria expression" error in delphi

Whenever this code runs the I get the above error. The code is supposed to insert a record into a table and then delete records from another table.
for I := iMax - K to iMax do
begin
Inc(a);
with dmMenu.qryMcDonalds do
begin
SQL.Text :=
'SELECT ID, ItemID, ItemPrice, ItemCategory FROM tblCheckout WHERE ID = ' + IntToStr(I);
Open;
sItemID := Fields[1].AsString;
rItemPrice := Fields[2].AsFloat;
sItemCategory := Fields[3].AsString;
ShowMessage(IntToStr(I));
// I get the error here
SQL.Text :=
'INSERT INTO tblOrderItems (OrderItemID, OrderID, ItemID, ItemCategory, ItemPrice) VALUES ("' + sOrderID + '_' + IntToStr(a) + '"' + ', "' + sOrderID + '", "' + sItemID + '", "' + sItemCategory + '", "' + FloatToStrF(rItemPrice, ffCurrency, 10, 2) + '")';
ExecSQL;
SQL.Text := 'DELETE FROM tblCheckout WHERE ID = ' + IntToStr(I);
ExecSQL;
end; // with SQL
end; // for I
edit: I think i the problem is in the 'INSERT INTO' part. All the columns are short text except the last one, ItemPrice is a currency. I am also using Access
Please start a new project with just the following items on the main form:
A TAdoConnection configured to connect to your database;
A TAdoQuery configured to use the TAdoConnection
A TDataSource and TDBGrid configured to display the contents of the TAdoQuery.
Then, add the following code to the form
const
sSelect = 'select * from tblOrderItems';
sInsert = 'insert into tblOrderItems(OrderItemID, OrderID, ItemID, ItemCategory, ItemPrice) '#13#10
+ 'values(:OrderItemID, :OrderID, :ItemID, :ItemCategory, :ItemPrice)';
sOrderItemID = '0999';
sOrderID = '1999';
sItemID = '2999';
sItemCategory = 'Bolt';
fItemPrice = 5.99;
procedure TForm2.FormCreate(Sender: TObject);
begin
AdoQuery1.SQL.Text := sInsert;
AdoQuery1.Prepared := True;
AdoQuery1.Parameters.ParamByName('OrderItemID').Value:= sOrderItemID;
AdoQuery1.Parameters.ParamByName('OrderID').Value := sOrderID;
AdoQuery1.Parameters.ParamByName('ItemID').Value := sItemID;
AdoQuery1.Parameters.ParamByName('ItemCategory').Value := sItemCategory;
AdoQuery1.Parameters.ParamByName('ItemPrice').Value := fItemPrice;
AdoQuery1.ExecSQL;
AdoQuery1.SQL.Text := sSelect;
AdoQuery1.Open;
end;
Before compiling and running the program, review and change the values of the constants
so that they don't conflict with any rows already in your table.
The constant sInsert defines a so-called "parameterised SQL statement" Inside the Values
list, the items :OrderItemID, :OrderID, etc, are placeholders which the AdoQuery will turn into
parameters whose values you can supply at run-time, as in the block after AdoQuery1.Parameters.ParamByName(...
You should always construct SQL statements this way and not by concatenating strings
as your code attempts to do. It is far less error-prone, because it's easy to get into a
syntax muddle if you use DIY code and it also makes sure your queries are not prone
to Sql-Injection exploits, which they would be if you
give the user the opportunity to edit the SQL.
If you are using a FireDAC FDQuery, this has Params (an FD data-type) rather thn Parameters
but they work in pretty much the same way - see the Online Help.
In future, please be a bit more careful to ensure you include important details, especially things like the exact line where the error occurs - which you can check by single-stepping through your code using the debugger - and things like the exact db-access components you are using. Also try to get details right like whether a column is a "short text" type or an autonumber.
For a problem that needs to be debugged, like this one, you should also provide a complete, minimal, reproducible example, not just a snippet of code. Quite often, you will realise what the problem is while you are preparing the mre, so it helps you as well as us.
I bet the problem is in incompatible floating point format.
Use parameters to avoid it.

Delphi/Lazarus Filter MSAccess data by SQL Query using DateTimePicker

I would like to filter data from MS Access and get them to the LazReport.
I tried few combinations of code but none of them worked. I am still getting error "Types of data does not equal in critteria expression".
MS Access field is set to Date/Time with format Short Date (01.01.2017).
DateTimePicker is set to YMD (Year, Month, Day).
This is my code:
procedure TForm12.BitBtn1Click(Sender: TObject);
begin
If ListBox1.ItemIndex=0 then
ReportSelected.Caption:=Listbox1.Items[Listbox1.ItemIndex];
If ListBox1.ItemIndex=1 then
ReportSelected.Caption:=Listbox1.Items[Listbox1.ItemIndex];
If ListBox1.ItemIndex=2 then
ReportSelected.Caption:=Listbox1.Items[Listbox1.ItemIndex];
If ListBox1.ItemIndex=3 then
ReportSelected.Caption:=Listbox1.Items[Listbox1.ItemIndex];
If (ReportSelected.Caption='Production Overview') And (DBLookUpListBox1.ItemIndex <> -1) And (CboShift.ItemIndex = 0) then
begin
SQLQuery_ReportShift.Active:=true;
SQLQuery_ReportShift.Close();
SQLQuery_ReportShift.SQL.Text:='SELECT ProductionDate, Shift, AssemblyLine, Product, OperatorsAvailable, ProductionTime, CleanProductionTime, DowntimeTime, GoodParts, ScrapTotal, ScrapRate, QualityRate, Availability, Performance, OEE FROM ProductionInfo WHERE AssemblyLine='''+DBLookUpListBox1.Items[DBLookUpListBox1.Itemindex]+''' AND ProductionDate='''+FormatDateTime('dd/mm/yyyy', DateTimePicker1.Date)+'''';
SQLQuery_ReportShift.Open();
frReport1.LoadFromFile('ProductionOverview.lrf');
frReport1.ShowReport;
end;
If I remember correctly Access uses the date in the order Y/M/D and puts '#' characters around it:
function AccessDate(d:TDateTime) : string;
var
yy,mm,dd : word;
begin
DecodeDate(d, yy,mm,dd);
result := Format('#%d/%d/%d#', [yy,mm,dd]);
end;
As for DateTimePicker to return only date: Turn off time parts by checking dtpHour, dtpMinute and dtpSecond in HideDateTimeParts.

How to display records with certain date from an editbox using ms access and delphi 7

I have a table in MS Access with a name MembersAccount and one of the field name as DatePaid. I am trying to display records from a date entered in an editbox but the output is not displaying any records at all. This is the code I am using.
if ADOQuery1.Locate('datepaid',edit1.Text,[]) THEN
begin
open;
SQL.Clear;
qry:= 'Select * from MembersAccount WHERE((MembersAccount.[DatePaid])='+edit1.Text+')';
SQL.Add(qry);
Active:= True;
Where am I getting the code wrong? Date entered in the editbox is in dd/mm/yyyy format
Use parts like these (not full code):
editStr := edit.Text;
myDate := StrToDate(editStr);
sqlDate := FormatDateTime('yyyy/mm/dd', myDate);
qry:= 'Select * from MembersAccount WHERE((MembersAccount.[DatePaid]) = #' + sqlDate + '#)';

Delphi 7 SQL Parameter found in Select Statement, but not Insert Statement

I am currently coding a Delphi 7 program that utilises SQL and an Access 2003 database.
The unit receives a 5 digit code from a previous unit, via a public variable (this is frmLogin.sCode). On form activation, the program will execute an SQL query to display the record from tblStudents that matches sCode. This statement uses a ParamByName line and works perfectly.
If no match is found, a message is displayed and the user is left with no option, but to click on the add user button. The user is then prompted to enter all of his details into the program, which are then passed to a class which sets out the SQL Insert Statement. A problem occurs now, however, as a message is displayed stating that Parameter Username is not found. I cannot understand why, as it is found when the Select statement is run. Please could someone help with this?
procedure TfrmProfilePage.FormActivate(Sender: TObject);
begin
//Instantiates the object.
objProfilePage := TProfilePage.Create;
sSQL := objProfilePage.SelectSQL;
ExecuteSQL(sSQl);
end;
procedure TfrmProfilePage.ExecuteSQL(sSQL : String);
begin
With dmTextbookSales do
Begin
dbgrdDisplay.DataSource := dsProfilePage;
qryProfilePage.SQL.Clear;
qryProfilePage.SQL.Add(sSQL);
qryProfilePage.Parameters.ParamByName('Username').Value := frmLogin.sCode;
qryProfilePage.Open;
If qryProfilePage.RecordCount = 0
Then
Begin
ShowMessage('Please click on the "Add Details" button to get started.');
btnChange.Enabled := False;
btnSelling.Enabled := False;
btnBuying.Enabled := False;
End;
End;
end;
procedure TfrmProfilePage.GetValues(VAR sStudentName, sStudentSurname, sCellNumber, sEmailAddress : String; VAR iCurrentGrade : Integer);
begin
ShowMessage('Fields may be left blank, but users wishing to sell textbooks should enter at least one contact field.');
sStudentName := InputBox('Name','Please enter your first name:','');
sStudentSurname := InputBox('Surame','Please enter your surname:','');
iCurrentGrade := StrToInt(InputBox('Current Grade','Please enter your current grade:',''));
sCellNumber := InputBox('Cellphone Number','Please enter your cellphone number:','');
sEmailAddress := InputBox('Email Address','Please enter your email address:','#dainferncollege.co.za');
end;
procedure TfrmProfilePage.btnAddClick(Sender: TObject);
begin
GetValues(sStudentName, sStudentSurname, sCellNumber, sEmailAddress, iCurrentGrade);
sSQL := objProfilePage.InsertSQL;
ExecuteSQL(sSQL);
btnChange.Enabled := True;
btnSelling.Enabled := True;
btnBuying.Enabled := True;
end;
The following code is obtained from the linked class, clsProfilePage:
function TProfilePage.InsertSQL: String;
begin
Result := 'INSERT INTO tblStudents (' + '[StudentID]' + ',' + '[StudentName]' + ',' + '[StudentSurname]' + ',' + '[CurrentGrade]' + ',' + '[CellNumber]' + ',' + '[EmailAddress]' + ') VALUES (' + 'Username' + ',' + QuotedStr(fStudentName) + ',' + QuotedStr(fStudentSurname) + ',' + IntToStr(fCurrentGrade) + ',' + QuotedStr(fCellNumber) + ',' + QuotedStr(fEmailAddress) + ')';
end;
function TProfilePage.SelectSQL: String;
begin
Result := 'SELECT * FROM tblStudents Where StudentID = Username';
end;
Your INSERT statement is wrong. You need to add parameters before you can set parameter values. In Delphi, you do that using : before the parameter name in your SQL statement.
In addition, Open is only used when performing a SELECT. INSERT, UPDATE, and DELETE don't return a rowset, and therefore you must use ExecSQL (the dataset method, not your function with the conflicting name) instead.
(While we're at it, never use RecordCount on a SQL dataset - it requires all rows to be retrieved in order to obtain a count, and it's not relevant when performing anything other than a SELECT anyway. Operations performed by ExecSQL should use RowsAffected instead, which tells you the number of rows that were affected by the operation.
(If you need a count of the number of rows, perform a SELECT COUNT(*) AS NumRecs FROM YourTable WHERE <some condition> instead, and access the NumRecs field using FieldByName.)
Change your function that returns the INSERT statement to something like this (the #13 in Result is a carriage return, which prevents having to manually insert spaces at the end of each line to separate SQL words):
function TProfilePage.InsertSQL: String;
begin
Result := 'INSERT INTO tblStudents ([StudentID],'#13 +
'[StudentName], [StudentSurname],'#13 +
'[CurrentGrade], [CellNumber], [EmailAddress])'#13 +
'VALUES (:Username, :StudentName,'#13 +
':StudentSurname, :CurrentGrade,'#13 +
':CellNumber, :EMailAddress)';
end;
You can then use it with ParamByName, without jumping through all of the hoops with QuotedStr and concatenation:
procedure TfrmProfilePage.AddUser;
begin
with dmTextbookSales do
begin
qryProfilePage.SQL.Text := InsertSQL;
qryProfilePage.Parameters.ParamByName('Username').Value := frmLogin.sCode;
qryProfilePage.Parameters.ParamByName('StudentName').Value := frmLogin.sUserName;
// Repeat for other parameters and values - you have to set every
// single parameter. To skip, set them to a null variant.
// Execute the INSERT statement
qryProfilePage.ExecSQL;
// See if row was inserted, and do whatever.
If qryProfilePage.RowsAffected > 0 then
ShowMessage('User added successfully');
// Perform a SELECT to populate the grid contents with the new
// rows after the update
end;
end;
I'd highly suggest you rethink this code. It's extremely convoluted, and it requires too much to accomplish a simple task (adding a new user).
If it were me, I'd use a separate query that was dedicated only to performing the INSERT operation, where you can set the SQL at design-time, and set the proper types for the parameters in the Object Inspector. Then at runtime, you simply set the parameter values and call ExecSQL on that insert query, and refresh your SELECT query to reflect the new row. It avoids all of the noise and clutter (as well as some unnecessary function calls and convoluted SQL building, opening and closing your SELECT query, etc.).
(It would also allow you to remove that horrific with statement, which leads to hard-to-find bugs and difficult to maintain code.)
You also have some bad linkages between forms, where you're referencing frmLogin (a specific form instance) from a second form. This means that you can never have more than one instance of either form in use at the same time, because you've hard-coded in that reference. I'd rethink that to use either parameters passed in when you create the form, or as properties that are set by the login form when creating the profile page form (or whatever TProfilePage is - your post doesn't say).
The best solution would be to move all of the non-UI related code into a separate unit (such as a TDataModule, which is designed to be used to work with non-visual components such as ADO queries) and remove it from the user-interface related forms anyway, which
Eliminates the coupling between forms, allowing the code to be reused.
Removes the non-visual data related components from cluttering the form and form code.
Separates the business logic (the part not related to interacting with the user) in a separate, distinct location, making it (and the code that uses it) easier to maintain.
Your insert statement is wrong. You need to replace the value for [StudentID].
'INSERT INTO tblStudents ('
+ '[StudentID]' + ','
+ '[StudentName]' + ','
+ '[StudentSurname]' + ','
+ '[CurrentGrade]' + ','
+ '[CellNumber]' + ','
+ '[EmailAddress]' + ')
VALUES ('
+ 'Username' + ',' // <-- UserName will never be replaced by a QuotedStr
// Access is looking for a column with the name
// 'UserName' which can not be found
+ QuotedStr(fStudentName) + ','
+ QuotedStr(fStudentSurname) + ','
+ IntToStr(fCurrentGrade) + ','
+ QuotedStr(fCellNumber) + ','
+ QuotedStr(fEmailAddress) + ')';

Oracle UPDATE has no effect

I am working on Sql Developper an I created the following procedure in a package:
PROCEDURE VALIDER(a_session IN NUMBER) AS
i NUMBER;
TYPE type_tab IS TABLE OF PANIER%ROWTYPE;
tabSeances type_tab;
BEGIN
SELECT * BULK COLLECT INTO tabSeances
FROM PANIER
WHERE a_session = sessionweb;
i:=0;
FOR i IN 1 .. tabSeances.count LOOP
-- UPADTE DU NOMBRE DE PLACES LIBRES
BEGIN
UPDATE PROJECTION
SET remaining_seats = (remaining_seats - tabseances(i).nbrplaces)
WHERE num_copy = tabseances(i).num_copy
AND day = tabseances(i).dateseance
AND time_slot = tabseances(i).time_slot
AND movie = tabseances(i).movie;
COMMIT;
--UPDATE ON PANIER
UPDATE PANIER
SET valide = 1
WHERE sessionweb = a_session
AND num_copy = tabseances(i).num_copy
AND dateseance = tabseances(i).dateseance
AND time_slot = tabseances(i).time_slot
AND movie = tabseances(i).movie;
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN raise_application_error(-20035, 'Pas de données');
WHEN OTHERS THEN raise_application_error(-20006,'Autres Erreurs');
END;
END LOOP;
END VALIDER;
The procedure executes normaly and I don't get an error.
I have a kind of product cart: "PANIER". I loop all the entries in thsi cart for one person (session) to validate them in the database and decrement the total number of seats.
But the field "remaining-seats" (from PROJECTIONS) in the first update don't work. The field isn't updated. I have already tried with other values but nothing.
I am sure that the procedure is executetd because the second update still works. It marks the cart entry as "CONFIRMED".
I don't have any trigger on this field.
My tables contains valid data (<>NULL).
I execute this procedure like this (in a BEGIN END; block):
CMDPLACES.VALIDER(1);
Thank for your reply.
Is it day or dateseance in your first update?
UPDATE PROJECTION
SET remaining_seats = (remaining_seats - tabseances(i).nbrplaces)
WHERE num_copy = tabseances(i).num_copy
AND dateseance = tabseances(i).dateseance
AND time_slot = tabseances(i).time_slot
AND movie = tabseances(i).movie;
Also as #ThorstenKettner was mentioning, the timestamp data in the date , may fail while comparing, so we have TRUNCATE the timestamp data using TRUNC() [if needed]!
If the date column is indexed, beware the index will not be used by the database .
To handle NO Data in UPDATE, you can check (SQL%ROWCOUNT > 0) to identify the number of rows updated!
Your first update compares days. What data type are these? In case you deal with DATETIME, make sure to compare without the time part if any. Use TRUNC to achieve this.
AND TRUNC(day) = TRUNC(tabseances(i).dateseance)