Postgres database "Query has no destination for result data " error - postgresql

I am trying to convert MSSQL queries to POSTGRES queries.
I am not able to execute the below query in Postgres
DO $$
BEGIN
IF EXISTS (SELECT ID FROM PROCESS WHERE ID = (SELECT MAX(ID) FROM PROCESS WHERE NAME = 'TRANSACTION')) THEN
SELECT * FROM MSG
WHERE msg_timestamp >= ( SELECT start_time FROM PROCESS WHERE NAME = 'TRANSACTION' AND STATUS = 'STARTED');
ELSE
SELECT * FROM MSG;
END IF;
END $$;
ERROR:
query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function inline_code_block line 7 at SQL statement
FCMDBPOSTGRES=#
My corresponding MSSQL query is as below which works fine
IF EXISTS (SELECT ID FROM PROCESS WHERE ID = (SELECT MAX(ID) FROM PROCESS WHERE NAME = 'TRANSACTION'))
BEGIN
SELECT * FROM MSG
WHERE msg_timestamp >= ( SELECT start_time FROM PROCESS WHERE NAME = 'TRANSACTION' AND STATUS = 'STARTED')
END
ELSE
BEGIN
SELECT * FROM MSG
END

You do not need the do statement for this, just plain SQL:
SELECT * FROM MSG
WHERE
NOT EXISTS (SELECT ID FROM PROCESS WHERE ID = (SELECT MAX(ID) FROM PROCESS WHERE NAME = 'TRANSACTION'))
OR msg_timestamp >= (SELECT start_time FROM PROCESS WHERE NAME = 'TRANSACTION' AND STATUS = 'STARTED');
Or to be more verbose:
SELECT * FROM MSG
WHERE
CASE
WHEN EXISTS (SELECT ID FROM PROCESS WHERE ID = (SELECT MAX(ID) FROM PROCESS WHERE NAME = 'TRANSACTION'))
THEN msg_timestamp >= (SELECT start_time FROM PROCESS WHERE NAME = 'TRANSACTION' AND STATUS = 'STARTED')
ELSE TRUE
END;
Or to be more simple:
SELECT * FROM MSG
WHERE
msg_timestamp >= coalesce(
(SELECT start_time FROM PROCESS WHERE NAME = 'TRANSACTION' AND STATUS = 'STARTED'),
'-infinity');
BTW If I understand correctly
EXISTS (SELECT ID FROM PROCESS WHERE ID = (SELECT MAX(ID) FROM PROCESS WHERE NAME = 'TRANSACTION'))
could be simplified to
EXISTS (SELECT 1 FROM PROCESS WHERE NAME = 'TRANSACTION')

It cannot to work. Postgres doesn't support free queries in procedures or scripts. Every result of SQL statement should be saved to variable (or returned as result of table function). More DO statement has not any input output mechanism. You can write table function:
CREATE OR REPLACE FUNCTION fx()
RETURNS SETOF MSG AS $$
BEGIN
IF EXISTS (SELECT ID FROM PROCESS
WHERE ID = (SELECT MAX(ID) FROM PROCESS
WHERE NAME = 'TRANSACTION'))
THEN
RETURN QUERY SELECT * FROM MSG
WHERE msg_timestamp >= ( SELECT start_time FROM PROCESS
WHERE NAME = 'TRANSACTION' AND STATUS = 'STARTED');
ELSE
RETURN QUERY SELECT * FROM MSG;
END IF;
END
$$ LANGUAGE plpgsql;
SELECT * FROM fx();
Note: if you have experience of MSSQL procedures, then the best start for work with Postgres is an reading of documentation - https://www.postgresql.org/docs/current/plpgsql.html . Lot of things are very very different. The stored procedures in Postgres are similar to Oracle, but very far to MSSQL.

Related

How to create with condition and select?

I want to do something like this :
Someone creates a customer and if the ct already exist so we check if the cn is the same and if it's not the same we raise an error but it' doesn't work and take a lot of time.
CREATE OR REPLACE FUNCTION existingCT()
RETURNS trigger AS $$ BEGIN
IF ((SELECT COUNT(*) FROM customer WHERE ct= NEW.ct)!= 0) THEN 
IF( (SELECT COUNT(*) FROM customer WHERE ct= NEW.ct) != (SELECT count(*) FROM customer WHERE ct= NEW.ct AND cn= NEW.cn)) THEN
RAISE EXCEPTION 'This ct already exist for a cn';
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql ;
You can look for a customer with the same ct and a different cn in one query:
IF EXISTS (SELECT * FROM customer WHERE ct = NEW.ct AND cn <> NEW.cn) THEN
A thrown exception should abort the INSERT for any type of trigger. But given that it's a check, I'd create the trigger as BEFORE INSERT.

How to check whether a stored procedure exists on PostgreSQL?

I'm pretty new to PostgreSQL and trying to learn PostgreSQL with the knowledge that I have on MS SQL Server & Oracle. I am looking for an equivalent of the following statement that we can use in MS SQL Server to check whether a Stored procedure exists or not, in PostgreSQL where SPName is your stored procedure's name.
SELECT 1 FROM sys.procedures WHERE Name = 'SPName')
SELECT 1 FROM sys.procedures WHERE object_id = OBJECT_ID(N'dbo.SPName')
SELECT EXISTS (
SELECT *
FROM pg_catalog.pg_proc
JOIN pg_namespace ON pg_catalog.pg_proc.pronamespace = pg_namespace.oid
WHERE proname = 'proc_name'
AND pg_namespace.nspname = 'schema_name'
)
If you've not created a specific schema then use public(pg_namespace.nspname = 'public')
OR
You can create a custom function to do the task like below:
create or replace function function_exists (sch text,fun text) returns boolean as
$$
begin
EXECUTE 'select pg_get_functiondef('''||sch||'.'||fun||'''::regprocedure)';
return true;
exception when others then
return false;
end;
$$ language plpgsql
and use :
select function_exists('public','function_name()')
Try like this:
SELECT EXISTS (
SELECT 1
FROM pg_proc JOIN pg_namespace on pg_proc.oid = pg_namespace.oid
WHERE pg_proc.proname = 'procedure name'
and pg_namespace.nspname = 'name' );
One-liner:
SELECT to_regproc('schema_name.proc_name') IS NOT NULL

Error: a column definition list is required for functions in dblink using PostgreSQL 9.3

I have the following function:
In which I am updating one database table by joining other database table by using the dblink().
I have installed:
create extension dblink;
The more details as shown below:
CREATE OR REPLACE FUNCTION Fun_test
(
Table_Name varchar
)
RETURNS void AS
$BODY$
DECLARE
dynamic_statement varchar;
BEGIN
perform dblink_connect('port=5234 dbname=testdb user=postgres password=****');
dynamic_statement := 'With CTE AS
(
Select HNumber,JoiningDate,Name,Address
From '|| Table_Name ||'c
)
, Test_A
AS
(
Select Row_Number() over ( Partition by PNumber order by Date1 Desc,Date2 Desc) AS roNum,
Name,PNumber,Date1,Address
From dblink(
''Select distinct PNumber,
(
case when fname is null then '' else fname end || '' ||
case when lname is null then '' else lname end
) as FullName,
Address,
Date1,Date2
From testdb_Table
inner join CTE on CTE.HNumber = PNumber''
) Num
)
Update CTE
Set
Name = Test_A.FullName
,SubAddress_A = Test_A.Address
,Date1 = Test_A.Date1
from CTE
left outer join Test_A on
CTE.HNumber= Test_A.PNumber
where roNum =1';
RAISE INFO '%',dynamic_statement;
EXECUTE dynamic_statement;
perform dblink_disconnect();
END;
$BODY$
LANGUAGE PLPGSQL;
Calling Function:
select fun_test('test1');
Getting an error:
ERROR: a column definition list is required for functions returning "record"
LINE 11: From dblink
^
You have to tell PostgreSQL what the columns the dblink query will return are.
See the manual for dblink for details.
This is the same as for any function returning a runtime-determined record type. You can't query it without telling PostgreSQL what the column layout of the results will be.
You use a column specifier list, e.g.
SELECT * FROM my_function_returning_record() f(col1 text, col2 integer);
If you are on a current PostgreSQL version you may want to look at postgres_fdw as an alternative to dblink.

PostgreSQL if else syntax error

i try a query that runs on mssql however does not run postgreSQL...
SQL Query is..
IF EXISTS (SELECT * FROM Kategoriler WHERE KategoriId = 119)
BEGIN
SELECT * FROM Kategoriler
END
ELSE
SELECT * FROM Adminler
i searched it and i found in stackoverflow
DO
$BODY$
BEGIN
IF EXISTS (SELECT 1 FROM orders) THEN
DELETE from orders;
ELSE
INSERT INTO orders VALUES (1,2,3);
END IF;
END;
$BODY$
but i do not want to use DO or, $body etc... I do not want to write any function or other etc...
i want to write only if else statement in postgreSQL... Please help me...
T-SQL supports some procedural statement like IF. PostgreSQL doesn't support it, so you cannot rewrite your query to postgres simply. Sometime you can use Igor's solution, sometime you can use plpgsql (functions) and sometime you have to modify your application and move procedural code from server to client.
Try something like
SELECT *
FROM Kategoriler
UNION ALL
SELECT *
FROM Adminler
WHERE NOT EXIST (SELECT * FROM Kategoriler WHERE KategoriId = 119)
Will only work if Kategoriler and Adminler have same structure. Otherwise you need to specify list of fields instead of *
In my case I needed to know if a record existed.
I had to write a function
CREATE OR REPLACE FUNCTION public.pro_device_exists(vdn character varying)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
BEGIN
IF EXISTS (SELECT 1 FROM tags WHERE device_name = upper(vdn)) THEN
return true;
ELSE
return false;
END IF;
END; $function$
Then I was able to call this function in my code ... just a portion of my code
if pro_device_exists(vdn) then
update tags
set device_id = 11 where device_id = pro_device_id(vdn) and tag_type=10;
update tags
set device_id = pro_device_id(vdn) where tag_id = vtag_id;
vmsg = (select 'Device Now set to ' || first_name || ' ' || last_name from tags where tag_id=vtag_id);
vaction = 'Refresh Device Data';
else
vmsg = 'Device is not registered on this system';
vaction = 'No Nothing';
end if;

Differences beween 'set' and 'select into' in IBM DB2 SQL PL

When developing in SQL PL, what is the difference between 'set' and 'select into'?
set var = (select count(1) from emp);
select count(1) into var from emp;
Are they completely equivalent? where can I find documention about them?
When issuing a select, and it does not return any value:
select into throws an exception
set gets a null value
You can check the difference with these two stored procedures:
Using set:
create or replace procedure test1 (
in name varchar(128)
)
begin
declare val varchar(128);
set val = (select schemaname
from syscat.schemata where schemaname = name);
end #
Using select into
create or replace procedure test2 (
in name varchar(128)
)
begin
declare val varchar(128);
select schemaname into val
from syscat.schemata where schemaname = name;
end #
Call set
$ db2 "call test1('nada')"
Return Status = 0
Call select into
$ db2 "call test2('nada')"
Return Status = 0
SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a
query is an empty table. SQLSTATE=02000
This is a difference between both of them. When using select into, you have to deal with handlers.
They are, to the best of my knowledge
In some cases, you would do one technique over the other ..
eg. You cannot use WITH UR in SET
SET var1=(selct....from t with ur)
but can do
select a into var1 from t with ur
When the result of the query is part of a test condition.
For example, when detaching paritions and waiting for the asynchronous process, the following works:
WHILE (STATUS_PART <> '') DO
CALL DBMS_LOCK.SLEEP(1);
SET STATUS_PART = (SELECT STATUS
FROM SYSCAT.DATAPARTITIONS
WHERE TABSCHEMA = TABLE_SCHEMA
AND TABNAME = TABLE_NAME
AND DATAPARTITIONNAME LIKE 'SQL%' WITH UR);
END WHILE;
But the following does not:
WHILE (STATUS_PART <> '') DO
CALL DBMS_LOCK.SLEEP(1);
SELECT STATUS INTO STATUS_PART
FROM SYSCAT.DATAPARTITIONS
WHERE TABSCHEMA = TABLE_SCHEMA
AND TABNAME = TABLE_NAME
AND DATAPARTITIONNAME LIKE 'SQL%' WITH UR;
END WHILE;
The SELECT INTO works for SELECT statements.
With SET you can directly assign the outcome of a function, do calculations or assign a different variable. e.g.
SET var = var + 1;
SET var1 = var;