i'm learning to use firemonkey, and i made an app that i'm using on my phone (it works like a reminder if u want) so basically there's a database file (SQLite) in my phone's internal storage, and in the form i put a TMemo + FDConnection + FDPhysSQLiteDriverLink so the app can read the database and display it on the TMemo, now i did put a TEdit and i added a button i want what i write on the TEdit to be added to the database.
i'm new to SQL delphi and stack overflow in general.
thanks
PS: i'm trying to do it without using livebindings.
var
query: TFDQuery;
begin
query := TFDQuery.Create(nil);
try
// Define the SQL Query
query.Connection := FDConnection1;
query.SQL.Text := 'SELECT * FROM Employee';
query.Open();
outputMemo.Text := '';
// Add the field names from the table.
outputMemo.Lines.Add(String.Format('|%8s|%-25s|%-25s|', [' ID ', ' NAME >',
' DEPARTMENT ']));
// Add one line to the memo for each record in the table.
while not query.Eof do
begin
outputMemo.Lines.Add(String.Format('|%8d|%-25s|%-25s|',
[query.FieldByName('ID').AsInteger, >query.FieldByName('Name').AsString,
query.FieldByName('Department').AsString]));
query.Next;
end;
finally
query.Close;
query.DisposeOf;
end;
end;
this is the code to display the database, i want to know if there's a way to write on the memo or a TEdit and press a button to change back the database, i'm wondering if it works both ways.
Related
Help me to fix this problem to delete records with TFDQuery.
When this value of record is choosed by me with Edit.Text or DBEdit.Text, I try like this but it is not working:
FDQuery.SQL.Text := 'delete * FROM MyTable column_name =:KEY1 ';
FDQuery.ParamByName('KEY1').AsString = 'dbedit.text';
FDQuery.Open;
fdquery.SQL.Text := 'DELETE FROM MyTable WHERE column_name = :KEY1';
fdquery.ParamByName('KEY1').AsString := dbedit.Text;
fdquery.Execute();
You could also use TFDCommand rather than TFDQuery as you are not expecting to read the result:
fdcommand.CommandText := 'DELETE FROM MyTable WHERE column_name = :KEY1';
fdcommand.ParamByName('KEY1').AsString := dbedit.Text;
fdcommand.Execute();
If this is a command you expect to re-use you could put the SQL statement into the command at design time, with the parameter name, and then at run time you would only need to do:
fdcommand.ParamByName('KEY1').AsString := dbedit.Text;
fdcommand.Execute();
Depending on the underlying database you are using have commands pre-populated can allow the query to be prepared in advance. For complex queries (unlike this one) this means that the execution plan is built only once.
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;
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;
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.
I have a view in my database that has a bunch of fields derived from other information in the database, this is how the view is defined:
create view patient_account_view AS
select patient.p_mrn,
p_fname,
p_lname,
ammount_paid,
quantity*item_cost + repeats*item_cost "ammount_owing",
(quantity*item_cost + repeats*item_cost) - ammount_paid "balance"
from patient_account,
patient,
diagnosis,
prescribed_treatment,
items_used,
item,
perscription
where patient.p_mrn = diagnosis.p_mrn AND
patient_account.p_mrn = patient.p_mrn AND
diagnosis.prescribed_treatment_id = prescribed_treatment.prescribed_treatment_id AND
prescribed_treatment.prescribed_treatment_id = perscription.prescribed_treatment_id AND
items_used.ptreatment_id = prescribed_treatment.prescribed_treatment_id AND
items_used.item_number = item.item_number;
I would like to use pl/sql to access the information in the view to stick it into a form, but I'm getting a 'bad bind variable' error. How do I access this kind of attribute without having to recalculate the information stored there?
Here is the plsql that is problematic:
DECLARE
pmrn patient.p_mrn%TYPE;
var_ptuple patient%ROWTYPE;
var_accttuple patient_account%ROWTYPE;
BEGIN
pmrn := :PATIENT_BLOCK.MRN_FIELD;
SELECT * INTO var_ptuple from patient WHERE patient.p_mrn = pmrn;
SELECT * INTO var_accttuple from patient_account_view WHERE patient_account_view.p_mrn = pmrn;
:PATIENT_BLOCK.FNAME := var_ptuple.p_fname;
:PATIENT_BLOCK.LNAME := var_ptuple.p_lname;
:PATIENT_BLOCK.BALACNCE_OWING := var_accttuple.balance;
END;
The columns of your view patient_account_view do not match exactly the columns of the table patient_account, but in your code you have:
var_accttuple patient_account%ROWTYPE;
which means when you run this:
SELECT * INTO var_accttuple from patient_account_view ...
You haven't specified which columns get mapped to which record attributes, so Oracle requires that the column list matches exactly.
In this case, I'd expect you probably want to change the definition of the variable, e.g.
var_accttuple patient_account_view%ROWTYPE;
Side note
Since you're only using one attribute from the view, you can simplify your code as follows:
SELECT balance INTO :PATIENT_BLOCK.BALACNCE_OWING
from patient_account_view WHERE patient_account_view.p_mrn = pmrn;
and you no longer need var_accttuple.