Get last row data after insert into using Delphi AdoQuery - sql-server

In Delphi 2007 using ADOQuery
when i add a row into a table using insert into
how do i get last row?
i'm adding row with this.
QD_TEMP1.close;
QD_TEMP1.sql.Clear;
QD_TEMP1.SQL.Add('insert into s_hasta_Kabul (Protokol,Muay_ID,Ocak_Kod,Tc_Kimlik_No) ');
QD_TEMP1.SQL.Add('values (:Protokol,:Muay_ID,:Ocak_Kod,:Tc_Kimlik_No) ');
QD_TEMP1.Parameters.ParamByName('Protokol').Value := 0;
QD_TEMP1.Parameters.ParamByName('Muay_ID').Value := 2;
QD_TEMP1.Parameters.ParamByName('Ocak_Kod').Value := 3;
QD_TEMP1.Parameters.ParamByName('Tc_Kimlik_No').Value := 4;
QD_TEMP1.ExecSQL;
How do i get last added row after execsql ?

You could use the OUTPUT clause to return the inserted recordset, whether or not you have an identity column (it seems form your comments you do not have one).
e.g.
QD_TEMP1.Close;
QD_TEMP1.sql.Clear;
QD_TEMP1.SQL.Add('insert into s_hasta_Kabul (Protokol,Muay_ID,Ocak_Kod,Tc_Kimlik_No) ');
QD_TEMP1.SQL.Add('OUTPUT inserted.*');
QD_TEMP1.SQL.Add('values (:Protokol,:Muay_ID,:Ocak_Kod,:Tc_Kimlik_No) ');
QD_TEMP1.Parameters.ParamByName('Protokol').Value := 0;
QD_TEMP1.Parameters.ParamByName('Muay_ID').Value := 2;
QD_TEMP1.Parameters.ParamByName('Ocak_Kod').Value := 3;
QD_TEMP1.Parameters.ParamByName('Tc_Kimlik_No').Value := 4;
QD_TEMP1.Open; // ExecSQL does NOT return recordset
// QD_TEMP1 now contains the inserted result set
ShowMessage(QD_TEMP1.FieldByName('Tc_Kimlik_No').AsString);
Note we use QD_TEMP1.Open (not ExecSQL) to return the inserted recordset.

It works if you set the parameter direction before "ExeSQL".
q.sql.text := 'insert into TABLE(x) values (:value) set :ID = scope_identity()';
q.Parameters.ParamByName('VALUE').value := 'XXXXX';
q.Parameters.ParamByName('ID').Direction := pdReturnValue;
q.ExecSQL;
ID := q.Parameters.ParamByName('ID').value;
Cheers

have not tested it but it should be something like this
QD_TEMP1.close;
QD_TEMP1.sql.Clear;
QD_TEMP1.SQL.Add('insert into s_hasta_Kabul (Protokol,Muay_ID,Ocak_Kod,Tc_Kimlik_No) ');
QD_TEMP1.SQL.Add('values (:Protokol,:Muay_ID,:Ocak_Kod,:Tc_Kimlik_No) SELECT :test = SCOPE_IDENTITY() ');
QD_TEMP1.Parameters.ParamByName('Protokol').Value := 0;
QD_TEMP1.Parameters.ParamByName('Muay_ID').Value := 2;
QD_TEMP1.Parameters.ParamByName('Ocak_Kod').Value := 3;
QD_TEMP1.Parameters.ParamByName('Tc_Kimlik_No').Value := 4;
QD_TEMP1.ExecSQL;
QD_TEMP1.Parameters.ParamByName('test').Value is your new id

qryTest.Close;
qryTest.SQL.Clear;
qryTest.SQL.Add(' INSERT INTO TEST_GET_IDX ');
qryTest.SQL.Add(' (SNAME, AGE) ');
qryTest.SQL.Add(' VALUES(:pSNAME, :pAGE) ' );
qryTest.SQL.Add(' SELECT IDX = SCOPE_IDENTITY() ' ); // IDX is IDENTITY
qryTest.Parameters.ParamByName('pSNAME').Value:= edSname.Text;
qryTest.Parameters.ParamByName('pAGE').Value:= edAge.Text;
qryTest.Open;
showMessage(dsTest.DataSet.FieldByName('IDX').AsString);

Related

Dataset not in edit or insert mode

Im a new user of stack overflow. I have a big project coming up and i keep getting the same error. I’m trying to load data into my database and no matter how many times I try or what I change I still get the same errors. I have labled nessarry variables, tried tbl().Edit and tbl().Insert inside, outside of my loops and in the case statement but i still seem to be getting the same error.
sPlayer1,sPlayer2, iScore1,iScore2 are global variables.
`
procedure TfrmInvigilator.btnSubmitClick(Sender: TObject);
Var
i, j, iRound: Integer;
begin
sPlayer1 := cmbName1.Text; // Assigning values to variables for cmbPlayers
sPlayer2 := cmbName2.Text;
iScore1 := sedScore1.Value; // Assigning values to variables for sedScores
iScore2 := sedScore2.Value;
iRound := cmbRound.ItemIndex; // Assigning values to variable for cmbRound
if (cmbName1.ItemIndex = -1) then
// Displays show message if cbmName1 is blank
begin
ShowMessage('Please select player name');
end
else
begin // If cbmName1 is not blank then:
i := pos(' ', sPlayer1); // Find position of ' ' in cbmName1.Text
sPlayer1 := Copy(sPlayer1, 1, i - 1); // sPlayer1 := Name of player
dmChess.tblPlayerInfo.Locate('Name', sPlayer1, []); // Locates Name in table
Num1 := dmChess.tblPlayerInfo['ID']; // Retrives ID of player
with dmChess do
begin
tblScoreboard.First;
while NOT tblScoreboard.EOF do
begin
tblScoreboard.Locate('ID', Num1, []);
case iRound of
0:
tblScoreboard['Round 1A'] := iScore1;
1:
tblScoreboard['Round 1B'] := iScore1;
2:
tblScoreboard['Round 1C'] := iScore1;
3:
tblScoreboard['Round 1D'] := iScore1;
4:
tblScoreboard['Round 2'] := iScore1;
5:
tblScoreboard['Semi-Final'] := iScore1;
6:
tblScoreboard['Final'] := iScore1;
end;
tblScoreboard.Post;
end;
end;
end;
if (cmbName2.ItemIndex = -1) then
// Displays show message if cbmName2 is blank
begin
ShowMessage('Please slecet player name');
end
else
begin // If cbmName2 is not blank then:
j := pos(' ', sPlayer2); // Find position of ' ' in cbmName2.Text
sPlayer2 := Copy(sPlayer2, 1, j - 1); // sPlayer2 := Name of player
dmChess.tblPlayerInfo.Locate('Name', sPlayer2, []); // Locates Name in table
Num1 := dmChess.tblPlayerInfo['ID']; // Retrives ID of player
end;
end;
`
You can't just set a new value in a DataSet field (table or query), the DataSet must know that you want to change the current record or insert a new one. An exception tells you this. How to solve: before you make changes to any field in the dataset, you need to set the desired mode. DataSet.edit; - to edit the current record, DataSet.Insert - to insert a new record in the current location, DataSet.Append - to add a new record to the end. For this code snippet, add the line tblScoreboard.Edit; after the line tblScoreboard.Locate('ID', Num1, []);
DataSet.Post – save current changes into record and change DataSet mode back to Browsing

Delphi - reading the records of a database into a string grid

So how do you write the records of a database (from a TADOTable component) into a String grid? (the record's fields are all strings)
I tried something like this but to no avail:
procedure TfrmPuntehou.WriteToList(tbl: TADOTable;grid:TStringGrid);
var
iNewRowCount:integer;
i,j,m: Integer;
const
separator = ',';
begin
tempList:= TStringList.Create;
try
tbl.First;
while not (tbl.Eof) do
begin
tempList.Add(tbl['Car Number']+separator+tbl['Racer Name']+separator+tbl['Licence']);
tbl.Next;
end;
for j:= 1 to (tempList.Count - 1) do
begin
grid.Rows[j].Text := tempList.Strings[(J-1)] ;
end;
finally
tempList.Free;
end;
//fill the row numbers
for m := 1 to grid.rowcount do
begin
grid.Cells[0,m]:= IntToStr(m);
end;
end;
Example of the output I'm trying to get on startup: (Row number column is not part of the db)
Thanks in advance for the help!
Kind Regards
PrimeBeat
You're going through far too much work. You don't need the separate stringlist at all, and your code could be much simpler.
var
i, Row: Integer;
begin
// Populate header row
Grid.Cells[0, 0] := 'Row';
Row := 0;
for i := 0 to Tbl.FieldCount - 1 do
Grid.Cells[i + 1, Row] := Tbl.Fields[i].FieldName; // The +1 skips the Row column
Inc(Row);
// Populate cells
Tbl.First;
while not Tbl.Eof do
begin
for i := 0 to Tbl.FieldCount - 1 do
begin
Grid.Cells[i, Row] := IntToStr(i); // Populate Row number
Grid.Cells[i + 1, Row] := Tbl.Fields[i].AsString; // Fill rest of row with table data
end;
Inc(Row);
Tbl.Next;
end;
end;
Here is an example using TADOQuery and a StringGrid:
procedure TForm1.Button1Click(Sender: TObject);
var
I : Integer;
ARow : Integer;
begin
ADOConnection1.Open('user', 'pass');
ADOQuery1.SQL.Text := 'SELECT * FROM dbo.Person';
ADOQuery1.Open;
if ADOQuery1.Eof then begin
ShowMessage('Data not found');
Exit;
end;
SGrid.RowCount := 1;
SGrid.ColCount := ADOQuery1.Fields.Count + 1;
// Create titles of row 0
for I := 0 to ADOQuery1.Fields.Count - 1 do
SGrid.Cells[I + 1, 0] := ADOQuery1.Fields[I].DisplayName;
// Populate the cells with data from result set
ARow := 1;
while not ADOQuery1.Eof do begin
Inc(ARow);
SGrid.RowCount := ARow + 1;
SGrid.Cells[0, ARow] := ARow.ToString;
for I := 0 to ADOQuery1.Fields.Count - 1 do
SGrid.Cells[I + 1, ARow] := ADOQuery1.Fields[I].AsString;
ADOQuery1.Next;
end;
end;
Thanks to Ken White's answer, I managed to solve the problem!
procedure TfrmPuntehou.WriteToList(tbl: TADOTable;grid:TStringGrid);
var
Row: Integer;
begin
tbl.Active:=True;
Row := 1;
// Populate cells
Tbl.First;
while not Tbl.Eof do
begin
grid.Cells[0,Row]:= IntToStr(Row);
grid.Cells[1,Row]:= tbl.fields[0].AsString;
grid.Cells[2,Row]:= tbl.fields[1].AsString;
grid.Cells[3,Row]:= tbl.fields[2].AsString;
Inc(Row);
IncreaseRowCount(grid);
Tbl.Next;
end;
tbl.Active:=false;
end;

Eof not triggering

I have a function where I get data from a DB, my test data set returns 6500 rows (I extracted the formatted SQL statement from the SQLText Variable and ran it as a test), but then when I run the following code Eof never triggers and I have seen over 100k rows imported.
ADOQuery := TADOQuery.Create(nil);
ADOQuery.ConnectionString := CONNECT_STRING;
// Build SQL Query
SQLText := Format( 'Select Temp.Serial, Temp.QCSample , Temp.Scrap , Temp.StationID , Temp.Defect , Temp.AddData , Temp2.Serial as Parent_Serial ' +
'from TAB_ELEMENT as Temp ' +
'left join TAB_ELEMENT as Temp2 on Temp.Parent_Id = Temp2.Element_Id ' +
'where Temp.Batch_ID = %d and Temp.StationID = 0 ',[iSearchID]);
ADOQuery.SQL.Clear; // Clear query of garbage values
ADOQuery.SQL.Text := SQLText; // Add query text to query module
ADOQuery.Open;
// Handle Results
iIndexPos := 0;
tDataImport.BeginUpdate;
while not ADOQuery.Eof do
begin
tDataImport.Items[iIndexPos].Serial := ADOQuery.FieldByName('Serial').AsString;
tDataImport.Items[iIndexPos].QCStatus := ADOQuery.FieldByName('QCSample').AsBoolean;
tDataImport.Items[iIndexPos].Scrap := ADOQuery.FieldByName('Scrap').AsInteger;
tDataImport.Items[iIndexPos].StationID := ADOQuery.FieldByName('StationID').AsInteger;
tDataImport.Items[iIndexPos].Defect := ADOQuery.FieldByName('Defect').AsBoolean;
tDataImport.Items[iIndexPos].AddData := ADOQuery.FieldByName('AddData').AsString;
tDataImport.Items[iIndexPos].ParentSerial := ADOQuery.FieldByName('Parent_Serial').AsString;
inc(iIndexPos);
end;
So in summery running this query with these parameters I expect 6500 rows, when I run this it never ends even after 100k+ rows are processed.
Open() places the cursor on the first record and sets Eof accordingly. You are not calling Next() to advance the cursor to the next record and update Eof, so you are processing the same record over and over:
ADOQuery.Open;
while not ADOQuery.Eof do
begin
//...
ADOQuery.Next; // <-- add this!
end;
On a side note, you should be using a parameterized query instead of a formatted SQL query string. It is safer, faster, and more efficient on the DB:
ADOQuery := TADOQuery.Create(nil);
ADOQuery.ConnectionString := CONNECT_STRING;
ADOQuery.SQL.Text := 'Select Temp.Serial, Temp.QCSample , Temp.Scrap , Temp.StationID , Temp.Defect , Temp.AddData , Temp2.Serial as Parent_Serial ' +
'from TAB_ELEMENT as Temp ' +
'left join TAB_ELEMENT as Temp2 on Temp.Parent_Id = Temp2.Element_Id ' +
'where Temp.Batch_ID = :iSearchID and Temp.StationID = 0 ';
with ADOQuery.Parameters.ParamByName('iSearchID') do
begin
DataType := ftInteger;
Value := iSearchID;
end;
ADOQuery.Open;
try
iIndexPos := 0;
tDataImport.BeginUpdate;
try
while not ADOQuery.Eof do
begin
tDataImport.Items[iIndexPos].Serial := ADOQuery.FieldByName('Serial').AsString;
tDataImport.Items[iIndexPos].QCStatus := ADOQuery.FieldByName('QCSample').AsBoolean;
tDataImport.Items[iIndexPos].Scrap := ADOQuery.FieldByName('Scrap').AsInteger;
tDataImport.Items[iIndexPos].StationID := ADOQuery.FieldByName('StationID').AsInteger;
tDataImport.Items[iIndexPos].Defect := ADOQuery.FieldByName('Defect').AsBoolean;
tDataImport.Items[iIndexPos].AddData := ADOQuery.FieldByName('AddData').AsString;
tDataImport.Items[iIndexPos].ParentSerial := ADOQuery.FieldByName('Parent_Serial').AsString;
inc(iIndexPos);
ADOQuery.Next;
finally
tDataImport.EndUpdate;
end;
end;
finally
ADOQuery.Close;
end;

Initiating a SOAP Array on Delphi

I am trying to initialize or create an array of a soap call:
Array_Of_ProductIdentifierClinicalType = array of ProductIdentifierClinicalType;
This is the way that I am trying to initialize it:
Product[0].IdentifierType := Array_Of_ProductIdentifierClinicalType.Create();
When I run the application I get this error: Access Violation at address...
The question would be: How to initialize this soap call??
Thank you for your time!!!!
You can do a WSDL import on: http://axelcarreras.dyndns.biz:3434/WSAlchemy.wsdl
procedure TFrmMain.Button16Click(Sender: TObject);
Var
ExcludeExpiradas: String;
Serv: AlchemyServiceSoap;
req: AlchemyClinical;
element: AlchemyClinicalRequest;
Prescribed: PrescribedType;
//Prescribing: Prescribing
Prescribing: PrescribedType;
alc: AlchemyIdentifierType;
D: TXSDate;
Counter: Integer;
ProductStr: AlchemyIdentifierClinicalType;
begin
With DM do
begin
ExcludeExpiradas := ' and (' + chr(39) + DateToStr(Date) + chr(39) + ' < (FECHARECETA + 180)) ';
CDSRx_Procesadas.Close;
CDSRx_Procesadas.CommandText := 'SELECT * ' +
' FROM RX_PROCESADAS WHERE ' +
' (NUMERORECETA IS NOT NULL AND CANTIDAD_DISPONIBLE > 0)' +
ExcludeExpiradas +
' and NumeroCliente = ' + CDSPacientesNumeroCliente.asString +
' Order by NumeroReceta';
//ShowMessage(CDSRx_Procesadas.CommandText);
CDSRx_Procesadas.Open;
ProductStr := AlchemyIdentifierClinicalType.Create;
With ProductStr do
begin
Identifier := 1;
end;
element := AlchemyClinicalRequest.Create;
//Prescribed := PrescribedType.Create;
With element do
begin
With Prescribed do
begin
Counter := 0;
while not CDSRx_Procesadas.eof do
begin
Product := Array_Of_ProductIdentifierClinicalType.Create();
With Product[0] do
begin
IdentifierType := ProductIdentifierTypeEnum.NDC9;
Identifier := Copy(DM.CDSInventarioNDC.Value, 1, 9);
end;
Counter := Counter + 1;
CDSRx_Procesadas.Next;
end;
end;
With Prescribing do
begin
Counter := 0;
Product[0].IdentifierType := ProductIdentifierTypeEnum.AlchemyProductID;
Product[0].Identifier := Copy(DM.CDSInventarioNDC.Value, 1, 9);
Counter := Counter + 1;
end;
With PatientDemographics do
begin
while not CDSAlergies.Eof do
begin
Allergy.AllergySubstanceClass[0].Identifier := CDSAlergiesNOALERGIA.Value;
CDSAlergies.Next;
end;
if CDSPacientesSEXO.Value = 1 then
Gender := GenderTypeEnum.Male
else
Gender := GenderTypeEnum.Female;
D := TXSDate.Create;
D.AsDate := CDSPacientesFECHANACIMIENTO.AsDateTime;
DateOfBirth := D;
end;
With RequestedOperations do
begin
DrugToDrug := True;
//DuplicateTherapy
Allergy := True;
With WarningLabels do
begin
Request := True;
LanguageCode := 'en-US';
MaxLines := 5;
CharsPerLine := 24;
end;
With DoseScreening do
begin
Request := True;
end;
AdverseReactions.Request := True;
end;
IgnorePrescribed := False;
IncludeConsumerNotes := True;
IncludeProfessionalNotes := True;
end;
end;
end;*
Assuming that this line of code from the question is accurate:
Array_Of_ProductIdentifierClinicalType = array of ProductIdentifierClinicalType;
then the problem lies here:
Product := Array_Of_ProductIdentifierClinicalType.Create();
This is a dynamic array constructor. It creates a dynamic array of length equal to the number of parameters to the constructor. And then assigns each element of the array, in turn, to the parameters passed.
Consider an example using TBytes = array of Byte.
Bytes := TBytes.Create(1, 2, 3);
This initialises Bytes to be an array of length 3 and having values 1, 2 and 3.
Now, let's look at your code again. This initialises Product to be an array of length 0. So when you access Product[0] that results in a runtime error because the array index is out of bounds.
To solve the problem you will need to make sure that the array is initialised to have sufficient elements. One option is certainly to use a dynamic array constructor. Another is to use SetLength. I suspect that your understanding of Delphi's dynamic arrays is poor. I suggest that you consult the documentation.

Delphi table Join

I’ve been tasked with patching a Delphi software to work with a new database(mssql) structure.
In the previous database(mssql), all the columns being read were in the same table.
In the new version the file_name and class_name are in a different table as the quantity values.
I believe I can fix this by writing a Join for these two database tables.
Issue is, I’m not familiar with Delphi!
The current code is below.
How do I get these two databases to join into one?
dataTable=style001
dataTable1=style
THANKS!!!
dataTable.Active := True;
datatable.Open;
dataTable1.Active := True;
datatable1.Open;
while not datatable1.Eof do
begin
Application.ProcessMessages;
file_name := trim(datatable1.FieldByName('code').asString) + '.jpg';
class_name := trim(datatable1.FieldByName('category').asString);
if not FileExists(picfolder + file_name) then
begin
dataTable1.next;
continue;
end;
instock := datatable.FieldByName('onhand').asString;
TryStrToInt(instock, instock_num);
quantity := datatable.FieldByName('onorder').asString;
TryStrToInt(quantity, num);
num := instock_num - num;
is_active := ( num > 10 );
for i := 0 to file_count-1 do
begin
if is_active = active_class[file_class[i]] then
if SameText(files[i],file_name) then
begin // use TStringList instead
if SameText(class_name, classes[file_class[i]]) then // same class
begin
file_ok[i] := true;
class_ok[file_class[i]] := true;
end;
break;
end;
end;
if class_name <> '' then class_name := class_name + '\';
dest := picfolder + active_path[is_active] + class_name;
deldest := picfolder + active_path[not is_active] + class_name;
{$I-}
if FileExists(deldest + file_name) then
DeleteFile(PChar(deldest + file_name ));
if not FileExists(dest + file_name) then
begin
ForceDirectories(dest);
CopyFile(PChar(picfolder + file_name ), PChar(dest + file_name ), true );
end;
dataTable1.next;
end;
for i := 0 to file_count-1 do
if not file_ok[i] then
begin // delete wrong file if empty
// ShowMessage('problem: ' + picfolder + active_path[active_class[file_class[i]]] + classes[file_class[i]] + '\' + files[i]);
DeleteFile(picfolder + active_path[active_class[file_class[i]]] + classes[file_class[i]] + '\' + files[i]);
end;
for i := 0 to class_count-1 do
if not class_ok[i] then
begin // delete old class if empty
// ShowMessage('problem: ' + picfolder + active_path[active_class[i]] + classes[i] + '\' );
RemoveDir(picfolder + active_path[active_class[i]] + classes[i] + '\' );
end;
beep;
dataTable.Active := False;
dataTable1.Active := False;
The JOIN is inside the SQL request, not the consuming loop code you are showing here.
You have to use a TQuery instead of two TTable, and write a SQL select for joining the two tables.
You have to modify the SQL request, add the two tables in the FROM clause of the SELECT, and a JOINture in the WHERE clause. See this article about JOIN.
Use a TDataSource and connect its DataSet property to one of the tables. Then set the MasterSource property of the other table to that datasource. Click on the ellipsis button of the MasterFields property and select the connecting fields.
Assuming that the style fields are unique, when you navigate through the first table the second one will follow automatically.

Resources