Filling DBGrid dynamically with related DataSource and DataSet (Delphi) - database

In my project i have 1 database with 20 DataSource & DataSet.
I would like to capture the customer name with "BGrid1.SelectedField.AsString;" and open another DbGrid2 with data (datasource and dataset) referring to the selected customer.
In sample below is working, but i have no idea how to do changing dynamically.
procedure TMain.DBGrid1DblClick(Sender: TObject);
begin
Form2.Caption := DBGrid1.SelectedField.AsString;
if DBGrid1.DataSource.DataSet.RecNo = 1 then
begin
DBGrid2.DataSource := Form2.DtSource05;
DBGrid2.DataSource.DataSet := Form2.ADO01;
Form2.Show;
end;
Can someone help me please?
Thanks in advance.

Related

Reading DateTime from Database using Delphi and FireDAC

I'm failing dismally to read a datetime from an SQLite database using Delphi 10.3 and FireDAC.
As the simplest example, I create a sample database using sqlite as follows:
.open Test.db
CREATE TABLE "TABLE1" ("Name"VarChar(16), "Time" datetime);
INSERT INTO Table1 (Name,Time) VALUES("Fred",time('now'));
Then
select * from Table1
gives Fred|16:52:57 as expected.
If I generate a Delphi program with an FDConnection1 and FDQuery1 linked to a datasource and DBgrid it will read "Fred" but not the time. The value returned by FDquery1 asstring is '' and asfloat is 0.
If I try the FireDAC explorer tool to look at the database it also fails to read the time value but I notice it does read datetimes from some of the example databases so it clearly can work.
Can anyone tell me what I'm missing.
Thanks
Trying to construct a SQL statement as a string in Delphi code can be a bit error prone. However, you should find that the following code executes correctly
procedure TForm2.btnInsertRowClick(Sender: TObject);
const
sInsertRow = ' INSERT INTO Table1 (Name,time) VALUES(''Fred'',datetime(''now''))';
begin
FDConnection1.ExecSql(sInsertRow);
end;
Btw, in general it would be better to use a parameterised INSERT statement than a literal one like the above, but it's not really practical here as you are inserting the now value returned by Sqlite rather than the Delphi now
function.
**Update: ** The code below is from a minimal project which creates your example table,
inserts a row into it and then presents it in db-aware controls (DBGrid, DBEdit) for editing.
It all works exactly as it ought to. In particular, any changes to the row data made through
those controls are retained when tha app is next run. Note that the SQL which creates the table specifies a primary key on the Name column/field: this is necessary for the FDQuery1 to generate the UPDATE statements needed to save changes back to the on-disk table.
The code in the btnSelectClick handler shows how to set the time field's DisplayFormat and EditMask properties so that it only displays the time part of the stored DateTime data.
type
TForm2 = class(TForm)
FDConnection1: TFDConnection;
FDQuery1: TFDQuery;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
DataSource1: TDataSource;
DBEdit1: TDBEdit;
btnCreateTable: TButton;
btnInsertRow: TButton;
btnSelect: TButton;
[...]
public
[...]
const
sCreateTable = 'CREATE TABLE ''TABLE1'' (''Name'' VarChar(16) primary key, ''Time'' datetime)';
sInsertRow = ' INSERT INTO Table1 (Name,time) VALUES(''Fred'',datetime(''now''))';
sSelect = 'select * from table1';
procedure TForm2.btnCreateTableClick(Sender: TObject);
begin
FDConnection1.Connected := True;
FDConnection1.ExecSql(sCreateTable);
end;
procedure TForm2.btnInsertRowClick(Sender: TObject);
const
begin
FDConnection1.ExecSql(sInsertRow);
end;
procedure TForm2.btnSelectClick(Sender: TObject);
var
AField : TDateTimeField;
begin
FDQuery1.SQL.Text := sSelect;
FDQuery1.Open;
// The following shows how to control how the time field is formatted for display
// in gui controls using the field's DisplayFormat
// and how to set up its EditMask for editing
AField := FDQuery1.FieldByName('time') as TDateTimeField;
AField.DisplayFormat := 'hh:nn:ss';
AField.EditMask := '!90:00:00;1;_';
end;
end.
.DFM file
object Form2: TForm2
[...]
object FDConnection1: TFDConnection
Params.Strings = (
'Database=D:\Delphi\Code\FireDAC\db1.sqlite'
'DriverID=SQLite')
LoginPrompt = False
end
object FDQuery1: TFDQuery
Connection = FDConnection1
end
object DataSource1: TDataSource
DataSet = FDQuery1
end
object DBGrid1: TDBGrid
DataSource = DataSource1
end
object DBNavigator1: TDBNavigator
DataSource = DataSource1
end
object DBEdit1: TDBEdit
DataField = 'Time'
DataSource = DataSource1
end
end

delphi DBGrid cell not empty

In my form, i have a TDBGRid, TDatasource , MessageTable and 2 button. I have a button to add new row in my DBGRID :
MessageTable.Append;
MessageTable.Edit;
MessageTable.FieldByName('FieldName').AsString := sName;
MessageTable.Post;
The second button is used to delete a current row :
MessageTable.Edit ;
MessageTable.Delete ;
How can ensure all Cell not empty before the post?
If there is an empty Cell, i need to ignore this row !
how can I do that?
You should use the features the dataset (in this case, TTable) already give you instead of trying to reinvent the wheel. TDataSet provides an event (OnBeforePost) that is specifically designed for this purpose.
Click on your TTable, and then switch to the Events tab in the Object Inspector. Find the OnBeforePost event and double click it to generate the event shell in the Code Editor. You'll see something like this:
procedure TForm1.Table1BeforePost(DataSet: TDataSet);
begin
// DataSet is the TDataSet (TTable, TQuery, TADOQuery, etc.) to which
// event is attached
end;
You can do all of your validations needed before a record is actually written to the database here. For instance, if you want to make sure every single field has something in it, you can do this:
procedure TForm1.Table1BeforePost(DataSet: TDataSet);
var
i: Integer;
begin
// You can replace DataSet with your actual table variable name, but using it
// this way allows you to use this same event for more than one table if you want.
for i := 0 to DataSet.FieldCount - 1 do
if DataSet.Fields[i].IsNull then
raise Exception.CreateFmt('Field %s has no value', DataSet.Fields[i].FieldName);
end;
If you want to make sure only certain fields have values, or that the value is within a certain range, you can access the field directly:
procedure TForm1.Table1BeforePost(DataSet: TDataSet);
begin
if DataSet.FieldByName('MyField').IsNull then
Abort; // This silently cancels the post without telling the user
if DataSet.FieldByName('AField').AsInteger = 0 then
raise Exception.Create('AField must not be 0');
end;
Now you don't have to do anything at all in your TDBGrid. If the user hits DownArrow on the last row and a new row is inserted, and they enter incomplete or wrong data, the events above will take care of it. They'll also work if you use two buttons (one to insert or edit and the other to post), because the events will handle everything else.
procedure TForm1.ButtonInsertClick(Sender: TObject);
begin
Table1.Insert; // Or Append - if you have an index on the table they're the same thing
end;
procedure TForm1.ButtonPostClick(Sender: TObject);
begin
Table1.Post; // This is 100% of the code you need here
end;

Delphi Database Connection Using ACCESS and ADO connections

Okay so basically I've been working on my computing project for a while now and I've got 90% of it working however I'm having a problem with Delphi where is says that my database is not connected/ there is a problem connecting however I've already tried writing the information to the screen and this showed me that the items I was looking to pick up where in fact being picked up so the failure is when the items are being input in to the database. This however shouldn't be happening as the System already has database information displayed from that table and the user can physically select things from the database tables within the program however when trying to store the information back into the database it just breaks. Me and my computing teacher can not work it out, any help would be appreciated.
The problem appears on the new orders page. If you'd rather look at the system then you can download it from here https://drive.google.com/folderview?id=0B_iRfwwM9QpHVXJnSkx4U1FjMlk&usp=sharing
procedure Tform1.btnSaveClick(Sender: TObject);
var orderID:integer;
count:integer;
begin
try
//save into the order table first
tblOrder.Open;
tblOrder.Insert;
tblOrder.FieldByName('CustomerID').value:= strtoint(cboCustomer.Text);
tblOrder.Close;
tblOrder.Open;
tblOrder.Last;
orderID:=tblOrder.FieldByName('OrderID').Value;
showmessage(inttostr(orderID));
for count := 1 to nextFree-1 do
begin
if itemOrdered[count,1]<>0 then
begin
tblOrderLine.Open;
tblOrderLine.AppendRecord([orderID, itemOrdered[count,1],itemOrdered[count,2]]);
end;
end;
showmessage('The order has been saved');
except
showmessage('There was a problem connecting to the database');
end;
end;
You're doing far too much open, do something, close, open. Don't do that, because it's almost certain that is the cause of your problem. If the data is already being displayed, the database is open already. If you want it to keep being displayed, the database has to remain open.
I also removed your try..except. You can put it back in if you'd like; I personally like to allow the exception to occur so that I can find out why the database operation failed from the exception message, rather than hide it and have no clue what caused it not to work.
procedure Tform1.btnSaveClick(Sender: TObject);
var
orderID: integer;
count: integer;
begin
//save into the order table first
tblOrder.Insert;
tblOrder.FieldByName('CustomerID').value:= strtoint(cboCustomer.Text);
tblOrder.Post;
orderID:=tblOrder.FieldByName('OrderID').Value;
showmessage(inttostr(orderID));
for count := 1 to nextFree-1 do
begin
if itemOrdered[count, 1] <> 0 then
begin
tblOrderLine.AppendRecord([orderID, itemOrdered[count,1],itemOrdered[count,2]]);
tblOrderLine.Post;
end;
end;
showmessage('The order has been saved');
end;

How to change Clientdataset field datatype at runtime

For Delphi ClientDataSets where fields have been define at design time, is there a way at runtime to change a specific field's datatype ( change the cds.Fields[n].DataType) ?
I have a legacy Delphi 7 program with both SQLDataSet and ClientDataSet fields set at design time (in order to override various properties).
These are connected to a 3rd-party Sybase SQL Anywhere 11 database.
Recently the vendor changed all 'Description' fields from VarChar(128) to long varchar, but only for certain of his customers. So, my code has to support both types of fields when I query on these 'Description' fields.
I was hoping to set conditional compilation on the class field types (then add the fields before opening the SQL/CLient Dataset), but the compiler ignores {$IFDEF } conditionals in the component definition section of the class (which, the more I think about it, makes good sense)!
There are dozens of modules, with hundreds of fields affected, so any insight is appreciated.
I have also faced this problem before, not with CDS, but with TADODataSet using persistent fields at design time. I think that the code below will help you get the idea of how to fix/patch your CDS datasets.
The idea is to query the relevant table schema; get the actual fileds data type; and "change" the persistent field type by un-attaching it from the DataSet and adding a new matching persistent filed instead:
// TData class
procedure TData.DataModuleCreate(Sender: TObject);
var
I: Integer;
begin
for I := 0 to ComponentCount - 1 do
if (Components[I] is TCustomADODataSet) then
DataSetPrepareMemoFields(TDataSet(Components[I]));
end;
procedure TData.DataSetPrepareMemoFields(DataSet: TDataSet);
var
Fld: TField;
I: Integer;
FldName, CompName: string;
AOwner: TComponent;
begin
// Here you need to query the actual table schema from the database
// e.g. ADOConnection.GetFieldNames and act accordingly
// check which DataSet you need to change
// if (DataSet = dsOrders) or ... then...
if DataSet.FieldList.Count > 0 then
for I := DataSet.FieldList.Count - 1 downto 0 do
begin
if DataSet.FieldList.Fields[I].ClassNameIs('TMemoField') and (DataSet.FieldList.Fields[I].FieldKind = fkData) then
begin
// save TMemoField properties
AOwner := DataSet.FieldList[I].Owner;
CompName := DataSet.FieldList[I].Name;
FldName := DataSet.FieldList.Fields[I].FieldName;
// dispose of TMemoField
DataSet.FieldList[I].DataSet := nil; // Un-Attach it from the DataSet
// create TWideADOMemoField instead
Fld := TWideADOMemoField.Create(AOwner); // Create new persistent Filed instead
Fld.Name := CompName + '_W';
Fld.FieldName := FldName;
Fld.DataSet := DataSet;
end;
end;
end;
That said, After I have fixed that issue, I have never ever used persistent fields again.
All my fields are generated in run time. including calculated/lookup/internal fields.

How to make ADO parameter to update SQL Server datetime column?

edit I should have mentioned that I'm trying to do this with Delphi 2006.
OK, I think I have hit on a question with no previous answers.
I have a SQL Server database with columns of type datetime. When I try to insert a row with a parametrized command, I get
Exception class EOleException with message
'[Microsoft][ODBC SQL Server Driver]Optional feature not implemented'.
My insert procedure looks like this:
procedure TForm1.btnZoomClick(Sender: TObject);
const
InsCmd = 'insert into dbo.foo (added, edited, editor, narrative) ' +
'values (:dateAdded, :dateEdited, :theEditor, :theNarrative);';
begin
dmDbToy2.DataModule2.ADOCommand1.CommandText := InsCmd;
with DataModule2.ADOCommand1.Parameters do
begin
// the following line was an attempt to trick VarAsType into making a
// adDbTimeStamp: VarAsType is having none of it.
// FindParam('dateAdded').Value := VarAsType(VarFromDateTime(Now), 135);
FindParam('dateAdded').Value := VarFromDateTime(Now);
FindParam('dateEdited').Value := Null;
FindParam('theEditor').Value := 'wades';
FindParam('theNarrative').Value := Null;
end;
DataModule2.ADOCommand1.Execute;
end;
I found some postings via google which seem to indicate that SQL Server wants a adDbTimeStamp type to update these columns, but VarAsType does not want to make one for me.
Is there a way to create a value for the dateAdded and dateEdited parameters in the code sample?
In the comments thread on the original question, user RRUZ made a suggestion that turned out to resolve the issue: The problem was with the provider. Namely, I was using the OLEDB Provider for ODBC rather than the OLEDB Provider for SQL Server. Changing the provider as suggested made the 'Optional feature not implemented' error message go away and enabled the insert to work with a simple assignment of TDateTime to TParameter.Value, thusly:
FindParam('dateAdded').Value := Now;
Set the datatype for the parameter, it might do a difference in how the parameters is treated. I would also recommend that you use ParamByName instead of FindParam. With ParamByName you get a Param xx not found exception if the parameters does not exist in the Parameters collection. FindParam returns nil if it is not found. I have never needed to use any variant conversion stuff when assigning parameters for a TADOCommand so think you should remove that as well.
Try this.
with ParamByName('dateAdded') do
begin
DataType := ftDateTime;
Value := Now;
end;
with ParamByName('dateEdited') do
begin
DataType := ftDateTime;
Value := Null;
end;
with ParamByName('theEditor') do
begin
DataType := ftString; // or ftWideString if you use nchar/nvarchar
Value := 'wades';
end;
with ParamByName('theNarrative') do
begin
//DataType := ftString // Don't know datatype here
Value := Null;
end;
Just set the parameter as a datetime. I do it all the time in ADO and other conection layers
DataModule2.ADOCommand1.Parameters.ParamByName('dateAdded').Value := Now();
//other code
DataModule2.ADOCommand1.Parameters.ParamByName('dateEdited').Value := Null;
//other code
DataModule2.AdoCommand1.Execute;

Resources