UNION ALL parameterised postgres - postgresql

I'd like to do something like this in postgres
select * from table t where t.one = 11 AND t.two = 12 and t.three = 13
union all
select * from table t where t.one = 21 AND t.two = 22 and t.three = 23
I tried join lateral and inner joins but the performance is too bad. So I need to union all these queries but I don't want just to concat an indefinate amount of these values, Is there something like these https://stackoverflow.com/a/55484168/1321514 for postgres?

I don't see the need for a UNION at all. And I don't understand how a JOIN would help here
Your query is equivalent to:
select *
from table t
where (t.one,t.two,t.three) in ( (11,12,13), (21,22,23) );
Alternatively you can try joining to a VALUES clause:
select t.*
from table t
join (
values (11,12,13), (21,22,23)
) as x(c1,c2,c3) on t.one = x.c1
and t.two = x.c2
and t.three = x.c3;

Related

How to SELECT only the "nth" record from a query result?

I have a T-SQL query that return X records ordered.
I want to get only on record , for instance, only the 5th record from that result: how ?
Thanks
For that you have to update your query.
I.e in oracle we have rownum that assign rownumber to every row.
You can do like this,
Select * from(
Select a.*, rownum as n from your table) where n = 3;
Something like this.
Try this:
WITH NumberedTable AS
(
SELECT
RowNo = ROW_NUMBER() OVER(ORDER BY <'Order Column here'>)
, *
FROM <'Table Name here'>
)
SELECT *
FROM NumberedTable
WHERE RowNo = <'Record No. here'>

ORDER BY complicated expressions in PostgreSQL

I am trying to ORDER BY a difference between 2 double values (which are aliased columns), but it does not see the aliased columns.
Example:
SELECT COALESCE(
ROUND(
SUM(amount * currency1.rate / currency2.rate)
, 4)
, 0) AS first_amount,
SUM(
(SELECT
COALESCE(
ROUND(
SUM(table2.amount * currency3.rate / currency2.rate)
, 4)
, 0)
FROM table2
JOIN currencies currency3
ON currency3.id = table2.currency_id
WHERE table2.date BETWEEN table1.start_date AND table1.end_date
)
) AS second_amount
FROM table1
JOIN currencies currency1
ON currency3.id = table1.currency_id
JOIN currencies currency2
ON currency3.id = 123 # some hardcoded ID
ORDER BY first_amount - second_amount ASC
Postgres tells me that column first_amount does not exist.
Reading the documentation, I saw that Postgres 9.0 does not allow expressions with aliased columns.
How can I solve the problem by sorting all the stuff I need in the correct manner ?
A column alias cannot be used directly in the where or order by clause. You need to wrap this in a derived table.
select *
from (
... your original query goes here ...
) as t
ORDER BY first_amount - second_amount ASC;

Can this query be simplified?

Is there a simpler way to perform this query?
Actually using this in part of a larger query
Would rather not use EXCEPT, UNION, INTERSECT
As part of the more larger query the optimizer can get stupid on the derived table and except
Not of value to post the larger query as it is dynamic
The PK on docSVsys is sID
The PK on docMVenum1 is sID, enumID, valueID
select sID from docSVsys
EXCEPT
select sID
from docMVenum1
where enumID = 140
and valueID in (1,2)
group by sID
having count(*) = 2
select docSVsys.sID from docSVsys
left outer join
( select sID
from docMVenum1
where enumID = 140
and valueID in (1,2)
group by sID
having count(*) = 2 ) as joinTable
on docSVsys.sID = joinTable.sID
where joinTable.sID is null
I know the two queries are the same
I am looking for a 3rd simpler
I believe the IN operator might cause an inefficiency. Try this:
select sID
from docSVsys
EXCEPT
select sID
from (
select d1.sID
from docMVenum1 d1
join docMVenum1 d2
on d1.sID = d2.sID
where d1.enumID = 140 and d1.valueID = 1
and d2.enumID = 140 and d2.valueID = 2
) T
do you absolutely need the aggregation? cant you just do the grouping and aggregation on the data as created tables, that would be a performance booster.
CREATE TABLE new_table
AS (select sID
from docMVenum1 int
where enumID = 140
and valueID in (1,2)
the only query you would then have to run is the below.
select * from new_table where count(*) = 2
group by sID

Filter union result

I'm making select with a union.
SELECT * FROM table_1
UNION
SELECT * FROM table_2
Is it possible to filter query results by column values?
Yes, you can enclose your entire union inside another select:
select * from (
select * from table_1 union select * from table_2) as t
where t.column = 'y'
You have to introduce the alias for the table ("as t"). Also, if the data from the tables is disjoint, you might want to consider switching to UNION ALL - UNION by itself works to eliminate duplicates in the result set. This is frequently not necessary.
A simple to read solution is to use a CTE (common table expression). This takes the form:
WITH foobar AS (
SELECT foo, bar FROM table_1
UNION
SELECT foo, bar FROM table_2
)
Then you can refer to the CTE in subsequent queries by name, as if it were a normal table:
SELECT foo,bar FROM foobar WHERE foo = 'value'
CTEs are quite powerful, I recommend further reading here
One tip that you will not find in that MS article is; if you require more than one CTE put a comma between the expression statements. eg:
WITH foo AS (
SELECT thing FROM place WHERE field = 'Value'
),
bar AS (
SELECT otherthing FROM otherplace WHERE otherfield = 'Other Value'
)
If you want to filter the query based on some criteria then you could do this -
Select * from table_1 where table_1.col1 = <some value>
UNION
Select * from table_2 where table_2.col1 = <some value>
But, I would say if you want to filter result to find the common values then you can use joins instead
Select * from table_1 inner join table_2 on table_1.col1 = table_2.col1

Query to get row from one table, else random row from another

tblUserProfile - I have a table which holds all the Profile Info (too many fields)
tblMonthlyProfiles - Another table which has just the ProfileID in it (the idea is that this table holds 2 profileids which sometimes become monthly profiles (on selection))
Now when I need to show monthly profiles, I simply do a select from this tblMonthlyProfiles and Join with tblUserProfile to get all valid info.
If there are no rows in tblMonthlyProfile, then monthly profile section is not displayed.
Now the requirement is to ALWAYS show Monthly Profiles. If there are no rows in monthlyProfiles, it should pick up 2 random profiles from tblUserProfile. If there is only one row in monthlyProfiles, it should pick up only one random row from tblUserProfile.
What is the best way to do all this in one single query ?
I thought something like this
select top 2 * from tblUserProfile P
LEFT OUTER JOIN tblMonthlyProfiles M
on M.profileid = P.profileid
ORder by NEWID()
But this always gives me 2 random rows from tblProfile. How can I solve this ?
Try something like this:
SELECT TOP 2 Field1, Field2, Field3, FinalOrder FROM
(
select top 2 Field1, Field2, Field3, FinalOrder, '1' As FinalOrder from tblUserProfile P JOIN tblMonthlyProfiles M on M.profileid = P.profileid
UNION
select top 2 Field1, Field2, Field3, FinalOrder, '2' AS FinalOrder from tblUserProfile P LEFT OUTER JOIN tblMonthlyProfiles M on M.profileid = P.profileid ORDER BY NEWID()
)
ORDER BY FinalOrder
The idea being to pick two monthly profiles (if that many exist) and then 2 random profiles (as you correctly did) and then UNION them. You'll have between 2 and 4 records at that point. Grab the top two. FinalOrder column is an easy way to make sure that you try and get the monthly's first.
If you have control of the table structure, you might save yourself some trouble by simply adding a boolean field IsMonthlyProfile to the UserProfile table. Then it's a single table query, order by IsBoolean, NewID()
In SQL 2000+ compliant syntax you could do something like:
Select ...
From (
Select TOP 2 ...
From tblUserProfile As UP
Where Not Exists( Select 1 From tblMonthlyProfile As MP1 )
Order By NewId()
) As RandomProfile
Union All
Select MP....
From tblUserProfile As UP
Join tblMonthlyProfile As MP
On MP.ProfileId = UP.ProfileId
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) >= 1
Union All
Select ...
From (
Select TOP 1 ...
From tblUserProfile As UP
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) = 1
Order By NewId()
) As RandomProfile
Using SQL 2005+ CTE you can do:
With
TwoRandomProfiles As
(
Select TOP 2 ..., ROW_NUMBER() OVER ( ORDER BY UP.ProfileID ) As Num
From tblUserProfile As UP
Order By NewId()
)
Select MP.Col1, ...
From tblUserProfile As UP
Join tblMonthlyProfile As MP
On MP.ProfileId = UP.ProfileId
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) >= 1
Union All
Select ...
From TwoRandomProfiles
Where Not Exists( Select 1 From tblMonthlyProfile As MP1 )
Union All
Select ...
From TwoRandomProfiles
Where ( Select Count(*) From tblMonthlyProfile As MP1 ) = 1
And Num = 1
The CTE has the advantage of only querying for the random profiles once and the use of the ROW_NUMBER() column.
Obviously, in all the UNION statements the number and type of the columns must match.