CASE statement when criterias are in different rows and columns - mysql

DB Fiddle
CREATE TABLE sales (
campaign VARCHAR(255),
event_type VARCHAR(255),
event_date VARCHAR(255),
quantity VARCHAR(255)
);
INSERT INTO sales
(campaign, event_type, event_date, quantity)
VALUES
("C001", "buy", "2019-05-08", "500"),
("C001", "sale", "2019-04-20", "400"),
("C002", "buy", "2019-06-07", "800"),
("C002", "sale", "2019-06-15", "900"),
("C003", "buy", "2020-02-23", "700"),
("C003", "sale", "2020-03-17", "300"),
("C004", "buy", "2020-04-05", "200"),
("C004", "sale", "2020-03-17", "600");
The table displays the event_types (buy, sell) and the corresponing quantities per campaign.
Now, I want to run a query which checks the following:
a) event_date for event_type buy > event_date for event_type sell
b) quantity for event_type buy > quantity for event_type sell
The result should look like this:
campaign Check_Date Check_Quantity
C001 Error OK
C002 OK Error
C004 OK Error
I think I need to go with the CASE statement somehow like this:
SELECT
campaign,
(CASE WHEN event_date of buy > event_date of sale THEN "Error" ELSE "OK") AS Check_Date,
(CASE WHEN quantity of buy > quantity of sale THEN "Error" ELSE "OK") AS Check_Quantity
quantity
FROM sales;

You want to compare values across rows, so this suggests aggregation. You can then implement the logic with case expressions:
select
campaign,
case when
max(case when event_type = 'buy' then event_date end)
> max(case when event_type = 'sale' then event_date end)
then 'OK'
else 'Error'
end check_date,
case when
max(case when event_type = 'buy' then quantity end)
> max(case when event_type = 'sale' then quantity end)
then 'OK'
else 'Error'
end check_date
from sales
group by campaign;
In your DB Fiddle, this produces:
campaign | check_date | check_date
:------- | :--------- | :---------
C001 | OK | OK
C002 | Error | Error
C003 | Error | OK
C004 | OK | Error

Split the table in 2 queries, then join and use CASE expressions:
select
b.campaign,
case when b.event_date > s.event_date then 'Error' else 'OK' end Check_Date,
case when b.quantity > s.quantity then 'Error' else 'OK' end Check_Quantity
from (select * from sales where event_type = 'buy') b
inner join (select * from sales where event_type = 'sale') s
on s.campaign = b.campaign
See the demo.
Results:
> campaign | Check_Date | Check_Quantity
> :------- | :--------- | :-------------
> C001 | Error | Error
> C002 | OK | OK
> C003 | OK | Error
> C004 | Error | OK

You can use sub-query & do aggregation :
select t.campaign,
(case when buy_dt > sale_dt then 'OK' else 'Error' end) as Check_date,
(case when buy_qty > sale_qty then 'OK' else 'Error' end) as Check_qty
from (select s.campaign,
max(case when s.event_type = 'buy' then s.event_date end) as buy_dt,
max(case when s.event_type = 'sale' then s.event_date end) as sale_dt,
max(case when s.event_type = 'buy' then s.quantity end) as buy_qty,
max(case when s.event_type = 'sale' then s.quantity end) as sale_qty
from sales s
group by s.campaign
) t;

SELECT t1.campaign,
CASE WHEN t1.event_date > t2.event_date
THEN 'OK'
ELSE 'Error'
END Check_Date,
CASE WHEN t1.quantity > t2.quantity
THEN 'OK'
ELSE 'Error'
END Check_Quantity
FROM sales t1
JOIN sales t2 USING ( campaign )
WHERE t1.event_type = 'buy'
AND t2.event_type = 'sale';
fiddle
For the result to be meaningful you MUST add unique index by (campaign, event_type) and restrict values list safe for event_type field (make it ENUM?).
Also you have not specified what must be returned for a campaign when only one record for it exists - my query will not return such campaign at all.

Related

Unified a data based on dates

I Have this query
SELECT DATE_FORMAT(bookings.start_at, '%m/%d/%Y') as "Date",
CASE WHEN payment.type = "CASH" THEN sum(payment.amount) end as "Cash" ,
CASE WHEN payment.type = "Credit" THEN sum(payment.amount) end as "Credit"
from orders
inner join bookings on bookings.id = orders.booking_id
inner join payment on payment.order_id = orders.id
where (bookings.start_at BETWEEN '2022-03-09' AND '2022-03-09 23:59:00')
group by payment.type;
And it returning like this
Date | Cash | Credit
2022/03/09 | NULL | NULL
2022/03/09 | 2000 | NULL
2022/03/09 | NULL | 5000
What i want to achieve is like this
Date | Cash | Credit
2022/03/09 | 2000 | 5000
i already tried GROUP BY for start_at but it return a different kind of time but same date
SELECT DATE_FORMAT(bookings.start_at, '%m/%d/%Y') as `Date`,
SUM(CASE WHEN payment.type = "CASH"
THEN payment.amount
ELSE 0
END) as `Cash`,
SUM(CASE WHEN payment.type = "Credit"
THEN payment.amount
ELSE 0
END) as `Credit`
from orders
inner join bookings on bookings.id = orders.booking_id
inner join payment on payment.order_id = orders.id
where bookings.start_at BETWEEN '2022-03-09' AND '2022-03-09 23:59:00'
group by 1;

Pivot Table MySQL?

I Have A Query :
SELECT item_id,
MAX(CASE WHEN `vendor_id` = 2
THEN price
END) AS "Bandung",
MAX(CASE WHEN `vendor_id` = 3
THEN price
END) AS "Vendor Bandung",
MAX(CASE WHEN `vendor_id` = 4
THEN price
END) AS "Jakarta",
MIN(price)
FROM transactions
GROUP BY item_id
Output:
I Want to get detail information about price from where,.. like below
|ItemID|Bandung|Vendor Bandung|Jakarta |Price Min|*Area*
|10001 |12000 |11000 |9000 |9000 |*Jakarta|*
|10002 |12000 |11000 |12400 |11000 |*Vendor Bandung|*
|10004 |12000 |11000 |12400 |11000 |*Vendor Bandung|*
|10005 |12000 |11000 |12400 |11000 |*Vendor Bandung|*
WITH cte AS (
SELECT item_id,
MAX(CASE WHEN vendor_id = 2
THEN price
END) AS Bandung,
MAX(CASE WHEN vendor_id = 3
THEN price
END) AS `Vendor Bandung`,
MAX(CASE WHEN vendor_id = 4
THEN price
END) AS Jakarta,
MIN(price) MinPrice
FROM transactions
GROUP BY item_id
)
SELECT *,
CASE MinPrice WHEN Bandung THEN 'Bandung'
WHEN `Vendor Bandung` THEN 'Vendor Bandung'
WHEN Jakarta THEN 'Jakarta'
ELSE 'unknown'
END Area
FROM cte
For ancient versions:
SELECT *,
CASE MinPrice WHEN Bandung THEN 'Bandung'
WHEN `Vendor Bandung` THEN 'Vendor Bandung'
WHEN Jakarta THEN 'Jakarta'
ELSE 'unknown'
END Area
FROM
(
SELECT item_id,
MAX(CASE WHEN vendor_id = 2
THEN price
END) AS Bandung,
MAX(CASE WHEN vendor_id = 3
THEN price
END) AS `Vendor Bandung`,
MAX(CASE WHEN vendor_id = 4
THEN price
END) AS Jakarta,
MIN(price) MinPrice
FROM transactions
GROUP BY item_id
) cte
SQL
SELECT DATASET.`bandung`,
DATASET.`vendor bandung`,
DATASET.`jakarta`,
DATASET.`price min`,
( CASE DATASET.`price min`
WHEN DATASET.`bandung` THEN "bandung"
WHEN DATASET.`vendor bandung` THEN "vendor bandung"
WHEN DATASET.`jakarta` THEN "jakarta"
ELSE "otrher"
end ) AS "Area"
FROM (SELECT item_id,
Max(CASE
WHEN `vendor_id` = 2 THEN price
end) AS "Bandung",
Max(CASE
WHEN `vendor_id` = 3 THEN price
end) AS "Vendor Bandung",
Max(CASE
WHEN `vendor_id` = 4 THEN price
end) AS "Jakarta",
Min(price) AS "Price Min"
FROM transactions
GROUP BY item_id) DATASET
Table
SELECT * FROM transactions;
Result

need absent and present count with month name

I need a month name with absent and present count. This is my database query:
SELECT sid,COUNT(CASE WHEN STATUS ='A' THEN 1 END) AS absent_count,COUNT(CASE WHEN STATUS ='P' THEN 1 END) AS present_count,
MONTHNAME(attendance_date) AS `Month_Name`
FROM attendance
WHERE SID = '2'
AND campus_id = 2
GROUP BY sid;
There's no point in group by sid - it will always be '2', as per your where clause. Instead, since you want to count per month name, that should appear in the group by clause:
SELECT MONTHNAME(attendance_date) AS `Month_Name`,
COUNT(CASE WHEN STATUS ='A' THEN 1 END) AS absent_count,
COUNT(CASE WHEN STATUS ='P' THEN 1 END) AS present_count,
FROM attendance
WHERE sid = '2' AND campus_id = 2
GROUP BY MONTHNAME(attendance_date);

Using CASE to align values with user IDs

I have a table which is a custom data table where name value pairs exist. Here's an example of some custom data in this table for an individual:
table custom_data
user_id | custom_data_name | custom_data_value
123 | initial contact date | 2014-01-01
123 | subscription start date | 2014-02-02
123 | favorite fruit | pears
I would like to create a query that parses this out at the individual level so I can see the data side by side like this:
user_id | initial_contact_date | subscription_start_date
123 | 2014-01-01 | 2014-02-02
I tried this:
SELECT
user_id,
CASE WHEN base1.custom_data_name = 'initial contact date' THEN custom_data.data END AS initial_contact_date,
CASE WHEN base1.custom_data_name = 'subscription start date' THEN custom_data.data END AS subscription_start_date
FROM
(
SELECT * FROM custom_data
WHERE custom_data_name = 'initial contact date' OR custom_data_name = 'subscription start date') base1
GROUP BY user_id
But I'm having a hard time interpreting the results. The results show, for each user, a value for EITHER initial_contact_date or subscription_start_date but not both together in one record.
How would I do that?
Try this:
SELECT
user_id,
MAX(CASE WHEN custom_data_name = 'initial contact date' THEN custom_data_value END) AS initial_contact_date,
MAX(CASE WHEN custom_data_name = 'subscription start date' THEN custom_data_value END) AS subscription_start_date
--MAX(CASE WHEN custom_data_name = 'favorite fruit' THEN custom_data_value END) AS favorite_fruit
FROM custom_data
GROUP BY user_id
Sample SQL Fiddle
You need to aggregate the data
select user_id
, min(case
when custom_data_name = 'initial contact date' then custom_data_value
end) initialContactDate
, min(case
when custom_data_name = 'subscription start date' then custom_data_value
end) subscriptionStartDate
from (
select '123' user_id, 'initial contact date' custom_data_name, '2014-01-01' custom_data_value union all
select '123' user_id, 'subscription start date' custom_data_name, '2014-01-02' custom_data_value union all
select '123' user_id, 'favorite fruit' custom_data_name, 'pears' custom_data_value
) tt
group by user_id
USER_ID INITIALCONTACTDATE SUBSCRIPTIONSTARTDATE
-----------------------------------------------------
123 2014-01-01 2014-01-02
SQLFiddle

Sum record between two dates for different users in mysql query Issue

I have two tables lead and lead_details. There is a field status. If status=1 is open, status=2 is close and status=3 is not specified.
I want to find sum of all Open,close and not specified for each user/agent.
Here is what I tried but it give me wrong data
select agent_id,
type,
status,
created_date,
category_id,
sum(case when status = 2 then val else 0 end) as closed1,
sum(case when status = 1 then val else 0 end) as opened1,
sum(case when status = 3 then val else 0 end) as notspecefied1
from ( select l.agent_id,
l.type,
ld.category_id,
l.status,
l.created_date,
count(*) as val
from crm_leads l,
crm_leads_details ld
where l.id=ld.lead_id AND
status in (2, 1, 3)
GROUP BY status, agent_id
) t
WHERE created_date BETWEEN '2013-8-2' AND '2013-9-2'
GROUP BY agent_id
You need to put the WHERE created_date clause in the subquery.
select agent_id,type,status,created_date,category_id,
sum(case when status = 2 then val else 0 end) as closed1,
sum(case when status = 1 then val else 0 end) as opened1,
sum(case when status = 3 then val else 0 end) as notspecefied1
from ( select l.agent_id,l.type,ld.category_id,l.status,l.created_date,
count(*) as val from crm_leads l JOIN crm_leads_details ld
ON l.id=ld.lead_id
WHERE created_date BETWEEN '2013-8-2' AND '2013-9-2' AND status in (2, 1, 3)
GROUP BY status, agent_id ) t
GROUP BY agent_id
Note that the created_date in the result will just be a randomly selected date in the period for each agent.