SQL Как извлечь разные данные из двух столбцов - PullRequest
0 голосов
/ 16 января 2020

У меня есть представление СОТРУДНИК с датами отчетов, которые ежедневно собирают информацию о сотрудниках в течение ряда лет.

Я написал это sql (SAP HANA), поскольку я новичок, но это оказывается очень очень медленным и ресурсоемким и не пригодным для использования, хотя это работает.

Я хочу захват между двумя отчетными датами - все активные сотрудники в последний день и все отозванные сотрудники с конечной датой, превышающей первую отчетную дату и первую дату, когда они были отозваны.

select E.EMPLOYEEID
, E.STATUS
, E.STARTDATE
, E.ENDDATE
, E.REPORTDATE
FROM
(
    SELECT EMPLOYEEID, Max(REPORTDATE) as MaxDate  
    FROM “EMPLOYEETABLE”
        WHERE REPORTDATE>= Date’2019-04-01’ AND REPORTDATE<=Date’2019-07-01’ AND STATUS='Active'
        GROUP BY EMPLOYEEID
) r
LEFT JOIN "EMPLOYEETABLE" E
ON E. EMPLOYEEID =r. EMPLOYEEID AND E.REPORTDATE=r.MaxDate
UNION ALL
select E.EMPLOYEEID
, E.STATUS
, E.STARTDATE
, E.ENDDATE
, E.REPORTDATE
FROM
(
    SELECT EMPLOYEEID, Min(REPORTDATE) as MinDate  
    FROM "EMPLOYEETABLE"
        WHERE REPORTDATE>= date’2019-04-01’ AND REPORTDATE<=Date’2019-07-01’ AND STATUS='Withdrawn' AND ENDDATE>= Date’2019-04-01’
        GROUP BY EMPLOYEEID
) w
LEFT JOIN "EMPLOYEETABLE" E
ON E. EMPLOYEEID =w. EMPLOYEEID AND E.REPORTDATE=w.MinDate

Может ли кто-нибудь помочь написать это более эффективно

Пример набора данных

Reportdate  EmployeeID  startdate   enddate     status
01/04/2019  Steve       12/02/2012  Null        Active
01/04/2019  Don         15/06/2016  Null        Active
01/04/2019  John        14/03/2015  01/04/2019  Withdrawn
01/04/2019  Anna        12/05/2017  Null        Active
02/04/2019  Steve       12/02/2012  Null        Active
02/04/2019  Don         15/06/2016  Null        Active
02/04/2019  John        14/03/2015  01/04/2019  Withdrawn
02/04/2019  Anna        12/05/2017  Null        Active
03/04/2019  Steve       12/02/2012  Null        Active
03/04/2019  Don         15/06/2016  Null        Active
03/04/2019  John        14/03/2015  01/04/2019  Withdrawn
03/04/2019  Anna        12/05/2017  03/04/2019  Withdrawn

Желаемый результат

Reportdate  EmployeeID  startdate   enddate     status
03/04/2019  Steve       12/02/2012  Null        Active
03/04/2019  Don         15/06/2016  Null        Active
01/04/2019  Jon         14/03/2015  01/04/2019  Withdrawn
03/04/2019  Anna        12/05/2017  03/04/2019  Withdrawn

Очевидно, мой набор данных очень большой, так как каждый день появляется один и тот же сотрудник и добавляются новые сотрудники.

Ответы [ 2 ]

1 голос
/ 17 января 2020

Разделение запроса на блоки, каждая из которых охватывает определенный c аспект отчета / query-logi c, как указано davidc2p , очень хорошая идея.

Но для этого нет необходимости во временных таблицах; для этого достаточно общих табличных выражений (CTE) aka «WITH CLAUSE».

Структура запроса

Очень важно понять, что EMPLOYEETABLE является таблицей снимков с захватом STATUS всех сотрудников каждую отдельную дату.

Для запроса существует общее условие - рассматривать только моментальные снимки за определенный период времени.

На основе этого «временного блока» В наборе данных запрос теперь имеет дело с сотрудниками (в отличие от снимков!) и их последним статусом (в «наборе данных с временными рамками»).

Это наблюдение позволяет легко определить MAX() - STATUS для каждый сотрудник. Поскольку для сотрудников "WIDTHDRAWN" существует специальное условие, что их соответствующие ENDDATE должны быть включены или после начала отчетного периода времени, двум отдельным группам / группам / группам сотрудников требуется собственный подзапрос.

Эти два подзапроса возвращают для каждого сотрудника, запись которого (EMPLOYEEID + REPORTDATE служит уникальным ключом для отчета-записи). ords) должны быть возвращены в результате запроса.

Для получения выходных данных две группы сотрудников объединяются (UNION ALL) и затем используются в качестве окончательного фильтра / селектора для всех записей, которые должны быть возвращены из базовой таблицы.

переписанный запрос

with report_base as (
-- all records relevant to the reporting timeframe)
    select
        reportdate, EmployeeID
      , startdate, enddate, status
    from
        employeetable
    where 
          reportdate >= date'2019-04-01' 
      and reportdate <= date'2019-07-01')

, active_employees as (
-- all employees with most recent status in reporting timeframe = ACTIVE 
-- should be DISJUNCT from WITHDRAWN_EMPLOYEES
    select
        employeeid
      , max(reportdate) as reportdate
    from 
        report_base
    group by 
        employeeid
    having
        max(status)='Active')

, withdrawn_employees as (
-- all employees with most recent status in reporting timefrawm = WITHDRAWN
-- the ENDDATE should be on or after the start of the reporting timeframe
-- should be DISJUNCT from ACTIVE_EMPLOYEES)
     select
        employeeid
      , min(reportdate) as reportdate
    from 
        report_base
    where 
        enddate >= date'2019-04-01'
    group by 
        employeeid
    having
        max(status)='Withdrawn')

, report_records as(
-- all records that should be returned)
        select
            employeeid, reportdate
        from 
           active_employees
    union all
        select
            employeeid, reportdate
        from 
            withdrawn_employees)

select
      rb.reportdate, rb.EmployeeID
    , rb.startdate, rb.enddate, rb.status
from
    report_base rb
 inner join report_records rr
 on (rb.employeeid, rb.reportdate)
  = (rr.employeeid, rr.reportdate);

почему он лучше?

Поскольку нет доступных данных объемного теста, я не смог проверить фактические различия производительности во время выполнения между запросом OP и реорганизованной версией.

Однако измененная версия приводит к одному меньшему объединению в EXPLAIN PLAN, что, вероятно, приводит к улучшению производительности и использования памяти.

В дополнение к этому, измененный план намного понятнее относительно того, как данные результата вычисляются и позволяют пошаговую разработку / отладку.

0 голосов
/ 16 января 2020

Я предполагаю, что вы можете создавать временные таблицы и предоставлять решение в SqlServer.

Я также предполагаю, что в вашем запросе указано следующее:

Последняя строка:

E. EMPLOYEEID =r.EMPLOYEEID

is

E. EMPLOYEEID =w.EMPLOYEEID

Второй подзапрос:

AND ENDDATE>= Date’2019-07-01’

is

AND ENDDATE<= Date’2019-07-01’

Таким образом, чтобы разделить, вы должны сохранить результаты вашего подзапроса в временное хранилище, как это:

IF OBJECT_ID('tempdb..#minreportdate') IS NOT NULL
    DROP TABLE #minreportdate

SELECT EMPLOYEEID, Max(REPORTDATE) as MaxDate  
INTO #minreportdate
FROM EMPLOYEETABLE
WHERE REPORTDATE BETWEEN '2019-04-01' AND '2019-07-01' 
AND STATUS = 'Active'
GROUP BY EMPLOYEEID

И

IF OBJECT_ID('tempdb..#maxreportdate') IS NOT NULL
    DROP TABLE #maxreportdate

SELECT EMPLOYEEID, Max(REPORTDATE) as MaxDate  
INTO #maxreportdate
FROM EMPLOYEETABLE
WHERE REPORTDATE BETWEEN '2019-04-01' AND '2019-07-01' 
AND STATUS = 'Withdrawn'
GROUP BY EMPLOYEEID

Затем выполните основной запрос для доступа к вашим двум временным таблицам:

SELECT E.EMPLOYEEID, E.STATUS, E.STARTDATE, E.ENDDATE, E.REPORTDATE
FROM #minreportdate r

    LEFT JOIN "EMPLOYEETABLE" E
    ON  E. EMPLOYEEID   = r.EMPLOYEEID 
    AND E.REPORTDATE    = r.MaxDate

UNION ALL

SELECT E.EMPLOYEEID, E.STATUS, E.STARTDATE, E.ENDDATE, E.REPORTDATE
FROM #maxreportdate w

    LEFT JOIN "EMPLOYEETABLE" E
    ON  E. EMPLOYEEID   = w.EMPLOYEEID 
    AND E.REPORTDATE    = w.MinDate
...