Simplify PostgreSQL function - sql

I have a PostgreSQL stored procedure that contains the following code:
IF something = TRUE THEN
SELECT id INTO some_id FROM some_table WHERE some conditions LIMIT 1;
RETURN QUERY SELECT * FROM some_table WHERE some conditions LIMIT 1;
ELSE
SELECT id INTO some_id FROM some_table WHERE some OTHER conditions LIMIT 1;
RETURN QUERY SELECT * FROM some_table WHERE some OTHER conditions LIMIT 1;
END IF;
DELETE FROM some_table where id = some_id;
Is there a way to simplify the above code? I guess there is nothing we can do
about the repeated code in the IF and in the ELSE, but is there a way to avoid
having 2 SELECT's every time? Is it possible to insert something in some_id while
RETURN QUERY?

If the function does only what you posted then it is not necessary:
delete from some_table
where
something and (some conditions)
or
something is not true and (some other conditions)
returning *

Related

Reduce duplication in postgres plpgsql function with CTE

In a SQL function I can return a boolean if I do
with myquery as (delete from mytable where id = 'value1' returning 1)
select exists (select * from another_function('value2') where (select count(*) from myquery) > 0);
But in a plpgsql function it doesn't work and gives error query has no destination for result data.
In this function I want to execute another_function if any rows were actually deleted from mytable. The problem is I'm repeating the entire select exists part, because I'm using that in multiple functions.
Is it possible to move more of that logic into another_function? So that I can do something like this?
with myquery as (delete from mytable where id = 'value1' returning 1)
select * from another_function('value2', myquery)
How can I pass the CTE into a function so I don't need to repeat the select exists and where (select count(*) from myquery) > 0) every time I want to call another_function?
I would expect an auxiliary function to take an argument, such as the id being returned from the delete. It would then look like:
with d as (
delete from mytable
where id = 'value1'
returning id
)
select another_function('value2', d.id)
from d;
This operates one value at a time, but that is typically what one would want. If you wanted all ids passed in at once, you could use an array:
with d as (
delete from mytable
where id = 'value1'
returning id
)
select another_function('value2', array_agg(d.id))
from d;

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.

Same query but different tables

I'm faced with a big query that is generated in a string and executed with "OPEN pCursor FOR vQuery" and I'm trying to get the query out of the string variable and as a proper "compilable" query.
I'm having this problem where a different table is query depending on a variable
vQuery := 'SELECT ...';
IF pVar = 1 Then
vQuery := vQuery || ' FROM table1';
ELSE
vQuery := vQuery || ' FROM table2';
END IF
vQuery := vQuery || ' WHERE ...';
The two tables have pretty much the same column name. Is there a way to have this as a single query
OPEN Pcursorout FOR
SELECT ... FROM CASE WHEN pVar = 1 THEN table1 ELSE table1 END WHERE ...;
Or I'm stuck at having two queries?
IF pVar = 1 Then
OPEN Pcursorout FOR SELECT ... FROM table1 WHERE ...;
ELSE
OPEN Pcursorout FOR SELECT ... FROM table2 WHERE ...;
END IF
The select and where part are large and exactly the same for both table.
You could use a UNION and use your variable pVar to only include the results from one query in the result set.
SELECT t1.col1, t1.col2, ..., t1.col10
FROM table1 t1
WHERE pVar = 1 and ...
UNION
SELECT t2.col1, t2.col2, ..., t2.col10
FROM table1 t2
WHERE pVar <> 1 and ...
This isn't exactly what you asked about -- not being required to have duplicate lists of columns for the two select statements -- but I think it might capture your intent. It will require that the columns selected by both queries have the same datatype so there will be a (somewhat weak) constraint that the columns of both query results are the same. For example, you won't be able to add a new column to one query but not the other.
Perhaps using UNION / UNION ALL to unite both queries? The requirement for using UNION/UNION ALL is that all SELECTs being united must return columns with the same names.
So if you have
SELECT t.f1,
t.f2,
t.f3
FROM t
WHERE ...
and your other query is
SELECT q.f1,
q.f2,
q.f3
FROM q
WHERE ...
you can have both running as a single SQL statement with UNION:
SELECT t.f1,
t.f2,
t.f3
FROM t
WHERE ...
UNION
SELECT q.f1,
q.f2,
q.f3
FROM q
WHERE ...
Keep in mind that if you need to return columns that exist in one table but not in the other, you can still use UNION, just return NULL and name the column correspondingly to the column name in the table that has it.
Its a bit of a kludge and you might need to look at the performance impact, but you could use an inline view that unions the two base tables, with a flag on each part that you then compare to your variable
SELECT ...
FROM (
SELECT 1 as var, table1.*
FROM table1
UNION ALL
SELECT 2 as var, table2.*
FROM table2
) t
WHERE t.var = pVar
AND ...;
Using an inline view means you don't have to duplicate the main select-list or the where clause etc. If the tables have different columns then you can (and maybe should anyway) only select the columns in the inner queries that will be referenced in the outer select-list.

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.

return a default record from a sql query

I have a sql query that I run against a sql server database eg.
SELECT * FROM MyTable WHERE Id = 2
This may return a number of records or may return none. If it returns none, I would like to alter my sql query to return a default record, is this possible and if so, how? If records are returned, the default record should not be returned. I cannot update the data so will need to alter the sql query for this.
Another way (you would get an empty initial rowset returned);
SELECT * FROM MyTable WHERE Id = 2
IF (##ROWCOUNT = 0)
SELECT ...
SELECT TOP 1 * FROM (
SELECT ID,1 as Flag FROM MyTable WHERE Id = 2
UNION ALL
SELECT 1,2
) qry
ORDER BY qry.Flag ASC
You can have a look to this post. It is similar to what you are asking
Return a value if no rows are found SQL
I hope that it can guide you to the correct path.
if not exists (SELECT top 1 * FROM mytable WHERE id = 2)
select * from mytable where id= 'whatever_the_default_id_is'
else
select * from mytable where id = 2
If you have to return whole rows of data (and not just a single column) and you have to create a single SQL query then do this:
Left join actual table to defaults single-row table
select
coalesce(a.col1, d.col1) as col1,
coalesce(a.col2, d.col2) as col2,
...
from (
-- your defaults record
select
default1 as col1,
default2 as col2,
...) as d
left join actual as a
on ((1 = 1) /* or any actual table "where" conditions */)
The query need to return the same number of fields, so you shouldn't do a SELECT * FROM but a SELECT value FROM if you want to return a default value.
With that in mind
SELECT value FROM MyTable WHERE Id = 2
UNION
SELECT CASE (SELECT count(*) FROM MyTable WHERE Id = 2)
WHEN 0 THEN 'defaultvalue'
END