Вынудить Redshift сначала оценить определенный предикат - PullRequest
0 голосов
/ 01 мая 2018

У меня есть небольшое приложение, которое по ежедневному расписанию выполняет серию сценариев SQL для базы данных Redshift и заполняет таблицы агрегированными данными, готовыми для извлечения клиентом. Скрипты хранятся в текстовых файлах и могут быть легко обновлены, SQL извлекается из файла '| businessday |' заменено на требуемую дату, например, «20180501». Нет надежной логики, чтобы основывать дату на текущей календарной дате.

Требования от клиента изменились, и теперь есть два сценария, которые должны заполнять таблицы только в последний день месяца. Я могу обновить скрипт так, чтобы предикат читал:

WHERE (SELECT businessday FROM bd) = LAST_DAY((SELECT businessday FROM bd))

Где bd - это CTE, поэтому я могу привести строку даты как DATE.

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

Есть ли способ реструктурировать SQL, чтобы сначала оценить этот предикат?

Насколько я понимаю, вы не можете использовать процедурные операторы IF в Redshift, поэтому я ограничен добавлением предикатов в строку SQL.

Я попытался добавить второй CTE, который не возвращает предикатов в столбцах рабочих дней ключевых таблиц:

WITH bd as (SELECT CAST('20180425' as date) as businessday WHERE 
    (SELECT CAST('20180425' as date)) = LAST_DAY(( CAST('20180425' as date)))
...
WHERE ts.businessday in (select businessday from bd)

(это нужно изменить, чтобы получить то, что мне нужно, но принцип, похоже, не работает)

Упрощенная строка SQL (пара таблиц и столбцов удалены):

with cte as (select storeid from ttl_store_processed where
        businessday = '20180425'),
    bd as (SELECT CAST('20180425' as date) as businessday 
        WHERE (SELECT CAST('20180425' as date)) = LAST_DAY(( CAST('20180425' as date))))
SELECT store.storenumber AS COST_CENTER,
     TO_CHAR(DATE(tii.BusinessDay), 'YYYYMM') AS YEAR_MONTH,
     ii.ItemCode AS MATERIAL_NUMBER,
     SUM(tii.Quantity) AS UNITS
FROM cte s
    inner join transactionsale ts 
        on s.storeid = ts.storeid
    inner join Store store 
        on ts.storeid = store.storeid
    inner join transactionsaleitem tsi 
        on ts.transactionsaleid = tsi.transactionsaleid
    inner join transactioninventoryitem tii 
        on tsi.transactionsaleitemid = tii.transactionsaleitemid
    inner join inventoryitem ii 
        on tii.inventoryitemid = ii.inventoryitemid
WHERE (SELECT businessday FROM bd) = LAST_DAY((SELECT businessday FROM bd))
    AND ts.storeid IN (SELECT storeid FROM cte)
    AND ts.businessday BETWEEN DATE_TRUNC('MONTH', (SELECT businessday FROM bd)) 
        AND LAST_DAY((SELECT businessday FROM bd))
GROUP BY 
     store.storenumber,
     TO_CHAR(DATE(tii.BusinessDay), 'YYYYMM'),
     ii.ItemCode;

cte в настоящее время возвращает ~ 20 магазинов, но потенциально возрастет до 180+. Я попытался применить логику, поэтому эта таблица пуста:

with cte as (select storeid from mcdonaldshk.ttl_store_processed 
    where businessday = '20180425' and (SELECT CAST('20180425' as date)) 
        = LAST_DAY(( CAST('20180425' as date))))

Это тоже не работает

1 Ответ

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

Итак, вы в основном говорите, что хотите, чтобы он работал очень быстро, когда (SELECT businessday FROM bd) = LAST_DAY((SELECT businessday FROM bd)) ложно, заставляя его сначала оценить это?

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

JOIN (SELECT 'end of month'
      FROM bd
      WHERE businessday = LAST_DAY(businessday)
      ) lastday ON (true)

Таким образом, если это НЕ последний день, он возвращает ноль строк, поэтому нет строк, к которым можно присоединиться. Если это будет оценено в первую очередь, остальная часть запроса не будет выполнена, потому что нет строк для JOIN.


Кстати, вы также можете упростить код:

WHERE (SELECT CAST('20180425' as date)) = LAST_DAY(( CAST('20180425' as date)))

может быть просто:

WHERE ('20180425'::date) = LAST_DAY('20180425'::date)

Кроме того, если вы добавите JOIN к bd, то вы можете упростить

ts.businessday BETWEEN 
    DATE_TRUNC('MONTH', (SELECT businessday FROM bd)) 
    AND LAST_DAY((SELECT businessday FROM bd))

в

ts.businessday BETWEEN 
    DATE_TRUNC('MONTH', businessday) 
    AND LAST_DAY(businessday)
...