Почему RANDOM () в SQLite CTE JOIN ведет себя иначе, чем другие RDBMS? - PullRequest
0 голосов
/ 11 мая 2018

RANDOM() значения в соединении Common Table Expression (CTE) не работают должным образом в SQLite .

SQL:

WITH
  tbl1(n) AS (SELECT 1 UNION ALL SELECT 2),
  tbl2(n, r) AS (SELECT n, RANDOM() FROM tbl1)
SELECT * FROM tbl2 t1 CROSS JOIN tbl2 t2;

Пример результатов SQLite:

n   r                       n   r
1   7058971975145008000     1   8874103142384122000
1   1383551786055205600     2   8456124381892735000
2   2646187515714600000     1   7558324128446983000
2   -1529979429149869800    2   7003770339419606000

Все случайные числа в каждом столбце различны.Но CROSS JOIN повторяет строки - так что я ожидал 2 пары одинаковых номеров в каждом столбце - что равно в случае PostgreSQL , Oracle 11g и SQL Server 2014 (при использовании начального числа на основе строк).

Пример результатов PostgreSQL / Oracle 11g / SQL Server 2014:

n   r                   n   r
1   0.117551110684872   1   0.117551110684872
1   0.117551110684872   2   0.221985165029764
2   0.221985165029764   1   0.117551110684872
2   0.221985165029764   2   0.221985165029764

Вопросы

  1. Можно ли объяснить поведение в SQLite?Это ошибка?
  2. Существует ли способ для таблицы B в CTE (на основе таблицы A в том же CTE) иметь дополнительный столбец случайно сгенерированных чисел, который останется фиксированным при использовании в JOIN?

1 Ответ

0 голосов
/ 12 мая 2018

Ваш вопрос довольно длинный и бессвязный - ни одного вопроса. Но это интересно, и я кое-что узнал.

Это утверждение не соответствует действительности:

SQL Server назначает случайное начальное число для функции RAND (): при использовании в SELECT, он высевается только один раз, а не для каждой строки.

SQL Server имеет концепцию постоянных функций во время выполнения. Это функции, которые извлекаются из скомпилированного запроса и выполняются один раз для выражения в начале запроса. Наиболее яркими примерами являются getdate() (и соответствующие функции даты / времени) и rand().

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

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

Каждый столбец имеет одинаковые значения, но значения между столбцами различны.

Большинство баз данных, включая SQLite, имеют более интуитивно понятную интерпретацию rand() / random(). (Как личное примечание, «случайная» функция, которая возвращает одно и то же значение в каждой строке, очень нелогична.) Каждый раз, когда она вызывается, вы получаете другое значение. Для SQL Server вы обычно используете выражение, использующее newid():

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

Что касается вашего второго вопроса, кажется , что SQLite материализует рекурсивные CTE. Так что это то, что вы хотите:

WITH tbl1(n) AS (
      SELECT 1 UNION ALL SELECT 2
     ),
     tbl2(n, r) AS (
       SELECT n, RANDOM()
       FROM tbl1
       union all
       select *
       from tbl2
       where 1=0
      )
SELECT *
FROM tbl2 t1 CROSS JOIN tbl2 t2;

Я не видел документации, что это так, поэтому используйте на свой страх и риск. Здесь - DB-Fiddle.

И, к сведению, похоже, что это работает и в SQL Server. Я только что чему-то научился!

EDIT:

Как предлагается в комментарии, материализация не всегда может происходить. Похоже, это относится к двум ссылкам на одном уровне:

WITH tbl1(n) AS (
      SELECT 1 UNION ALL SELECT 2),
     tbl2(n, r) AS (
       SELECT n, RANDOM()
       FROM tbl1
       union all
       select *
       from tbl2
       where 1=0
      )
SELECT t2a.r, count(*)
FROM tbl2 t2a left JOIN
     tbl2 t2b
     on t2a.r = t2b.r
GROUP BY t2a.r;
...