查找顺序相同值的组

时间:2015-06-05 20:04:45

标签: sql-server tsql sql-server-2008-r2 gaps-and-islands

如果我有一个看起来像这样的表:

+---------------------------------------------------+
| SalesPerson | SalesYear | SalesMonth | TotalSales |
+-------------+-----------+------------+------------|
| Dave        | 2011      | 1          |27          |
| Meg         | 2012      | 7          |162         |
| Randy       | 2011      | 3          |0           |
| Julio       | 2013      | 8          |15          |
| Bob         | 2014      | 12         |0           |
| Mary        | 2012      | 5          |20          |
+---------------------------------------------------+

我想找到不活动的时期,让我们说至少连续三个月销售人员没有销售,我该怎么做?我不想要一份没有销售的所有月份的清单;我需要看到长时间不活动。多个连续的零。我无法弄清楚。

3 个答案:

答案 0 :(得分:0)

第一步是将年和月转换为日期时间。 接下来,我们应该在SalesPerson的分区中找到每个不活动时段的左右边界。 然后我们对左边界和右边界进行排序以确定如何连接它们。 最后加入左右边界。

--------------creating test data------------------------------------
    declare @t table(SalesPerson varchar(max), SalesYear int, SalesMonth int, TotalSales int)

     select replicate('0', 2 - len(cast(3 as varchar))) + '3'

    insert into @t(SalesPerson, SalesYear , SalesMonth , TotalSales)
    select 'Dave', 2011, 1, 27 union all
    select 'Meg', 2012, 7, 162 union all
    select 'Randy', 2011, 3, 0 union all
    select 'Julio', 2013, 8, 15 union all
    select 'Bob', 2014, 12, 0 union all
    select 'Mary', 2012, 5, 20 union all
    select 'Mary', 2012, 6, 0 union all
    select 'Mary', 2012, 7, 0 union all
    select 'Mary', 2012, 8, 20 union all
    select 'Mary', 2012, 9, 20 
-------------------------------------------

    ;with cte as
    (
        select SalesPerson
            , cast(cast(SalesYear as varchar) + replicate('0', 2 - len(cast(SalesMonth as varchar))) + cast(SalesMonth as varchar) + '01' as datetime) as dt
            , TotalSales
            , row_number() over (partition by SalesPerson order by  cast(cast(SalesYear as varchar) + replicate('0', 2 - len(cast(SalesMonth as varchar))) + cast(SalesMonth as varchar) + '01' as datetime)) as rn
            , row_number() over (partition by SalesPerson order by  cast(cast(SalesYear as varchar) + replicate('0', 2 - len(cast(SalesMonth as varchar))) + cast(SalesMonth as varchar) + '01' as datetime) desc)  as rnd
        from @t 
    ),
    l as
    (
    select *, row_number() over(partition by SalesPerson order by dt) n
    from cte t1
    where TotalSales = 0
        and (
            exists
            (
                select * 
                from cte 
                where t1.SalesPerson = SalesPerson 
                    and dateadd(mm,-1, t1.dt) = dt
                    and TotalSales > 0
            )
            or rn = 1
            )
    ),
    r as
    (
    select *,row_number() over(partition by SalesPerson order by dt) n
    from cte t1
    where TotalSales = 0
        and (
            exists
            (
                select * 
                from cte 
                where t1.SalesPerson = SalesPerson 
                    and dateadd(mm,1, t1.dt) = dt
                    and TotalSales > 0
            )
            or rnd = 1
            )
    )
    select l.SalesPerson, l.dt as dateStart, r.dt as dateEnd
    from l 
        join r on l.n = r.n
            and l.SalesPerson = r.SalesPerson

答案 1 :(得分:0)

您对不活动时段的定义有点模糊,例如:你考虑租用/开火日期吗?以下代码实现了一种不需要递归的解释。

-- Sample data.
declare @Sales as Table (
  SalesPerson VarChar(10), SalesYear Int, SalesMonth Int, TotalSales Int );
insert into @Sales ( SalesPerson, SalesYear, SalesMonth, TotalSales ) values
  ( 'Dave', 2011, 1, 27) ,
  ( 'Meg', 2012, 7, 162 ),
  ( 'Randy', 2011, 3, 0 ),
  ( 'Julio', 2013, 8, 15 ),
  ( 'Bob', 2014, 12, 0 ),
  ( 'Mary', 2012, 5, 20 ),
  ( 'William', 2014, 1, 30 ),
  ( 'William', 2014, 2, 0 ),
  ( 'William', 2014, 4, 10 ),
  ( 'William', 2014, 6, 3 ),
  ( 'William', 2014, 7, 90 ),
  ( 'William', 2014, 12, 5 );
select * from @Sales;

-- Analyze it.
with
  -- Get only the nonzero sales rows and combine the year/month into a single integer.
  NonZeroSales as (
    select SalesPerson, SalesYear * 12 + SalesMonth as CombinedMonth, TotalSales
      from @Sales
      where TotalSales <> 0 ),
  -- Add row numbers for each sales person.
  NonZeroSalesWithRN as (
    select SalesPerson, CombinedMonth, TotalSales,
      Row_Number() over ( partition by SalesPerson order by CombinedMonth ) as RN
      from NonZeroSales )
  -- Match adjacent rows for each sales person.
  --   If there is a gap of three or more months then indicate it in a status column.
  select L.SalesPerson,
    Floor( L.CombinedMonth / 12 ) as SalesYear, L.CombinedMonth % 12 as SalesMonth,
    L.TotalSales,
    case when R.CombinedMonth - L.CombinedMonth > 3 then 'Gap > 3 Months' else 'Okay' end as SalesStatus
    from NonZeroSalesWithRN as L inner join
      NonZeroSalesWithRN as R on R.SalesPerson = L.SalesPerson and R.RN = L.RN + 1;
  -- Tip: To see what is going on, or debug, multiple CTEs replace the last select with
  --   select * from NonZeroSales
  --   select * from NonZeroSalesWithRN

答案 2 :(得分:-1)

HAVING(Transact-SQL)就是您的答案。

https://msdn.microsoft.com/en-us/library/ms180199.aspx

首先误读了这个问题。