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.
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 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.
What does the symbol "*=" stand for in a SELECT statement? How does this affect the performance? Can we replace it with JOINS?
Thanks
STR
In some database systems,
SELECT ... FROM a, b WHERE a.field1 *= b.field2
is an old syntax for a LEFT OUTER JOIN:
SELECT ... FROM a LEFT JOIN b ON a.field1 = b.field2
Microsoft SQL Server, for example, considers the *= syntax to be deprecated since version 2005.
So yes, you can replace *= with a JOIN. In fact, you should. (I doubt that it affects performance in any relevant way, but your queries might stop working in newer versions of your database engine.)
In some implementations of SQL, a SELECT statement that has a assignment operator(=) can be used to create the relationship between a column heading and the expression that defines the values for the column.
So an example might be:
SELECT name = 'renamed_column_name'
FROM users
More Info:
Unfortunately the = operator can mean both assign and equality.
For assignment:
DECLARE #Counter INT;
SET #Counter = 1;
For equality:
The = is a equality operator states that the left side must equal the right side.
The could mean a value must equal the result returned by a subquery, or a variable must equal a literal, no matter the case... a = b means that a and b have to have the same value.
SELECT * FROM users LEFT JOIN posts ON users.id = posts.user_id
I have 14 fields which are similar and I search the string 'A' on each of them. I would like after that order by "position" field
-- some set in order to remove a lot of useless text
def col='col01'
select '&col' "Fieldname",
&col "value",
position
from oneTable
where &col like '%A%'
/
-- then for the second field, I only have to type two lines
def col='col02'
/
...
def col='col14'
/
Write all the fields which contains 'A'. The problem is that those field are not ordered by position.
If I use UNION between table, I cannot take advantage of the substitution variables (&col), and I have to write a bash in unix in order to make the replacement back into ksh. The problem is of course that database code have to be hard-coded in this script (connection is not easy stuff).
If I use a REFCURSOR with OPEN, I cannot group the results sets together. I have only one request and cannot make an UNION of then. (print refcursor1 union refcursor2; print refcursor1+refcursor2 raise an exception, select * from refcursor1 union select * from refcursor2, does not work also).
How can concatenate results into one big "REFCURSOR"? Or use a union between two distinct run ('/') of my request, something like holding the request while typing new definition of variables?
Thank you for any advice.
Does this answer your question ?
CREATE TABLE #containingAValueTable
(
FieldName VARCHAR(10),
FieldValue VARCHAR(1000),
position int
)
def col='col01'
INSERT INTO #containingAValueTable
(
FieldName , FieldValue, position
)
SELECT '&col' "Fieldname",
&col "value",
position
FROM yourTable
WHERE &col LIKE '%A%'
/
-- then for the second field, I only have to type two lines
def col='col02'
INSERT INTO...
/
def col='col14'
/
select * from #containingAValueTable order by postion
DROP #containingAValueTable
But I'm not totally sure about your use of the 'substitution variable' called "col" (and i only have SQL Server to test my request so I used explicit field names)
edit : Sorry for the '#' charcater, we use it so often in SQL Server for temporaries, I didn't even know it was SQL Server specific (moreover I think it's mandatory in sql server for creating temporary table). Whatever, I'm happy I could be useful to you.