How do you make a nested select with a restriction (WHERE) - mysql

I have a database for a small calendar app in which people are stored in the clients table, dates are stored in the table calendarDate and since the relations are many to many there is a connecting table called client_date which holds both of their ids.
I want to make a nested select to get all the dates for a particular person lets say with id = 2.
I came up with this, but it prints all of the dates and asigns them to the person with that id, instead of just printing the only ones he is asigned to:
SELECT c.username
, c.country
, d.day
, d.month
, d.year
, d.dayOfWeek
, d.weekOfYear
, d.emotionId
, d.id
from clients as c
join calendarDate as d
on d.id in (SELECT dateId
from client_date
WHERE clientId in (SELECT id
from clients )
)
where c.id = 2;
Is there something I am doing wrong or is there another way to make a nested select statement ?
My database and data:
DROP DATABASE IF EXISTS calendar;
CREATE DATABASE calendar;
USE calendar;
CREATE TABLE clients(
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
joinedOnDate DATE NOT NULL,
country VARCHAR(100) NOT NULL
);
CREATE TABLE emotions(
id INT NOT NULL PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE,
value DOUBLE
);
CREATE TABLE calendarDate(
id INT AUTO_INCREMENT PRIMARY KEY,
day INT NOT NULL,
month INT NOT NULL,
year INT NOT NULL,
dayOfWeek VARCHAR(20) NOT NULL,
weekOfYear int NOT NULL,
emotionId INT NOT NULL,
CONSTRAINT FOREIGN KEY (emotionId)
REFERENCES emotions( id )
);
CREATE TABLE client_date(
dateId INT NOT NULL,
clientId INT NOT NULL,
CONSTRAINT FOREIGN KEY ( dateId )
REFERENCES calendarDate( id ) ,
CONSTRAINT FOREIGN KEY ( clientId )
REFERENCES clients( id ) ,
UNIQUE KEY( dateId, clientId )
);
USE calendar;
INSERT INTO emotions (id, name, value) VALUES
(0, 'None', 1),
(1, 'Excited', 2.0),
(2, 'Happy', 2.0),
(3, 'Positive', 1.5),
(4, 'Average', 1.0),
(5, 'Mixed', 1),
(6, 'Negative', 0.5),
(7, 'Sad', 0);
INSERT INTO clients (username, joinedOnDate, country) VALUES
('Malazzar', DATE(NOW()), 'Bulgaria'),
('Preslava981', DATE(NOW()), 'Bulgaria'),
('Thusnake', DATE(NOW()), 'United Kingdom');
INSERT INTO calendarDate (day, month, year, dayOfWeek, weekOfYear, emotionId) VALUES
(1, 1, 2019, 'Tuesday', 1, 0),
(2, 1, 2019, 'Wednesday', 1, 0),
(3, 1, 2019, 'Thursday', 1, 0),
(4, 1, 2019, 'Friday', 1, 0),
(5, 1, 2019, 'Saturday', 1, 0),
(6, 1, 2019, 'Sunday', 1, 0),
(7, 1, 2019, 'Monday', 2, 0),
(8, 1, 2019, 'Tuesday', 2, 0),
(9, 1, 2019, 'Wednesday', 2, 0),
(10, 1, 2019, 'Thursday', 2, 0),
(11, 1, 2019, 'Friday', 2, 0),
(12, 1, 2019, 'Saturday', 2, 0),
(13, 1, 2019, 'Sunday', 2, 0),
(14, 1, 2019, 'Monday', 3, 0);
INSERT INTO client_date (clientId, dateId) VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(1, 6),
(1, 7),
(2, 8),
(2, 9),
(2, 10),
(2, 11),
(2, 12),
(2, 13),
(2, 14);
The output I get for the particular client:
Preslava981 Bulgaria 1 1 2019 Tuesday 1 0 1
Preslava981 Bulgaria 2 1 2019 Wednesday 1 0 2
Preslava981 Bulgaria 3 1 2019 Thursday 1 0 3
Preslava981 Bulgaria 4 1 2019 Friday 1 0 4
Preslava981 Bulgaria 5 1 2019 Saturday 1 0 5
Preslava981 Bulgaria 6 1 2019 Sunday 1 0 6
Preslava981 Bulgaria 7 1 2019 Monday 2 0 7
Preslava981 Bulgaria 8 1 2019 Tuesday 2 0 8
Preslava981 Bulgaria 9 1 2019 Wednesday 2 0 9
Preslava981 Bulgaria 10 1 2019 Thursday 2 0 10
Preslava981 Bulgaria 11 1 2019 Friday 2 0 11
Preslava981 Bulgaria 12 1 2019 Saturday 2 0 12
Preslava981 Bulgaria 13 1 2019 Sunday 2 0 13
Preslava981 Bulgaria 14 1 2019 Monday 3 0 14

Your mistake is here:
WHERE clientId in (SELECT id from clients)
Every client ID exists in the clients table. You want this instead:
WHERE clientId = c.id
You could also use a non-corelated subquery instead, which is easier to read:
on (c.id, d.id) in (select clientid, dateid from client_date)
But as has been mentioned by others, a mere join to client_date would do the same job.

Why not just use joins?
select c.username, c.country, d.*
from clients c join
client_date cd
on cd.clientId = c.id join
calendarDate as d
on d.id cd.dateId
where c.id = 2;

I want to make a nested select to get all the dates for a particular
person lets say with id = 2
This can be done with:
select *
from calendarDate
where day in (
select dateId
from client_date
where clientId = 2
)
An equivalent JOIN query would be:
select d.*
from calendarDate d
join client_date cd
on cd.dateId = d.day
where cd.clientId = 2
View on DB Fiddle
Note: The two queries are only equivalent, if there are no duplicates in the client_date table.
If you want to select data from more than one table, then you actualy need to use a JOIN. In that case asking for how to do it without joins, wouldn't make sense.

Related

How do i get a limited number of rows for each value in a field? (db is mysql but could be changed if easier)

I found this difficult to search for this question. I have a table of sports fixtures (tbl_fixture) and a table of sports participants (tbl_participant) which have a many-to-many relationship via a linking table (tbl_fixture_participant)
I need to return the most recent 3 fixtures (ie latest tbl_fixture.start_datetime) of multiple participants and whether they won each of the fixtures, (eg more recent 3 fixtures of participant 1 and most recent 3 fixtures of participant 2, and most recent 3 fixtures of participant 3, with each record returning the fixture_id, participant_id, start_datetime and is_winner fields).
The number of participants that i need to get the data for could be between 1 and 100.
If there's a better way to structure my data, or a better database for this type of query (graph db?) then i'm happy to look into those.
Here's a sample schema:
CREATE TABLE tbl_fixture (
fixture_id INT AUTO_INCREMENT PRIMARY KEY,
start_datetime DATETIME NOT NULL
);
CREATE TABLE tbl_participant (
participant_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE TABLE tbl_fixture_participant (
fixture_id INT NOT NULL,
participant_id INT NOT NULL,
is_winner TINYINT NOT NULL,
FOREIGN KEY (fixture_id)
REFERENCES tbl_fixture (fixture_id)
ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (participant_id)
REFERENCES tbl_participant (participant_id)
ON UPDATE RESTRICT ON DELETE CASCADE
);
INSERT INTO tbl_fixture (fixture_id, start_datetime)
VALUES (1, "2021-01-14 15:00:00"),
(2, "2021-01-13 16:00:00"),
(3, "2021-01-12 17:00:00"),
(4, "2021-01-11 15:00:00"),
(5, "2021-01-19 16:00:00"),
(6, "2021-01-18 17:00:00"),
(7, "2021-01-05 15:00:00"),
(8, "2021-01-03 16:00:00"),
(9, "2021-01-03 17:00:00"),
(10, "2021-01-11 15:00:00"),
(11, "2021-01-12 16:00:00"),
(12, "2021-01-13 17:00:00"),
(13, "2021-01-14 15:00:00"),
(14, "2021-01-19 16:00:00");
INSERT INTO tbl_participant (participant_id, name) VALUES
( 1,"Team 1"),
( 2,"Team 2"),
( 3,"Team 3");
INSERT INTO tbl_fixture_participant (fixture_id, participant_id, is_winner)
VALUES (1, 1, 0)
,(2, 1, 1)
,(2, 2, 0)
,(3, 1, 1)
,(12, 2, 0)
,(4, 3, 1)
,(4, 2, 0)
,(6, 3, 1)
,(1, 2, 1)
,(10, 1, 1)
,(5, 2, 0)
,(6, 1, 0)
,(11, 1, 1)
,(14, 1, 0)
,(7, 2, 0)
,(7, 3, 1)
,(3, 3, 0)
,(8, 1, 0)
,(5, 3, 1)
,(13, 2, 0)
,(8, 3, 1)
,(13, 3, 1)
,(9, 1, 0)
,(9, 2, 1)
,(10, 2, 0)
,(11, 3, 0)
,(12, 3, 1)
,(14, 3, 1);
And SQL Fiddle of same.
I would like the data to come back like:
fixture_id
start_datetime
participant_id
is_winner
14
2021-01-19T16:00:00Z
1
0
6
2021-01-18T17:00:00Z
1
0
1
2021-01-14T15:00:00Z
1
0
5
2021-01-19T16:00:00Z
2
0
13
2021-01-14T15:00:00Z
2
0
1
2021-01-14T15:00:00Z
2
1
EDITED TO REFLECT FACT THAT DATES ARE NOT NECESSARILY SEQUENTIAL...
E.g. (for older versions of MySQL)...
SELECT x.*
, fx.start_datetime
FROM fixture_participant x
JOIN fixture fx
ON fx.fixture_id = x.fixture_id
JOIN fixture_participant y
ON y.participant_id = x.participant_id
JOIN fixture fy
ON fy.fixture_id = y.fixture_id
AND fy.start_datetime > fx.start_datetime
GROUP
BY x.fixture_id
, x.participant_id
, x.is_winner
, fx.start_datetime
HAVING COUNT(x.fixture_id) <=3
ORDER
BY participant_id,fixture_id;
...or something like that.

Get summary grouped by category with three tables on mysql

category
---------------------------
id_category primary key
category
id_user foreign key
counterpart
---------------------------
id_counterpart primary key
counterpart
id_category foreign key
id_user foreign key
transaction
---------------------------
transaction primary key
date
id_counterpart foreign key
amount
id_card foreign key
id_user foreign key
Hello,
I have thoses table on mysql database and i want to have summary of each category (with 0 if there is any transaction) by month and year based on id_user.
I tried this command to have grouped by counterpart and it works but cannot reach when i add category and group by id_category.
select counterpart, s2.total from counterpart as s1
left join (select coalesce(sum(amount),0) as total, id_counterpart from transaction where year(date) = 2019 and month(date) = 7 and id_user = 2 group by id_counterpart) as s2
on s1.id_counterpart = s2.id_counterpart
left join category on s1.id_category = category.id_category
group by counterpart;
Do you have any idea to do that ? Else, i will do with php.
Thank you.
Edit : Add example
INSERT INTO `category` (`id_category`, `category`, `id_user`) VALUES
(1, 'cat_a', 1),
(2, 'cat_b', 1),
(3, 'cat_c', 1);
INSERT INTO `counterpart` (`id_counterpart`, `counterpart`, `id_category`, `id_user`) VALUES
(1, 'cp_a', 1, 1),
(2, 'cp_b', 2, 1),
(3, 'cp_c', 2, 1);
INSERT INTO `transaction` (`id_transaction`, `date`, `id_counterpart`, `amount`, `id_card`, `id_user`) VALUES
(1, '2019-07-01 00:00:00', 1, 400.00, 2, 1),
(2, '2019-07-01 00:00:00', 1, -24.95, 2, 1),
(3, '2019-07-31 00:00:00', 2, -20.04, 2, 1);
(4, '2019-07-30 00:00:00', 2, -1.00, 2, 1);
(5, '2019-07-29 00:00:00', 3, -2.00, 2, 1);
(6, '2019-07-28 00:00:00', 1, -3.00, 2, 1);
(7, '2019-07-27 00:00:00', 3, 2.00, 2, 1);
(8, '2019-07-26 00:00:00', 2, 5.00, 2, 1);
On july 2019 i want to have this, for user 1 :
cat_a 372.05
cat_b 16.04
cat_c 0.00
Join the tables and then group by category:
select c.category, coalesce(sum(t.amount), 0) total
from category c
left join counterpart as cp
on c.id_category = cp.id_category and c.id_user = cp.id_user
left join transaction t
on t.id_counterpart = cp.id_counterpart and t.id_user = cp.id_user and year(t.date) = 2019 and month(t.date) = 7 and t.id_user = 1
group by c.id_category, c.category
See the demo.
Results:
| category | total |
| -------- | ------ |
| cat_a | 372.05 |
| cat_b | -16.04 |
| cat_c | 0 |

multiple WHERE clauses on a joined table

I have a simple application that tracks diners and their favorite flavors and desserts. The records table is just the diner's name and ID, the mid table tracks the desserts and flavors (again by an ID linked to another table of values).
CREATE TABLE IF NOT EXISTS `records` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `records` (`id`, `name`) VALUES
(1, 'Jimmy Jones'),
(2, 'William Henry');
CREATE TABLE IF NOT EXISTS `mid` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`diner` int(11) NOT NULL,
`dessert` int(11) NOT NULL DEFAULT '0',
`flavor` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ;
INSERT INTO `mid` (`id`, `diner`, `dessert`, `flavor`) VALUES
(1, 1, 3, 0),
(2, 1, 2, 0),
(3, 1, 15, 0),
(4, 1, 0, 1),
(5, 2, 3, 0),
(6, 2, 6, 0),
(7, 2, 0, 4),
(8, 1, 34, 0),
(9, 2, 0, 4),
(10, 2, 0, 22);
I'm a little stumped by what should be a simple query-- I want to get all IDs from the records table where certain dessert or flavor requirements are met:
SELECT a.id
FROM records AS a
JOIN mid AS b ON a.id = b.diner
WHERE b.dessert IN (3,2,6)
AND b.flavor IN (4,22)
This query returns no rows, even though there are records that match the where clauses. I am pretty sure I'm missing something obvious with the JOIN but I've tried INNER, OUTER, LEFT and RIGHT with no success.
Can someone put me on the right track and explain what I'm missing?
Thanks
You seem to want diners that have the combinations. Here is one way:
select diner
from records
group by diner
having max(b.dessert = 3) = 1 and
max(b.dessert = 2) = 1 and
max(b.dessert = 6) = 1 and
max(b.flavor = 4) = 1 and
max(b.flavor = 22) = 1
This answers your comment:
select diner
from records
group by diner
having max(case when b.dessert in (2, 3, 6) then 1 esle 0 end) = 1 and
max(case when b.dessert in (4, 22) then 1 else 0 end) = 1
If you are just looking for the records in a that match the conditions, use:
select r.*, d.name
from records r join
diner d
on r.diner = d.id
where b.dessert IN (3,2,6) AND b.flavor IN (4,22)
If this is what you want, the join condition in your query is wrong (a.id should be a.diner).
You SQL statement is fine, but non of your sample records meet your condition, records that would match should look like this
dessert flavor
3 4
3 22
2 4
2 33
6 4
6 22
Non of your input record has any of these combinations
Your WHERE condition does not fit any record in the "mid" table.
There are no records that have dessert in (3, 2, 6) AND flavor in (4, 22), so the query (correctly)returns no result.
You don't have any records that match both where conditions.
( 1, 1, 3, 0) - Matches dessert IN (3,2,6)
( 2, 1, 2, 0) - Matches dessert IN (3,2,6)
( 3, 1, 15, 0)
( 4, 1, 0, 1)
( 5, 2, 3, 0) - Matches dessert IN (3,2,6)
( 6, 2, 6, 0) - Matches dessert IN (3,2,6)
( 7, 2, 0, 4) - Matches flavor IN (4,22)
( 8, 1, 34, 0)
( 9, 2, 0, 4) - Matches flavor IN (4,22)
(10, 2, 0, 22) - Matches flavor IN (4,22)
Perhaps you meant OR?
SELECT a.id
FROM records AS a
JOIN mid AS b ON a.id = b.diner
WHERE b.dessert IN (3,2,6)
OR b.flavor IN (4,22)
Should return 7 results.
Also, your thoughts on JOIN are a red herring. The difference between LEFT and RIGHT is just which table gets precedence when the join clause doesn't match records between them. The difference between INNER and OUTER is just what happens when there isn't a matching record between the two tables. Try this explanative article from coding horror for more details on joins (helpfully pointed out to me in a different SO question, heh).

count number of items in a row in mysql

I have a list of students that shows whether they were present or absent from a particular class.
CREATE TABLE classlist
(`id` int, `studentid` int, `subjectid` int, `presentid` int)
;
CREATE TABLE student
(`id` int, `name` varchar(4))
;
CREATE TABLE subject
(`id` int, `name` varchar(4))
;
CREATE TABLE classStatus
(`id` int, `name` varchar(8))
;
INSERT INTO classlist
(`id`, `studentid`, `subjectid`, `presentid`)
VALUES
(1, 111, 1, 1),
(2, 222, 3, 0),
(3, 333, 2, 1),
(4, 111, 4, 0),
(5, 111, 1, 1),
(6, 222, 3, 0),
(7, 333, 2, 1),
(8, 111, 4, 0),
(9, 111, 4, 0),
(10, 111, 4, 0),
(11, 111, 1, 1),
(12, 333, 3, 1),
(13, 333, 2, 1),
(14, 333, 3, 1)
;
INSERT INTO student
(`id`, `name`)
VALUES
(111, 'John'),
(222, 'Kate'),
(333, 'Matt')
;
INSERT INTO subject
(`id`, `name`)
VALUES
(1, 'MATH'),
(2, 'ENG'),
(3, 'SCI'),
(4, 'GEO')
;
INSERT INTO classStatus
(`id`, `name`)
VALUES
(0, 'Absent'),
(1, 'Present')
;
See Fiddle http://sqlfiddle.com/#!2/a2d93/5
I can count who is absent and present overall with something like the below.
SELECT
studentid,
students.name AS NAME,
SUM(presentid = 1) AS present,
SUM(presentid = 0) AS absent
FROM classlist
INNER JOIN student as students ON classlist.studentid=students.id
GROUP BY studentid, NAME
But I want to know how many classes in a row a student has attended/missed, so that the teachers can easily see if someone has missed a lot of time or someone is rewarded for good attendance etc. I have seen some posts which talk about streaks but none of them seem to match the way the data has been presented to me so I am not sure how to acheive this?
Just to be clear based on my sample data below the out put for the streaks should be.
(1, 111, 1, 1), /* John Present 1 times in a row */
(2, 222, 3, 0), /* Kate Absent 1 times in a row */
(3, 333, 2, 1), /* Matt Present 1 times in a row */
(4, 111, 4, 0), /* John Absent 1 times in a row */
(5, 111, 1, 1), /* John Present 1 times in a row */
(6, 222, 3, 0), /* Kate Absent 2 times in a row */
(7, 333, 2, 1), /* Matt Present 2 times in a row */
(8, 111, 4, 0), /* John Absent 1 times in a row */
(9, 111, 4, 0), /* John Absent 2 times in a row */
(10, 111, 4, 0), /* John Absent 2 times in a row */
(11, 111, 1, 1), /* John Present 1 times in a row */
(12, 333, 3, 1), /* Matt Present 3 times in a row */
(13, 333, 2, 1), /* Matt Present 4 times in a row */
(14, 333, 3, 1) /* Matt Present 5 times in a row */
/*Showing the latest status for each user*/
/* John Present 1 times in a row */
/* Kate Absent 2 times in a row */
/* Matt Present 5 times in a row */
John present 1.
Kate absent 2.
Matt Present 5.
This should give the count of the consecutive rows with the same value as the last row:
select
classlist.studentid,
student.name,
classStatus.name status,
count(*) presentcnt
from
classlist inner join student
on classlist.studentid=student.id
inner join classstatus
on classlist.presentid=classstatus.id
inner join (
select
studentid,
max(case when presentid=0 then id end) max_0,
max(case when presentid=1 then id end) max_1
from classlist
group by studentid
) s
on coalesce(classlist.id>least(max_0,max_1) and
classlist.id<=greatest(max_0,max_1),1)
and s.studentid=classlist.studentid
group by classlist.studentid
On the subquery I'm extracting the max id where presentid = 0 and the max id where presentid = 1.
On the outer query I'm extracting and counting all rows where id is > than the least of max_0 and max_1, and <= than the greatest of those two. No matter what the last value is, these are all the rows with the same value as the last one.
If either max_0 or max_1 is null, it means that all rows have just one value, 1 or 0, and we have to get all of them. If one of max_0 or max_1 is null, the whole conditions will be null as well. Using Coalesce( condition, 1) I'm returning all rows in that case.
Notice that I am grouping by classlist.studentid and showing some nonaggregated columns, but this is a situation in which it is allowed since all nonaggregated columns have the same value.
Do you want this?:
SELECT
studentid,
name,
SUM(present = 1) AS present,
SUM(present = 0) AS absent,
count(subject) as classTimes,
subject
FROM your_table GROUP BY studentid, name,subject
If id from classlist can be used for ordering then you will get your expected result with
SELECT
s.name,
CASE t1.presentid
WHEN 0 THEN 'absent'
WHEN 1 THEN 'present'
END state,
t1.pc
FROM (
SELECT
c.id,
c.studentid,
c.presentid,
#pcount := IF( #pid = presentid AND #sid = studentid, #pcount + 1, 1 ) as pc,
#pid := presentid,
#sid := studentid
FROM
classlist c
ORDER BY
studentid, id ) t1
JOIN student s
ON t1.studentid = s.id
WHERE
( t1.studentid, t1.id ) IN ( SELECT
studentid,
MAX( id )
FROM classlist
GROUP BY studentid );
SQL Fiddle DEMO
Try this:
SELECT A.studentid, s.name, IF(presentid = 1, 'present', 'absent') STATUS,
ABS(SUM(IF(presentid = 1, 1, -1))) AS presentcnt
FROM classlist A
INNER JOIN student s ON A.studentid = s.id
LEFT JOIN (SELECT MAX(id) id, studentid
FROM classlist GROUP BY studentid
) B ON A.studentid = B.studentid AND A.id = B.id
GROUP BY A.studentid
Check this SQL FIDDLE DEMO
OUTPUT
| STUDENTID | NAME | STATUS | PRESENTCNT |
-------------------------------------------
| 111 | John | present | 1 |
| 222 | Kate | absent | 2 |
| 333 | Matt | present | 5 |
I'm not sure what point about your question.
If this you want
John present 1.
Kate absent 2.
Matt Present 5.
U may try this:
SELECT
studentid,
students.name AS NAME,
SUM(presentid = 1) AS present,
SUM(presentid = 0) AS absent,
IF( SUM(presentid = 1)-SUM(presentid = 0)>=0,SUM(presentid = 1)-SUM(presentid = 0),SUM(presentid = 0)-SUM(presentid = 1)) AS aliase
FROM classlist
INNER JOIN student as students ON classlist.studentid=students.id
GROUP BY studentid, NAME

Select and show business open hours from MySQL

I dont need to check if business is open or close, but I need to show open hours by days.
There are some options:
1 - Business open once in day (sample - from 10:00 to 18:30) - one
rows in table
2 - Business open TWICE in day (samlpe - from 10:00 to
14:00 and from 15:00 to 18:30) - two rows in table
3 - Business may
be closed (no row inserted)
Here my MySql table of hours storing. In this sample business (affiliate_id) are open twice in days from 0 to 4, once in day 5 and closed in day 6 (no records for this day)
http://postimage.org/image/yplj4rumj/
What I need to show in website its like (according to this database example:
0,1,2,3,4 - open 10:00-14:00 and 15:00-18:30
5 - open 10:00-12:00
6 - closed
How I get results like:
http://postimage.org/image/toe53en63/
?
I tried to make queries with GROUPֹ_CONCAT and LEFT JOIN the same table ON a.day=b.day but with no luck :(
There sample of my query (that is wrong)
SELECT GROUP_CONCAT( DISTINCT CAST( a.day AS CHAR )
ORDER BY a.day ) AS days, DATE_FORMAT( a.time_from, '%H:%i' ) AS f_time_from, DATE_FORMAT( a.time_to, '%H:%i' ) AS f_time_to, DATE_FORMAT( b.time_from, '%H:%i' ) AS f_time_from_s, DATE_FORMAT( b.time_to, '%H:%i' ) AS f_time_to_s
FROM business_affiliate_hours AS a LEFT
JOIN business_affiliate_hours AS b ON a.day = b.day
WHERE a.affiliate_id =57
GROUP BY a.time_from, a.time_to, b.time_from, b.time_to
ORDER BY a.id ASC
This my table:
CREATE TABLE IF NOT EXISTS `business_affiliate_hours` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`affiliate_id` int(10) unsigned NOT NULL DEFAULT '0',
`time_from` time NOT NULL,
`time_to` time NOT NULL,
`day` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
INSERT INTO `business_affiliate_hours` (`id`, `affiliate_id`, `time_from`, `time_to`, `day`) VALUES
(53, 57, '10:00:00', '12:00:00', 5),
(52, 57, '15:00:00', '18:30:00', 4),
(51, 57, '10:00:00', '14:00:00', 4),
(50, 57, '15:00:00', '18:30:00', 3),
(49, 57, '10:00:00', '14:00:00', 3),
(48, 57, '15:00:00', '18:30:00', 2),
(47, 57, '10:00:00', '14:00:00', 2),
(46, 57, '15:00:00', '18:30:00', 1),
(45, 57, '10:00:00', '14:00:00', 1),
(44, 57, '15:00:00', '18:30:00', 0),
(43, 57, '10:00:00', '14:00:00', 0);
Open hours may be different for every day, so I want to GROUP by the same open hours, and get list of days for all unique order of open hours.
Need your help!
Sorry for links to images, I cant upload images yes to here.
First build a materialised table of each day's combined times, then group on that:
SELECT GROUP_CONCAT(day ORDER BY day) AS days,
DATE_FORMAT(f1, '%H:%i') AS f_time_from,
DATE_FORMAT(t1, '%H:%i') AS f_time_to,
DATE_FORMAT(f2, '%H:%i') AS f_time_from_s,
DATE_FORMAT(t2, '%H:%i') AS f_time_to_s
FROM (
SELECT day,
MIN(time_from) AS f1,
MIN(time_to ) AS t1,
IF(COUNT(*) > 1, MAX(time_from), NULL) AS f2,
IF(COUNT(*) > 1, MAX(time_to ), NULL) AS t2
FROM business_affiliate_hours
WHERE affiliate_id = 57
GROUP BY day
) t
GROUP BY f1, t1, f2, t2
ORDER BY days
See it on sqlfiddle.