Postgres - If not exists not working in postgresql? - sql

I'm currently building a query and apparently, it doesn't work. This is my current query (this is the full one)
IF NOT EXISTS(SELECT * FROM invite WHERE uid=$1::uuid)
BEGIN
INSERT INTO invite (uid, link) VALUES ($1::uuid, $2::text);
END
ELSE
BEGIN
SELECT * FROM invite WHERE uid=$1::uuid;
END
In my console, i'm getting syntax error at or near "IF"

Many errors in your code:
plpgsql code can return result only from function (not from DO)
IF clause must have THEN
you do not need BEGIN / END in each statement inside IF ... THEN ... ELSE ... END (not error also has no sense)
Probably is better to do this by pure SQL:
WITH q AS (
SELECT *
FROM invite
WHERE uid=$1::uuid
), i AS (
INSERT INTO invite (uid, link)
SELECT $1::uuid, $2::text
WHERE (SELECT count(*) = 0 FROM q)
RETURNING *
) SELECT * FROM q
UNION SELECT * FROM i

Related

Is it possible to use SQL Update on a Sub Query

Is it possible to use the sql UPDATE on a sub query? I am using Big
Query Standard SQL and have tried every permutation of the below that I can come up with:
WITH test AS (SELECT * FROM 'my.database.table'),
test2 AS (UPDATE test SET myField = 100 WHERE myField < 100)
SELECT * FROM test2
I always receive the error:
Syntax error: Expected "(" or keyword SELECT or keyword WITH but got
keyword UPDATE
Use below instead
with test as (
select * from `my.database.table`
), test2 as (
select * replace(greatest(myfield, 100) as myfield) from test
)
select * from test2

Check SQL SYS_REFCURSOR without changing the cursor position

I have a requirement to check if a cursor was able to get some rows from table A. If yes, then do nothing else pull rows from table B.
Currently there are two stored procedures for now.
I am trying to do this in one stored procedure.
I tried using %ROWCOUNT but it doesn't work(because it will return 0) without changing the location of the cursor.
The issue is that my output is the cursor so I don't want to make any changes to.
If I do a fetch, then it shows error also that the return type has changed.
Any idea how to do this, like even if create a copy the cursor so that the fetch and row count could be done on the copy instead of the output cursor.
Pseudo Example
create or replace PROCEDURE "proc"
(
output OUT SYS_REFCURSOR,
)
.
BEGIN
.
.
OPEN output for select * from A
END
BEGIN
//check if output was empty then
OPEN output for select * from B
.
.
END
Update:
I did as suggested
....
BEGIN
...
OPEN output for
with
A as (select ...),
,B as (select ...),
,C as (select ...),
,D as (select ...)
select * from A
union
select * from B where not exists(select null from A)
union
select * from C where not exists(select null from B)
union
select * from D where not exists(select null from C)
END;
Since I know for sure that either one these tables will have data, I also tried the below
....
BEGIN
...
OPEN output for
with
A as (select ...)
,B as (select ...)
,C as (select ...),
,D as (select ...)
select * from A
union
select * from B
union
select * from C
union
select * from D
END;
But it gives me error now that
Error(64,7): PL/SQL: ORA-01789: query block has incorrect number of result columns
The table structure foe these 4 is diff. So they might return diff columns.
Would join make sense if 3 out of 4 are empty?
It's much better to implement your requirement in the same cursor, because it will use the same point-in-time read consistency: in your approach second open opens cursor for a different time than your first cursor and really data in A and B can change already.
This approach is better:
create or replace PROCEDURE "proc"( output OUT SYS_REFCURSOR,...)
....
BEGIN
...
OPEN output for
with
A as (select ...)
,B as (select ...)
select * from A
union all
select * from B where not exists(select null from A)
END;
Another possible solution is to create pipelined table instead, like:
create or replace function ... return {collection type} PIPELINED as
...flag boolean := true;
begin
....
for i in (select * from A) loop
flag:=false;
pipe row(...)
end loop;
if flag then
for i in (select * from B) loop
flag:=false;
pipe row(...)
end loop;
end if;
end;
/
But as you can see both query are opened at different time too.

Select Query in if else in Postgres Sql

something like ths
if(1=1)
select * from Table_a
else
slect * from Table_b
without using functions
I am trying something like this
DO $$
DECLARE
a integer := 10;
b integer := 20;
BEGIN
IF a >b THEN
select * from online.fandi_workflow_options ;
else
select * from online.credit_workflow_options ;
END IF;
END
$$;
Can anyone help me here
select * from online.fandi_workflow_options
where a > b
union
select * from online.credit_workflow_options
where a <= b
You can usually replace a logical "if" with a "where" clause; in your case, you're selecting from two different tables, so you have to use a union. This query only works if both tables have the same columns - if not, you can select explicit column names, and add "bogus" columns to each select statement to make them identical.

PostgreSQL: CASE: SELECT FROM two different tables

Is it possible to do something like the following with SQL, not PL/pgSQL (note if it's only possible with PL/pgSQL, then how)?
IF password = 'swordfish' THEN
SELECT a, b, c FROM users;
ELSE
SELECT -1; -- unauthorized error code
END IF;
Ideally, could I wrap the above in a function with TRUE being an argument?
Rather, is it possible to set the command status string to -1?
I'm asking this because I want the query to return an error code, like -1, if someone tries to get a list of all the users with the wrong password. This is for a web app with user accounts that each have a password. So, this is not something I want to manage with database roles/permissions.
Algorithm
Select 1 into a (authorized) if we find a user_id_1-session_id match.
Select 0, NULL, NULL into u (unauthorized) if we didn't find a match in step 1.
Select user_id, body, sent into s (select) if we did find a match in step 1.
Union u and s.
Code
-- List messages between two users with `user_id_1`, `session_id`, `user_id_2`
CREATE FUNCTION messages(bigint, uuid, bigint) RETURNS TABLE(i bigint, b text, s double precision) AS
$$
WITH a AS (
SELECT 1
FROM sessions
WHERE user_id = $1
AND id = $2
), u AS (
SELECT 0, NULL::text, NULL::double precision
WHERE NOT EXISTS (SELECT 1 FROM a)
), s AS (
SELECT user_id, body, trunc(EXTRACT(EPOCH FROM sent))
FROM messages
WHERE EXISTS (SELECT 1 FROM a)
AND chat_id = pair($1, $3)
LIMIT 20
)
SELECT * FROM u UNION ALL SELECT * FROM s;
$$
LANGUAGE SQL STABLE;
The PL/pgsql function below returns the messages sent between user_id & with_user_id if the user_id:key pair is authorized, as determined by the user-defined function (UDF) user_auth. Otherwise, it returns one row with from = -1 . The other UDF, pair, is a unique unordered pairing function that, given two user IDs, returns the chat_id to which the messages belong.
--- Arguments: user_id, key, with_user_id
CREATE FUNCTION messages(bigint, uuid, bigint)
RETURNS TABLE(from bigint, body text, sent double precision) AS $$
BEGIN
IF user_auth($1, $2) THEN
RETURN QUERY SELECT from, body, trunc(EXTRACT(EPOCH FROM sent))
FROM messages WHERE chat_id = pair($1, $3);
ELSE
i := -1;
RETURN NEXT;
END IF;
END;
$$ LANGUAGE plpgsql STABLE;
I don't know how to translate this to an SQL function or whether that would be better.
This will work, but it's not pretty:
WITH
u AS (SELECT * FROM user WHERE mail = '..'),
code AS (
SELECT
CASE (SELECT count(*) FROM u)
WHEN 0 THEN
'not found'
ELSE
CASE (SELECT count(*) FROM u WHERE password = '..')
WHEN 1 THEN
'right password'
ELSE
'wrong password'
END
END)
SELECT
code.*,
u.*
FROM code NATURAL LEFT OUTER JOIN u
I think you might want to look into creating a result set returning function instead.
There is a CASE expression in addition to the (pl/pgsql only) CASE control structure.
EDIT: CASE expression in sql context:
SELECT CASE
WHEN my_conditions_are_met THEN a
ELSE NULL
END AS a_or_null,
b,
c
FROM users;
EDIT 2: given your example that's how you can do it in pure SQL:
WITH params AS (
SELECT user_auth(:user_id, :key) AS user_auth,
pair(:user_id, :with_user_id) AS chat_id
), error_message AS (
SELECT -1 AS "from",
'auth error' AS "body",
EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) AS "sent"
)
SELECT from, body, trunc(EXTRACT(EPOCH FROM sent))
FROM messages
JOIN params ON messages.chat_id = params.chat_id AND params.user_auth
UNION ALL
SELECT error_message.*
FROM error_message
JOIN params ON NOT params.user_auth

Yield Return equivalent in SQL Server

I am writing down a view in SQL server (DWH) and the use case pseudo code is:
-- Do some calculation and generate #Temp1
-- ... contains other selects
-- Select statement 1
SELECT * FROM Foo
JOIN #Temp1 tmp on tmp.ID = Foo.ID
WHERE Foo.Deleted = 1
-- Do some calculation and generate #Temp2
-- ... contains other selects
-- Select statement 2
SELECT * FROM Foo
JOIN #Temp2 tmp on tmp.ID = Foo.ID
WHERE Foo.Deleted = 1
The result of the view should be:
Select Statement 1
UNION
Select Statement 2
The intended behavior is the same as the yield returnin C#. Is there a way to tell the view which SELECT statements are actually part of the result and which are not? since the small calculations preceding what I need also contain selects.
Thank you!
Yield return in C# returns rows one at a time as they appear in some underlying function. This concept does not exist in SQL statements. SQl is set-based, returning the entire result set, conceptually as a unit. (That said, sometimes queries run slowly and you will see rows returned slowly or in batches.)
You can control the number of rows being returns using TOP (in SQL Server). You can select particular rows to be returned using WHERE statements. However, you cannot specify a UNION statement that conditionally returns rows from some components but not others.
The closest you may be able to come is something like:
if UseTable1Only = 'Y'
select *
from Table1
else if UseTable2Only = 'Y'
select *
from Table2
else
select *
from table1
union
select *
from table2
You can do something similar using dynamic SQL, by constructing the statement as a string and then executing it.
I found a better work around. It might be helpful for someone else. It is actually to include all the calculation inside WITH statements instead of doing them in the view core:
WITH Temp1 (ID)
AS
(
-- Do some calculation and generate #Temp1
-- ... contains other selects
)
, Temp2 (ID)
AS
(
-- Do some calculation and generate #Temp2
-- ... contains other selects
)
-- Select statement 1
SELECT * FROM Foo
JOIN Temp1 tmp on tmp.ID = Foo.ID
WHERE Foo.Deleted = 1
UNION
-- Select statement 2
SELECT * FROM Foo
JOIN Temp2 tmp on tmp.ID = Foo.ID
WHERE Foo.Deleted = 1
The result will be of course the UNION of all the outiside SELECT statements.