2016-06-08 1 views
1

Я работаю над написанием запроса, который бьет очень большую базу данных нашей компании, для того, чтобы тянуть назад максимальные суммы счетов (A и B, в данном примере) для клиентов. Мы хотим оттянуть максимальный A/B для каждого клиента за последний месяц и максимальный A/B за последний год.Oracle запрос выполняется очень медленно из-за большой набор данных, а отрицательных значениями

Один вопрос, который мы заметили, с нашей базой данных биллинговой путь он хранит «отмененные» счета. Это делается путем добавления второй, отрицательной версии первой платежной записи в таблицу фактурирования. Как так:

enter image description here

В этом случае 41040 был неправильный счет, поэтому был добавлен отрицательный вариант записи. Однако, когда я пытаюсь выбрать максимальное значение в этом столбце, я все равно вернусь 41040 вместо правильного выставленного счета 50. Эта таблица, похоже, не помещает эти неправильные счета каким-либо образом, что облегчит их отфильтровано.

Мое текущее решение было принять максимальное значение столбца ID, как правильный счет. Это делает предположение, что окончательный счет, который был введен в течение месяца, является правильным.

Это, видимо, возвращает правильные данные, но запрос выполняется невероятно медленно на большом наборе данных, и у меня нет доступа к этой таблице, чтобы добавлять или просматривать индексы. Всего насчитывается 98 007 807 строк и 1 596 491 уникальных клиентов. Нужно ли оптимизировать запрос для повышения производительности?

select mth.KY_CUSTOMER_NO,max(QY_MTH_BILLED_A) as QY_MTH_BILLED_A, max(QY_MTH_B) as QY_MTH_BILLING_B, max.MAX_BILLING_A, max.MAX_BILLING_B 
from (
    --Get the max A/B values for the past month 
    select m.* 
    from CUSTOMER_USAGE m 
    where rev_year = to_number(to_char(sysdate,'yyyy')) 
    and rev_mth in (to_number(to_char(add_months(sysdate, -1), 'mm')),to_number(to_char(sysdate,'mm'))) 
    and ID in (select max(ID) from CUSTOMER_USAGE where KY_CUSTOMER_NO = m.KY_CUSTOMER_NO group by rev_mth, rev_year) 
) mth join 
(
    --Get the max A/B values for the past year 
    select KY_CUSTOMER_NO, max(QY_MTH_B) as MAX_BILLING_B, max(QY_MTH_BILLED_A) as MAX_BILLING_A from CUSTOMER_USAGE m 
    where DT_ADDED > current_timestamp - 365 ID in (select max(ID) from CUSTOMER_USAGE where KY_CUSTOMER_NO = m.KY_CUSTOMER_NO group by rev_mth, rev_year) 
    group by KY_CUSTOMER_NO 
) max on mth.KY_CUSTOMER_NO = max.KY_CUSTOMER_NO 
group by mth.KY_CUSTOMER_NO, max.MAX_BILLING_KVA, max.MAX_BILLING_KW 
+0

Какие индексы существуют и каков текущий план запросов? –

ответ

1

Аналитические функции, по-видимому, являются решением.

Я оставил статьи WHERE, поскольку они не нужны для ваших данных образца, но вы должны быть в состоянии добавить их обратно к внутреннему внутреннему виду. Вы также можете использовать EXTRACT(YEAR FROM SYSDATE) вместо преобразования в строку и из нее.

Oracle Setup:

CREATE TABLE customer_usage (id, ky_customer_no, rev_mth, rev_year, qy_mth_billed_a, qy_mth_billed_b) AS 
SELECT 1, 1, 1, 2016, 41040, 0 FROM DUAL UNION ALL 
SELECT 2, 1, 1, 2016, -41040, 0 FROM DUAL UNION ALL 
SELECT 3, 1, 1, 2016,  50, 0 FROM DUAL UNION ALL 
SELECT 4, 1, 1, 2016,  0, 0 FROM DUAL; 

Запрос:

SELECT id, 
     ky_customer_no, 
     rev_mth, 
     rev_year, 
     qy_mth_billed_a, 
     qy_mth_billed_b 
FROM (
    SELECT c.*, 
     ROW_NUMBER() 
      OVER (PARTITION BY ky_customer_no, rev_year, rev_mth 
        ORDER BY total_mth_billed_a DESC) AS rn 
    FROM (
    SELECT c.*, 
      SUM(qy_mth_billed_a) 
      OVER (PARTITION BY ky_customer_no, rev_year, rev_mth, ABS(qy_mth_billed_a) 
        ORDER BY id DESC) AS total_mth_billed_a    
    FROM customer_usage c 
) c 
) 
WHERE rn = 1; 

Выход:

 ID KY_CUSTOMER_NO REV_MTH REV_YEAR QY_MTH_BILLED_A QY_MTH_BILLED_B 
---------- -------------- ---------- ---------- --------------- --------------- 
     3    1   1  2016    50    0 
0

Я попытался ANO но используя большинство настроек @ MT0.

CREATE TABLE customer_usage (id, ky_customer_no, rev_mth, rev_year, qy_mth_billed_a, qy_mth_billed_b) AS 
SELECT 1, 1, 1, 2016, 41040, 0 FROM DUAL UNION ALL 
SELECT 2, 1, 1, 2016, -41040, 0 FROM DUAL UNION ALL 
SELECT 3, 1, 1, 2016,  50, 0 FROM DUAL UNION ALL 
SELECT 4, 1, 1, 2016,  0, 0 FROM DUAL; 

Поскольку мы хотим избавиться от этих значений которых ABS() равно, но имеют разные знаки, я попытался это:

SELECT c.KY_CUSTOMER_NO, c.REV_MTH, c.REV_YEAR, max(qy_mth_billed_a) as qy_mth_billed_a , max(QY_MTH_BILLED_B) as qy_mth_billed_b 
    FROM (
    SELECT c.*, 
      max(qy_mth_billed_a) 
      OVER (PARTITION BY ky_customer_no, rev_year, rev_mth,ABS(qy_mth_billed_a)) AS max_mth_billed_a, 
      min(qy_mth_billed_a) 
      OVER (PARTITION BY ky_customer_no, rev_year, rev_mth,ABS(qy_mth_billed_a)) AS min_mth_billed_a  
    FROM customer_usage c 
) c where max_mth_billed_a+min_mth_billed_a!=0 
group by c.KY_CUSTOMER_NO, c.REV_MTH, c.REV_YEAR; 

Выход это то же самое и, так как вы столкнулись с некоторыми проблемы производительности, я хотел бы попробовать оба подхода:

KY_CUSTOMER_NO REV_MTH REV_YEAR qy_mth_billed_a qy_mth_billed_b 
1 1 1 2016 50 0 

EDIT на самом деле, если считать другую си gns каждого абс (значение), и это нечетное число, я думаю, что он будет работать еще быстрее (требуется только одна функция окна)

SELECT c.KY_CUSTOMER_NO, c.REV_MTH, c.REV_YEAR, max(qy_mth_billed_a) as qy_mth_billed_a , max(QY_MTH_BILLED_B) as qy_mth_billed_b 
    FROM (
    SELECT c.*, 
      count(sign(qy_mth_billed_a)) 
      OVER (PARTITION BY ky_customer_no, rev_year, rev_mth,ABS(qy_mth_billed_a)) AS signo  
    FROM customer_usage c 
) c where mod(signo,2) =1 
group by c.KY_CUSTOMER_NO, c.REV_MTH, c.REV_YEAR 
Смежные вопросы