OVER子句

时间:2017-02-13 09:57:04

标签: sql sql-server database tsql

我曾经拥有以下代码,以便获得所有' - '对所有客户的行动:

with
T1 as
(
select
    [Contract] = 'Contract1',
    [Customer] = 'Customer4',
    [Date] = '2017-01-01',
    [Action] = '+'

union all 

select
    [Contract] = 'Contract1',
    [Customer] = 'Customer6',
    [Date] = '2017-01-02',
    [Action] = '+'

union all 

select
    [Contract] = 'Contract1',
    [Customer] = 'Customer4',
    [Date] = '2017-01-03',
    [Action] = '-'

union all 

select
    [Contract] = 'Contract1',
    [Customer] = 'Customer4',
    [Date] = '2017-01-04',
    [Action] = '+'

union all 

select
    [Contract] = 'Contract1',
    [Customer] = 'Customer4',
    [Date] = '2017-01-05',
    [Action] = '-'

union all 

select
    [Contract] = 'Contract1',
    [Customer] = 'Customer6',
    [Date] = '2017-01-06',
    [Action] = '-'

union all 

select
    [Contract] = 'Contract1',
    [Customer] = 'Customer8',
    [Date] = '2017-01-07',
    [Action] = '+'

union all 

select
    [Contract] = 'Contract1',
    [Customer] = 'Customer8',
    [Date] = '2017-01-08',
    [Action] = '-'

union all 

select
    [Contract] = 'Contract1',
    [Customer] = 'Customer4',
    [Date] = '2017-01-09',
    [Action] = '+'
)

select 
    [Customer],
    [Date]  
from T1
where [Action] = '-'

现在我需要通过合同字段来完成。这意味着我必须在最后一次操作时返回合同和日期值 - ' - '所有客户都使用' +'在该日期之前的行动。非常期望的输出应该是:

Date         | Contract
------------ | ------ 
2017-01-06   | Contract1   
2017-01-08   | Contract1

预期算法应如下所示:

[PlusDC] = count(distinct iif([Action] = '+',Customer,NULL)) over (partition by [Contract] order by [Date])
[MinusDC] = count(distinct iif([Action] = '-',Customer,NULL)) over (partition by [Contract] order by [Date])

可是:

  • 无论如何它都不起作用。
  • 即使它有效,即使[PlusDC] = [MinusDC],它也会返回值2017-01-09,这是不正确的。

粗略地说,我必须针对所有客户检查以下代码:

  1. [动作] =' - '对于当前行。

  2. 滞后([行动],1)=' - ' (或如果客户记录在当天晚些时候出现,则为每个客户。)

  3. 更新:为了使事情更加清晰,我已经以列为导向查看了我的数据:

    -----------------------------------------------------------------------
    | Date         | Contract  | Customer4  | Customer6 | Customer8 | All |
    | ------------ | --------- | ---------  | --------- | --------- | --- |
    | 2017-01-01   | Contract1 |     +      |           |           |     |
    | 2017-01-02   | Contract1 |            |     +     |           |     |
    | 2017-01-03   | Contract1 |            |           |           |     |
    | 2017-01-04   | Contract1 |     -      |           |           |     | <-- Customer6 still has a '+'
    | 2017-01-05   | Contract1 |     +      |           |           |     | 
    | 2017-01-06   | Contract1 |     -      |           |           |     | <-- Customer6 still has a '+'
    | 2017-01-07   | Contract1 |            |     -     |           |  -  | <-- All customers has '-' or null as a last action
    | 2017-01-08   | Contract1 |            |           |     +     |     |
    | 2017-01-09   | Contract1 |            |           |     -     |  -  | <-- All customers has '-' or null as a last action
    -----------------------------------------------------------------------
    

    All列表示所有客户的实际状态(我需要的行)。您可能会注意到2017-01-04和2017-01-06并不是真实的 - &#39; - &#39;在合同字段内。 Contract1尚未关闭,它仍然打开了Customer6。如果每份合同都有一定数量的客户,那就很容易了。无数呢?

    有任何实际建议吗?

3 个答案:

答案 0 :(得分:1)

我认为你可以像这样使用ROW_NUMBER()

;with tt as (
    select T1.[Contract], T1.[Date], T1.[Action], t.[Customer], t.[Action] lAction, t.[Date] lDate
        -- this `rn` will give me the last action for each other customer older that each Date
        , row_number() over (partition by T1.[Contract], T1.[Date], t.[Customer] order by t.[Date] desc) rn
    from T1
    -- I use this self left join to gather data with:
    left join T1 t
        on T1.[Contract] = t.[Contract]    -- same Contract
        and T1.[Date] > t.[Date]           -- older than current date
        and T1.[Customer] != t.[Customer]  -- for other customers
    -- So I will have actions of other customer older than each date
)
select [Contract], [Date]
from T1
-- I just check if there is not any data in `tt` with:
where not exists(
    select 1
    from tt 
    where tt.[Contract] = T1.[Contract]   -- same contract
      and tt.[Date] = T1.[Date]           -- same date
      and rn = 1                          -- only last action
      and (T1.[Action] = '+'              -- current customer's action is '+'
      or isnull(lAction, '+') = '+')      -- or others last actions is '+'
    )   
group by [Contract], [Date];

答案 1 :(得分:1)

好的我要先填写一张像你一样的表来解决这个问题。我要做的是重复每个日期的客户和合同的每个组合。

我将此CTE附加到您的示例代码:

,
FullTable as
(
select
a.[Contract]
,a.[Customer]
,b.[Date]
,c.[Action]
,count(c.[Action]) over (partition by a.[Contract],a.[Customer] order by b.[Date]) c
from
(select distinct  
    [Contract],
    [Customer]
from T1) a
inner join 
(select distinct  
    [Contract],
    [Date]
from T1) b
on a.[Contract]=b.[Contract]
left join t1 c
on c.[Contract]=a.[Contract] and a.[Customer]=c.[Customer] and b.[Date]=c.[Date]
)

现在Fulltable做了两件事,它确保每个客户每天都有一行。如果源数据中没有该客户的操作,则Action为NULL。 我做的第二件事是使用窗口计数

计算先前操作的数量
count(c.[Action]) over (partition by a.[Contract],a.[Customer] order by b.[Date]) c

计数不计算NULL值,因此这实际上对数据进行分组,每个客户的一个组对于每个日期都有一个值,并且任何直接来自NULL操作的行都会获得相同的组

以下是客户4的数据

Contract    Customer    Date        c   Action
Contract1   Customer4   2017-01-01  1   +
Contract1   Customer4   2017-01-02  1   NULL
Contract1   Customer4   2017-01-03  2   -
Contract1   Customer4   2017-01-04  3   +
Contract1   Customer4   2017-01-05  4   -
Contract1   Customer4   2017-01-06  4   NULL
Contract1   Customer4   2017-01-07  4   NULL
Contract1   Customer4   2017-01-08  4   NULL
Contract1   Customer4   2017-01-09  5   +

现在我创建了一个名为DaillyStatus的新CTE。此CTE填写NULLS,因此现在每天都保留该合同和客户的最新状态而不是NULL。这意味着对于表中的每一天,可以找到每个客户合同组合的状态。要做到这一点,我只需为刚刚找到的每个组获取MAX

,DailyStatus as
(
select
[Contract]
,[Customer]
,[Date]
,[Action]
,c
,max([Action]) over (partition by [Contract],[Customer],c) FilledAction
from
FullTable
)

Contract    Customer    Date        c   FilledAction    Action
Contract1   Customer6   2017-01-01  0   NULL    NULL
Contract1   Customer6   2017-01-02  1   +       +
Contract1   Customer6   2017-01-03  1   +       NULL
Contract1   Customer6   2017-01-04  1   +       NULL
Contract1   Customer6   2017-01-05  1   +       NULL
Contract1   Customer6   2017-01-06  2   -       -
Contract1   Customer6   2017-01-07  2   -       NULL
Contract1   Customer6   2017-01-08  2   -       NULL
Contract1   Customer6   2017-01-09  2   -       NULL

使用此表,我们可以获取表中每个日期的每个客户的状态。自'+'&gt;' - '&gt; NULL我们可以找到所有客户都有' - '的日期,因为最新的动作在该日期或之前没有动作(NULL)

select
[Contract]
,[Date]
,max(FilledAction) 
from  DailyStatus
group by [Contract],[Date]
having max(FilledAction) ='-'

完整的解决方案在这里:

,FullTable as
(
select
a.[Contract]
,a.[Customer]
,b.[Date]
,c.[Action]
,count(c.[Action]) over (partition by a.[Contract],a.[Customer] order by b.[Date]) c
from
(select distinct  
    [Contract],
    [Customer]
from T1) a
inner join 
(select distinct  
    [Contract],
    [Date]
from T1) b
on a.[Contract]=b.[Contract]
left join t1 c
on c.[Contract]=a.[Contract] and a.[Customer]=c.[Customer] and b.[Date]=c.[Date]
)
,DailyStatus as
(
select
[Contract]
,[Customer]
,[Date]
,[Action]
,c
,max([Action]) over (partition by [Contract],[Customer],c) FilledAction
from
FullTable
)
select
[Contract]
,[Date]
,max(FilledAction) 
from  DailyStatus
group by [Contract],[Date]
having max(FilledAction) ='-'

答案 2 :(得分:0)

感谢答案,我提出了自己的解决方案,这个解决方案和Søren一样正确,并且和shA.t的解决方案一样快。多谢你们!

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if indexPath.section == 0 {
        // DO NITHING
    } else {
        // DO WHATEVER YOU WANT TO DO WITH THE CELLS IN YOUR OTHER SECTIONS
    }
}