Подход к выбору топ-элемента, соответствующего критерию - PullRequest
0 голосов
/ 28 мая 2010

РЕДАКТИРОВАТЬ: мои извинения, это была проблема MSSQL2008.

У меня проблема с SQL, с которой я постоянно сталкивался и обычно просто решал с помощью вложенного запроса. Я надеюсь, что кто-то может предложить более элегантное решение.

Часто случается, что мне нужно выбрать набор результатов для пользователя, обусловленный тем, что он является самым последним или наиболее значительным или каким-либо другим.

Например: создан их полный список страниц, но мне нужно только самое последнее имя, которое они применили к странице. Случается, что база данных содержит много записей для каждой страницы, и желательна только самая последняя.

Я использовал вложенный выбор, например:

SELECT pg.customName, pg.id
FROM (
     select id, max(createdAt) as mostRecent
     from pages
     where userId = @UserId
     GROUP BY id
) as MostRecentPages
JOIN pages pg
ON pg.id = MostRecentPages.id
AND pg.createdAt = MostRecentPages.mostRecent

Есть ли лучший синтаксис для выполнения этого выбора?

Ответы [ 6 ]

2 голосов
/ 31 мая 2010

выглядит так, как ты хочешь

SELECT id, customname
FROM (SELECT id, customname,
             row_number() OVER(PARTITION BY id ORDER BY createdat DESC) as pos
      FROM pages
      WHERE pages.userid = @UserId
     ) x
WHERE x.row_number = 1

(я предполагаю, что вы используете SQL Server из параметра @UserId. Row_number () будет работать для SQL Server 2005, и вышеописанное должно также работать для Oracle, Postgresql 8.4 ...)

Это выберет все страницы по идентификатору пользователя и определит, какая из них является самой последней с использованием сортировки. Альтернативой может быть что-то вроде:

SELECT id, (SELECT TOP 1 customname
            FROM pages pages_inner
            WHERE pages_inner.id = pages_outer.id
            ORDER BY pages_inner.createdat DESC) as customname
FROM (SELECT DISTINCT id FROM pages WHERE pages.userid = @UserId) pages_inner

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

1 голос
/ 31 мая 2010

Для какой базы данных (включая версию)? То, что вы опубликовали, может быть MySQL, SQL Server или Sybase ...

Использование:

SELECT pg.customName, 
       pg.id
  FROM PAGES pg
  JOIN (SELECT t.userid, 
               MAX(t.createdAt) as mostRecent
          FROM PAGES t
      GROUP BY t.userid) x ON x.id = pg.id
                          AND x.mostRecent = pg.createdAt
                          AND x.userid = @UserId 

Это лучший подход для переносимого запроса, при условии, что ссылки на столбцы верны. Но есть альтернативы для ограничения набора данных - SQL Server использует TOP, MySQL / Postgre / SQLite использует LIMIT, Oracle использует ROWNUM.

Что лучше всего зависит от ваших данных и того, как соответствующий оптимизатор их видит, и от ваших потребностей (портативные или нет). Проверьте план объяснения для соответствующей базы данных, чтобы увидеть, насколько эффективен запрос.

1 голос
/ 31 мая 2010

Я не уверен в том, что лучше, но вы можете попробовать другой синтаксис:

SELECT pg.customName, pg.id
FROM pages pg
WHERE userId = @UserId  
AND NOT EXISTS 
        (
         SELECT * FROM pages pg2
         WHERE pg2.UserId = pg.UserId
         AND pg2.id = pg.id
         AND pg2.createdAt > pg.createdAt
         )

Другой альтернативой будет ВНЕШНЕЕ СОЕДИНЕНИЕ, как в ответе Билла Карвина здесь Как получить все поля строки с помощью функции SQL MAX?

0 голосов
/ 28 мая 2010

Предполагая, что SQL Server и ваша таблица страниц выглядят так:

CREATE TABLE Pages (
    Id int IDENTITY(1, 1) PRIMARY KEY
    , CustomName nvarchar(20) NOT NULL
    , CreatedAt datetime NOT NULL DEFAULT GETDATE()
    , UserId int references Users(Id)
)

Я бы сделал следующее:

select TOP 1 p.Id as PageId
        , p.CustomName
    from Pages p
    where p.UserId = @UserId
    order by p.Created desc

Или даже:

select TOP 1 p.Id as PageId
        , p.CustomName
        , MAX(p.CreatedAt) DateTimeCreated
    from Pages p
    where p.UserId = @UserId
    group by p.Id
        , p.CustomName

Надеюсь, это поможет! ( Если нет, пожалуйста, предоставьте дополнительную информацию, чтобы мы могли помочь вам лучше )

0 голосов
/ 28 мая 2010

Вы используете Oracle? Попробуйте посмотреть, подойдет ли вам этот запрос, использующий аналитическую функцию. (У меня сейчас нет доступа к БД, поэтому я не могу проверить себя.)

SELECT DISTINCT pg.id, 
FIRST_VALUE(pg.customName) OVER (PARTITION BY pg.id ORDER BY pg.createdAt DESC) AS customName
FROM pages pg
0 голосов
/ 28 мая 2010

Я не знаю, как выглядит твой стол

Select top 1  pg.createdAt
             ,pg.customName
             ,pg.id
from          table pg
where         pg.UserId = @UserId
order by      pg.createdAt Desc

Мне нужно больше информации о ваших таблицах

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...