Извлечение верхних 1 и 2 записей из каждой группы из таблицы

#sql-server-2008 #tsql

#sql-server-2008 #tsql

Вопрос:

У меня есть запрос, который должен получить первый и второй по величине артикул в списке пожеланий каждого участника. Приведенный ниже запрос работает, но это занимает слишком много времени, потому что существует около 9 миллионов пользователей, и у каждого пользователя есть около 10 элементов списка желаний, поэтому вы можете видеть, что приведенный ниже запрос никогда не завершится.

 SELECT MAX(CASE WHEN wl.rank = 1 THEN wl.SKU ELSE NULL END) AS [highestSku],
       MAX(CASE WHEN wl.rank = 2 THEN wl.SKU ELSE NULL END) AS [secondHighestSku],
FROM Member m
     LEFT JOIN (SELECT *
                 FROM (SELECT DENSE_RANK() OVER (PARTITION BY wl.MemberID ORDER BY wli.Price DESC) AS rank, wl.MemberID, wli.SKU
                       FROM WishListItem wli
                            INNER JOIN WishList wl ON wli.WishListID = wl.ID) T1) w ON w.MemberID = m.ID
  

Мой вопрос в том, есть ли лучший способ получить верхние первую и вторую записи для каждого пользователя? Если нет, есть ли способ оптимизировать этот запрос? В идеале, если я смогу восстановить количество уровней, извлеченных из запроса ранжирования (тот, у которого есть DENSE_RANK ()), это мне поможет. Я хотел сделать что-то вроде WHERE DENDS_RANK() <= 2, но это невозможно, и выполнение этого вне скобок противоречит цели soultion.

Кроме того, это всего лишь часть запроса. На самом деле у меня есть еще больше левых соединений в большем количестве таблиц, в которых столько же элементов, и мне нужно получить верхние 1 и 2 записи для каждого пользователя.

И это должно быть сделано в одном запросе или как можно больше в одном, потому что я добавляю его в таблицу данных. Я также могу уменьшить количество записей, т.Е.. ВЕРХНИЕ 1000 и разбейте запрос, но мне нужно будет иметь возможность продолжить с того места, где я остановился … кроме того, я попробовал TOP 1000, и через 10 минут я отменил запрос, потому что мне нужно извлечь все 9 миллионов записей.

Комментарии:

1. есть ли у вас индексы в какой-либо из таблиц?

2. да, таблицы полностью проиндексированы. Я заметил, что я даже не могу выполнить прямой SELECT * FROM member без того, чтобы SQL не исчерпал память, потому что записей слишком много.

3. для начала попробуйте обновить статистику индекса

4. Я не уверен, что понимаю ваш дизайн, почему цена указана в таблице списка пожеланий, а артикул — в WLI? Я бы ожидал, что Member <- WishList <- WishlistItem -> Product или что-то в этом роде. Вы уверены, что это правильно?

5. исправлено. Я переписал код, чтобы извлечь его из большого запроса.

Ответ №1:

Я бы взял относительно небольшое подмножество данных, поместил его в табличную переменную и запустил запрос к ней вместо основных (и, вероятно, очень «загруженных») таблиц:

 DECLARE @Member TABLE
(
    ID int IDENTITY (1, 1) PRIMARY KEY NOT NULL,
    -- add necessary columns to this definition.
)

INSERT INTO @Member (field1, field2...)
SELECT field1, field2  -- etc. 
FROM YourTables
WHERE SomeCriteria = Whatever
  

Убедитесь, что предложение WHERE определяет более узкое подмножество данных, чем ваши рабочие таблицы. Если производительность все еще страдает, вы могли бы создать табличные переменные для других объединяемых таблиц, а затем использовать их в окончательном запросе.