What would be Postgres equivalent of this T-SQL - postgresql

I want to run this either interactively from psql or from code.
create proc recent_orders_by_region
as
select top(3) * from view_orders where region = 'NA' order by order_date desc
select top(3) * from view_orders where region = 'WE' order by order_date desc
select top(3) * from view_orders where region = 'EE' order by order_date desc

You can use rank() over () but remember that order must be unique, otherwise RANK will assign the same number to multiple rows in group.
Query with example data
SELECT a.* FROM (
SELECT *,
rank() OVER (
PARTITION BY region
ORDER BY order_date DESC,ident DESC
)
FROM (
values
(1, current_date, 'NA'),(2, current_date-1, 'NA'),
(3, current_date-1, 'NA'),(4, current_date-4, 'NA'),
(5, current_date, 'NA1'),(6, current_date-1, 'NA1'),
(7, current_date-1, 'NA1'),(8, current_date-4, 'NA1')
) view_orders (ident, order_date, region)
) a WHERE RANK <=3
ident is only for demonstration purposes and should be replaced by real column from view_orders table

Related

recursive query to replicate/imitate dense_rank

BEGIN;
CREATE temp TABLE teacher (
name text,
salary numeric
);
INSERT INTO teacher
VALUES ('b1', 90000);
INSERT INTO teacher
VALUES ('f1', 87000);
INSERT INTO teacher
VALUES ('a', 65000),
('b', 90000),
('c', 40000),
('d', 95000),
('e', 60000),
('f', 87000);
COMMIT;
query
with recursive cte as(
(select name, salary, 1 as rn
from teacher order by salary desc limit 1)
union all
select l.* from cte c cross join lateral(
select name, salary, rn + 1 from teacher t
where t.salary < c.salary
order by salary desc
limit 1
) l
)
table cte order by salary desc;
If all salary are distinct,then above mentioned query can imitate as rank/row_number.
I am wondering how to use recursive query to replicate/imitate dense_rank.
related post: https://dba.stackexchange.com/questions/286627/get-top-two-rows-per-group-efficiently

Delete duplicate rows with different values in columns

I didn't find my case on the Internet. Tell me how i can delete duplicates if the values are in different columns.
I have a table with a lot of values, for example:
|Id1|Id2|
|89417980|89417978|
|89417980|89417979|
|89417978|89417980|
|89417979|89417980|
I need to exclude duplicates and leave in the answer only:
|Id1|Id2|
|89417980|89417978|
|89417980|89417979|
min/max does not work here, as the values may be different.
I tried to union/join tables on a table/exclude results with temporary tables, but in the end I come to the beginning.
Assuming id1 and id2 are primary keys columns you could try this
DECLARE #tbl table (id1 int, id2 int )
INSERT INTO #tbl
SELECT 89417980, 89417978
UNION SELECT 89417980, 89417979
UNION SELECT 89417978, 89417980
UNION SELECT 89417979, 89417980
SELECT * FROM #tbl
;WITH CTE AS (--Get comparable value as "cs"
SELECT
IIF(id1 > id2, CHECKSUM(id1, id2), CHECKSUM(id2,id1)) as cs
, id1
, id2
, ROW_NUMBER() OVER (order by id1, id2) as rn
FROM #tbl
)
, CTE2 AS ( --Get rows to keep
SELECT MAX (rn) as rn
FROM CTE
GROUP BY cs
HAVING COUNT(*) > 1
)
DELETE tbl -- Delete all except the rows to keep
FROM #tbl tbl
WHERE NOT EXISTS(SELECT 1
FROM CTE2
JOIN CTE ON CTE.rn = CTE2.rn
WHERE CTE.id1 = tbl.id1
AND CTE.id2 = tbl.id2
)
SELECT * FROM #tbl

Using WITH clause and INSERT statement in POSTGRESQL

There was a question asked several years ago with a similar title (Using WITH clause with INSERT statement in POSTGRESQL), but I need to figure out a more complicated situation.
I've used the example from POSTGRESQL for using a "With" statement (https://www.postgresql.org/docs/current/queries-with.html). Let's say I made a pre-computed table and wanted to insert the output from the query into it (top_region, total_sales, product_units, and product_sales), how would I do that?
Precomputed table
INSERT INTO top (top_region, total_sales, product_units, product_sales)
select top_region, total_sales, product_units, product_sales
from #not sure here
WITH regional_sales AS (
SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region
), top_regions AS (
SELECT region
FROM regional_sales
WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT region,
product,
SUM(quantity) AS product_units,
SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;
My query
WITH sales_data AS (
SELECT s.customer_id, s.product_id, s.quantity, pr.list_price, pr.category_id, c.state_id
FROM order_products s
INNER JOIN Products pr ON (s.product_id = pr.product_id)
INNER JOIN Customer c ON (s.customer_id = c.customer_id)
),
t_20cat AS (
SELECT category_id, ROW_NUMBER() OVER (ORDER BY SUM(list_price*quantity) DESC) AS "rank", SUM(list_price*quantity) AS total_sales FROM sales_data
GROUP BY category_id ORDER BY total_sales DESC LIMIT 20
),
t_20cust AS (
SELECT customer_id, ROW_NUMBER() OVER (ORDER BY SUM(list_price*quantity) DESC) AS "rank", SUM(list_price*quantity) AS total_sales FROM sales_data
GROUP BY customer_id ORDER BY total_sales DESC LIMIT 20
),
t_20cc AS (
SELECT customer_id, t_20cust.rank as customer_rank, category_id, t_20cat.rank as category_rank FROM t_20cat, t_20cust
)
SELECT t_20cc.*, COALESCE(SUM(sales_data.quantity), 0) AS quantity_sold, COALESCE(SUM(sales_data.list_price*sales_data.quantity), 0) AS dollar_value
FROM t_20cc
LEFT JOIN sales_data ON (
t_20cc.customer_id = sales_data.customer_id AND t_20cc.category_id = sales_data.category_id
)
GROUP BY t_20cc.customer_id, t_20cc.customer_rank, t_20cc.category_id, t_20cc.category_rank
INSERT INTO top_20(customer_id, customer_rank, category_id, category_rank
select category_id, category_rank, category_id, category_rank
from t_20cc
)
Move the other SELECT query into its own CTE (region_summary), then reference that in the INSERT statement's SELECT ... FROM clause:
WITH regional_sales AS (
SELECT
region,
SUM(amount) AS total_sales
FROM
orders
GROUP BY
region
),
top_regions AS (
SELECT
region
FROM
regional_sales
WHERE
total_sales > ( SELECT SUM( total_sales ) / 10 FROM regional_sales )
),
region_summary AS (
SELECT
region,
product,
SUM(quantity) AS product_units,
SUM(amount) AS product_sales
FROM
orders
WHERE
region IN ( SELECT region FROM top_regions )
GROUP BY
region,
product
)
INSERT INTO top ( top_region, total_sales, product_units, product_sales )
SELECT
region AS top_region,
product AS total_sales,
product_units,
product_sales
FROM
region_summary;

How to order by result set in union query in T-SQL

I want to use order by clause in my last sql query and I have more than 3 union queries. I do not want to order the top 2 union query but I want to use order by clause in my last sql statement.
Currently, getting error
ORDER BY items must appear in the select list if the statement contains a UNION, INTERSECT or EXCEPT operator.
select 'Total Number of pat' Name, convert(varchar(20), count(id)) Number from table2 where id = 5
union
select 'Total Number of Doc' Name, convert(varchar(20), count(id)) Number from table3
union
select x.usertype, count(distinct userid) cnt
from [dbo].table1 t
cross apply (values (
case when t.userid like '%[0-9][0-9[0-9]' then 'transition' else 'non transition' end,
t.userid
)) x(usertype, userid)
where t.date >= dateadd(day,-7, getdate())
group by x.usertype
order by usertype desc
order by is sorting the result of the unions all together however you can introduce a orderIndex column for imlementing the right ordering.
Here the sample:
I've tried to build sample data in the following code.
create table table1(
userid varchar(100),
usertype varchar(100),
date date
)
insert into table1(userid, date) values ('Einsmayr', '2020-10-27')
insert into table1(userid, date) values ('Eins123', '2020-10-27')
insert into table1(userid, date) values ('Einschmid', '2020-10-27')
insert into table1(userid, date) values ('Einshuber', '2020-10-27')
insert into table1(userid, date) values ('Einsreitmayr', '2020-10-27')
create table table2 (
Name varchar(100),
id int
)
insert into table2(Name, id) values('Zweirich', 5)
insert into table2(Name, id) values('Zweifel', 6)
create table table3 (
Name varchar(100),
id int
)
insert into table3(Name, id) values('Dreisinger', 17)
insert into table3(Name, id) values('Dreibert', 18)
This allows the following queries:
select usertype, Number
from (
select 'Total Number of pat' usertype, convert(varchar(20), count(id)) Number, 1 orderIndex from table2 where id = 5
union
select 'Total Number of Doc' Name, convert(varchar(20), count(id)) Number, 2 orderIndex from table3
union
select usertype, count(distinct userid) Number, 3 orderIndex
from (
select userid, case when userid like '%[0-9][0-9[0-9]' then 'transition' else 'non transition' end usertype
from table1
where date >= dateadd(day,-7, getdate())
) x
group by x.usertype
) y
order by y.orderIndex, y.usertype
Find the solution here: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=ac396c48f5dbcb4a53ad40fac70e9236

SQL Server : group by with corresponding row values

I need to write a T-SQL group by query for a table with multiple dates and seq columns:
DROP TABLE #temp
CREATE TABLE #temp(
id char(1),
dt DateTime,
seq int)
Insert into #temp values('A','2015-03-31 10:00:00',1)
Insert into #temp values('A','2015-08-31 10:00:00',2)
Insert into #temp values('A','2015-03-31 10:00:00',5)
Insert into #temp values('B','2015-09-01 10:00:00',1)
Insert into #temp values('B','2015-09-01 10:00:00',2)
I want the results to contains only the items A,B with their latest date and the corresponding seq number, like:
id MaxDate CorrespondentSeq
A 2015-08-31 10:00:00.000 2
B 2015-09-01 10:00:00.000 2
I am trying with (the obviously wrong!):
select id, max(dt) as MaxDate, max(seq) as CorrespondentSeq
from #temp
group by id
which returns:
id MaxDate CorrespondentSeq
A 2015-08-31 10:00:00.000 5 <-- 5 is wrong
B 2015-09-01 10:00:00.000 2
How can I achieve that?
EDIT
The dt datetime column has duplicated values (exactly same date!)
I am using SQL Server 2005
You can use a ranking subselect to get only the highest ranked entries for an id:
select id, dt, seq
from (
select id, dt, seq, rank() over (partition by id order by dt desc, seq desc) as r
from #temp
) ranked
where r=1;
SELECT ID, DT, SEQ
FROM (
SELECT ID, DT, SEQ, Row_Number()
OVER (PARTITION BY id ORDER BY dt DESC, seq DESC) AS row_number
FROM temp
) cte
WHERE row_number = 1;
Demo : http://www.sqlfiddle.com/#!3/3e3d5/5
With trial and errors maybe I have found a solution, but I'm not completely sure this is correct:
select A.id, B.dt, max(B.seq)
from (select id, max(dt) as maxDt
from #temp
group by id) as A
inner join #temp as B on A.id = B.id AND A.maxDt = B.dt
group by A.id, B.dt
Select id, dt, seq
From #temp t
where dt = (Select Max(dt) from #temp
Where id = t.Id)
If there are duplicate rows, then you also need to specify what the query processor should use to determine which of the duplicates to return. Say you want the lowest value of seq,
Then you could write:
Select id, dt, seq
From #temp t
where dt = (Select Max(dt) from #temp
Where id = t.Id)
and seq = (Select Min(Seq) from #temp
where id = t.Id
and dt = t.dt)