How can I get the table description (fields and types) from Firebird with dbExpress - delphi

I have written a tool for displaying database structures using the GetTableNames and GetFieldNames methods of TSQLConnection. How can I get the types of each field name similar to the following list (which is part of the DDL required to build the table)?
TABLE: ARTICLES
ID INTEGER NOT NULL
PRINTED SMALLINT DEFAULT 0
ACADEMIC SMALLINT
RELEVANCE SMALLINT
SOURCE VARCHAR(64) CHARACTER SET WIN1251 COLLATE WIN1251
NAME VARCHAR(128) CHARACTER SET WIN1251 COLLATE WIN1251
FILENAME VARCHAR(128) CHARACTER SET WIN1251 COLLATE WIN1251
NOTES VARCHAR(2048) CHARACTER SET WIN1251 COLLATE WIN1251

This is incomplete (because I've never used Firebird array data types) and not much tested but perhaps it will give you a good starting point:
SELECT
RF.RDB$FIELD_NAME FIELD_NAME,
CASE F.RDB$FIELD_TYPE
WHEN 7 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'SMALLINT'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 8 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'INTEGER'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 9 THEN 'QUAD'
WHEN 10 THEN 'FLOAT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 14 THEN 'CHAR(' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ') '
WHEN 16 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'BIGINT'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 27 THEN 'DOUBLE'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR(' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ')'
WHEN 40 THEN 'CSTRING' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ')'
WHEN 45 THEN 'BLOB_ID'
WHEN 261 THEN 'BLOB SUB_TYPE ' || F.RDB$FIELD_SUB_TYPE
ELSE 'RDB$FIELD_TYPE: ' || F.RDB$FIELD_TYPE || '?'
END FIELD_TYPE,
IIF(COALESCE(RF.RDB$NULL_FLAG, 0) = 0, NULL, 'NOT NULL') FIELD_NULL,
CH.RDB$CHARACTER_SET_NAME FIELD_CHARSET,
DCO.RDB$COLLATION_NAME FIELD_COLLATION,
COALESCE(RF.RDB$DEFAULT_SOURCE, F.RDB$DEFAULT_SOURCE) FIELD_DEFAULT,
F.RDB$VALIDATION_SOURCE FIELD_CHECK,
RF.RDB$DESCRIPTION FIELD_DESCRIPTION
FROM RDB$RELATION_FIELDS RF
JOIN RDB$FIELDS F ON (F.RDB$FIELD_NAME = RF.RDB$FIELD_SOURCE)
LEFT OUTER JOIN RDB$CHARACTER_SETS CH ON (CH.RDB$CHARACTER_SET_ID = F.RDB$CHARACTER_SET_ID)
LEFT OUTER JOIN RDB$COLLATIONS DCO ON ((DCO.RDB$COLLATION_ID = F.RDB$COLLATION_ID) AND (DCO.RDB$CHARACTER_SET_ID = F.RDB$CHARACTER_SET_ID))
WHERE (RF.RDB$RELATION_NAME = :TABLE_NAME) AND (COALESCE(RF.RDB$SYSTEM_FLAG, 0) = 0)
ORDER BY RF.RDB$FIELD_POSITION;

Use direct access to RDB$ tables. For example:
SELECT * FROM rdb$relations
will give you a list of all tables in a database.
SELECT
*
FROM
rdb$relation_fields rf JOIN rdb$fields f
ON f.rdb$field_name = rf.rdb$field_source
WHERE
rf.rdb$relation_name = :RN
will result in a list of all fields of given table
with information of field type. Param RN is a name of the table.
Using information from RDB$tables one can easily construct DDL
statement. The query below gives you a hint how to do it:
SELECT
TRIM(rf.rdb$field_name) || ' ' ||
IIF(rdb$field_source LIKE 'RDB$%',
DECODE(f.rdb$field_type,
8, 'INTEGER',
12, 'DATE',
37, 'VARCHAR',
14, 'CHAR',
7, 'SMALLINT'),
TRIM(rdb$field_source)) ||
IIF((rdb$field_source LIKE 'RDB$%') AND (f.rdb$field_type IN (37, 14)),
'(' || f.rdb$field_length || ')',
'') ||
IIF((f.rdb$null_flag = 1) OR (rf.rdb$null_flag = 1),
' NOT NULL', '')
FROM
rdb$relation_fields rf JOIN rdb$fields f
ON f.rdb$field_name = rf.rdb$field_source
WHERE
rf.rdb$relation_name = '<put_your_table_name_here>'

Using the link which TLama provided, I found my own solution, which is somewhat similar to the above solutions, but simpler.
SELECT R.RDB$FIELD_NAME AS field_name,
CASE F.RDB$FIELD_TYPE
WHEN 7 THEN 'SMALLINT'
WHEN 8 THEN 'INTEGER'
WHEN 9 THEN 'QUAD'
WHEN 10 THEN 'FLOAT'
WHEN 11 THEN 'D_FLOAT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 14 THEN 'CHAR'
WHEN 16 THEN 'INT64'
WHEN 27 THEN 'DOUBLE'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR'
WHEN 40 THEN 'CSTRING'
WHEN 261 THEN 'BLOB'
ELSE 'UNKNOWN'
END AS field_type,
F.RDB$FIELD_LENGTH AS field_length,
CSET.RDB$CHARACTER_SET_NAME AS field_charset
FROM RDB$RELATION_FIELDS R
LEFT JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME
LEFT JOIN RDB$CHARACTER_SETS CSET ON F.RDB$CHARACTER_SET_ID = CSET.RDB$CHARACTER_SET_ID
WHERE R.RDB$RELATION_NAME= :p1
ORDER BY R.RDB$FIELD_POSITION
p1 is the table name which is passed as a parameter to the query.
In context, I have a treeview which has as its nodes the table names of a given database; for each node, the child nodes are the fields along with their definitions.
sqlcon.GetTableNames (dbTables); // sqlcon is the TSQLConnection
tv.items.Clear;
for i:= 1 to dbTables.count do
begin
node:= tv.items.Add (nil, dbTables[i - 1]);
with qFields do // the above query
begin
params[0].asstring:= dbTables[i - 1];
open;
while not eof do
begin
tv.items.addchild (node, trim (fieldbyname ('field_name').asstring) + ', ' +
trim (fieldbyname ('field_type').asstring) + ', ' +
fieldbyname ('field_length').asstring + ', ' +
fieldbyname ('field_charset').asstring);
next
end;
close
end
end;
Here is a screenshot of the program in action. I realise that the format is not the same as the DDL which I quoted, but it's obvious what each field means (at least to me, and this is a program for my private use).

I made a litle change to the first option to support computed by fields, add field_position and made a view to make more easy.
CREATE VIEW TABLES (
TABLE_NAME,
FIELD_NAME,
FIELD_POSITION,
FIELD_TYPE,
FIELD_NULL,
FIELD_CHARSET,
FIELD_COLLATION,
FIELD_DEFAULT,
FIELD_CHECK,
FIELD_DESCRIPTION
)
AS
SELECT
RF.RDB$RELATION_NAME,
RF.RDB$FIELD_NAME FIELD_NAME,
RF.RDB$FIELD_POSITION FIELD_POSITION,
CASE F.RDB$FIELD_TYPE
WHEN 7 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'SMALLINT'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 8 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'INTEGER'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 9 THEN 'QUAD'
WHEN 10 THEN 'FLOAT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 14 THEN 'CHAR(' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ') '
WHEN 16 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'BIGINT'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 27 THEN 'DOUBLE'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN
IIF (COALESCE(f.RDB$COMPUTED_SOURCE,'')<>'',
'COMPUTED BY ' || CAST(f.RDB$COMPUTED_SOURCE AS VARCHAR(250)),
'VARCHAR(' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ')')
WHEN 40 THEN 'CSTRING' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ')'
WHEN 45 THEN 'BLOB_ID'
WHEN 261 THEN 'BLOB SUB_TYPE ' || F.RDB$FIELD_SUB_TYPE
ELSE 'RDB$FIELD_TYPE: ' || F.RDB$FIELD_TYPE || '?'
END FIELD_TYPE,
IIF(COALESCE(RF.RDB$NULL_FLAG, 0) = 0, NULL, 'NOT NULL') FIELD_NULL,
CH.RDB$CHARACTER_SET_NAME FIELD_CHARSET,
DCO.RDB$COLLATION_NAME FIELD_COLLATION,
COALESCE(RF.RDB$DEFAULT_SOURCE, F.RDB$DEFAULT_SOURCE) FIELD_DEFAULT,
F.RDB$VALIDATION_SOURCE FIELD_CHECK,
RF.RDB$DESCRIPTION FIELD_DESCRIPTION
FROM RDB$RELATION_FIELDS RF
JOIN RDB$FIELDS F ON (F.RDB$FIELD_NAME = RF.RDB$FIELD_SOURCE)
LEFT OUTER JOIN RDB$CHARACTER_SETS CH ON (CH.RDB$CHARACTER_SET_ID = F.RDB$CHARACTER_SET_ID)
LEFT OUTER JOIN RDB$COLLATIONS DCO ON ((DCO.RDB$COLLATION_ID = F.RDB$COLLATION_ID) AND (DCO.RDB$CHARACTER_SET_ID = F.RDB$CHARACTER_SET_ID))
WHERE (COALESCE(RF.RDB$SYSTEM_FLAG, 0) = 0)
ORDER BY RF.RDB$FIELD_POSITION
;

Related

Drop and Create table if exist if not then Create in oracle Procedure

I have a query that takes start and end year for data also it takes a name of a table as procedure parameters..
the query then will create a table with that name if it doesn't exist or if exist it will drop and recreate it
CREATE OR REPLACE Procedure USE_RAWDATA
( START_RP IN NUMBER, END_RP IN NUMBER,TABLE_NAME varchar)
IS
v_listStr CLOB;
DAT VARCHAR(500):=to_char(to_date(SYSDATE), 'yyyymmdd');
cnt NUMBER;
BEGIN
BEGIN
SELECT COUNT(*) INTO cnt FROM user_tables WHERE table_name = TABLE_NAME||'_'||DAT;
dbms_output.put_line(TABLE_NAME||'_'||DAT);
dbms_output.put_line(cnt);
IF (cnt) = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE '||TABLE_NAME||'_'||DAT;
dbms_output.put_line('DROP TABLE '||TABLE_NAME||'_'||DAT);
END IF;
END;
select rtrim(xmlagg(xmlelement(e,column_name,', ').extract('//text()') order by column_id).getclobval(),', ') x INTO v_listStr from RAW_DATA_METADATA WHERE (START_ROUND<= END_RP and END_ROUND is NULL) or (START_ROUND >= START_RP and END_ROUND <= END_RP );
EXECUTE IMMEDIATE 'CREATE TABLE '||TABLE_NAME||'_'||DAT||' AS SELECT '||v_listStr ||' FROM RAW_DATA WHERE ROUND_ID BETWEEN '||START_RP ||' AND '||END_RP;
END;
/
my problem is that cnt is always giving me 0 even the table exists then the procedure will end with an error table already exist...
I don't know why .. since when I try it as PL\SQL query it gives me a correct result cnt =1 if exist
declare
v_listStr CLOB;
DAT VARCHAR(500):=to_char(to_date(SYSDATE), 'yyyymmdd');
cnt NUMBER;
BEGIN
BEGIN
SELECT COUNT(*) INTO cnt FROM user_tables WHERE :TABLE_NAME = :TABLE_NAME||'_'||DAT;
dbms_output.put_line(:TABLE_NAME||'_'||DAT);
dbms_output.put_line(cnt);
IF (cnt) = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE '||:TABLE_NAME||'_'||DAT;
dbms_output.put_line('DROP TABLE '||:TABLE_NAME||'_'||DAT);
END IF;
END;
select rtrim(xmlagg(xmlelement(e,column_name,', ').extract('//text()') order by column_id).getclobval(),', ') x INTO v_listStr from RAW_DATA_METADATA WHERE (START_ROUND<= :END_RP and END_ROUND is NULL) or (START_ROUND >= :START_RP and END_ROUND <= :END_RP );
EXECUTE IMMEDIATE 'CREATE TABLE '||:TABLE_NAME||'_'||DAT||' AS SELECT '||v_listStr ||' FROM RAW_DATA WHERE ROUND_ID BETWEEN '||:START_RP ||' AND '||:END_RP;
END;
/
can you help me figure out why it always cnt gives 0 even the table is exist
This query can only ever return 0:
select count(*) into cnt
from user_tables
where table_name = table_name || '_' || dat;
You need to either prefix table_name with the procedure name
select count(*) into cnt
from user_tables
where table_name = use_rawdata.table_name || '_' || dat;
or else name your parameters differently (tableName, p_table_name etc).
I'd also suggest removing the first begin and end as they aren't doing anything, and adjusting your indentation to reflect the code structure more accurately.
I make it something like this:
create or replace procedure use_rawdata
( start_rp in number
, end_rp in number
, table_name varchar2 )
is
v_liststr clob;
dat varchar2(8) := to_char(sysdate, 'yyyymmdd');
cnt number;
begin
select count(*) into cnt
from user_tables
where table_name = upper(use_rawdata.table_name) || '_' || dat;
dbms_output.put_line(table_name || '_' || dat);
dbms_output.put_line(cnt);
if cnt = 1 then
execute immediate 'DROP TABLE ' || table_name || '_' || dat;
dbms_output.put_line('DROP TABLE ' || table_name || '_' || dat);
end if;
select rtrim(xmlagg(xmlelement(e, column_name, ', ').extract('//text()') order by column_id).getclobval(), ', ') x
into v_liststr
from raw_data_metadata
where (start_round <= end_rp and end_round is null)
or (start_round >= start_rp and end_round <= end_rp);
execute immediate 'create table ' || table_name || '_' || dat || ' as select ' || v_liststr || ' from raw_data where round_id between ' || start_rp || ' and ' || end_rp;
end;

How to output error record as part of DB2 Stored Procedure standard output

Sometimes when we execute the error record we get bad data and the below stored procedure errors out with error code as
"sql> call REFLOADER.SP_ALL_ES_CODE()
[2018-11-20 10:14:01] [22004][-87] A null value was specified in a context where a null value is not allowed.. SQLCODE=-87, SQLSTATE=22004, DRIVER=4.23.42".
As such I would like to **split out the error code along with the error values/record set that is failing in the below script. can you please help with the code where and how can i get the error record output.**
enter code here
CREATE OR REPLACE PROCEDURE SCHEMA1.SP_ALL_CODE ()
DYNAMIC RESULT SETS 1
P1: BEGIN
DECLARE SQLSTATE CHAR(5) DEFAULT '00000';
DECLARE l_stmt VARCHAR(5000);
DECLARE l_dynamic_sql VARCHAR(5000);
DECLARE l_uuid CHAR(36);
DECLARE l_ID VARCHAR(255);
DECLARE l_table VARCHAR(100);
DECLARE l_exists integer;
DECLARE l_changed integer;
DECLARE l_CID VARCHAR(255);
DECLARE l_Tt VARCHAR(255);
DECLARE l_TL VARCHAR(255);
DECLARE l_DateStart DATE;
DECLARE l_DateEnd DATE;
DECLARE l_DateEnd_Format DATE;
P2: BEGIN
-- Declare cursor
DECLARE cursor1 CURSOR WITH RETURN TO CLIENT FOR
SELECT c.ID , REPLACE(CID,'''',''''''), REPLACE(tt,'''',''''''), TL ,DateStart ,DateEnd , tablename
FROM SCHEMA1.ALL_CODE c, SCHEMA1.ID_TABLENAME t
WHERE c.id = t.id
and t.tabletype = 1
-- Access Location/Site (ignore end dated)
--and (c.oid != '2.16.840.1.113883.3.2390.2.2.26' OR DateEnd is null)
and (t.tablename != 'FREQUENCY' OR DateEnd is null)
and (t.tablename != 'ORDER_FREQUENCY' OR DateEnd is null)
Order By c.ID , CID;
-- Cursor left open for client application
OPEN cursor1;
FETCH FROM cursor1 INTO
l_ID , l_CID , l_Tt , l_TL , l_DateStart , l_DateEnd, l_table ;
WHILE(SQLSTATE = '00000')
DO
--Insert or Update
IF (l_DateStart IS NULL) THEN
SET l_DateStart = '1900-01-01';
END IF;
--exception
-- L_TEST (smart) 216840
If l_id = '216840' Then
SET l_CID = LPAD(l_CID,4,'0' );
End if;
SET l_exists = 0;
SET l_dynamic_sql = 'select count(*) from m_ref.' || l_table || ' where c_id =' || '''' || l_id || ':' || l_cId || '''' ;
--INSERT INTO SCHEMA1.dml_ins_upd VALUES (l_dynamic_sql);
PREPARE p_exist_cursor from l_dynamic_sql;
P3: BEGIN
DECLARE exist_cursor CURSOR FOR p_exist_cursor;
OPEN exist_cursor;
FETCH exist_cursor INTO l_exists;
CLOSE exist_cursor;
END P3;
IF (l_exists = 0)
THEN
SET l_uuid = RANDOMUUID();
SET l_stmt = 'Insert into SCHEMA1.' || l_table ||
' (ID, CID, DISPLAY_NAME, END_DATE, START_DATE) values (' ||
'''' || l_uuid || '''' || ',' ||
'''' || l_oid || ':' || l_cId || '''' || ',' ||
'''' || l_Tt || '''' || ',';
IF (l_DateEnd IS NULL) THEN
SET l_stmt = l_stmt || 'NULL' || ',' ;
ELSE
SET l_stmt = l_stmt || '''' || VARCHAR_FORMAT(l_DateEnd, 'YYYY-MM-DD') || '''' || ',' ;
END IF;
SET l_stmt = l_stmt || '''' || VARCHAR_FORMAT(l_DateStart, 'YYYY-MM-DD') || '''' || ');' ;
if (l_stmt is null) then
set l_stmt = 'YYY';
end if;
INSERT INTO SCHEMA1.dml_ins_upd VALUES (l_stmt);
-- Exceptions
-- CC
If l_table = 'CARECAT' Then
SET l_stmt = 'Update SCHEMA1.CARE_CATEGORY SET CONTEXT_NAME = ' || '''' || 'NOTE' || '''';
SET l_stmt = l_stmt || ' WHERE cid = ' || '''' || (l_id || ':' || l_cId) || '''' || ';' ;
if (l_stmt is null) then
set l_stmt = 'BBB';
end if;
INSERT INTO REFLOADER.dml_ins_upd VALUES (l_stmt);
End if;
-- LTEST (Test) 2168401
If l_id = '2168401' Then
SET l_stmt = 'Update SCHEMA1.LTEST SET IS_FLAG = 0';
SET l_stmt = l_stmt || ' WHERE code_id = ' || '''' || (l_id || ':' || l_cId) || '''' || ';' ;
if (l_stmt is null) then
set l_stmt = 'XXX';
end if;
INSERT INTO REFLOADER.dml_ins_upd VALUES (l_stmt);
End if;
-- LAB_TEST (Panel) 2168401
If l_id = '2168401' Then
SET l_stmt = 'Update SCHEMA1.LTEST SET IS_FLAG = 1';
SET l_stmt = l_stmt || ' WHERE code_id = ' || '''' || (l_id || ':' || l_cId) || '''' || ';' ;
if (l_stmt is null) then
set l_stmt = 'TTT';
end if;
INSERT INTO SCHEMA1.dml_ins_upd VALUES (l_stmt);
End if;
------------------------------
END IF;
IF (l_exists != 0)
THEN
SET l_changed = 0;
SET l_dynamic_sql = 'select count(*) ' ||
' from SCHEMA1.' || l_table ||
' where cid = ' || '''' || l_id || ':' || l_cId || '''' ;
if (l_termshort is null) then
SET l_dynamic_sql = l_dynamic_sql ||
' and (display_name != null ';
else
SET l_dynamic_sql = l_dynamic_sql ||
' and (display_name != ' || '''' || l_tt || '''' ;
end if;
--Exception FREQUENCY
If (l_table = 'FREQUENCY') Then
if (l_tl is null) then
SET l_dynamic_sql = l_dynamic_sql ||
' OR numeric_value is not null ';
else
SET l_dynamic_sql = l_dynamic_sql ||
' OR numeric_value is null ' ||
' OR numeric_value != ' || l_tl ;
end if;
End IF;
---
SET l_dynamic_sql = l_dynamic_sql ||
' OR date(start_date) != ' || '''' || VARCHAR_FORMAT(l_DateStart, 'YYYY-MM-DD') || '''' ;
if (l_DateEnd is null) then
SET l_dynamic_sql = l_dynamic_sql || ' OR end_date is not null';
else
SET l_dynamic_sql = l_dynamic_sql || ' OR end_date is null OR date(end_date) != ' || '''' || VARCHAR_FORMAT(l_DateEnd, 'YYYY-MM-DD') || '''' ;
end if;
SET l_dynamic_sql = l_dynamic_sql || ')';
--INSERT INTO REFLOADER.dml_ins_upd VALUES (l_dynamic_sql);
PREPARE p_changed_cursor from l_dynamic_sql;
P4: BEGIN
DECLARE change_cursor CURSOR FOR p_changed_cursor;
OPEN change_cursor;
FETCH change_cursor INTO l_changed;
CLOSE change_cursor;
END P4;
IF (l_changed = 1)
THEN
SET l_stmt = 'Update SCHEMA1.' || l_table || ' SET display_name = ' || '''' || l_TS || '''' || ',';
--Exception FREQUENCY
If (l_table = 'FREQUENCY') Then
IF (l_TL IS NULL) THEN
SET l_stmt = l_stmt || ' numeric_value = ' || 'NULL' || ',' ;
ELSE
SET l_stmt = l_stmt || ' numeric_value = ' || l_tl || ',' ;
END IF;
End IF;
---
IF (l_DateEnd IS NULL) THEN
SET l_stmt = l_stmt || ' end_date = ' || 'NULL' || ',' ;
ELSE
SET l_stmt = l_stmt || ' end_date = ' || '''' || VARCHAR_FORMAT(l_DateEnd, 'YYYY-MM-DD') || '''' || ',' ;
END IF;
SET l_stmt = l_stmt || ' start_date = ' || '''' || VARCHAR_FORMAT(l_DateStart, 'YYYY-MM-DD') || '''' ;
SET l_stmt = l_stmt || ' WHERE cid = ' || '''' || (l_id || ':' || l_cId) || '''' || ';' ;
if (l_stmt is null) then
set l_stmt = 'XXX';
end if;
INSERT INTO SCHEMA1.dml_ins_upd VALUES (l_stmt);
END IF;
END IF;
-- Fetch Next
FETCH FROM cursor1 INTO
l_ID , l_CID , l_Tt , l_TL , l_DateStart , l_DateEnd, l_table;
END WHILE;
CLOSE cursor1;
END P2;
commit work;
END P1;
Your tool must be able to display so called SQLCA structure returned by the database manager on your statement. DB2 Command Line Processor displays this structure with '-a' parameter. The line of code with an error is in the sqlerrd(3) field (see the example at the link).

Dynamic Sql Error , Invalid syntax near '>'

I have the below dynamic sql statement and I'm getting the "Incorrect syntax near '>'. error message. I can not figure out what the problem is, not sure if I'm missing a tick mark or not. I've tried to add extra tick marks and nothing seems to work.
Declare #filters varchar(max)
SET #filters = 'Where PaymentAmount > 0'
BEGIN
SET #filters = #filters + ' AND CONVERT(DATE, AccountingDate) >= '''+ cast #BeginDate as nvarchar) + ''''
SET #filters = #filters + ' AND CONVERT(DATE, AccountingDate) <= '''+ cast(#EndDate as nvarchar) + ''''
END
SET #SQLString = 'Select
,[ReturnDate]
,[PolicyNumber]
From dbo.Bil_ReturnsRepository' + #filters
EXEC(#SQLString)
You need another space before you concat #filters to #SQLString.
SET #SQLString = 'Select
,[ReturnDate]
,[PolicyNumber]
From dbo.Bil_ReturnsRepository ' + #filters
Otherwise the generated sql would be
...
From dbo.Bil_ReturnsRepositoryWhere PaymentAmount > 0
...

Dynamic Relational operator in a where clause is Invalid (PL/SQL Oracle)

I am using Oracle 10g and I have the following stored procedure:
CREATE OR REPLACE PACKAGE BODY RF_PKG_STFCA_PositivePay
AS
PROCEDURE RF_SP_STFCA_PositivePay(IN_DATE IN NUMBER, IN_DATE_OPERATOR IN NVARCHAR2, OUT_DATA OUT CUR_DATA)
IS
/* this procedure returns a Ref Cursor with all the requested parameters
calling the stored procedure from an asp page (and anywhere else)
does not require posting a predefined number of records */
PaymentBatchNumber NVARCHAR2(4);
CurrencyCode NVARCHAR2(3);
TransactionCode NVARCHAR2(3);
Transit_BranchNumber NVARCHAR2(5);
BankAccountNumber NVARCHAR2(7);
ChequeNumber NVARCHAR2(8);
ChequeAmount NVARCHAR2(10);
ClientReference NVARCHAR2(19);
IssueDate NVARCHAR2(8);
PayeeName1 NVARCHAR2(60);
AddressLine1 NVARCHAR2(60);
AddressLine2 NVARCHAR2(60);
AddressLine4 NVARCHAR2(60);
AddressLine5 NVARCHAR2(60);
DateCreated NVARCHAR2(25);
DateVoided NVARCHAR2(25);
BEGIN
OPEN OUT_DATA FOR
SELECT LPAD(NVL(CD.PAYMENT_BATCH_NO, '0'), 4, '0') AS PaymentBatchNumber,
SUBSTR(NVL(CD.CURRENCY_ID, ' '), 1, 1) AS CurrencyCode,
NVL(CD.STATUS, ' ') AS TransactionCode,
LPAD(NVL(BA.BRANCH_ID, '0'), 5, '0') AS Transit_BranchNumber,
LPAD(NVL(BA.ACCOUNT_NO, '0'), 7, '0') AS BankAccountNumber,
LPAD(NVL(CD.CHECK_NO, '0') , 8, '0') AS ChequeNumber,
LPAD(TO_CHAR(NVL(CD.AMOUNT, 0)), 10, '0') AS ChequeAmount,
LPAD(NVL(CD.CONTROL_NO, '0'), 19, '0') AS ClientReference,
TO_CHAR(NVL(CD.CHECK_DATE, LPAD(' ', 8, ' ')), 'YYYYMMDD') AS IssueDate,
RPAD(NVL(CD.NAME, ' '), 60, ' ') AS PayeeName1,
RPAD(NVL(CD.ADDR_1, ' '), 60, ' ') AS AddressLine1,
RPAD(NVL(CD.ADDR_2, ' '), 60, ' ') AS AddressLine2,
RPAD(NVL(CD.CITY, '') || CASE WHEN CD.CITY IS NULL OR CD.STATE IS NULL THEN ' ' ELSE ', ' END || NVL(CD.STATE, ''), 60, ' ') AS AddressLine4,
RPAD(NVL(CD.ZIPCODE, ' '), 60, ' ') AS AddressLine5,
TO_CHAR(CD.CREATE_DATE, 'YYYYMMDDHH24MISS') AS DateCreated,
CASE WHEN CD.VOID_DATE IS NULL THEN ' ' ELSE TO_CHAR(CD.VOID_DATE, 'YYYYMMDDHH24MISS') END AS DateVoided
INTO PaymentBatchNumber, CurrencyCode, TransactionCode, Transit_BranchNumber, BankAccountNumber, ChequeNumber,
ChequeAmount, ClientReference, IssueDate, PayeeName1, AddressLine1, AddressLine2, AddressLine4, AddressLine5,
DateCreated, DateVoided
FROM BANK_ACCOUNT BA
INNER JOIN CASH_DISBURSEMENT CD ON BA.ID = CD.BANK_ACCOUNT_ID
WHERE BA.ACCOUNT_NO IS NOT NULL AND CD.CHECK_NO > 0 AND CD.STATUS != 'X' AND CD.AMOUNT != 0 AND ((TO_NUMBER(TO_CHAR(CD.CREATE_DATE, 'YYYYMMDDHH24MISS')) || IN_DATE_OPERATOR || IN_DATE) OR
(CASE WHEN CD.VOID_DATE IS NULL THEN 0 ELSE TO_NUMBER(TO_CHAR(CD.VOID_DATE, 'YYYYMMDDHH24MISS')) END || IN_DATE_OPERATOR || IN_DATE))
ORDER BY BA.BRANCH_ID, BA.ACCOUNT_NO;
END RF_SP_STFCA_PositivePay;
END RF_PKG_STFCA_PositivePay;
And I get the following error when entering this into SQL plus:
invalid relational operator
What I'm trying to do: I have this stored procedure that returns a secordset to my asp.net application using the REF CURSOR. I give it 2 input parameters. 1 is a date (IN_DATE) and 1 is an operator (IN_DATE_OPERATOR). The program works if "|| IN_DATE_OPERATOR ||" is replaces with either = or >= just the way I want it to work. The problem is based on what happens in the .Net application I want the operater it uses in the where clause to be either ">=" or "=" and I wont know which until runtime.
I know I'm doing this wrong but I don't know how to get oracle to reconize that IN_DATE_OPERATOR is a relational operator. I am open to other methods to have a dynamic operator (i tried CASE WHEN IN_DATE_OPERATOR = '=' THEN '=' ELSE '>=' END to no avail too) but I don't want to create a whole seperate stored procedure I will have to maintin in addition to this or a completely dynamic where clause. My ideal solution would make the least amount of changes to this query as possible. Any suggestions?
Edit: ok I've edited my query do be the following:
CREATE OR REPLACE PACKAGE BODY RF_PKG_STFCA_PositivePay
AS
PROCEDURE RF_SP_STFCA_PositivePay(IN_DATE IN NUMBER, IN_DATE_OPERATOR IN VARCHAR2, OUT_DATA OUT CUR_DATA)
IS
/* this procedure returns a Ref Cursor with all the requested parameters
calling the stored procedure from an asp page (and anywhere else)
does not require posting a predefined number of records */
SQL_Statement VARCHAR2(8000);
BEGIN
SQL_Statement := 'SELECT LPAD(NVL(CD.PAYMENT_BATCH_NO, ''0''), 4, ''0'') AS PaymentBatchNumber, ' ||
' SUBSTR(NVL(CD.CURRENCY_ID, '' ''), 1, 1) AS CurrencyCode, ' ||
' NVL(CD.STATUS, '' '') AS TransactionCode, ' ||
' LPAD(NVL(BA.BRANCH_ID, ''0''), 5, ''0'') AS Transit_BranchNumber, ' ||
' LPAD(NVL(BA.ACCOUNT_NO, ''0''), 7, ''0'') AS BankAccountNumber, ' ||
' LPAD(NVL(CD.CHECK_NO, ''0'') , 8, ''0'') AS ChequeNumber, ' ||
' LPAD(TO_CHAR(NVL(CD.AMOUNT, 0)), 10, ''0'') AS ChequeAmount, ' ||
' LPAD(NVL(CD.CONTROL_NO, ''0''), 19, ''0'') AS ClientReference, ' ||
' TO_CHAR(NVL(CD.CHECK_DATE, LPAD('' '', 8, '' '')), ''YYYYMMDD'') AS IssueDate, ' ||
' RPAD(NVL(CD.NAME, '' ''), 60, '' '') AS PayeeName1, ' ||
' RPAD(NVL(CD.ADDR_1, '' ''), 60, '' '') AS AddressLine1, ' ||
' RPAD(NVL(CD.ADDR_2, '' ''), 60, '' '') AS AddressLine2, ' ||
' RPAD(NVL(CD.CITY, '''') || CASE WHEN CD.CITY IS NULL OR CD.STATE IS NULL THEN '' '' ELSE '', '' END || NVL(CD.STATE, ''''), 60, '' '') AS AddressLine4, ' ||
' RPAD(NVL(CD.ZIPCODE, '' ''), 60, '' '') AS AddressLine5, ' ||
' TO_CHAR(CD.CREATE_DATE, ''YYYYMMDDHH24MISS'') AS DateCreated, ' ||
' CASE WHEN CD.VOID_DATE IS NULL THEN '' '' ELSE TO_CHAR(CD.VOID_DATE, ''YYYYMMDDHH24MISS'') END AS DateVoided ' ||
' FROM BANK_ACCOUNT BA ' ||
' INNER JOIN CASH_DISBURSEMENT CD ON BA.ID = CD.BANK_ACCOUNT_ID ' ||
' WHERE BA.ACCOUNT_NO IS NOT NULL AND CD.CHECK_NO > 0 AND CD.STATUS != ''X'' AND CD.AMOUNT != 0 ' ||
' AND ((TO_NUMBER(TO_CHAR(CD.CREATE_DATE, ''YYYYMMDDHH24MISS'')) ' || IN_DATE_OPERATOR || ' :1) ' ||
' OR (CASE WHEN CD.VOID_DATE IS NULL THEN 0 ELSE TO_NUMBER(TO_CHAR(CD.VOID_DATE, ''YYYYMMDDHH24MISS'')) END ' || IN_DATE_OPERATOR || ' :2)) ' ||
' ORDER BY BA.BRANCH_ID, BA.ACCOUNT_NO ';
OPEN OUT_DATA FOR SQL_Statement USING IN_DATE, IN_DATE;
END RF_SP_STFCA_PositivePay;
END RF_PKG_STFCA_PositivePay;/
but I get the following error:
LINE/COL ERROR
32/3 PL/SQL: Statement ignored
32/21 PLS-00382: expression is of wrong type
You would need to dynamically assemble the SQL statement in a string and then use that string to open the cursor. You'll need something along the lines of the get_cur procedure below where you assemble the SQL statement in a local VARCHAR2 variable including the placeholders for the bind variables and then open the cursor using the SQL statement you assembled and the bind variable you passed in.
SQL> create or replace procedure get_cur( p_date in date, p_operator in varchar2, p_cur out sys_refcursor )
2 as
3 l_sql_stmt varchar2(1000);
4 begin
5 l_sql_stmt := 'select * from emp where hiredate ' || p_operator || ' :1';
6 open p_cur for l_sql_stmt using p_date;
7 end;
8 /
Procedure created.
SQL> var rc refcursor;
SQL> exec get_cur( date '2001-01-01', '>=', :rc );
PL/SQL procedure successfully completed.
SQL> print rc;
no rows selected
SQL> exec get_cur( date '2001-01-01', '<=', :rc );
PL/SQL procedure successfully completed.
SQL> print rc;
EMPNO ENAME JOB MGR HIREDATE SAL COMM
---------- ---------- --------- ---------- --------- ---------- ----------
DEPTNO
----------
7369 SMITH CLERK 7902 17-DEC-80 801
20
7499 ALLEN SALESMAN 7698 20-FEB-81 1601 300
30
7521 WARD SALESMAN 7698 22-FEB-81 1251 500
30
EMPNO ENAME JOB MGR HIREDATE SAL COMM
---------- ---------- --------- ---------- --------- ---------- ----------
DEPTNO
----------
7566 JONES MANAGER 7839 02-APR-81 2976
20
7654 MARTIN SALESMAN 7698 28-SEP-81 1251 1400
30
7698 BLAKE MANAGER 7839 01-MAY-81 2851
30
EMPNO ENAME JOB MGR HIREDATE SAL COMM
---------- ---------- --------- ---------- --------- ---------- ----------
DEPTNO
----------
7782 CLARK MANAGER 7839 09-JUN-81 2451
10
7788 SCOTT ANALYST 7566 19-APR-87 3001
20
7839 KING PRESIDENT 17-NOV-81 5001
10
EMPNO ENAME JOB MGR HIREDATE SAL COMM
---------- ---------- --------- ---------- --------- ---------- ----------
DEPTNO
----------
7844 TURNER SALESMAN 7698 08-SEP-81 1501 0
30
7876 ADAMS CLERK 7788 23-MAY-87 1101
20
7900 JAMES CLERK 7698 03-DEC-81 951
30
EMPNO ENAME JOB MGR HIREDATE SAL COMM
---------- ---------- --------- ---------- --------- ---------- ----------
DEPTNO
----------
7902 FORD ANALYST 7566 03-DEC-81 3001
20
7934 MILLER CLERK 7782 23-JAN-82 1301
10
14 rows selected.
My guess is that you want something like this (obviously, since I don't have your tables or types, I can't test that this actually compiles so you'll likely need to correct typos)
CREATE OR REPLACE PACKAGE BODY RF_PKG_STFCA_PositivePay
AS
PROCEDURE RF_SP_STFCA_PositivePay(IN_DATE IN NUMBER, IN_DATE_OPERATOR IN NVARCHAR2, OUT_DATA OUT CUR_DATA)
IS
/* this procedure returns a Ref Cursor with all the requested parameters
calling the stored procedure from an asp page (and anywhere else)
does not require posting a predefined number of records */
l_sql_stmt VARCHAR2(4000);
BEGIN
l_sql_stmt := q'[SELECT LPAD(NVL(CD.PAYMENT_BATCH_NO, '0'), 4, '0') AS PaymentBatchNumber, ]' ||
q'[SUBSTR(NVL(CD.CURRENCY_ID, ' '), 1, 1) AS CurrencyCode, ]' ||
q'[NVL(CD.STATUS, ' ') AS TransactionCode, ]' ||
q'[LPAD(NVL(BA.BRANCH_ID, '0'), 5, '0') AS Transit_BranchNumber, ]' ||
q'[LPAD(NVL(BA.ACCOUNT_NO, '0'), 7, '0') AS BankAccountNumber, ]' ||
q'[LPAD(NVL(CD.CHECK_NO, '0') , 8, '0') AS ChequeNumber, ]' ||
q'[LPAD(TO_CHAR(NVL(CD.AMOUNT, 0)), 10, '0') AS ChequeAmount, ]' ||
q'[LPAD(NVL(CD.CONTROL_NO, '0'), 19, '0') AS ClientReference, ]' ||
q'[TO_CHAR(NVL(CD.CHECK_DATE, LPAD(' ', 8, ' ')), 'YYYYMMDD') AS IssueDate, ]' ||
q'[RPAD(NVL(CD.NAME, ' '), 60, ' ') AS PayeeName1, ]' ||
q'[RPAD(NVL(CD.ADDR_1, ' '), 60, ' ') AS AddressLine1, ]' ||
q'[RPAD(NVL(CD.ADDR_2, ' '), 60, ' ') AS AddressLine2, ]' ||
q'[RPAD(NVL(CD.CITY, '') || CASE WHEN CD.CITY IS NULL OR CD.STATE IS NULL THEN ' ' ELSE ', ' END || NVL(CD.STATE, ''), 60, ' ') AS AddressLine4, ]' ||
q'[RPAD(NVL(CD.ZIPCODE, ' '), 60, ' ') AS AddressLine5, ]' ||
q'[TO_CHAR(CD.CREATE_DATE, 'YYYYMMDDHH24MISS') AS DateCreated, ]' ||
q'[CASE WHEN CD.VOID_DATE IS NULL THEN ' ' ELSE TO_CHAR(CD.VOID_DATE, 'YYYYMMDDHH24MISS') END AS DateVoided ]' ||
q'[FROM BANK_ACCOUNT BA ]' ||
q'[INNER JOIN CASH_DISBURSEMENT CD ON BA.ID = CD.BANK_ACCOUNT_ID ]' ||
q'[WHERE BA.ACCOUNT_NO IS NOT NULL AND CD.CHECK_NO > 0 ]' ||
q'[AND CD.STATUS != 'X' ]' ||
q'[AND CD.AMOUNT != 0 ]' ||
q'[AND ((TO_NUMBER(TO_CHAR(CD.CREATE_DATE, 'YYYYMMDDHH24MISS'))]' || IN_DATE_OPERATOR || ':1') OR ' ||
q'[(CASE WHEN CD.VOID_DATE IS NULL THEN 0 ELSE TO_NUMBER(TO_CHAR(CD.VOID_DATE, 'YYYYMMDDHH24MISS')) END]' || IN_DATE_OPERATOR || ':2')) ' ||
q'[ORDER BY BA.BRANCH_ID, BA.ACCOUNT_NO ]';
OPEN out_data
FOR l_sql_stmt
USING in_date, in_date;
END RF_SP_STFCA_PositivePay;
END RF_PKG_STFCA_PositivePay;

SQLite/Postgres/Heroku: Problem translating query

Heroku throws an error on my Postgres-Query stating:
ActiveRecord::StatementInvalid
(PGError: ERROR: syntax error at or
near "date" 2011-05-03T13:58:22+00:00
app[web.1]: LINE 1: ...2011-05-31')
GROUP BY EXTRACT(YEAR FROM TIMESTAMP
date)||EXT...
The SQLite query in development works as expected. Here is the code:
def self.calculate(year, month, user_id, partner_id)
case ActiveRecord::Base.connection.adapter_name
when 'SQLite'
where(':user_id = entries.user_id OR :partner_id = entries.user_id', {
:user_id => user_id,
:partner_id => partner_id
}).
where('entries.date <= :last_day', {
:last_day => Date.new(year, month, 1).at_end_of_month
}).
select('entries.date, ' +
'sum(case when joint = "f" then amount_calc else 0 end) as sum_private, ' +
'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' +
'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +
'sum(case when compensation = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
'sum(case when compensation = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
).
group("strftime('%Y-%m', date)")
when 'PostgreSQL'
where(':user_id = entries.user_id OR :partner_id = entries.user_id', {
:user_id => user_id,
:partner_id => partner_id
}).
where('entries.date <= :last_day', {
:last_day => Date.new(year, month, 1).at_end_of_month
}).
select('entries.date, ' +
'sum(case when joint = "f" then amount_calc else 0 end) as sum_private, ' +
'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' +
'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +
'sum(case when compensation = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
'sum(case when compensation = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
).
group("EXTRACT(YEAR FROM TIMESTAMP date)||EXTRACT(MONTH FROM TIMESTAMP date)")
else
raise 'Query not implemented for this DB adapter'
end
end
I would really appreciate any hints. And as I am already asking a question here, I am uncertain about the case when joint = "t" in the sums in both queries too, is there a better way to do this?
UPDATE
Thanks to both peufeu and a horse with no name the code now looks like:
when 'PostgreSQL'
where(':user_id = entries.user_id OR :partner_id = entries.user_id', {
:user_id => user_id,
:partner_id => partner_id
}).
where('entries.date <= :last_day', {
:last_day => Date.new(year, month, 1).at_end_of_month
}).
select('min(entries.date) as date, ' +
'sum(case when joint = false then amount_calc else 0 end) as sum_private, ' +
'sum(case when joint = true and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' +
'sum(case when joint = true and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +
'sum(case when compensation = true and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
'sum(case when compensation = true and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
).
group('EXTRACT(YEAR FROM "date"), EXTRACT(MONTH FROM "date")')
...and works like expected.
Another of my statement runs into troubles now and I edit it here as it seems related to the answer of peufeu. Model/Controller:
def self.all_entries_month(year, month, user_id, partner_id)
mydate = Date.new(year, month, 1)
where(':user_id = entries.user_id OR (:partner_id = entries.user_id AND entries.joint = :true)', {
:user_id => user_id,
:partner_id => partner_id,
:true => true
}).
where(':first_day <= entries.date AND entries.date <= :last_day', {
:first_day => mydate,
:last_day => mydate.at_end_of_month
})
end
# group by tag and build sum of groups named group_sum
def self.group_by_tag
group('tag').
select('entries.*, sum(amount_calc) as group_sum')
end
controller:
#income = Entry.all_entries_month(#year, #month, current_user.id, current_partner.id).income
#cost = Entry.all_entries_month(#year, #month, current_user.id, current_partner.id).cost
# group cost by categories
#group_income = #income.group_by_tag.order('group_sum desc')
#group_cost = #cost.group_by_tag.order('group_sum')
The error is:
ActionView::Template::Error (PGError: ERROR: column "entries.id" must appear in the GROUP BY clause or be used in an aggregate function
2011-05-03T18:35:20+00:00 app[web.1]: : SELECT entries.*, sum(amount_calc) as group_sum FROM "entries" WHERE (1 = entries.user_id OR (2 = entries.user_id AND entries.joint = 't')) AND ('2011-04-01' <= entries.date AND entries.date <= '2011-04-30') AND (amount_calc <= 0 AND compensation = 'f') GROUP BY tag ORDER BY group_sum):
2011-05-03T18:35:20+00:00 app[web.1]: 6: </thead>
2011-05-03T18:35:20+00:00 app[web.1]: 7: <tbody>
2011-05-03T18:35:20+00:00 app[web.1]: 8: <% if categories %>
2011-05-03T18:35:20+00:00 app[web.1]: 9: <% categories.each do |category| %>
2011-05-03T18:35:20+00:00 app[web.1]: 10: <tr>
2011-05-03T18:35:20+00:00 app[web.1]: 11: <td class="align-left"><%= category.tag %></td>
2011-05-03T18:35:20+00:00 app[web.1]: 12: <td class="align-right"><%= my_number_to_percentage (category.group_sum.to_f / total_cost) * 100 %></td>
UPDATE 2: I found the solution
# group by tag and build sum of groups named group_sum
def self.group_by_tag
group('tag').
select('tag, sum(amount_calc) as group_sum')
end
Problem is there :
EXTRACT(YEAR FROM TIMESTAMP date)||EXTRACT(MONTH FROM TIMESTAMP date)
Solution :
EXTRACT(YEAR FROM "date"), EXTRACT(MONTH FROM "date")
The word "TIMESTAMP" is a bit out of place there ;) ... also :
Since DATE is a reserved SQL keyword, it is a very bad idea to use it as a column name. Here, I quoted it using " so postgres doesn''t get confused.
And you don't need to waste CPU time building a string concatenating your two EXTRACTs, just remember GROUP BY can use several parameters.
Then of course the query will fail because you SELECT "date" but your dont' GROUP BY date. postgres has no way to know which date you want from all the rows which have the same Year-Month. MySQL will return a value at random from the rows, postgres likes correctness so it will throw an error. You could SELECT min(date) for instance, which would be correct.
I am uncertain about the case when joint = "t"
That depends on how you want to get your results, nothing wrong there.
Maybe using several queries would scan a smaller portion of the table (I don't know about your dataset) or maybe not.
I don't know ruby/heroku, but the expression
joint = "t"
refers to a column t in PostgreSQL because object names are quoted with double quotes. So unless Ruby/Heroku is replacing those double quotes with single quotes that will be an invalid condition.
If t should be string literal (the character t) then you need to use single quotes: joint = 't'
If joint is of type boolean then you should use joint = true in PostgreSQL

Resources