#sql #amazon-redshift
#sql #amazon-redshift
Вопрос:
У меня есть запрос ниже, который я запускаю, дает мне единый счет за month
текущий месяц и dates_for_week
содержит список всех дат за последнюю неделю с воскресенья по субботу.
select COUNT(DISTINCT(CLIENTID))
FROM process.data
where type = 'pots'
and stype= 'kites'
and tires IN ('abc', 'def', 'ghi', 'jkl')
and comp IN ('data', 'hello', 'world')
AND year = '2020'
-- this is for month october but week 43
and (month = '10' and dates_for_week IN ('18', '19', '20', '21', '22', '23', '24'))
На данный момент результат, который я вижу, таков —
Count
-----
982
Теперь я пытаюсь сделать этот запрос динамическим, чтобы он мог дать мне подсчет за последние 6 недель, как показано ниже, в качестве вывода:
Count Week
------------
982 W43
123 W42
126 W41
127 W40
128 W39
129 W38
Я могу преобразовать приведенный выше запрос динамическим способом, который дает мне счет за текущий месяц октябрь и предыдущую неделю, который равен 43, и он отлично работает, как показано ниже, но я не уверен, как я могу изменить его, чтобы он мог выдавать мне данные за все последние 6 недель в указанном выше формате вывода. Похоже, мне нужно также динамически изменять месяц в течение какой-то недели, чтобы получить выходные данные за последние 6 недель.
select COUNT(DISTINCT(CLIENTID))
FROM process.data
where type = 'pots'
and stype= 'kites'
and tires IN ('abc', 'def', 'ghi', 'jkl')
and comp IN ('data', 'hello', 'world')
AND year = '2020'
-- this is for month october but week 43
and (
month = extract(month from current_date)
and dates_for_week IN (
select
date_part('d',((DATE_TRUNC('week', CURRENT_DATE) - 9) row_number() over (order by true))::date)
from process.data
limit 7
)
)
Итак, что мне нужно, это за последние 6 недель и группировать по неделям, чтобы дать мне подсчет, как показано выше. Возможно ли это сделать случайно?
and (month = '10' and dates_for_week IN ('18', '19', '20', '21', '22', '23', '24'))
and (month = '10' and dates_for_week IN ('11', '12', '13', '14', '15', '16', '17'))
and (month = '10' and dates_for_week IN ('4', '5', '6', '7', '8', '9', '10'))
and (month IN ('9', '10') and dates_for_week IN ('27', '28', '29', '30', '1', '2', '3'))
and (month = '9' and dates_for_week IN ('20', '21', '22', '23', '24', '25', '26'))
and (month = '9' and dates_for_week IN ('13', '14', '15', '16', '17', '18', '19'))
Комментарии:
1. Мне пришлось прочитать ответ Филиппа Йоханниса, чтобы наконец понять, что
dates_for_week
это просто номер дня в дате, например, 29 для 2020-10-29. Название меня смутило. Есть ли причина, по которой вы храните год, месяц и день отдельно? Почему бы вам просто не сохранить даты как даты? Это значительно упростило бы запрос данных, а также предотвратило бы хранение в базе данных недопустимых дат (например, 2020-02-30 или 2020-29-10).
Ответ №1:
У вас есть годы, месяцы и дни в отдельных столбцах, если я правильно понимаю. Я думаю, что самый простой способ — «построить» правильный столбец даты, а затем работать с этим столбцом.
Следующий запрос должен предоставить вам последние 6 недель, включая текущую неделю.
select
EXTRACT(week from TO_DATE(year||'-'||month||'-'|| dates_for_week,'YYYY-MM-DD')) week_num
,COUNT(DISTINCT(CLIENTID))
FROM process.data
where type = 'pots'
and stype= 'kites'
and tires IN ('abc', 'def', 'ghi', 'jkl')
and comp IN ('data', 'hello', 'world')
and TO_DATE(year||'-'||month||'-'|| dates_for_week,'YYYY-MM-DD') >= DATEADD(day,-42,DATE_TRUNC('week', sysdate))
GROUP BY 1
ORDER BY 1 desc
Однако может возникнуть проблема, поскольку недели начинаются в понедельник в redshift, поэтому может потребоваться небольшая манипуляция (добавление одного дня):
select
EXTRACT(week from DATEADD(day,1,TO_DATE(year||'-'||month||'-'|| dates_for_week,'YYYY-MM-DD'))) week_num
,COUNT(DISTINCT(CLIENTID))
FROM process.data
where type = 'pots'
and stype= 'kites'
and tires IN ('abc', 'def', 'ghi', 'jkl')
and comp IN ('data', 'hello', 'world')
and DATEADD(day,1,TO_DATE(year||'-'||month||'-'|| dates_for_week,'YYYY-MM-DD')) BETWEEN DATEADD(day,-42,DATE_TRUNC('week', sysdate)) AND DATEADD(day,-1,DATE_TRUNC('week', sysdate))
GROUP BY 1
ORDER BY 1 desc
Отладка:
Сначала я бы начал выполнять этот запрос, чтобы проверить, правильно ли рассчитана дата
select COUNT(DISTINCT(CLIENTID))
FROM process.data
where type = 'pots'
and stype= 'kites'
and tires IN ('abc', 'def', 'ghi', 'jkl')
and comp IN ('data', 'hello', 'world')
AND year = '2020'
-- this is for month october but week 43
and TO_DATE(year||'-'||month||'-'|| dates_for_week,'YYYY-MM-DD') between '2020-10-18' and '2020-10-24'
После этого я бы посмотрел, правильно ли рассчитана неделя:
select
EXTRACT(week from DATEADD(day,1,TO_DATE(year||'-'||month||'-'|| dates_for_week,'YYYY-MM-DD'))) week_num
,COUNT(DISTINCT(CLIENTID))
FROM process.data
where type = 'pots'
and stype= 'kites'
and tires IN ('abc', 'def', 'ghi', 'jkl')
and comp IN ('data', 'hello', 'world')
AND year = '2020'
-- this is for month october but week 43
and TO_DATE(year||'-'||month||'-'|| dates_for_week,'YYYY-MM-DD') between '2020-10-18' and '2020-10-24'
group by 1
order by 1
И последнее, но не менее важное, я бы расширил временные рамки и сделал их динамическими:
select
EXTRACT(week from DATEADD(day,1,TO_DATE(year||'-'||month||'-'|| dates_for_week,'YYYY-MM-DD'))) week_num
,COUNT(DISTINCT(CLIENTID))
FROM process.data
where type = 'pots'
and stype= 'kites'
and tires IN ('abc', 'def', 'ghi', 'jkl')
and comp IN ('data', 'hello', 'world')
AND year = '2020'
-- this is for month october but week 43
and DATEADD(day,1,TO_DATE(year||'-'||month||'-'|| dates_for_week,'YYYY-MM-DD')) Between DATEADD(day,-42,DATE_TRUNC('week', sysdate)) and DATEADD(day,-1,DATE_TRUNC('week', sysdate))
group by 1
order by 1
Комментарии:
1. Это потрясающе. Я многому научился. Я не знал, что мы можем написать такой запрос. Похоже, я усложнил это самостоятельно. Спасибо за вашу помощь! Позвольте мне попробовать это и тоже понять.
2. Я попробовал этот запрос, но почему-то результаты не совпадают, если я сравниваю вывод этого динамического запроса с запросом вручную для каждой недели. Также в моем случае мне это не нужно для текущей недели, мне это нужно для завершения шести недель, то есть на прошлой неделе, а затем еще на 5 недель. Есть идеи, почему результаты не совпадают? Есть ли какой-нибудь способ разделить этот запрос, чтобы я мог его отладить и посмотреть, какие даты он использует для каждой недели, поскольку мне нужно с воскресенья по субботу для каждой недели.
3. Добавлена некоторая отладка и обнаружена небольшая ошибка. Я вычитал один день, чтобы получить воскресенье в субботу, но я должен был добавить один день. Не могли бы вы повторить попытку?
4. Я выполнил все ваши 3 запроса в разделе отладки — данные точно совпадают, если я использую ваш первый и второй отладочный запрос по отдельности с моим исходным запросом вручную, но я заметил несколько вещей, если я запускаю третий отладочный запрос. Первое, что я заметил из третьего запроса, это то, что он дает данные за неделю
37, 38, 39, 40, 41, 42, 43
, но мне нужны только последние 6 недель, поэтому мне не нужна неделя37
. Во-вторых,38, 39, 40, 41, 42
данные недели точно совпадают из третьего запроса с вашим первым отладочным запросом, но данные недели 43 не совпадают из третьего запроса с вашим первым и вторым запросом. Есть мысли?5. Итак, я думаю, проблема связана с рассчитанным диапазоном дат. Просто дважды проверил это, я думаю, это должно быть действительно правильное условие where:
and DATEADD(day,1,TO_DATE(year||'-'||month||'-'|| dates_for_week,'YYYY-MM-DD')) Between DATEADD(day,-42,DATE_TRUNC('week', sysdate)) and DATEADD(day,-1,DATE_TRUNC('week', sysdate))
— Возможно, вам придется поиграть с-42
— это означает, что вы возвращаетесь на 42 дня назад с последнего начала недели, что должно составлять 6 недель, потому что 6 * 7 = 42.
Ответ №2:
Предполагая, что у вас есть какой-то столбец даты, вы можете просто использовать что-то вроде этого
select date_part(w, {your_date_column) as week_number,
COUNT(DISTINCT(CLIENTID))
FROM process.data
where type = 'pots'
and stype= 'kites'
and tires IN ('abc', 'def', 'ghi', 'jkl')
and comp IN ('data', 'hello', 'world')
AND year = '2020'
group by 1
Ответ №3:
Вы могли бы использовать order by
и limit
:
select year, week, COUNT(DISTINCT CLIENTID)
from process.data
where type = 'pots' and
stype= 'kites' and
tires IN ('abc', 'def', 'ghi', 'jkl') and
comp IN ('data', 'hello', 'world')
group by year, dates_for_week
order by year desc, week desc
limit 6;
Это предполагает, что у вас есть столбец недели, что кажется разумным предположением.
Это простой способ выполнить то, что вы хотите сделать. Я предполагаю, что при Redshift он должен иметь приличную производительность.