#sql #db2
#sql #db2
Вопрос:
У меня есть таблица с более чем 200 миллионами записей, и я пытаюсь получить результат в одном запросе, и на выполнение следующего запроса ушло около 1,5 часов.
Данные моей таблицы:
--------- ------------ ----- ----- -----
| cust_id | product_id | p_a | p_b | p_c |
--------- ------------ ----- ----- -----
| 1 | a | 1 | 0 | 0 |
| 1 | b | 0 | 2 | 0 |
| 1 | a | 1 | 0 | 0 |
| 1 | c | 0 | 0 | 3 |
| 2 | a | 1 | 0 | 0 |
| 2 | c | 0 | 0 | 3 |
| 2 | c | 0 | 0 | 3 |
| 3 | b | 0 | 1 | 0 |
--------- ------------ ----- ----- -----
Мне нужен следующий результат:
---------------- ----- ----- ----- ------ -------
| No.ofcustomers | p_a | p_b | p_c | P_ab | P_abc |
---------------- ----- ----- ----- ------ -------
| 3 | 0 | 1 | 0 | 0 | 1 |
---------------- ----- ----- ----- ------ -------
p_a возвращает количество клиентов, которые покупают только продукт a. Аналогично, p_b, p_c, p_ab, p_abc.
До сих пор я написал следующий запрос, который дает мне результат. Но есть ли какой-либо способ, которым я мог бы уменьшить время, затрачиваемое на выполнение запроса. Я имею в виду, что 1,5 часа — это реально очень много для любого запроса.
WITH CTE AS
(
SELECT CUST_ID, SUM(P_A) AS PA, SUM(P_B) AS PB, SUM(P_C) AS PC
FROM TABLE
GROUP BY CUST_ID), CTE1 AS
(
SELECT COUNT(DISTINCT CUST_ID) AS NOOFCUSTOMERS,
COUNT(DISTINCT (CASE WHEN PA >= 1 AND PB = 0 AND PC = 0 THEN CUST_ID END)) AS P_A,
COUNT(DISTINCT (CASE WHEN PA = 0 AND PB >= 1 AND PC = 0 THEN CUST_ID END)) AS P_B,
COUNT(DISTINCT (CASE WHEN PA = 0 AND PB = 0 AND PC >= 1 THEN CUST_ID END)) AS P_C,
COUNT(DISTINCT (CASE WHEN PA >= 1 AND PB >= 1 AND PC = 0 THEN CUST_ID END)) AS P_AB,
COUNT(DISTINCT (CASE WHEN PA <> 0 AND PB <> 0 AND PC <> 0 THEN CUST_ID END)) AS P_ABC
FROM CTE)
SELECT *
FROM CTE1;
Комментарии:
1. Похоже, что это «запрос отчета», который обрабатывает огромное количество данных, а не «транзакционный запрос», который обрабатывает минимальное подмножество данных. Запрос отчета обязательно будет медленным.
2. Я предполагаю, что у вас уже есть покрывающий индекс (cust_id, p_a, p_b, p_c). Это первый шаг в оптимизации.
3. Кроме того, если это хранилище, на вашу учетную запись или запрос могут распространяться ограничения и ограничения рабочей нагрузки, определенные для базы данных. Какая у вас операционная система Db2-сервера (Z / OS, i-series, Linux / Unix / Windows, cloud)? Использовали ли вы инструменты оптимизации запросов, поставляемые с вашей платформой версия Db2 ?
4. Вам действительно нужна оперативная информация в течение дня? Возможно, вы можете создать материализованное представление, которое вычисляется ночью, а днем вы получаете информацию за прошлую ночь.
5. Сколько времени требуется для запуска первого CTE? Я предполагаю, что это то, что повышает производительность.
Ответ №1:
Вы можете упростить запрос до:
WITH CTE AS (
SELECT CUST_ID, SUM(P_A) AS PA, SUM(P_B) AS PB, SUM(P_C) AS PC
FROM TABLE
GROUP BY CUST_ID
)
SELECT COUNT(*) AS NOOFCUSTOMERS,
SUM(CASE WHEN PA >= 1 AND PB = 0 AND PC = 0 THEN 1 ELSE 0 END) AS P_A,
SUM(CASE WHEN PA = 0 AND PB >= 1 AND PC = 0 THEN 1 ELSE 0 END) AS P_B,
SUM(CASE WHEN PA = 0 AND PB = 0 AND PC >= 1 THEN 1 ELSE 0 END) AS P_C,
SUM(CASE WHEN PA >= 1 AND PB >= 1 AND PC = 0 THEN 1 ELSE 0 END) AS P_AB,
SUM(CASE WHEN PA <> 0 AND PB <> 0 AND PC <> 0 THEN 1 ELSE 0 END) AS P_ABC
FROM CTE;
Первый CTE суммируется по идентификатору клиента, поэтому они уже уникальны. COUNT(DISTINCT)
дороже, чем другие функции агрегирования, так что это может снизить производительность.
Комментарии:
1. Это сработало как шарм!!! Но почему count(distinct) дороже, чем другие функции агрегирования? Не могли бы вы, пожалуйста, объяснить, как оба работают в короткой заметке?
2. @saikumarvoruganti . . .
count(distinct)
должен проделать гораздо больше работы, чтобы удалить дубликаты.