Can't use having sum or having count mysql 5.7 - mysql

Let's say I have a table like this
CREATE TABLE order_match(ID int(10) NOT NULL PRIMARY KEY AUTO_INCREMENT,
quantity decimal(10,2), createdAt date NOT NULL, order_status_id int(10) NOT NULL,
createdby int(11), code_order varchar(20) NOT NULL);
CREATE TABLE air_way_bills (id int(10) NOT NULL PRIMARY KEY AUTO_INCREMENT,
code_order varchar(30) NOT NULL, customer_regency varchar(30) NOT NULL);
insert into air_way_bills values
(1, 0001, 'KOTA DEPOK'),
(2, 0002, 'KOTA JAKARTA'),
(3, 0003, 'KOTA BOGOR'),
(4, 0004, 'KOTA BOGOR'),
(5, 0005, 'KOTA TANGERANG'),
(6, 0006, 'KOTA JAMBI'),
(7, 0007, 'KOTA BOGOR'),
(8, 0009, 'KOTA TANGERANG');
insert into order_match values
(1, 0.2, '2020-02-02', 6, 01, 0001),
(2, 1, '2020-02-03', 7, 02, 0002),
(3, 1.3, '2020-02-04', 7, 03, 0003),
(4, 1.4, '2020-02-08', 5, 08, 0004),
(5, 1.2, '2020-02-05', 8, 04, 0005),
(6, 1.4, '2020-03-01', 8, 05, 0006),
(7, 0.23, '2020-01-01', 8, 03, 0007),
(8, 2.3, '2020-02-07', 8, 04, 0009);
this is table order_match, the id with the primary key, quantity is the quantity of the transaction, createdAt are the date transaction, order_status_id is the status of the transaction (with order_status_id 7 are not approved transaction), createdby are the users, and code_order are the destination and connected with air_way_bills code_order column
+----+----------+------------+-----------------+-----------+------------+
| ID | quantity | createdAt | order_status_id | createdby | code_order |
+----+----------+------------+-----------------+-----------+------------+
| 1 | 0.20 | 2020-02-02 | 6 | 1 | 1 |
| 2 | 1.00 | 2020-02-03 | 7 | 2 | 2 |
| 3 | 1.30 | 2020-02-04 | 7 | 3 | 3 |
| 4 | 1.40 | 2020-02-08 | 5 | 8 | 4 |
| 5 | 1.20 | 2020-02-05 | 8 | 4 | 5 |
| 6 | 1.40 | 2020-03-01 | 8 | 5 | 6 |
| 7 | 0.23 | 2020-01-01 | 8 | 3 | 7 |
| 8 | 2.30 | 2020-02-07 | 8 | 4 | 9 |
+----+----------+------------+-----------------+-----------+------------+
this is air_way_bills table, with order_match.code_order = air_way_bills.code_order
+----+------------+------------------+
| id | code_order | customer_regency |
+----+------------+------------------+
| 1 | 1 | KOTA DEPOK |
| 2 | 2 | KOTA JAKARTA |
| 3 | 3 | KOTA BOGOR |
| 4 | 4 | KOTA BOGOR |
| 5 | 5 | KOTA TANGERANG |
| 6 | 6 | KOTA JAMBI |
| 7 | 7 | KOTA BOGOR |
| 8 | 9 | KOTA TANGERANG |
+----+------------+------------------+
i want to find out the new users (createdby) in range date '2020-02-03' until '2020-02-07' with approval transaction (order_status_id not in 7) and sort with in the destination. new users is the users where doing transaction in between the range date, but never doing transaction before the date range (on this case, before '2020-02-03')
i used this query
SELECT COALESCE(customer_regency, 'Total') AS `Destination`,
SUM(quantity) AS `Qty(kg)`,
round(SUM(quantity) / any_value(totalsum) * 100, 1) AS `Qty(%)`,
COUNT(a.id) AS `Jumlah Order`,
round(COUNT(a.id) / any_value(totalcount) * 100, 1) AS `Jumlah Order(%)`
FROM order_match a
/* 1 */ INNER JOIN air_way_bills b
/* 1 */ ON a.code_order = b.code_order
/* 2 */ INNER JOIN ( SELECT s1.createdby
FROM order_match s1
WHERE s1.order_status_Id in (4, 5, 6, 8)
GROUP BY s1.createdby
HAVING COUNT(s1.createdAt BETWEEN '2020-02-03' AND '2020-02-07') >= 1)
AND COUNT(s1.createdAt < '2020-02-03') = 0 ) clients
/* 2 */ ON a.createdby = clients.createdby
JOIN ( SELECT SUM(quantity) totalsum,
COUNT(id) totalcount
FROM order_match
/* 3 */ INNER JOIN ( SELECT s2.createdby
FROM order_match s2
WHERE s2.order_status_id in (4, 5, 6, 8)
GROUP BY s2.createdby
HAVING COUNT(s2.createdAt BETWEEN '2020-02-03' AND '2020-02-07') >= 1)
AND COUNT(s2.createdAt < '2020-02-03') = 0 ) clients
/* 3 */ ON order_match.createdby = clients.createdby
WHERE order_status_Id in (4, 5, 6, 8)) totals
WHERE a.order_status_Id in (4, 5, 6, 8)
GROUP BY customer_regency WITH ROLLUP;
but it says
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 'AND COUNT(s1.createdAt < '2020-02-03') = 0 ) clients
/* 2 */ ON a.createdby = c' at line 15
expected results
+----------------+---------+--------+--------------+-----------------+
| Destination | Qty(kg) | Qty(%) | Count Order | Count Order(%) |
+----------------+---------+--------+--------------+-----------------+
| KOTA TANGERANG | 3.50 | 100 | 2 | 100 |
| Total | 3.50 | 100.0 | 2 | 100.0 |
+----------------+---------+--------+--------------+-----------------+
explanation : because users (Createdby) 4 are only fit with the condition (doing transaction in range date and never doing transaction before, and had approval transaction (order_status_id not in 7)

Please try the following query:
SELECT COALESCE(customer_regency, 'Total') AS `Destination`,
SUM(quantity) AS `Qty(kg)`,
round(SUM(quantity) / any_value(totalsum) * 100, 1) AS `Qty(%)`,
COUNT(a.id) AS `Jumlah Order`,
round(COUNT(a.id) / any_value(totalcount) * 100, 1) AS `Jumlah Order(%)`
FROM order_match a
/* 1 */ INNER JOIN air_way_bills b
/* 1 */ ON a.code_order = b.code_order
/* 2 */ INNER JOIN ( SELECT s1.createdby
FROM order_match s1
WHERE s1.order_status_Id in (4, 5, 6, 8)
GROUP BY s1.createdby
HAVING (SUM(case when createdAt >= '2020-02-03' AND createdAt <= '2020-02-07' then 1 else 0 end) >= 1)
AND SUM(case when createdAt < '2020-02-03' then 1 else 0 end) = 0 ) clients
/* 2 */ ON a.createdby = clients.createdby
JOIN ( SELECT SUM(quantity) totalsum,
COUNT(id) totalcount
FROM order_match
/* 3 */ INNER JOIN ( SELECT s2.createdby
FROM order_match s2
WHERE s2.order_status_id in (4, 5, 6, 8)
GROUP BY s2.createdby
HAVING SUM(case when createdAt >= '2020-02-03' AND createdAt <= '2020-02-07' then 1 else 0 end) >= 1
AND SUM(case when createdAt < '2020-02-03' then 1 else 0 end) = 0 ) clients
/* 3 */ ON order_match.createdby = clients.createdby
WHERE order_status_Id in (4, 5, 6, 8)) totals
WHERE a.order_status_Id in (4, 5, 6, 8)
GROUP BY customer_regency WITH ROLLUP;
remove redundant ) after COUNT(s1.createdAt BETWEEN '2020-02-03' AND '2020-02-07') >= 1)
change count to sum. their differences please refer to office doc
The COUNT(expression) returns the number of rows that do not contain
NULL values as the result of the expression.
The SUM() function is an aggregate function that allows you to
calculate the sum of values in a set. The syntax of the SUM() function
is as follows:

Related

how to track score gains in mysql

I would like to display a players current score as well as how many points they have gained within a selected time frame.
I have 2 tables
skills table
+----+---------+---------------------+
| id | name | created_at |
+----+---------+---------------------+
| 1 | skill 1 | 2020-06-05 00:00:00 |
| 2 | skill 2 | 2020-06-05 00:00:00 |
| 3 | skill 3 | 2020-06-05 00:00:00 |
+----+---------+---------------------+
scores table
+----+-----------+----------+-------+---------------------+
| id | player_id | skill_id | score | created_at |
+----+-----------+----------+-------+---------------------+
| 1 | 1 | 1 | 5 | 2020-06-06 00:00:00 |
| 2 | 1 | 1 | 10 | 2020-07-06 00:00:00 |
| 3 | 1 | 2 | 1 | 2020-07-06 00:00:00 |
| 4 | 2 | 1 | 11 | 2020-07-06 00:00:00 |
| 5 | 1 | 1 | 13 | 2020-07-07 00:00:00 |
| 6 | 1 | 2 | 10 | 2020-07-07 00:00:00 |
| 7 | 2 | 1 | 12 | 2020-07-07 00:00:00 |
| 8 | 1 | 1 | 20 | 2020-07-08 00:00:00 |
| 9 | 1 | 2 | 15 | 2020-07-08 00:00:00 |
| 10 | 2 | 1 | 17 | 2020-07-08 00:00:00 |
+----+-----------+----------+-------+---------------------+
my expected results are:-
24 hour query
+-----------+---------+-------+------+
| player_id | name | score | gain |
+-----------+---------+-------+------+
| 1 | skill 1 | 20 | 7 |
| 1 | skill 2 | 15 | 5 |
+-----------+---------+-------+------+
7 day query
+-----------+---------+-------+------+
| player_id | name | score | gain |
+-----------+---------+-------+------+
| 1 | skill 1 | 20 | 10 |
| 1 | skill 2 | 15 | 14 |
+-----------+---------+-------+------+
31 day query
+-----------+---------+-------+------+
| player_id | name | score | gain |
+-----------+---------+-------+------+
| 1 | skill 1 | 20 | 15 |
| 1 | skill 2 | 15 | 14 |
+-----------+---------+-------+------+
so far I have the following, but all this does is return the last 2 records for each skill, I am struggling to calculate the gains and the different time frames
SELECT player_id, skill_id, name, score
FROM (SELECT player_id, skill_id, name, score,
#skill_count := IF(#current_skill = skill_id, #skill_count + 1, 1) AS skill_count,
#current_skill := skill_id
FROM skill_scores
INNER JOIN skills
ON skill_id = skills.id
WHERE player_id = 1
ORDER BY skill_id, score DESC
) counted
WHERE skill_count <= 2
I would like some help figuring out the query I need to build to get the desired results, or is it best to do this with php instead of in the db?
EDIT:-
MYSQL 8.0.20 dummy data id's are primary_key auto increment but I didnt ad that for simplicity:-
CREATE TABLE skills
(
id bigint,
name VARCHAR(80)
);
CREATE TABLE skill_scores
(
id bigint,
player_id bigint,
skill_id bigint,
score bigint,
created_at timestamp
);
INSERT INTO skills VALUES (1, 'skill 1');
INSERT INTO skills VALUES (2, 'skill 2');
INSERT INTO skills VALUES (3, 'skill 3');
INSERT INTO skill_scores VALUES (1, 1, 1 , 5, '2020-06-06 00:00:00');
INSERT INTO skill_scores VALUES (2, 1, 1 , 10, '2020-07-06 00:00:00');
INSERT INTO skill_scores VALUES (3, 1, 2 , 1, '2020-07-06 00:00:00');
INSERT INTO skill_scores VALUES (4, 2, 1 , 11, '2020-07-06 00:00:00');
INSERT INTO skill_scores VALUES (5, 1, 1 , 13, '2020-07-07 00:00:00');
INSERT INTO skill_scores VALUES (6, 1, 2 , 10, '2020-07-07 00:00:00');
INSERT INTO skill_scores VALUES (7, 2, 1 , 12, '2020-07-07 00:00:00');
INSERT INTO skill_scores VALUES (8, 1, 1 , 20, '2020-07-08 00:00:00');
INSERT INTO skill_scores VALUES (9, 1, 2 , 15, '2020-07-08 00:00:00');
INSERT INTO skill_scores VALUES (10, 2, 1 , 17, '2020-07-08 00:00:00');
WITH cte AS (
SELECT id, player_id, skill_id,
FIRST_VALUE(score) OVER (PARTITION BY player_id, skill_id ORDER BY created_at DESC) score,
FIRST_VALUE(score) OVER (PARTITION BY player_id, skill_id ORDER BY created_at DESC) - FIRST_VALUE(score) OVER (PARTITION BY player_id, skill_id ORDER BY created_at ASC) gain,
ROW_NUMBER() OVER (PARTITION BY player_id, skill_id ORDER BY created_at DESC) rn
FROM skill_scores
WHERE created_at BETWEEN #current_date - INTERVAL #interval DAY AND #current_date
)
SELECT cte.player_id, skills.name, cte.score, cte.gain
FROM cte
JOIN skills ON skills.id = cte.skill_id
WHERE rn = 1
ORDER BY player_id, name;
fiddle
Ps. I don't understand where gain=15 is taken for 31-day period - the difference between '2020-07-08 00:00:00' and '2020-06-06 00:00:00' is 32 days.
Well i think you need a (temporary) table for this. I will call it "player_skill_gains". Its basically the players skills ordered by created_at and with an auto_incremented id:
CREATE TABLE player_skill_gains
(`id` int PRIMARY KEY AUTO_INCREMENT NOT NULL
, `player_id` int
, skill_id int
, score int
, created_at date)
;
INSERT INTO player_skill_gains(player_id, skill_id, score, created_at)
SELECT player_skills.player_id AS player_id
, player_skills.skill_id
, SUM(player_skills.score) AS score
, player_skills.created_at
FROM player_skills
GROUP BY player_skills.id, player_skills.skill_id, player_skills.created_at
ORDER BY player_skills.player_id, player_skills.skill_id, player_skills.created_at ASC;
Using this table we can relatively easily select the last skill for each row (id-1). Using this we can calculate the gains:
SELECT player_skill_gains.player_id, skills.name, player_skill_gains.score
, player_skill_gains.score - IFNULL(bef.score,0) AS gain
, player_skill_gains.created_at
FROM player_skill_gains
INNER JOIN skills ON player_skill_gains.skill_id = skills.id
LEFT JOIN player_skill_gains AS bef ON (player_skill_gains.id - 1) = bef.id
AND player_skill_gains.player_id = bef.player_id
AND player_skill_gains.skill_id = bef.skill_id
For the different queries you want to have (24 hours, 7 days, etc.) you just have to specify the needed where-part for the query.
You can see all this in action here: http://sqlfiddle.com/#!9/1571a8/11/0

count existing buyer with mysql

i have table like this with mysql version 5.7
CREATE TABLE order_match (
ID INT,
user_id INT,
createdAt DATE,
status_id INT,
quantity INT
);
INSERT INTO order_match VALUES
(1, 12, '2020-01-01', 4, 1),
(2, 12, '2020-01-03', 7, 1),
(3, 12, '2020-01-06', 7, 2),
(4, 13, '2020-01-02', 5, 2),
(5, 13, '2020-01-03', 6, 1),
(6, 14, '2020-03-03', 8, 0.5),
(7, 13, '2020-03-04', 4, 1),
(8, 15, '2020-04-04', 7, 3),
(9, 14, '2020-03-02', 7, 2),
(10, 14, '2020-03-10', 5, 4),
(11, 13, '2020-04-10', 8, 3),
(12, 13, '2020-04-11', 8, 2),
(13, 16, '2020-04-15', 8, 3);
select * from order_match
order by createdAt;
the output just like this
+---------+---------+------------+-----------+----------+
| ID | user_id | createdAt | status_id | quantity |
+---------+---------+------------+-----------+----------+
| 1 | 12 | 2020-01-01 | 4 | 1 |
| 4 | 13 | 2020-01-02 | 5 | 2 |
| 2 | 12 | 2020-01-03 | 7 | 1 |
| 5 | 13 | 2020-01-03 | 6 | 1 |
| 3 | 12 | 2020-01-06 | 7 | 2 |
| 9 | 14 | 2020-03-02 | 7 | 2 |
| 6 | 14 | 2020-03-03 | 8 | 1 |
| 7 | 13 | 2020-03-04 | 4 | 1 |
| 10 | 14 | 2020-03-10 | 5 | 4 |
| 8 | 15 | 2020-04-04 | 7 | 3 |
| 11 | 13 | 2020-04-10 | 8 | 3 |
| 12 | 13 | 2020-04-11 | 8 | 2 |
| 13 | 16 | 2020-04-15 | 8 | 3 |
| 13 rows | | | | |
+---------+---------+------------+-----------+----------+
with ID as the id of transaction, user_id as the buyer who doing transaction, createdAt as the date transaction happen, status_id as the status of transaction (which 4, 5, 6, 8 as the approval transaction) and quantity as the amount of quantity of every transaction
this is the fiddle
so i want to find out the statistic of how many transaction, total amount of quantity, and total frequency of unique user between 2020-03-01 until 2020-04-01, unique user is the user who doing his first approval transaction before 2020-03-01 and at least doing 1 approval transaction in between 2020-03-01 until 2020-04-01, based on the table i made the expected result just like this
+------------+------------------+-----------------+
| count user | total_order (kg) | total_order (x) |
+------------+------------------+-----------------+
| 1 | 1 | 1 |
+------------+------------------+-----------------+
explanation : as we know the user who become unique user in between 2020-03-01 until 2020-04-01 are user_id 13, because he doing his first approval transaction on 2020-01-02 (before 2020-03-01) and then doing his approval transaction at least one time on 2020-03-01 until 2020-04-01, on time range, user_id 13 (count user) doing 1 transaction (total_order (x)) and the amount are 1 kg (total_order (kg )
i've doing this syntax
select
count(distinct om.user_id) as count,
sum(om.quantity) as total_order_kg,
count(om.id) as order_x
from (select count(xx.count_) as count_
from (select count(user_id) as count_ from order_match
where status_Id in (4, 5, 6, 8)
group by user_id
) xx
) x1,
(select user_id
from order_match
group by user_id
) yy,
order_match om
where yy.user_id = om.user_id and
status_id in (4, 5, 6, 8)
and om.createdAt < '2020-03-01'
and EXISTS (select 1 from order_match om2
where om.user_id = om2.user_id
and status_id in (4, 5, 6, 8)
and om2.createdAt >= '2020-03-01'
and om2.createdAt <= '2020-04-01');
but idk why the result like this
+------------+------------------+-----------------+
| count user | total_order (kg) | total_order (x) |
+------------+------------------+-----------------+
| 1 | 3 | 2 |
+------------+------------------+-----------------+
THE FIDDLE
-- separate users statistic
SELECT user_id,
SUM(quantity * (createdAt >= #start)) total_order_kg,
SUM(createdAt >= #start) order_x
FROM order_match
WHERE createdAt <= #finish
GROUP BY user_id
HAVING SUM(createdAt >= #start)
AND SUM(createdAt >= #start) < COUNT(createdAt);
-- overall statistic
SELECT COUNT(*) users_count,
SUM(order_kg) total_order_kg,
SUM(order_count) total_order_count
FROM ( SELECT user_id,
SUM(quantity * (createdAt >= #start)) order_kg,
SUM(createdAt >= #start) order_count
FROM order_match
WHERE createdAt <= #finish
GROUP BY user_id
HAVING SUM(createdAt >= #start)
AND SUM(createdAt >= #start) < COUNT(createdAt) ) totals;
fiddle
'why the result like this' - you are using comma joins so are starting from a cartesian product you can see what is happening if you substitute the aggregations for actual values for example
select
om.user_id,
om.quantity,
om.id,
x1.count_,
yy.user_id
from (select count(xx.count_) as count_
from (select count(user_id) as count_ from t
where status_Id in (4, 5, 6, 8)
group by user_id
) xx
) x1,
(select user_id
from t
group by user_id
) yy,
t om
where yy.user_id = om.user_id and
status_id in (4, 5, 6, 8)
and om.createdAt < '2020-03-01'
and EXISTS (select 1 from t om2
where om.user_id = om2.user_id
and status_id in (4, 5, 6, 8)
and om2.createdAt >= '2020-03-01'
and om2.createdAt <= '2020-04-01');
Where t is my table name and a copy of order_match.
If you run this query without the where clause then you get 65 rows returned, if you run it with the where clause but not the exists check you get 3 rows returned if you run it in it's entirety you get
---------+----------+------+--------+---------+
| user_id | quantity | id | count_ | user_id |
+---------+----------+------+--------+---------+
| 13 | 2 | 4 | 4 | 13 |
| 13 | 1 | 5 | 4 | 13 |
+---------+----------+------+--------+---------+
2 rows in set (0.002 sec)
Which when aggregated produces the result you get from your query.
NB group by without any aggregation functions is just wrong.

How to get first and last record of each user in MySQL (for attendance)

I have the following table:
CREATE TABLE IF NOT EXISTS `access_log` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11) NOT NULL DEFAULT 0,
`room_id` INT(11) NOT NULL DEFAULT 0,
`created` TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
A new record is added everytime a user enters a room room_id. I would like to select the first and last record of every user for every room.
Currently I have the following queries that don't seem to give the right records:
For first record of each room for each user:
SELECT al.* FROM `access_log` AS `al`
LEFT JOIN `rooms` AS `r` ON al.room_id = r.id
INNER JOIN (
SELECT user_id, room_id, min(created) AS min_date
FROM `access_log`
WHERE `user_id` != 0
GROUP BY user_id, room_id) AS al2
ON al.user_id = al2.user_id AND al.room_id = al2.room_id AND al.created = al2.min_date
WHERE `al`.`created` >= '2019-06-09 00:00:00' AND `al`.`created` <= '2019-06-12 23:59:59'
For last record of each room for each user:
SELECT al.* FROM `access_log` AS `al`
LEFT JOIN `rooms` AS `r` ON al.room_id = r.id
INNER JOIN (
SELECT user_id, room_id, max(created) AS max_date
FROM `access_log`
WHERE `user_id` != 0
GROUP BY user_id, room_id) AS al2
ON al.user_id = al2.user_id AND al.room_id = al2.room_id AND al.created = al2.max_date
WHERE `al`.`created` >= '2019-06-09 00:00:00' AND `al`.`created` <= '2019-06-12 23:59:59'
Here's an SQLFiddle demo including sample data http://www.sqlfiddle.com/#!9/fc5f8b/2. You can see that the query display unintended results. They do not list the different days, although they list the different rooms. Also, the number of rows for the first and last queries are different.
DDLs of same:
CREATE TABLE IF NOT EXISTS `access_log` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11) NOT NULL DEFAULT 0,
`room_id` INT(11) NOT NULL DEFAULT 0,
`created` TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `access_log` (`id`, `user_id`, `room_id`, `created`) VALUES
(1, 90000017, 6, '2019-06-10 01:15:00'),
(2, 90000017, 6, '2019-06-10 01:25:00'),
(3, 90000018, 6, '2019-06-10 02:15:00'),
(4, 90000018, 6, '2019-06-10 02:25:00'),
(5, 90000019, 6, '2019-06-10 03:15:00'),
(6, 90000019, 6, '2019-06-10 03:25:00'),
(7, 90000017, 5, '2019-06-10 11:15:00'),
(8, 90000017, 5, '2019-06-10 11:25:00'),
(9, 90000018, 5, '2019-06-10 12:15:00'),
(10, 90000018, 5, '2019-06-10 12:25:00'),
(11, 90000019, 5, '2019-06-10 13:15:00'),
(12, 90000019, 5, '2019-06-10 13:25:00'),
(13, 90000017, 6, '2019-06-11 04:10:00'),
(14, 90000017, 6, '2019-06-11 04:20:00'),
(15, 90000018, 6, '2019-06-11 05:10:00'),
(16, 90000018, 6, '2019-06-11 05:20:00'),
(17, 90000019, 6, '2019-06-11 06:10:00'),
(18, 90000019, 6, '2019-06-11 06:20:00'),
(19, 90000017, 5, '2019-06-11 14:10:00'),
(20, 90000017, 5, '2019-06-11 14:20:00'),
(21, 90000018, 5, '2019-06-11 15:10:00'),
(22, 90000018, 5, '2019-06-11 15:20:00'),
(23, 90000019, 5, '2019-06-11 16:20:00'),
(24, 90000019, 5, '2019-06-11 16:20:00');
The expected results should be something like:
First per user per room per day
+------+-----------+---------+---------------------+
| id | user_id | room_id | created |
+------+-----------+---------+---------------------+
| 1 | 90000017 | 6 | 2019-06-10 01:15:00 |
| 3 | 90000018 | 6 | 2019-06-10 02:15:00 |
| 5 | 90000019 | 6 | 2019-06-10 03:15:00 |
| 7 | 90000017 | 5 | 2019-06-10 11:15:00 |
| 9 | 90000018 | 5 | 2019-06-10 12:15:00 |
| 11 | 90000019 | 5 | 2019-06-10 13:15:00 |
| 13 | 90000017 | 6 | 2019-06-11 04:10:00 |
| 15 | 90000018 | 6 | 2019-06-11 05:10:00 |
| 17 | 90000019 | 6 | 2019-06-11 06:10:00 |
| 19 | 90000017 | 5 | 2019-06-11 14:10:00 |
| 21 | 90000018 | 5 | 2019-06-11 15:10:00 |
| 23 | 90000019 | 5 | 2019-06-11 16:20:00 |
+------+-----------+---------+---------------------+
Last per user per room per day
+------+-----------+---------+---------------------+
| id | user_id | room_id | created |
+------+-----------+---------+---------------------+
| 2 | 90000017 | 6 | 2019-06-10 01:25:00 |
| 4 | 90000018 | 6 | 2019-06-10 02:25:00 |
| 6 | 90000019 | 6 | 2019-06-10 03:25:00 |
| 8 | 90000017 | 5 | 2019-06-10 11:25:00 |
| 10 | 90000018 | 5 | 2019-06-10 12:25:00 |
| 12 | 90000019 | 5 | 2019-06-10 13:25:00 |
| 14 | 90000017 | 6 | 2019-06-11 04:20:00 |
| 16 | 90000018 | 6 | 2019-06-11 05:20:00 |
| 18 | 90000019 | 6 | 2019-06-11 06:20:00 |
| 20 | 90000017 | 5 | 2019-06-11 14:20:00 |
| 22 | 90000018 | 5 | 2019-06-11 15:20:00 |
| 24 | 90000019 | 5 | 2019-06-11 16:20:00 |
+------+-----------+---------+---------------------+
I've suggest with this single query to cross check with the expected result:
SELECT
GROUP_CONCAT(id ORDER BY created,id SEPARATOR ' ') all_id,
-- this return all id present in the group
SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY created,id SEPARATOR ' '),' ',1) min_id_in,
-- this part is taking the first value from the GROUP_CONCAT operation above
SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY created,id SEPARATOR ' '),' ',-1) max_id_in,
-- this part is taking the last value from the first GROUP_CONCAT operation
user_id,room_id,
MIN(created),
MAX(created) -- Min/max value are both shown in same query
FROM access_log
GROUP BY user_id,room_id,
date(created); -- the missing condition where OP's asks results to return by each date.
I've added date(created) in the GROUP BY .. condition.
In your original query from the fiddle:
SELECT al.* FROM `access_log` AS `al`
INNER JOIN (
SELECT user_id, room_id, min(created) AS min_date
FROM `access_log`
WHERE `user_id` != 0
GROUP BY user_id, room_id,
date(created) -- I've added the condition here
) AS al2
ON al.user_id = al2.user_id AND al.room_id = al2.room_id AND al.created = al2.min_date
WHERE `al`.`created` >= '2019-06-09 00:00:00' AND `al`.`created` <= '2019-06-12 23:59:59'
ORDER BY al.user_id ASC;
SELECT al.* FROM `access_log` AS `al`
INNER JOIN (
SELECT user_id, room_id, max(created) AS max_date
FROM `access_log`
WHERE `user_id` != 0
GROUP BY user_id, room_id,
date(created) -- and here
) AS al2
ON al.user_id = al2.user_id AND al.room_id = al2.room_id AND al.created = al2.max_date
WHERE `al`.`created` >= '2019-06-09 00:00:00' AND `al`.`created` <= '2019-06-12 23:59:59'
ORDER BY al.user_id ASC;
The subquery should select from the same table as the main query, so it should select from access_log, not pacc_leapaccess_access_log.
SELECT al.* FROM `access_log` AS `al`
LEFT JOIN `pacc_leapaccess_rooms` AS `r` ON al.room_id = r.id
INNER JOIN (
SELECT user_id, room_id, min(created) AS min_date
FROM `access_log`
WHERE `user_id` != 0
GROUP BY user_id, room_id) AS al2
ON al.user_id = al2.user_id AND al.room_id = al2.room_id AND al.created = al2.min_date
WHERE `al`.`created` >= '2019-06-09 00:00:00' AND `al`.`created` <= '2019-06-12 23:59:59';
SELECT al.* FROM `access_log` AS `al`
LEFT JOIN `pacc_leapaccess_rooms` AS `r` ON al.room_id = r.id
INNER JOIN (
SELECT user_id, room_id, max(created) AS max_date
FROM `access_log`
WHERE `user_id` != 0
GROUP BY user_id, room_id) AS al2
ON al.user_id = al2.user_id AND al.room_id = al2.room_id AND al.created = al2.max_date
WHERE `al`.`created` >= '2019-06-09 00:00:00' AND `al`.`created` <= '2019-06-12 23:59:59';

MySQL - count open items and getting dates from different table

Happy Friday All, I have something I can not get sorted. I have asked before for support and found it was the best thing I could do :) So, I want to calculate number of open items based on two dates, open and close. All the data will be counted in t2 and the dates will go from t1 (that stores a lot of dates and hence I use SELECT DISTINCT)
So, the tables are as follows:
CREATE TABLE t1
(
ID int (10),
Date1 date);
insert into T1 values
( 1, '2018-12-17'),
( 2, '2018-12-18'),
( 3, '2018-12-19'),
( 4, '2018-12-19'),
( 5, '2018-12-19'),
( 6, '2018-12-20'),
( 7, '2018-12-20'),
( 8, '2018-12-21'),
( 9, '2018-12-22'),
(10, '2018-12-23'),
(11, '2018-12-24'),
(12, '2018-12-25'),
(13, '2018-12-26'),
(14, '2018-12-27'),
(15, '2018-12-28');
CREATE TABLE t2
(
ID int (10),
Open_Date date,
Close_Date date);
insert into t2 values
( 1, '2018-12-17', '2018-12-18'),
( 2, '2018-12-18', '2018-12-18'),
( 3, '2018-12-18', '2018-12-18'),
( 4, '2018-12-19', '2018-12-20'),
( 5, '2018-12-19', '2018-12-21'),
( 6, '2018-12-20', '2018-12-22'),
( 7, '2018-12-20', '2018-12-22'),
( 8, '2018-12-21', '2018-12-25'),
( 9, '2018-12-22', '2018-12-26'),
(10, '2018-12-23', '2018-12-27');
This is the outcome I want:
+------------+------------+
| Date | Count_open |
+------------+------------+
| 17/12/2018 | 1 |
| 18/12/2018 | 3 |
| 19/12/2018 | 2 |
| 20/12/2018 | 3 |
| 21/12/2018 | 4 |
| 22/12/2018 | 4 |
| 23/12/2018 | 3 |
| 23/12/2018 | 0 |
| 25/12/2018 | 0 |
| 27/12/2018 | 0 |
| 27/12/2018 | 0 |
| 28/12/2018 | 0 |
+------------+------------+
I have a total black out with the code and need your help.
Maybe this is what you are looking for?
select
x.Date1 as 'Date',
count(distinct t2.id) as 'Count_open'
from (select distinct Date1 from t1) x
left join t2 on x.Date1 between t2.Open_Date and t2.Close_Date
group by x.Date1
order by x.Date1
Result:
Date Count_open
---------- ----------
2018-12-17 1
2018-12-18 3
2018-12-19 2
2018-12-20 4
2018-12-21 4
2018-12-22 4
2018-12-23 3
2018-12-24 3
2018-12-25 3
2018-12-26 2
2018-12-27 1
2018-12-28 0

SQL - Aggregate same column through different criteria

Say I have a table Orders that looks like this,
|country| customer_id | order_id |
| CA | 5 | 3 |
| CA | 5 | 4 |
| CA | 6 | 5 |
| CA | 6 | 6 |
| US | 2 | 7 |
| US | 7 | 8 |
| US | 7 | 9 |
| US | 7 | 10 |
| US | 2 | 11 |
and I want to write a query to populate a table as so,
| country | customers_w_2_orders | customers_w_2_plus_orders |
| CA | 2 | 0 |
| US | 1 | 1 |
where it aggregates number of customers with 2 orders and number of customers with 3 orders by country.
Here's what I did and it did not give the result I want..
SELECT country, count(*) as cnt1, count(*) as cnt2
FROM Orders
GROUP BY country
HAVING cnt1=2 AND cnt2>2;
declare #orders table (country char(2), customer_id int, order_id int);
insert into #orders values
('CA', 5, 3),
('CA', 5, 4),
('CA', 6, 5),
('CA', 6, 6),
('US', 2, 7),
('US', 7, 8),
('US', 7, 9),
('US', 7, 10),
('US', 2, 11);
select country,
sum(case when num_orders <= 2 then 1 else 0 end) as cust_w_2_orders,
sum(case when num_orders > 2 then 1 else 0 end) as cust_2_plus_orders
from (
select country, customer_id, count(*) num_orders
from #orders
group by country, customer_id
) x
group by country;
GO
country | cust_w_2_orders | cust_2_plus_orders
:------ | --------------: | -----------------:
CA | 2 | 0
US | 1 | 1
dbfiddle here
First construct a table that contains every customer and the # of orders they have per country where each row is country, customer_id, number_of_orders
Now you can count how often number_of_orders is 2 or greater than 2 by grouping on the derived table
select country, sum(num_orders = 2), sum(num_orders > 2)
from (
select country, customer_id, count(*) as num_orders
from Orders
group by country, customer_id
) t group by country
SELECT country,
(select count(distinct(customer_id)) from Orders o where o.country = Orders.country and (select count(*) from Orders o2 where o2.country = orders.country and o2.customer_id = o.customer_id) = 2) as customers_w_2_orders,
(select count(distinct(customer_id)) from Orders o where o.country = Orders.country and (select count(*) from Orders o2 where o2.country = orders.country and o2.customer_id = o.customer_id) > 2) as customers_w_2_plus_orders
FROM Orders
GROUP BY country;