2015-10-21 3 views
0

Я манипулирую длинный, очень запрошенный стол (> 500 миллионов записей), поэтому очень важно избегать больших запросов.Избегайте нескольких подзапросов

В настоящее время мне нужно получить некоторые значения с условием (объясним лучше в одно мгновение), а затем проверить, находятся ли эти значения в другой группе значений (все это относится к одному полю). Я создаю представление таблицы, используя with.

Итак, вот синтаксис таблицы: (таблица сотрудников)

+--------+-------------+-----------+--------+---------+-----------+ 
| period | employee_id | operation | sub_op | payment | work_zone | 
+--------+-------------+-----------+--------+---------+-----------+ 

Периоды имеют этот формат «YYMM», один период относится к одному месяцу.

Конечно, таблица намного длиннее, чем этот образец, но мне нужны только те поля в запросе. Краткое объяснение того, что мне нужно, а затем сам запрос.

мне нужно, чтобы получить все employee_id в текущем period, со значительным payment (по крайней мере, $ 250) и определенной operation (первой I группы, значение operations с sub_op). Требуемое значение operation равно 97, и в запросе вы увидите, как я его группирую.

Теперь к этим значениям я группирую их по work_zone и сгруппированным значениям operation. И теперь начинаются подзапросы ... Мне нужно:

  1. Все значения, которые не были в прошлом периоде.
  2. Все значения, которые не были в последние 36 периодов (3 года).
  3. Все значения, которые были по меньшей мере в одном из последних 36 периодов.
  4. Все значения, которые были по меньшей мере в одном из последних 36 периодов, но с другой операцией.
  5. Все значения, которые были по крайней мере в одном из последних 36 периодов, но с платежом ниже 250 долларов США.

Итак, вот запрос, который у меня до сих пор. (Я использую в период «1109»)

CREATE OR REPLACE VIEW hired_fired AS 
WITH query_hired_fired AS ( 
    SELECT work_zone, operation, sub_op, employee_id, 
     CASE 
      WHEN operation = 97 THEN 
       CASE 
        WHEN sub_op IN (1,3,5) THEN 'Cookers' 
        WHEN sub_op IN (2,6) THEN 'Waitress' 
        WHEN sub_op IN (4,7,8,9,10) THEN 'Cashier' 
        WHEN sub_op = 11 THEN 'Security' 
        WHEN sub_op IN (12,13) THEN 'Cleaners' 
        ELSE 'Others' 
       END 
     END AS opgroup 
    FROM employee 
    WHERE period = 1109 AND payment >= 250 AND operation = 97 
) 
SELECT 201109 AS periodo, opgroup, work_zone 
(SELECT COUNT(DISTINCT employee_id) FROM query_hired_fired WHERE employee_id NOT IN (SELECT employee_id FROM employee WHERE period = 1108 AND payment >= 250 AND operation = 97)) AS total, 
(SELECT COUNT(DISTINCT employee_id) FROM query_hired_fired WHERE employee_id NOT IN (SELECT employee_id FROM employee WHERE period BETWEEN 0808 AND 1108 AND payment >= 250 AND operation = 97)) AS absolut, 
(SELECT COUNT(DISTINCT employee_id) FROM query_hired_fired WHERE employee_id IN (SELECT employee_id FROM employee WHERE period BETWEEN 0808 AND 1108 AND payment >= 250 AND operation = 97)) AS reincorporated, 
(SELECT COUNT(DISTINCT employee_id) FROM query_hired_fired WHERE employee_id IN (SELECT employee_id FROM employee WHERE period BETWEEN 0808 AND 1108 AND payment >= 250 AND operation != 97)) AS operation_change, 
(SELECT COUNT(DISTINCT employee_id) FROM query_hired_fired WHERE employee_id IN (SELECT employee_id FROM employee WHERE period BETWEEN 0808 AND 1108 AND payment < 250 AND operation = 97)) AS raised, 
FROM query_hired_fired 
GROUP BY work_zone, opgroup 

Итак, мой вопрос ... Есть ли в любом случае я могу сделать этот запрос без всех подзапросов? Я думаю, для этого потребуется несколько часов, и это не похоже на работу с этой таблицей.

Извините, если я что-то не понял, я буду как можно скорее ответить на все вопросы и замечания. Благодарю.

ответ

1

Попробуйте этот запрос:

WITH query_hired_fired AS ( 
    SELECT work_zone, operation, sub_op, employee_id, 
     CASE 
      WHEN operation = 97 THEN 
       CASE 
        WHEN sub_op IN (1,3,5) THEN 'Cookers' 
        WHEN sub_op IN (2,6) THEN 'Waitress' 
        WHEN sub_op IN (4,7,8,9,10) THEN 'Cashier' 
        WHEN sub_op = 11 THEN 'Security' 
        WHEN sub_op IN (12,13) THEN 'Cleaners' 
        ELSE 'Others' 
       END 
     END AS opgroup 
    FROM employee 
) 
SELECT opgroup, work_zone, 
     SUM(x_period_1109 * x_total)   As total, 
     SUM(x_period_1109 * x_absolut)   As absolut, 
     SUM(x_period_1109 * x_reincorporated) As reincorporated, 
     SUM(x_period_1109 * x_operation_change) As operation_change, 
     SUM(x_period_1109 * x_raised)   As raised 
FROM (
    SELECT opgroup, work_zone, employee_id, 
      MAX(CASE WHEN period = 1108 AND payment >= 250 AND operation = 97 THEN 1 ELSE 0 END) as x_total, 
      MAX(CASE WHEN period = 1108 AND payment >= 250 AND operation = 97 THEN 1 ELSE 0 END) as x_absolut, 
      MAX(CASE WHEN period BETWEEN 0808 AND 1108 AND payment >= 250 AND operation = 97 THEN 1 ELSE 0 END) as x_reincorporated, 
      MAX(CASE WHEN period BETWEEN 0808 AND 1108 AND payment >= 250 AND operation != 97 THEN 1 ELSE 0 END) as x_operation_change, 
      MAX(CASE WHEN period BETWEEN 0808 AND 1108 AND payment < 250 AND operation = 97 THEN 1 ELSE 0 END) as x_raised, 
      MAX(CASE WHEN period = '1109' AND payment >= 250 AND operation = 97 THEN 1 ELSE 0 END) As x_period_1109 
    FROM query_hired_fired 
    WHERE period BETWEEN 0808 AND 1109 
    GROUP BY opgroup, work_zone, employee_id 
) x 
GROUP BY work_zone, opgroup 

Это условие в запросе: BETWEEN 1108 AND 0808 всегда принимает значение ложь,
Я думаю, что это должно быть: BETWEEN 0808 AND 1108

+0

Спасибо за исправление, уже отредактированное в вопросе. Спасибо за ответ также – AleOtero93

1

Я несколько похож на Kordirko , но завернутый в один. Внутренний запрос «PreCalc» предназначен для вычисления одной строки на одного сотрудника с флагом 1 или 0, если они удовлетворяют условию.Поскольку все ваши критерии основаны на диапазоне, либо равным 1108 OR (между 0808 и 1108), этот подзапрос получает ВСЕ записи между 0808 и 1108, что упрощает читаемость сложных случаев/при условиях. Единственное условие, которое я применил, было первым, когда вы специально искали точный предшествующий период. Тем не менее, остальные элементы были квалификаторами суммы платежа и являются (или нет) операцией 97. Таким образом, для любого сотрудника флаги будут установлены в 1 или 0 соответственно.

Теперь это применяется к внешнему запросу, выполняя SUM/CASE. Чтобы учесть ваш «NOT IN», я ищу данный флаг = 0 (таким образом, он не соответствовал базовым данным) vs flag = 1, который DID имеет право на базовые данные.

Поскольку предварительный запрос также вычислял «opgroup», все это прекрасно обернуто.

Я бы удостоверился, что ваша таблица имеет индекс на (period, employee_id, work_zone), чтобы помочь оптимизировать. Вы можете пойти дальше с помощью клавиш индекса, чтобы сделать его индексом покрытия, но посмотрите, как это работает в первую очередь.

SELECT 
     201109 AS periodo, 
     work_zone, 
     opgroup, 
     SUM(case when PreCalc.LPOver250 == 0 end) as EmpsNotInLastPeriodOver250, 
     SUM(case when PreCalc.Over250Op97 == 0 end) as EmpsNotInOver250Per97, 
     SUM(case when PreCalc.Over250Op97 == 1 end) as EmpsInOver250Per97, 
     SUM(case when PreCalc.Over250NotOp97 == 1 end) as EmpsOver250NotInOp97, 
     SUM(case when PreCalc.Under250 == 1 end) as EmpsUnder250 
    from 
     (SELECT 
       Employee_ID, 
       work_zone, 
       CASE WHEN operation = 97 THEN 
        CASE WHEN sub_op IN (1,3,5) THEN 'Cookers' 
         WHEN sub_op IN (2,6) THEN 'Waitress' 
         WHEN sub_op IN (4,7,8,9,10) THEN 'Cashier' 
         WHEN sub_op = 11 THEN 'Security' 
         WHEN sub_op IN (12,13) THEN 'Cleaners' 
         ELSE 'Others' 
        END 
       END AS opgroup, 
       MAX(case when period = 1108 
         and payment >= 250 
         and operation = 97 then 1 else 0 end) as LPOver250, 
       MAX(case when payment >= 250 
         and operation = 97 then 1 else 0 end) as Over250Op97, 
       MAX(case when payment >= 250 
         and operation != 97 then 1 else 0 end) as Over250NotOp97, 
       MAX(case when payment < 250 
         and operation = 97 then 1 else 0 end) as Under250 
      from 
       employee 
      where 
       period between 0808 and 1108 
      group by 
       Employee_ID, 
       work_zone, 
       opgroup) PreCalc 
    group by 
     work_zone, 
     opgroup 
Смежные вопросы