【问题标题】:SQL How to Extracting different data from two columnsSQL如何从两列中提取不同的数据
【发布时间】:2020-01-17 11:22:40
【问题描述】:

我有一个查看 EMPLOYEE 的报表日期,该报表日期捕获了多年来每天的员工信息。

作为初学者,我已经编写了此 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

显然我的数据集非常大,因为每天都会出现相同的员工并添加新员工。

【问题讨论】:

  • 请提供样本数据和期望的结果。
  • 您有创建临时表的权限吗?通过编写临时结果来划分查询将大大提高性能。
  • 您是否在查询中运行了EXPLAIN PLAN?它可能会提供有关查询执行的更多信息。

标签: sql hana


【解决方案1】:

将查询拆分为单元,每个单元涵盖报告/查询逻辑的特定方面,如 davidc2p 所示。

但是没有必要使用临时表来做到这一点;公用表表达式 (CTE) 又名“WITH CLAUSE”就足够了。

查询结构

对此真正重要的见解是看到EMPLOYEETABLE 是一个快照表,其中包含每个日期所有员工的STATUS

对于查询,一般条件是只考虑某个时间范围内的快照。

基于这个“timeboxed”数据集,查询现在处理员工(而不是快照!)和他们最近的状态(在“timeboxed 数据集中)。

此观察可以轻松确定每个员工的MAX()-STATUS。由于“WIDTHDRAWN”员工有一个特殊条件,即他们各自的ENDDATE 需要在报告时间范围开始时或之后,因此两个不同的员工组/集合/群组需要自己的子查询。

这两个子查询为每个员工返回哪个记录(EMPLOYEEID+REPORTDATE 作为报告记录的唯一键)应作为查询结果报告回来。

为了产生输出,将两个员工组合并 (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 中的一个连接,这可能会转化为性能和内存使用的改进。

除此之外,重构计划更清楚地说明了结果数据的计算方式,并允许逐步开发/调试。

【讨论】:

    【解决方案2】:

    我假设您可以在 SqlServer 中创建临时表并提供解决方案。

    我还将假设您的查询中存在以下错误:

    最后一行:

    E. EMPLOYEEID =r.EMPLOYEEID
    

    E. EMPLOYEEID =w.EMPLOYEEID
    

    第二个子查询:

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

    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
    

    【讨论】:

    • 不幸的是我不能创建临时表。我已经更正了我的查询。我必须使用虚拟数据
    • SAP HANA 和 Microsoft SQL Server 有什么联系?
    • SAP HANA 提供远超 Microsoft SQL 的能力和功能,SAP HANA 所需的服务器规格也更高。但在概念上sql查询逻辑是相似的
    • 只要您想提高性能,就可以分割您的查询。您可能会丢失存储空间,但会大大增加执行时间。
    猜你喜欢
    • 2023-04-01
    • 2018-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多