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

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;

Related

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.

Get result in single query rather then three different query

Table structure and sample data
CREATE TABLE IF NOT EXISTS `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`customer_id` int(11) NOT NULL,
`restaurant_id` int(11) NOT NULL,
`bill_id` int(11) NOT NULL,
`source_id` int(1) NOT NULL,
`order_medium_id` int(11) NOT NULL,
`purchase_method` varchar(255) NOT NULL,
`totalamount` int(11) NOT NULL,
`delivery_charg` int(11) NOT NULL,
`discount` int(11) NOT NULL,
`vat` int(11) NOT NULL,
`total_price` int(11) NOT NULL DEFAULT '0',
`date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `customer_id` (`customer_id`),
KEY `source_id` (`source_id`),
KEY `restaurant_id` (`restaurant_id`),
KEY `bill_id` (`bill_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=22 ;
--
-- Dumping data for table `orders`
--
INSERT INTO `orders` (`id`, `customer_id`, `restaurant_id`, `bill_id`, `source_id`, `order_medium_id`, `purchase_method`, `totalamount`, `delivery_charg`, `discount`, `vat`, `total_price`, `date_created`) VALUES
(1, 1, 1, 1, 1, 0, 'cash', 1600, 0, 0, 0, 1600, '2016-05-29 13:05:40'),
(2, 2, 1, 2, 2, 1, 'cash', 1820, 0, 0, 0, 1820, '2016-06-27 07:21:25'),
(4, 1, 1, 3, 3, 0, 'cash', 1770, 0, 0, 0, 1770, '2016-05-31 13:05:56'),
(5, 3, 1, 4, 2, 1, 'cash', 1300, 0, 0, 0, 1300, '2016-06-27 07:21:31'),
(6, 1, 1, 5, 1, 0, 'cash', 950, 0, 0, 0, 950, '2016-06-02 13:06:15'),
(7, 1, 1, 6, 1, 0, 'cash', 1640, 0, 0, 0, 1640, '2016-06-03 13:06:24'),
(8, 1, 1, 7, 2, 2, 'cash', 1600, 0, 0, 0, 1600, '2016-06-27 07:21:36'),
(9, 1, 1, 8, 2, 2, 'cash', 1575, 0, 0, 0, 1575, '2016-06-27 07:21:40'),
(10, 1, 1, 9, 3, 0, 'cash', 1125, 0, 0, 0, 1125, '2016-06-06 13:06:48'),
(11, 1, 1, 10, 2, 3, 'cash', 1920, 0, 0, 0, 1920, '2016-06-27 07:21:51');
Requirement :
I want to segment records as per customer as following.
Get Rating on the basis of last purchase by customer
1. customers who ordered in last 2 week then give ratingflag 5
2. customers who ordered between 2 weeks to 4 week then give ratingflag 3
3. customers who ordered between 4 weeks to 8 week then give ratingflag 2
and so on.
Get Rating on the basis of number of order by customer
1. Customer who ordered more then 5 in a month then give rating 5
2. Customer who ordered less then 5 and more then in a month then 4 give rating 4
and so on.
Get Rating on the basis of total transaction by customer
1. Customer who ordered more then 5000 rs in a month then give rating 5
2. Customer who ordered less then 5000 rs and more then in a month then 4000 give rating 4
and so on.
Customer should be unique. We write three different query for getting records according to requirement.
I tried following . Is there any way to get result in single query. I would appreciate if you could help me with better approach of doing the same :
1.) Query for last purchase
select o.customer_id,
(case when max(date_created) >= date_sub(now(), interval 2 week) then 5
when max(date_created) >= date_sub(now(), interval 4 week) then 4
when max(date_created) >= date_sub(now(), interval 8 week) then 3
when max(date_created) >= date_sub(now(), interval 10 week) then 2
when max(date_created) >= date_sub(now(), interval 12 week) then 1
end) as rating
from orders o where o.restaurant_id = 1
group by o.customer_id;
Output
customer_id rating
1 5
2 5
5 5
2.) Query for number of order
select o.customer_id,
(case when count(bill_id) >= 6 then 5
when count(bill_id) >= 4 and count(bill_id) < 6 then 4
when count(bill_id) >= 3 and count(bill_id) < 4 then 3
when count(bill_id) >= 2 and count(bill_id) < 3 then 2
when count(bill_id) >= 1 then 1
end) as rating
from orders o where o.restaurant_id = 1
group by o.customer_id
Output
customer_id rating
1 5
2 1
5 1
3.) Query for total transaction by customer
select o.customer_id,
(case when sum(total_price) >= 5000 then 5
when sum(total_price) >= 3000 and sum(total_price) < 5000 then 4
when sum(total_price) >= 2000 and sum(total_price) < 3000 then 3
when sum(total_price) >= 1000 and sum(total_price) < 2000 then 2
when sum(total_price) < 1000 then 1
end) as rating
from orders o where o.restaurant_id = 1
group by o.customer_id
Output
customer_id rating
1 5
2 2
5 2
Expected Output
customer_id R1 R2 R3
1 5 5 5
2 5 1 2
3 5 1 2
select o.customer_id,
(case when max(date_created) >= date_sub(now(), interval 2 week) then 5
when max(date_created) >= date_sub(now(), interval 4 week) then 4
when max(date_created) >= date_sub(now(), interval 8 week) then 3
when max(date_created) >= date_sub(now(), interval 10 week) then 2
when max(date_created) >= date_sub(now(), interval 12 week) then 1
end) as rating1,
(case when count(bill_id) >= 6 then 5
when count(bill_id) >= 4 and count(bill_id) < 6 then 4
when count(bill_id) >= 3 and count(bill_id) < 4 then 3
when count(bill_id) >= 2 and count(bill_id) < 3 then 2
when count(bill_id) >= 1 then 1
end) as rating2,
(case when sum(total_price) >= 5000 then 5
when sum(total_price) >= 3000 and sum(total_price) < 5000 then 4
when sum(total_price) >= 2000 and sum(total_price) < 3000 then 3
when sum(total_price) >= 1000 and sum(total_price) < 2000 then 2
when sum(total_price) < 1000 then 1
end) as rating3
from orders o where o.restaurant_id = 1
group by o.customer_id
Try this. It is faster than above answer. No need to use joins. Check this http://sqlfiddle.com/#!9/192b0/5
You can use join on these different resultsets of your queries. http://sqlfiddle.com/#!9/192b0/3
SELECT * FROM (
select o.customer_id,
(case when max(date_created) >= date_sub(now(), interval 2 week) then 5
when max(date_created) >= date_sub(now(), interval 4 week) then 4
when max(date_created) >= date_sub(now(), interval 8 week) then 3
when max(date_created) >= date_sub(now(), interval 10 week) then 2
when max(date_created) >= date_sub(now(), interval 12 week) then 1
end) as R1
from orders o where o.restaurant_id = 1
group by o.customer_id) AS lastPurchase
LEFT JOIN
(
select o.customer_id,
(case when count(bill_id) >= 6 then 5
when count(bill_id) >= 4 and count(bill_id) < 6 then 4
when count(bill_id) >= 3 and count(bill_id) < 4 then 3
when count(bill_id) >= 2 and count(bill_id) < 3 then 2
when count(bill_id) >= 1 then 1
end) as R2
from orders o where o.restaurant_id = 1
group by o.customer_id
) AS orderQuery USING(customer_id)
LEFT JOIN
(
select o.customer_id,
(case when sum(total_price) >= 5000 then 5
when sum(total_price) >= 3000 and sum(total_price) < 5000 then 4
when sum(total_price) >= 2000 and sum(total_price) < 3000 then 3
when sum(total_price) >= 1000 and sum(total_price) < 2000 then 2
when sum(total_price) < 1000 then 1
end) as R3
from orders o where o.restaurant_id = 1
group by o.customer_id
) AS totalTransactions USING(customer_id)

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

SQL multiple select with multiple where condition

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

Correct my MySQL query?

I have table like
CREATE TABLE `survey` (
`id` int(11) NOT NULL auto_increment,
`submitdate` datetime default NULL,
`answer` varchar(5) collate utf8_unicode_ci default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=499 ;
now to get values like
c t Clicks
2012-10-29 2012-10-22 10
2012-11-04 2012-10-30 20
2012-11-11 2012-11-05 30
2012-11-19 2012-11-12 34
I am using this query
SELECT uq.timespan, COALESCE(tsq.TotalClicks, 0) as Clicks FROM (
SELECT DATE( DATE_ADD( NOW( ) , INTERVAL -21
DAY ) ) c, DATE( DATE_ADD( NOW( ) , INTERVAL -28
DAY ) ) l
union SELECT DATE( DATE_ADD( NOW( ) , INTERVAL -15
DAY ) ) c, DATE( DATE_ADD( NOW( ) , INTERVAL -20
DAY ) ) l
union SELECT DATE( DATE_ADD( NOW( ) , INTERVAL -8
DAY ) ) c, DATE( DATE_ADD( NOW( ) , INTERVAL -14
DAY ) ) l
union SELECT curdate() c,DATE( DATE_ADD( NOW( ) , INTERVAL -7
DAY ) ) l
)uq LEFT JOIN (
SELECT CASE
WHEN submitdate >= NOW() - INTERVAL 4 WEEK
AND submitdate < NOW() - INTERVAL 3 WEEK THEN c 'to' l
DAY ) )
WHEN submitdate >= NOW() - INTERVAL 3 WEEK
AND submitdate < NOW() - INTERVAL 2 WEEK THEN c 'to' l
WHEN submitdate >= NOW() - INTERVAL 2 WEEK
AND submitdate < NOW() - INTERVAL 1 WEEK THEN c 'to' l
DAY ) )
WHEN submitdate >= NOW() - INTERVAL 1 WEEK THEN c 'to' l
END Weeksubmitdate,
count(id) TotalClicks
FROM survey
WHERE submitdate >= NOW() - INTERVAL 4 WEEK
GROUP BY Weeksubmitdate
)tsq ON uq.timespan = tsq.Weeksubmitdate";
problem is with 16th line c to l.
I am getting the following error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''to' l
DAY ) )
WHEN submitdate >= NOW() - INTERVAL 3 WEEK
' at line 16
CASE is supposed to evaluate to a scalar expression. That means its THEN clauses must evaluate to scalar expressions too. Now, what does this c 'to' l thing stand for? Is it a scalar expression? It doesn't seem one to me, however I may be unaware of some things in MySQL, so it's more important whether MySQL itself recognises that as a scalar expression. And apparently it doesn't.
There is another issue. You are trying to reference a derived table's columns inside another derived table. More specifically, you seem to be trying to reference the columns c and l of uq inside the tsq subselect, and that is illegal. If uq was a normal table, it would be fine, but since it is a virtual table, the query doesn't know about its existence at that point, i.e. at the time of parsing the tsq subquery.
Anyway, what you seem to be doing with your query could probably be rewritten more simply, like this, for instance:
SELECT
MIN(submitdate) AS startdate,
MAX(submitdate) AS enddate,
COUNT(*) AS clicks
FROM (
SELECT
CASE
WHEN submitdate >= NOW() - INTERVAL 1 WEEK THEN 1
WHEN submitdate >= NOW() - INTERVAL 2 WEEK THEN 2
WHEN submitdate >= NOW() - INTERVAL 3 WEEK THEN 3
WHEN submitdate >= NOW() - INTERVAL 4 WEEK THEN 4
END AS weekid,
*
FROM survey
) s
GROUP BY
weekid
ORDER BY
startdate
;
The subquery assigns surrogate week IDs to every row of survey. The main query groups the results by those IDs and produces the counts as well as starting & ending dates for every group.