MySQL converting subquery to join - mysql

Is it possible to convert this subquery to join?
SELECT `news`.`newsId`,
(SELECT `comments`.`text`
FROM `comments`
WHERE `comments`.`newsId` = `news`.`newsId`
order by `comments`.`date` desc
limit 1)
FROM `news` , `comments`
where `news`.`newsId` = `comments`.`newsId`
GROUP BY `news`.`newsId`
order by news.date desc;

I assume newsId is unique.
SELECT `news`.`newsId`,
`comments`.`text`
FROM `news`
CROSS APPLY (SELECT `comments`.`text`
FROM `comments`
WHERE `comments`.`newsId` = `news`.`newsId`
order by `comments`.`date` desc
limit 1) cm
order by news.date desc;

I think that what you're trying to do is:
SELECT n.newsId FROM news n
INNER JOIN comments c ON c.newsId = n.newsId
ORDER BY c.date DESC, n.date
LIMIT 1
The GROUP BY is not necessary as you are not using any aggregation function. You can have unique entries with DISTINCT

Related

Solution to MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'

For some reason MYSQL doesn't support LIMIT inside a subquery:
SELECT m.*
FROM `my_table` m
WHERE m.`id` IN (
SELECT o.`id`
FROM (SELECT DISTINCT i.`id`, i.`label`, i.`client`, i.`place`
FROM `my_table` i
ORDER BY i.`label`, -i.`client` DESC, -i.`place` DESC) o
WHERE m.`label` = o.`label` LIMIT 1
);
I've tried using join from this link: INNER JOIN INSTEAD OF IN(LIMIT error) but not succeeded. Has anyone any clues for it? Thanks.
Since the subquery returns only 1 row with 1 column there is no need for IN.
You can use =:
SELECT m.*
FROM `my_table` m
WHERE m.`id` = (
SELECT o.`id`
FROM (
SELECT DISTINCT i.`id`, i.`label`, i.`client`, i.`place`
FROM `my_table` i
ORDER BY i.`label`, -i.`client` DESC, -i.`place` DESC) o
WHERE m.`label` = o.`label` LIMIT 1
);
But as it is written, your query uses LIMIT without ORDER BY (you do use ORDER BY in the inner subquery where it is useless).
Do you mean to do something like this:
SELECT m.*
FROM `my_table` m
WHERE m.`id` = (
SELECT o.`id`
FROM (
SELECT DISTINCT i.`id`, i.`label`, i.`client`, i.`place`
FROM `my_table` i
) o
WHERE m.`label` = o.`label`
ORDER BY o.`label`, -o.`client` DESC, -o.`place` DESC
LIMIT 1
);
Also ordering by the negative value of a column descending is equivalent to ordering just ascending, so the ORDER BY clause can be simplified to:
ORDER BY o.`label`, o.`client`, o.`place`

sql join Query the database ,There is no desired result,

my demand is:from database query newest data,according to time field as a standard,query alarmtime this field ,but alarmtime < time
executive sql:
SELECT
c.entryTime AS alarmtime ,
a.time AS time
FROM (
SELECT
t.DB33,
MAX( t.Time ) AS time,
t.Stream,
t.Coil,
t.`View`
FROM (
SELECT DB33, Time, Stream, Coil, `View`
FROM running_check
ORDER BY id DESC
LIMIT 1000 ) as t
GROUP BY t.DB33 ) AS a
LEFT JOIN (
select cameraID,cameraName
from monitor_link_info) as b ON a.db33 = b.cameraID
LEFT JOIN (
SELECT entryTime, cameraID
FROM result_video_v2
ORDER BY id DESC
limit 1000 ) AS c ON a.db33 = c.cameraId
GROUP BY a.db33, b.cameraName
ORDER BY a.time DESC, alarmtime DESC
execute result is unamiable,please look at image
now time column not what i want,Because he didn't change.
SELECT
c.entryTime AS alarmtime ,
a.time AS TIME
FROM
(
SELECT DB33, MAX(TIME) TIME, Stream, Coil, `View`
FROM running_check
ORDER BY id DESC
GROUP BY t.DB33
LIMIT 1000
) AS a
LEFT JOIN
(
SELECT cameraID,cameraName
FROM monitor_link_info
GROUP BY cameraID
LIMIT 1000
) AS b ON (a.db33 = b.cameraID)
LEFT JOIN (
SELECT entryTime, cameraID
FROM result_video_v2
GROUP BY cameraID
ORDER BY id DESC
LIMIT 1000
) AS c ON (a.db33 = c.cameraID)
GROUP BY a.db33, b.cameraName
ORDER BY a.time DESC, c.entryTime DESC

ORDER BY and LIMIT in GROUP BY

I'm trying to get a subset of records in a GROUP BY, I've seen a lot of crazy solutions out there, but they just seem too complicated, is there any more efficient way to do this.
SELECT user_id, GROUP_CONCAT(item_id ORDER BY `timestamp`) AS items
FROM wb_user_book_current_item GROUP BY user_id
So this will return me all the current items for all users which is okay so far. But I only want the ten most recent items. Adding ORDER BY to the GROUP_CONCAT helps, but it still doesn't give me the last ten records.
EDIT
If I do something like this and hard code the user_id then I can get the results I want for that one user, problem is combining it so that I don't need to hard code the user_id and can for instance just get ALL users last ten items
SELECT GROUP_CONCAT(cp2.item_id) AS items
FROM (SELECT cp.user_id, cp.item_id
FROM wb_user_book_current_item cp
WHERE cp.user_id=1 ORDER BY cp.`timestamp`
LIMIT 10) AS cp2
GROUP BY cp2.user_id
This is a difficult problem, but how about this:
SELECT user_id, GROUP_CONCAT(item_id ORDER BY `timestamp`) AS items
FROM wb_user_book_current_item T
WHERE NOT EXISTS
(
SELECT 1
FROM wb_user_book_current_item T2
WHERE T2.user_id = T.user_id
ORDER BY T2.`timestamp` DESC
LIMIT 10,1
)
OR T.`timestamp` > (
SELECT T2.`timestamp`
FROM wb_user_book_current_item T2
WHERE T2.user_id = T.user_id
ORDER BY T2.`timestamp` DESC
LIMIT 10,1
)
GROUP BY user_id
This of course assumes you won't have two rows with the same timestamp for the same user.
If your timestamp field is always a positive integer, you can also replace the NOT EXISTS...OR with a COALESCE:
SELECT user_id, GROUP_CONCAT(item_id ORDER BY `timestamp`) AS items
FROM wb_user_book_current_item T
WHERE T.`timestamp` > COALESCE((
SELECT T2.`timestamp`
FROM wb_user_book_current_item T2
WHERE T2.user_id = T.user_id
ORDER BY T2.`timestamp` DESC
LIMIT 10,1
), 0)
GROUP BY user_id
Original answer, but apparently MySQL doesn't understand how to do this properly and complains the subselect returns multiple rows. Of course we want multiple rows; it's a GROUP_CONCAT. Grr.
Unfortunately, I think there's no real way around using a subquery:
SELECT T.user_id,
GROUP_CONCAT((SELECT T2.item_id
FROM wb_user_book_current_item T2
WHERE T2.user_id = T.user_id
ORDER BY T2.`timestamp`
LIMIT 10)) AS items
FROM wb_user_book_current_item T
GROUP BY user_id
Otherwise, adding LIMIT anywhere else will either limit the number of groups, or limit from the total recordset over the table (and not the group) - neither of which are what you are trying to achieve.
So came across a nice solution here that works pretty well.
http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
It's something like this put all together:
SET #num := 0, #user_id := '';
SELECT cp2.user_id, CONCAT(cp2.item_id) AS items
FROM (
SELECT cp.user_id, cp.item_id,
#num := IF(#user_id = cp.user_id, #num + 1, 1) AS row_number,
#user_id := cp.user_id AS dummy
FROM wb_user_curent_item AS cp
ORDER BY cp.user_id ASC, cp.`timestamp` DESC
) AS cp2 WHERE cp2.row_number <= 10
GROUP BY cp2.user_id
So basically it just uses the num increment to limit the records rather than using LIMIT
SELECT
i.user_id,
GROUP_CONCAT(i.item_id ORDER BY i.timestamp) AS items
FROM
( SELECT DISTINCT user_id
FROM wb_user_book_current_item
) AS du
JOIN
wb_user_book_current_item AS i
ON i.user_id = du.user_id
AND i.timestamp <= COALESCE(
( SELECT i2.item_id
FROM wb_user_book_current_item AS i2
WHERE i2.user_id = du.user_id
ORDER BY i2.timestamp ASC
LIMIT 1 OFFSET 9
)
, '2038-01-19 03:14:07')
GROUP BY
i.user_id ;
An index on (user_id, timestamp, item_id) will help efficiency.
Try this:
SELECT
user_id,
GROUP_CONCAT(item_id ORDER BY `timestamp`) AS items
FROM wb_user_book_current_item
GROUP BY user_id
LIMIT 0, 10
UPDATE: I didn't notice the GROUP_CONCAT so you will have to use sub query in conunction with LIMIT
use LIMIT
SELECT column_name(s)
FROM table_name
LIMIT number

SQL - Should I use a join?

I have the following sample query (MySQL):
SELECT * FROM `action`
WHERE `customer_id` IN
(SELECT `id` FROM `customer` WHERE `status`=1)
ORDER BY
action.date ASC
LIMIT 0, 10
I need to be able to ORDER BY the customer.status field. Do I accomplish this with a join?
status is a field on the customer table.
Edited Query:
SELECT * FROM `action`
ORDER BY
action.date ASC
LIMIT 0, 10
IMPORTANT!
I am parsing the return data via PHP. After running the revised query:
SELECT * FROM `action` a INNER JOIN `customer` c ON a.customer_id = c.id ORDER BY a.form_id ASC LIMIT 0, 10
My PHP code breaks...
This post helped me out.
My revised query looks like this:
SELECT
*, a.id AS lead_id, c.id AS customer_id
FROM
`action` a
INNER JOIN
`customer` c ON a.customer_id = c.id
ORDER BY c.status DESC
Thanks everyone!
UPDATE
Because I have some customer records without an action record, an INNER JOIN was not returning all relevant records. I use a JOIN now, and all results come back as expected.
SELECT *
FROM `action` a
INNER JOIN `customer` c on a.`customer_id` = c.`id`
WHERE c.`status` in (1, 4, 7, 8)
ORDER BY a.date, c.status
LIMIT 0, 10
You can do either:
SELECT * FROM `action` a
INNER JOIN `customer` c on c.id = a.customer_id
WHERE c.status = 1
ORDER BY a.date ASC, c.status
LIMIT 0, 10
Or:
SELECT * FROM `action` a
INNER JOIN `customer` c on (c.id = a.customer_id and c.status = 1)
ORDER BY a.date ASC, c.status
LIMIT 0, 10
EDIT:
It's probably worth pointing out there's no sense in ordering by c.status, as it will always be 1. However, I put that in there since it was brought up by others as well as mentioned in the OP. I would think it could be removed from both queries.
Yes, you may accomplish it with a join and may be faster:
SELECT * FROM `action` a join customer c on c.id=a.customer_id
where c.status=1
ORDER BY
a.date ASC
LIMIT 0, 10
Also, consider not using * and instead list the columns that you need. It will improve performance in case you need to select less than all columns and you won't get surprises in the future if the table changes.
SELECT * FROM `action` a
JOIN `customer` c on a.customer_id=c.id
WHERE c.status=1 order by a.date, c.status ASC
LIMIT 0, 10

Join with multiple queries in SQL

I have this query already:
SELECT * FROM (SELECT * FROM `prefix_messages`
WHERE `category_id`=4
ORDER BY `id` DESC LIMIT 30) ilv
ORDER BY `id` ASC
How to use join query to add data from users table if in prefix_messages I have user_id column?
Thanx!
Try this:
SELECT * FROM (SELECT pm.ID as prefixID, * FROM prefix_messages as pm
INNER JOIN users as u
ON pm.User_id = u.UserId
WHERE pm.category_id=4
ORDER BY pm.id DESC LIMIT 30)
ORDER BY prefixID ASC
I assume that you are only doing the subselect because you want to reverse the order after you get the top 30?