2010-02-17 2 views
31

Я пишу sql-запрос, чтобы получить сообщение и только последний комментарий этого сообщения (если существует). Но я не могу найти способ ограничить только 1 строку для правого столбца в левом соединении.TSQL left join и только последняя строка справа

Вот пример этого запроса.

SELECT post.id, post.title,comment.id,comment.message 
from post 
left outer join comment 
on post.id=comment.post_id 

Если пост имеет 3 комментария я получаю 3 строку с этой должностью, но я хочу только 1 строку с последним комментарием (упорядоченного по дате).

Может ли кто-нибудь помочь мне с этим вопросом?

ответ

51
SELECT post.id, post.title, comment.id, comment.message 
FROM post 
OUTER APPLY 
     (
     SELECT TOP 1 * 
     FROM comment с 
     WHERE c.post_id = post.id 
     ORDER BY 
       date DESC 
     ) comment 

или

SELECT * 
FROM (
     SELECT post.id, post.title, comment.id, comment.message, 
       ROW_NUMBER() OVER (PARTITION BY post.id ORDER BY comment.date DESC) AS rn 
     FROM post 
     LEFT JOIN 
       comment 
     ON  comment.post_id = post.id 
     ) q 
WHERE rn = 1 

Бывший является более эффективным для нескольких сообщений с большим количеством замечаний в каждом; последний более эффективен для многих должностей с несколькими комментариями в каждом.

+0

Спасибо за ответ. Я использую следующий код. ВЫБРАТЬ post.id, post.title, c.id в comment_id, c.message из почтового левого внешнего соединения (выберите comment.id, comment.post_id, comment.message, row_number() OVER (PARTITION BY комментарий .post_id ORDER BY comment.date DESC) AS rn из комментария) c на post.id = c.post_id где c.rn = 1 или c.rn имеет значение null – barbarian

+0

Оператор APPLY сделал трюк для меня. Я делал соединение «1-ко-многим», но мне нужно было уменьшить совпадения правой стороны до только самого последнего. Благодаря! –

+0

Интересно поставить эти два вместе, чтобы показать оценочный план запроса, опция «apply» была равна 99% от стоимости запроса: o –

14

подзапросов:

SELECT p.id, p.title, c.id, c.message 
FROM post p 
LEFT join comment c 
ON c.post_id = p.id AND c.id = 
       (SELECT MAX(c.id) FROM comment c2 WHERE c2.post_id = p.id) 
+0

Если я не пропущу что-то, это должно быть намного быстрее (например, 10 раз) для выполнения, чем принятый ответ, судя по моему плану выполнения. – kape123

+0

Также мне кажется, что это намного эффективнее, чем использование громоздких подзапросов, используемых в принятом ответе. Есть связанный ответ в http://stackoverflow.com/questions/4692419/how-do-i-join-the-first-row-of-a-subquery. Он использует решение TOP 1/ORDER BY вместо MAX в подзапросе. –

+0

Я считаю, что это более короткий и лучший ответ. – MurifoX

3

Вы хотите присоединиться к югу от запроса, который возвращает последний комментарий на пост. Например:

select post.id, post.title. lastpostid, lastcommentmessage 
from post 
inner join 
(
    select post.id as lastpostid, max(comment.id) as lastcommentmessage 
    from post 
    inner join comment on commment.post_id = post.id 
    group by post.id 
) lastcomment 
    on lastpostid = post.id 
1

Пара вариантов ....

Один из способов сделать JOIN на:

SELECT TOP 1 comment.message FROM comment ORDER BY comment.id DESC 

(обратите внимание, я предполагаю, что comment.id это поле идентификации)

+0

Что делать, если поле Identity имеет отрицательный прирост? –

+1

Вы когда-нибудь сталкивались с такой вещью? –

1

какая версия SQL Server? Если у вас есть функция Row_Number(), вы можете сортировать свои комментарии тем, что для вас означает «первым», а затем просто добавить предложение «where RN = 1». У вас нет удобного примера или правильного синтаксиса с моей головы, но у вас есть тонны запросов, которые делают именно это. Другие должности - все это в тысячах способов, которыми вы могли бы это сделать.

Я бы сказал, профиль и посмотрим, какой из них лучше всего подходит для вас.

+0

Я использую SQL SERVER 2005 – barbarian

0

Вы не указали название своего поля даты, поэтому я заполнил его номером [DateCreated]. Это по существу то же самое, что и сообщение AGoodDisplayName выше, но с использованием поля даты вместо того, чтобы полагаться на упорядочение столбцов идентификатора.

SELECT post.id, post.title, comment.id, comment.message 
FROM post p 
LEFT OUTER JOIN comment 
ON comment.id = (
    SELECT TOP 1 id 
    FROM comment 
    WHERE p.id = post_id 
    ORDER BY [DateCreated] ASC 
) 
Смежные вопросы