Joining multiple tables and getting MAX value in subquery PostgreSQL - sql

I have 4 Tables in PostgreSQL with the following structure as you can see below:
"Customers"
ID | NAME
101 Max
102 Peter
103 Alex
"orders"
ID | customer_id | CREATED_AT
1 101 2022-05-12
2 101 2022-06-14
3 101 2022-07-9
4 102 2022-02-14
5 102 2022-06-18
6 103 2022-05-22
"orderEntry"
ID | order_id | product_id |
1 3 10
2 3 20
3 3 30
4 5 20
5 5 40
6 6 20
"product"
ID | min_duration
10 P10D
20 P20D
30 P30D
40 P40D
50 P50D
Firstly I need to select "orders" with the max(created_at) date for each customer this is done with the query (it works!):
SELECT c.id as customerId,
o.id as orderId,
o.created_at
FROM Customer c
INNER JOIN Orders o
ON c.id = o.customer_id
INNER JOIN
(
SELECT customer_id, MAX(created_at) Max_Date
FROM Orders
GROUP BY customer_id
) res ON o.customer_id = res.customer_id AND
o.created_at = res.Max_date
the result will look like this:
customer_id | order_id | CREATED_AT
101 3 2022-07-9
102 5 2022-06-18
103 6 2022-05-22
Secondly I need to select for each order_id from "orderEntry" Table, "products" with the max(min_duration) the result should be:
order_id | max(min_duration)
3 P30D
5 P40D
6 P20D
and then join results from 1) and 2) queries by "order_id" and the total result which I'm trying to get should look like this:
customer_name | customer_id | Order_ID | Order_CREATED_AT | Max_Duration
Max 101 3 2022-07-9 P30D
Peter 102 5 2022-06-18 P40D
Alex 103 6 2022-05-22 P20D
I'm struggling to get query for 2) and then join everything with query from 1) to get the result. Any help I would appreciate!

You could make the first query to an CTE and use that to join the rest of the queries.
Like this.
WITH CTE AS ( SELECT c.id as customerId,
o.id as orderId,
o.created_at
FROM Customer c
INNER JOIN Orders o
ON c.id = o.customer_id
INNER JOIN
(
SELECT customer_id, MAX(created_at) Max_Date
FROM Orders
GROUP BY customer_id
) res ON o.customer_id = res.customer_id AND
o.created_at = res.Max_date)
SELECT customerId,orderId,created_at,p.min_duration
FROM CTE
JOIN (SELECT "orderId", MAX("product_id") as product_id FROM "orderEntry" GROUP BY orderId) oe ON CTE.orderId = oe.orderId
JOIN "product" pr ON oe.product_id = pr."ID"

Related

multiple two column values with grouping by a column

I've two tables tblOrder and tblOrderDetails. I want to get order no, total price per order (Quantity*UnitCost) and OrderDate as given below.
Order No
Total
OrderDate
ORD 1
3000
01/01/2021
ORD 2
2750
01/03/2021
What I've tried is giving me quantity is not a part of aggregate function.
SELECT tblOrder.OrderNo, tblOrderDetails.UnitCost*tblOrderDetails.Quantity AS Total, OrderDate
FROM tblOrderDetails INNER JOIN tblOrder ON tblOrderDetails.OrderId = tblOrder .OrderId
GROUP BY tblOrder.OrderNo;
Table structures and data
Table tblOrder:
OrderId
OrderNo
OrderDate
1
ORD 1
01/01/2021
2
ORD 2
01/03/2021
Table tblOrderDetails:
OrderDetailId
Quantity
UnitCost
OrderId
1
100
30
1
2
50
40
2
2
10
15
2
2
20
30
2
select o.OrderNo
,od.total
,o.OrderDate
from
(
select OrderId
,sum(Quantity*UnitCost) as total
from tblOrderDetails
group by OrderId
) od join tblOrder o on o.OrderId = od.OrderId
OrderNo
total
OrderDate
ORD 1
3000
2021-01-01
ORD 2
2750
2021-01-03
Fiddle
Your requirements are not 100% clear, but maybe, you can just do this, without any subquery:
SELECT tblOrder.OrderNo,
SUM(tblOrderDetails.UnitCost*tblOrderDetails.Quantity) AS Total,
OrderDate
FROM tblOrderDetails
INNER JOIN tblOrder ON tblOrderDetails.OrderId = tblOrder.OrderId
GROUP BY tblOrder.OrderNo,OrderDate;
To see the difference to Danny's answer - which might also be fine - have a look here: db<>fiddle

How to find out first product item client purchased whose bought specific products?

I want to write a query to locate a group of clients whose purchased specific 2 product categories, at the same time, getting the information of first transaction date and first item they purchased. Since I used group by function, I could only get customer id but not first item purchase due to the nature of group by. Any thoughts to solve this problem?
What I have are transaction tables(t), customer_id tables(c) and product tables(p). Mine is SQL server 2008.
Update
SELECT t.customer_id
,t.product_category
,MIN(t.transaction_date) AS FIRST_TRANSACTION_DATE
,SUM(t.quantity) AS TOTAL_QTY
,SUM(t.sales) AS TOTAL_SALES
FROM transaction t
WHERE t.product_category IN ('VEGETABLES', 'FRUITS')
AND t.transaction_date BETWEEN '2020/01/01' AND '2022/09/30'
GROUP BY t.customer_id
HAVING COUNT(DISTINCT t.product_category) = 2
**Customer_id** **transaction_date** **product_category** **quantity** **sales**
1 2022-05-30 VEGETABLES 1 100
1 2022-08-30 VEGETABLES 1 100
2 2022-07-30 VEGETABLES 1 100
2 2022-07-30 FRUITS 1 50
2 2022-07-30 VEGETABLES 2 200
3 2022-07-30 VEGETABLES 3 300
3 2022-08-01 FRUITS 1 50
3 2022-08-05 FRUITS 1 50
4 2022-08-07 FRUITS 1 50
4 2022-09-05 FRUITS 2 100
In the above, what I want to show after executing the SQL query is
**Customer_id** **FIRST_TRANSACTION_DATE** **first_product_category** **TOTAL_QUANTITY** **TOTAL_SALES**
2 2022-07-30 VEGETABLES, FRUITS 4 350
3 2022-07-30 VEGETABLES 5 400
Customer_id 1 and 4 will not be shown as they only purchased either vegetables or fruits but not both
Check now, BTW need find logic with product_category
select CustomerId, transaction_date, product_category, quantity, sales
from(
select CustomerId, transaction_date, product_category , sum(quantity) over(partition by CustomerId ) as quantity , sum(sales) over(partition by CustomerId ) as sales, row_number() over(partition by CustomerId order by transaction_date ASC) rn
from(
select CustomerId, transaction_date, product_category, quantity, sales
from tablee t
where (product_category = 'FRUITS' and
EXISTS (select CustomerId
from tablee tt
where product_category = 'VEGETABLES'
and t.CustomerId = tt.CustomerId)) OR
(product_category = 'VEGETABLES' and
EXISTS (select CustomerId
from tablee tt
where product_category = 'FRUITS'
and t.CustomerId = tt.CustomerId)))x)over_all
where rn = 1;
HERE is FIDDLE

Subtracting rows depending on values of another column

I have two tables purchase, I want to subtract purchase date. depending on Customer ID, there are repeating customer ID's, so I want to subtract purchase date of Customer ID 105 and 105, 108 and 108 etc.
I have the following code, but it is subtracting each purchase date from the next purchase date
SELECT DATEDIFF(DAY,P1.PURCHASEDATE,P2.PURCHASEDATE) AS "diff in days since last purchase"
FROM Purchases P1
JOIN Purchases P2
ON P1.CustomerID= P2.CustomerID
Try adding to your ON a not equal: P1.PURCHASEID <> P2.PURCHASEID , meaning something like this:
SELECT DATEDIFF(DAY,P1.PURCHASEDATE,P2.PURCHASEDATE) AS "diff in days"
FROM Purchases P1
JOIN Purchases P2
(ON P1.CustomerID= P2.CustomerID and P1.PURCHASEID <> P2.PURCHASEID )
You can use OUTER APPLY:
;WITH Purchases AS (
SELECT *
FROM (VALUES
(1,'2012-08-15',1,105,'a510'),
(2,'2012-08-15',2,102,'a510'),
(3,'2012-08-15',3,103,'a506'),
(4,'2012-08-16',1,105,'a510'),
(5,'2012-08-17',5,106,'a507'),
(6,'2012-08-17',5,107,'a509'),
(7,'2012-08-18',4,108,'a502'),
(8,'2012-08-19',2,108,'a510'),
(9,'2012-08-19',3,109,'a502'),
(10,'2012-08-20',3,110,'a503')
) as t(PurchaseID,PurchaseDate,Qty,CustomerID,ProductID)
)
SELECT p1.*,
DATEDIFF(DAY,P2.PurchaseDate,P1.PurchaseDate) as ddiff
FROM Purchases p1
OUTER APPLY (
SELECT TOP 1 *
FROM Purchases
WHERE p1.CustomerID = CustomerID
AND PurchaseDate < p1.PurchaseDate
ORDER BY PurchaseDate DESC
) p2
Will output:
PurchaseID PurchaseDate Qty CustomerID ProductID ddiff
1 2012-08-15 1 105 a510 NULL
2 2012-08-15 2 102 a510 NULL
3 2012-08-15 3 103 a506 NULL
4 2012-08-16 1 105 a510 1
5 2012-08-17 5 106 a507 NULL
6 2012-08-17 5 107 a509 NULL
7 2012-08-18 4 108 a502 NULL
8 2012-08-19 2 108 a510 1
9 2012-08-19 3 109 a502 NULL
10 2012-08-20 3 110 a503 NULL
Also you can use LAG (SQL Server 2012 and up):
SELECT *,
DATEDIFF(DAY,LAG(PurchaseDate,1,NULL) OVER (PARTITION BY CustomerID ORDER BY PurchaseDate),PurchaseDate) as ddiff
FROM Purchases

Query to pull previous order date corresponding to a customer?

I have a schema with Customer table and Order table. A customer can place order in multiple dates. I need to have previous order_date for every order_date corresponding to a customer.
Say a customer placed 4 orders, then for newest order(4th order) - it must pull current order_date and previous order_date(3rd order). For 3rd order placed by customer, it must pull 3rd order_date as current order_date and previous order_date(2nd order) as so on.
I am using below query to get previous order_date and then joining with current_query to get result::
select customerid, orderid, order_date as previous_order_date
from (
select c.customerid, o.orderid, o.order_date,
row_number() over (partition by c.customerid, o.orderid
order by o.order_date) rown
from customers c join orders o on c.customerid = o.customerid
) a
where rown = 2
But the issue is, I am getting a single date corresponding to a customerid whereas the requirement is - just previous order_date corresponding to current order_date for a customer.
Any suggestion would help! Thanks
Try with LAG() window function per customerid:
select
c.customerid, o.orderid, o.order_date,
lag(o.order_date) over (partition by c.customerid order by o.order_date) AS prev_order_date
from customers c
join orders o on c.customerid = o.customerid
For the earliest order of every customer prev_order_date will be null.
Sample result (don't mind orderid, it's just for the example):
customerid | orderid | order_date | prev_order_date
------------+---------+------------+-----------------
1 | 6 | 2015-02-08 |
1 | 2 | 2016-02-05 | 2015-02-08
1 | 3 | 2016-02-08 | 2016-02-05
1 | 1 | 2016-03-05 | 2016-02-08
2 | 5 | 2016-07-01 |
2 | 4 | 2016-07-08 | 2016-07-01
If one customer can place the same order within different dates (weird, but this seems to be your case) add o.orderid to the PARTITION BY clause.
Unfortunately, LAG() didn't work when used in SQL node for reporting purpose. I tried using below query and got desired result:
SELECT c.customer_code, o.customer_sid, o.order_id, o.order_no,
o.order_created_date,
(SELECT MAX (o1.order_created_date)
FROM d_customer c1 LEFT JOIN f_order o1
ON c1.customer_sid =
o1.customer_sid
WHERE c1.customer_sid = c.customer_sid
AND o1.order_created_date < o.order_created_date
AND EXISTS (SELECT 1
FROM f_invoice i
WHERE i.order_id = o1.order_id))
AS prev_order_created_date,
t.financial_year, t.financial_month_no
FROM d_customer c JOIN f_order o
ON c.customer_sid = o.customer_sid
AND c.customer_type = 'PATIENT'
AND c.customer_country = 'UNITED STATES'
AND o.customer_type = 'PATIENT'
AND o.bill_to_country = 'UNITED STATES'
AND o.order_status = 'SHIPPED'
AND o.order_type = 'SALES'
AND o.order_group = 'REVENUE'
-- AND c.customer_code = '233379PT'
LEFT JOIN d_time t ON t.time_sid = o.order_created_date_sid
ORDER BY order_created_date DESC

How to Retrieve Maximum Value of Each Group? - SQL

There is a table tbl_products that contains data as shown below:
Id Name
----------
1 P1
2 P2
3 P3
4 P4
5 P5
6 P6
And another table tbl_inputs that contains data as shown below:
Id Product_Id Price Register_Date
----------------------------------------
1 1 10 2010-01-01
2 1 20 2010-10-11
3 1 30 2011-01-01
4 2 100 2010-01-01
5 2 200 2009-01-01
6 3 500 2011-01-01
7 3 270 2010-10-15
8 4 80 2010-01-01
9 4 50 2010-02-02
10 4 92 2011-01-01
I want to select all products(id, name, price, register_date) with maximum date in each group.
For Example:
Id Name Price Register_Date
----------------------------------------
3 P1 30 2011-01-01
4 P2 100 2010-01-01
6 P3 500 2011-01-01
10 P4 92 2011-01-01
select
id
,name
,code
,price
from tbl_products tp
cross apply (
select top 1 price
from tbl_inputs ti
where ti.product_id = tp.id
order by register_date desc
) tii
Although is not the optimum way you can do it like:
;with gb as (
select
distinct
product_id
,max(register_date) As max_register_date
from tbl_inputs
group by product_id
)
select
id
,product_id
,price
,register_date
from tbl_inputs ti
join gb
on ti.product_id=gb.product_id
and ti.register_date = gb.max_register_date
But as I said earlier .. this is not the way to go in this case.
;with cte as
(
select t1.id, t1.name, t1.code, t2.price, t2.register_date,
row_number() over (partition by product_id order by register_date desc) rn
from tbl_products t1
join tbl_inputs t2
on t1.id = t2.product_id
)
select id, name, code, price, register_date
from cte
where rn = 1
Something like this..
select id, product_id, price, max(register_date)
from tbl_inputs
group by id, product_id, price
you can use the max function and the group by clause. if you only need results from the table tbl_inputs you even don't need a join
select product_id, max(register_date), price
from tbl_inputs
group by product_id, price
if you need field from the tbl_prducts you have to use a join.
select p.name, p. code, i.id, i.price, max(i.register_date)
from tbl_products p join tbl_inputs i on p.id=i.product_id
grooup by p.name, p. code, i.id, i.price
Try this:
SELECT id, product_id, price, register_date
FROM tbl_inputs T1 INNER JOIN
(
SELECT product_id, MAX(register_date) As Max_register_date
FROM tbl_inputs
GROUP BY product_id
) T2 ON(T1.product_id= T2.product_id AND T1.register_date= T2.Max_register_date)
This is, of course, assuming your dates are unique. if they are not, you need to add the DISTINCT Keyword to the outer SELECT statement.
edit
Sorry, I didn't explain it very well. Your dates can be duplicated, it's not a problem as long as they are unique per product id. if you can have duplicated dates per product id, then you will have more then one row per product in the outcome of the select statement I suggested, and you will have to find a way to reduce it to one row per product.
i.e:
If you have records like that (when the last date for a product appears more then once in your table with different prices)
id | product_Id | price | register_date
--------------------------------------------
1 | 1 | 10.00 | 01/01/2000
2 | 1 | 20.00 | 01/01/2000
it will result in having both of these records as outcome.
However, if the register_date is unique per product id, then you will get only one result for each product id.