Using sum function with when case - mysql

SELECT '2017-05-30' AS `short_date`,
'Stats' AS `group`,
'Active ' AS `subgroup`,
'' AS `row`,
CASE
WHEN COUNT(DISTINCT(guild_name)) >= 1 AND COUNT(DISTINCT(guild_name)) <= 5 THEN '1 - 5 '
WHEN COUNT(DISTINCT(guild_name)) >= 6 AND COUNT(DISTINCT(guild_name)) <= 10 THEN '6 - 10 '
WHEN COUNT(DISTINCT(guild_name)) >= 11 AND COUNT(DISTINCT(guild_name)) <= 15 THEN '11 - 15 '
WHEN COUNT(DISTINCT(guild_name)) >= 16 AND COUNT(DISTINCT(guild_name)) <= 20 THEN '16 - 20 '
WHEN COUNT(DISTINCT(guild_name)) >= 21 AND COUNT(DISTINCT(guild_name)) <= 25 THEN '21 - 25 '
WHEN COUNT(DISTINCT(guild_name)) >= 30 THEN '> 30' END AS `value`
FROM table
WHERE guild_name !=0
GROUP BY
`short_date`,
`group`,
`subgroup`
How not to count a value in each case, how to count it once and use in each case?

How about you move the count to a subquery and move the case outside like this:
select `short_date`, `group`, `subgroup`, `row`,
CASE
WHEN nb_guild >= 1 AND nb_guild <= 5 THEN '1 - 5 '
WHEN nb_guild >= 6 AND nb_guild <= 10 THEN '6 - 10 '
WHEN nb_guild >= 11 AND nb_guild <= 15 THEN '11 - 15 '
WHEN nb_guild >= 16 AND nb_guild <= 20 THEN '16 - 20 '
WHEN nb_guild >= 21 AND nb_guild <= 25 THEN '21 - 25 '
WHEN nb_guild >= 30 THEN '> 30'
END AS `value`
from
(
SELECT '2017-05-30' AS `short_date`,
'Stats' AS `group`,
'Active ' AS `subgroup`,
'' AS `row`,
COUNT(DISTINCT(guild_name)) as nb_guild
FROM table
WHERE guild_name !=0
GROUP BY
`short_date`,
`group`,
`subgroup`
) as subquery
This way your count only gets executed once in the subquery and your outer query does its treatment on the resulting resultset.

Related

How to Sum "case when" clause after left join without duplication

How do I get the SUM of an amount in a CASE WHEN clause?
Table real:
id
name
goal
year
10
ronaldo
5
2022
10
ronaldo
5
2022
11
messi
5
2022
11
messi
5
2022
10
ronaldo
10
2021
11
messi
10
2021
Table target:
id
name
goal
year
10
ronaldo
10
2022
11
messi
10
2022
10
ronaldo
10
2021
11
messi
10
2021
I tried inner join, but the result was wrong:
id
name
real 2022
target 2022
real 2021
target 2021
10
ronaldo
20
30
20
30
11
messi
20
30
20
30
Desired result:
id
name
real 2022
target 2022
real 2021
target 2021
10
ronaldo
10
10
10
10
11
messi
10
10
10
10
<?php
$sql = $pdo->prepare("SELECT *,
SUM( case when YEAR(real.year) = YEAR(CURDATE()) then real.goal else 0 end) AS goal_now,
SUM( case when YEAR(real.year) = YEAR(CURDATE() - INTERVAL 1 YEAR) then real.goal else 0 end) AS goal_then,
SUM( case when YEAR(target.year) = YEAR(CURDATE()) then target.goal else 0 end) AS goal_target,
SUM( case when YEAR(target.year) = YEAR(CURDATE() - INTERVAL 1 YEAR) then target.goal else 0 end) AS goal_target_then
FROM real
left join target
on id_real = id_target
group by real.id_real
having
real.id_real LIKE '1%'
");
$sql->execute();
while($data = $sql->fetch()){
?>
select
id,
name,
sum(real_goal_now) as real_goal_now,
sum(real_goal_then) as real_goal_then,
sum(target_goal_now) as target_goal_now,
sum(target_goal_then) as target_goal_then
from
(
select
a.id,
a.name,
case
when a.year = year(curdate()) then a.goal else 0 end as real_goal_now,
case
when a.year = year(curdate() - interval 1 year) then a.goal else 0 end as real_goal_then,
case
when b.year = year(curdate()) then b.goal else 0 end as target_goal_now,
case
when b.year = year(curdate() - interval 1 year) then b.goal else 0 end as target_goal_then
from
(
select
id, name, year, sum(goal) goal
from
real
group by
id, year) a,
(
select
id, name, year, sum(goal) goal
from
target
group by
id, year) b
where
a.id = b.id
and a.year = b.year
group by
a.id, a.year) c
group by
id;
<?php
$sql = $pdo->prepare("SELECT *,
SUM( case when YEAR(real.year) = YEAR(CURDATE()) then real.goal else 0 end) AS goal_now,
SUM( case when YEAR(real.year) = YEAR(CURDATE() - INTERVAL 1 YEAR) then real.goal else 0 end) AS goal_then
FROM real
left join ( select *,
SUM( case when YEAR(target.year) = YEAR(CURDATE()) then target.goal else 0 end) AS goal_target,
SUM( case when YEAR(target.year) = YEAR(CURDATE() - INTERVAL 1 YEAR) then target.goal else 0 end) AS goal_target_then
from target
group by id_target ) as t
on real.id_real = t.id_target
group by real.id_real
having
real.id_real LIKE '1%'
");
$sql->execute();
while($data = $sql->fetch()){
?>

Get specific data from 2 date for a timerange

The data is stored in DB in UTC , I have to convert local to UTC fetch it then show result in local time .
Now I am from India so if I want to search the data for today I have to query from 22nd March 6:30 PM UTC to 23rd March 6:30 PM UTC . Now Say I want to check data for every day of week until now which is 4:30 PM local . Now I wrote this query [ In simple words my objective is to get visitor number of each day from 12 AM to 4:30 PM ]
SELECT
FLOOR(TIMESTAMPDIFF(HOUR, "2017-03-16 18:29:59",
visit.date_created)/24) as dayofweek,
DAYOFWEEK(visit.date_created) day_num,
#rownum := #rownum + 1 as date_created_set_av,
count(distinct(visit.pkey)) AS Visits,
sum(revenue) AS Revenue,
sum(revenue) / count(distinct(visit.pkey)) as EPC
FROM la_20.visit, la_20.action
cross join (select #rownum := 0) r
WHERE visit.pkey=action.pkey and (visit.is_bot = 0)
AND visit.date_created >="2017-03-16 18:29:59"
AND visit.date_created <="2017-03-23 18:29:59"
AND TIME(visit.date_created)<="16:30:00"
GROUP BY dayofweek order by day_num
but what it does is fetch the value from 2017-03-23 00:00:00 to 2017-03-23 16:30:00 . What I need is to show result of 16th to 23rd everyday's data from 18:29:59 to 16:30:00. Remember I need the result of everyday's not one day's. Can anyone help
You should change your WHERE clause into
WHERE visit.pkey=action.pkey and (visit.is_bot = 0)
AND visit.date_created >="2017-03-22 18:29:59" and visit.date_created <="2017-03-23 14:29:59"
UPDATED
Today midnight: DATEADD(d,0,DATEDIFF(d,0,GETDATE()))
Today 4pm: DATEADD(HOUR, 16, DATEADD(d,0,DATEDIFF(d,0,GETDATE())))
WHERE visit.pkey=action.pkey and (visit.is_bot = 0)
AND visit.date_created >= DATEADD(d,0,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,0,DATEDIFF(d,0,GETDATE())))
Update 2:
Then you'll have to expand your where clause like this I guess.
WHERE visit.pkey=action.pkey and (visit.is_bot = 0)
AND (
(visit.date_created >= DATEADD(d, 0,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d, 0,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-1,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-1,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-2,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-2,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-3,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-3,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-4,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-4,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-5,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-5,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-6,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-6,DATEDIFF(d,0,GETDATE()))))
)
Have you tried the following where clause instead?
WHERE visit.pkey=action.pkey and (visit.is_bot = 0)
AND visit.date_created >="2017-03-22 18:29:59"
AND visit.date_created <="2017-03-23 14:29:59"
3. Edit
OK, since OP wants something different again - and why not ;-) - here is another solution that might (or might not) solve his problem:
SELECT
COUNT( CASE WHEN d between
adddate(curdate(), interval -7 day) AND addtime(adddate(curdate(), interval -7 day),'16:00:00')
THEN 1 END ) dm7,
COUNT( CASE WHEN d between
adddate(curdate(), interval -6 day) AND addtime(adddate(curdate(), interval -6 day),'16:00:00')
THEN 1 END ) dm6,
COUNT( CASE WHEN d between
adddate(curdate(), interval -5 day) AND addtime(adddate(curdate(), interval -5 day),'16:00:00')
THEN 1 END ) dm5,
COUNT( CASE WHEN d between
adddate(curdate(), interval -4 day) AND addtime(adddate(curdate(), interval -4 day),'16:00:00')
THEN 1 END ) dm4,
COUNT( CASE WHEN d between
adddate(curdate(), interval -3 day) AND addtime(adddate(curdate(), interval -3 day),'16:00:00')
THEN 1 END ) dm3,
COUNT( CASE WHEN d between
adddate(curdate(), interval -2 day) AND addtime(adddate(curdate(), interval -2 day),'16:00:00')
THEN 1 END ) dm2,
COUNT( CASE WHEN d between
adddate(curdate(), interval -1 day) AND addtime(adddate(curdate(), interval -1 day),'16:00:00')
THEN 1 END ) dm1,
COUNT( CASE WHEN d between
curdate() AND addtime( curdate() ,'16:00:00')
THEN 1 END ) today
FROM (select '2017-03-17 15:00:01' d
union all select '2017-03-19 14:00:01'
union all select '2017-03-19 12:00:01'
union all select '2017-03-19 13:00:01'
union all select '2017-03-19 11:00:01'
union all select '2017-03-20 11:20:01'
union all select '2017-03-20 11:30:01'
union all select '2017-03-20 10:40:01'
union all select '2017-03-20 10:23:01' ) dates
This is an isolated query that works directly on the sample data given in the subquery. It results in the following list ("dm1"=day minus 1):
dm7 | dm6 | dm5 | dm4 | dm3 | dm2 | dm1 | today
1 | 0 | 4 | 4 | 0 | 0 | 0 | 0
You can test it here: http://rextester.com/ITLBL24540

How do I add in a "range of years" to my Case Statement

How do I add in a "range of years" to my Case Statement below? I just need to fix the "INTERVAL 6 YEAR" & "INTERVAL 5 YEAR" criteria.
Here is the criteria I need to model:
If 1~5 years, then 11
If 6~10 years, then 14
If 11+ years, then 18
CASE WHEN hiredate < DATE_SUB(NOW(),INTERVAL 11 YEAR)
THEN '18'
WHEN hiredate < DATE_SUB(NOW(),INTERVAL 6 YEAR)
THEN '14'
WHEN hiredate < DATE_SUB(NOW(),INTERVAL 5 YEAR)
THEN '11'
ELSE '0' END AS Non_Management_Accrual
You can use DATEDIFF method to calculate number of days between two days and divide it by 365 to get years. Based on these, you can write up the case statement, e.g.:
SELECT id, CASE WHEN FLOOR(DATEDIFF(NOW(), hiredate) / 365) < 1
THEN '0'
WHEN FLOOR(DATEDIFF(NOW(), hiredate) / 365) < 6
THEN '11'
WHEN FLOOR(DATEDIFF(NOW(), hiredate) / 365) < 11
THEN '14'
ELSE '18'
END AS Non_Management_Accrual
FROM emp;
Here is the SQL Fiddle.
Darshan, I ended up creating Views on the MySQL side then adding the view into FROM statement of your code above. This gave me the correct output. Thanks.
<!--- Create the Manager Monthly Accruals (Mgt) dataset --->
<cfquery name="rsLeaveTimeManagerAccruals" datasource="care">
SELECT username, status, active, email, hiredate, tblUsers.picture AS eP
, TIMESTAMPDIFF(YEAR, hiredate, NOW()) AS year_passed
, TIMESTAMPDIFF(MONTH, hiredate, NOW()) MOD 12 AS month_passed
, TIMESTAMPDIFF(DAY, hiredate, NOW()) MOD 365 AS day_passed
, CONCAT(TIMESTAMPDIFF(YEAR, hiredate, NOW())," years, ",TIMESTAMPDIFF(MONTH, hiredate, NOW()) MOD 12," months, ",TIMESTAMPDIFF(DAY, hiredate, NOW()) MOD 365," days") AS EmployLengthActive
, TIMESTAMPDIFF(YEAR, hiredate, NOW()) AS monthly_accrual_time
, CASE WHEN FLOOR(DATEDIFF(NOW(), hiredate) / 365) < 1
THEN '0'
WHEN FLOOR(DATEDIFF(NOW(), hiredate) / 365) <= 5
THEN '14'
WHEN FLOOR(DATEDIFF(NOW(), hiredate) / 365) >= 6
THEN '18'
END AS Management_Accrual
FROM v_reports_to_mgt_list LEFT OUTER JOIN tblusers ON tblusers.ID = v_reports_to_mgt_list.id
WHERE clr_accrual = '1' AND hiredate IS NOT NULL
ORDER BY hiredate
</cfquery>

How can I JOIN static values and use them as GROUP BY identifier?

I have created a SELECT statement where I fetch the time difference from now to a certain date in my database.
SELECT
`username`,
TIMESTAMPDIFF(HOUR, `a_date`, NOW()) AS `timediff`
FROM
`User`
... results in something like
username | timediff
-------------------
john | 441
henry | 1624
mike | 4
kyle | NULL
Now I would like to group them into pre defined groups, e.g.
NULL
less than 20
greater than 20
How can I JOIN the above result with a static set of predefined values and how can I use those values as my grouping identifier?
group | amount
--------------
NULL | 1
lt 20 | 1
gt 20 | 2
You can use case statement with group by:
SELECT (CASE WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) < 20 THEN 'lt 20'
WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) >= 20 THEN 'gt 20'
END) as grp, COUNT(*)
FROM `User`
GROUP BY grp;
As a side note: I usually repeat the case statement in the group by, because many databases don't allow column aliases there:
SELECT (CASE WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) < 20 THEN 'lt 20'
WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) >= 20 THEN 'gt 20'
END) as grp, COUNT(*)
FROM `User`
GROUP BY (CASE WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) < 20 THEN 'lt 20'
WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) >= 20 THEN 'gt 20'
END);

How to get time from db depending upon conditions

I have a table in which the value are
Table_hello
date col2
2012-01-31 23:01:01 a
2012-06-2 12:01:01 b
2012-06-3 20:01:01 c
Now i want to select date
in days if it is 3 days before or less
in hours if it is 24 hours before or less
in minutes if it is 60 minutes before or less
in seconds if it is 60 seconds before or less
in simple format if it is before 3days or more
OUTPUT
for row1 2012-01-31 23:01:01
for row2 1 day ago
for row3 1 hour ago
UPDATE
My sql query
select case
when TIMESTAMPDIFF(SECOND, `date`,current_timestamp) <= 60
then concat(TIMESTAMPDIFF(SECOND, `date`,current_timestamp), ' seconds')
when TIMESTAMPDIFF(DAY, `date`,current_timestamp) <= 3
then concat(TIMESTAMPDIFF(DAY, `date`,current_timestamp), ' days')end
when TIMESTAMPDIFF(HOUR, `date`,current_timestamp) <= 60
then concat(TIMESTAMPDIFF(HOUR, `date`,current_timestamp), ' hours')
when TIMESTAMPDIFF(MINUTE, `date`,current_timestamp) <= 60
then concat(TIMESTAMPDIFF(MINUTE, `date`,current_timestamp), ' minutes')
from table_hello
Only problem is i am unable to use break and default in sql like switch case in c++
Use the timestampdiff function for that and CASE:
select case
when TIMESTAMPDIFF(SECOND, `date`,current_timestamp) <= 60
then concat(TIMESTAMPDIFF(SECOND, `date`,current_timestamp), ' seconds')
when TIMESTAMPDIFF(DAY, `date`,current_timestamp) <= 3
and TIMESTAMPDIFF(DAY, `date`,current_timestamp) >= 1
...
end as time_diff
from Table_hello
where TIMESTAMPDIFF(DAY, `time`,current_timestamp) >= 3