I am using unidac components in a Delphi 7 project to connect to a SQLite database.
Connecting and quering works fine, except for calculated fields.
My query is this :
select c.CardID,
c.FirstName,
c.SurName,
c.Street,
c.City,
c.PostCode,
c.Points,
(select count(1) from FullCard f where f.CardID = c.CardID and f.Paid = 1) as PaidCards,
(select count(1) from FullCard f where f.CardID = c.CardID and f.Paid = 0) as OpenCards,
(select count(1) from FullCard f where f.CardID = c.CardID) as FullCards
from Card c
This query returns a correct resultset when I run it in SQLiteStudio, but when I run it in delphi the Calculated fields are all empty.
The unidac version is 5.0.1 for Delphi 7.
I have a UniConnection component and a UniQuery component. The connection properties seem correct since I can connect and query from the database.
The UniQuery component has the SQL property filled with the above query, and all fields are made persistent.
When I do UniQuery1.Open a DBGrid fills up with all records, but the fields PaidCards, OpenCards and FullCards are all empty.
The same query does returns these fields properly filled when executing it in SQLiteStudio so I guess there is nothing wrong with the query itself.
I am hoping someone else has encountered the same problem and can point me to a solution for this problem.
The workaround for this bug is not using persistent fields.
When I don't create persistent fields than all fields are filled properly and everything works perfect.
Only downfall is I have to use query1.FieldByName('FirstName').asString in stead of query1FirstName.asString in my code.
Related
I have a select that return a single column, using as where clause 4 columns, and I created a specific index for this query. When I do this select using Dbeaver, the result return in 30~50 milliseconds.
SELECT
column1
FROM
myTable
WHERE
column2 = a
AND column3 = b
AND (column4 = c AND column5 = d )
FOR READ ONLY WITH UR;
Now I created a procedure with this same simple select. The proc declare a P1, a 'cursor with return', do the select, open the cursor and close the P1. When I use the same dbeaver connection the result is returning between 1.2 ~ 1.6 seconds.
CREATE PROCEDURE myProc (
IN a DECIMAL(10),
IN b DECIMAL(10),
IN c DECIMAL(10),
IN d DECIMAL(10)
)
LANGUAGE SQL
DYNAMIC RESULT SETS 1
SPECIFIC myProc
P1: BEGIN
DECLARE cursor1 CURSOR WITH RETURN for
SELECT
column1
FROM
myTable
WHERE
column2 = a
AND column3 = b
AND (column4 = c AND column5 = d )
FOR READ ONLY WITH UR;
OPEN cursor1;
END P1
Is this huge return difference correct? If wrong, Is there something wrong in my procedure that justifies this return time difference? Or could be something wrong in the DB configuration or the server that justifies this difference, something like few resources in the DB server or something like the proc not using the index( I don't know if procs in the DB2 use the index by default like the queries )?
I am new in DB2 and new in procedures creation. Thank you in advance.
Best regards,
Luis
I don't know if is the best way, but I solved the problem using a solution that I read for SQL Server. The solution is create local variables that will receive the parameters values, and use the variable in the queries, to guarantee always the best execution plan. I didn't knew if this was valid to DB2, but my proc now has almost the same response time compared to query. Worked!
Link of SQL Server post: SQL Server: Query fast, but slow from procedure
In this link above a user called #Jake give this explanation:
"The reason this happens is because the procedures query plan is being cached, along with the parameters that were passed to it. On subsequent calls, this query plan generated will be reused with new parameters. This can cause problems because if the data is unevenly distributed, one parameter can generate a sub-optimal plan vs. another. Using local variables essentially does the same as OPTIMIZE FOR UNKNOWN because local variables cannot be sniffed."
I think that is the same for DB2, because worked. After I change these old procedures to use local variables my execution plan begun to use the indexes recently created
I'm having a problem with a synchronisation issue... I have a source table (mtAllowanceCategory) which I want to update to a copy (qryAllowanceCategory) of it. To make sure records in the copy are deleted if they are no longer present in the source, the copy has a "StillHere" boolean field, which is set to on when the record is added or updated and otherwise stays off. Afterwards, all records with StillHere=false are deleted.
That's the idea, anyway... in practice, the flag fields isn't turned on when posting updates. When I trace the code, the statement is executed; when I look in Access, it stays off. Hence the delete SQL afterwards clears the entire table.
Been trying to figure this for hours now; what am I missing??
mtAllowanceCategory:TFDMemTable (filled from an API call, this works fine)
qryAllowanceCategory:TFDQuery
conn:TFDConnection to a local Access database (also used for qryAllowanceCategory)
conn.ExecSQL('UPDATE AllowanceCategory SET StillHere=false;');
while not mtAllowanceCategory.eof do
begin
if qryAllowanceCategory.locate('WLPid',mtAllowanceCategory.FieldByName('Id').AsString,[loCaseInsensitive]) then
begin
Updating:=true;
qryAllowanceCategory.Edit;
end
else
begin
Updating:=false;
qryAllowanceCategory.Insert;
end;
qryAllowanceCategory.fieldbyname('createdBy').AsString:=mtAllowanceCategory.FieldByName('createdBy').AsString;
qryAllowanceCategory.fieldbyname('createdOn').AsString:=mtAllowanceCategory.FieldByName('createdOn').AsString;
qryAllowanceCategory.fieldbyname('description').AsString:=mtAllowanceCategory.FieldByName('description').AsString;
qryAllowanceCategory.fieldbyname('WLPid').AsString:=mtAllowanceCategory.FieldByName('id').AsString;
qryAllowanceCategory.fieldbyname('isDeleted').Asboolean:=mtAllowanceCategory.FieldByName('isDeleted').Asboolean;
qryAllowanceCategory.fieldbyname('isInUse').Asboolean:=mtAllowanceCategory.FieldByName('isInUse').Asboolean;
qryAllowanceCategory.fieldbyname('modifiedBy').AsString:=mtAllowanceCategory.FieldByName('modifiedBy').AsString;
qryAllowanceCategory.fieldbyname('modifiedOn').AsString:=mtAllowanceCategory.FieldByName('modifiedOn').AsString;
qryAllowanceCategory.fieldbyname('WLPname').AsString:=mtAllowanceCategory.FieldByName('name').AsString;
qryAllowanceCategory.fieldbyname('number').AsInteger:=mtAllowanceCategory.FieldByName('number').AsInteger;
qryAllowanceCategory.fieldbyname('percentage').AsFloat:=mtAllowanceCategory.FieldByName('number').AsFloat;
qryAllowanceCategory.fieldbyname('remark').AsString:=mtAllowanceCategory.FieldByName('remark').AsString;
qryAllowanceCategory.fieldbyname('LocalEdited').AsBoolean:=false;
qryAllowanceCategory.fieldbyname('LocalInserted').AsBoolean:=false;
qryAllowanceCategory.fieldbyname('LocalDeleted').AsBoolean:=false;
qryAllowanceCategory.fieldbyname('StillHere').AsBoolean:=true;
qryAllowanceCategory.Post;
mtAllowanceCategory.next;
end;
conn.commit;
conn.ExecSQL('DELETE FROM AllowanceCategory WHERE StillHere=false;');
When I read your q, I was struck by two thoughts:
One was that I couldn't immediately
see the cause of your problem and the other that you could probably avoid the problem anyway
if you used Sql rather than table traversals in code.
It seemed to me that you might be able to do most
if not all of what you need, in terms of synchronising the two tables, using Access
Sql rather than traversing the qryAllowanceCategory table using a while not EOF loop.
(btw, in the following I'm going to use 'mtAC' and qryAC to reduce typing & typos)
Using Access SQL
Initially, I did not have much luck, as Access rejected my attempts to
refer to both tables in an Update statement against the qryAC one using a Join
or Outer Join, but then I came across a reference that showed that Access does
support an Inner Join syntax. These SQL statements execute successfully by calling
ExecSQL on the FireDAC connection to the database:
update qryAC set qryAC.StillHere = True
where exists(select mtAC.* from mtAC inner join qryAC on mtAC.WLPid = qryAC.WLPid)
and
update qryAC inner join mtAC on mtAC.WLPid = qryAC.WLPid set qryAC.AValue = mtAC.AValue
This first of these obviously provides a way to update the StillHere field to set it to True,
or False with a trivial modification.
The second shows a way to update a set of fields in qryAC from the matching rows in mtAC
and this could, of course, be limited to a subset of rows with a suitable Where clause.
Access Sql also supports checking whether a row in one table exists in the other, as in
select * from qryAC q where exists (select * from mtac m where q.wlpid = m.wlpid)
and for deleting rows in one table which do not exist in the other
delete from qryAC q where not exists (select * from mtac m where q.wlpid = m.wlpid)
Using FireDAC's LocalSQL
I also mentioned LocalSQL in a comment. This supports a far broader range
of Sql statements that native Access Sql and can operate on any TDataSet descendant,
so if you find something that Access Sql syntax doesn't support, it is worth considering
using LocalSQL instead. Its main downside is that it operates on the datasets using
traversals, so in not quite as "instant" as native Sql. It can be a bit tricky to set up,
so here are the settings from the DFM which show how the components need connecting up. You would use it by feeding what you want to FDQuery1.
object AccessConnection: TFDConnection
Params.Strings = (
'Database=D:\Delphi\Code\FireDAC\LocalSQL\Allowance.accdb'
'DriverID=MSAcc')
Connected = True
LoginPrompt = False
end
object mtAC: TFDQuery
AfterOpen = mtACAfterOpen
Connection = AccessConnection
SQL.Strings = (
'select * from mtAC')
end
object qryAC: TFDQuery
Connection = AccessConnection
end
object LocalSqlConnection: TFDConnection
Params.Strings = (
'DriverID=SQLite')
Connected = True
LoginPrompt = False
end
object FDLocalSQL1: TFDLocalSQL
Connection = LocalSqlConnection
DataSets = <
item
DataSet = mtAC
end
item
DataSet = qryAC
end>
end
object FDGUIxWaitCursor1: TFDGUIxWaitCursor
Provider = 'Forms'
end
object FDPhysSQLiteDriverLink1: TFDPhysSQLiteDriverLink
end
object FDQuery1: TFDQuery
Connection = LocalSqlConnection
end
If anyone is interested:
The problem was in not refreshing qryAllowanceCategory after the initial SQL setting StillHere to false. The memory version (qryAllowanceCategory) of the record didn't get that update, so according to him, the flag was still on; after the field updates it appeared there were no changes (all the other fields were unchanged as well) so the post was ignored. In the actual table it was off though, so the final delete SQL removed it.
The problem was solved by adding a refresh after the first UPDATE SQL statement.
I want to update whole table from on query. Following is my update functionality is happened:
Database (Database A) stored in the iDevice
Temperory Database (Batabase B) downloads to the device and store in the temp folder inside the device. (Both DB has same database structure)
First I attach temp db to device database. Attached db name is SECOND1
Then I insert new records from temp db to device folder from following Insert code. It is working fine.
INSERT INTO main.fList SELECT * FROM SECOND1.fList WHERE not exists (SELECT 1 FROM main.fList WHERE main.fList.GUID = SECOND1.fList.GUID)
But when I use following code to update it is not working fine. It update same top value for all device db table's rows.
UPDATE fList SET Notes = (SELECT SECOND1.fList.Notes FROM SECOND1.fList WHERE SECOND1.fList.GUID = fDefectList.GUID) WHERE EXISTS (SELECT * FROM SECOND1.fList WHERE SECOND1.fList.GUID = fList.GUID
I found SQL query for bulk update. Following is the code,
UPDATE fTempRank7
SET
fTempRank7.int_key = fRank7.int_key,
fTempRank7.int_rank6 = fRank7.int_rank6,
fTempRank7.title = fRank7.title,
fTempRank7.sequence = fRank7.sequence,
fTempRank7.lastupdated = fRank7.lastupdated
FROM
fTempRank7 INNER JOIN fRank7 ON
fTempRank7.int_key = fRank7.int_key
But in sqlite this code does not work.
Anyone knows bulk update in sqlite?
SQLite does not support joins in an UPDATE statement.
When you use a table name without a database name, it refers to the first matching table in the innermost query. In other words, if you use SECOND1.fList in a subquery, any other occurrence of fList refers to the table in SECOND1.
To ensure that you always refer to the correct table, use the database name in all table references.
The main database is always named main, so all table references should be either main.fList or SECOND1.fList.
Anyway, if you are updating all columns, you can simplify the update by deleting the rows that would be updated, so that all new data can just be inserted:
BEGIN;
DELETE FROM fList WHERE GUID IN (SELECT GUID FROM SECOND1.fList);
INSERT INTO fList SELECT * FROM SECOND1.fList;
COMMIT;
When you have a UNIQUE constraint on the GUID column, this can be simplified into a single statement:
INSERT OR REPLACE INTO fList SELECT * FROM SECOND1.fList;
And here I don't use main. because I know what I'm doing. ☺
I'm using Zeos 7, and Delphi 2009 and want to check to see if a value is already in the database under a specific field before I post the data to the database.
Example: Field Keyword
Values of Cheese, Mouse, Trap
tblkeywordKEYWORD.Value = Cheese
What is wrong with the following? And is there a better way?
zQueryKeyword.SQL.Add('IF NOT EXISTS(Select KEYWORD from KEYWORDLIST ='''+
tblkeywordKEYWORD.Value+''')INSERT into KEYWORDLIST(KEYWORD) VALUES ('''+
tblkeywordKEYWORD.Value+'''))');
zQueryKeyword.ExecSql;
I tried using the unique constraint in IBExpert, but it gives the following error:
Invalid insert or update value(s): object columns are
constrained - no 2 table rows can have duplicate column values.
attempt to store duplicate value (visible to active transactions) in unique index "UNQ1_KEYWORDLIST".
Consider to use UPDATE OR INSERT or MERGE statements:
update or insert into KEYWORDLIST (KEYWORD) values(:KEYWORD) matching(KEYWORD)
For details check the following documents in your Firebird installation folder:
doc\sql.extensions\README.update_or_insert.txt
doc\sql.extensions\README.merge.txt
I'm trying to populate a TDBGrid with the results of the following TQuery against the file Journal.db:
select * from Journal
where Journal.where = "RainPump"
I've tried both Journal."Where" and Journal.[Where] to no avail.
I've also tried: select Journal.[Where] as "Location" with the same result.
Journal.db is a file created by a third party and I am unable to change the field names.
The problem is that the field I'm interested in is called 'where' and understandably causes the above error. How do I reference this field without causing the BDE (presumably) to explode?
Aah, I'm loving delphi again... I found a workaround. The TQuery component has the Filter property :-)
I omitted the "Where=" where clause from the query whilst still keeping all the other 'and' conditions.
I set the Filter property to "Where = 'RainPump'".
I set the Filtered property to True and life is good again.
I'm still wondering if there's a smarter way to do this using this old technology but if it's stupid and it works, then it's not stupid.
I'm afraid that someone reading this thread will get the impression that the BDE SQL engine cannot handle the query:
select * from Journal where Journal."Where" = "RainPump"
and will waste their time unnecessarily circumlocuting around it.
In fact this construction works fine. The quotes around "Where" keeps the BDE from interpreting it as a keyword, just as you would expect.
I don't know what is wrong in Baldric's particular situation, or what he tried in what order. He describes the problem as querying a *.db table, but his SQL error looks more like something you'd get in passthrough mode. Or, possibly he simplified his code for submission, thus eliminating the true cause of the error.
My tests performed with:
BDE v.5.2 (5.2.0.2)
Paradox for Windows v. 7 (32b)
Delphi 5.0 (5.62)
Various versions of the statement that succeed:
select * from Journal D0 where D0."Where" = "RainPump"
select * from Journal where Journal."Where" = "RainPump"
select * from ":common:Journal" D0 where D0."Where" = "RainPump"
select * from ":common:Journal" where ":common:Journal"."Where" = "RainPump"
select * from :common:Journal where Journal."Where" = "RainPump"
select * from ":common:Journal" D0 where D0."GUMPIK" = 3
select * from ":common:Journal" where ":common:Journal"."GUMPIK" = 3
select * from :common:Journal where Journal."GUMPIK" = 3
Versions of the statement that look correct but fail with "Invalid use of keyword":
select * from ":common:Journal" where :common:Journal."Where" = "RainPump"
select * from :common:Journal where :common:Journal."Where" = "RainPump"
select * from ":common:Journal" where :common:Journal."GUMPIK" = 3
select * from :common:Journal where :common:Journal."GUMPIK" = 3
-Al.
You can insert the resultset into a new table with "values" (specifying no column names) where you have given your own column names in the new table and then do a select from that table, Using a TQuery, something like:
Query1.sql.clear;
query1,sql.add('Insert into newtable values (select * from Journal);');
query1.sql.add('Select * from newtable where newcolumn = "Rainpump";');
query1.open;
Rewrite it like this, should work:
select * from Journal where Journal.[where] = "RainPump"
Me, I'd rename the awkward column.
In MySQL, table/column names can be enclosed in `` (the angled single quotes). I'm not sure what the BDE allows, but you could try replacing [where] with `where`
select * from Journal where Journal."where" = "RainPump"
Ok, so naming columns after keyboards is bad in ANY SQL system. Would you name a column "select" or "count" or "alter" or "table" or perhaps just for the fun of it "truncate" or "drop"? I would hope not.
Even if you build in the work around for this instance you are creating a mine field for whomever comes after you. Do what mj2008 said and rename the bloody column.
Allowing this column name to persist is the worst example of someone who is building a system and would get you on the poop list for any project manager.