I'm using Delphi XE2 with AnyDac Components and Advantage Database 10.
In my code I'm using parametrized querys like this:
q.SQL.Text := 'SELECT * FROM Table1 ' +
'LEFT JOIN Table2 ON (Table1.ID = Table1 .ID_Table2) ' +
'WHERE ' +
':0 BETWEEN Table1.StartAm AND Table1.EndeAm ' +
'AND Table2 = :1';
q.Params[0].Value := AStartDateTime;
q.Params[1].Value := AIDRessourcenGruppe;
q.Open;
this ends up in an exception:
Exception der Klasse EADSNativeException mit der Meldung
'[AnyDAC][Phys][ADS] Error 7200: AQE Error: State = 22018;
NativeError = 2112; [iAnywhere Solutions][Advantage SQL
Engine]Assignment error' aufgetreten.
of course AStartDateTime is a valid delphi TDateTime value, AIDRessourcenGruppe is a integer value.
interestingly, these two variants work:
q.SQL.Text := 'SELECT * FROM Table1 ' +
'LEFT JOIN Table2 ON (Table1.ID = Table1 .ID_Table2) ' +
'WHERE ' +
':0 BETWEEN Table1.StartAm AND Table1.EndeAm ' +
'AND Table2 = :1';
q.Params[0].AsDateTime:= AStartDateTime;
q.Params[1].AsInteger:= AIDRessourcenGruppe;
q.Open;
-
q.SQL.Text := 'SELECT * FROM Table1 ' +
'LEFT JOIN Table2 ON (Table1.ID = Table1 .ID_Table2) ' +
'WHERE ' +
':SomeDate BETWEEN Table1.StartAm AND Table1.EndeAm ' +
'AND Table2 = :ID_PT_Ressourcengruppe';
q.ParamByName('SomeDate').Value := AStartDateTime;
q.ParamByName('ID_PT_Ressourcengruppe').Value := AIDRessourcenGruppe;
q.Open;
Do I miss something? Thanks for any help!
Answer - just a guess:
I would say the indexed write access to Value property of the parameters doesn't determine data type of parameters whilst the named access does. And if that's correct, then you're in trouble because you're passing all the values through Variant type which must converted to a proper value format before the query is performed. But all of that is just my guess - I don't know AnyDAC at all!
AsType value access:
I'm posting this just because I don't like Value access to parameters to be called professional :-)
It's better to use AsType typecast at least because:
it is faster because you directly say what type you're passing to a certain parameter, thus the query parameter engine doesn't need to determine this, and in comparison with Value access, it doesn't need to convert Variant type
it's safer for you, because you can't pass e.g. string value to AsDateTime accessed parameter, so you have an extra protection against parameter mismatch
What I commend in your example, is the use of indexed access to parameters, instead of commonly used named which needs to search the parameter list before the access and which is slower.
Related
How can I set a variable in stored procedure and use it in following query to be executed.
create or replace procedure sp1()
returns table (dealer_id varchar, dealershipgroup_name varchar)
language sql
as
$$
declare
create_query varchar;
res resultset;
MISSING_DEALER NUMBER(38,0) default 0;
begin
MISSING_DEALER := 100;
select_query := 'WITH CTE AS(
SELECT dealer_id,
CASE WHEN dealer_id=:MISSING_DEALER then \'Abc\'
WHEN dealershipgroup IS NULL then \'\'
ELSE dealershipgroup end as dealershipgroup FROM TBL )
select * from CTE';
res:= (execute immediate : select_query);
return table(res);
end;
$$;
call sp1();
Could someone please suggest how can I use MISSING_DEALER in the query. I am currently getting the following error
Uncaught exception of type 'STATEMENT_ERROR' on line 28 at position 9 : SQL compilation error: error line 8 at position 26 Bind variable :MISSING_DEALER not set
You need to concatenate the string parts of your SQL statement with the variable. This is covered in the documentation if you look at the end of the section here
I'm trying to call the LPAD function during the UNLOAD command inside a Redshift stored procedure and it keeps returning an error: Amazon Invalid operation: syntax error at or near "9" . Can anyone help me address this error?
The column that LPAD is applied to is VARCHAR data type. Basically, I'm adding leading "0" to records that are less than 9 character long.
If it matters, I'm using Aginity Pro as the query editor while connecting to Redshift. Here is an example stored procedure:
CREATE OR REPLACE PROCEDURE schemaname.sp_example ()
LANGUAGE plpgsql
AS
$$
DECLARE _CurrentDate VARCHAR(10);
_UnloadQuery VARCHAR(65000);
BEGIN
WITH CTE_CurrentDate AS (SELECT TO_CHAR(CAST(GETDATE() AS DATE), 'YYYYMMDD') AS CurrentDateParm)
SELECT CurrentDateParm INTO _CurrentDate FROM CTE_CurrentDate;
_UnloadQuery := '
UNLOAD ('''||
'
SELECT B.column_name
FROM tbl_a a
INNER JOIN tbl_b B
on LPAD(a.column_name,'''||$9||''',''0'') = B.column_name
GROUP BY B.column_name
'
||'''
)
to '''||'s3://address_'||_CurrentDate||'.CSV'||'''
credentials '''||'aws_access_key_id=xxx'||';'||'aws_secret_access_key=xxx'||'''
Header
ALLOWOVERWRITE
delimiter '''||','||'''
addquotes
PARALLEL OFF
';
EXECUTE _UnloadQuery;
END;
$$;
Here i want to fetch some result from table for that I i have writter Sp as like below.
create proc GetData
(
#tableName nvarchar(max),
#groupLetter nvarchar(max)
)
as
begin
EXEC('Select * from ' + #tablename + 'where LastName LIKE'''+'%'+#groupLetter+'%'+'''ORDER BY LastName')
end
to this SP i am passing table name and the text to find a result.
this create SP successfully but gives error while executing it.
this is the way i executes SP.
EXEC GetData Employees,ab
and am getting error as below.
Incorrect syntax near the keyword 'LIKE'.
It's just a syntax error in your string (not enough spaces). It should be:
begin
EXEC('Select * from ' + #tablename + ' where LastName LIKE '''+'%'+#groupLetter+'%'+''' ORDER BY LastName')
end
Your code should look like this:
declare #sql nvarchar(max) = N'Select * from ' + #tablename + N' where LastName LIKE '''+N'%'+#groupLetter+N'%'+N''' ORDER BY LastName'
EXEC(#sql);
The first error that you had was you missed some spaces between keywords of your query, the second error: exec does not accept string concatenation so you should construct your query in a variable and then pass it into exec.
I'm using a TDataSet where the CommandText property is set to an SQL query. I have also made the following function which creates part of an SQL query based on the fields of TDataSet. It is however incomplete. As you can see I still need to get the name of the table that a TField is from. How do I achieve this?
function GetDataSetFieldsMSSQL(Dataset: TDataSet): String;
var
I, L: Integer;
TableName: String;
begin
Result := '';
L := Dataset.Fields.Count;
if (L > 0) then
begin
TableName := ... // Name of the table for the Dataset.Fields[0] field.
Result := '[' + TableName + '].[' + Dataset.Fields[0].FieldName + ']';
I := 1;
while (I < L) do
begin
TableName := ... // Name of the table for the Dataset.Fields[I] field.
Result := Result + ',[' + TableName + '].[' + Dataset.Fields[I].FieldName + ']';
Inc(I);
end;
end;
end;
You can use the Delphi Function GetTableNameFromQuery(SQL : String):String; from the DBCommon unit. Just Add The DBCommon on the uses. =)
Maybe there is no solution at all for a simple TDataSet?
I believe not. Because an TDataset can source its' data not only from RDBMS' tables.
It can be:
an RSS feed
An XML file. Example: TCliendataset is an TDataset descendant that can read XML from its'
own format or using an XMLTransformProvider.
It can be an SQL for reading an Excel spreadsheet or a text file if you have an ODBC driver for
that and configured the datasource.
Sky (and the imagination of Delphi's programmers around the world) is the limit for what a field can represent in an TDataset.
You have some alternatives, since you are using an ADODataset:
Parsing the commandText of ADOCommand
Using the BASETABLENAME property of ADORecordSet (as in kobik's comment)
Guessing by convention ( Abelisto's answer )
As I know there is no any way to get the name of the table from the SQL query component.
However you can give aliases for fields, for example: "select foo_field as foo_dot_foo_field from foo" and then replace them to the correct syntax: "Result := '[' + StringReplace(DataSet.Fields[0].FieldName, 'dot', '].[', [rfReplaceAll]) + ']'"
What you are trying to do is impossible if you have no knowledge or control over the SQL used in the query.
The query could contain calculated/computed fields or could be returning fields from a view etc. Furthermore the database might have several tables that contain the same field names.
If possible you can query the SQL server view INFORMATION_SCHEMA.COLUMNS and that way try to figure out what table a fieldname is from. However if the field names are not unique this might also prove impossible.
I have a long SQL text that I want to assign to a query SQL. I do this the following way:
SQL.Text:= 'SELECT T1.COLUMN1,T2.COLUMN2,T1COLUMN3..........,'+
' T1.COLUMNn FROM TABLE1 T1 INNER JOIN '+
' TABLE2 T2 ON T1.ID=T2.ID'+
' WHERE T1.COLUMN10=100'
The actual SQL is 20 times longer than this. My problem is with the line breaks. When I format the source code (Ctrl+D) it sometimes leaves the lines as I typed, but other times it deletes the line breaks and I get something like this:
'SELECT T1.COLUMN1,T2.COLUMN2,T1COLUMN3 ' + 'FROM TABLE1 T1 INNER JOIN '+ 'TABLE2 T2 ON T1.ID=T2.ID'
And this leads to a "line too long (more than 1023 charactes)" error. What's interesting is this does not happen with all lines. I can't catch the difference between the lines which will be affected and those that won't. I need a line break after or before the "+" sign. How do I do this?
You can also use the Add function.
SQL.Clear;
SQL.ADD('SELECT T1.COLUMN1,T2.COLUMN2,T1COLUMN3..........,');
SQL.ADD(' T1.COLUMNn FROM TABLE1 T1 INNER JOIN');
SQL.ADD(' TABLE2 T2 ON T1.ID=T2.ID');
SQL.ADD(' WHERE T1.COLUMN10=100');
Try to assign the value in every line:
SQL.Text := 'SELECT T1.COLUMN1,T2.COLUMN2,T1COLUMN3..........,';
SQL.Text := SQL.Text + ' T1.COLUMNn FROM TABLE1 T1 INNER JOIN ';
SQL.Text := SQL.Text + ' TABLE2 T2 ON T1.ID=T2.ID';
SQL.Text := SQL.Text + ' WHERE T1.COLUMN10=100';
I know it looks ugly, but I think it solves your problem.