#sql #postgresql
#sql #postgresql
Вопрос:
Как я должен собирать свои данные в соответствии с продажами? Если никакие продажи не попадают под конкретную корзину, то она должна содержать NA. Выходные данные должны содержать все сегменты, также, если они пусты. Для формирования сегмента не используются операторы CASE или branching . Возможно ли выполнить пакетирование с помощью CTE или объединений или с вложением? Например, образец данных:
Таблица1
ID | продажи |
---|---|
1 | 4 |
2 | 5 |
3 | 10 |
4 | 22 |
5 | 6 |
Вывод должен быть таким:
корзина | ID | продажи |
---|---|---|
1-5 | 1 | 4 |
1-5 | 2 | 5 |
6-10 | 5 | 6 |
6-10 | 3 | 10 |
11-15 | NA | NA |
16-20 | NA | NA |
21-25 | 4 | 22 |
Комментарии:
1. . . Я удалил несовместимые теги базы данных. Пожалуйста, помечайте только ту базу данных, которую вы действительно используете.
2. @GordonLinoff извиняется, сэр. Я новичок в этой платформе и языке, который я использую.
3. Не вандализируйте свой вопрос.
Ответ №1:
Вы можете определить сегменты и использовать left join
:
select b.name, t1.id, count(t1.id) as num_sales
from (select '1-5' as name, 1 as lo, 5 as hi union all
select '6-10' as name, 6 as lo, 10 as hi union all
select '11-15' as name, 11 as lo, 15 as hi union all
select '16-20' as name, 16 as lo, 20 as hi union all
select '21-25' as name, 21 as lo, 25 as hi
) b left join
table1 t1
on t1.sales >= b.lo and t1.sales <= b.hi
group by b.name, t1.id
order by min(lo);
Вышесказанное должно работать практически в любой базе данных. В Postgres вы можете упростить его до:
select b.name, t1.id, count(t1.id) as num_sales
from (values ('1-5', 1, 5),
('6-10', 6, 10),
('11-15', 11, 15),
('16-20', 16, 20),
('21-25', 21, 25)
) b left join
table1 t1
on t1.sales >= b.lo and t1.sales <= b.hi
group by b.name, t1.id
order by min(lo);
Если все ячейки имеют одинаковый размер, вы также можете сыграть с ними злую шутку generate_series()
.
Комментарии:
1. Здравствуйте, сэр, не могли бы вы, пожалуйста, дать решение для вычисления сегментов с помощью формулы или выражения? На самом деле, данные в моей системе слишком велики, поскольку они не могут быть жестко запрограммированы таким образом.
Ответ №2:
Вы можете использовать рекурсивный CTE, который возвращает сегменты и присоединяет их к таблице слева:
WITH RECURSIVE cte(bucket) AS (SELECT 1 UNION ALL SELECT bucket 5 FROM cte WHERE bucket < 20)
SELECT CONCAT(c.bucket, '-', c.bucket 4) bucket, t1.id, t1.sales
FROM cte c LEFT JOIN Table1 t1
ON t1.sales BETWEEN c.bucket AND c.bucket 4
Или с generate_series()
:
SELECT CONCAT(g.bucket, '-', g.bucket 4) bucket, t1.id, t1.sales
FROM generate_series(1, 25, 5) g(bucket) LEFT JOIN Table1 t1
ON t1.sales BETWEEN g.bucket AND g.bucket 4
Посмотрите демонстрацию.
Результаты:
корзина | ID | продажи |
---|---|---|
1-5 | 1 | 4 |
1-5 | 2 | 5 |
6-10 | 3 | 10 |
6-10 | 5 | 6 |
11-15 | null | null |
16-20 | null | null |
21-25 | 4 | 22 |
Ответ №3:
Другой способ — генерировать сегменты в зависимости от максимальных продаж в таблице :
with buckets as (
select t.b as lowerBound,
t.b 4 as upperBound
from generate_series(1, (select max(Sales) from Table1), 5) t(b)
)
select concat(lowerBound, '-', upperBound) as bucket,
t1.id,
t1.sales
from buckets b
left join Table1 t1
on t1.sales between b.lowerBound and b.upperBound