Случайные значения в итерационных cte's - PullRequest
2 голосов
/ 28 июня 2019

Использование SQL Server 2016 +

У меня возникли некоторые трудности при выборе случайных строк из таблицы, которая была сужена до проблемы с тем, как генерируются случайные числа.Для эффективности использование TOP 10 * ORDER BY NEWID() является медленным.В приведенном ниже коде я использовал набор семян для повторяющихся результатов, но в живую я бы не стал.

Пример кода:

SELECT  RAND(100) RN
,       RAND()
,       RAND()
,       RAND()
,       RAND();

SELECT  RAND(100) RN
UNION ALL SELECT  RAND()
UNION ALL SELECT  RAND()
UNION ALL SELECT  RAND()
UNION ALL SELECT  RAND();

WITH cte AS
    (SELECT 1         ID
     ,      RAND(100) RN
     UNION ALL
     SELECT cte.ID + 1
     ,      RAND()
       FROM cte
      WHERE ID < 5)
SELECT  RN
  FROM  cte;

Результирующий набор

RN                                                                                          
---------------------- ---------------------- ---------------------- ---------------------- ----------------------
0.715436657367485      0.28463380767982       0.0131039082850364     0.28769876521071       0.100505471175005

(1 row affected)

RN
----------------------
0.715436657367485
0.28463380767982
0.0131039082850364
0.28769876521071
0.100505471175005

(5 rows affected)

RN
----------------------
0.715436657367485
0.28463380767982
0.28463380767982
0.28463380767982
0.28463380767982

(5 rows affected)

Как видно из результатов, когда я вызываю функцию RAND(), повторяемую в строке или черезсоюз я получаю разные результаты с каждым звонком.Однако если я вызываю функцию внутри итеративного cte, то получаю повторное значение.

Этот код - пример, показывающий проблему, а не весь кодовый набор.Я создал это исключительно в качестве примера, чтобы продемонстрировать проблему.У меня есть решение, основанное на комбинации Checksum & NewID() и вызовах модуля и умножении, чтобы получить значения в желаемом диапазоне, но это довольно сложно и кажется чрезмерным для простого создания списка случайных чисел вдиапазон.

Я ищу любые рекомендации, которые можно предложить по

  1. Почему это происходит
  2. Любые способы решения проблемы
  3. Другие варианты создания списков случайных чисел (которые не являются RBAR)

Большое спасибо.

1 Ответ

1 голос
/ 28 июня 2019

RAND() возвращает «постоянное» значение в запросе. То есть он оценивается один раз за «упоминание» в запросе.

Вы можете увидеть это, если запустите:

select rand(), rand()
from (values (1), (2), (3)) v(x);

Каждая строка имеет два разных значения. Однако столбцы имеют одинаковые значения.

В любом случае каноническим ответом является использование RAND(CHECKSUM(NEWID())). Это обеспечивает случайное начальное значение для RAND() при каждом вызове:

WITH cte AS
    (SELECT 1 as ID, RAND(CHECKSUM(NEWID())) as RN
     UNION ALL
     SELECT cte.ID + 1, RAND(CHECKSUM(NEWID())) as RN
     FROM cte
     WHERE ID < 5
    )
SELECT RN
FROM cte;

Это, вероятно, достаточно хорошо, чтобы получить список случайных значений. Я подозреваю, что ваша реальная проблема в другом - что-то вроде извлечения случайных строк из таблицы. Если это реальная проблема, задайте новый вопрос или проверьте похожие вопросы.

...