【问题标题】:Syntax issue with CTE to Pivot functionCTE 到 Pivot 函数的语法问题
【发布时间】:2020-10-16 01:32:31
【问题描述】:

我在获取从变量到 CTE 到 Pivot 的语法正确时遇到问题。我正在将 sp_whoisactive 捕获到一个表中,现在正在按区域聚合 CPU 和持续时间。然后,我想对这些结果进行透视,以便在 Excel 中轻松绘制图表。我的解决方法是输出到 #Temp 表,这会在尝试输出到 Excel 工作表时导致 SSIS 出现问题。

为简洁起见,我缩减了 CASE 语句。大约有 30 个地区。

如何将其包装在 CTE 中,以便可以使用开始和结束变量日期进行透视并消除 #Temp 表?

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


DECLARE @StartDate DATETIME, @EndDate DATETIME
SET @StartDate = DATEADD(hh,23,DATEADD(dd,DATEDIFF(dd,0,GETDATE()-1),0))
SET @EndDate =  DATEADD(hh,9,DATEADD(dd,DATEDIFF(dd,0,GETDATE()-0),0))

;WITH CTE AS
(
select --login_name, login_time, session_id,
(Substring([dd hh:mm:ss.mss],1,2) * 86400) + (Substring([dd hh:mm:ss.mss],4,2) * 3600) + (Substring([dd hh:mm:ss.mss],7,2) * 60) + (Substring([dd hh:mm:ss.mss],10,2))  as TotalSec
  ,t.*
from (
    select w.*,
        row_number() over(partition by session_id, login_time, login_name order by collection_time desc) rn
    from Regional.DBA.WhoIsActive w
) t 
where rn = 1 and login_time between  @StartDate and @EndDate 
), RESULT as
(
select  CTE.login_name  ,   SUM(CTE.TotalSec ) as UserSecTot, SUM(CONVERT(int,REPLACE(cte.CPU,',',''))) as UserCPUTot from CTE 
Group by CTE.login_name 
) 
select  @StartDate as StartDate, @EndDate as EndDate,
CASE
    WHEN login_name = 'sa' then 'sa'
    WHEN ParseName(Replace(login_name,'\','.'),2) like '%vhamaster%' and login_name like '%Region01%' or login_name like '%_V1%' THEN 'Region01'
    WHEN ParseName(Replace(login_name,'\','.'),2) like '%vhamaster%' and login_name like '%Region02%' or login_name like '%_V2%' THEN 'Region02'
    WHEN ParseName(Replace(login_name,'\','.'),2) like '%vhamaster%' then 'z' + ParseName(Replace(login_name,'\','.'),1)
    WHEN ParseName(Replace(login_name,'\','.'),2) like '%Reg01%' then 'Region01'
    WHEN ParseName(Replace(login_name,'\','.'),2) like '%Reg02%' then 'Region02'

ELSE ParseName(Replace(login_name,'\','.'),2) 
END as Domain , login_name,  DBA.udf_SecToHHMMSS(UserSecTot) as Total_Time, UserSecTot, UserCPUTot 
into #MySP
from RESULT

select * from #MySP 

select * from
(select Domain, UserCPUTot from #MySP) as sourcetable
PIVOT(
    sum(UserCPUTot) 
    FOR Domain IN (
        [Region01], 
        [Region02] 

        )
) AS pivot_table;

select * from
(select Domain, UserSecTot from #MySP) as sourcetable
PIVOT(
    sum(UserSecTot) 
    FOR Domain IN (
        [Region01], 
        [Region02]
 
        )
) AS pivot_table;

【问题讨论】:

    标签: sql sql-server pivot


    【解决方案1】:
    DECLARE @StartDate DATETIME, @EndDate DATETIME
    SET @StartDate = DATEADD(hh,23,DATEADD(dd,DATEDIFF(dd,0,GETDATE()-1),0))
    SET @EndDate =  DATEADD(hh,9,DATEADD(dd,DATEDIFF(dd,0,GETDATE()-0),0))
    
    ;WITH CTE AS
    (
        select --login_name, login_time, session_id,
        (Substring([dd hh:mm:ss.mss],1,2) * 86400) + (Substring([dd hh:mm:ss.mss],4,2) * 3600) + (Substring([dd hh:mm:ss.mss],7,2) * 60) + (Substring([dd hh:mm:ss.mss],10,2))  as TotalSec
          ,t.*
                from (
                    select w.*,
                        row_number() over(partition by session_id, login_time, login_name order by collection_time desc) rn
                    from Regional.DBA.WhoIsActive w
                ) t 
        where rn = 1 and login_time between  @StartDate and @EndDate 
    ), RESULT as
    (
        select  CTE.login_name  ,   SUM(CTE.TotalSec ) as UserSecTot, SUM(CONVERT(int,REPLACE(cte.CPU,',',''))) as UserCPUTot from CTE 
        Group by CTE.login_name 
    ) 
    
    , cteoutput as (
    select  @StartDate as StartDate, @EndDate as EndDate,
    CASE
        WHEN login_name = 'sa' then 'sa'
        WHEN ParseName(Replace(login_name,'\','.'),2) like '%vhamaster%' and login_name like '%Region01%' or login_name like '%_V1%' THEN 'Region01'
        WHEN ParseName(Replace(login_name,'\','.'),2) like '%vhamaster%' and login_name like '%Region02%' or login_name like '%_V2%' THEN 'Region02'
        WHEN ParseName(Replace(login_name,'\','.'),2) like '%vhamaster%' then 'z' + ParseName(Replace(login_name,'\','.'),1)
        WHEN ParseName(Replace(login_name,'\','.'),2) like '%Reg01%' then 'Region01'
        WHEN ParseName(Replace(login_name,'\','.'),2) like '%Reg02%' then 'Region02'
    
    ELSE ParseName(Replace(login_name,'\','.'),2) 
    END as Domain , login_name,  DBA.udf_SecToHHMMSS(UserSecTot) as Total_Time, UserSecTot, UserCPUTot 
    
    from RESULT
    )
    
    
    select * from
    (select Domain, UserCPUTot from cteoutput) as sourcetable
    PIVOT(
        sum(UserCPUTot) 
            FOR Domain IN (
            [Region01], 
            [Region02] 
    
            )
    ) AS pivot_table;
    

    【讨论】:

    • 感谢您的反馈。让我试试然后回来。
    • 优秀!现在来研究我是如何出错的。真的就像使用 CASE 进行最后一次选择一样简单吗?那是因为 cteoutput 没有被调用,因此在 PIVOT 中有效吗?
    【解决方案2】:

    如果临时表导致问题,那么一种解决方案是创建物理表。这可以防止不必要地重复每个枢轴的代码。有很多方法可以做到这一点,一种方法是创建一个“report_datetimes”表来存储唯一的日期时间范围,以及一个“report_whoisactive”表来存储域数据。然后,SQL 脚本可以在必要时删除现有行、插入新行并创建数据透视表。像这样。

    DDL

    drop table if exists report_datetimes;
    go
    create table report_datetimes(
      r_id                      int identity(1,1) primary key not null,
      start_dtm                 datetime not null,
      end_dtm                   datetime not null);
    go
    create unique index ndx_unq_start_end_dtm on report_datetimes(start_dtm, end_dtm);
    go
    
    drop table if exists report_whoisactive;
    go
    create table report_whoisactive(
      w_id                      int identity(1,1) primary key not null,
      r_id                      int not null references report_datetimes(r_id),
      domain                    varchar(10) not null,
      login_name                varchar(256) not null,
      Total_Time                time,
      UserSecTot                time,
      UserCPUTot                time);
    go
    create unique index ndx_unq_r_domain_login on report_whoisactive(r_id, domain, login_name);
    go
    

    脚本第 1 部分:填充表格

    declare
      @start_dtm    datetime=dateadd(hh,23,dateadd(dd,datediff(dd,0,getdate()-1),0)),
      @end_dtm      datetime=dateadd(hh,9,dateadd(dd,datediff(dd,0,getdate()-0),0)),
      @r_id         int;
    
    /* get r_id if exists (and delete existing data), create if it doesn't */
    select @r_id=r_id
    from report_datetimes
    where start_dtm=@start_dtm
          and end_dtm=@end_dtm;
    if @@rowcount=0
        begin
            insert report_datetimes(start_dtm, end_dtm) values (@start_dtm, @end_dtm);
            select @r_id=cast(scope_identity() as int);
        end
    else
        delete report_whoisactive where r_id=@r_id;
    
    ;WITH 
    CTE AS (
        select (Substring([dd hh:mm:ss.mss],1,2) * 86400) + 
               (Substring([dd hh:mm:ss.mss],4,2) * 3600) + 
               (Substring([dd hh:mm:ss.mss],7,2) * 60) + 
               (Substring([dd hh:mm:ss.mss],10,2))  as TotalSec, t.*
        from (select w.*,
                     row_number() over(partition by session_id, login_time, login_name 
                                       order by collection_time desc) rn
              from Regional.DBA.WhoIsActive w) t 
        where rn = 1
              and login_time between @StartDate and @EndDate), 
    RESULT as (
        select  CTE.login_name, SUM(CTE.TotalSec ) as UserSecTot,
                SUM(CONVERT(int,REPLACE(cte.CPU,',',''))) as UserCPUTot
        from CTE 
        Group by CTE.login_name) 
    insert report_whoisactive(r_id, domain, login_name, Total_Time,UserSecTot, UserCPUTot)
    select @r_id,
        CASE
            WHEN login_name = 'sa' then 'sa'
            WHEN ParseName(Replace(login_name,'\','.'),2) like '%vhamaster%' and login_name like '%Region01%' or login_name like '%_V1%' THEN 'Region01'
            WHEN ParseName(Replace(login_name,'\','.'),2) like '%vhamaster%' and login_name like '%Region02%' or login_name like '%_V2%' THEN 'Region02'
            WHEN ParseName(Replace(login_name,'\','.'),2) like '%vhamaster%' then 'z' + ParseName(Replace(login_name,'\','.'),1)
            WHEN ParseName(Replace(login_name,'\','.'),2) like '%Reg01%' then 'Region01'
            WHEN ParseName(Replace(login_name,'\','.'),2) like '%Reg02%' then 'Region02'
    
        ELSE ParseName(Replace(login_name,'\','.'),2) 
        END as Domain , login_name,  DBA.udf_SecToHHMMSS(UserSecTot) as Total_Time, UserSecTot, UserCPUTot 
    from RESULT;
    

    脚本第 2 部分:引用 r_id 的数据透视表

    select * from
    (select Domain, UserCPUTot from report_whoisactive where r_id=@r_id) as sourcetable
    PIVOT(
        sum(UserCPUTot) 
        FOR Domain IN (
            [Region01], 
            [Region02] 
    
            )
    ) AS pivot_table;
    
    select * from
    (select Domain, UserSecTot from report_whoisactive where r_id=@r_id) as sourcetable
    PIVOT(
        sum(UserSecTot) 
        FOR Domain IN (
            [Region01], 
            [Region02]
     
            )
    ) AS pivot_table;
    

    【讨论】:

    • '已经考虑过这一点,如果不能解决语法问题,可能会走这条路。我只是创建一个阶段表并每次截断它而不是重新创建。
    • 是的,这很有意义,并且与现有流程更匹配。我不确定是否有必要添加密钥和累积数据,所以我只是保持谨慎。关于语法,不可能重复使用 CTE 来创建单独的枢轴 afaik。如果您想更新此答案,请告诉我
    • 实际上 - 我正在删除我的答案(因为它不能解决您的基本问题)。这是一个更好的解决方案。 PS。有趣的事实......你不能对自己的答案投反对票!
    猜你喜欢
    • 1970-01-01
    • 2021-04-14
    • 1970-01-01
    • 2013-06-24
    • 2012-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多