Свертывание строк набора результатов с последовательными значениями - PullRequest
1 голос
/ 23 января 2012

У меня есть запрос, который генерирует праздники, которые человек зарегистрировал в своем расписании. Каждый день в расписании представляет собой отдельную запись, но если вы берете неделю отпуска (с 26 декабря 2011 г. по 30 декабря 2011 г.), это должно быть представлено запросом в одном блоке (1 строка).

Вот мой запрос, немного упрощенный для вопроса:

SELECT -1 ord_li_pers_plan_id
       ,ts.person_id person_id
       ,-1 order_line_id
       ,ts.timesheet_dt start_dt
       ,ts.timesheet_dt end_dt
       ,'Vacation' project
       ,null color
       ,1013011 planning_type
FROM timesheets ts
  JOIN persons pers ON ts.person_id = pers.person_id
  JOIN person_holidays per_hol ON 
    ts.person_holiday_id = per_hol.person_holiday_id
  JOIN v_holiday_types hoty_peho ON 
    per_hol.holiday_type_id = hoty_peho.holiday_type_id
WHERE ts.person_holiday_id IS NOT NULL
AND per_hol.holiday_type_id IN (SELECT holiday_type_id FROM v_holiday_types)
AND ts.person_id = :p_person_id

И вот что я получаю сейчас:

-1  11182   -1  30-DEC-11   30-DEC-11   Vacation    1013011
-1  11182   -1  29-DEC-11   29-DEC-11   Vacation    1013011
-1  11182   -1  28-DEC-11   28-DEC-11   Vacation    1013011
-1  11182   -1  27-DEC-11   27-DEC-11   Vacation    1013011
-1  11182   -1  26-DEC-11   26-DEC-11   Vacation    1013011
-1  11182   -1  31-OCT-11   31-OCT-11   Vacation    1013011
-1  11182   -1  02-SEP-11   02-SEP-11   Vacation    1013011
-1  11182   -1  29-JUL-11   29-JUL-11   Vacation    1013011
-1  11182   -1  22-JUL-11   22-JUL-11   Vacation    1013011
-1  11182   -1  25-APR-11   25-APR-11   Vacation    1013011

ОБНОВЛЕНИЕ:

Вместо этого запрос должен вывести следующее:

-1  11182   -1  26-DEC-11   30-DEC-11   Vacation    1013011
-1  11182   -1  31-OCT-11   31-OCT-11   Vacation    1013011
-1  11182   -1  02-SEP-11   02-SEP-11   Vacation    1013011
-1  11182   -1  29-JUL-11   29-JUL-11   Vacation    1013011
-1  11182   -1  22-JUL-11   22-JUL-11   Vacation    1013011
-1  11182   -1  25-APR-11   25-APR-11   Vacation    1013011

Обратите внимание на первый ряд. Теперь он представляет весь отпуск.

ОБНОВЛЕНИЕ 2

Мой клиент предложил другую идею. Этот запрос связан с расписанием со всеми проектами, которым назначен человек. Даты проекта изначально были сохранены в виде блока (1 запись за диапазон дат, например, 01.01.2012 - 01.10.2012 => 1 запись). Теперь клиент хочет сохранить все даты в виде отдельных записей. Это не сложно сделать, и это работает.

Проблема, с которой я сейчас сталкиваюсь, заключается в том, что group by больше не работает, потому что ord_li_pers-plan_id - это PK.

Прилагается запрос. Это довольно большой.

    SELECT person_id ,
    order_line_id ,
    MIN(start_dt) start_dt ,
    MAX(end_dt) end_dt ,
    project ,
    color ,
    planning_type,
    ord_li_pers_plan_id
  FROM
    (SELECT ord_li_pers_plan_id ,
      person_id ,
      order_line_id ,
      start_dt ,
      end_dt ,
      project ,
      color ,
      planning_type ,
      SUM(gap) OVER (PARTITION BY person_id ORDER BY start_dt) contiguous_grp
    FROM
      (SELECT ord_li_pers_plan_id ,
        person_id ,
        order_line_id ,
        start_dt ,
        end_dt ,
        project ,
        color ,
        planning_type ,
        CASE
          WHEN lag(end_dt) over(PARTITION BY person_id ORDER BY start_dt) + 1 >= start_dt
          THEN 0
          ELSE 1
        END gap
      FROM
        (SELECT ord_li_pers_plan_id ,
          person_id ,
          order_line_id ,
          start_dt ,
          end_dt ,
          project ,
          color ,
          planning_type
        FROM
          (SELECT op.ord_li_pers_plan_id ord_li_pers_plan_id ,
            p.person_id person_id ,
            p.order_line_id order_line_id ,
            op.start_dt start_dt ,
            op.end_dt end_dt ,
            pl$planning.prep_tooltip(NVL2(ord.end_customer_id, end_cus.name, cus.name)
            || ' - '
            || NVL2(ord_li.project_cd,ord_li.project_cd,ord.project_cd)
            || ' - '
            || func_tp.name
            || ' - '
            || ROUND((con_tp.contract_tp / 5), 2)
            ||' - '
            || bl$gen_codes.Name_by_Code_Id (op.planning_type)) project ,
            olc.color color ,
            op.planning_type planning_type
          FROM order_line_person_planning op
          JOIN order_line_persons p
          ON p.ORDER_LINE_PERSON_ID = op.ORDER_LINE_PERSON_ID
          JOIN order_lines ord_li
          ON ord_li.order_line_id = p.order_line_id
          JOIN orders ord
          ON ord.order_id = ord_li.order_id
          LEFT JOIN order_line_colors olc
          ON olc.order_line_id = ord_li.order_line_id
          JOIN customers cus
          ON ord.customer_id=cus.customer_id
          LEFT JOIN customers end_cus
          ON ord.end_customer_id=end_cus.customer_id
          LEFT JOIN v_contract_types con_tp
          ON ord.contract_type_id=con_tp.contract_type_id
          JOIN v_function_types func_tp
          ON ord_li.function_id=func_tp.function_id
          UNION
          SELECT -1 ord_li_pers_plan_id ,
            ts.person_id person_id ,
            -1 order_line_id ,
            ts.timesheet_dt start_dt ,
            ts.timesheet_dt end_dt ,
            'Vacation' project ,
            '#99FF33' color ,
            -1 planning_type
          FROM hrm_iadvise.timesheets ts
          JOIN hrm_iadvise.persons pers
          ON ts.person_id = pers.person_id
          JOIN hrm_iadvise.person_holidays per_hol
          ON ts.person_holiday_id = per_hol.person_holiday_id
          JOIN hrm_iadvise.v_holiday_types hoty_peho
          ON per_hol.holiday_type_id  = hoty_peho.holiday_type_id
          WHERE ts.person_holiday_id IS NOT NULL
          UNION
          SELECT -1 ord_li_pers_plan_id ,
            per_hol.person_id person_id ,
            -1 order_line_id ,
            hol.dt start_dt ,
            hol.dt end_dt ,
            'Vacation' project ,
            '#99FF33' color ,
            -1 planning_type
          FROM holidays hol
          JOIN person_holidays per_hol
          ON per_hol.holiday_type_id = hol.holiday_type_id
          JOIN countries coty
          ON coty.country_id    = hol.country_id
          WHERE coty.country_cd = 150
          )
        )
      )
    )
  GROUP BY person_id ,
    order_line_id ,
    project ,
    color ,
    planning_type ,
    contiguous_grp,
    ord_li_pers_plan_id;

Первый запрос получает всех проектов, которым назначен человек Второй запрос получает все праздники, которые человек уже зарегистрировал Третий запрос получает всех национальных праздников

Заранее спасибо

Ответы [ 2 ]

2 голосов
/ 23 января 2012

Вы можете использовать аналитику. Данные читаются только один раз:

SQL> WITH your_query AS (
  2     SELECT 11182 person_id, to_date('30-DEC-11') start_dt,
  3            to_date('30-DEC-11') end_dt FROM dual
  4  UNION ALL SELECT 11182,to_date('29-DEC-11'),to_date('29-DEC-11') FROM DUAL
  5  UNION ALL SELECT 11182,to_date('28-DEC-11'),to_date('28-DEC-11') FROM DUAL
  6  UNION ALL SELECT 11182,to_date('27-DEC-11'),to_date('27-DEC-11') FROM DUAL
  7  UNION ALL SELECT 11182,to_date('26-DEC-11'),to_date('26-DEC-11') FROM DUAL
  8  UNION ALL SELECT 11182,to_date('31-OCT-11'),to_date('31-OCT-11') FROM DUAL
  9  UNION ALL SELECT 11182,to_date('02-SEP-11'),to_date('02-SEP-11') FROM DUAL
 10  UNION ALL SELECT 11182,to_date('29-JUL-11'),to_date('29-JUL-11') FROM DUAL
 11  UNION ALL SELECT 11182,to_date('22-JUL-11'),to_date('22-JUL-11') FROM DUAL
 12  UNION ALL SELECT 11182,to_date('25-APR-11'),to_date('25-APR-11') FROM DUAL
 13  )
 14  SELECT person_id, MIN(start_dt) start_dt, MAX(end_dt) end_dt
 15    FROM (SELECT person_id, start_dt, end_dt,
 16                 SUM(gap) over(PARTITION BY person_id
 17                               ORDER BY start_dt) contiguous_grp
 18             FROM (SELECT person_id, start_dt, end_dt,
 19                           CASE WHEN lag(end_dt) over(PARTITION BY person_id
 20                                     ORDER BY start_dt) + 1 >= start_dt
 21                              THEN 0 ELSE 1
 22                           END gap
 23                      FROM (SELECT * FROM your_query)))
 24   GROUP BY person_id, contiguous_grp
 25   ORDER BY 1, 2 DESC;
 PERSON_ID START_DT  END_DT
---------- --------- ---------
     11182 26-DEC-11 30-DEC-11
     11182 31-OCT-11 31-OCT-11
     11182 02-SEP-11 02-SEP-11
     11182 29-JUL-11 29-JUL-11
     11182 22-JUL-11 22-JUL-11
     11182 25-APR-11 25-APR-11
1 голос
/ 23 января 2012

Для этого вам нужно использовать иерархический запрос.Используйте что-то вроде этого:

SELECT
    person_id,
    person_holiday_id,
    MIN(BEGIN),
    END
FROM
(
    SELECT
        person_id,
        person_holiday_id,
        connect_by_root(timesheet_dt) AS BEGIN,
        timesheet_dt AS END,
        connect_by_isleaf AS is_leaf
    FROM
        timesheets
        CONNECT BY
            PRIOR TRUNC(timesheet_dt) + 1 = TRUNC(timesheet_dt)
            AND PRIOR person_id = person_id
)
WHERE
    is_leaf = 1
GROUP BY
    person_id,
    person_holiday_id,
    END
;

Возвращает одну строку за период продолжительности отпуска на пользователя.Поместите это в представление и используйте его вместо таблицы timesheets в запросе, который вы разместили в своем вопросе.

Обратите внимание:

  1. Этот запросне будет иметь максимальной производительности, потому что каждая строка в timesheets используется как корневая строка иерархического запроса и, возможно, как листовая строка и промежуточная строка.Это можно увидеть, сравнив запуск подзапроса и сравнение количества возвращаемых строк с тем, сколько строк возвращает полный запрос.
  2. Вы можете повысить производительность, если поместите индекс в timesheet_dt и убедитесь, чточто он никогда не содержит часть времени - тогда вы можете избавиться от функций TRUNC.
  3. Если в таблицу timesheets не введены субботы и воскресенья, максимальный период отпуска, который вернет этот оператор, равен единице.неделю с понедельника по пятницу.Если это проблема, предложение CONNECT BY необходимо скорректировать.Однако это не решит проблемы, связанные с тем, что праздничные дни не будут включены в таблицу timesheets ...
...