i have tried looking online but had no luck,
How i could delete all records in an adotable in button click, which match a varying criteria. For example i want to be able to delete all records in an adotable where Labour_ID (this is a field name within the adotable) is equal to DBedit.Text.
sorry this is a bit vague, but suggestions would be appreciated. thanks
You can delete the rows with a simple loop:
while ADOTable1.Locate('Labour_ID', Edit1.Text, []) do
ADOTable1.Delete;
Better yet is to use a TADOQuery instead, and do it with SQL:
ADOQuery1.SQL.Text := 'DELETE FROM YourTable WHERE Labour_ID = :Labour_ID';
ADOQuery1.Params.ParamByName('Labour_ID').AsString := Edit1.Text;
ADOQuery1.ExecSQL;
See the Delphi documentation on TDataSet.Locate for info on the last LocateOptions parameter. (The link is to XE2's docs, but it hasn't changed much (if at all) for ADO since D7).
Related
In Tsql I can execute a stored procedure in Query Analyzer and view the content of a resultset right there query analyzer window without know anything about the query structure (tables, columns, ...)
--Tsql sample
exec myproc parm1, parm2, parm3
Now I am working with PLsql and Toad (which I am relatively new at for Toad). I need to view the content of a resultset of a convoluted stored procedure, and I don't know what the number of columns is -- let alone their data types (this proc is composed of several freaky subqueries -- which I can view individually, but they get pivoted, and the number of columns varies in the final resultset). How can I view the content of this resultset in Toad when I execute the procedure when I don't know how many columns there are or their data types?
Below is code that I have mustered together for viewing the content of a result set of stored procedures where I know how many columns there are and their data types ahead of time. In my code sample below I use a sys_refcursor that I named x_out and I also create a temporary table to store the content of the resultset for additional viewing. Is there a way I can do this when I don't know how many columns there are in the resultset? How to do this with PLsql -- Toad?
create global temporary table tmpResult (fld1 number, fld2 varchar(50), fld3 date);
declare
x_out sys_refcursor;
tmpfld1 number;
tmpfld2 varchar2(50);
tmpfld3 date;
BEGIN
myschema.mypkg.myproc(parm1, parm2, x_out);
LOOP
FETCH x_out INTO tmpfld1, tmpfld2, tmpfld3;
DBMS_OUTPUT.Put_Line ('fld1:-- '||tmpfld1||': fld2:-- '||tmpfld2||': fld3:-- '||tmpfld3);
-- I also insert the result set to a temp table for additional viewing of the data from the stored procedure
Insert Into tmpResult values(tmpfld1, tmpfld2, tmpfld3);
EXIT WHEN x_out%NOTFOUND;
END LOOP;
END;
Toad can automatically retrieve the cursor for you. You have a few options, #3 perhaps is the easiest if you just want to see the data.
If you have the myschema.mypkg loaded in the Editor you can hit F11 to execute it. In the dialog that shows select your package member to the left and select the Output Options tab. Check the option to fetch cursor results or use the DBMS Output options. Click OK and the package executes. Depending on your Toad version you'll see a grid at the bottom of Editor for your results or you'll see a PL/SQL results tab. If you see the latter double click the (CURSOR) value in the output column for your output argument. I suggest using the fetch option as long as your dataset isn't so large that it will cause Out of Memory errors.
Locate your package in the Schema Browser and rt-click, Execute Package. You'll see the same dialog as mentioned in #1. Follow the remaining steps there.
Use a bind variable from an anonymous block. Using your example you'd want something like this...
declare
x_out sys_refcursor;
begin
myschema.mypkg.myproc(parm1, parm2, x_out);
:retval := x_out;
end;
Execute this with F9 in the Editor. In the Bind Variable popup set the datatype of retval to Cursor. Click OK. Your results are then shown in the data grid. Again if your dataset is very large you may run out of memory here.
StackOverflow not letting me post this other solution:
I try posting part of this other solution (if SOF lets me) - this the 2nd half of the other way:
BEGIN
myschema.mypkg.myproc(parm1, parm2, parm3 x_out);
FOR rec_ IN get_columns LOOP
DBMS_OUTPUT.put_line(rec_.name || ': ' || rec_.VALUE);
END LOOP;
END;
and here is the 1st half of the other way:
DECLARE
x_out SYS_REFCURSOR;
CURSOR get_columns IS
...
You should bind the cursor to ":data_grid" in order to show SP result in Toad Data Grid pane.
Call Store Procedure in PL/SQL Script:
Run with F9 not F5
Toad is the best I know when it comes to DB IDE.
Press "F9" and bind it. That is all
I am dynamically adding fields to a TDataSet using the following code:
while not ibSQL.Eof do
fieldname := Trim(ibSql.FieldByName('columnnameofchange').AsString);
TDataSet.FieldDefs.Add(fieldname , ftString, 255);
end
Problem is that I might get duplicate names so what is the easiest way to screen for duplicates and not add the duplicates that are already added.
I hope not to traverse through the TDataSet.FieldDefList for each column added as this would be tedious for every single column addition. And there can be many additions.
Please supply another solution if possible. If not then I am stuck using the FieldDefList iteration.
I will also add that screening out duplicates on the SQL query is an option but not a desired option.
Thanks
TFieldDefs has a method IndexOf that returns -1 when a field with the given name does not exist.
If I understand you correctly, the easiest way would probably be to put all of the existing field names in a TStringList. You could then check for the existence before adding a new field, and if you add it you simply add the name to the list:
var
FldList: TStringList;
i: Integer;
begin
FldList := TStringList.Create;
try
for i := 0 to DataSet.FieldCount - 1 do
FldList.Add(DataSet.Fields[i].FieldName);
while not ibSQL.Eof do
begin
fieldname := Trim(ibSql.FieldByName('columnnameofchange').AsString);
if FldList.IndexOf(fieldName) = -1 then
begin
FldList.Add(fieldName);
DataSet.FieldDefs.Add(fieldname , ftString, 255);
end;
ibSQL.Next;
end;
finally
FldList.Free;
end;
end;
I'm posting this anyway as I finished writing it, but clearly screening on the query was my preference for this problem.
I'm having a bit of trouble understanding what you're aiming for so forgive me if I'm not answering your question. Also, it has been years since I used Delphi regularly so this is definitely not a specific answer.
If you're using the TADOQuery (or whatever TDataSet you're using) in the way I expect my workaround was to do something like:
//SQL
SELECT
a.field1,
a.... ,
a.fieldN,
b.field1 as "AlternateName"
FROM
Table a INNER JOIN Table b
WHERE ...
As which point it automatically used AlternateName instead of field1 (thus the collision where you're forced to work by index or rename the columns.
Obviously if you're opening a table for writing this isn't a great solution. In my experience with Delphi most of the hardship could be stripped out with simple SQL tricks so that you did not need to waste time playing with the fields.
Essentially this is just doing what you're doing at the source instead of the destination and it is a heck of a lot easier to update.
What I'd do is keep a TStringList with Sorted := true and Duplicates := dupError set. For each field, do myStringList.Add(UpperCase(FieldName)); inside a try block, and if it throws an exception, you know it's a duplicate.
TStringList is really an incredibly versatile class. It's always a bit surprising all the uses you can find for it...
I have a table called Artist which currently contains four records and a TSQLQuery that contains the following statement:
SELECT name
FROM Artist
The table Artist also contains the following records:
id name
1 Artist 1
2 Artist 2
3 Artist 3
4 Artist 4
Current method of record retrieval:
Query1.Open;
for i := 0 to qArtist.FieldCount -1 do
with cbArtist.ListBox.ListItems[i] do
Text := qArtist.Fields[i].AsString;
Previous method of record retrieval:
Data bind Query1 to ComboBox1.
With the "previous" method, ComboBox1 would display all the expected records from the Artist table. However, when I try to use "current" method Query1 is only selecting the very first record from the Artist table despite there being three other existing values. I have tried the "current" method across other queries and they also returned only the first value of the table.
The reason I am going for this new approach is because I feel that I am very limited in what I can do if I continue to the "previous" / data bind method, but that is besides the point.
So how can I fix this problem? i.e. the problem of the query only selecting the very first record from the table.
You must use the Eof and Next methods to iterate over the records.
Query1.Open;
while not Query1.eof do
begin
cbArtist.Items.Add(Query1.FieldByName('Artist').AsString);
Query1.Next;
end;
You code show an interaction over fields, if you need iterate all record then you must use a code like:
Query1.Open;
Query1.first;
while not Query1.eof do
begin
with cbArtist.ListBox.ListItems[i] do
Text := qArtist.Fields[1].AsString; //put here field you want to bind on ListBox.
Query1.next;
end;
I don't think you are navigating your query's dataset correctly. The FieldCount and Fields[i] access the field metadata (columns going across), not the rows. I beleive in Delphi you use While not Eof begin... end.
Navigating Datasets
I would consider altering the data binding fields to suit your needs. Delphi's Databinding is very powerful. Manually iterating the dataset just to populate a control will just be extra code where bugs can hide. Utilize the built-in capabilities of the tools and it will be easier to understand and maintain.
Does anybody know (or care to make a suppostion as to) why TSqlDataset has a commandtext property (string) whereas TSqlQuery has a sql property (tstrings)?
Consider the sql statement
select id, name from
table
order by name
If I use a TSqlQuery, then I can change the table name in the query dynamically by accessing sql[1], but if I am using a TSqlDataset (as I have to if I need a bidrectional dataset, the dataset is connected to a provider and thence to a tclientdataset), I have to set the commandtext string literally. Whilst the above example is trivial, it can be a problem when the sql statement is much more involved.
Update:
Judging by the comments and answers so far, it seems that I was misunderstood. I don't care very much for improving the runtime performance of the components (what does one millisecond matter when the query takes one second) but I do care about the programmer (ie me) and the ability to maintain the program. In real life, I have the following query which is stored in a TSqlQuery:
select dockets.id, dockets.opendate, customers.name, statuses.statname,
dockets.totalcost, dockets.whopays, dockets.expected, dockets.urgent,
(dockets.totalcost - dockets.billed) as openbill,
(dockets.totalcost - dockets.paid) as opencost,
location.name as locname, dockets.attention,
statuses.colour, statuses.disporder, statuses.future, dockets.urgcomment
from location, statuses, dockets left join customers
on dockets.customer = customers.id
where dockets.location = location.id
and dockets.status = statuses.id
I haven't counted the number of characters in the string, but I'm sure that there are more than 255, thus precluding storing the query in a simple string. In certain circumstances, I want to filter the amount of data being displayed by adding the line 'and statuses.id = 3' or 'and customers.id = 249'. If the query were stored as TStrings, then I could add to the basic query the dummy line 'and 1 = 1', and then update this line as needed. But the query is one long string and I can't easily access the end of it.
What I am currently doing (in lieu of a better solution) is creating another TSqlDataSet, and setting its commandtext to the default TSqlDataSet's commandtext whilst appending the extra condition.
1) TSQLQuery is rather for compatibility with BDE TQuery. And BDE TQuery has SQL: TStrings property. TSQLDataSet is what supposed to be used for new applications.
2) Although SQL: TStrings is usefull for some tasks, it is also error prone. Often programmers forget to clear SQL property before filling again. Also if your query is a big one, the filling of SQL may lead to performance degradation. Because on each SQL.Add(...) call dbExpress code parses query when ParamCheck is True. That may be solved by using BeginUpdate / EndUpdate or setting ParamCheck to False. But note, setting ParamCheck to False stops automatic parameters creation.
SQLQuery1.SQL.BeginUpdate;
try
SQLQuery1.SQL.Clear;
SQLQuery1.SQL.Add('SELECT * FROM');
SQLQuery1.SQL.Add('Orders');
finally
SQLQuery1.SQL.EndUpdate;
end;
CommandText does not have such issues.
3) You can use Format function for building a dynamic SQL string:
var
sTableName: String;
...
sTableName := 'Orders';
SQLDataSet1.CommandText := Format('select * from %s', [sTableName]);
4) Other data access libraries, like AnyDAC, have macro variables, simplifying dynamic query text building. For example:
ADQuery1.SQL.Text := 'SELECT * FROM &TabName';
ADQuery1.Macros[0].AsRaw := 'Orders';
ADQuery1.Open;
I would have to say that the TSqlQuery uses TStrings (TWideStrings in Delphi 2010) because it is much more flexible.
Suppose your query was:
Select
Item1,
Item2,
Item3,
Item4
FROM MyTable
It's a lot easier to read
You can copy and paste into an external query tool and it stays formatted
It's easy to comment out sections
Select
Item1,
/*
Item2,
Item3,
*/
Item4
FROM MyTable
You can easily add items
Select
Item1,
Item2,
Item2a,
Item2b,
Item3,
Item3a,
Item3b,
Item4
FROM MyTable
Try doing that to a contiguous set of characters that goes on forever in one long line with no line breaks inside an edit window that is always to small for viewing that doesn't allow for wrapped text etc. etc. etc.
Just $0.02.
Delphi does not seem to like multi-field indexes.
How do I physically sort a a table so that I wind up with a table that has the rows in the desired order?
Example:
mytable.dbf
Field Field-Name Field-Type Size
0 Payer Character 35
1 Payee Character 35
2 PayDate Date
3 Amount Currency
I need to produce a table sorted alphabetically by "Payee"+"Payer"
When I tried using an index of "Payee+Payer", I got an error:
"Field Index out of range"
The index field names need to be separated by semicolons, not plus symbols. Try that and it should work.
Ok, let's try to put some order.
First, isn't advisable to physically sort a table. In fact the most RDBMS even don't provide you this feature. Usually, one, in order to not force a full table scan (it is called sometimes natural scan) creates indexes on the table fields on which he thinks that the table will be sorted / searched.
As you see, the first step in order to sort a table is usually index creation. This is a separate step, it is done once, usually at, let's say, "design time". After this, the DB engine will take care to automatically update the indexes.
The index creation is done by you (the developer) using (usually) not Delphi (or any other development tool) but the admin tool of your RDBMS (the same tool which you used when you created your table).
If your 'DB engine' is, in fact, a Delphi memory dataset (TClientDataSet) then you will go to IndexDefs property, open it, add a new index and set the properties there accordingly. The interesting property in our discussion is Fields. Set it to Payee;Payer. Set also the Name to eg. "idxPayee". If you use other TDataSet descendant, consult the docs of your DB engine or ask another question here on SO.com providing the details.
Now, to use the index. (IOW, to sort the table, as you say). In your program (either at design time either at run time) set in your 'Table' the IndexName to "idxPayee" or any other valid name you gave or set IndexFieldNames to Payee;Payer.
Note once again that the above is an example based on TClientDataSet. What you must retain from the above (if you don't use it) is that you must have an already created index in order to use it.
Also, to answer at your question, yes, there are some 'table' types (TDataSet descendants in Delphi terminology) which support sorting, either via a Sort method (or the like) either via a SortFields property.
But nowadays usually when one works with a SQL backend, the preferred solution is to create the indexes using the corresponding admin tool and then issue (using Delphi) an SELECT * FROM myTable ORDER BY Field1.
HTH
If you're still using BDE you can use the BDE API to physically sort the DBF table:
uses
DbiProcs, DbiTypes, DBIErrs;
procedure SortTable(Table: TTable; const FieldNums: array of Word; CaseInsensitive: Boolean = False; Descending: Boolean = False);
var
DBHandle: hDBIDb;
RecordCount: Integer;
Order: SORTOrder;
begin
if Length(FieldNums) = 0 then
Exit;
Table.Open;
RecordCount := Table.RecordCount;
if RecordCount = 0 then
Exit;
DBHandle := Table.DBHandle;
Table.Close;
if Descending then
Order := sortDESCEND
else
Order := sortASCEND;
Check(DbiSortTable(DBHandle, PAnsiChar(Table.TableName), nil, nil, nil, nil, nil,
Length(FieldNums), #FieldNums[0], #CaseInsensitive, #Order, nil, False, nil, RecordCount));
end;
for example, in your case:
SortTable(Table1, [2, 1]); // sort by Payee, Payer
Cannot check, but try IndexFieldNames = "Payee, Payer".
Sure indexes by these 2 fields should exist.
You can create an index on your table using the TTable.AddIndex method in one call. That will sort your data when you read it, that is if you use the new index by setting the TTable.IndexName property to the new index. Here's an example:
xTable.AddIndex('NewIndex','Field1;Field2',[ixCaseInsensitive]);
xTable.IndexName := 'NewIndex';
// Read the table from top to bottom
xTable.First;
while not xTable.EOF do begin
..
xTable.Next;
end;