SQL multiple sums in one query - mysql

I am trying to learn SQL and am wondering how to write this query for multiple dates. I tried using CASE but it is not outputting the correct totals. This query works.
I am trying to total the dailyrate for each reservation which is the effectively the daily sales.
SELECT SUM(dailyrate) AS 1June
FROM reservations
WHERE start_date < '2018-06-02' AND end_date > '2018-06-01';
This was my attempt using CASE but it does not produce the correct totals.
select dailyrate,
sum(case when start_date < '2018-06-02' AND end_date > '2018-06-01' then 1 else 0 end) as 1june,
sum(case when start_date < '2018-06-03' AND end_date > '2018-06-02' then 1 else 0 end) as 2june,
sum(case when start_date < '2018-06-04' AND end_date > '2018-06-03' then 1 else 0 end) as 3june
FROM reservations;
+------------------+------------------+----------+-
| start_date | end_date | dailyrate |
+------------------+------------------+----------+--
| 2018-06-01 05:00 | 2018-06-01 15:00 | 22 |
| 2018-05-21 05:00 | 2018-06-04 19:00 | 11.5 |
| 2018-06-01 15:00 | 2018-06-07 05:00 | 24 |
| 2018-06-03 05:00 | 2018-06-02 22:00 | 9.5 |
| 2018-05-21 12:00 | 2018-06-11 05:00 | 31 |
+------------------+------------------+----------+-

Are you looking for the COUNT of each daily_rate on each day?
If so, this may be the query you're after:
SELECT dailyrate,
COUNT(CASE WHEN start_date < '2018-06-02' AND end_date > '2018-06-01' THEN 1 ELSE 0 end) AS 1june,
COUNT(CASE WHEN start_date < '2018-06-03' AND end_date > '2018-06-02' THEN 1 ELSE 0 end) AS 2june,
COUNT(CASE WHEN start_date < '2018-06-04' AND end_date > '2018-06-03' THEN 1 ELSE 0 end) AS 3june
FROM reservations
GROUP BY dailyrate;
If you're looking for the SUM of the daily rates for each table, then this query may work for you:
SELECT dailyrate,
SUM(CASE WHEN start_date < '2018-06-02' AND end_date > '2018-06-01' THEN dailyrate ELSE 0 end) AS 1june,
SUM(CASE WHEN start_date < '2018-06-03' AND end_date > '2018-06-02' THEN dailyrate ELSE 0 end) AS 2june,
SUM(CASE WHEN start_date < '2018-06-04' AND end_date > '2018-06-03' THEN dailyrate ELSE 0 end) AS 3june
SUM reservations
GROUP BY dailyrate;
I think you were missing the GROUP BY, as both the SUM and COUNT functions are aggregate functions and need a GROUP BY to show the correct data.

Related

query to find closest lesser date

I have a table with lunch effective date and its rate.
I need to display rate from its nearest lesser effective date (created_on) for each date column.
lunch_rate table:
created_on | rate
-----------+-------
2018-06-01 | 30
2018-06-04 | 60
Here's what I tried to do:
SELECT userId,
SUM(CASE WHEN date= '2018-06-01' AND lunchStatus = 1 THEN (SELECT MAX(rate) FROM lunch_rate WHERE DATE(created_on) <= date LIMIT 1) ELSE 0 END) '2018-06-01',
SUM(CASE WHEN date= '2018-06-02' AND lunchStatus = 1 THEN (SELECT MAX(rate) FROM lunch_rate WHERE DATE(created_on) <= date LIMIT 1) ELSE 0 END) '2018-06-02',
SUM(CASE WHEN date= '2018-06-03' AND lunchStatus = 1 THEN (SELECT MAX(rate) FROM lunch_rate WHERE DATE(created_on) <= date LIMIT 1) ELSE 0 END) '2018-06-03',
SUM(CASE WHEN date= '2018-06-04' AND lunchStatus = 1 THEN (SELECT MAX(rate) FROM lunch_rate WHERE DATE(created_on) <= date LIMIT 1) ELSE 0 END) '2018-06-04'
FROM
(
SELECT userId, lunchStatus, DATE(issuedDateTime) as date
FROM `lunch_status`
WHERE DATE(issuedDateTime) BETWEEN '2018-06-01' AND '2018-06-04'
) as a
GROUP BY userId;
But this query only gives maximum rate of all, without considering the nearest effective date.
Here's the outcome:
userId | 2018-06-01 | 2018-06-02 | 2018-06-03 | 2018-06-04
------------------------------------------------------------------------
131 | 60 | 60 | 0 | 60
132 | 60 | 60 | 60 | 0
133 | 0 | 0 | 0 | 60
134 | 0 | 0 | 0 | 60
Expected outcome:
userId | 2018-06-01 | 2018-06-02 | 2018-06-03 | 2018-06-04
------------------------------------------------------------------------
131 | 30 | 30 | 0 | 60
132 | 30 | 30 | 30 | 0
133 | 0 | 0 | 0 | 60
134 | 0 | 0 | 0 | 60
SUM(CASE WHEN ... THEN (SELECT MAX(rate) FROM lunch_rate WHERE DATE(created_on) <= date LIMIT 1) ELSE 0 END) ....',
How can I select lunch rate that was effective on that date?
If I understand correctly, you want the calculation in the subquery:
SELECT userId,
SUM(CASE WHEN date = '2018-06-01' AND lunchStatus = 1
THEN rate ELSE 0
END) as `2018-06-01`,
SUM(CASE WHEN date = '2018-06-02' AND lunchStatus = 1
THEN rate ELSE 0
END) as `2018-06-02`,
SUM(CASE WHEN date = '2018-06-03' AND lunchStatus = 1
THEN rate ELSE 0
END) as `2018-06-03`,
SUM(CASE WHEN date = '2018-06-04' AND lunchStatus = 1
THEN rate ELSE 0
END) as `2018-06-04`
FROM (SELECT ls.*, DATE(ls.issuedDateTime) as date
(SELECT lr.rate
FROM lunch_rate lr
WHERE DATE(lr.created_on) <= DATE(ls.issuedDateTime)
ORDER BY lr.created_on DESC
LIMIT 1
) as rate
FROM lunch_status ls
WHERE DATE(issuedDateTime) BETWEEN '2018-06-01' AND '2018-06-04'
) lr
GROUP BY lr.userId;
Note the other changes:
The subquery for lunch_rate does not use MAX(). Instead, it uses ORDER BY.
The column aliases are surrounded by backticks, not single quotes. I don't approve of the names (because they need to be escaped). But if you want them, use proper escape characters.
The tables are given reasonable aliases and column names are qualified.
You could try something like this:
SELECT lr1.rate
FROM lunch_rate lr1
WHERE lr1.created_on <= my_date
AND NOT EXISTS (SELECT *
FROM lunch_rate lr2
WHERE lr2.created_on > lr1.created_on
AND lr2.created_on <= my_date);

SQL select all data that past 45 days

I have "alerts" table with date field - targetDate.
I would like to select all data that past 45 days.
I tried the code below but it's not return any results...
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND DATEDIFF( CURDATE( ) , targetDate ) > 45
Table
id userID type refID createDate targetDate lastSendDate sent valid
1 26 travelSoon NO 2018-05-02 13:54:25 0000-00-00 2018-05-02 00:00:00 0 1
2 26 travelSoon NO 2018-05-02 13:55:50 2018-06-01 0000-00-00 00:00:00 0 1
3 26 travelSoon DK 2018-05-02 13:56:12 2018-12-01 0000-00-00 00:00:00 0 1
4 26 travelSoon 2018-05-02 13:59:50 0000-00-00 0000-00-00 00:00:00 0 1
5 26 travelSoon 2018-05-02 14:00:09 2018-08-01 0000-00-00 00:00:00 0 1
6 26 travelSoon DK 2018-05-02 14:00:48 2018-08-01 0000-00-00 00:00:00 0 1
7 26 travelSoon 2018-05-02 16:45:18 2018-05-01 0000-00-00 00:00:00 0 1
8 26 travelSoon RO 2018-05-02 16:45:45 2018-04-01 0000-00-00 00:00:00 0 1
Using DATEDIFF() is a bad idea. It blocks the ability to use indexes, and there is an alternative that doesn't...
SELECT *
FROM alerts
WHERE type = 'travelSoon'
AND targetDate >= DATEADD(DAY, -45, GETDATE()) -- SQL Server
AND targetDate >= CURDATE() - INTERVAL 45 DAY -- MySQL
http://www.sqlfiddle.com/#!9/4ecdc0/6
In MSSQL DATEDIFF(interval, date1, date2) returns interval of date2 - date1.
Interval should be selected from this list:
- year, yyyy, yy = Year
- quarter, qq, q = Quarter
- month, mm, m = month
- dayofyear = Day of the year
- day, dy, y = Day
- week, ww, wk = Week
- weekday, dw, w = Weekday
- hour, hh = hour
- minute, mi, n = Minute
- second, ss, s = Second
- millisecond, ms = Millisecond`
Then use:
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND DATEDIFF(day, targetDate, GETDATE() ) > 45
For MySQL you can use TIMESTAMPDIFF(unit,date1,date2) which returns interval of date1 - date2.
unit can be selected from MICROSECOND (microseconds), SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, or YEAR.
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND TIMESTAMPDIFF(DAY, CURDATE( ), targetDate) > 45
The ANSI Standard syntax would be:
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon' AND
targetDate >= CURRENT_DATE - interval '45 day' AND
targetDate <= CURRENT_DATE;
In MySQL (which your syntax suggests:
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon' AND
targetDate >= CURRENT_DATE - interval 45 day AND
targetDate <= CURRENT_DATE;
Try this...
SELECT userid, refid, `targetdate`
FROM alerts
WHERE type = 'travelSoon'
AND Datediff(Curdate(), targetDate) < 45 -- or <=45
AND Datediff(Curdate(), targetDate) > 0
Online Demo: http://www.sqlfiddle.com/#!9/4ecdc0/4/0
If you only use Datediff(Curdate(), targetDate) < 45 condition, it may return both past and future dates. Please refer the below table.
Today: May 10, 2018
+----+-------------+
| id | targetDate | DATEDIFF(CURDATE(), targetDate)
+----+-------------+
| 2 | 𝟮𝟬𝟭𝟴-𝟬𝟲-𝟬𝟭 | -22
| 3 | 𝟮𝟬𝟭𝟴-𝟭𝟮-𝟬𝟭 | -205
| 5 | 𝟮𝟬𝟭𝟴-𝟬𝟴-𝟬𝟭 | -83
| 6 | 𝟮𝟬𝟭𝟴-𝟬𝟴-𝟬𝟭 | -83
| 7 | 2018-05-01 | 9
| 8 | 2018-04-01 | 39
+----+-------------+
To avoid this, you can use another condition like this...
Datediff(Curdate(), targetDate) > 0
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND targetDate >= ( CURDATE() - INTERVAL 45 DAY )

SUM DISTINCT MYSQL | WHERE CLAUSE

I would like to get results based on SUM from table (history), where username contains 'red' and grouped by month. here the query :
select month(date),
SUM(CASE WHEN status='success' THEN 1 ELSE 0 END) as total_sucess,
SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) as total_failed
from history
where date between '201305%' AND '201311%' AND username like '%#red%'
GROUP BY month(history.date);
the results :
+------------+--------------+--------------+
| month(date) | total_sucess | total_failed |
+------------+--------------+--------------+
| 5 | 10960 | 3573 |
| 6 | 2336 | 1202 |
| 7 | 2211 | 1830 |
| 8 | 5312 | 3125 |
| 9 | 9844 | 5407 |
| 10 | 6351 | 3972 |
+------------+--------------+--------------+
the question is , how do I get distinct total_success and total_failed SUM? just in one query ?
I've tried using this
select month(tgl),
SUM(CASE WHEN status='success' THEN 1 ELSE 0 END) as total_sucess,
SUM(DISTINCT (username) CASE WHEN status='success' THEN 1 ELSE 0 END) as distinct_total_sucess,
SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) as total_failed,
SUM(DISTINCT (username) CASE WHEN status='failed' THEN 1 ELSE 0 END) as distinct_failed_sucess
from history_auth
where tgl between '201305%' AND '201311%' AND username like '%#t.sel%'
GROUP BY month(history_auth.tgl);
but get error sql syntax... i have no idea with this :(
Best I can make out of your requirement is that you want the number of distinct usernames each month that succeeded / failed.
If so I think you need a pair of sub selects to get those figures.
Rejigged the query (adding another sub select to get the 6 months, rather than relying on all months being represented.
SELECT Sub1.aMonth,
SUM(CASE WHEN history.status='success' THEN 1 ELSE 0 END) as total_sucess,
SUM(CASE WHEN history.status='failed' THEN 1 ELSE 0 END) as total_failed,
IFNULL(SuccessCount, 0),
IFNULL(FailedCount, 0)
FROM
(
SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 0 MONTH)) AS aMonth
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 1 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 2 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 3 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 4 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 5 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 6 MONTH))
) Sub1
LEFT OUTER JOIN history
ON MONTH(history.date) = Sub1.aMonth
AND username LIKE '%#red%'
LEFT OUTER JOIN
(
SELECT MONTH(date) AS aMonth, COUNT(DISTINCT username) AS SuccessCount
FROM history
WHERE status='success'
AND username LIKE '%#red%'
GROUP BY MONTH(date)
) Sub2
ON Sub1.aMonth = Sub2.aMonth
LEFT OUTER JOIN
(
SELECT MONTH(date) AS aMonth, COUNT(DISTINCT username) AS FailedCount
FROM history
WHERE status='failed'
AND username LIKE '%#red%'
GROUP BY MONTH(date)
) Sub3
ON Sub1.aMonth = Sub3.aMonth
GROUP BY Sub1.aMonth, SuccessCount, FailedCount

Get data from two tables to make an event-calendar

I have two tables
Table Users:
user_id Name
1 John
2 Alice
3 Tom
4 Charles
Table Events:
id event_id start_date end_date user_id
1 1 2013-03-02 2013-03-03 1
2 2 2013-03-02 2013-03-03 3
3 3 2013-03-04 2013-03-04 1
4 2 2013-03-10 2013-03-15 2
I already made the "input" part and is not possible to have two events for the same user in the same day (no overlapping).
I provide the month and the year as variables.
I would like to have a mysql query with results like this:
user_id 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1 0 1 1 3 0 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2
3 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0
Thank you!
UPDATED
SELECT user_id,
COALESCE(MIN(CASE WHEN 1 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `1`,
COALESCE(MIN(CASE WHEN 2 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `2`,
COALESCE(MIN(CASE WHEN 3 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `3`,
COALESCE(MIN(CASE WHEN 4 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `4`,
COALESCE(MIN(CASE WHEN 5 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `5`,
COALESCE(MIN(CASE WHEN 6 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `6`,
COALESCE(MIN(CASE WHEN 7 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `7`,
COALESCE(MIN(CASE WHEN 8 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `8`,
COALESCE(MIN(CASE WHEN 9 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `9`,
COALESCE(MIN(CASE WHEN 10 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `10`,
COALESCE(MIN(CASE WHEN 11 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `11`,
COALESCE(MIN(CASE WHEN 12 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `12`,
COALESCE(MIN(CASE WHEN 13 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `13`,
COALESCE(MIN(CASE WHEN 14 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `14`,
COALESCE(MIN(CASE WHEN 15 BETWEEN DAY(start_date) AND DAY(end_date) THEN event_id END), 0) `15`
FROM
(
SELECT id, event_id,
CASE WHEN start_date < '2013-03-01' THEN '2013-03-01' ELSE start_date END start_date,
CASE WHEN end_date > '2013-03-31' THEN '2013-03-31' ELSE end_date END end_date,
user_id
FROM events
WHERE (start_date >= '2013-03-01' AND end_date <= '2013-03-31')
OR (start_date < '2013-03-01' AND end_date > '2013-03-31')
OR (start_date < '2013-03-01' AND end_date BETWEEN '2013-03-01' AND '2013-03-31')
OR (start_date BETWEEN '2013-03-01' AND '2013-03-31' AND end_date > '2013-03-31')
) q
GROUP BY user_id
Output:
| USER_ID | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
-----------------------------------------------------------------------------
| 1 | 0 | 1 | 1 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 2 | 2 | 2 | 2 | 2 |
| 3 | 0 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Here is SQLFiddle demo
I tryed this:
SELECT user_id,
COALESCE(MIN(CASE WHEN '2013-03-01' BETWEEN start_date AND end_date THEN event_id END), 0) `1`,
COALESCE(MIN(CASE WHEN '2013-03-02' BETWEEN start_date AND end_date THEN event_id END), 0) `2`,
COALESCE(MIN(CASE WHEN '2013-03-03' BETWEEN start_date AND end_date THEN event_id END), 0) `3`,
COALESCE(MIN(CASE WHEN '2013-03-04' BETWEEN start_date AND end_date THEN event_id END), 0) `4`,
COALESCE(MIN(CASE WHEN '2013-03-05' BETWEEN start_date AND end_date THEN event_id END), 0) `5`,
COALESCE(MIN(CASE WHEN '2013-03-06' BETWEEN start_date AND end_date THEN event_id END), 0) `6`,
COALESCE(MIN(CASE WHEN '2013-03-07' BETWEEN start_date AND end_date THEN event_id END), 0) `7`,
COALESCE(MIN(CASE WHEN '2013-03-08' BETWEEN start_date AND end_date THEN event_id END), 0) `8`,
COALESCE(MIN(CASE WHEN '2013-03-09' BETWEEN start_date AND end_date THEN event_id END), 0) `9`,
COALESCE(MIN(CASE WHEN '2013-03-10' BETWEEN start_date AND end_date THEN event_id END), 0) `10`,
COALESCE(MIN(CASE WHEN '2013-03-11' BETWEEN start_date AND end_date THEN event_id END), 0) `11`,
COALESCE(MIN(CASE WHEN '2013-03-12' BETWEEN start_date AND end_date THEN event_id END), 0) `12`,
COALESCE(MIN(CASE WHEN '2013-03-13' BETWEEN start_date AND end_date THEN event_id END), 0) `13`,
COALESCE(MIN(CASE WHEN '2013-03-14' BETWEEN start_date AND end_date THEN event_id END), 0) `14`,
COALESCE(MIN(CASE WHEN '2013-03-15' BETWEEN start_date AND end_date THEN event_id END), 0) `15`
FROM events
GROUP BY user_id

How do I count multiple specific values for each item?

I'm trying to create a query that will group items by description, and then count that item for the past 3 months, with each month having its own column. Ultimately I'm trying to achieve something like the following:
+-------------+------------+------------+--------------------+
| Description | This Month | Last Month | The Previous Month |
+-------------+------------+------------+--------------------+
| Item_1 | 4 | 3 | 5 |
| Item_2 | 3 | 0 | 2 |
| Item_3 | 1 | 3 | 5 |
+-------------+------------+------------+--------------------+
SELECT
"Description",
SUM(
CASE
WHEN "Date" > NOW() THEN 0
WHEN "Date" > NOW() - INTERVAL 1 MONTH THEN 1
ELSE 0
END CASE
) AS "This month",
SUM(
CASE
WHEN "Date" > NOW() - INTERVAL 1 MONTH THEN 0
WHEN "Date" > NOW() - INTERVAL 2 MONTH THEN 1
ELSE 0
END CASE
) AS "Last month",
SUM(
CASE
WHEN "Date" > NOW() - INTERVAL 2 MONTH THEN 0
WHEN "Date" > NOW() - INTERVAL 3 MONTH THEN 1
ELSE 0
END CASE
) AS "The previous month"
FROM Items
WHERE "Date" > NOW() - INTERVAL 3 MONTH
GROUP BY "Description"