Get result in single query rather then three different query - mysql

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)

Related

count by hours in between with start and end time data

In table, data is in Timestamp format, but I shared it in Time(start_at), Time(end_at) format.
Table structure:
id, start_at, end_at
1, 03:00:00, 06:00:00
2, 02:00:00, 05:00:00
3, 01:00:00, 08:00:00
4, 08:00:00, 13:00:00
5, 09:00:00, 21:00:00
6, 13:00:00, 16:00:00
6, 15:00:00, 19:00:00
For result we need to count ids which were active in between the start_at, end_at time.
hours, count
0, 0
1, 1
2, 2
3, 3
4, 3
5, 2
6, 1
7, 1
8, 1
9, 2
10, 2
11, 2
12, 2
13, 3
14, 2
15, 3
16, 2
17, 2
18, 2
19, 1
20, 1
21, 0
22, 0
23, 0
Either
WITH RECURSIVE
cte AS (
SELECT 0 `hour`
UNION ALL
SELECT `hour` + 1 FROM cte WHERE `hour` < 23
)
SELECT cte.`hour`, COUNT(test.id) `count`
FROM cte
LEFT JOIN test ON cte.`hour` >= HOUR(test.start_at)
AND cte.`hour` < HOUR(test.end_at)
GROUP BY 1
ORDER BY 1;
or
WITH RECURSIVE
cte AS (
SELECT CAST('00:00:00' AS TIME) `hour`
UNION ALL
SELECT `hour` + INTERVAL 1 HOUR FROM cte WHERE `hour` < '23:00:00'
)
SELECT cte.`hour`, COUNT(test.id) `count`
FROM cte
LEFT JOIN test ON cte.`hour` >= test.start_at
AND cte.`hour` < test.end_at
GROUP BY 1
ORDER BY 1;
The 1st query returns hours column in time format whereas the 2nd one returns numeric value for this column. Select the variant which is safe for you.
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=5a77b6e3158be06c7a551cb7e64673de

Duplicated data count as per baseline date

I’d like to create a query that checks if there is any duplicated ID that has been used as per date.
To this end, 60 days prior to, not the subsequent to, the baseline date, should be reviewed for any duplication.
A table example is as below.
CREATE TABLE SampleTable (
pKey INT PRIMARY KEY,
personalID INT NOT NULL,
createDate DATETIME NOT NULL,
value INT NULL
);
The baseline date corresponds to DATE(createDate) whereas ID to be checked for duplication is personallD.
Information needed herein can boil down to the number of today’s data and that of duplicated data.
The number of today’s data can be easily queried as follows.
SELECT
COUNT(*)
FROM SampleTable
WHERE
DATE(createDate) = DATE(NOW())
Out of today’s data, in addition, the number of duplicated data can be checked as follows.
SELECT
COUNT(*)
FROM (
SELECT
personalID,
COUNT(*)
FROM SampleTable
WHERE
DATEDIFF(NOW(), trDate) <= 60
GROUP BY personalID HAVING COUNT(*) > 1
) AS T
In conclusion, all I need to do is get the number of total data by date and the number of data that have the same personalID during the previous days.
[Sample Data]
pKey personalID createDate value
1 1 2018-01-01 100
2 2 2018-01-01 300
3 3 2018-01-01 500
7 1 2018-01-02 100
8 2 2018-01-02 200
9 3 2018-01-02 200
10 4 2018-01-02 100
11 5 2018-01-02 100
12 3 2018-01-03 200
13 4 2018-01-03 100
14 5 2018-01-03 100
15 6 2018-01-03 50
[Desired result]
date totalCount duplicated
2018-01-01 3 0
2018-01-02 5 3
2018-01-03 4 3
If you want the ids that have multiple rows in the past 60 days:
select personid
from sampledata
where trdate >= curdate() - interval 60 day
group by personid
having count(*) >= 2;
If you want to also insist that the personid appears on the most recent date:
select personid
from sampledata
where trdate >= curdate() - interval 60 day
group by personid
having count(*) >= 2 and date(max(trdate)) = curdate();
EDIT:
This seems to be what you want, assuming no duplicates on a given day
select trdate, count(*) as num_persons,
sum(num_dups > 0) as num_dups
from (select sd.*,
(select count(*)
from sampledata sd2
where sd2.personid = sd.personid and
sd2.trdate < sd.trdate and
sd2.trdate >= sd.trdate - interval 60 day
) as num_dups
from sampledata
) sd
group by trdate;
You can use the self join approach as well to find out this type of data. If you need to find out what ID repeated by comparing with previous dates , you can also use this approach.
Create table Testtbl (pkey int, personalID int, createddate date, value int);
insert into Testtbl values
(1 , 1, '2018-01-01' , 100) ,
(2 , 2, '2018-01-01' , 300) ,
(3 , 3, '2018-01-01' , 500) ,
(4 , 1, '2018-01-02' , 100) ,
(5 , 2, '2018-01-02' , 200) ,
(6 , 3, '2018-01-02' , 200) ,
(7 , 4, '2018-01-02' , 100) ,
(8 , 5, '2018-01-02' , 100) ,
(9 , 3, '2018-01-03' , 200) ,
(14 , 3, '2018-01-03' , 500) ,
(10 , 4, '2018-01-03' , 100) ,
(11 , 5, '2018-01-03' , 100) ,
(12 , 6, '2018-01-03' , 50),
(13 , 6, '2018-01-03' , 100)
Query: Left join will help to find out duplicated data without loss of total count. Distinct is to make sure that the same ID is not counted twice .
select t.createddate, count(Distinct t.pkey) TotalCount,
case when t.Createddate > t1.createddate
then Count(distinct t1.PersonalID) + case when t.Createddate =
t1.createddate and
t.personalID = t1.personalID and t.pkey != t1.pkey then Count(distinct
t1.PersonalID)
else 0 end else 0
end Duplicated from Testtbl t
left join Testtbl t1 on t.personalID = t1.personalID
and t.Createddate >= t1.Createddate and t.pkey != t1.pkey
and DATEDIFF(t1.Createddate, t.Createddate) <= 60
Group by t.createddate
Output:
createddate TotalCount Duplicated
2018-01-01 3 0
2018-01-02 5 3
2018-01-03 6 5

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;

How to calculate the initial Stock with MySql

I'm trying to find a way to calculate the initial stock by the number of weeks.
I'm have just managed to calculate the 'in' and 'out' of stock between the weeks 10 and 20 with this request.
Can someone give me the full request to calculate the final stock and the initial stock.
SELECT
`week` ,
SUM(case
when `week` between 10 AND 20 and `etat` =1
then 1
else 0
end) AS inn
,
SUM(case
when `week` between 10 AND 20 and `etat` =2
then 1
else 0
end) AS out
FROM fait_stock f
where 1=1
group by `week`
having `week` between 10 AND 20
EDIT
I want to calculate the stock of the previous weeks (initial stock that means weeks<10) for example week 10 I have 5. week 11 It should be 5+(stock of week 11). Week 12 it should be 5+(stock of week 11)+(stock of week 12) the same for others weeks.
First of all - you can write your query much shorter:
SELECT
`week` ,
SUM(IF(`etat`=1, 1, 0)) AS inn,
SUM(IF(`etat`=2, 1, 0)) AS `out`
FROM fait_stock
WHERE `week` BETWEEN 10 AND 20
GROUP BY `week`
If you now join it with itself (with a small modification, if you want the sums of weeks < 10), you can calculate the sums for all weeks that are smaller or equal than the given week:
SELECT f1.`week`, SUM(f2.inn) AS inn, SUM(f2.`out`) AS `out`
FROM (
SELECT
`week` ,
SUM(IF(`etat`=1, 1, 0)) AS inn,
SUM(IF(`etat`=2, 1, 0)) AS `out`
FROM fait_stock
WHERE `week` BETWEEN 10 AND 20
GROUP BY `week`
) AS f1
JOIN (
SELECT
`week` ,
SUM(IF(`etat`=1, 1, 0)) AS inn,
SUM(IF(`etat`=2, 1, 0)) AS `out`
FROM fait_stock
WHERE `week` BETWEEN 1 AND 20
GROUP BY `week`
) AS f2 ON f2.`week` <= f1.`week`
GROUP BY f1.`week`

How to merge results of two mysql tables in one output

I have two similar mysql tables and want to get the data from both of them in one output (merge). How can I do it?
For this reason I have created two separate queries to check that I get what I'm looking for.
First: Table -> web_session
SELECT date_format(booking_time, '%m-%Y') AS m, count(booking_time) AS b
FROM web_session
WHERE
date(booking_time) >= date_sub(curdate(), interval 12 month)
AND
date(booking_time) <= date_add(curdate(), interval 6 month)
GROUP BY month(booking_time)
ORDER BY booking_time ASC;
Output:
m, b
10-2013, 15
11-2013, 6
12-2013, 13
01-2014, 10
02-2014, 10
03-2014, 25
04-2014, 1
Second: Table -> web_log
SELECT date_format(request_time, '%m-%Y') AS m, count(request_time) AS r
FROM web_log
WHERE
date(request_time) >= date_sub(curdate(), interval 12 month)
AND
date(request_time) <= date_add(curdate(), interval 6 month)
GROUP BY month(request_time)
ORDER BY request_time ASC;
Output:
m, r
03-2014, 45
04-2014, 35
desired output:
m, b, r
10-2013, 15, null
11-2013, 6, null
12-2013, 13, null
01-2014, 10, null
02-2014, 10, null
03-2014, 25, 45
04-2014, 1, 35
You need to join them. Try this:
SELECT T1.m,T1.b,T2.r FROM
(
SELECT date_format(booking_time, '%m-%Y') AS m, count(booking_time) AS b
FROM web_session
WHERE
date(booking_time) >= date_sub(curdate(), interval 12 month)
AND
date(booking_time) <= date_add(curdate(), interval 6 month)
GROUP BY month(booking_time)
ORDER BY booking_time ASC;) T1
LEFT OUTER JOIN
(SELECT date_format(request_time, '%m-%Y') AS m, count(request_time) AS r
FROM web_log
WHERE
date(request_time) >= date_sub(curdate(), interval 12 month)
AND
date(request_time) <= date_add(curdate(), interval 6 month)
GROUP BY month(request_time)
ORDER BY request_time ASC;) T2
ON T1.m=T2.m
ORDER BY T1.m
Output will be:
m b r
10-2013 15 null
11-2013 6 null
12-2013 13 null
01-2014 10 null
02-2014 10 null
03-2014 25 45
04-2014 1 35