mysql left join with limit 0,2 - mysql

comment table
+------+----------+
| id | comment |
+------+----------+
| 1 | foo |
| 2 | bar |
| 3 | foobar |
+------+----------+
reply table
+------+----------+------------+
| id | reply |comment_id |
+------+----------+------------+
| 1 | nice lol | 1 |
| 2 | ok ok | 2 |
| 3 | hello | 1 |
| 4 | hello2 | 1 |
| 5 | hello1 | 1 |
+------+----------+------------+
SELECT
`comment`.`comment`,
`x`.`reply`
FROM `comment` LEFT JOIN
(SELECT GROUP_CONCAT(`reply`) as reply ,reply.commnt_id FROM `reply`
GROUP BY `reply`.`comment_id` ORDER BY `reply`.`id` LIMIT 0,1)x ON x.comment_id = comment.id
the result will be
+----------+-----------------+
| comment | reply |
+----------+-----------------+
| foo | nice lol,hello |
| bar | NULL |
| off | null |
+------+---------------------+
the question why the second comment have null but if i make limit 0,4 its will show it

Mysql does not support limits in group by clause. To achieve this type of feature we can hack group_concat as shown below:
SELECT
comment.comment,
x.replay
FROM comment LEFT JOIN
(SELECT
REPLACE(substring_index(group_concat(replay SEPARATOR '##'), '##', 2), '##', ',') as replay ,replay.commnt_id
FROM replay
GROUP BY replay.comment_id ORDER BY replay.id LIMIT 0,1)x
ON x.comment_id = comment.id
this is considering that your replies will not have '##' in them.
related posts:
GROUP_CONCAT with limit
Mysql group_concat limit rows in grouping

Related

mysql table ordering incorrect with group by and order by

table 1: forum_threads
+-----+------+-------+
| id | title| status|
+-----+------+-------+
| 1 | a | 1 |
| 2 | b | 1 |
| 3 | c | 1 |
| 4 | d | 1 |
| 5 | e | 1 |
| 6 | f | 1 |
+-----+------+-------+
table 2: forum_comments
+-----+----------+--------------------+
| id | thread_id| comment |
+-----+----------+--------------------+
| 1 | 4 | hai |
| 2 | 4 | hello |
| 3 | 2 | welcome |
| 4 | 2 | whats your name |
| 5 | 6 | how are you |
| 6 | 5 | how old are you |
| 7 | 5 | good |
+-----+----------+--------------------+
wanted output
+-----------+----------+-----------------+
| thread_id | title | comment_count |
+-----------+----------+-----------------+
| 5 | e | 2 |
| 6 | f | 1 |
| 2 | b | 2 |
| 4 | d | 2 |
+-----------+----------+-----------------+
my Query
SELECT forum_threads.*,forum_comments.*,count(forum_comments.id) as comment_count
FROM forum_comments
LEFT JOIN forum_threads ON forum_comments.thread_id = forum_threads.id
GROUP BY forum_threads.id
ORDER BY forum_comments.id desc
Here I am trying to get the titles by the latest comment.
when I give ORDER BY forum_comments.id this returns the wrong order.
I need to order by the latest comments in the forum_comments table.
this query returns the wrong order please help me to find out the correct order.
how could I solve this easily?
This query should give you the expected result:
select t2.thread_id, t1.title, t2.comment_count from forum_threads as t1,
(SELECT id, thread_id, count(comment) as comment_count from forum_comments group by thread_id) as t2
where t1.id = t2.thread_id order by t2.id desc;
Instead of using forum_threads.* and forum_comments.* can you give specific column names and try.
If that doesn't work you should try explicitly assigning primary and foreign keys.

How to get last added field from MySQL database JOIN

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;

Incorrect update with inner join

I encountered a problem that I just can not solve.
For example, I have the table with rows, id, season, episode, order.
Data in the table looks like:
+--------+---------------+----------------+--------------+
| id | season | episode | order |
+--------+---------------+----------------+--------------+
| 153914 | 1 | 1 | NULL |
| 153915 | 1 | 3 | NULL |
| 153916 | 1 | 2 | NULL |
| 153919 | 1 | 3 | NULL |
| 153920 | 1 | 4 | NULL |
| 153921 | 1 | 3 | NULL |
+--------+---------------+----------------+--------------+
So, when I run SELECT query without UPDATE, row order is sorted absolutely correctly
SELECT id, season, episode, (#row:=#row+1) as order
FROM `shows`, (select #row:=0) as rc
WHERE `show_id`= 14670
ORDER BY CAST(season AS UNSIGNED) ASC, CAST(episode AS UNSIGNED) ASC
+--------+--------+---------+--------+
| id | season | episode | order |
+--------+--------+---------+--------+
| 153914 | 1 | 1 | 1 |
| 153916 | 1 | 2 | 2 |
| 153915 | 1 | 3 | 3 |
| 153919 | 1 | 3 | 4 |
| 153921 | 1 | 3 | 5 |
| 153920 | 1 | 4 | 6 |
+--------+--------+---------+--------+
But when I use the same query as the subquery of UPDATE statement it doesn't sort the same way and set different order values.
UPDATE `shows` f
JOIN
(
SELECT id, (#row:=#row+1) as rowOrder
FROM `shows` as Fl, (select #row:=0) as rc
WHERE Fl.`show_id` = 14670
ORDER BY Fl.season ASC, Fl.episode ASC
) t
ON t.id = f.id
SET f.order = t.rowOrder
mysql> SELECT id, season, episode, order FROM `shows` WHERE `show_id`=14670;
+--------+--------+---------+--------+
| id | season | episode | order |
+--------+--------+---------+--------+
| 153914 | 1 | 1 | 1 |
| 153915 | 1 | 3 | 2 |
| 153916 | 1 | 2 | 3 |
| 153919 | 1 | 3 | 4 |
| 153920 | 1 | 4 | 5 |
| 153921 | 1 | 3 | 6 |
+--------+--------+---------+--------+
Please, explain to me why it happens and how to solve it.
MySQL version:
>mysql --version
mysql Ver 14.14 Distrib 5.7.18, for Linux (x86_64) using EditLine wrapper
Hmmm. It would appear that the order by is not affecting the variable. I wonder if this has changed in recent versions of MySQL. It certainly used to work.
In any case, you can fix it by using a subquery:
UPDATE shows s JOIN
(SELECT id, (#row:=#row+1) as rowOrder
FROM (SELECT id, sea
FROM shows s2
WHERE s2.show_id = 14670
ORDER BY s2.season ASC, s2.episode ASC
) s2 CROSS JOIN
(SELECT #row := 0) as rc
) s3
ON s.id = s3.id
SET s.order = s3.rowOrder;

mysql: how to save ORDER BY after LEFT JOIN without reorder?

I've two table:
1) profiles
+----+---------+
| id | name |
+----+---------+
| 1 | WILLIAM |
| 2 | JOHN |
| 3 | ROBERT |
| 4 | MICHAEL |
| 5 | JAMES |
| 6 | DAVID |
| 7 | RICHARD |
| 8 | CHARLES |
| 9 | JOSEPH |
| 10 | THOMAS |
+----+---------+
2) request_for_friendship
+----+---------+-------+
| id | from_id | to_id |
+----+---------+-------+
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 8 |
| 5 | 4 | 1 |
| 6 | 9 | 1 |
+----+---------+-------+
I need to get all profiles with some sorting and join it with request_for_friendship
For example, get all users with some sorting:
mysql> SELECT *
-> FROM profiles
-> ORDER BY name ASC;
+----+---------+
| id | name |
+----+---------+
| 8 | CHARLES |
| 6 | DAVID |
| 5 | JAMES |
| 2 | JOHN |
| 9 | JOSEPH |
| 4 | MICHAEL |
| 7 | RICHARD |
| 3 | ROBERT |
| 10 | THOMAS |
| 1 | WILLIAM | <-- WILLIAM IS LAST!
+----+---------+
Everything looks good, sorting is present. After that I join with request_for_friendship and my sotring will breaks:
mysql> SELECT * FROM
-> (
-> SELECT *
-> FROM profiles
-> ORDER BY name ASC
-> ) as users
-> LEFT JOIN request_for_friendship
-> AS request_for_friendship_copy
-> ON
-> (
-> request_for_friendship_copy.from_id = 1
-> AND
-> request_for_friendship_copy.to_id = users.id
-> )
-> OR
-> (
-> request_for_friendship_copy.from_id = users.id
-> AND
-> request_for_friendship_copy.to_id = 1
-> );
+----+---------+------+---------+-------+
| id | name | id | from_id | to_id |
+----+---------+------+---------+-------+
| 2 | JOHN | 1 | 1 | 2 |
| 3 | ROBERT | 2 | 1 | 3 |
| 8 | CHARLES | 3 | 1 | 8 |
| 4 | MICHAEL | 5 | 4 | 1 |
| 9 | JOSEPH | 6 | 9 | 1 |
| 1 | WILLIAM | NULL | NULL | NULL | <-- WILLIAM IN THE MIDDLE!
| 5 | JAMES | NULL | NULL | NULL |
| 6 | DAVID | NULL | NULL | NULL |
| 7 | RICHARD | NULL | NULL | NULL |
| 10 | THOMAS | NULL | NULL | NULL |
+----+---------+------+---------+-------+
How to JOIN LEFT with original sorting saving?
I can't sort result after JOIN LEFT besause when I do ORDER BY before JOIN it takes ~0.02s in my db (~1 000 000 users) but when I do ORDER BY after JOIN it takes ~3.2s, it's very big time :(
Demo: rextester.com/DLLM29415
Demo: http://sqlfiddle.com/#!9/167792/1
In sqlfiddle order is saved! But how? MySQL 5.6 saved order?
(Explaining the loss of ORDER BY)
The SQL standard essentially says that a subquery is an unordered set of rows. This implies that the Optimizer is free to ignore the ORDER BY in the 'derived' table: FROM ( SELECT ... ORDER BY ). In "recent" versions of MySQL and MariaDB, such ORDER BYs are being dropped. There are other cases where ORDER BY is ignored.
In some situations (not sure about this one), adding a LIMIT 99999999 (big number) after the ORDER BY tricks the Optimizer into doing the ORDER BY. However, it is still free to ignore the "order" later.
A general rule for MySQL: Avoid subqueries. (There are cases where subqueries are faster, but not yours.)
A strong rule: You must have an ORDER BY on the outermost if you want the results to be sorted.
If you had added LIMIT 3 to the derived table in your first query, you would get only CHARLES, DAVID, JAMES, but not necessarily in that order. That is, you would need two ORDER BYs - one in the derived table, one at the very end.
SELECT *
FROM profiles p
LEFT
JOIN request_for_friendship r
ON (r.from_id = p.id AND r.to_id = 1)
OR (r.from_id = 1 AND r.to_id = p.id)
ORDER
BY name;
+----+---------+------+---------+-------+
| id | name | id | from_id | to_id |
+----+---------+------+---------+-------+
| 8 | CHARLES | 3 | 1 | 8 |
| 6 | DAVID | NULL | NULL | NULL |
| 5 | JAMES | NULL | NULL | NULL |
| 2 | JOHN | 1 | 1 | 2 |
| 9 | JOSEPH | 6 | 9 | 1 |
| 4 | MICHAEL | 5 | 4 | 1 |
| 7 | RICHARD | NULL | NULL | NULL |
| 3 | ROBERT | 2 | 1 | 3 |
| 10 | THOMAS | NULL | NULL | NULL |
| 1 | WILLIAM | NULL | NULL | NULL |
+----+---------+------+---------+-------+
10 rows in set (0.02 sec)
mysql>
Try this:
SELECT
a.name as `from_name`,
b.name as `to_name`,
c.from_id,
c.to_id
FROM profiles a
LEFT JOIN request_for_friendship c
ON a.id = c.from_id
LEFT JOIN profiles b
ON c.to_id = b.id
GROUP BY a.name,b.name
ORDER BY a.name,b.name;
Or, if you want one row per "from" name:
SELECT
a.name as `from_name`,
IFNULL(GROUP_CONCAT(b.name),'-none-') as `to_name`,
IFNULL(c.from_id,'-none-') as `from_id`,
IFNULL(GROUP_CONCAT(c.to_id),'-none-') as `to_id`
FROM profiles a
LEFT JOIN request_for_friendship c
ON a.id = c.from_id
LEFT JOIN profiles b
ON c.to_id = b.id
GROUP BY a.name
ORDER BY a.name,b.name
I know this question is a couple of years old, but I didn't find this possible solution already offered. This is the solution that worked best for me to keep the subquery results in the correct order.
Consider adding a "row_number" to your subquery. Then use ORDER BY on row_number.
This explains how to add the row_number:
select increment counter in mysql
In my case, I have an unknown number of possible rows in a hierarchical recursive query that I need to keep the order results of the subquery to remain the same in the outer query.
This is my query:
SELECT l.row_number, l.userid, l.child, p.id, p.username
FROM (
SELECT #rownum := #rownum + 1 AS row_number, u.parent AS userid, _id AS child
FROM (
SELECT #r AS _id, (SELECT #r := parent FROM new_clean WHERE userid = _id) AS parent
FROM (SELECT #r := ?) AS vars, new_clean h
WHERE #r <> 0
) u
CROSS JOIN (SELECT #rownum := 0) r
WHERE u.parent <> 0
) l
LEFT JOIN profile p ON p.userid = l.userid
ORDER BY row_number

SQL sorting with multiple criteria

I have two tables.
Tab1:
+------------+
| id | title |
+------------+
| 1 | B |
| 2 | C |
| 3 | A |
| 4 | A |
| 5 | A |
| 6 | A |
| ... |
+------------+
Tab2:
+-------------------------------------------+
| id | item_id | item_key | item_value |
+-------------------------------------------+
| 1 | 1 | value | $4 |
| 2 | 1 | url | http://h.com/ |
| 3 | 2 | value | $5 |
| 4 | 3 | url | http://i.com/ |
| 5 | 3 | value | $1 |
| 6 | 3 | url | http://y.com/ |
| 7 | 4 | value | $2 |
| 8 | 4 | url | http://z.com/ |
| 9 | 5 | value | $1 |
| 10 | 5 | url | http://123.com/ |
| ... |
+-------------------------------------------+
item_id is a foreign key from tab1.
How do I make it so I get a table of ids from Tab1 in order according to criteria from both tables. The criteria are the following:
Order ASC by title. If title is the same,
Order DESC by value. If both title and value is the same,
Prioritize items who's 'url' key contains '123.com'.
The resulting table with the ordered results would be:
+------------+
| id | title |
+------------+
| 4 | A |
| 5 | A |
| 3 | A |
| 6 | A |
| 1 | B |
| 2 | C |
| ... |
+------------+
The results should include items that don't have the one, both, or none of the fields from Tab2 set.
As far as I understand, a simple join will do it. You'll have to join Tab2 twice, since you want to order by values from both rows.
SELECT Tab1.id, Tab1.title
FROM Tab1
JOIN Tab2 t2_val ON t2_val.item_id = Tab1.id AND t2_val.item_key='value'
JOIN Tab2 t2_url ON t2_url.item_id = Tab1.id AND t2_url.item_key='url'
ORDER BY title,
t2_val.item_value DESC,
t2_url.item_value LIKE '%123.com%' DESC
An SQLfiddle to test with.
A little complicated, because when you do the join you will get multiple rows. Here is an approach that aggregates tab2 before doing the join:
select t1.*
from Tab1 t1 left outer join
(select id,
max(case when item_key = 'value' then item_value end) as value,
max(case when item_key = 'url' then item_value end) as url
from Tab2 t2
group by id
) t2
on t1.id = t2.id
order by t1.title, t2.value desc,
(t2.url like '%123.com%') desc;