First, I just used union, and I don't really understand it.
basically, to change the name of the month I usually use
select MONTHNAME (date)
But in this case I dont know Union more deeply so it is very confusing to change the number of months to the name of the month. Can you help me? please don't just give an answer but please also include an explanation.
this my query, and fyi this query was helped by the 'sticky bit' in my previous topic.
SELECT m.month month_table,
coalesce(s.count, 0) cstart,
coalesce(e.count, 0) cend
FROM (SELECT 1 month
UNION ALL
SELECT 2 month
UNION ALL
SELECT 3 month
UNION ALL
SELECT 4 month
UNION ALL
SELECT 5 month
UNION ALL
SELECT 6 month
UNION ALL
SELECT 7 month
UNION ALL
SELECT 8 month
UNION ALL
SELECT 9 month
UNION ALL
SELECT 10 month
UNION ALL
SELECT 11 month
UNION ALL
SELECT 12 month) m
LEFT JOIN (SELECT month(n.start_date) month,
count(*) count
FROM newdata n
GROUP BY month(n.start_date)) s
ON s.month = m.month
LEFT JOIN (SELECT month(n.end_date) month,
count(*) count
FROM newdata n
GROUP BY month(n.end_date)) e
ON e.month = m.month
ORDER BY m.month;
I have tried to replace all 'month' to 'monthname' but the results is 0.
Before i change month to monthname
month_table|cstart|cend
1 | 1 | 0
2 | 0 | 1
3 | 0 | 0
4 | 0 | 0
5 | 0 | 0
6 | 0 | 0
7 | 0 | 0
8 | 1 | 0
9 | 0 | 0
10 | 1 | 2
11 | 0 | 0
12 | 0 | 0
and when i change month to monthname the results be
month_table|cstart|cend
1 | 0 | 0
2 | 0 | 0
3 | 0 | 0
4 | 0 | 0
5 | 0 | 0
6 | 0 | 0
7 | 0 | 0
8 | 0 | 0
9 | 0 | 0
10 | 0 | 0
11 | 0 | 0
12 | 0 | 0
You can just use CONCAT to convert your month number into a date string that you can then pass to MONTHNAME i.e. change the first line of your query to:
SELECT MONTHNAME(CONCAT('2018-', m.month, '-01')) month_table,
Output (for your sample data from your previous question):
month_table cstart cend
January 1 0
February 0 1
March 0 0
April 0 0
May 0 0
June 0 0
July 0 0
August 1 0
September 0 0
October 1 2
November 0 0
December 0 0
SQLFiddle demo
Most people just create look up table for this kind of thing, populate it once then use it. Not only does it perform vastly better, but it’s easily understood and reused.
Another common tick is to create a “calendar” table that contains all the dates within a decade or so, with day names, public holiday indicators, etc, which makes writing queries that output data for each day, even if there is no data for that day, super easy. And it’s completely portable.
Forget the SQL Kung Fu and apply KISS.
Related
I have a dataset like this as shown below, for example row 1, 0 purchase is made for the item priced at $3, 3 purchases are made for item priced at $30. I would need to write a query to provide the summary of how many passes purchased, by categorising the prices into price range of "0-10", "11-20", "21-30", "31-40". I assume case when should be used but I am unsure of how. Please help.
+-----------------+-----------------+--------------------------+--------------------------+
| price_category1 | price_category2 | purchase_count_category1 | purchase_count_category2 |
+-----------------+-----------------+--------------------------+--------------------------+
| 3 | 30 | 0 | 3 |
| 20 | 6 | 1 | 4 |
| 25 | 11 | 4 | 0 |
| 17 | 12 | 0 | 1 |
+-----------------+-----------------+--------------------------+--------------------------+
+------+-------+-------+-------+
| 0-10 | 11-20 | 21-30 | 31-40 |
+------+-------+-------+-------+
| 4 | 2 | 7 | 0 |
+------+-------+-------+-------+
You can try below.
Working Solution
WITH MAIN
AS (SELECT 3 AS price_category1,
30 AS price_category2,
0 purchase_count_category1,
3 AS purchase_count_category2
FROM DUAL
UNION ALL
SELECT 20 AS price_category1,
6 AS price_category2,
1 purchase_count_category1,
4 AS purchase_count_category2
FROM DUAL
UNION ALL
SELECT 25 AS price_category1,
11 AS price_category2,
4 purchase_count_category1,
0 AS purchase_count_category2
FROM DUAL
UNION ALL
SELECT 17 AS price_category1,
12 AS price_category2,
0 purchase_count_category1,
1 AS purchase_count_category2
FROM DUAL),
M2
AS (SELECT price_category1 CAT, purchase_count_category1 CNT FROM MAIN
UNION
SELECT price_category2, purchase_count_category2 FROM MAIN)
SELECT CASE
WHEN CAT >= 0 AND CAT < 11 THEN '0-10'
WHEN CAT >= 11 AND CAT < 21 THEN '11-20'
WHEN CAT >= 21 AND CAT < 31 THEN '21-30'
END
CAT,
SUM (CNT) SUMM
FROM M2
GROUP BY CASE
WHEN CAT >= 0 AND CAT < 11 THEN '0-10'
WHEN CAT >= 11 AND CAT < 21 THEN '11-20'
WHEN CAT >= 21 AND CAT < 31 THEN '21-30'
END
SELECT SUM(CASE WHEN price_category1 BETWEEN 0 AND 10
THEN purchase_count_category1
END) + SUM(CASE WHEN price_category2 BETWEEN 0 AND 10
THEN purchase_count_category2
END) AS `0-10`,
SUM(CASE WHEN price_category1 BETWEEN 11 AND 20
THEN purchase_count_category1
END) + SUM(CASE WHEN price_category2 BETWEEN 11 AND 20
THEN purchase_count_category2
END) AS `11-20`,
SUM(CASE WHEN price_category1 BETWEEN 21 AND 30
THEN purchase_count_category1
END) + SUM(CASE WHEN price_category2 BETWEEN 21 AND 30
THEN purchase_count_category2
END) AS `21-30`
FROM source_table
or
SELECT SUM(CASE WHEN cat BETWEEN 0 AND 10
THEN cnt
END ) AS `0-10`,
SUM(CASE WHEN cat BETWEEN 11 AND 20
THEN cnt
END ) AS `11-20`,
SUM(CASE WHEN cat BETWEEN 21 AND 30
THEN cnt
END ) AS `21-30`
FROM ( SELECT price_category1 cat, purchase_count_category1 cnt
FROM source_table
UNION ALL
SELECT price_category2, purchase_count_category2
FROM source_table ) src
PS. This solution gives "horizontal" output - one row with all needed statistic. If you need "vertical" output then use the solution provided by ismetguzelgun.
Pay attention - my solution is not extendable (if you need to alter the ranges amount or borders you must alter the query text) whereas alternative solution can be extended easily after converting hardcoded ranges borders to according CTE or (the best) additional criteria table.
I'm trying to finalize a query I have that is wanting an average of two metrics, inbound calls and missed calls. But I've never worked this granularly with the day of week and each hour block so I'm not sure if I'm even totalling correctly, let alone get the right average.
Basically, starting at 1/1/18, I want an average of inbound calls and missed calls for each hour from 7am to 6pm monday to friday. We were closed 7 days, and I'm getting 48 rows, so that's what I expect.
So if the last 3 mondays looked like:
creationtimestamp | Legtype1 | answered
07/23/18 08:15:00 | 2 | 0
07/23/18 08:25:00 | 2 | 1
07/23/18 08:35:00 | 2 | 1
07/30/18 08:15:00 | 2 | 0
07/30/18 08:25:00 | 2 | 0
07/30/18 08:35:00 | 2 | 0
07/30/18 08:45:00 | 2 | 1
07/30/18 08:55:00 | 2 | 0
08/06/18 08:15:00 | 2 | 0
08/06/18 08:25:00 | 2 | 1
08/06/18 08:35:00 | 2 | 0
08/06/18 08:45:00 | 2 | 0
That's a total of 12 calls, 4 missed, for monday between 8 and 9 am. If I were querying those three mondays from 8 to 9 I would expect:
Monday | 8 | 4 | 1.3
But I can't figure out how to take the sum of all calls for each individual week day, sum those and divide by the number of that weekday? My query below I currently have ```SUM''' instead of average but I'm not sure how to take the average I needs since it's hinging on the number of each individual weekday.
SELECT
dayname(s.creationtimestamp) as day, -- weekdays
HOUR(s.creationtimestamp) as Hour, -- Hours
sum(case when legtype1 = 2 then 1 else 0 end) as total_calls, -- total inbound
sum(case when legtype1 = 2 and answered = 0 then 1 else 0 end)as total_missed
FROM session s
WHERE (s.creationtimestamp >= '2018-01-01' AND creationtimestamp < now())
and WEEKDAY(s.creationtimestamp) BETWEEN 0 AND 4 -- Monday through friday
AND HOUR(s.creationtimestamp) between 7 and 18 -- 7am to 6pm
GROUP BY dayname(s.creationtimestamp), HOUR(s.creationtimestamp)
order by dayofweek(s.creationtimestamp), hour asc;
To reiterate: The query works but I'm not sure if I'm aggregating correctly based on each weekday and hour block from the 1st of the year to now.
here's a fiddle:
http://sqlfiddle.com/#!9/7b6b72
I think all things are fine for avg you just a bit more, hope below query will help you
select day,Hour,Avg(total_calls) as avg_total_calls,
Avg(total_missed) as avg_total_missed from
(
SELECT
dayname(s.creationtimestamp) as day, -- weekdays
HOUR(s.creationtimestamp) as Hour, -- Hours
sum(case when legtype1 = 2 then 1 else 0 end) as total_calls, -- total inbound
sum(case when legtype1 = 2 and answered = 0 then 1 else 0 end)as total_missed
FROM session s
WHERE (s.creationtimestamp >= '2018-01-01' AND creationtimestamp < now())
and WEEKDAY(s.creationtimestamp) BETWEEN 0 AND 4 -- Monday through friday
AND HOUR(s.creationtimestamp) between 7 and 18 -- 7am to 6pm
GROUP BY dayname(s.creationtimestamp), HOUR(s.creationtimestamp)
) as T group by day,Hour
order by day,Hour asc
This question already has answers here:
MySQL how to fill missing dates in range?
(6 answers)
Closed 6 years ago.
I want the count of my table data having in date range 7 days before from now. So I have tried this query :
SELECT DATE(leads_update_on), IFNULL(COUNT(*),0) leads
FROM tbl_leads
WHERE project_id=4
AND DATE(leads_update_on) >= DATE_SUB('2016-05-11', INTERVAL 6 DAY)
GROUP BY DATE(leads_update_on)
But it returns following result :
`DATE(leads_update_on)|leads
----------------------|-----
2016-05-06 | 7
2016-05-07 | 4`
Since other dates does not have any data but I want the result like below if there is no data in specific date :
`DATE(leads_update_on)|leads
----------------------|-----
2016-05-05 | 0
2016-05-06 | 7
2016-05-07 | 4
2016-05-08 | 0
2016-05-09 | 0
2016-05-10 | 0
2016-05-11 | 0`
What I have to change in my sql query so that I can find the above result. Any help will be appreciated. Thanks in advance.
Sample Input as requested :
`DATE |id
----------------------|-----
2016-05-06 | 1
2016-05-07 | 2
Here only two data is present so for others dates it should return 0 value. It should output like this :
`DATE(date) |leads
----------------------|-----
2016-05-05 | 0
2016-05-06 | 1
2016-05-07 | 1
2016-05-08 | 0
2016-05-09 | 0
2016-05-10 | 0
2016-05-11 | 0`
But using this query :-
SELECT DATE(`date`), IFNULL(COUNT(*),0) leads FROM test where DATE(`date`) >= DATE_SUB('2016-05-11', INTERVAL 6 DAY) GROUP BY DATE(`date`)
It returns below result which I don't want:
`DATE(date) |leads
----------------------|-----
2016-05-06 | 1
2016-05-07 | 1`
from what I understand you need to change your if nullcondition
Updated
select distinct(res.leadDate), res.leads from
(select mon.aDate as leadDate , ifnull(sa.leads, 0) as leads
from (
select '2016-05-11' - interval (a.a ) day as aDate from
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) a
) mon
left join tbl_leads sa on mon.aDate = sa.leads_date ) res, tbl_leads ss
where res.leadDate between ss.leads_date and '2016-05-11'
order by res.leadDate asc;
This question already has answers here:
need to return two sets of data with two different where clauses
(2 answers)
Closed 8 years ago.
I'm building a feedback tool, and I have a feedback table that the following structure:
ID | Satisfaction | Timestamp
--------------------------------------------
1 0 2014-01-01 00:00:00
2 5 2014-01-01 00:00:00
3 10 2014-01-02 00:00:00
4 5 2014-01-02 00:00:00
5 10 2014-01-03 00:00:00
6 0 2014-01-03 00:00:00
7 10 2014-01-03 00:00:00
8 5 2014-01-04 00:00:00
9 5 2014-01-04 00:00:00
How can I get a daily count of the number of each "satisfaction" value?
For example:
Date | 0's | 5's | 10's
--------------------------------------
2014-01-01 | 1 | 1 | 0
2014-01-02 | 0 | 1 | 1
2014-01-03 | 1 | 0 | 2
2014-01-04 | 0 | 2 | 0
I'd imagine it involves a GROUP BY timestamp, but I'm not sure how to select
The simplest way to pivot this data in MySQL:
select date(timestamp),
sum(satisfaction = 0) as zeroes,
sum(satisfaction = 5) as fives,
sum(satisfaction = 10) as tens
from feedback
group by date(timestamp);
Solved! I was able to grab the counts of the individual values using a combination of sum() and case statements.
SELECT
DATE(timestamp),
IFNULL(sum(case when satisfaction = 0 then 1 end), 0) as 'unhappy',
IFNULL(sum(case when satisfaction = 5 then 1 end), 0) as 'neutral',
IFNULL(sum(case when satisfaction = 10 then 1 end), 0) as 'happy'
FROM feedback
GROUP BY DATE(timestamp)
What I want to do is select all the IDs that have the most matches and list them in order of the most hits
TABLE - SUNDAY
ID | 8AM | 9AM | 10AM | 11AM
A | 0 | 1 | 0 | 0
B | 0 | 0 | 1 | 1
C | 0 | 0 | 0 | 1
TABLE - MONDAY
ID | 8AM | 9AM | 10AM | 11AM
A | 0 | 0 | 1 | 1
B | 0 | 1 | 0 | 0
C | 0 | 0 | 0 | 1
TABLE - TUESDAY
ID | 8AM | 9AM | 10AM | 11AM
A | 0 | 1 | 0 | 0
B | 0 | 1 | 0 | 0
C | 0 | 0 | 0 | 1
Ex: I want to find all IDs that have Sunday at 9am and 11am, monday at 10am and 11am and tuesday at 9am and then order them by the most hits
I would get the following returned.
A 4 Hits
B 2 hits
C 2 hits
First, let's get a query to massage the data into something a bit easier to work with:
SELECT ID, 0 AS dow, 8 as hr, `8AM` AS hits
FROM SUNDAY
UNION ALL
SELECT ID, 0 AS dow, 9 as hr, `9AM` AS hits
FROM SUNDAY
UNION ALL
SELECT ID, 0 AS dow, 10 as hr, `10AM` AS hits
FROM SUNDAY
UNION ALL
SELECT ID, 0 AS dow, 11 as hr, `11AM` AS hits
FROM SUNDAY
UNION ALL
SELECT ID, 1 AS dow, 8 as hr, `8AM` AS hits
FROM MONDAY
UNION ALL
SELECT ID, 1 AS dow, 9 as hr, `9AM` AS hits
FROM MONDAY
UNION ALL
SELECT ID, 1 AS dow, 10 as hr, `10AM` AS hits
FROM MONDAY
UNION ALL
SELECT ID, 1 AS dow, 11 as hr, `11AM` AS hits
FROM MONDAY
UNION ALL
SELECT ID, 2 AS dow, 8 as hr, `8AM` AS hits
FROM TUESDAY
UNION ALL
SELECT ID, 2 AS dow, 9 as hr, `9AM` AS hits
FROM TUESDAY
UNION ALL
SELECT ID, 2 AS dow, 10 as hr, `10AM` AS hits
FROM TUESDAY
UNION ALL
SELECT ID, 2 AS dow, 11 as hr, `11AM` AS hits
FROM TUESDAY
SQL Fiddle example of the data this returns
Then you can just select from this (derived) table:
SELECT ID, SUM(hits) AS hits
FROM
(
--above query, either as a view or derived table
--or permanently changed
) hitsTable
WHERE (dow = 0 AND hr IN (9,11))
OR (dow = 1 AND hr IN (10,11))
OR (dow = 2 AND hr = 9)
GROUP BY ID
ORDER BY SUM(hits) DESC
SQL Fiddle example
Note the simplicity of the WHERE clause here. If you have control over your schema I would recommend permanently changing the way data is stored into this format. It will make your life much easier. If you can't, I suggest at least making a view that combines all the tables in this fashion, so you can easily query against them.
Can you do it with your current schema? Sure, and it may seem "shorter" or "simpler" but it's not as pretty if you are building these queries dynamically:
SELECT ID, SUM(hits) AS hits
FROM
(
SELECT ID, `9AM` + `11AM` AS hits
FROM SUNDAY
UNION ALL
SELECT ID, `10AM` + `11AM` AS hits
FROM MONDAY
UNION ALL
SELECT ID, `9AM` AS hits
FROM TUESDAY
) x
GROUP BY ID
ORDER BY SUM(hits) DESC