SQL multiple select with multiple where condition - mysql

I have simple table
ID usr_id card_amount state created
----------------------------------------------------------------
1 a1 10.000 2 2014-03-13 14:33:39
2 a2 30.000 2 2014-03-11 14:33:39
3 a3 50.000 1 2014-03-10 14:33:39
4 a4 20.000 2 2014-04-13 14:33:39
5 a5 40.000 2 2014-03-19 14:33:39
----------------------------------------------------------------
I have this query but it take too long time ! Help me solution to make this faster
SELECT
DATE_FORMAT(`created`, '%Y-%m-%d') AS `key_date`,
(
SELECT
SUM(`card_amount`)
FROM `payment`
WHERE `card_amount`> 0 AND `state` = 2 AND `created` >= `key_date` AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY)
) AS `totalamount`,
(
SELECT
COUNT(`id`)
FROM `payment`
WHERE `card_amount`> 0 AND `state` =2 AND `created` >= `key_date` AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY)
) AS `turncharge`,
(
SELECT
COUNT(DISTINCT `usr_id`)
FROM `payment`
WHERE `card_amount`> 0 AND `state` =2 AND `created` >= `key_date` AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY)
) AS `pu`,
(
SELECT
SUM(`card_amount`)
FROM `payment`
WHERE `card_amount`> 0 AND `state` = 2 AND `created` >= DATE_SUB(`key_date`,INTERVAL 6 DAY) AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY)
) AS `totalamount7`,
(
SELECT
COUNT(`id`)
FROM `payment` WHERE `card_amount`> 0 AND `state` =2 AND `created` >= DATE_SUB(`key_date`,INTERVAL 6 DAY) AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY)
) AS `turncharge7`,
(
SELECT
COUNT(DISTINCT `usr_id`)
FROM `payment`
WHERE `card_amount`> 0 AND `state` =2 AND `created` >= DATE_SUB(`key_date`,INTERVAL 6 DAY) AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY)
) AS `pu7`,
(
SELECT
SUM(`card_amount`)
FROM `payment` WHERE `card_amount`> 0 AND `state` = 2 AND `created` >= DATE_SUB(`key_date`,INTERVAL 29 DAY) AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY)
) AS `totalamount30`,
(
SELECT
COUNT(`id`)
FROM `payment` WHERE `card_amount`> 0 AND `state` =2 AND `created` >= DATE_SUB(`key_date`,INTERVAL 29 DAY) AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY)
) AS `turncharge30`,
(
SELECT
COUNT(DISTINCT `usr_id`)
FROM `payment`
WHERE `card_amount`> 0 AND `state` =2 AND `created` >= DATE_SUB(`key_date`,INTERVAL 29 DAY) AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY)
) AS `pu30`
FROM `payment`
WHERE MONTH(`created`)=$month AND YEAR(`created`)=$year
GROUP BY `key_date`
ORDER BY `key_date` DESC
I use LEFT JOIN but it don't give what i need : c1.totalamount = c2 totalmount7, c1.turncharge =c2.turncharge7 ........(not right)
SELECT DATE_FORMAT(`created`, '%Y-%m-%d') AS `key_date`,c1.totalamount,c1.turncharge,c1.pu,c2.totalamount7,c2.turncharge7,c2.pu7
FROM `payment`
LEFT JOIN (SELECT DATE_FORMAT(`created`, '%Y-%m-%d') AS `date1`,SUM(`card_amount`) AS `totalamount`, COUNT(`id`) AS `turncharge`,COUNT(DISTINCT `usr_id`) AS `pu`
FROM `payment`
WHERE `card_amount`> 0 AND `state` = 2 AND `created` >= DATE_FORMAT(`created`, '%Y-%m-%d')
AND `created` < DATE_SUB(DATE_FORMAT(`created`, '%Y-%m-%d'),INTERVAL -1 DAY) GROUP BY date1) AS c1 ON date1 = DATE_FORMAT(`created`, '%Y-%m-%d')
LEFT JOIN (SELECT DATE_FORMAT(`created`, '%Y-%m-%d') AS `date2`,SUM(`card_amount`) AS `totalamount7`, COUNT(`id`) AS `turnchargep7`,COUNT(DISTINCT `usr_id`) AS `pu7`
FROM `payment`
WHERE `card_amount`> 0 AND `state` = 2 AND created >= DATE_SUB(DATE_FORMAT(`created`, '%Y-%m-%d'),INTERVAL 6 DAY)
AND `created` < DATE_SUB(DATE_FORMAT(`created`, '%Y-%m-%d'),INTERVAL -1 DAY) GROUP BY date2) AS c2 ON date2 = DATE_FORMAT(`created`, '%Y-%m-%d')
WHERE MONTH(`created`)=4 AND YEAR(`created`)=2014
GROUP BY `key_date`
ORDER BY `key_date` DESC
Thanks Mladen Prajdic
I use CASE expression and it worked faster
SELECT date.key_date,
SUM(case when `card_amount`> 0 AND `state` = 2 AND `created` >= `key_date` AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY) then `card_amount` else 0 end) AS totalamount,
COUNT(DISTINCT(case when `card_amount`> 0 AND `state` =2 AND `created` >= date.`key_date` AND `created` < DATE_SUB(date.`key_date`,INTERVAL -1 DAY) then payment.`id` else 0 end)) AS `turncharge`,
COUNT(DISTINCT(case when `card_amount`> 0 AND `state` =2 AND `created` >= `key_date` AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY) then `usr_id` else 0 end) )AS `pu` ,
SUM(case when `card_amount`> 0 AND `state` =2 AND `created` >= DATE_SUB(`key_date`,INTERVAL 6 DAY) AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY) then `card_amount` else 0 end) AS `totalamount7`,
COUNT(DISTINCT(case when `card_amount`> 0 AND `state` =2 AND `created` >= DATE_SUB(date.`key_date`,INTERVAL 6 DAY) AND `created` < DATE_SUB(date.`key_date`,INTERVAL -1 DAY) then payment.`id` else 0 end)) AS `turncharge7`,
COUNT(DISTINCT(case when `card_amount`> 0 AND `state` = 2 AND `created` >= DATE_SUB(date.`key_date`,INTERVAL 6 DAY) AND `created` < DATE_SUB(date.`key_date`,INTERVAL -1 DAY) then `usr_id` else 0 end) )AS `pu7` ,
SUM(case when `card_amount`> 0 AND `state` = 2 AND `created` >= DATE_SUB(`key_date`,INTERVAL 29 DAY) AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY) then `card_amount` else 0 end) AS `totalamount30`,
COUNT(DISTINCT(case when `card_amount`> 0 AND `state` = 2 AND `created` >= DATE_SUB(date.`key_date`,INTERVAL 29 DAY) AND `created` < DATE_SUB(date.`key_date`,INTERVAL -1 DAY) then payment.`id` else 0 end)) AS `turncharge30`,
COUNT(DISTINCT(case when `card_amount`> 0 AND `state` = 2 AND `created` >= DATE_SUB(`key_date`,INTERVAL 29 DAY) AND `created` < DATE_SUB(`key_date`,INTERVAL -1 DAY) then `usr_id` else 0 end) )AS `pu30`
FROM `payment`,(SELECT DATE_FORMAT(`created`, '%Y-%m-%d') AS `key_date` FROM `payment`
WHERE MONTH(`created`)=3 AND YEAR(`created`)=2014
GROUP BY `key_date`) AS date
GROUP BY date.key_date
ORDER BY date.key_date DESC

Rewrite your correlated subqueries with case statements.
This will make a single pass through your table.
How is shown in this article:
Rewriting correlated sub-queries with CASE expressions

Related

MySql version 8.0.17 average datediff

In the table stored an a database MySql version 8.0.17 I have these four field set as datetime type
+---------------------+-----------+---------------------+------------+
| tdate | tigx | tclosed | tcompleted |
+---------------------+-----------+---------------------+------------+
the field tdate never null;
the field tigx it could be null;
the field tclosed it could be null;
the field tcompleted it could be null;
Now I get the average datediff respecting this sequence
If tclosed IS NOT NULL AND tcompleted IS NOT NULL AND tigx IS NOT NULL the AVG is ROUND(AVG(DATEDIFF(tclosed, tdate)),1)
If tclosed IS NULL AND tcompleted IS NULL AND tigx IS NULL the AVG is ROUND(AVG(DATEDIFF(CURDATE(), tdate)),1)
But if at least one of the fields tigx or tclosed or tcompleted is null I have to take the first populated field from tigx or tclosed or tcompleted... and using this first populated field for get the average datediff.
Here I am stuck... any suggestion?
My query below
SELECT
tkt,
tdate,
tstate,
tigx,
tclosed,
tcompleted,
CASE
WHEN ( tclosed IS NOT NULL AND tcompleted IS NOT NULL AND tigx IS NOT NULL ) THEN
ROUND( AVG( DATEDIFF( tclosed, tdate )), 1 )
WHEN ( tclosed IS NULL AND tcompleted IS NULL AND tigx IS NULL ) THEN
ROUND( AVG( DATEDIFF( CURDATE(), tdate )), 1 )
END AS `avg`
FROM
`tbl_c`
WHERE
NOT ( `tkt` LIKE '%I%' OR `tkt` LIKE '%L%' AND tstate IN ( 'Closed' ) AND tigx IS NULL AND tclosed IS NULL AND tcompleted IS NULL )
ORDER BY
tdate DESC;
UPDATE 2
SELECT
tkt,
tdate,
tstate,
tigx,
tclosed,
tcompleted,
CASE
WHEN ( tclosed IS NOT NULL AND tcompleted IS NOT NULL AND tigx IS NOT NULL ) THEN
ROUND( AVG( DATEDIFF( tclosed, tdate )), 1 )
WHEN ( tclosed IS NULL AND tcompleted IS NULL AND tigx IS NULL ) THEN
ROUND( AVG( DATEDIFF( CURDATE(), tdate )), 1 )
WHEN COALESCE(tclosed,tcompleted,tigx) THEN
ROUND(AVG(DATEDIFF(?????, tdate)),1)
END AS `avg`
FROM
`tbl_c`
WHERE
NOT ( `tkt` LIKE '%I%' OR `tkt` LIKE '%L%' AND tstate IN ( 'Closed' ) AND tigx IS NULL AND tclosed IS NULL AND tcompleted IS NULL )
ORDER BY
tdate DESC;
UPDATE
SELECT
tkt,
tdate,
tstate,
tigx,
tclosed,
tcompleted,
CASE
WHEN ( tclosed IS NOT NULL AND tcompleted IS NOT NULL AND tigx IS NOT NULL ) THEN
ROUND( AVG( DATEDIFF( tclosed, tdate )), 1 )
WHEN ( tclosed IS NULL AND tcompleted IS NULL AND tigx IS NULL ) THEN
ROUND( AVG( DATEDIFF( CURDATE(), tdate )), 1 )
WHEN COALESCE ( tclosed ) THEN
ROUND( AVG( DATEDIFF( tcompleted, tdate )), 1 )
WHEN COALESCE ( tcompleted ) THEN
ROUND( AVG( DATEDIFF( tigx, tdate )), 1 )
WHEN COALESCE ( tigx ) THEN
ROUND( AVG( DATEDIFF( tclosed, tdate )), 1 )
END AS `avg`
FROM
`tbl_c`
WHERE
NOT ( `tkt` LIKE '%I%' OR `tkt` LIKE '%L%' AND tstate IN ( 'Closed' ) AND tigx IS NULL AND tclosed IS NULL AND tcompleted IS NULL )
ORDER BY
tdate DESC;
You seem to be struggling with this, following is at least conceptually correct(not sure about the bracketing and outcome)
CASE
WHEN ( tclosed IS NOT NULL AND tcompleted IS NOT NULL AND tigx IS NOT NULL ) THEN
ROUND( AVG( DATEDIFF( tclosed, tdate )), 1 )
WHEN ( tclosed IS NULL AND tcompleted IS NULL AND tigx IS NULL ) THEN
ROUND( AVG( DATEDIFF( CURDATE(), tdate )), 1 )
else
ROUND(AVG(DATEDIFF(COALESCE(tclosed,tcompleted,tigx), tdate)),1
END AS `avg`
I have had a go at this one for you, but I have had to make 2 assumptions:
Dates have rising values across the columns: tdate <= tigx <= tclosed <= tcompleted
All date values are <= CURRENT_DATE
Given those assumptions are correct, you might like to have a look at this. My solution is for you to create a view to handle your date / null logic. I hope it helps:
/*
Create some test data
need this number generator to create data rows
*/
CREATE OR REPLACE VIEW `number_generator_16` AS
SELECT 0 AS `n`
UNION ALL SELECT 1 AS `1`
UNION ALL SELECT 2 AS `2`
UNION ALL SELECT 3 AS `3`
UNION ALL SELECT 4 AS `4`
UNION ALL SELECT 5 AS `5`
UNION ALL SELECT 6 AS `6`
UNION ALL SELECT 7 AS `7`
UNION ALL SELECT 8 AS `8`
UNION ALL SELECT 9 AS `9`
UNION ALL SELECT 10 AS `10`
UNION ALL SELECT 11 AS `11`
UNION ALL SELECT 12 AS `12`
UNION ALL SELECT 13 AS `13`
UNION ALL SELECT 14 AS `14`
UNION ALL SELECT 15 AS `15`
;
/*Create a table to contain test data*/
DROP TABLE IF EXISTS `a_testdatestdate`;
CREATE TABLE `a_testdatestdate` (
`n` INT(10) SIGNED NOT NULL,
`tdate` DATE NOT NULL,
`tigx` DATE DEFAULT NULL,
`tclosed` DATE DEFAULT NULL,
`tcompleted` DATE DEFAULT NULL
) ENGINE=INNODB DEFAULT CHARSET=utf8;
/*
(Truncate - if necessary) and repopulate
TRUNCATE TABLE `a_testdatestdate`;
This statement generates a series of 100 rows where
there is always a value in tdate
the other 4 columns are populated with rising date values
Populated with data in columns 2/3/4/(2-3)/(2-4)/(3-4)/(2-3-4)
ASSUMPTIONS:
1) dates have rising values across the columns: tdate <= tigx <= tclosed <= tcompleted
2) all date values are <= CURRENT_DATE
*/
SET #STARTDATE = DATE_ADD(CURRENT_DATE, INTERVAL -200 DAY);
INSERT INTO `a_testdatestdate` (`n`, `tdate`, `tigx`, `tclosed`, `tcompleted`)
SELECT
`n`.`n`,
DATE_FORMAT(DATE_ADD( #STARTDATE, INTERVAL `n`.`n` DAY),"%Y-%m-%d") AS `tDate`
, CASE
WHEN n.n BETWEEN 10 AND 40 THEN DATE_FORMAT(DATE_ADD( #STARTDATE, INTERVAL `n`.`n` + 1 DAY),"%Y-%m-%d")
WHEN n.n BETWEEN 70 AND 80 THEN DATE_FORMAT(DATE_ADD( #STARTDATE, INTERVAL `n`.`n` + 2 DAY),"%Y-%m-%d")
ELSE NULL
END
, CASE
WHEN n.n BETWEEN 30 AND 50 THEN DATE_FORMAT(DATE_ADD( #STARTDATE, INTERVAL `n`.`n` + 3 DAY),"%Y-%m-%d")
WHEN n.n BETWEEN 90 AND 100 THEN DATE_FORMAT(DATE_ADD( #STARTDATE, INTERVAL `n`.`n` + 4 DAY),"%Y-%m-%d")
ELSE NULL
END
, CASE
WHEN n.n BETWEEN 35 AND 60 THEN DATE_FORMAT(DATE_ADD( CURRENT_DATE, INTERVAL `n`.`n` + 6 DAY),"%Y-%m-%d")
WHEN n.n BETWEEN 70 AND 80 THEN DATE_FORMAT(DATE_ADD( CURRENT_DATE, INTERVAL `n`.`n` + 7 DAY),"%Y-%m-%d")
ELSE NULL
END
FROM (
SELECT
((`hi`.`n` << 4) | `lo`.`n`) AS `n`
FROM (`number_generator_16` `lo`
JOIN `number_generator_16` `hi`)
) `n`
WHERE n.n <100
;
/*
review test data
SELECT * FROM a_testdatestdate;
Create a view to handle your logic around dates and NULL values
*/
DROP VIEW IF EXISTS v_testdatestdate;
CREATE OR REPLACE VIEW v_testdatestdate AS SELECT
n,
tdate,
CASE
WHEN ISNULL(tigx) AND ISNULL(tclosed) AND ISNULL(tcompleted) THEN CURRENT_DATE
WHEN NOT ISNULL(tigx) AND NOT ISNULL(tclosed) AND NOT ISNULL(tcompleted) THEN `tclosed`
ELSE LEAST(IFNULL(tigx, CURRENT_DATE), IFNULL(tclosed, CURRENT_DATE), IFNULL(tcompleted, CURRENT_DATE))
END AS dte
FROM a_testdatestdate;
/*
Then generate your averages from a statement like this
I have left the statement in this state so you can unpick it to suit your requirements
*/
SELECT
ROUND(AVG(`DateDiff`),1)
FROM
(
SELECT
DATEDIFF(dte, tdate) AS `DateDiff`
FROM v_testdatestdate V
) B
;
--> Result = 57.9 in this case.

Show all data in a date range using MYSQL recursive function

I'm trying to get a list of sales for the past 6 months and get 0 values if I have no data for a specific month. So I'm using recursive_all_dates to generate a date range for the past 6 months which works great:
with recursive all_dates(dt) as (
-- anchor
select DATE_SUB(now(), INTERVAL 6 MONTH) dt
union all
-- recursion with stop condition
select dt + interval 1 month from all_dates where dt + interval 1 month <= DATE(now())
)
select DATE_FORMAT(dt, '%Y-%m') as ym from all_dates
This will return:
ym
------
2019-10
2019-11
2019-12
2020-01
2020-02
2020-03
2020-04
Now I want to left join this with my real data:
with recursive all_dates(dt) as (
-- anchor
select DATE_SUB(now(), INTERVAL 6 MONTH) dt
union all
-- recursion with stop condition
select dt + interval 1 month from all_dates where dt + interval 1 month <= now()
)
SELECT
DATE_FORMAT(ad.dt, '%Y-%m') as ym,
sum(profit) as profit
FROM
all_dates as ad
LEFT JOIN organisation_invoices as i
ON
DATE_FORMAT(ad.dt, '%Y-%m') = DATE_FORMAT(i.issue_date, '%Y-%m')
JOIN (
SELECT
invoice_id,
SUM(value) as profit
FROM organisation_invoice_services isrv
GROUP BY invoice_id
) isrv
ON i.id = isrv.invoice_id
WHERE
i.organisation_id = '4b166dbe-d99d-5091-abdd-95b83330ed3a' AND
i.issue_date >= DATE_SUB(NOW(), INTERVAL 6 MONTH)
GROUP BY `ym`
ORDER BY `ym` ASC
But I still only get the populated months:
ym profit
------------------
2019-12 8791
2020-02 302
2020-04 10452
The desired result:
ym profit
------------------
2019-10 0
2019-11 0
2019-12 8791
2020-01 0
2020-02 302
2020-03 0
2020-04 10452
What am I missing?
Edit: Sample data set and fiddle:
CREATE TABLE `organisation_invoices` (
`id` varchar(255) NOT NULL,
`organisation_id` varchar(255) NOT NULL,
`issue_date` date NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `organisation_invoice_services` (
`id` varchar(255) NOT NULL,
`organisation_id` varchar(255) NOT NULL,
`invoice_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`qty` float NOT NULL,
`value` float NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `organisation_invoices` (id, organisation_id, issue_date)
VALUES ('e11cec69-138f-4e20-88e5-5430b6c8d0a1', '4b166dbe-d99d-5091-abdd-95b83330ed3a', '2020-01-20');
INSERT INTO `organisation_invoice_services` (id, organisation_id, invoice_id, qty, `value`)
VALUES ('fe45dfd67-138f-4e20-88e5-5430b6c8d0a1', '4b166dbe-d99d-5091-abdd-95b83330ed3a', 'e11cec69-138f-4e20-88e5-5430b6c8d0a1', 1, 1000);
https://www.db-fiddle.com/f/dibyQi31CBtr2Cr8vjJA8i/0
You can use the following:
with recursive all_dates(dt) as (
-- anchor
select DATE_SUB(now(), INTERVAL 6 MONTH) dt
union all
-- recursion with stop condition
select dt + interval 1 month from all_dates where dt + interval 1 month <= now()
)
SELECT DATE_FORMAT(ad.dt, '%Y-%m') as ym, IFNULL(sum(profit),0) as profit
FROM all_dates as ad
LEFT JOIN organisation_invoices as i
ON DATE_FORMAT(ad.dt, '%Y-%m') = DATE_FORMAT(i.issue_date, '%Y-%m')
LEFT JOIN (
SELECT
invoice_id,
SUM(value) as profit
FROM organisation_invoice_services isrv
GROUP BY invoice_id
) isrv
ON i.id = isrv.invoice_id
WHERE
(i.organisation_id = '4b166dbe-d99d-5091-abdd-95b83330ed3a' AND
i.issue_date >= DATE_SUB(NOW(), INTERVAL 6 MONTH)) OR i.organisation_id IS NULL
GROUP BY `ym`
ORDER BY `ym` ASC
demo on dbfiddle.uk
Changes:
The conditions on the WHERE clause change the behaviour of your LEFT JOIN. Since you check for a specific organization_id, you only get matches between your month table and data (the LEFT JOIN behaves like a INNER JOIN). You need the following WHERE clause instead:
WHERE (i.organisation_id = '4b166dbe-d99d-5091-abdd-95b83330ed3a' AND
i.issue_date >= DATE_SUB(NOW(), INTERVAL 6 MONTH)) OR i.organisation_id IS NULL
You also have to change the second JOIN to a LEFT JOIN.

How to select data from 1 to 5 days with Group By

I've tried it here but I could not, I can only display the total.
I have a download table and a program table.
Every time I download a program I record the date and time, I need to do a grouping of downloaded programs and then 5 columns with the dates, here's an example.
PROGRAMA | HOJE | ONTEM| 2 DIAS | 3 DIAS | 4 DIAS
Programa 1 11 110 55 66 12
Programa 2 25 140 60 90 12
Programa 3 10 20 20 10 10
TOTAL 46 270 135 166 32
Below is my query
select `k`.`app_id` AS `app_id`,`b`.`aplicativo` AS `aplicativo`,count(0) AS `HOJE`,
(select count(0) AS `count(*)` from (`registration` `a` join `aplicativos` `b`) where `k`.`app_id`= `b`.`id` and created_at > (cast(now() as date) - interval 1 day) and (`a`.`created_at` < cast(now() as date)- interval 0 day) ) as ONTEM ,
(select count(0) AS `count(*)` from (`registration` `a` join `aplicativos` `b`) where `k`.`app_id` = `b`.`id`
and created_at > (cast(now() as date) - interval 2 day) and (`a`.`created_at` < cast(now() as date)- interval 1 day) ) as 2_DIAS_ANTES ,
(select count(0) AS `count(*)` from (`registration` `a` join `aplicativos` `b`) where `k`.`app_id` = `b`.`id`
and created_at > (cast(now() as date) - interval 3 day) and (`a`.`created_at` < cast(now() as date)- interval 2 day) ) as 3_DIAS_ANTES ,
(select count(0) AS `count(*)` from (`registration` `a` join `aplicativos` `b`) where `k`.`app_id` = `b`.`id`
and created_at > (cast(now() as date) - interval 4 day) and (`a`.`created_at` < cast(now() as date)- interval 3 day) ) as 4_DIAS_ANTES ,
(select count(0) AS `count(*)` from (`registration` `a` join `aplicativos` `b`) where `k`.`app_id` = `b`.`id`
and created_at > (cast(now() as date) - interval 5 day) and (`a`.`created_at` < cast(now() as date)- interval 4 day) ) as 5_DIAS_ANTES
from (`registration` `k` join `aplicativos` `b`) where ((`k`.`app_id` = `b`.`id`) and (`k`.`created_at` > (cast(now() as date) - interval 0 day)))
group by `b`.`aplicativo`
Table structure
Table aplicativos
CREATE TABLE IF NOT EXISTS `aplicativos` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_usuario` int(11) NOT NULL,
`aplicativo` varchar(200) NOT NULL,
`link` varchar(400) NOT NULL,
`quantidade_notificacoes` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=13 ;
Table registration
CREATE TABLE IF NOT EXISTS `registration` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`gcm_regid` varchar(300) NOT NULL,
`app_id` int(11) NOT NULL,
`email` varchar(200) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=73876 ;
Here is one approach for MySQL:
SELECT a.aplicativo as PROGRAMA,
sum(Date(r.created_at) = CURDATE()) AS HOJE,
sum(date(r.created_at) = DATE_SUB(CURDATE(), INTERVAL 1 DAY), 1, 0)) AS ONTEM,
...
FROM registration r INNER JOIN
aplicativos a
on r.app_id = a.id
GROUP BY r.app_id ;
Does this give you the expected result?
SELECT
a.aplicativo as PROGRAMA,
COUNT(IF(DATE(r.created_at) = CURDATE(), 1, 0)) AS HOJE,
COUNT(IF(DATE(r.created_at) = DATE_SUB(CURDATE(), INTERVAL 1 DAY), 1, 0)) AS ONTEM,
COUNT(IF(DATE(r.created_at) = DATE_SUB(CURDATE(), INTERVAL 2 DAY), 1, 0)) AS 2DIAS,
COUNT(IF(DATE(r.created_at) = DATE_SUB(CURDATE(), INTERVAL 3 DAY), 1, 0)) AS 3DIAS,
COUNT(IF(DATE(r.created_at) = DATE_SUB(CURDATE(), INTERVAL 4 DAY), 1, 0)) AS 4DIAS,
COUNT(IF(DATE(r.created_at) = DATE_SUB(CURDATE(), INTERVAL 5 DAY), 1, 0)) AS 5DIAS
FROM registration r
INNER JOIN aplicativos a
ON r.app_id = a.id
GROUP BY r.app_id, DATE(r.created_at) with ROLLUP;

MYSQL Query using id in subquery or join

I am trying to get the earliest review date for users based on a 28day, 6week and 13week cycle, when a 6week review is performed an entry is also made in the 28day table (effectively resetting it), and when a 13week review is performed an entry is made in both the 28day and 6week table.
This all works fine when I specify a specific user, but I would like to perform a select on the user table and have this calculated for each user and appended to the end of the row.
The offending line are the ones like this
WHERE `user_review_28_user_id` = '6'
that provide the list of users, but only the matching one, in this case user_id=6 is populated.
What I am trying to do is
WHERE `user_review_28_user_id` = `user_id`
but the user_id is not propagated through to the sub-query, therefore I get 'NULL' entries for the user_review_next field and user_review_next_type.
Things I have tried include JOIN and VARIABLES eg,
SELECT *, #user_id:=user_id
FROM `user`
and replacing the offending WHERE with this
WHERE `user_review_28_user_id` = #user_id
This is my query as it stands, been at it several hours and now it is holding me back :(
SELECT `user_id`, `user_first`, `user_last`, `user_review_next`, `user_review_next_type`
FROM `user`
LEFT JOIN (
SELECT *
FROM
(
SELECT `user_review_28_user_id` as user_review_id, DATE_ADD(`user_review_28_date`, INTERVAL 28 DAY) AS 'user_review_next', '1' AS 'user_review_next_type'
FROM `user_review_28`
WHERE `user_review_28_user_id` = '6'
UNION
SELECT `user_id` as user_review_id, DATE_ADD(`user_start_date`, INTERVAL 28 DAY) AS 'user_review_next', '1' AS 'user_review_next_type'
FROM `user`
WHERE `user_id` = '6'
ORDER BY `user_review_next` DESC
LIMIT 1
) AS tmp_28d
UNION
SELECT *
FROM
(
SELECT `user_review_6_user_id` as user_review_id, DATE_ADD(`user_review_6_date`, INTERVAL 6 WEEK) AS 'user_review_next', '2' AS 'user_review_next_type'
FROM `user_review_6`
WHERE `user_review_6_user_id` = '6'
UNION
SELECT `user_id` as user_review_id, DATE_ADD(`user_start_date`, INTERVAL 6 WEEK) AS 'user_review_next', '2' AS 'user_review_next_type'
FROM `user`
WHERE `user_id` = '6'
ORDER BY `user_review_next` DESC
LIMIT 1
) AS tmp_6w
UNION
SELECT *
FROM
(
SELECT `user_review_13_user_id` as user_review_id, DATE_ADD(`user_review_13_date`, INTERVAL 13 WEEK) AS 'user_review_next', '3' AS 'user_review_next_type'
FROM `user_review_13`
WHERE `user_review_13_user_id` = '6'
UNION
SELECT `user_id` as user_review_id, DATE_ADD(`user_start_date`, INTERVAL 13 WEEK) AS 'user_review_next', '3' AS 'user_review_next_type'
FROM `user`
WHERE `user_id` = '6'
ORDER BY `user_review_next` DESC
LIMIT 1
) AS tmp_13w
ORDER BY user_review_next ASC, user_review_next_type DESC
LIMIT 1
) AS tmp_user_review
ON user.user_id = tmp_user_review.user_review_id
This is an example output from the query as shown above.
1 David Berry NULL NULL
2 Joseph Armstrong NULL NULL
3 Thomas Brown NULL NULL
4 Paul Armstrong NULL NULL
5 Calum Blair NULL NULL
6 Craig Bridges 2015-05-27 1
7 Donald Branscombe NULL NULL
8 Kenneth Bacon NULL NULL
9 Jason Bambrick NULL NULL
SQLFiddle
Have you tried this?
SELECT `user_id`, `user_first`, `user_last`, `user_review_next`, `user_review_next_type`
FROM `user`
LEFT JOIN (
SELECT *
FROM
(
SELECT `user_review_28_user_id` as user_review_id, DATE_ADD(`user_review_28_date`, INTERVAL 28 DAY) AS 'user_review_next', '1' AS 'user_review_next_type'
FROM `user_review_28`
UNION
SELECT `user_id` as user_review_id, DATE_ADD(`user_start_date`, INTERVAL 28 DAY) AS 'user_review_next', '1' AS 'user_review_next_type'
FROM `user`
WHERE `user_id` = '6'
ORDER BY `user_review_next` DESC
LIMIT 1
) AS tmp_28d
UNION
SELECT *
FROM
(
SELECT `user_review_6_user_id` as user_review_id, DATE_ADD(`user_review_6_date`, INTERVAL 6 WEEK) AS 'user_review_next', '2' AS 'user_review_next_type'
FROM `user_review_6`
UNION
SELECT `user_id` as user_review_id, DATE_ADD(`user_start_date`, INTERVAL 6 WEEK) AS 'user_review_next', '2' AS 'user_review_next_type'
FROM `user`
WHERE `user_id` = '6'
ORDER BY `user_review_next` DESC
LIMIT 1
) AS tmp_6w
UNION
SELECT *
FROM
(
SELECT `user_review_13_user_id` as user_review_id, DATE_ADD(`user_review_13_date`, INTERVAL 13 WEEK) AS 'user_review_next', '3' AS 'user_review_next_type'
FROM `user_review_13`
UNION
SELECT `user_id` as user_review_id, DATE_ADD(`user_start_date`, INTERVAL 13 WEEK) AS 'user_review_next', '3' AS 'user_review_next_type'
FROM `user`
WHERE `user_id` = '6'
ORDER BY `user_review_next` DESC
LIMIT 1
) AS tmp_13w
ORDER BY user_review_next ASC, user_review_next_type DESC
LIMIT 1
) AS tmp_user_review
ON user.user_id = tmp_user_review.user_review_id
WHERE tmp_user_review= '6'
This is what I have come up with, but now I get an error
#1104 - The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay
Unless I include the line SET SQL_BIG_SELECTS=1; before the query, so I am hoping someone will still jump in and help my sort my SQL woes
SELECT `user_id`,
`user_first`,
`user_last`,
LEAST
(
DATE_ADD(user_review_28_date, INTERVAL 28 DAY),
DATE_ADD(user_review_6_date, INTERVAL 6 WEEK),
DATE_ADD(user_review_13_date, INTERVAL 13 WEEK)
) AS user_review_next_date,
CASE
WHEN LEAST
(
DATE_ADD(user_review_28_date, INTERVAL 28 DAY),
DATE_ADD(user_review_6_date, INTERVAL 6 WEEK),
DATE_ADD(user_review_13_date, INTERVAL 13 WEEK)
) = DATE_ADD(user_review_28_date, INTERVAL 28 DAY) THEN '1'
WHEN LEAST
(
DATE_ADD(user_review_28_date, INTERVAL 28 DAY),
DATE_ADD(user_review_6_date, INTERVAL 6 WEEK),
DATE_ADD(user_review_13_date, INTERVAL 13 WEEK)
) = DATE_ADD(user_review_6_date, INTERVAL 6 WEEK) THEN '2'
WHEN LEAST
(
DATE_ADD(user_review_28_date, INTERVAL 28 DAY),
DATE_ADD(user_review_6_date, INTERVAL 6 WEEK),
DATE_ADD(user_review_13_date, INTERVAL 13 WEEK)
) = DATE_ADD(user_review_13_date, INTERVAL 13 WEEK) THEN '3'
END AS user_review_next_type
FROM `user` AS a
LEFT JOIN
(
SELECT user_review_28_user_id, user_review_28_date
FROM `user_review_28`) AS b_28
ON a.user_id = b_28.user_review_28_user_id
AND b_28.user_review_28_date=(SELECT MAX(user_review_28_date)
FROM `user_review_28` AS c_28
WHERE a.user_id = c_28.user_review_28_user_id
)
LEFT JOIN
(
SELECT user_review_6_user_id, user_review_6_date
FROM `user_review_6`) AS b_6
ON a.user_id = b_6.user_review_6_user_id
AND b_6.user_review_6_date=(SELECT MAX(user_review_6_date)
FROM `user_review_6` AS c_6
WHERE a.user_id = c_6.user_review_6_user_id
)
LEFT JOIN
(
SELECT user_review_13_user_id, user_review_13_date
FROM `user_review_13`) AS b_13
ON a.user_id = b_13.user_review_13_user_id
AND b_13.user_review_13_date=(SELECT MAX(user_review_13_date)
FROM `user_review_13` AS c_13
WHERE a.user_id = c_13.user_review_13_user_id
)
WHERE a.user_assessor_id ='5'
ORDER BY user_review_next_date ASC, user_review_next_type DESC

MySQL nested SELECTs

I'm in over my head with this query. I have a table which looks something like this (simplified):
Date Weight kg
-------------------
2012-04-16 12.4
2012-04-17 9.6
2012-04-16 5.4
2012-04-18 2.8
2012-04-16 4.5
... ...
I want the query to return this result:
Week.no. <3kg 3-7kg >7kg
----------------------------
16 2.8 9.9 22.0
... ... ... ....
This is what I have so far:
SELECT *, CONCAT(WEEK(`Date`)) AS Week, SUM(`Weight`) AS TotalWeight,
(SELECT SUM(`Weight`) FROM tbl_fangster WHERE `Weight` < 3 AND
`Date` >= '2012-04-01 00:00:00' AND `Date` <= '2012-04-30 00:00:00'
AND `Species` = 'Salmon' ) AS SumSmall
FROM tbl_fangster
WHERE `Date` >= '2012-04-01 00:00:00'
AND `Date` <= '2012-04-30 00:00:00'
AND `Species` = 'Salmon'
GROUP BY CONCAT(WEEK(`Date`))
but SumSmall returns the same number on each row, namely the total SumSmall rather than the SumSmall for each week. I've tried to copypaste my GROUP clause into the subquery, but it didn't work.
Use a CASE statement to handle the various conditions.
SELECT *, CONCAT(WEEK(`Date`)) AS Week, SUM(`Weight`) AS TotalWeight,
SUM(CASE WHEN Weight < 3 THEN Weight ELSE 0 END) AS SumSmall,
SUM(CASE WHEN Weight >= 3 AND Weight <= 7 THEN Weight ELSE 0 END) AS SumMedium,
SUM(CASE WHEN Weight > 7 THEN Weight ELSE 0 END) AS SumLarge
FROM tbl_fangster
WHERE `Date` >= '2012-04-01 00:00:00'
AND `Date` <= '2012-04-30 00:00:00'
AND `Species` = 'Salmon'
GROUP BY CONCAT(WEEK(`Date`))