if-statement with string containing the condition - sql

This question is about Postgresql 8.3.
I've got a table with a field containing conditions like 'lastcontact is null'. In code, I want to loop through this table and for each record, I want to check 'if condition then', like in this example:
FOR myrec IN
SELECT * FROM tabel ORDER BY colorlevel, volgnummer
LOOP
if (myrec.conditie) then
raise notice 'Condition % is true', myrec.conditie;
else
raise notice 'Condition % is false', myrec.conditie;
end if;
END LOOP;
The table which I have called 'tabel' in this example:
ID | Conditie | Colorlevel | Volgnummer | Code | Description
1 | lastcontact is null | 1 | 1 | ... | ...
2 | lastchanged is null | 1 | 2 | ... | ...
3 | lastmodified is null | 1 | 3 | ... | ...
Is it possible to do the check I desire? The code above results in the following error:
ERROR: invalid input syntax for type boolean: "lastcontact is null"
New section containing the result of Erwin's function
I have used this function:
CREATE OR REPLACE FUNCTION foo(lastcontact timestamptz)
RETURNS void AS
$BODY$
DECLARE
myrec record;
mycond boolean;
BEGIN
FOR myrec IN
SELECT * FROM tabel ORDER BY colorlevel, volgnummer
LOOP
EXECUTE 'SELECT ' || myrec.conditie || ' FROM tabel' INTO mycond;
IF mycond then
RAISE NOTICE 'Condition % is true', myrec.conditie;
ELSE
RAISE NOTICE 'Condition % is false', COALESCE(myrec.conditie, 'NULL');
END IF;
END LOOP;
END;
$BODY$
language 'plpgsql' volatile
cost 100;
I get this error:
ERROR: column "lastcontact" does not exist
LINE 1: SELECT lastcontact is null FROM tabel
^
QUERY: SELECT lastcontact is null FROM tabel
CONTEXT: PL/pgSQL function "foo" line 9 at EXECUTE statement1
I tried to find an explanation myself, but to no avail. It obviously is trying to run the statement against the database, but it should understand that 'lastcontact' is the variable that's been given as a function parameter.

From the comments I finally think I understand. You need dynamic SQL:
CREATE OR REPLACE FUNCTION foo(lastcontact timestamptz)
RETURNS void AS
$func$
DECLARE
myrec record;
mycond boolean;
BEGIN
FOR myrec IN
SELECT * FROM tabel ORDER BY colorlevel, volgnummer
LOOP
IF myrec.conditie ~~ '%lastcontact %' THEN -- special case for input param
myrec.conditie := replace (myrec.conditie
, 'lastcontact '
, CASE WHEN lastcontact IS NULL THEN 'NULL '
ELSE '''' || lastcontact::text || ''' ' END);
END IF;
EXECUTE 'SELECT ' || myrec.conditie || ' FROM tabel' INTO mycond;
IF mycond then
RAISE NOTICE 'Condition % is true', myrec.conditie;
ELSE
RAISE NOTICE 'Condition % is false', COALESCE(myrec.conditie, 'NULL');
END IF;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Be aware, however, that this setup is wide open for SQL injection. Only use verified input.
A function works in PostgreSQL 8.3 as well (no DO statements, yet).
You cannot refer to parameters inside dynamic SQL (EXECUTE statement). You have to put the value into the query string.
In PostgreSQL 8.4 or later you have the superior commodity of the USING clause. Alas, not in version 8.3. You should consider upgrading if you can.
I put in a workaround for your old version. You have to take special care of the NULL value.

Related

adding to variables inside of a oracle procedure

I created a procedure that takes the account number of a user and it returns every transaction they've made plus their running balance. everything works except the balance. The code compiles and runs but nothing shows up in the running balance column of the dbms.output
CREATE OR REPLACE PROCEDURE print_dett(
in_account_nbr NUMBER
)
IS
dCount NUMBER;
BEGIN
dbms_output.put_line(' Date : transaction amount : balance - For: ' || in_account_nbr);
FOR r IN(
SELECT t.tx_nbr, t.tx_date, t.tx_amount, t.tx_type_code, a.balance
FROM transaction t, account a
WHERE t.account_nbr = a.account_nbr
AND t.account_nbr = in_account_nbr
ORDER BY tx_date)
LOOP
IF r.tx_type_code = 'D' OR r.tx_type_code = 'R' THEN
dCount := dCount + r.tx_amount;
ELSE
dCount := dCount - r.tx_amount;
END IF;
dbms_output.put_line(r.tx_date || ' : ' || r.tx_amount || ' : ' || dCount );
END LOOP;
END;
What can I change in the code so the running balance will actually show up when printed?
Number variables get initialized with NULL. Your variable dCount is hence null first and adding values doesn't change this.
Initialize it with zero instead:
CREATE OR REPLACE PROCEDURE print_dett(
in_account_nbr NUMBER
)
IS
dCount NUMBER := 0;
...

PLS-00103: Encountered the symbol "=" when expecting one of the following.... in PL/SQL script

create or replace function lstnation (listdisplay in varchar2)
return varchar2 is
nName varchar2 (1000) default null;
listD varchar2(1000) default null;
cursor display_nation
is
select nation.n_name
from nation
inner join region
on region.r_regionkey = nation.n_nationkey
where region.r_regionname = listdisplay;
BEGIN
open display_nation;
loop
fetch display_nation into nName;
exit when display_nation%notfound;
IF
listD := listD || RTRIM(nName)||' , ';
end loop;
close display_nation;
return listD;
end lstnation;
/
DECLARE
rKey region.r_regionkey%type;
rName region.r_name%type;
nList varchar2(1000);
cursor outer_block is
select region.r_regionkey, region.r_name, lstnation(region.r_name)
from region;
BEGIN
open outer_block;
loop
fetch outer_block into rKey, rName, nList;
exit when outer_block%notfound;
dbms.output.put_line(rkey || ' ' || RTRIM(rName) || ': '|| nList);
end loop;
close outer_block;
end;
/
I get two errors, how can I fix it
LINE/COL ERROR
19/12 PLS-00103: Encountered the symbol "=" when expecting one of the
following:
. ( * # % & = - + < / > at in is mod remainder not rem then
<an exponent (**)> <> or != or ~= >= <= <> and or like like2
like4 likec between || multiset member submultiset
20/2 PLS-00103: Encountered the symbol "END" when expecting one of the
following:
begin function pragma procedure subtype type
current cursor delete
exists prior
You can save some coding and efficiency by replacing the cursor loop with the listagg function
select listagg(rtrim(nation.n_name),',')
from nation
inner join region
on region.r_regionkey = nation.n_nationkey
where region.r_regionname = listdisplay;
So that will collate all the matching rows, and use whatever delimiter is passed in. One thing to be aware of, you have listD varchar2(1000) so as long as the results from the query are less than 1000, you are OK. If you expect a larger result set, you may need to increase or use a clob.
If for some reason, you still want to use the loop method, then you need to fix your IF statement:
loop
fetch display_nation into nName;
exit when display_nation%notfound;
IF <condition> THEN
listD := listD || RTRIM(nName)||' , ';
END IF;
end loop;

PL/SQL: Cursor doubles the last record from the table

I created the following PL/SQL anonymous block. The cursor below retrieves data from the select statement:
select mod_benutzer, count(*)
from dok_auspraegung
where parent_objekt_id = 1093
group by mod_benutzer;
This statement displays exactly two records:
DDMS_USER | 8
HU2MAMU | 14
But when I want to display these two records by cursor, it displays "HU2MAMU|14" two times like below:
Modifications:
DDMS_USER, 8x
HU2MAMU, 14x
HU2MAMU, 14x
declare
my_exception_1 exception;
var_parent_objekt_id dok_auspraegung.parent_objekt_id%TYPE := 1093;
var_date varchar(30);
var_mod_benutzer varchar2(10);
var_benutzer_modifs number;
cursor cursor_dok_auspraegung
is select mod_benutzer, count(*) from dok_auspraegung
where parent_objekt_id = 10935797565
group by mod_benutzer;
begin
select distinct to_char(mod_datum,'YYYY-MON-DD') into var_date from dok_auspraegung where parent_objekt_id = var_parent_objekt_id;
IF var_date is not null THEN
dbms_output.put_line('Parent Object ID' || ': ' || var_parent_objekt_id);
dbms_output.put_line('Date: ' || ' ' || var_date);
ELSE RAISE my_exception_1;
END IF;
open cursor_dok_auspraegung;
dbms_output.put_line('Modifications:');
loop
fetch cursor_dok_auspraegung into var_mod_benutzer, var_benutzer_modifs;
dbms_output.put(var_mod_benutzer);
dbms_output.put_line(', ' || var_benutzer_modifs || 'x');
exit when cursor_dok_auspraegung%notfound;
end loop;
dbms_output.put_line(cursor_dok_auspraegung%rowcount);
close cursor_dok_auspraegung;
exception
when NO_DATA_FOUND then
dbms_output.put_line('Parent Object ID not found!');
when my_exception_1 then
dbms_output.put_line('');
end;
What is the reason of that?
Because exiting from the cursor occurs after printing the value of the variables in the current case, this repeats the last value to be printed. So, it should occur before printing as follows
loop
fetch cursor_dok_auspraegung into var_mod_benutzer, var_benutzer_modifs;
exit when cursor_dok_auspraegung%notfound;
dbms_output.put(var_mod_benutzer);
dbms_output.put_line(', ' || var_benutzer_modifs || 'x');
end loop;

PostgreSQL how to disable plan caching to fix my base62 hashing?

From the documentation i gather, that PostgreSQL somehow employs its own cache and prepares satements ahead...
This is really bad for my base62 hash values. At some point, after 2-3 tries, they start returning the same number:
LOG: base62_id.val 1501145675089
CONTEXT: PL/pgSQL function copy_article(text) line 23 at RAISE
STATEMENT: select to_json("public"."copy_article"($1)) as value
LOG: copied_article_id QQZCFzm | article_count 1
CONTEXT: PL/pgSQL function copy_article(text) line 37 at RAISE
STATEMENT: select to_json("public"."copy_article"($1)) as value
LOG: base62_id.val 1501145675089
CONTEXT: PL/pgSQL function copy_article(text) line 23 at RAISE
STATEMENT: select to_json("public"."copy_article"($1)) as value
LOG: copied_article_id QQZCFzm | article_count 1
CONTEXT: PL/pgSQL function copy_article(text) line 37 at RAISE
STATEMENT: select to_json("public"."copy_article"($1)) as value
LOG: base62_id.val 1501145675089
CONTEXT: PL/pgSQL function copy_article(text) line 23 at RAISE
STATEMENT: select to_json("public"."copy_article"($1)) as value
LOG: copied_article_id QQZCFzm | article_count 1
CONTEXT: PL/pgSQL function copy_article(text) line 37 at RAISE
STATEMENT: select to_json("public"."copy_article"($1)) as value
LOG: base62_id.val 1501145675089
Here is my function:
CREATE OR REPLACE FUNCTION base62_id() RETURNS character varying
LANGUAGE plpgsql IMMUTABLE
AS $$
DECLARE
chars char[];
ret varchar;
val bigint;
BEGIN
chars := ARRAY['0','1','2','3','4','5','6','7','8','9'
,'A','B','C','D','E','F','G','H','I','J','K','L','M'
,'N','O','P','Q','R','S','T','U','V','W','X','Y','Z'
,'a','b','c','d','e','f','g','h','i','j','k','l','m'
,'n','o','p','q','r','s','t','u','v','w','x','y','z'];
val := (CEIL(EXTRACT(EPOCH FROM now()) * 1000))::bigint;
RAISE LOG 'base62_id.val %', val;
ret := '';
IF val < 0 THEN
val := val * -1;
END IF;
WHILE val != 0 LOOP
ret := chars[(val % 62)+1] || ret;
val := val / 62;
END LOOP;
RETURN ret;
END;$$;
In theory, this should work...
Any ideas?
Edit: How i use the function:
DECLARE
copied_article_id text := base62_id();
duplication_check int := 1;
copied_article articles;
BEGIN
WHILE duplication_check IS NOT NULL LOOP
SELECT COUNT(*) INTO duplication_check FROM articles WHERE id = copied_article_id;
END LOOP;
INSERT ... INTO ... SELECT ...
FROM
articles
WHERE
id = base_id;
SELECT * INTO copied_article FROM articles WHERE id = copied_article_id LIMIT 1;
Your explanation is pretty bogus. Plan caching has nothing to do with this outcome, the plancache doesn't care about individual function outputs.
There's at least two giant gaping bugs in the function:
You declare it IMMUTABLE but you call now() which is not immutable. IMMUTABLE functions must return the same result for every call with the same inputs. In fact, your function must be declared VOLATILE if it's allowed to return a different value for each call with the same inputs.
now() is STABLE. It actually returns the same value for each call within a transaction. So presumably using it when you want unique values makes no sense at all. I imagine you actually want clock_timestamp().
The latter problem with now() (a.k.a. current_timestamp) being STABLE across a transaction is likely the cause for the results you report.
BTW, the function will also probably be amazingly slow implemented in plpgsql. If you can turn it into a set operation plus string_agg it might be more tolerable, but still slow.

Postgresql function auto add 's' character to the end of string

I have a function in PostgreSQL database. However, every time I run the function in PostgreSQL, it auto-adda the character s behind of the query, therefore, I receive an error when it executes.
An example query ends up looking like this:
WHAT IN HERE: query SELECT uom_id FROM ps_quantity where id = 11s
My version is PostgreSQL 9.2.13. How can I solve this? Thanks.
CREATE OR REPLACE FUNCTION select_field(
selected_table text,
selected_field text,
field_type_sample anyelement,
where_clause text DEFAULT ''::text)
RETURNS anyelement AS
$BODY$ DECLARE
-- Log
ME constant text := 'selected_field()';
-- Local variables
_qry varchar := '';
_result_value ALIAS FOR $0;
where_clause1 varchar := 'asdasdsad';
BEGIN
RAISE NOTICE 'FUNCTION: SELECT_FIELD';
-- BANGPH
RAISE NOTICE 'BANGPH - CHANGE 11s to 11';
_qry := 'SELECT uom_id FROM ps_quantity where id = 11';
RAISE NOTICE 'WHERE = %s', where_clause1;
RAISE NOTICE 'WHAT IN HERE: query %s', _qry;
As the manual explains:
instead of:
RAISE NOTICE 'WHAT IN HERE: query %s', _qry;
you need to use:
RAISE NOTICE 'WHAT IN HERE: query %', _qry;
Placeholders for the RAISE statement don't have a "type modifier", it's a plain %