How to get 2 most recent values in postgreSQL in one row? - sql

I have this table of items in order:
orderitemid orderid itemid quantity price createdate
1 1 12 5 15.5 2016-12-04 11:35:02.06629
2 1 17 5 13.2 2016-12-04 11:32:02.06629
3 2 12 2 12.5 2016-12-05 11:35:02.06629
4 2 17 1 12.6 2016-12-05 11:35:02.06629
5 2 18 15 14.5 2016-12-04 11:35:02.06629
6 3 12 45 3 2015-12-04 11:35:02.06629
I have a query which gives the most recent order of each item so:
select distinct on (itemid) *
from orderitems
order by itemid,createdate
this gives:
orderitemid orderid itemid quantity price createdate
3 2 12 2 12.5 2016-12-05 11:35:02.06629
4 2 17 1 12.6 2016-12-05 11:35:02.06629
5 1 18 15 14.5 2016-12-04 11:35:02.06629
Now what I want is to get in the same row per item information about the previous order of the item. Basically to compare the most recent order of item with the 2nd most recent order of item
This is what I want:
orderitemid itemid quantity price 2ndquantity 2ndprice 2ndorderitemid
3 12 2 12.5 5 15.5 1
4 17 1 12.6 1 13.2 2
5 18 15 14.5
How can I modify my query to do that?

with ranked as (
select orderitemid, orderid, itemid, quantity, price, createdate,
row_number() over (partition by itemid order by createdate desc) as rn
from orderitems
)
select r1.*, r2.quantity as "2ndquantity", r2.price as "2ndprice",
r2.orderitemid as "2ndorderitemid"
from ranked r1
left join ranked r2 on r1.itemid = r2.itemid and r2.rn = 2
where r1.rn = 1;
The CTE calculates the 1st and 2nd items and the final select then brings them together with a join. Note that you need a left join as there might not be a 2nd row and in that case that item would not show up at all.
Online example: http://rextester.com/SDBZ21144

Hmmm you want to compare all orderitem rows against the next most recent price?
WITH comparison_rank AS (
SELECT orderitemid, itemid, price, quantity
rank() over (partition by itemid order by createddate)
FROM orderitems
)
SELECT o.orderitemid, o.itemid, o.price, o.quantity, o.price, o.createddate,
p.price as prevprice, p.quantity as prevqty
FROM orderitems o JOIN comparison_rank c ON o.orderitemid = c.orderitemid
LEFT JOIN comparison_rank p ON c.rank = p.rank + 1 AND c.itemid = p.itemid;
This will not perform so well over a large data set however. To improve performance I think you need to improve your data model to improve performance.

Related

How to select the most frequent value for each group within two tables?

I have two tables and I want to select the most ordered (amount) product for each shipmentType (see result table)
shipment table
id
shipmentTyp
amount
productID
1
A
3
1
2
S
7
1
3
A
12
3
4
T
15
2
5
T
7
1
6
T
4
3
7
A
1
3
8
S
78
2
Products table
productID
productName
1
P1
2
P2
3
P3
Result table
shipmentType
productName
amount
A
P3
12
S
P2
78
T
P2
15
It seems a simple ROW_NUMBER() problem to me -
SELECT
SH.shipmentType, P.productName, SH.amount
FROM
(SELECT
shipmentType, amount, productID,
ROW_NUMBER() OVER (PARTITION BY shipmentType ORDER BY amount DESC) RN
FROM
shipment) SH
JOIN
Products P ON SH.productID = P.productID
WHERE
RN = 1;

How to SQL distinct with ORDER BY [duplicate]

This question already has answers here:
Get top 1 row of each group
(19 answers)
Closed last year.
How do i bring the rows distinctly by OrderID and those who has ProductID 1. if there is no 1 then bring 2
I've tried:
Select DISTINCT OrderDetailID,OrderID,ProductID,Quantity from Products
ORDER BY OrderID,ProductID
OrderDetailID
OrderID
ProductID
Quantity
1
10248
1
12
2
10248
2
10
3
10249
1
5
4
10250
1
9
5
10250
2
40
6
10251
2
10
7
10252
1
35
8
10252
2
15
9
10253
2
6
10
10254
2
15
Output should be like this:
OrderDetailID
OrderID
ProductID
Quantity
1
10248
1
12
3
10249
1
5
4
10250
1
9
6
10251
2
10
7
10252
1
35
9
10253
2
6
10
10254
2
15
Use row_number window function
select OrderDetailID,OrderID,ProductID,Quantity
from
select *,row_number()over(partition by OrderID order by OrderDetailID) as seq
from ) t
where seq = 1
You can use row_number to asign ordering to each group of ProductId
with o as (
select *,
Row_Number() over(partition by orderid order by productid) rn
from t
)
select OrderDetailID, OrderID, ProductID, Quantity
from o
where rn=1

complex paratition sum in postgresql

I have tables as follow:
A deliveries
delveryid clientid deliverydate
1 10 2015-01-01
2 10 2015-02-02
3 11 2015-04-08
B items in deliveris
itemid deliveryid qty status
70 1 5 1
70 1 8 2
70 2 10 1
72 1 12 1
70 3 100 1
I need to add a column to my query that gives me the qty of each part in other deliveris of the same client.
meaning that for given data of client 10 and delivery id 1 I need to show:
itemid qty status qtyOther
70 5 1 10 //itemid 70 exists in delivery 2
70 8 2 10 //itemid 70 exists in delivery 2
72 12 1 0 //itemid 72 doesn't exists in other delivery of client 11
Since I need to add qtyOther to my existing qry i'm trying to avoid using Group By as it's a huge query and if I use SUM in select I will have to group by all items in select.
This is what I have so far:
Select ....., coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid) ,0) AS qtyOther
FROM B b
LEFT JOIN A a USING
LEFT JOIN (other tables)
WHERE clientid=10 ....
This query gives me the total sum of qty per itemid for specific clientid, regardless of which delivery it is. How do I change it so it will consider the delivryid? I need something like:
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid) FROM B where deliveryid<>b.deliveryid ,0) AS qtyOther
Any suggestions how to do that?
Note: I can NOT change the condition in WHERE.
I think you just want to subtract out the total for the current delivery:
Select .....,
(coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid), 0) -
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid, a.deliveryid), 0)
) as qtyOther

SQL sum values from different tables using item_details group by order_id and order_date

I have multiple tables. I only wanted to operate on these two. The objective is to project the order_id, sum of (total order cost calculated from multiple table), order_date ---grouped by each one order_id and sorted by order_date.
It sounds quite simple, but I have multiple queries, sum gave error & wrong result. I m far from getting the correct simple query for this.
Here are the tables and data :
Order_Table :
ORDERID CUSTOMERID EMPLOYEEID ORDERDATE PURCHASEORDERNUMBER SHIPDATE SHIPPINGMETHODID FREIGHTCHARGE TAXES PAYMENTRECEIVED COMMENTS
------- ---------- ---------- --------- ------------------- --------- ---------------- ------------- ----- --------------- -------------------
1 2 1 23-JAN-05 10 29-JAN-05 1 64 5 0
2 1 1 23-JAN-05 11 29-JAN-05 1 0 5 0
3 3 3 21-JAN-05 30 28-JAN-05 5 0 5 0
4 2 2 05-JAN-05 26 19-JAN-05 2 0 5 0
5 4 5 02-JAN-05 32 27-JAN-05 2 0 5 0
13 1 1 29-JAN-08 1 2 50 5 0
6 rows selected
Order_Detail_Table :
ORDERDETAILID ORDERID PRODUCTID QUANTITY UNITPRICE DISCOUNT
1 1 4 15 5 0
2 2 7 10 75 0
3 3 8 5 4 0
5 4 9 100 5 0.05
6 5 6 5 7 0
7 5 9 30 5 0.05
9 1 1 9 6 0
11 13 4 1 5 0
12 13 1 2 25 0
13 13 7 1 75 0
10 rows selected
So for example for orderid = 13. We have 3 items ordered listed on the order_detail_table.
For the unique orderid=13, I want to calculate 3 rows of (quantity*unitprice)*(1-discount). It will be 130 for orderid=13
select o.orderid, sum((od.unitprice*od.quantity)*(1-od.discount*0.01)) as total from order_table o, order_detail_table od
where o.orderid=od.orderid group by o.orderid;
Then using those values, I would like to add the final summed values with the individual freightcharge + taxes. = (130+50)(1+tax%) = 180(1.05) = 189 for the row orderid=13.
I am stuck here, to add with the order_table.freight etc. I just met with compile and logic errors.
select o.orderid, (sum((od.unitprice*od.quantity)*(1-od.discount*0.01)) + o.freightcharge) as total from order_table o, order_detail_table od
where o.orderid=od.orderid group by o.orderid;
Gives me SQL Error: ORA-00979: not a GROUP BY expression --- Idk why as I think the sum is only aggregate function to be grouped, while + o.freightcharge is outside of sum.
select o.orderid, sum((od.unitprice*od.quantity)*(1-od.discount*0.01)) + sum(o.freightcharge) as total from order_table o, order_detail_table od
where o.orderid=od.orderid group by o.orderid;
Gives me 3 x freightcharges, for single order in case of orderid=13. --Wrong logic.
There should be 6 rows for each unique orderid.
I tried building from several simpler queries. But I havent got success. As I think I might need to use subqueries
Pls Help.
The reason is that you combine the freightcharge from order_detail_table with aggregated data. Something like that should solve the problem:
select o.orderid, sq.total+o.freightcharge
from order_table o
left join
(
select o.orderid, sum((od.unitprice*od.quantity)*(1-od.discount*0.01)) as total
from order_table o, order_detail_table od
where o.orderid=od.orderid group by o.orderid
) sq
on sq.orderid=o.order_id;
I think this will work for you:
;with OrderTotals
AS
(
select
o.orderid
,o.freightcharge
,sum((od.unitprice*od.quantity)*(1-od.discount*0.01)) as total
from
order_table o
inner join
order_detail_table od
on o.orderid = od.orderid
group by
o.orderid
,o.freightcharge
)
select
OrderTotals.OrderId AS OrderId
,OrderTotals.Total AS OrderTotal
,OrderTotals.Total + freightcharge AS Total
from
OrderTotals
SELECT o.orderid,((od.total + o.FREIGHTCHARGE) * (1 + o.TAXES * 0.01)) AS result
FROM order_table o JOIN
(
SELECT orderid, sum((unitprice*quantity)*(1-discount*0.01)) AS total FROM
order_detail_table GROUP BY orderid
) as od ON o.orderid=od.orderid

Use column from first table as condition for joining second table

Suppose I have
products:
folio price quantity
1 100.00 1
1 450.00 2
3 150.00 1
4 600.00 2
terms:(to know how many payment terms depending on the price of the product)
level term
0.01 12
100.00 14
200.00 16
300.00 18
400.00 20
500.00 22
What can I do to have a resulting table like this:
folio price quantity term
1 100.00 1 14
1 450.00 2 20
I've tried using:
SELECT a.*, b.term
FROM products AS a
JOIN terms AS b ON b.level <= a.price
WHERE a.folio = 1
But I end up getting:
folio price quantity term
1 100.00 1 12
1 100.00 1 14
1 450.00 2 12
1 450.00 2 14
1 450.00 2 16
1 450.00 2 18
1 450.00 2 20
What can I do so I only get the row with the biggest term? Please help!
You are looking for one row from the terms table, not all of them. One way to do this is with a correlated subquery:
SELECT p.*,
(select t.term from terms t where p.price >= t.level order by t.level desc limit 1
) as term
FROM products p
WHERE p.folio = 1;
If you can modify your terms table to have a minimum and maximum price, then that would make it easier to user.
And, you can mimic this with the lead() function:
select p.*, t.term
from products p left outer join
(select t.*, lead(level) over (order by level) as nextlevel
from terms t
) t
on p.price >= t.level and (p.price < t.nextlevel or t.nextlevel is null)
where p.folio = 1;