Мы уже умеем использовать в запросах интервалы. Например так:
SELECT p.purchase_date,
p.purchase_date + interval '1 month' AS add_month
FROM purchase p
# | purchase_date | add_month |
---|---|---|
1 | 2019-03-11 17:15:02.206+07 | 2019-04-11 17:15:02.206+07 |
2 | 2019-03-10 17:12:34.206+07 | 2019-04-10 17:12:34.206+07 |
3 | 2019-04-05 16:12:33.206+07 | 2019-05-05 16:12:33.206+07 |
4 | 2019-07-06 20:12:33.206+07 | 2019-08-06 20:12:33.206+07 |
... | ... | ... |
Бывает так, что на момент написания запроса неизвестно, какое количество месяцев, дней, минут нужно использовать. Эти значения могут храниться в базе данных или вычисляться по формуле.
Для примера мы будем использовать константу из подзапроса, как будто мы ее получили из таблицы БД:
SELECT c.count_months,
p.purchase_date,
p.purchase_date + interval '1 month' AS add_month
FROM purchase p,
(SELECT 5 AS count_months) c
# | count_months | purchase_date | add_month |
---|---|---|---|
1 | 5 | 2019-03-11 17:15:02.206+07 | 2019-04-11 17:15:02.206+07 |
2 | 5 | 2019-03-10 17:12:34.206+07 | 2019-04-10 17:12:34.206+07 |
3 | 5 | 2019-04-05 16:12:33.206+07 | 2019-05-05 16:12:33.206+07 |
4 | 5 | 2019-07-06 20:12:33.206+07 | 2019-08-06 20:12:33.206+07 |
... | ... | ... | ... |
И вроде бы все просто: нужно заменить interval '1 month'
на interval c.count_months || ' month'
и должно сработать. Но нет:
SELECT c.count_months,
p.purchase_date,
p.purchase_date + interval c.count_months || ' month' AS add_month
FROM purchase p,
(SELECT 5 AS count_months) c
error: syntax error at or near "."
Избежать этой ошибки можно двумя способами: воспользовавшись функцией make_interval
или приведением строки к типу interval
.
make_interval
В PostgreSQL есть функция создания интервала make_interval
со следующей сигнатурой:
make_interval(
years int DEFAULT 0,
months int DEFAULT 0,
weeks int DEFAULT 0,
days int DEFAULT 0,
hours int DEFAULT 0,
mins int DEFAULT 0,
secs double precision DEFAULT 0.0
)
Использовать ее можно последовательно перечисляя параметры:
SELECT make_interval(1, 6, 0, 10)
# | make_interval |
---|---|
1 | 1 year 6 mons 10 days |
Такой способ не удобен, если нам нужно передать только пару параметров. В этом случае можно воспользоваться стрелочной нотацией:
SELECT make_interval(months => 6)
# | make_interval |
---|---|
1 | 6 mons |
Воспользуемся make_interval
для добавления динамического количества месяцев к дате продажи:
SELECT c.count_months,
p.purchase_date,
p.purchase_date + make_interval(months => c.count_months) AS add_month
FROM purchase p,
(SELECT 5 AS count_months) c
# | count_months | purchase_date | add_month |
---|---|---|---|
1 | 5 | 2019-03-11 17:15:02.206+07 | 2019-08-11 17:15:02.206+07 |
2 | 5 | 2019-03-10 17:12:34.206+07 | 2019-08-10 17:12:34.206+07 |
3 | 5 | 2019-04-05 16:12:33.206+07 | 2019-09-05 16:12:33.206+07 |
4 | 5 | 2019-07-06 20:12:33.206+07 | 2019-12-06 20:12:33.206+07 |
... | ... | ... | ... |
Преобразование строки в интервал
Преобразовать строку в интервал можно с помощью ::
SELECT '5 months'::interval
# | interval |
---|---|
1 | 5 mons |
Решим нашу основную задачу
SELECT c.count_months,
p.purchase_date,
p.purchase_date + (c.count_months || ' months')::interval AS add_month
FROM purchase p,
(SELECT 5 AS count_months) c
# | count_months | purchase_date | add_month |
---|---|---|---|
1 | 5 | 2019-03-11 17:15:02.206+07 | 2019-08-11 17:15:02.206+07 |
2 | 5 | 2019-03-10 17:12:34.206+07 | 2019-08-10 17:12:34.206+07 |
3 | 5 | 2019-04-05 16:12:33.206+07 | 2019-09-05 16:12:33.206+07 |
4 | 5 | 2019-07-06 20:12:33.206+07 | 2019-12-06 20:12:33.206+07 |
... | ... | ... | ... |