Проблема с использованием ROW_NUMBER () для случайного получения записей (SQL Server 2005) - PullRequest
2 голосов
/ 01 марта 2010

Я хочу получить 1000 записей из таблицы случайным образом, поэтому я использую:

SELECT top 1000 
       mycol1
     , mycol2
     , ROW_NUMBER() OVER (ORDER BY NEWID()) rn
FROM mytable

Однако я не хочу видеть rn в моем наборе результатов, поэтому я делаю:

SELECT mycol1
     , mycol2
FROM (
    SELECT top 1000 
           mycol1
         , mycol2
         , ROW_NUMBER() OVER (ORDER BY NEWID()) rn
    FROM mytable
) a

Когда я это делаю, результаты больше не появляются случайно. Они приходят, как будто я только что сказал топ 10000 без рандомизации с использованием row_number ().

Когда я меняю запрос на

SELECT mycol1
     , mycol2
     , rn
FROM (
    SELECT top 1000 
           mycol1
         , mycol2
         , ROW_NUMBER() OVER (ORDER BY NEWID()) rn
    FROM mytable
) a

они снова случайны.

Я полагаю, что sql server выполняет какую-то оптимизацию, говоря: "Эй, этому парню не нужен столбец rn, так что просто проигнорируйте его". Но это приводит к неожиданному поведению в этом случае. Есть ли способ избежать этого?

PS: я использую трюк ROW_NUMBER (), потому что mytable имеет 10 млн. строки и

SELECT top 10000 *
FROM mytable
ORDER BY NEWID()

работает вечно, тогда как с ROW_NUMBER () это занимает всего до 30 секунд.

Ответы [ 3 ]

2 голосов
/ 01 марта 2010

Вы также можете попробовать использовать поле rn в каком-то маленьком предложении where, например

ГДЕ rn> 0 в вашем внешнем запросе, что, возможно, заставит компилятор пропустить поле RN.

Кроме того, я думаю, что ваш общий запрос будет проблемой, если вы хотите случайным образом выбрать все миллионы записей. Это будет захватывать только блок записей «первый диск», который, хотя и не гарантированно будет тем же, чаще всего будет одним и тем же 10000.

Я бы предложил создать набор из 10 000 случайных чисел между MIN (PrimaryKey) и MAX (PrimaryKey) и затем выполнить WHERE PrimaryKey IN (...) или аналогичный

1 голос
/ 01 марта 2010

Добавьте что-то вроде Где rn не равен NULL к внешнему запросу, чтобы rn было включено в план запроса и не оптимизировано

0 голосов
/ 08 ноября 2012

Я боролся с этой же проблемой. Я решил это с CROSS APPLY и TOP. Помня, что CROSS APPLY тянет мою внешнюю таблицу в область видимости производной таблицы, я знал, что должен быть способ сделать это.

Следующий код приводит к добавлению 3 (*) случайных связанных продуктов в зависимости от производителя.

INSERT INTO     ProductGroup (
                    ParentId,
                    ChildId
                )
SELECT          DISTINCT
                P.ProductId,
                CandidateInner.ChildId
FROM            ProductRelated PR 
JOIN            Product P
ON              PR.ChildId = P.ProductId
CROSS APPLY     
                (   
                    SELECT      DISTINCT TOP 3
                                NewId() AS RandId,
                                Product.ManufacturerId,
                                ProductRelated.ChildId 
                    FROM        ProductRelated 
                    JOIN        Product 
                    ON          Product.ProductId = ProductRelated.ChildId
                    WHERE       ManufacturerId IS NOT NULL
                    AND         Product.ManufacturerId = P.ManufacturerId
                    ORDER BY    NewId()
                ) CandidateInner
LEFT JOIN       (
                    SELECT      DISTINCT TOP 100 PERCENT
                                ParentId,
                                COUNT(DISTINCT ChildId) AS Ct
                    FROM        ProductGroup 
                    GROUP BY    ParentId
                    HAVING      COUNT(DISTINCT ChildId) >= 3
                ) AlreadyGrouped
ON              P.ProductId = AlreadyGrouped.ParentId
WHERE           P.ProductId <> CandidateInner.ChildId
AND             AlreadyGrouped.ParentId IS NULL
ORDER BY        P.ProductId

* Обратите внимание, что это вставит менее 3 в следующих 2 случаях:

1) Где <3 товаров, связанных с производителем 2) (Проблематично), где случайная вершина 3 возвращает тот же продукт себе. </p>

(1) выше неизбежно.

Способ, который я обработал (2) выше, состоял в том, чтобы выполнить это дважды, а затем удалить дубликаты. Это все еще не 100%, но по статистике, это более чем достаточно для моего требования. Это в сценарии ночного запуска, но мне все еще нравится быстрота того, что <> за пределами CROSS APPLY - все, что тянет эту проверку области действия, приводит к сканированию производных таблиц, полученных в результате объединения производителей, даже при том, что оно вытягивается внутри будет означать, что (2) больше не является проблемой, но она мучительно медленна по сравнению с мгновенной с правильными индексами.

...