Два запроса, возвращающие разные результаты, когда они должны быть эквивалентны? - PullRequest
1 голос
/ 28 января 2020

Наш набор данных в основном объединяет набор дат (недели от текущей недели до прошлого) с набором разделов, основанных на том, начинались ли эти разделы в или до и заканчивались в или после этой недели. Хотя изначально этот запрос дал нам ожидаемые результаты, на этой неделе он начал давать нам неправильные результаты. После нескольких попыток мы обнаружили, что если мы изменили запрос на LEFT JOIN, а затем отфильтровали запрос с помощью предложения WHERE, он снова дал бы нам правильные результаты.

В чем разница? Почему один работает, а другой нет? ( Бонусные баллы: почему исходный запрос работал в течение нескольких недель, прежде чем внезапно возникла эта ошибка?) Выполнение того же внутреннего соединения в Redshift дает правильные результаты, так что это нюанс снежинки, который мы не понимаем .

Исходный запрос:

WITH week_list AS
(
    SELECT DATEADD(week, -4, DATE_TRUNC(week, CURRENT_DATE())) AS week_value

    UNION ALL

    SELECT DATEADD(week, 1, week_value)
    FROM week_list
    WHERE DATEADD(week, 1, week_value) < CURRENT_DATE()
),
active_sections_per_week AS
(
    SELECT 
        wl.week_value, s.id section_id
    FROM week_list wl
    JOIN schema.sections s ON wl.week_value >= DATE_TRUNC(week, s.starts_at)
                           AND wl.week_value <= DATE_TRUNC(week, s.ends_at)
)
SELECT 
    aspw.week_value,
    COUNT(DISTINCT aspw.section_id) count_sections
FROM 
    active_sections_per_week aspw
GROUP BY 1
ORDER BY 1 DESC

Результаты: Одна строка, датированная 2019-12-30 (4 недели go) , Нет данных за последние три недели.

Примечание. Если вы отрегулируете DATEADD в первом CTE, то, какова бы ни была возвращенная первая дата, всегда будет успешно выполнено присоединение. Такое поведение началось только в течение последней недели - ранее этот запрос предоставлял ожидаемое количество строк (другими словами, количество недель, указанное в этом первом DATEADD).

«Исправлено» запрос:

WITH week_list AS
(
    SELECT DATEADD(week, -4, DATE_TRUNC(week, CURRENT_DATE())) AS week_value

    UNION ALL

    SELECT DATEADD(week, 1, week_value)
    FROM week_list
    WHERE DATEADD(week, 1, week_value) < CURRENT_DATE()
),
active_sections_per_week AS
(
    SELECT wl.week_value, s.id section_id
    FROM week_list wl
    LEFT JOIN schema.sections s ON wl.week_value >= DATE_TRUNC(week, s.starts_at)
                                AND wl.week_value <= DATE_TRUNC(week, s.ends_at)
    WHERE s.id IS NOT NULL
)
SELECT aspw.week_value, COUNT(DISTINCT aspw.section_id) count_sections
FROM active_sections_per_week aspw
GROUP BY 1
ORDER BY 1 DESC

Результаты: возвращает четыре строки, недели от 2019-12-30 до 2020-01-20, с соответствующим количеством секций.

Ответы [ 3 ]

2 голосов
/ 28 января 2020

можно избежать рекурсивного CTE , если -4 недели является константой с этим кодом:

WITH week_list AS (
    SELECT DATEADD(week, column1, DATE_TRUNC(week, CURRENT_DATE())) 
    FROM VALUES (-4),(-3),(-2),(-1),(0)
)

со снежинкой JOIN сместит фильтры выше при выполнении стек, и вы, возможно, нашли ошибку. Где-как с LEFT JOIN (даже если в нем есть эквивалентное предложение WHERE, это, скорее всего, позволит избежать агрессивной неработающей оптимизации.

Вчера вечером был выпуск программного обеспечения для нас, но мы находимся на предприятии аккаунт, поэтому вы могли обновиться за 2 дня до этого выпуска. В этом выпуске было несколько ошибок, которые повлияли на нас, мы откатили его (для нас)

2 голосов
/ 28 января 2020

Это рекурсивный CTE в "week_list". Redshift не поддерживает рекурсивные CTE .

Snowflake поддерживает рекурсивные CTE , что объясняет разницу в поведении.

Трудно проверить это без базовых данных. Если вы получаете правильные результаты в Redshift, скорее всего, вам не нужен или вам нужен рекурсивный CTE. Вы можете изменить его так, чтобы «week_list» не ссылался на себя.

Что касается того, почему он работал раньше, то, вероятно, состояние таблицы и рекурсивный CTE работали только в особых случаях. Когда CURRENT_DATE () продвинулся, это исключило его из этого особого случая. Кроме того, внутреннее соединение и левое внешнее соединение, где s.id НЕ НУЛЬ будет эквивалентным, если не находится в рекурсивном CTE.

Вы можете прочитать больше о рекурсивном CTE здесь:

https://docs.snowflake.net/manuals/user-guide/queries-cte.html#recursive -ctes-and-иерархических данных

0 голосов
/ 28 января 2020

Спасибо за все отзывы! Хорошая новость заключается в том, что вы все помогли мне найти решение, которое, я думаю, меня устраивает. Я также разработал Snowflake, чтобы они могли исследовать это поведение и посмотреть, была ли это ошибка пользователя с моей стороны из-за непонимания того, как работают рекурсивные CTE, или, возможно, это ошибка, появившаяся в недавнем выпуске.

Вот что я нашел: хотя рекурсия работает для варианта использования, к которому я ее применял (создание списка дат на основе CURRENT_DATE), это не является строго необходимым. Поскольку нам нужен список дат, я мог бы так же легко сгенерировать таблицу и использовать номера строк для выполнения корректировок DATEADD.

Это выглядит так:

SELECT DATEADD(week, '-' || ROW_NUMBER() OVER (ORDER BY NULL), 
               DATEADD(week, 1, DATE_TRUNC(week, CURRENT_DATE()))) AS week_value
FROM table (generator(rowcount => 200))

One из больших преимуществ этого подхода я больше не ограничен настройкой MAX_RECURSIONS в Snowflake (которая по умолчанию установлена ​​на 100). Поскольку я использую эти данные для создания графиков активности с течением времени, наличие 200 значений дает мне более трех лет истории, а не просто застенчивость двухлетней истории. Мне также не нужно связываться с моим представителем Snowflake, если я хочу его расширить.

Изменение week_list CTE на этот нерекурсивный подход, похоже, решает проблему, из-за которой INNER JOIN работал некорректно , Мы до сих пор не понимаем, почему рекурсивный CTE, казалось, работал в течение нескольких недель, а затем внезапно начал плохо себя вести, но если Snowflake сможет пролить свет на это с помощью нашей заявки в службу поддержки, я вернусь сюда, чтобы предоставить обновление. Спасибо всем за вашу помощь и руководство!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...