LeetCode 1179: Reformat Department Table (MySQL) [closed] - mysql

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
This is a LeetCode question for interviews.
What would be the most efficient way (time/space complexity) to write this MySQL query? Does the coding style follow the MySQL variable naming conventions?
Problem
Table: Department
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| id | int |
| revenue | int |
| month | varchar |
+---------------+---------+
(id, month) is the primary key of this table.
The table has information about the revenue of each department per month.
The month has values in ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"].
Write an SQL query to reformat the table such that there is a department id column and a revenue column for each month.
The query result format is in the following example:
Department table:
+------+---------+-------+
| id | revenue | month |
+------+---------+-------+
| 1 | 8000 | Jan |
| 2 | 9000 | Jan |
| 3 | 10000 | Feb |
| 1 | 7000 | Feb |
| 1 | 6000 | Mar |
+------+---------+-------+
Result table:
+------+-------------+-------------+-------------+-----+-------------+
| id | Jan_Revenue | Feb_Revenue | Mar_Revenue | ... | Dec_Revenue |
+------+-------------+-------------+-------------+-----+-------------+
| 1 | 8000 | 7000 | 6000 | ... | null |
| 2 | 9000 | null | null | ... | null |
| 3 | null | 10000 | null | ... | null |
+------+-------------+-------------+-------------+-----+-------------+
Note that the result table has 13 columns (1 for the department id + 12 for the months).
Create table If Not Exists Department (id int, revenue int, month varchar(5))
Truncate table Department
insert into Department (id, revenue, month) values ('1', '8000', 'Jan')
insert into Department (id, revenue, month) values ('2', '9000', 'Jan')
insert into Department (id, revenue, month) values ('3', '10000', 'Feb')
insert into Department (id, revenue, month) values ('1', '7000', 'Feb')
insert into Department (id, revenue, month) values ('1', '6000', 'Mar')
Attempt
SELECT id,
SUM(CASE WHEN month = 'jan' THEN revenue END) AS Jan_Revenue,
SUM(CASE WHEN month = 'feb' THEN revenue END) AS Feb_Revenue,
SUM(CASE WHEN month = 'mar' THEN revenue END) AS Mar_Revenue,
SUM(CASE WHEN month = 'apr' THEN revenue END) AS Apr_Revenue,
SUM(CASE WHEN month = 'may' THEN revenue END) AS May_Revenue,
SUM(CASE WHEN month = 'jun' THEN revenue END) AS Jun_Revenue,
SUM(CASE WHEN month = 'jul' THEN revenue END) AS Jul_Revenue,
SUM(CASE WHEN month = 'aug' THEN revenue END) AS Aug_Revenue,
SUM(CASE WHEN month = 'sep' THEN revenue END) AS Sep_Revenue,
SUM(CASE WHEN month = 'oct' THEN revenue END) AS Oct_Revenue,
SUM(CASE WHEN month = 'nov' THEN revenue END) AS Nov_Revenue,
SUM(CASE WHEN month = 'dec' THEN revenue END) AS Dec_Revenue
FROM department
GROUP BY id
ORDER BY id;
Reference
1179. Reformat Department Table

I would probably use MAX as the aggregate function, not SUM, assuming a given id and month would only have one record for revenue. That being said, if you want to use SUM, then your CASE expressions should have an else condition with zero revenue:
SELECT
id,
SUM(CASE WHEN month = 'Jan' THEN revenue ELSE 0 END) AS Jan_Revenue,
SUM(CASE WHEN month = 'Feb' THEN revenue ELSE 0 END) AS Feb_Revenue,
SUM(CASE WHEN month = 'Mar' THEN revenue ELSE 0 END) AS Mar_Revenue,
SUM(CASE WHEN month = 'Apr' THEN revenue ELSE 0 END) AS Apr_Revenue,
SUM(CASE WHEN month = 'May' THEN revenue ELSE 0 END) AS May_Revenue,
SUM(CASE WHEN month = 'Jun' THEN revenue ELSE 0 END) AS Jun_Revenue,
SUM(CASE WHEN month = 'Jul' THEN revenue ELSE 0 END) AS Jul_Revenue,
SUM(CASE WHEN month = 'Aug' THEN revenue ELSE 0 END) AS Aug_Revenue,
SUM(CASE WHEN month = 'Sep' THEN revenue ELSE 0 END) AS Sep_Revenue,
SUM(CASE WHEN month = 'Oct' THEN revenue ELSE 0 END) AS Oct_Revenue,
SUM(CASE WHEN month = 'Nov' THEN revenue ELSE 0 END) AS Nov_Revenue,
SUM(CASE WHEN month = 'Dec' THEN revenue ELSE 0 END) AS Dec_Revenue
FROM department
GROUP BY
id
ORDER BY
id;

Related

How to Query Maximum Integer Value from Substring of Varchar Column with Condition

I want to get the total maximum number of column CODE which the maximum is defined by the last five digits from mybarcode column.
mybarcode | code | judge | create_date |
-------------+------+--------+-------------+
M71X400001 | 7 | pass |
M71X400002 | 7 | pass |
M71X400005 | 7 | pass |
M71X400010 | 7 | pass |
M81X400001 | 8 | pass |
M81X400002 | 8 | pass |
M81X400007 | 8 | pass |
M91X400001 | 9 | pass |
M91X400003 | 9 | pass |
```
Example:
>The maximum value of 7 from CODE column is 10 ( from M71X4'00010')
>The maximum value of 8 from CODE column is 7 ( from M81X4'00007')
>The maximum value of 9 from CODE column is 3 ( from M91X4'00003')
The result should be 10+7+3=20.
And want display in the result table below.
```
SELECT DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail
**??? as number**
from MYTABLE
where MONTH(create_date) = '04' and YEAR(create_date) = '2019'
GROUP BY DAY
Result Table
day | pass | fail | number |
--------+------+--------+----------+
1 | 9 | 0 | 20 |
2 | 9 | 0 | ?? |
3 | 9 | 0 | ?? |
I think you need to do group by two times. Please try below code -
For MySQL -
SELECT
DAY,
SUM(pass),
SUM(fail),
SUM(max_barcode)
FROM (
SELECT
DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail,
Code,
CAST(MAX(SUBSTRING(mybarcode, 5)) AS SIGNED) AS max_barcode
FROM MYTABLE
WHERE MONTH(create_date) = '%s' and YEAR(create_date) = '%s'
GROUP BY DAY, Code
) AS CTE
GROUP BY DAY;
FOR MS SQL Server -
;WITH CTE AS (
SELECT
DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail,
Code,
max_barcode = cast(max(right(mybarcode, 5)) as int)
FROM MYTABLE
WHERE MONTH(create_date) = '%s' and YEAR(create_date) = '%s'
GROUP BY DAY, Code
)
SELECT
DAY,
SUM(pass),
SUM(fail),
SUM(max_barcode)
FROM CTE
GROUP BY DAY;

mysql table data rows to column

I'm trying to achieve yearly data as pivot, this is my query
SELECT
month(te.topup_date) as month,
sum(CASE WHEN year(te.topup_date) = '2015' THEN te.topup_amount+ tp.topup_amount END) as '2015',
sum(CASE WHEN year(te.topup_date) = '2016' THEN te.topup_amount+ tp.topup_amount END) as '2016'
from te_daily_topup as te
inner join tp_daily_topup as tp on year(te.topup_date) = year(tp.topup_date)
where year(te.topup_date) between '2015' and '2016'
group by year(te.topup_date), month(te.topup_date)
The result from the above query
month | 2015 | 2016
--------------------
1 | 123 |
2 | 2343 |
1 | |234
2 | |7667
The result I'm looking for is below:
month | 2015 | 2016
--------------------
1 | 123 | 234
2 | 2343 | 7667
help me to modify this query..
Just remove year() from the GROUP BY:
select month(te.topup_date) as month,
sum(case when year(te.topup_date) = 2015 then te.topup_amount + tp.topup_amount end) as `2015`,
sum(case when year(te.topup_date) = 2016 then te.topup_amount + tp.topup_amount end) as `2016`
from te_daily_topup te inner join
tp_daily_topup tp
on year(te.topup_date) = year(tp.topup_date)
where year(te.topup_date) between 2015 and 2016
group by month(te.topup_date);
Note: Do not use single quotes for integer constants nor for column aliases. Only use single quotes for string and date constants.

SQL: Grouped average-if / case SELECT statement

I have a database that looks like this SQL Fiddle: http://sqlfiddle.com/#!9/aa02e/1
CREATE TABLE Table1
(`Store` varchar(1), `Date` date, `Product` varchar(2), `Weekday` int, `Month` int, `Revenue` float)
;
INSERT INTO Table1
(`Store`, `Date`, `Product`, `Weekday`, `Month`, `Revenue`)
VALUES
('a', '20160101', 'aa', 5, 1, 1.5),
('a', '20160101', 'bb', 5, 1, 4),
('a', '20160101', 'cc', 5, 1, 3.5),
('a', '20160108', 'dd', 5, 1, 2.5),
('a', '20160108', 'ee', 5, 1, 5),
('b', '20160204', 'aa', 4, 2, 9.5),
('b', '20160204', 'bb', 4, 2, 4),
('b', '20160204', 'cc', 4, 2, 3),
('b', '20160211', 'dd', 4, 2, 1.5),
('b', '20160211', 'ee', 4, 2, 2.5)
;
SELECT * FROM table1;
+-------+------------+---------+---------+-------+---------+
| Store | Date | Product | Weekday | Month | Revenue |
+-------+------------+---------+---------+-------+---------+
| a | 2016-01-01 | aa | 5 | 1 | 1.5 |
| a | 2016-01-01 | bb | 5 | 1 | 4 |
| a | 2016-01-01 | cc | 5 | 1 | 3.5 |
| a | 2016-01-08 | dd | 5 | 1 | 2.5 |
| a | 2016-01-08 | ee | 5 | 1 | 5 |
| b | 2016-02-04 | aa | 4 | 2 | 9.5 |
| b | 2016-02-04 | bb | 4 | 2 | 4 |
| b | 2016-02-04 | cc | 4 | 2 | 3 |
| b | 2016-02-11 | dd | 4 | 2 | 1.5 |
| b | 2016-02-11 | ee | 4 | 2 | 2.5 |
+-------+------------+---------+---------+-------+---------+
It shows revenue data for stores incl. products, date and the respective day/month.
I want to select the following:
Store
Monthly revenue totals (i.e. what is the total revenue for store a in Jan?)
Weekday revenue averages (i.e. what is the avg revenue for store a on Thu?)
The first and second bullet are straightforward, but I'm having problems with the last one.
Currently, it takes the average over all products and all dates (assuming the weekday matches). What I need are the following steps:
Sum up all revenues for a store and a particular date (e.g. for store b: 9.5+4+3=16.5 for Feb 4th, and 1.5+2.5=4 for Feb 11th) if that date has the same weekday (here Thursday)
Take the average of the two values (e.g. avg(16.5,4)=10.25)
How can I accomplish that?
Thank you
Here is the query:
SELECT
Store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
AVG(CASE WHEN Weekday = 4 THEN Revenue ELSE NULL END) AS REVENUE_THU,
AVG(CASE WHEN Weekday = 5 THEN Revenue ELSE NULL END) AS REVENUE_FRI
FROM Table1
GROUP BY
Store
;
The weekday average is tricky. Your query is getting the average "order size" per weekday. But you want the total revenue.
One method is to first aggregate by weekday, but that is a bit of a mess. Instead, you can use this trick of calculating the average by dividing the total revenue by the number of days:
SELECT Store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
(SUM(CASE WHEN Weekday = 4 THEN Revenue END) /
COUNT(DISTINCT CASE WHEN Weekday = 4 THEN Date END)
) AS REVENUE_THU,
(SUM(CASE WHEN Weekday = 5 THEN Revenue END) /
COUNT(DISTINCT CASE WHEN Weekday = 5 THEN Date END)
) AS REVENUE_FRI
FROM Table1
GROUP BY Store;
SELECT
t1.store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
daily.REVENUE_THU,
daily.REVENUE_FRI
FROM Table1 t1
JOIN (
SELECT
Store,
weekday,
avg(CASE WHEN weekday = 4 THEN sum_rev END) as REVENUE_THU,
avg(CASE WHEN weekday = 5 THEN sum_rev END) as REVENUE_FRI
FROM (
SELECT
Store, date, weekday,
SUM(revenue) AS sum_rev
FROM Table1
GROUP BY
Store, date, weekday
) AS foo
GROUP BY Store, weekday
) AS daily ON daily.store = t1.store
GROUP BY
t1.store
How about this solution it return average for chosen day of chosen store
CREATE PROCEDURE sumForDayStore(IN vday INTEGER, IN vStore VARCHAR(50))
BEGIN
DECLARE totalDays INTEGER;
DECLARE totalRevenu INTEGER;
SET totalDays = (SELECT count(*) FROM Table1 WHERE WeekDay = vDay AND store = vStore);
SET totalRevenu = (SELECT sum(Revenue) FROM Table1 WHERE WeekDay = vDay AND store = vStore);
SELECT totalRevenu/totalDays;
END;
CALL sumForDayStore(5,'a');
How about this one:
SELECT mnth.Store, REVENUE_JAN, REVENUE_FEB, avg(rthu) REVENUE_THU, avg(rfri) REVENUE_FRI
FROM
(Select Store, sum(case when Month = 1 then Revenue else NULL END) REVENUE_JAN,
sum(case when Month = 2 then Revenue else NULL END) REVENUE_FEB
From Table1 group by Store) as mnth
join
(Select Store, sum(case when Weekday = 4 then Revenue end) rThu,
sum(case when Weekday = 5 then Revenue end) rFri from Table1 group by Store, Date) as dys
on mnth.Store = dys.Store
group by mnth.Store, REVENUE_JAN, REVENUE_FEB
I compared the performance of this with the query in the first answer and it shows better performance according to SQL server execution plan (1.6 times faster). Maybe this would be helpful on a larger data set.

Filtering out good company result based on quarter date in MySQL

I have a table 'QuarterlyReport' which has multiple company's quarterly profit figures with the following column 'CompanyName', 'QuarterEndDate' & 'Profit'
|CompanyName|QuarterEndDate|Profit|
|---------------------------------|
|A |2013-06-30 |29878 |
|A |2013-09-30 |33712 |
|A |2013-12-31 |60764 |
|A |2014-03-31 |260734|
|B |2013-06-30 |-1234 |
|B |2013-09-30 |0 |
|B |2013-12-31 |20114 |
|B |2014-03-31 |-984 |
...
I am trying to construct a MySQL query to see which company has a profit performance which increases in every consecutive quarters (Q4>Q3>Q2>Q1), with a 1 year (4 quarters) date range.
In the case of example above, only Company 'A' will meet this requirement and shall be return as the query's result
Currently I only able to construct query for (Q4>0 AND Q3>0 AND Q2>0) using follow MySQL query:
SELECT * FROM (SELECT q.CompanyName, q.QuarterEndDate, q.Profit FROM `QuarterlyReport` q) a
WHERE a.QuarterEndDate >= '2013-06-30' AND
a.QuarterEndDate < '2014-06-30' AND
a.CompanyName IN (SELECT CompanyName FROM `QuarterlyReport` WHERE
a.CompanyName IN (SELECT Q4.CompanyName FROM `QuarterlyReport` AS Q4 WHERE Q4.QuarterEndDate = '2014-03-31' AND Q4.Profit > '0') AND
a.CompanyName IN (SELECT Q3.CompanyName FROM `QuarterlyReport` AS Q3 WHERE Q3.QuarterEndDate = '2013-12-31' AND Q3.Profit > '0') AND
a.CompanyName IN (SELECT Q2.CompanyName FROM `QuarterlyReport` AS Q2 WHERE Q2.QuarterEndDate = '2013-09-30' AND Q2.Profit > '0') AND
a.CompanyName IN (SELECT Q1.CompanyName FROM `QuarterlyReport` AS Q1 WHERE Q1.QuarterEndDate = '2013-06-30' AND Q1.Profit > '0')
GROUP BY a.CompanyName ORDER BY a.CompanyName ASC
Can anyone suggest some idea on how to archive my targeted query?
You can do
SELECT *
FROM
(
SELECT CompanyName,
MAX(CASE WHEN QUARTER(QuarterEndDate) = 2 THEN Profit END) q1,
MAX(CASE WHEN QUARTER(QuarterEndDate) = 3 THEN Profit END) q2,
MAX(CASE WHEN QUARTER(QuarterEndDate) = 4 THEN Profit END) q3,
MAX(CASE WHEN QUARTER(QuarterEndDate) = 1 THEN Profit END) q4
FROM QuarterlyReport
WHERE QuarterEndDate >= '2013-06-30' AND QuarterEndDate < '2014-06-30'
GROUP BY CompanyName
) q
WHERE q1 < q2 AND q2 < q3 AND q3 < q4
Output:
| COMPANYNAME | Q1 | Q2 | Q3 | Q4 |
|-------------|-------|-------|-------|--------|
| A | 29878 | 33712 | 60764 | 260734 |
Here is SQLFiddle demo
Try:
select q.*
from quarterlyreport q
join
(
select companyname
from quarterlyreport
where quarterenddate between '2013-06-30' and '2014-06-30'
group by companyname
having sum(case when quarter(quarterenddate) = 1 then profit else 0 end)
> sum(case when quarter(quarterenddate) = 4 then profit else 0 end)
and sum(case when quarter(quarterenddate) = 4 then profit else 0 end)
> sum(case when quarter(quarterenddate) = 3 then profit else 0 end)
and sum(case when quarter(quarterenddate) = 3 then profit else 0 end)
> sum(case when quarter(quarterenddate) = 2 then profit else 0 end)
) v
on q.companyname = v.companyname

Weekly report behalf of specific date field

I am using this query for weekly reporting but can not found a way like this
week_number | week_startdate | organization_1 | organization_2
---------------------------------------------------------------
1 | 2013-01--05 |count(date) like 4,24,etc_ | count(date) like 4,24,etc_
SQL:
SELECT WEEK(signed_date) AS week_name, signed_date AS Week_Starting,
YEAR(signed_date), WEEK(signed_date), COUNT(*)
FROM business
WHERE YEAR(signed_date) = YEAR(CURDATE())
GROUP BY CONCAT(YEAR(signed_date), '/', WEEK(signed_date))
ORDER BY YEAR(signed_date), WEEK(signed_date
SAMPLE DATA:
signed_date | organization_id
01-01-2013 | 1
02-01-2013 | 1
03-01-2013 | 2
In 1 week organization_1 have 2 signed & organization_2 has 1 signed.
You should use case within count or sum:
SELECT WEEK(signed_date) AS week_name, signed_date AS Week_Starting,
YEAR(signed_date), WEEK(signed_date),
SUM(CASE WHEN organization_id=1 THEN 1 ELSE 0 END) as organization_1,
SUM(CASE WHEN organization_id=2 THEN 1 ELSE 0 END) as organization_2
FROM business
WHERE YEAR(signed_date) = YEAR(CURDATE())
GROUP BY CONCAT(YEAR(signed_date), '/', WEEK(signed_date))
ORDER BY YEAR(signed_date), WEEK(signed_date);
http://sqlfiddle.com/#!2/587ad/3