How to pair rows with imperfecty in mysql? Imperfect, meaning the rows are not exactly paired as in and out. Sometimes, there are two in with one out or no out at all or two or more out with one in or no in at all.
The table look like this:
| id | action | date |
+----+--------+---------------------+
| 1 | in | 19.1.2012 15:41:52 |
| 1 | out | 19.1.2012 15:55:52 |
| 2 | in | 19.2.2012 15:55:52 |
| 2 | out | 19.2.2012 17:55:53 |
| 1 | in | 19.3.2012 15:55:54 |
| 1 | in | 19.3.2012 17:55:55 |
| 1 | out | 19.3.2012 19:55:56 |
| 3 | in | 19.4.2012 15:55:57 |
| 3 | out | 19.4.2012 17:55:58 |
| 3 | out | 19.4.2012 19:55:59 |
+----+--------+---------------------+
The desired result is like this:
+----+--------+---------------------+
| id | action | date |
+----+--------+---------------------+
| 1 | in | 19.1.2012 15:41:52 |
| 1 | out | 19.1.2012 15:55:52 |
| 2 | in | 19.2.2012 15:55:52 |
| 2 | out | 19.2.2012 17:55:53 |
| 1 | in | 19.3.2012 17:55:55 |
| 1 | out | 19.3.2012 19:55:56 |
| 3 | in | 19.4.2012 15:55:57 |
| 3 | out | 19.4.2012 17:55:58 |
+----+--------+---------------------+
This is the best desireed result
+----+---------------------+---------------------+
| id | date_in | date_out |
+----+---------------------+---------------------+
| 1 | 19.1.2012 15:41:52 | 19.1.2012 15:55:52 |
| 2 | 19.2.2012 15:55:52 | 19.2.2012 17:55:53 |
| 1 | 19.3.2012 17:55:55 | 19.3.2012 19:55:56 |
| 3 | 19.4.2012 15:55:57 | 19.4.2012 17:55:58 |
+----+---------------------+---------------------+
Here is a code, but it yields different result, can anyone figure out where the error is?
enter code here SELECT c.e_id
, CAST(c.in_time AS DATETIME) AS in_time
, c.out_time
FROM (
SELECT IF(#prev_id = d.id,#in_time,#in_time:=NULL) AS reset_in_time
, #in_time := IF(d.action = 'in',d.date,#in_time) AS in_time
, IF(d.action = 'out',d.date,NULL) AS out_time
, #prev_id := d.id AS id
FROM (
SELECT id, date_, action
FROM e
JOIN (SELECT #prev_id := NULL, #in_time := NULL) f
ORDER BY id, date, action
) d
) c
WHERE c.out_time IS NOT NULL
ORDER BY c.out_time enter code here
This selects an in event into a, an out event into b and uses a LEFT JOIN to eliminate the row if there exists an event c for the same id between them; that will simply get all in/out times that don't have an extra in or out between them.
SELECT a.id, a.date date_in, b.date date_out
FROM mytable a
JOIN mytable b
ON a.id = b.id AND a.date < b.date
LEFT JOIN mytable c
ON a.id = c.id AND c.date < b.date AND c.date > a.date
WHERE a.action = 'in' AND b.action = 'out' AND c.action IS NULL
ORDER BY a.date;
An SQLfiddle to test with.
Related
My database has two tables
MariaDB [testnotes]> describe contactstbl;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(6) | YES | | NULL | |
| name | varchar(30) | YES | | NULL | |
| phone | varchar(20) | YES | | NULL | |
| email | varchar(40) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
MariaDB [testnotes]> describe notestbl;
+-----------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+----------+------+-----+---------+-------+
| id | int(6) | YES | | NULL | |
| notes | blob | YES | | NULL | |
| dateadded | datetime | YES | | NULL | |
+-----------+----------+------+-----+---------+-------+
I want a query that will show the last notes in the notestbl table for the give ID
contactstbl has about 100ish records I want to show them all even without notes
MariaDB [testnotes]> select * from contactstbl;
+------+------+-------+--------+
| id | name | phone | email |
+------+------+-------+--------+
| 1 | fran | 12335 | gf#g.m |
| 2 | tony | 45355 | ck#g.m |
| 3 | samm | 46545 | fs#g.m |
+------+------+-------+--------+
MariaDB [testnotes]> select * from notestbl;
+------+------------------+---------------------+
| id | notes | dateadded |
+------+------------------+---------------------+
| 1 | 2 days ago notes | 2020-01-12 00:00:00 |
| 3 | 5 days ago notes | 2020-01-09 00:00:00 |
| 3 | 3 days ago notes | 2020-01-11 00:00:00 |
| 1 | 1 days ago notes | 2020-01-13 00:00:00 |
| 1 | 3 days ago notes | 2020-01-11 00:00:00 |
+------+------------------+---------------------+
5 rows in set (0.00 sec)
I have tried a couple different queries and just cannot seem to get it right.
SELECT c.id,c.name,c.email,n.id,n.dateadded,n.notes FROM contactstbl c left join notestbl n using(id) GROUP BY c.id ORDER BY n.dateadded ASC;
Which is very close.
+------+------+--------+------+---------------------+------------------+
| id | name | email | id | dateadded | notes |
+------+------+--------+------+---------------------+------------------+
| 2 | tony | ck#g.m | NULL | NULL | NULL |
| 3 | samm | fs#g.m | 3 | 2020-01-09 00:00:00 | 5 days ago notes |
| 1 | fran | gf#g.m | 1 | 2020-01-12 00:00:00 | 2 days ago notes |
+------+------+--------+------+---------------------+------------------+
What I want to see is:
+------+------+--------+------+---------------------+------------------+
| id | name | email | id | dateadded | notes |
+------+------+--------+------+---------------------+------------------+
| 2 | tony | ck#g.m | NULL | NULL | NULL |
| 3 | samm | fs#g.m | 3 | 2020-01-11 00:00:00 | 3 days ago notes |
| 1 | fran | gf#g.m | 1 | 2020-01-13 00:00:00 | 1 days ago notes |
+------+------+--------+------+---------------------+------------------+
Just use subquery in SELECT clause:
SELECT
c.id,
c.name,
c.email,
(SELECT n.id FROM notestbl n WHERE n.id=c.id ORDER BY n.dateadded DESC LIMIT 1) nid,
(SELECT n.dateadded FROM notestbl n WHERE n.id=c.id ORDER BY n.dateadded DESC LIMIT 1) ndateadded,
(SELECT n.notes FROM notestbl n WHERE n.id=c.id ORDER BY n.dateadded DESC LIMIT 1) nnotes
FROM
contactstbl c
GROUP BY c.id
ORDER BY ndateadded ASC;
Result:
MariaDB [test]> SELECT
-> c.id,
-> c.name,
-> c.email,
-> (SELECT n.id FROM notestbl n WHERE n.id=c.id ORDER BY n.dateadded DESC LIMIT 1) nid,
-> (SELECT n.dateadded FROM notestbl n WHERE n.id=c.id ORDER BY n.dateadded DESC LIMIT 1) ndateadded,
-> (SELECT n.notes FROM notestbl n WHERE n.id=c.id ORDER BY n.dateadded DESC LIMIT 1) nnotes
-> FROM
-> contactstbl c
-> GROUP BY c.id
-> ORDER BY ndateadded ASC;
+----+------+--------+------+---------------------+------------------+
| id | name | email | nid | ndateadded | nnotes |
+----+------+--------+------+---------------------+------------------+
| 2 | tony | ck#g.m | NULL | NULL | NULL |
| 3 | sam | fs#g. | 3 | 2020-01-11 00:00:00 | 3 days ago notes |
| 1 | fran | gf#g.m | 1 | 2020-01-13 00:00:00 | 1 days ago notes |
+----+------+--------+------+---------------------+------------------+
3 rows in set (0.07 sec)
SELECT C.ID,
C.NAME,
C.EMAIL,
N1.ID,
N1.DATEADDED,
N1.NOTES
FROM CONTACTSTBL C
LEFT JOIN NOTESTBL N1 USING(ID)
LEFT JOIN NOTESTBL N2 ON N1.ID = N2.ID
AND N1.DATEADDED < N2.DATEADDED
WHERE N2.ID IS NULL
ORDER BY N1.DATEADDED;
also try some ideas from here
how do I query sql for a latest record date for each user
First, I think that you should change the schema of your notestbl table as it doesn't have its own id field, but instead relies purely on the id of the contactstbl table. This is bad design and should be normalised so as to prevent you pain in the future :)
I'd recommend it is changed to something like this:
mysql> select * from notestbl;
+------+------------+------------------+---------------------+
| id | contact_id | notes | dateadded |
+------+------------+------------------+---------------------+
| 1 | 1 | 2 days ago notes | 2020-01-12 00:00:00 |
| 2 | 3 | 5 days ago notes | 2020-01-09 00:00:00 |
| 3 | 3 | 3 days ago notes | 2020-01-11 00:00:00 |
| 4 | 1 | 1 days ago notes | 2020-01-13 00:00:00 |
| 5 | 1 | 3 days ago notes | 2020-01-11 00:00:00 |
+------+------------+------------------+---------------------+
5 rows in set (0.00 sec)
Then you can use this single line query to get the result you're after:
select c.id, c.name, c.email, n.id, n.dateadded, n.notes from contactstbl c left join (select t1.id, t1.contact_id, t1.dateadded, t1.notes from notestbl t1, (select contact_id, max(dateadded) as maxdate from notestbl group by contact_id) t2 where t1.contact_id=t2.contact_id and t1.dateadded=t2.maxdate) n on c.id=n.contact_id;
+------+------+--------+------+---------------------+------------------+
| id | name | email | id | dateadded | notes |
+------+------+--------+------+---------------------+------------------+
| 1 | fran | gf#g.m | 4 | 2020-01-13 00:00:00 | 1 days ago notes |
| 2 | tony | ck#g.m | NULL | NULL | NULL |
| 3 | samm | fs#g.m | 3 | 2020-01-11 00:00:00 | 3 days ago notes |
+------+------+--------+------+---------------------+------------------+
3 rows in set (0.00 sec)
A more visually pleasing representation of the query:
select c.id,
c.name,
c.email,
n.id,
n.dateadded,
n.notes
from contactstbl c
left join (select t1.id,
t1.contact_id,
t1.dateadded,
t1.notes
from notestbl t1,
(select contact_id, max(dateadded) as maxdate from notestbl group by contact_id) t2
where t1.contact_id=t2.contact_id
and t1.dateadded=t2.maxdate) n
on c.id=n.contact_id;
Got following Tables in my SQL-Database (simplified):
Table Blogs:
+----+----------------------+----------+
| ID | Date | TitleGer |
+----+----------------------+----------+
| 1 | 2017-04-28 15:09:46 | Huhu |
| 2 | 2017-04-28 15:16:18 | Miau |
| 3 | 2017-04-28 15:17:14 | Kleff |
+----+----------------------+----------+
Table PicturesJoin:
+-------------+---------+---------------------+
| IDPicture | IDBlog | Date |
+-------------+---------+---------------------+
| 86 | 1 | 2017-06-28 17:41:11 |
| 87 | 1 | 2017-06-28 17:41:11 |
+-------------+---------+---------------------+
Table Pictures:
+------+-------------------------+---------------------+
| ID | Filename | Date |
+------+-------------------------+---------------------+
| 86 | 20170512200326_320.jpg | 2017-05-12 20:03:26 |
| 87 | 20170512200326_384.jpg | 2017-05-12 20:03:30 |
+------+-------------------------+---------------------+
PictureJoin "joins" the Picture with the Blog-Table. Now I use following SQL-Command to joine these two Tables (Blog - PictureJoin) / (PictureJoin - Pictures) together.
SELECT
Blogs.ID,
Blogs.Date,
TitleGer,
Pictures.Filename
FROM
Blogs
LEFT JOIN
PicturesJoin ON PicturesJoin.IDBlog = Blogs.ID
LEFT JOIN
Pictures ON Pictures.ID = PicturesJoin.IDPicture
ORDER BY
DATE DESC
The result might look like that:
+------+----------------------+-----------+------------------------+
| ID | Date | TitleGer | Filename |
+------+----------------------+-----------+------------------------+
| 1 | 2017-06-28 15:09:46 | Huhu | 20170512200326_320.jpg |
| 1 | 2017-06-28 15:09:46 | Huhu | 20170512200326_384.jpg |
| 2 | 2017-04-28 15:16:18 | Miau | NULL |
| 3 | 2017-04-28 15:17:14 | Kleff | NULL |
+------+----------------------+-----------+------------------------+
He makes a cross-product out of the available Pictures, which is also logical. But I want him to just use the first Picture he finds. In the end it should look like that:
+------+----------------------+-----------+------------------------+
| ID | Date | TitleGer | Filename |
+------+----------------------+-----------+------------------------+
| 1 | 2017-06-28 15:09:46 | Huhu | 20170512200326_320.jpg |
| 2 | 2017-04-28 15:16:18 | Miau | NULL |
| 3 | 2017-04-28 15:17:14 | Kleff | NULL |
+------+----------------------+-----------+------------------------+
Tried several hours but couldn't get it to work. Please help!
The easiest approach may be to select one IDPicture per IDBlog from PicturesJoin only:
SELECT
b.ID,
b.Date,
b.TitleGer,
p.Filename
FROM Blogs b
LEFT JOIN
(
SELECT
IDBlog,
MIN(IDPicture) AS IDPicture
FROM PicturesJoin
GROUP BY IDBlog
) pj ON pj.IDBlog = b.ID
LEFT JOIN Pictures p ON p.ID = pj.IDPicture
ORDER BY b.Date DESC;
SELECT b.ID,b.Date,b.TitleGer,p.Filename
FROM Blogs b
LEFT JOIN
(
SELECT main_table.*
FROM PicturesJoin main_table LEFT JOIN PicturesJoin child_table
ON (main_table.IDBlog= child_table.IDBlog AND main_table.IDPicture> child_table.IDPicture)
WHERE child_table.id IS NULL
)
OUTER_TABLE ON OUTER_TABLE .IDBlog = b.ID
LEFT JOIN Pictures p ON p.ID = pj.IDPicture
ORDER BY b.Date DESC;
Try above query.
Evening. Hope someone can help. Been stumped with this for a few days now...
I have the following table....
| ID | Supplier | Item_1 | Cost_1 | Item_2 | Cost_2 | Item_3 | Cost_3 |
+----+----------+--------+--------+--------+--------+--------+--------+
| 1 | 1 | 732w | 3.99 | 314d | 7.58 | 399p | 15.44 |
| 1 | 2 | SyYh33 | 3.78 | GjuUh4 | 7.60 | 2su7js | 15.45 |
| 1 | 3 | 5443 | 4.01 | 9833 | 7.63 | 7433 | 15.22 |
| 2 | 1 | 596q | 15.42 | 933k | 28.56 | 732c | 69.99 |
| 2 | 2 | hyjs9k | 15.86 | ka7snf | 28.99 | h23nfs | 68.99 |
| 2 | 3 | 5477 | 14.99 | 5658 | 28.49 | 8153 | 70.15 |
+----+----------+--------+--------+--------+--------+--------+--------+
Now what i would like to do is return the cheapest price from columns Cost_1, Cost_2, Cost_3 with it's corresponding Item column and Supplier for each ID....
So basically would like the following result
| ID | Supplier_1 | Item_1 | Cost_1 | Supplier_2 | Item_2 | Cost_2 | Supplier_3 | Item_3 | Cost_3 |
+----+------------+--------+--------+------------+--------+--------+------------+--------+--------+
| 1 | 2 | SyYh33 | 3.78 | 1 | 314d | 7.58 | 3 | 7433 | 15.22 |
| 2 | 3 | 5477 | 14.99 | 3 | 5658 | 28.49 | 2 | h23nfs | 68.99 |
Any pointers would be great. Tried with joins and MIN() but i haven't been able to return the desired results. Hopefully there is a MySQL guru out there that can put me out of my misery. Thanks in advance
Normalize your schema - Otherwise you will have to live with queries like this:
select f.ID,
i1.Supplier as Supplier_1,
i1.Item_1,
i1.Cost_1,
i2.Supplier as Supplier_2,
i2.Item_2,
i2.Cost_2,
i3.Supplier as Supplier_3,
i3.Item_3,
i3.Cost_3
from (select distinct ID from following_table) f
join following_table i1 on i1.ID = f.ID and i1.Supplier = (
select t.Supplier
from following_table t
where t.ID = i1.ID
order by Cost_1 asc
limit 1
)
join following_table i2 on i2.ID = f.ID and i2.Supplier = (
select t.Supplier
from following_table t
where t.ID = i2.ID
order by Cost_2 asc
limit 1
)
join following_table i3 on i3.ID = f.ID and i3.Supplier = (
select t.Supplier
from following_table t
where t.ID = i3.ID
order by Cost_3 asc
limit 1
)
http://sqlfiddle.com/#!9/04933/2
I think what you are looking for is a UNION to combine your results.
SELECT *
FROM your_table
WHERE Cost_1 = (SELECT MIN(Cost_1) FROM your_table)
UNION
SELECT *
FROM your_table
WHERE Cost_2 = (SELECT MIN(Cost_2) FROM your_table)
UNION
SELECT *
FROM your_table
WHERE Cost_3 = (SELECT MIN(Cost_3) FROM your_table)
Here my table structure:
___Rooms:
|--------|------------|
| ROO_Id | ROO_Name |
|--------|------------|
| 1 | Room 1 |
| 2 | Room 2 |
| 3 | Room 3 |
|--------|------------|
___Bookings:
|--------|------------|
| BOO_Id | BOO_RoomId |
|--------|------------|
| 1 | 1 |
| 2 | 2 |
| 3 | 2 |
|--------|------------|
___BillableDatas:
|--------|---------------|------------|------------|
| BIL_Id | BIL_BookingId | BIL_Date | BIL_Item |
|--------|---------------|------------|------------|
| 1 | 1 | 2017-02-21 | Night |
| 2 | 1 | 2017-02-22 | Night |
| 3 | 1 | 2017-02-23 | Night |
| 4 | 1 | 2017-02-24 | Night |
| 5 | 2 | 2017-02-30 | Night |
| 6 | 2 | 2017-02-31 | Night |
| 7 | 1 | 2017-02-31 | Night |
|--------|---------------|------------|------------|
I would like to know the most popular room.
The desired result should be:
|------------|------------|------------|
| ROO_Name | Night Nb | Percentage |
|------------|------------|------------|
| Room 1 | 5 | 71.42 |
| Room 2 | 2 | 28.57 |
| Room 3 | 0 | 0 |
|------------|------------|------------|
What I already tried:
SELECT r.ROO_Id
, Sum(CASE WHEN BOO_Id IS NULL THEN 0 ELSE 1 END) NumBookings
, Concat(
Format(
Sum(CASE WHEN BOO_Id IS NULL THEN 0 ELSE 1 END)
/ TotalBookings
* 100
, 0) ) AS PercentageTotal
FROM ( ___Rooms r LEFT JOIN ___Bookings b ON r.ROO_Id = b.BOO_RoomId
) INNER JOIN (SELECT BOO_HotelId
, Count(*) AS TotalBookings
FROM ___Bookings
GROUP BY BOO_HotelId
) AS TotalHotelBookings
ON r.ROO_HotelId = TotalHotelBookings.BOO_HotelId
WHERE r.ROO_HotelId = :hotel_id
GROUP BY r.ROO_Id
ORDER BY NumBookings DESC
But it doesn't work actually.
You could use the SQL Fiddle:
http://sqlfiddle.com/#!9/390b1
try this
select Roo_Name,coalesce(bookid,0) as nightdb,coalesce(bookid * 10/Boo_Id,0) as percentage
from ___Rooms r1
left join
(select count(BOO_RoomId) as book, BOO_Id
from ___Bookings group by BOO_Id) b1
on r1.Roo_Id = b1.Boo_id
left join
(select count(Bil_BookingId) as bookid,BIL_BookingId
from ___BillableDatas
group by BIL_BookingId) b2
on b2.BIL_BookingId = b1.BOO_Id group by r1.Roo_Name;
DEMO
I have two tables like this
profile_answers
+---------+------------+
| id | class_name |
+---------+------------+
| 1 | Class 1 |
| 2 | Class 2 |
| 3 | Class 1 |
+---------+------------+
educations
+---------+--------------------+------------+
| id | profile_answers_id | sample |
+---------+--------------------+------------+
| 1 | 1 | 1234 |
| 2 | 1 | 2334 |
| 3 | 1 | 3434 |
+---------+------------+--------------------+
I ran the query,
select educations.profile_answer_id, GROUP_CONCAT(educations.sample) from educations
LEFT JOIN profile_answers ON educations.profile_answers_id = profile_answers.id
I got
+--------+--------------------+-------------+
| id | sample |
+---------+--------------------+------------+
| 1 | 1234,2334,3434 |
+---------+------------+--------------------+
I actually want,
+--------+--------------------+-------------+
| id | sample |
+---------+--------------------+------------+
| 1 | 1234,2334,3434 |
| 2 | NULL |
| 3 | NULL |
+---------+------------+--------------------+
SELECT id,IFNULL(samples,'NULL') sample FROM
(
SELECT
AA.id,
GROUP_CONCAT(DISTINCT BB.sample) samples
FROM
profile_answers AA LEFT JOIN educations BB
ON AA.id = BB.profile_answers_id
GROUP BY AA.id
) A;
Looks like you're missing your GROUP BY:
select profile_answers.id, GROUP_CONCAT(educations.sample)
from profile_answers
LEFT JOIN educations ON educations.profile_answers_id = profile_answers.id
GROUP BY profile_answers.id
I also altered your JOIN to make the profile_answers table your main table.
Good luck.