Sql server 计算值大于0的连续天数

Sql server 计算值大于0的连续天数,sql-server,rank,gaps-and-islands,Sql Server,Rank,Gaps And Islands,使用SQL Server 2012,我尝试创建一个查询,从气候数据库中为我提供前10个最长的雨季或旱季 我的临时表提供以下数据输出: select monthid as [id], date, rain_today from #raindays order by monthid asc, date asc 输出: id date rain_today ------------------------------- 1 24 Dec 2014 2.4 1

使用SQL Server 2012,我尝试创建一个查询,从气候数据库中为我提供前10个最长的雨季或旱季

我的临时表提供以下数据输出:

select monthid as [id], date, rain_today 
from #raindays
order by monthid asc, date asc
输出:

id  date            rain_today
-------------------------------
1   24 Dec 2014     2.4
1   25 Dec 2014     0
1   26 Dec 2014     8.7
1   27 Dec 2014     1.8
1   28 Dec 2014     0.3
1   29 Dec 2014     0
1   30 Dec 2014     0
1   31 Dec 2014     0.3
2   01 Jan 2015     0.3
2   02 Jan 2015     0.3
2   03 Jan 2015     18.3
2   04 Jan 2015     0.3
id  FirstDryDay     LatestDryDay    countdays
-----------------------------------------------
23  21 Oct 2016     31 Oct 2016     11
21  23 Aug 2016     31 Aug 2016     9
**15    23 Feb 2016     29 Feb 2016     7**
10  25 Sep 2015     30 Sep 2015     6
8   28 Jul 2015     31 Jul 2015     4
24  28 Nov 2016     30 Nov 2016     3
29  29 Apr 2017     30 Apr 2017     2
30  30 May 2017     31 May 2017     2
31  29 Jun 2017     30 Jun 2017     2
20  30 Jul 2016     31 Jul 2016     2
7   29 Jun 2015     30 Jun 2015     2
5   30 Apr 2015     30 Apr 2015     1
11  31 Oct 2015     31 Oct 2015     1
17  30 Apr 2016     30 Apr 2016     1
22  30 Sep 2016     30 Sep 2016     1
等等等等

我想返回一个排名表,该表将计算今天的雨>0或今天的雨=0的时段,即:

Rank Start_Date   End_Date    Wet Period
----------------------------------------
1    31 Dec 2014  04 Jan 2015 5
2    26 Dec 2014  28 Dec 2014 3

通过查看其他类似查询,我得到的最接近的结果如下:这是针对干旱天的:

 select 
     #raindays.monthid as id,
     min(#raindays.date) as [FirstDryDay],
     max(#raindays.date) as [LatestDryDay],
     count(*) as countdays
 from 
     (select 
          monthid, 
          coalesce(max(case 
                          when rain_today > '0' 
                             then #raindays.date end), '19000101') as latestdry
     from 
         #raindays
     group by 
         monthid) g
join 
    #raindays on #raindays.monthid = g.monthid
              and #raindays.date > g.latestdry
group by 
    #raindays.monthid
order by 
    countdays desc
输出:

id  date            rain_today
-------------------------------
1   24 Dec 2014     2.4
1   25 Dec 2014     0
1   26 Dec 2014     8.7
1   27 Dec 2014     1.8
1   28 Dec 2014     0.3
1   29 Dec 2014     0
1   30 Dec 2014     0
1   31 Dec 2014     0.3
2   01 Jan 2015     0.3
2   02 Jan 2015     0.3
2   03 Jan 2015     18.3
2   04 Jan 2015     0.3
id  FirstDryDay     LatestDryDay    countdays
-----------------------------------------------
23  21 Oct 2016     31 Oct 2016     11
21  23 Aug 2016     31 Aug 2016     9
**15    23 Feb 2016     29 Feb 2016     7**
10  25 Sep 2015     30 Sep 2015     6
8   28 Jul 2015     31 Jul 2015     4
24  28 Nov 2016     30 Nov 2016     3
29  29 Apr 2017     30 Apr 2017     2
30  30 May 2017     31 May 2017     2
31  29 Jun 2017     30 Jun 2017     2
20  30 Jul 2016     31 Jul 2016     2
7   29 Jun 2015     30 Jun 2015     2
5   30 Apr 2015     30 Apr 2015     1
11  31 Oct 2015     31 Oct 2015     1
17  30 Apr 2016     30 Apr 2016     1
22  30 Sep 2016     30 Sep 2016     1
如你所见,我并不想按id分组,因为我希望能够跨越不同的月份,而且我错过了本月早些时候发生的其他时段。实际计数工作正常,检查上述突出显示的周期:

id  date    rain_today
15  22 Feb 2016     3.9
15  23 Feb 2016     0
15  24 Feb 2016     0
15  25 Feb 2016     0
15  26 Feb 2016     0
15  27 Feb 2016     0
15  28 Feb 2016     0
15  29 Feb 2016     0
16  01 Mar 2016     3

提前感谢您的帮助

这就是你想要的吗

IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL 
DROP TABLE #TestData;

CREATE TABLE #TestData (
    id INT NOT NULL ,
    [Date] DATE NOT NULL,
    Rain_Today DECIMAL(9,2) NOT NULL 
    );

INSERT #TestData (id, Date, Rain_Today) VALUES 
    (1, '24 Dec 2014', 2.4),
    (1, '25 Dec 2014', 0),
    (1, '26 Dec 2014', 8.7),
    (1, '27 Dec 2014', 1.8),
    (1, '28 Dec 2014', 0.3),
    (1, '29 Dec 2014', 0),
    (1, '30 Dec 2014', 0),
    (1, '31 Dec 2014', 0.3),
    (2, '01 Jan 2015', 0.3),
    (2, '02 Jan 2015', 0.3),
    (2, '03 Jan 2015', 18.3),
    (2, '04 Jan 2015', 0.3);

--======================================

WITH 
    cte_AddRankGroup AS (
        SELECT 
            td.id,
            td.Date,
            td.Rain_Today,
            hr.HasRain,
            RankGroup = DENSE_RANK() OVER (PARTITION BY td.id ORDER BY td.Date) -
                DENSE_RANK() OVER (PARTITION BY td.id, hr.HasRain ORDER BY td.Date)
        FROM 
            #TestData td
            CROSS APPLY ( VALUES (IIF(td.Rain_Today = 0, 0, 1)) ) hr (HasRain)
        )
SELECT 
    arg.id,
    BegDate = MIN(arg.Date),
    EndDate = MAX(arg.Date),
    WetPeriod = IIF(arg.HasRain = 1, 'Wet', 'Dry'),
    ConsecutiveDays = COUNT(1)
FROM 
    cte_AddRankGroup arg
GROUP BY 
    arg.id,
    arg.HasRain,
    arg.RankGroup
ORDER BY 
    arg.id,
    MIN(arg.Date);
结果

id          BegDate     EndDate     WetPeriod ConsecutiveDays
----------- ----------  ----------  --------- ---------------
1           2014-12-24  2014-12-24  Wet       1
1           2014-12-25  2014-12-25  Dry       1
1           2014-12-26  2014-12-28  Wet       3
1           2014-12-29  2014-12-30  Dry       2
1           2014-12-31  2014-12-31  Wet       1
2           2015-01-01  2015-01-04  Wet       4
编辑:代码版本使用大小写表达式代替IIF

--======================================

WITH 
    cte_AddRankGroup AS (
        SELECT 
            td.id,
            td.Date,
            td.Rain_Today,
            hr.HasRain,
            RankGroup = DENSE_RANK() OVER (PARTITION BY td.id ORDER BY td.Date) -
                DENSE_RANK() OVER (PARTITION BY td.id, hr.HasRain ORDER BY td.Date)
        FROM 
            #TestData td
            CROSS APPLY ( VALUES (CASE WHEN td.Rain_Today = 0 THEN 0 ELSE 1 END) ) hr (HasRain)
        )
SELECT top 10
    arg.id,
    BegDate = MIN(arg.Date),
    EndDate = MAX(arg.Date),
    WetPeriod = CASE WHEN arg.HasRain = 1 THEN 'Wet' ELSE 'Dry' END,
    ConsecutiveDays = COUNT(1)
FROM 
    cte_AddRankGroup arg
WHERE 
    arg.HasRain = '0' -- Top 10 Dry
    --arg.HasRain = '1' -- Top 10 Wet
GROUP BY 
    arg.id,
    arg.HasRain,
    arg.RankGroup
ORDER BY 
    ConsecutiveDays desc, MIN(arg.Date);
修改原始脚本,按每个时段类型生成前10名,这是我的最终目标输出来自完整数据集:

id  BegDate EndDate WetPeriod   ConsecutiveDays
31  10 Jun 2017     26 Jun 2017     Dry 17
4   02 Mar 2015     14 Mar 2015     Dry 13
5   12 Apr 2015     24 Apr 2015     Dry 13
20  15 Jul 2016     26 Jul 2016     Dry 12
29  01 Apr 2017     11 Apr 2017     Dry 11
26  17 Jan 2017     27 Jan 2017     Dry 11
23  21 Oct 2016     31 Oct 2016     Dry 11
25  01 Dec 2016     09 Dec 2016     Dry 9
21  10 Aug 2016     18 Aug 2016     Dry 9
21  23 Aug 2016     31 Aug 2016     Dry 9

此问题可通过以下方式通过递归解决:

-- this variable is needed to stop the recursion
declare @numrows int=(select count(1) from #raindays)

-- add a row number to the table creating a new table as "tabseq"
;WITH tabseq as (select row_number() over(order by date) as rownum, * from #raindays),
-- apply recursion to tabseq keeping a toggle running totals of wet and dry periods
CTE as
(
select *, 
 (case when rain_today=0 then 1 else 0 end) as dry,
 (case when rain_today>0 then 1 else 0 end) as wet 
from tabseq where rownum=1
union all 
select s.*,
 (case when s.rain_today=0 then cte.dry+1 else 0 end) as dry,
 (case when s.rain_today>0 then cte.wet+1 else 0 end) as wet
from tabseq s
join cte on s.rownum=cte.rownum+1
where s.rownum<=@numrows
)
select * from cte
一旦您有了带有干/湿蓄能器的cte表,您就可以订购并从中进行选择,以满足您的输出要求


请注意,这是假设表中连续出现几天,如果存在间隙,而不是在case语句中加上+1,则可能需要将一个DATEDIFF添加到一侧或另一侧,这取决于您如何考虑丢失的日期是湿的还是干的。

您想要的输出是什么?对于您提供一个带有填充的临时数据的DDL,以及使用2012或更新的期望值,将是非常有帮助的。可以使用窗口函数,一个运行总数,比如sumcase when rain\u today>0,然后1 else 0结束于按id顺序按日期划分的分区抱歉,这可能没有多大帮助,您很可能需要更复杂的东西,使用case 0/1和类似row\u number的东西来表示所有行减去由该0/1划分的row\u number,然后按此分组如果你按monthid分组会发生什么?嗨Jason,谢谢你的帮助…我在两行IIF上得到了不正确的语法错误…虽然它们看起来没问题:交叉应用值IIFtd.Rain_Today=0,'0','1'hr HasRain-Msg 102,级别15,状态1,第13行“=”附近的语法不正确。奇怪。我刚刚对我的SQL Server 2012副本进行了重新测试。您确定这是您正在使用的版本吗?是的,SQL Server 2012。Microsoft SQL Server Management Studio 11.0.2100.60更新了答案,以使用CASE表达式代替IIF。CASE版本可以正常工作。有趣…虽然我在2012年,但这一点即将出现:Microsoft SQL Server 2008 R2 RTM-10.50.1600.1 X64 2010年4月2日15:48:46版权c Microsoft Corporation Developer Edition 64位Windows NT 6.1 Build 7601:Service Pack 1-我将清理我机器上的版本,然后就有了2012。谢谢,模棱两可的列名“rain_today”@16+17?如果您有更多问题,请告诉我,因为当我写它时,我无法测试它。我注意到declare stmt上也缺少一个。假设表raindays符合OPYes,此代码应该可以工作,我注意到缺少括号。我还必须在查询的末尾添加以下行。选项maxrecursion 0。现在查看结果…是的,正确,如果您期望的递归级别超过100,则需要设置一个限制或0表示无限制