Google Bigquery не согласуется при изменении имен переменных в предложении ORDER BY
Моя цель состоит в том, чтобы проверить, являются ли grp, сгенерированные одним запросом, теми же, что и выходные данные того же запроса. Однако, когда я изменяю одно имя переменной, я получаю разные результаты.
Ниже я показываю пример того же запроса, где мы знаем, что результаты одинаковы. Однако, если вы запустите эту группу, вы обнаружите, что один запрос дает результаты, отличные от другого.
SELECT grp
FROM
(
SELECT CONCAT(word, corpus) AS grp, rank1, rank2
FROM (
SELECT
word, corpus,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY test1 DESC) AS rank1,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY word_count DESC) AS rank2,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus DESC) AS rank3,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus_date DESC) AS rank4
FROM
(
SELECT *, (word_count * word_count * corpus_date) AS test1
FROM [bigquery-public-data:samples.shakespeare]
)
)
)
WHERE rank1 <= 3 OR rank2 <= 3
HAVING grp NOT IN
(
SELECT grp FROM (
SELECT CONCAT(word, corpus) AS grp, rank1, rank2
FROM
(
SELECT
word, corpus,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY test2 DESC) AS rank1,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY word_count DESC) AS rank2,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus DESC) AS rank3,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus_date DESC) AS rank4
FROM
(
SELECT *, (word_count * word_count * corpus_date) AS test2
FROM [bigquery-public-data:samples.shakespeare]
)
)
)
WHERE rank1 <= 3 OR rank2 <= 3
)
Гораздо хуже... теперь, если вы попытаетесь выполнить точно такой же запрос, но просто измените имя переменной test1 на test3, вы получите совершенно другие результаты.
SELECT grp
FROM
(
SELECT CONCAT(word, corpus) AS grp, rank1, rank2
FROM (
SELECT
word, corpus,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY test3 DESC) AS rank1,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY word_count DESC) AS rank2,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus DESC) AS rank3,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus_date DESC) AS rank4
FROM
(
SELECT *, (word_count * word_count * corpus_date) AS test3
FROM [bigquery-public-data:samples.shakespeare]
)
)
)
WHERE rank1 <= 3 OR rank2 <= 3
HAVING grp NOT IN
(
SELECT grp FROM (
SELECT CONCAT(word, corpus) AS grp, rank1, rank2
FROM
(
SELECT
word, corpus,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY test2 DESC) AS rank1,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY word_count DESC) AS rank2,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus DESC) AS rank3,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus_date DESC) AS rank4
FROM
(
SELECT *, (word_count * word_count * corpus_date) AS test2
FROM [bigquery-public-data:samples.shakespeare]
)
)
)
WHERE rank1 <= 3 OR rank2 <= 3
)
Я не могу придумать никакого объяснения, которое удовлетворяло бы обоим этим странным поведениям, и это мешало мне иметь возможность проверять свои данные. Есть идеи?
РЕДАКТИРОВАТЬ:
Я обновил BigQuery SQL так, как подсказывают ответы, и возникают те же несоответствия.
3 ответа
Проблема в недетерминированности вашей нумерации строк.
В этой таблице много примеров (word_count * word_count * corpus_date)
то же самое для нескольких корпусов. Поэтому, когда вы разделяете word
и заказать по test2
порядок, который вы используете для назначения номеров строк, является недетерминированным.
Когда вы запускаете один и тот же подзапрос дважды в одном и том же запросе верхнего уровня, BigQuery фактически выполняет этот подзапрос дважды и может давать разные результаты между двумя запусками из-за этого недетерминизма.
Изменение псевдонима могло привести к тому, что ваш запрос не попадал в кеш, что приводило к другому набору недетерминированных вариантов и разному перекрытию результатов.
Вы можете подтвердить это, изменив ORDER BY
пункт в ваших аналитических функциях, чтобы включить corpus
, Например, изменить ORDER BY test2
в ORDER BY test2, corpus
, Тогда нумерация строк будет детерминированной, и запросы будут возвращать ноль результатов независимо от того, какие псевдонимы вы используете.
Я не понимаю вопроса. Синтаксис SQL в целом и BigQuery в частности достаточно ясны: псевдонимы, определенные в SELECT
не может быть использован в SELECT
для других выражений. Как объяснено в документации BigQuery:
Псевдонимы, определенные в
SELECT
на статью можно сослаться вGROUP BY
,HAVING
, а такжеORDER BY
пункты запроса, но неFROM
,WHERE
, или жеOMIT RECORD IF
пункты, ни с помощью других выражений в том жеSELECT
пункт. [акцент мой]
Следовательно, ваш запрос будет работать только если test1
, test2
, а также test3
столбцы в таблице Шекспира. Нет оснований полагать, что такие столбцы будут иметь одинаковые значения, поэтому я не ожидаю, что запросы будут возвращать одинаковые результаты.
РЕДАКТИРОВАТЬ:
Если мы предположим, что документация неверна, то проблема, вероятно, дублируется в order by
критерии для row_number()
, Сортировка в SQL нестабильна - это означает, что две строки с одинаковым значением ключа сортировки могут появляться в любом порядке во время сортировки. Даже один и тот же запрос может вернуть разные результаты за два запуска. Сортировки SQL, очевидно, нестабильны, поскольку таблицы не имеют внутреннего порядка в строках (порядок определяется только столбцами).
Итак, все, что происходит, - это то, что выбираются разные строки с одинаковыми значениями ключа сортировки. Я не думаю, что это связано с псевдонимами.
Как вы можете это исправить? Добавить дополнительный ключ сортировки, такой как id
, в качестве окончательного ключа. Альтернативно использовать rank()
или же dense_rank()
и выяснить, что делать с дубликатами.
Я заметил, что вы всегда задаете сложные вопросы, а потом вам трудно принять или даже проголосовать за ответ. Это нормально! И я хочу попробовать еще раз, так что давайте перейдем к теме:
Похоже, использование псевдонимов в одном и том же операторе SELECT недокументировано и не поддерживается. Примечание ниже в документации по предложению SELECT:
Каждому выражению можно присвоить псевдоним, добавив пробел с последующим идентификатором после выражения. Необязательное ключевое слово AS может быть добавлено между выражением и псевдонимом для улучшения читабельности. На псевдонимы, определенные в предложении SELECT, можно ссылаться в предложениях GROUP BY, HAVING и ORDER BY запроса, но не в предложениях FROM, WHERE или OMIT RECORD IF, а также в других выражениях этого же предложения SELECT.
Таким образом, здесь происходит странное поведение без ошибок. Таким образом, вы можете использовать его на свой страх и риск, но лучше этого не делать (все равно было бы здорово услышать от Google Team - но, поскольку он не поддерживается - вы можете ожидать, что не будет много информации, объясняющей это поведение)
А пока - я бы предложил просто следовать тому, что поддерживается, и преобразовать ваш запрос в версию ниже "стабильной".
Это не проблема, с которой вы сталкиваетесь в оригинале!
(обратите внимание, что я изменил предложение WHERE в первом подзапросе - иначе он всегда возвращает ноль строк - что имеет смысл)
SELECT grp
FROM
(
SELECT CONCAT(word, corpus) AS grp, rank2,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY [try_any_alias_1] DESC) AS rank1
FROM (
SELECT
word, corpus,
(word_count * word_count * corpus_date) AS [try_any_alias_1],
ROW_NUMBER() OVER (PARTITION BY word ORDER BY word_count DESC) AS rank2,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus DESC) AS rank3,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus_date DESC) AS rank4
FROM [bigquery-public-data:samples.shakespeare]
)
)
WHERE rank1 <= 3 OR rank2 <= 4 // if rank2 <= 3 as in second subquery - result is always empty as expected
HAVING grp NOT IN
(
SELECT grp FROM (
SELECT CONCAT(word, corpus) AS grp, rank2,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY [try_any_alias_2] DESC) AS rank1
FROM
(
SELECT
word, corpus,
(word_count * word_count * corpus_date) AS [try_any_alias_2],
ROW_NUMBER() OVER (PARTITION BY word ORDER BY word_count DESC) AS rank2,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus DESC) AS rank3,
ROW_NUMBER() OVER (PARTITION BY word ORDER BY corpus_date DESC) AS rank4
FROM [bigquery-public-data:samples.shakespeare]
)
)
WHERE rank1 <= 3 OR rank2 <= 3
)