多个CTE和分组依据

时间:2019-01-25 00:59:50

标签: sql sql-server

我正在尝试使用以下查询计算两个日期之间的差值

with cte (clientid, calls)
as
(
select clientid, max(isnull(cast(calls as int),0)) as calls from STATS
where clientid != 0
group by CLIENTID), 
cteb (minCal, calls, clientid) 
as
(
select calendar as minCal, (cast(calls as int)) as calls, CLIENTID from STATS
)
select min(CONVERT(datetime, convert(varchar(10), minCalendar))) as dayHighCall, cte.clientid, max(cte.calls) as highestCall from cte
join cteb 
on cteb.clientid = cte.clientid
where cteb.tot = cte.totalcalls
group by cte.clientid)

它返回此输出

dayHighCall             clientid    highestCall
2017-11-27 00:00:00.000 2200        2
2017-11-17 00:00:00.000 2500        65

我希望它返回另一个日期,因此我将其写为上述查询的扩展-

cted as
(
select min(CONVERT(datetime, convert(varchar(10), CALENDAR))) as mincal from STATS
group by CLIENTID
)

现在,我希望通过以下查询获得结果-

select cte.clientid,
    DATEDIFF(day,
    ctec.dayHighCall,
    mincal)
    as datedifference
 from cte, ctec, cted
 group by cte.clientid

这会在ctec.dayHighCallmincal上引发错误,因为它们既不是集合也不是子查询的一部分。如何修改此查询,以便得到clientiddatediffcalls的结果。

2 个答案:

答案 0 :(得分:1)

我猜您有一个STATS表,每个客户(CLIENTID)和日期(calendar)最多有一行。

我不得不用更清晰的名称和注释(修正不匹配的名称,从而使查询在此过程中无效)重新编写代码,以了解您要执行的操作:

with daily_stats as (
  select clientid
       , isnull(cast(calls as int),0) as num_calls
       -- whatever the format of "calendar" is, you made sure this converted it to datetime properly
       , convert(datetime, convert(varchar(10), calendar)) as date
  from STATS
  where clientid != 0
)
, max_calls_for_client as ( -- this was "cte"
  select clientid, max(calls) as max_daily_calls from daily_stats group by clientid
)
, date_with_highest_calls as ( -- this was "ctec", presumably - the query for which you provided an example result
  select a.clientid
       , min(b.date) as dayHighCall -- the first day with the highest number of calls
       , max(a.max_daily_calls) as max_daily_calls
  from max_calls_for_client a
  join daily_stats b
    on a.clientid = b.clientid
   and a.max_daily_calls = b.num_calls -- get the date(s) with the maximum number of calls
  group by a.clientid
)
, first_call_date as ( -- "cted"
  select clientid, min(date) as min_call_date from daily_stats group by clientid
)
select max_calls_for_client.clientid
     , DATEDIFF(day,
                date_with_highest_calls.dayHighCall,
                first_call_date.min_call_Date) as datedifference
from max_calls_for_client
   , date_with_highest_calls
   , first_call_date
group by max_calls_for_client.clientid

如果我猜对了,那么通过加入客户机ID可以很容易地解决您的最后一点问题:您有三个“表”,每个表都按客户机ID分组,需要将它们组合在一起,所以这应该起作用:

from max_calls_for_client a
join date_with_highest_calls b on a.clientid = b.clientid
join first_call_date c on a.clientid = c.clientid

还请注意,您可以分两个步骤执行此操作,而不是四个步骤:

  1. 您可以将first_call_datemax_calls_for_client合并为一个(我们称其为client_stats),因为两者都是clientid上的简单聚合。
  2. 然后,您可以在同一查询中计算找到呼叫次数最多的日期的天数:

    select c.clientid
         , min(d.date) as dayHighCall -- the first day with the highest number of calls
         -- aggregations are no-op here, as `client_stats` has a single value for each clientid
         , max(c.min_call_date) as max_daily_calls
         , max(c.max_daily_calls) as max_daily_calls
         , DATEDIFF(day,
                    min(d.date), -- the first day with the highest number of calls
                    max(c.min_call_date)
                   ) as datedifference
    
    from client_stats c
    join daily_stats d
      on c.clientid = d.clientid
     and c.max_daily_calls = d.num_calls
    group by c.clientid
    

答案 1 :(得分:1)

如果您使用的是SQL Server 2012或更高版本,则不需要CTE,则可以使用适当的分区通过FIRST_VALUE函数解决此问题

SELECT DISTINCT 
    ClientID, 
    FIRST_VALUE(Calendar) OVER (PARTITION BY ClientID ORDER BY Calls DESC) AS dayHighCall, 
    MAX(Calls) OVER (PARTITION BY ClientID) AS highestCall,
    DATEDIFF(DAY, MIN(Calendar) OVER (), FIRST_VALUE(Calendar) OVER (PARTITION BY ClientID ORDER BY Calls DESC)) AS datedifference
FROM STATS
ORDER BY ClientID

如果您使用的是旧版SQL Server,则可以使用CTE和CROSS APPLY

;WITH CTE AS (
    SELECT DISTINCT ClientID, MAX(Calls) OVER (Partition BY ClientID) AS Calls, MIN(Calendar) OVER () AS minCalendar
    FROM STATS
),
SELECT 
    ClientID, 
    dayHighCall.Calendar AS dayHighCall,
    Calls AS highestCall,
    DATEDIFF(day, minCalendar, dayHighCall.Calendar) datedifference
FROM CTE
CROSS APPLY (
    SELECT TOP 1 Calendar FROM STATS S WHERE S.ClientID = CTE.ClientID AND S.Calls = CTE.Calls 
) dayHighCall
ORDER BY ClientID

PS。我假设您的minCal用于整个表,如果您还需要ClientID,则可以将Partition By添加到两个Over()子句中的任何一个