cannot open SELECT query as cursor - postgresql

I am trying to construct a string from a table. I need to construct a string from each row and then concatenate these strings (with an appropriate separator) together. In a simple example the select produces a table with 3 rows; however, I get the message above.
Can anyone help? Thanks
for mFiles in
select url || pth || basename || '/' || basename || '.2.' || clientname into fName from files
where ( userId is null or uid != userId ) and (url is not null) and (pth is not null)
order by RANDOM() limit nImages LOOP
res := res || fName || '|';
RAISE NOTICE ' fName: % ' , fName;
END LOOP;

The problem was 'into fName'. Replacing this by 'as fName' and writing
res:= res || mFiles.fName || '|';
fixed the problem. Don't understand why exactly but ...

Following from Craig Ringer's suggestion, what if you remove the limit clause and instead break out of the loop with code like this (which would both test whether limit is the culprit and provide a good workaround):
idx := 1
for mFiles in
select url || pth || basename || '/' || basename || '.2.' || clientname into fName from files
where ( userId is null or uid != userId ) and (url is not null) and (pth is not null)
order by RANDOM()
LOOP
res := res || fName || '|';
RAISE NOTICE ' fName: % ' , fName;
IF idx = nImages THEN
EXIT
END IF;
END LOOP;
With the addition of declaring idx in the appropriate section higher up in the code.

Related

PL/SQL: how to convert to 'ddmmyyyy'

I can't seem to make the to_date/to_char (var, 'ddmmyyyy') to work. How do I get it in this format. I've tried to change the select as the output, all giving me errors.
Any help much appreciated!
DECLARE
auditnr NUMBER(7) := 186725;
pap_combinatie NUMBER(3) := 986;
gids VARCHAR(240) := 'G-040V.5';
begindatum date;
einddatum date;
maxeinddatum date;
operatorid number;
BEGIN
SELECT
-- Tried: to_date(MAX(aa.datum_begin_validatie),'ddmmyyyy'),
-- Tried: to_char(MAX(aa.datum_begin_validatie),'ddmmyyyy'),
MAX(aa.datum_begin_validatie),
max(aa.datum_eind_validatie),
max(aa.datum_eind_max),
max(o.operator_id)
INTO begindatum, einddatum, maxeinddatum, operatorid
FROM autocontrole2.activiteit_audit aa,
autocontrole2.pap_operator o
WHERE aa.pap_operator_id = o.pap_operator_id
AND aa.audit_oci_id = auditnr;
DBMS_OUTPUT.PUT_LINE(begindatum || ',' || einddatum || ',' || maxeinddatum || ',' || operatorid);
-- Tried: DBMS_OUTPUT.PUT_LINE(to_date(begindatum,'ddmmyyyy') || ',' || einddatum || ',' || maxeinddatum || ',' || operatorid);
-- Tried: DBMS_OUTPUT.PUT_LINE(to_char(begindatum,'ddmmyyyy') || ',' || einddatum || ',' || maxeinddatum || ',' || operatorid);
END;
Everything is giving me errors.
The OUTPUT is: 22-NOV-20,21-NOV-23,21-NOV-23,4775291
The output I want: 22112020, 21112023, 21112023, 4775291
To convert datatype DATE to VARCHAR2, use TO_CHAR. TO_DATE is used to convert a string (VARCHAR2) to a DATE.
So in your code that would become:
DBMS_OUTPUT.PUT_LINE(TO_CHAR(begindatum, 'DDMMYYYY')||...

Postgresql: EXECUTE sql_cmd merge with CREATE TEMP TABLE temp_tbl AS SELECT

Soo, here is the thing, I am using in my DB methods 2 approaches:
1.) Is compose and SQL query from various string, depending on what I need to filter out:
sql_cmd := 'SELECT count(*) FROM art_short_term_finished WHERE (entry_time <= ''' || timestamp_up || ''' AND exit_time >= ''' || timestamp_down || ''') AND ' || time_filter || ' AND entry_zone = ' || zone_parameter || ' AND park_uuid = ' || park_id_p || '';
EXECUTE sql_cmd INTO shortterm_counter;
2.) Copy part of the big table, into smaller temp table and work with it:
-- Get the data from FPL into smaller table for processing
DROP TABLE IF EXISTS temp_fpl_filtered;
CREATE TEMP TABLE temp_fpl_filtered AS SELECT car_id FROM flexcore_passing_log fpl WHERE fpl.zone_leaved = '0' AND fpl.status IN (SELECT status_id FROM fpl_ok_statuses) AND fpl.park_uuid = park_id_p AND (fpl.datetime BETWEEN row_i.start_d AND row_i.end_d);
But what If I want to mix those two?
I want to have the SELECT after CREATE TEMP TABLE temp_fpl_filtered AS to have different WHERE clauses depending on input parameters of stored procedure, without having to write the same statement xy times in one stored procedure.
But my approach:
-- art class is shortterm, check shortterm history
IF art_class_p = 1 OR article_p = 0 THEN
-- create temporary table derivated from shortterm history
IF article_p = 0 THEN
article_p_filter := '';
ELSE
article_p_filter := ' AND article_id = ' || article_p;
END IF;
short_cmd := 'SELECT car_id, article_id, entry_time, exit_time FROM art_short_term_finished WHERE zone_leaved = ''0'' AND status IN (SELECT status_id FROM fpl_ok_statuses) ''' || article_p_filter || ''' AND park_uuid = ''' || park_id_p || ''' AND (entry_time <= ''' || timestamp_up || ''' AND exit_time >= ''' || timestamp_down || ''')';
DROP TABLE IF EXISTS temp_short_full;
CREATE TEMP TABLE temp_short_full AS short_cmd;
--EXECUTE sql_cmd INTO shortterm_counter;
END IF;
throws me an exception when I try to inser stored procedure:
psql:report_parking_average.sql:107: ERROR: syntax error at or near "short_cmd"
LINE 50: CREATE TEMP TABLE temp_fpl_filtered AS short_cmd;
^
Also, the another try:
EXECUTE short_cmd INTO TEMP TABLE temp_short_full;
is not working..
You need to include the CREATE TABLE part into the SQL you generate:
short_cmd := 'CREATE TEMP TABLE temp_short_full AS SELECT car_id, article_id, entry_time, exit_time FROM art_short_term_finished WHERE zone_leaved = ''0'' AND status IN (SELECT status_id FROM fpl_ok_statuses) ''' || article_p_filter || ''' AND park_uuid = ''' || park_id_p || ''' AND (entry_time <= ''' || timestamp_up || ''' AND exit_time >= ''' || timestamp_down || ''')';
DROP TABLE IF EXISTS temp_short_full;
execute short_cmd;

Concat text variables in postgresql function

I have this code and I want to concatenate the variables but don't work.
This is my DDL code for the view:
CREATE OR REPLACE function acd.add_credito2()
RETURNS void
SET SCHEMA 'acd'
SET search_path = acd
AS $$
DECLARE
auxsigla text;
auxnome text;
_sql text := 'CREATE OR REPLACE VIEW acd.teste AS SELECT md.matriz_disciplina_id AS id, dcp.nome, mc.curso, mc.versao AS matriz';
_join text := ' FROM matriz_disciplina as md LEFT JOIN disciplina as dcp ON md.disciplina_id = dcp.disciplina_id LEFT JOIN matriz_curricular as mc ON md.matriz_curricular_id = mc.matriz_curricular_id';
BEGIN
select into auxsigla, auxnome from ( select sigla, nome from acd.categoria_credito where categoria_credito_id = 9) as foo;
_join := _join || ' LEFT JOIN (SELECT creditos, matriz_disciplina_id FROM acd.disciplina_credito WHERE categoria_credito_id = ' || x || ') AS ' || "auxsigla" ' ON ' || "auxsigla" || '.matriz_disciplina_id = md.matriz_disciplina_id';
_sql := _sql || ', ' || "auxsigla" || '.' || auxnome || ' AS ' || auxnome;
_sql := _sql || _join;
EXECUTE _sql;
END;
$$ LANGUAGE plpgsql
So, when I execute the function
database-1=# select acd.add_credito2();
This error appears:
ERROR: type "auxsigla" does not exist
LINE 1: ...WHERE categoria_credito_id = ' || x || ') AS ' || "auxsigla"...
^
QUERY: SELECT _join || ' LEFT JOIN (SELECT creditos, matriz_disciplina_id FROM acd.disciplina_credito WHERE categoria_credito_id = ' || x || ') AS ' || "auxsigla" ' ON ' || "auxsigla" || '.matriz_disciplina_id = md.matriz_disciplina_id'
CONTEXT: PL/pgSQL function add_credito2() line 13 at assignment
Can anyone help me? I don't know what to do now.
(I know, this study view don't have a purpose but this is the idea that I want to use in the real view)
The error comes from this construct:
"auxsigla" ' ON '
You forgot a concatenation operator || between these two tokens, and now the SQL parser interprets it as
data_type string_constant
which is a way to specify constants of a certain data type.
Working examples would be DATE '2018-09-20' or INTEGER '-20'.
Your function has numerous other problems, two of which I could spot:
select into auxsigla, auxnome from will always set the variables to NULL because you forgot to specify which columns you want to select.
you do not properly escape single quotes while composing your dynamic query string. What if auxsigla has the value with'quote?
Use format() or quote_literal() and quote_ident() for that.

PL/SQL Error Happening

Select * from cat;
Attempting this
You double-fetch your cursor:
Your problem
[...]
OPEN UPDATETRIGGER;
FOR UT IN UPDATETRIGGER -- This performs fetching into UT
LOOP
FETCH UPDATETRIGGER -- Here You Fetch again..
[...]
What your loop should look like
CREATE OR REPLACE TRIGGER Display_Update_Message
BEFORE UPDATE
ON JOBS
FOR EACH ROW
WHEN ( (old.IsFilled != new.IsFilled) AND (new.isFilled = 'yes'))
DECLARE
CURSOR UPDATETRIGGER
IS
SELECT J.JobID JobID,
J.JobName JobName,
J.StopDate StopDate,
JS.LastName LastName,
JS.FirstName FirstName,
JS.Email Email
FROM JOBS J
FULL OUTER JOIN JOBAPPLICATIONS JA ON J.JobID = JA.JobID
FULL OUTER JOIN JOBSEEKERS JS ON JA.JSID = JS.JSID
WHERE J.JobID = :new.JobID;
JobID NUMBER (3);
JobName CHAR (30);
LastName CHAR (15);
FirstName CHAR (15);
Email CHAR (30);
StopDate DATE;
BEGIN
DBMS_OUTPUT.PUT_LINE (
'Seekers affected by closing job ' || JobID || ': ' || :new.JobName);
OPEN UPDATETRIGGER;
LOOP -- infinite loop
FETCH UPDATETRIGGER
INTO JobID,
JobName,
LastName,
FirstName,
Email,
StopDate;
EXIT WHEN UPDATETRIGGER%NOTFOUND; -- loop-breaker
:new.StopDate := SYSDATE;
DBMS_OUTPUT.PUT_LINE (
'--' || UT.LastName || ', ' || UT.FirstName || ' ' || UT.Email);
END LOOP;
CLOSE UPDATETRIGGER;
END;
You do not need to include the JOB table in the query. You have the information you require in the :NEW namespace. Not including the JOB table in the query will stop the ORA-04091 error.
Also you have two sets of cursor control statements. Choose one. I prefer the implicit cursor syntax because it is less typing (and marginally more efficient).
CREATE OR REPLACE TRIGGER Display_Update_Message
BEFORE UPDATE ON JOBS
FOR EACH ROW WHEN ((old.IsFilled != new.IsFilled) AND (new.isFilled = 'yes'))
BEGIN
DBMS_OUTPUT.PUT_LINE('Seekers affected by closing job '
|| :new.JobID || ': ' || :new.JobName);
FOR UT IN (SELECT JS.LastName
, JS.FirstName
, JS.Email Email
FROM JOBAPPLICATIONS JA
FULL OUTER JOIN JOBSEEKERS JS ON JA.JSID = JS.JSID
WHERE JA.JobID = :new.JobID )
LOOP
DBMS_OUTPUT.PUT_LINE('--' || UT.LastName || ', ' || UT.FirstName || ' ' || UT.Email);
END LOOP;
:new.StopDate := sysdate;
END;
/
Incidentally, I'm not sure why you have FULL OUTER JOIN in your cursor. I would have thought INNER JOIN was the correct solution. Surely you only want Job Seekers who have applied for the job you're closing? However, I have left that in because I don't know your business rules, and anyway I've changed enough of your code already :)

How can I measure the amount of space taken by blobs on a Firebird 2.1 database?

I have a production database, using Firebird 2.1, where I need to find out how much space is used by each table, including the blobs. The blob-part is the tricky one, because it is not covered using the standard statistical report.
I do not have easy access to the server's desktop, so installing UDFs etc. is not a good solution.
How can I do this easily?
You can count total size of all BLOB fields in a database with following statement:
EXECUTE BLOCK RETURNS (BLOB_SIZE BIGINT)
AS
DECLARE VARIABLE RN CHAR(31) CHARACTER SET UNICODE_FSS;
DECLARE VARIABLE FN CHAR(31) CHARACTER SET UNICODE_FSS;
DECLARE VARIABLE S BIGINT;
BEGIN
BLOB_SIZE = 0;
FOR
SELECT r.rdb$relation_name, r.rdb$field_name
FROM rdb$relation_fields r JOIN rdb$fields f
ON r.rdb$field_source = f.rdb$field_name
WHERE f.rdb$field_type = 261
INTO :RN, :FN
DO BEGIN
EXECUTE STATEMENT
'SELECT SUM(OCTET_LENGTH(' || :FN || ')) FROM ' || :RN ||
' WHERE NOT ' || :FN || ' IS NULL'
INTO :S;
BLOB_SIZE = :BLOB_SIZE + COALESCE(:S, 0);
END
SUSPEND;
END
I modified the code example of Andrej to show the size of each blob field, not only the sum of all blobs.
And used SET TERM so you can copy&paste this snippet directly to tools like FlameRobin.
SET TERM #;
EXECUTE BLOCK
RETURNS (BLOB_SIZE BIGINT, TABLENAME CHAR(31), FIELDNAME CHAR(31) )
AS
DECLARE VARIABLE RN CHAR(31) CHARACTER SET UNICODE_FSS;
DECLARE VARIABLE FN CHAR(31) CHARACTER SET UNICODE_FSS;
DECLARE VARIABLE S BIGINT;
BEGIN
BLOB_SIZE = 0;
FOR
SELECT r.rdb$relation_name, r.rdb$field_name
FROM rdb$relation_fields r JOIN rdb$fields f
ON r.rdb$field_source = f.rdb$field_name
WHERE f.rdb$field_type = 261
INTO :RN, :FN
DO BEGIN
EXECUTE STATEMENT
'SELECT SUM(OCTET_LENGTH(' || :FN || ')) AS BLOB_SIZE, ''' || :RN || ''', ''' || :FN || '''
FROM ' || :RN ||
' WHERE NOT ' || :FN || ' IS NULL'
INTO :BLOB_SIZE, :TABLENAME, :FIELDNAME;
SUSPEND;
END
END
#
SET TERM ;#
This example doesn't work with ORDER BY, maybe a more elegant solution without EXECUTE BLOCK exists.