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)
Related
This question already has answers here:
MYSQL Sum Query with IF Condition
(2 answers)
Closed 2 years ago.
I have a MySQL Table as for example
Date | Branch | shift_time | Total Order | Avg Price
20-06-08 | A | morning | 4 | 5.6
20-06-08 | A | night | 3 | 3.4
20-06-08 | B | morning | 2 | 2.7
20-06-08 | B | night | 6 | 5.9
20-06-09 | A | morning | 9 | 8.9
20-06-09 | A | night | 4 | 6.9
The column shift_time is an enum and will be constant
We need to convert that into single record per each date by branch
Date | Branch | morning_total_order | morning_price | night_total_order | night_avg_price
20-06-08 | A | 4 | 5.6 | 3 | 3.4
20-06-08 | B | 2 | 2.7 | 6 | 5.9
20-06-09 | A | 9 | 8.9 | 4 | 6.9
I tried using GROUP_CONCAT but that query merges the shift time with data. We want that on the column header.
Maybe we need to use CASE WHEN. But I am not sure about that.
You can use conditional aggregation to generate the results you want:
SELECT Date, Branch,
SUM(CASE WHEN shift_time = 'morning' THEN `Total Order` ELSE 0 END) AS morning_total_order,
SUM(CASE WHEN shift_time = 'morning' THEN `Avg Price` * `Total Order` ELSE 0 END) /
SUM(CASE WHEN shift_time = 'morning' THEN `Total Order` ELSE 0 END) AS morning_avg_price,
SUM(CASE WHEN shift_time = 'night' THEN `Total Order` ELSE 0 END) AS night_total_order,
SUM(CASE WHEN shift_time = 'night' THEN `Avg Price` * `Total Order` ELSE 0 END) /
SUM(CASE WHEN shift_time = 'night' THEN `Total Order` ELSE 0 END) AS night_avg_price
FROM shifts
GROUP BY Date, Branch
Output:
Date Branch morning_total_order morning_avg_price night_total_order night_avg_price
20-06-08 A 4 5.6 3 3.4
20-06-08 B 2 2.7 6 5.9
20-06-09 A 9 8.9 4 6.9
Demo on SQLFiddle
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.
This question already has answers here:
How to count most consecutive occurrences of a value in a Column in SQL Server
(2 answers)
Closed 6 years ago.
Let's say I have this table:
date | has_login
------------------------
2015-01-01 | 1
2015-01-02 | 1
2015-01-03 | 0
2015-01-04 | 1
2015-01-05 | 1
2015-01-06 | 1
2015-01-07 | 0
2015-01-08 | 1
How do I get the most number of consecutive logins?
In this case, it should be 3
try
select max(cons) as max_consecutives
from
(
select #cons := case when has_login = 1
then #cons + 1
else 1
end as cons
from your_table
cross join (select #cons := 0) c
order by date
) tmp
I am trying to get a query that will return a result set with reading total within certain hour ranges (defined in the working_hours table) depending on the DAYOFTHEWEEK for the date with a result that looks like:
working | nonworking | weekend | date | group_id
-----------------------------------------------------------------
50.3 | 30.8 | 0 | 2015-04-01 00:00 | 7
40.3 | 60.8 | 0 | 2015-04-01 00:00 | 8
50.3 | 30.8 | 0 | 2015-04-02 00:00 | 7
40.3 | 60.8 | 0 | 2015-04-02 00:00 | 8
Working and Weekend ranges are stored in the database in working_hours, Nonworking time ranges are implied (NOT BETWEEN the other ranges on that day basically)
The tables are as following:
Readings table has the hourly readings, named readings
group_id | reading | datestamp
------------------------------------------------------
7 | 30.8 | 2015-04-01 00:00
7 | 20.2 | 2015-04-01 01:00
7 | 11.2 | 2015-04-02 00:00
7 | 20.2 | 2015-04-02 01:00
8 | 26.2 | 2015-04-01 00:00
8 | 30.2 | 2015-04-01 01:00
8 | 26.2 | 2015-04-02 00:00
8 | 30.2 | 2015-04-02 01:00
Hour Ranges are stored in the working_hours table, the day column is DAYOFTHEWEEK format (1 = Sunday, 2 = Monday, etc):
group_id | day | range_start | range_end | range_type_id | day_type_id
------------------------------------------------------------------------------
7 | 5 | 08:00:00 | 15:59:00 | 1 | 1
7 | 6 | 00:00:00 | 05:59:00 | 1 | 2
7 | 6 | 06:00:00 | 23:59:00 | 2 | 2
7 | 1 | 00:00:00 | 22:59:00 | 2 | 4
7 | 1 | 23:00:00 | 23:59:00 | 1 | 4
Day Types are in the working_hours_day_type table and where things get complicated for me, Weekday and Weekend only have one range but Start/End Weekend have two ranges ('Start Weekend' first range is working hours, second range weekend hours and 'End Weekend' first range is weekend hours, second range working hours).
id | type
------------------
1 | Weekday
2 | Start Weekend
3 | Weekend
4 | End Weekend
Range Types are in the working_hours_range_type table:
id | type
------------------
1 | Working
2 | Weekend
My Mysql knowledge is limited to simple SELECT, INSERT etc and the basics of JOINs - I have found out about HOUR(datestamp) BETWEEN 8 AND 14 but dont know how to get subqueries to iterate within a parent query using WHERE datestamp BETWEEN '2015-04-01 00:00:00' AND '2015-04-02 23:59:00' if in fact thats how its done...
I am not totally clear on what constitutes working hours or non-working hours, but does this work?
SELECT
sum(CASE WHEN rtype.range_type_id = 1 THEN reading ELSE 0 END) AS working
sum(CASE WHEN rtype.range_type_id = 2 THEN reading ELSE 0 END) AS nonworking
CASE WHEN r1.daynum in (7,1) THEN 1 ELSE 0 END as weekend
date(datestamp) as date
r1.group_id
FROM
(SELECT
group_id,
reading,
time(datestamp) as rTime,
case when weekday(datestamp) = 0 THEN 2 #weekday() monday to working hours monday
when weekday(datestamp) = 1 THEN 3
when weekday(datestamp) = 2 THEN 4
when weekday(datestamp) = 3 THEN 5
when weekday(datestamp) = 4 THEN 6
when weekday(datestamp) = 5 THEN 7
when weekday(datestamp) = 6 THEN 1
else NULL
END CASE AS daynum
FROM readings) AS r1
LEFT JOIN working_hours w1
ON (r1.daynum = w1.day)
AND (r1.group_id = w1.group_id)
AND (r1.rTime BETWEEN w1.range_start AND w1.range_end)
LEFT JOIN working_hours_day_type dtype
ON w1.day_type_id = dtype.id
LEFT JOIN working_hours_range_type rtype
ON w1.range_type_id = rtype.id
GROUP BY
CASE WHEN daynum in (7,1) THEN 1 ELSE 0 END,
date(datestamp) as date,
r1.group_id
I have a table of events with timestamps, and types (1 or 0). Im looking to select all the type 0 rows where a type 1 row has a timestamp within 10 (or whatever) seconds.
event_id | type | timestamp
1 | 0 | 2012-1-1 00:00:00
2 | 0 | 2012-1-1 00:00:01
3 | 1 | 2012-1-1 00:00:09
4 | 1 | 2012-1-1 00:00:10
5 | 0 | 2012-1-1 00:00:14
6 | 0 | 2012-1-1 00:00:20
7 | 1 | 2012-1-1 00:00:25
8 | 0 | 2012-1-1 00:00:40
9 | 0 | 2012-1-1 00:00:50
10 | 1 | 2012-1-1 00:01:00
So in this example it would grab rows 1,2, and 6
I know how to do it if I run a new query for each type 0 event, but obviously that can be incredibly slow once the table becomes thousands of rows.
As you suggested, doing this query for each row would be inefficient. However, a JOIN seems to fit your need nicely:
SELECT ones.*
FROM my_table ones
JOIN my_table zeroes
ON zeroes.type = 0 AND
TIME_TO_SEC(TIMEDIFF(ones.timestamp, zeroes.timestamp)) <= 10
WHERE ones.type = 1
SQLFiddle demo
select distinct t1.* from t as t1
JOIN t as t2 on (t2.type=1) and
(t2.timestamp between t1.timestamp
AND t1.timestamp + INTERVAL 10 SECOND
)
where t1.type=0