Select count & sum from order table, as well as count & sum from event table where order_id matches & event = expiry - sql

I have 2 tables, one containing Order information and one containing Order Event information, example structure below:
Orders Table:
merchant_id
order_id
amount
order_date
111111
123456
100
2021-07-01
111111
789012
50
2021-07-20
111111
642443
75
2021-08-12
Events Table:
merchant_id
order_id
event
amount
date
111111
789012
EXPIRY
50
2021-08-03
111111
642443
EXPIRY
75
2021-08-28
Desired Output:
I am trying to get a breakdown by Merchant Id and month of:
Order Count
Order Sum
Expiry Count (how many of the orders placed in that month have expired regardless of date expired)
Expiry Sum (value of the expiry count above)
Example Output:
merchant_id
order_month
order_count
order_sum
expiry_count
expiry_sum
111111
7
3
150
1
50
111111
8
1
75
1
50
I have tried a few queries with no luck, the furthest I've gotten is:
select o.merchant_id, extract(month from o.order_date) as order_month, count(o.order_id) as order_count, sum(o.order_amount) as order_sum, count(e.order_id) as expiry_count, sum(e.amount) as expiry_sum
from orders o
left join events e on e.order_id = o.order_id
where o.merchant_id = '111111'
and o.order_date >= '2021-07-01'
group by o.merchant_id, order_month
order by o.merchant_id, order_month
However that outputs the exact same values for order_count & expiry_count, as well as order_sum & expiry_sum. Additionally I need to only retrieve events where event = 'EXPIRY' however I get no results when I add that filter.
Any help would be much appreciated!

Add the condition on event to the join (not the where):
select o.merchant_id, extract(month from o.order_date) as order_month, count(o.order_id) as order_count, sum(o.order_amount) as order_sum, count(e.order_id) as expiry_count, sum(e.amount) as expiry_sum
from orders o
left join events e on e.order_id = o.order_id
and e.event = 'EXPIRY'
where o.merchant_id = '111111'
and o.order_date >= '2021-07-01'
group by o.merchant_id, order_month
order by o.merchant_id, order_month
If you put a condition on an outer joined table in the where clause, you force the join to behave as an inner join (as if you deleted the left keyword).

Related

Is there a way to get the most recent and the original record with a number of conditions included using SQL

I want to get the most recent data and also the original data for each group in a table but with a set of conditions.
Below is the current structure of dataset/table.
Each group can have multiple items
Each item_id can have the same item_name and these are known as change item_names with one significant difference the (). The number inside defines how many iterations of changes are made.
Each item_id can have multiple status but for the example below it is simplified to only 2 status Draft->Approved.
group
date
item_id
item_name
status
price
stock
A
2022-01-01
36FG-34-45
AB-1234
Draft
15
100
B
2022-01-02
28AE-23-67
CD-4567
Approved
30
120
A
2022-01-05
45RE-12-99
DE-1234
Approved
20
300
C
2022-01-07
78ED-14-88
EA-4532
Draft
10
500
B
2022-01-05
45AB-16-77
CD-4567(1)
Draft
35
200
A
2022-01-03
76JJ-98-66
DE-1234(1)
Approved
50
250
A
2022-02-02
17KL-10-43
DE-1234(2)
Draft
12
400
C
2022-03-03
97EE-42-17
AE-2468
Approved
25
450
The output required: take the most recent item_id for each group & when involved in the change process and the status is not equal to approve then take the most recent item_id that has been approved for each group.
Also to note it won't necessarily be the second most recent record per group that is approved can be further back in the timeline and process.
group
date
item_id
item_name
status
price
stock
original_item_id
original_item_name
original_status
original_price
original_stock
A
2022-02-02
17KL-10-43
DE-1234(2)
Draft
12
400
76JJ-98-66
DE-1234(1)
Approved
50
250
B
2022-01-05
28AE-23-67
CD-4567(1)
Draft
35
200
45AB-16-77
CD-4567
Approved
30
120
C
2022-03-03
97EE-42-17
AE-2468
Approved
25
450
NULL
NULL
NULL
NULL
NULL
Your example output for group A shows the original item name (most recent item name for that group that was approved) as DE-1234(1). This has a date of 1/3/2022, however item name DE-1234 has a date of 1/5/2022 making it the most recent item id that was approved from group A. Because of that, my output differs from yours for that reference.
Here is a link to the SQL Fiddle where I recreated this.
Here is the query I created for this:
First we create a CTE that ranks your items by group to get the most recent per group.
WITH cte AS--rank records by group ordered by date DESC
(
SELECT
[group]
,[date]
,item_id
,item_name
,status
,price
,stock
,ROW_NUMBER() OVER (PARTITION BY [group] ORDER BY [date] DESC) AS rn
FROM t
)
Then we get filter the CTE to only approved and re-rank to get the most recently approved by group.
,cte2 AS--rank joined records where status = approved by group ordered by date DESC
(
SELECT
a.[group]
,a.[date]
,a.item_id
,a.item_name
,a.status
,a.price
,a.stock
,b.[group] AS original_group
,b.[date] AS original_date
,b.item_id AS original_item_id
,b.item_name AS original_item_name
,b.status AS original_status
,b.price AS original_price
,b.stock AS original_stock
,ROW_NUMBER() OVER (PARTITION BY a.[group] ORDER BY b.rn) rn--get most recent record that was approved
FROM cte a
LEFT JOIN cte b ON
a.[group] = b.[group]
AND b.rn > a.rn--b is a previous record
AND b.status = 'Approved'
WHERE a.rn = 1--Most recent item id
)
Lastly, we query cte2 filtering for only the most recent record that was approved
SELECT
[group]
,[date]
,item_id
,item_name
,status
,price
,stock
--,original_group
--,original_date
,original_item_id
,original_item_name
,original_status
,original_price
,original_stock
FROM cte2
WHERE rn = 1--filter for most recent record that was approved

Displaying results for fixed values in SQL

I am having difficulty in solving the below problem:
I have a table which contains the shopid, date, hour, category and sales amount.
shopid date hour category amount
------------------------------------
1 date1 7 food 10
1 date1 8 food 15
1 date1 10 misc. 5
2 date1 7 food 6
...................................
I am trying to calculate the total sales amount in each hour by food category and display like the following:
shopid category hour amount
------------------------------------
1 food 6 0
1 food 7 5
1 food 8 20
2 food 9 40
...................................
The shops' opening hours are 6 am -10 pm. So for each hour, there might be any sales or not. I was able to perform the hourly summation. But I am unable to display zero and the time when there are no sales at a particular time (e.g. 6 am or any other time between the opening hours) for each sale category.
Use a left join against a list of hours:
select t.shopid, t.category. g.hour, sum(t.amount)
from generate_series(6,22) as g(hour)
left join the_table t on t.hour = g.hour
group by t.shopid, t.category, g.hour
order by t.shopid, t.category, g.hour;
I am trying to calculate the total sales amount in each hour by food category.
This makes sense, but it doesn't make sense to include the shopid in the results.
To do this, you need to generate the rows -- which are all hours and food categories. Then bring in the actual results using left join:
select c.category. g.hour, coalesce(sum(s.amount), 0)
from generate_series(6, 22) g(hour) cross join
(select distinct category from sales) c left join
sales s
on s.hour = g.hour and s.category = c.category
group by c.category, g.hour
order by c.category, g.hour;
If you want results by shop/category/hour, then you can use the same idea:
select sh.shopid, c.category. g.hour,
coalesce(sum(s.amount), 0)
from generate_series(6, 22) g(hour) cross join
(select distinct category from sales) c cross join
(select distinct shopid from sales) sh left join
sales s
on s.shopid = sh.shopid and
s.hour = g.hour and
s.category = c.category
group by sh.shopid, c.category, g.hour
order by sh.shopid, c.category, g.hour;

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

Firebird Query- Return first row each group

In a firebird database with a table "Sales", I need to select the first sale of all customers. See below a sample that show the table and desired result of query.
---------------------------------------
SALES
---------------------------------------
ID CUSTOMERID DTHRSALE
1 25 01/04/16 09:32
2 30 02/04/16 11:22
3 25 05/04/16 08:10
4 31 07/03/16 10:22
5 22 01/02/16 12:30
6 22 10/01/16 08:45
Result: only first sale, based on sale date.
ID CUSTOMERID DTHRSALE
1 25 01/04/16 09:32
2 30 02/04/16 11:22
4 31 07/03/16 10:22
6 22 10/01/16 08:45
I've already tested following code "Select first row in each GROUP BY group?", but it did not work.
In Firebird 2.5 you can do this with the following query; this is a minor modification of the second part of the accepted answer of the question you linked to tailored to your schema and requirements:
select x.id,
x.customerid,
x.dthrsale
from sales x
join (select customerid,
min(dthrsale) as first_sale
from sales
group by customerid) p on p.customerid = x.customerid
and p.first_sale = x.dthrsale
order by x.id
The order by is not necessary, I just added it to make it give the order as shown in your question.
With Firebird 3 you can use the window function ROW_NUMBER which is also described in the linked answer. The linked answer incorrectly said the first solution would work on Firebird 2.1 and higher. I have now edited it.
Search for the sales with no earlier sales:
SELECT S1.*
FROM SALES S1
LEFT JOIN SALES S2 ON S2.CUSTOMERID = S1.CUSTOMERID AND S2.DTHRSALE < S1.DTHRSALE
WHERE S2.ID IS NULL
Define an index over (customerid, dthrsale) to make it fast.
in Firebird 3 , get first row foreach customer by min sales_date :
SELECT id, customer_id, total, sales_date
FROM (
SELECT id, customer_id, total, sales_date
, row_number() OVER(PARTITION BY customer_id ORDER BY sales_date ASC ) AS rn
FROM SALES
) sub
WHERE rn = 1;
İf you want to get other related columns, This is where your self-answer fails.
select customer_id , min(sales_date)
, id, total --what about other colums
from SALES
group by customer_id
So simple as:
select CUSTOMERID min(DTHRSALE) from SALES group by CUSTOMERID

MS-Access Get price of product at certain order date

I have a table filled with purchase prices, like this:
sku price btw startdate
PCR-CA5425023181515 21,17 € 1 01/01/2009
PCR-CA5425023181515 999,00 € 1 06/06/2009
PCR-CA5425023181515 444,00 € 4 09/07/2009
PCR-CA5425023181515 100,00 € 4 10/08/2009
I have another table filled with orders, like this:
sku quantity orderdate
PCR-CA5425023181515 5 01/05/2009
PCR-CA5425023181515 10 01/12/2009
PCR-CA5425023181515 10 24/12/2009
My goal is to get every purchase price per order from that date.
(For example: when I ordered the product on the first of may (01/05) it cost 21,17 euros.
When I ordered it on the first of december (01/12) it cost 100,00 euros.)
I've been struggling with this for the past hour, but haven't found anything useful yet.
SELECT
O.sku,
O.qty,
PP.price
FROM
Orders O
INNER JOIN Purchase_Prices PP ON
PP.sku = O.sku AND
PP.start_date <= O.order_date
WHERE
NOT EXISTS
(
SELECT
*
FROM
Purchase_Prices PP2
WHERE
PP2.sku = PP.sku AND
PP2.start_date <= O.order_date AND
PP2.start_date > PP.start_date
)
Alternatively:
SELECT
O.sku,
O.qty,
PP.price
FROM
Orders O
INNER JOIN Purchase_Prices PP ON
PP.sku = O.sku AND
PP.start_date <= O.order_date
LEFT OUTER JOIN Purchase_Prices PP2 ON
PP2.sku = O.sku AND
PP2.start_date <= O.order_date AND
PP2.start_date > PP.start_date
WHERE
PP2.sku IS NULL