Мы уже умеем использовать в запросах интервалы. Например так:
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 |
| ... | ... | ... | ... |