sql 语句:一个字段,连续几天值大于0,获得天数 怎么解决的?请教

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sql 语句:一个字段,连续几天值大于0,获得天数 怎么解决的?请教相关的知识,希望对你有一定的参考价值。

a b
xxx 2011-6-1
ooo 2011-6-2
xxx 2011-6-3
xxx 2011-6-4
xxx 2011-6-5

判断是否存在连续三天a的值为xxx ,sql怎么写 最好是mysql的

select * from tb where a = xxx筛选后,难点就是如何判断是否有3天是连续的
可以将表下移一行后作差比较:
b 错行 差
1 2011-6-1
2 2011-6-3 2011-6-1 2
3 2011-6-4 2011-6-3 1
4 2011-6-5 2011-6-4 1
2011-6-5
然后再用游标判断差这一列是否有两个1是挨着的

或者直接判断时期是否连续,sqlserver里可以这样写

DECLARE @id INT
DECLARE @time DATETIME
DECLARE @tb TABLE (id INT IDENTITY(1,1) ,b DATETIME)
INSERT INTO @tb(b) SELECT b FROM tb ORDER BY b
SELECT @id=MIN(INT_I) FROM @tb
WHILE NOT @id IS NULL BEGIN
SELECT @time = b FROM @tb WHERE INT_I=@id
IF EXISTS(SELECT 1 FROM tb WHERE b = DATEADD(dd,1,@time))
BEGIN
IF EXISTS(SELECT 1 FROM tb WHERE b = DATEADD(dd,2,@time))
BEGIN
PRINT '存在!'
END
END

SELECT @id=MIN(id) FROM @tb WHERE id>@id
END
参考技术A 一个sql比较难.如果不是必须就是游标吧. 参考技术B
加一个用来判断的字段 ttt
该字段为0和1
如果纪录的上面一条记录的 ttt 为0 并且该纪录的bbb不是负 ttt 为 0
否则为1
然后根据 ttt,把1的去掉就行。

加入一系列日期并计算连续天数

【中文标题】加入一系列日期并计算连续天数【英文标题】:Joining series of dates and counting continous days 【发布时间】:2015-08-18 14:18:29 【问题描述】:

假设我有一张如下表

date        add_days
2015-01-01  5
2015-01-04  2
2015-01-11  7
2015-01-20  10
2015-01-30  1

我想要做的是检查days_balance,即如果date大于或小于前一个日期+ N天(add_days),如果它们是连续系列,则取累计天数总和.

所以算法应该像这样工作

for i in 2:N_rows 
   days_balance[i] := date[i-1] + add_days[i-1] - date[i]
   if days_balance[i] >= 0 then
      date[i] := date[i] + days_balance[i]

预期的结果应该如下

date        days_balance
2015-01-01  0
2015-01-04  2
2015-01-11  -3
2015-01-20  -2
2015-01-30  0

纯SQL可以吗?我想它应该带有一些条件连接,但看不到它是如何实现的。

【问题讨论】:

如果您使用的是 PostgreSQL 9.1.13 或更高版本,则需要查看 PostgresSQL 中的 laglead 窗口函数。另外,您如何计算第一条记录的 previous_date 和 previous_add_days? @FutbolFan 按当前和以前的日期我是指后续行 - 为了清楚起见,我将进行编辑。 是的,我知道了。但是,对于 date = 2015-01-01 的行,我要问的是该记录的 previous_date 和 previous_add_days 是多少? 预期结果是否正确?第四行不应该是-5吗?这是我的数学:第 01 天到第 4 天 = 0 余额减去 3 天变化 + 5 add_days = 2;第 04 天到第 11 天 = 2 余额减去 7 天变化 + 2 add_days = -3;第 11 天到第 20 天 = -3 余额减去 9 天变化 + 7 add_days = -5;第 20 天到第 30 天 = -5 余额减去 10 天变化 + 10 add_days = -5; @FutbolFan 我更新了我的问题和伪代码 - 现在清楚了吗? 【参考方案1】:

我发布了另一个答案,因为比较它们可能会很好,因为它们使用不同的方法(这个只是进行 n^2 样式连接,另一个使用递归 CTE)。这一个利用了这样一个事实,即您不必在为特定行计算之前为每一行计算 days_balance ,您只需要将前几天的内容相加......

drop table junk
create table junk(date DATETIME, add_days int)

insert into junk values
('2015-01-01',5  ),       
('2015-01-04',2  ),      
('2015-01-11',7  ),      
('2015-01-20',10 ),      
('2015-01-30',1  )    

;WITH cte as
(
    select ROW_NUMBER() OVER (ORDER BY date) i, date, add_days, ISNULL(DATEDIFF(DAY, LAG(date) OVER (ORDER BY date), date), 0) days_since_prev
    FROM Junk
)
, combinedWithAllPreviousDaysCte as
(
    select i [curr_i], date [curr_date], add_days [curr_add_days], days_since_prev [curr_days_since_prev], 0 [prev_add_days], 0 [prev_days_since_prev] from cte where i = 1 --get first row explicitly since it has no preceding rows
    UNION ALL
    select curr.i [curr_i], curr.date [curr_date], curr.add_days [curr_add_days], curr.days_since_prev [curr_days_since_prev], prev.add_days [prev_add_days], prev.days_since_prev [prev_days_since_prev]
    from cte curr 
    join cte prev on curr.i > prev.i --join to all previous days
)
select curr_i, curr_date, SUM(prev_add_days) - curr_days_since_prev - SUM(prev_days_since_prev) [days_balance]
from combinedWithAllPreviousDaysCte
group by curr_i, curr_date, curr_days_since_prev
order by curr_i

输出:

+--------+-------------------------+--------------+
| curr_i |        curr_date        | days_balance |
+--------+-------------------------+--------------+
|      1 | 2015-01-01 00:00:00.000 |            0 |
|      2 | 2015-01-04 00:00:00.000 |            2 |
|      3 | 2015-01-11 00:00:00.000 |           -3 |
|      4 | 2015-01-20 00:00:00.000 |           -5 |
|      5 | 2015-01-30 00:00:00.000 |           -5 |
+--------+-------------------------+--------------+

【讨论】:

【参考方案2】:

好吧,我想我有一个递归 CTE(抱歉,我目前只有 Microsoft SQL Server 可用,所以它可能不符合 PostgreSQL)。

我也认为你的预期结果是错误的(见上面的评论)。如果没有,这可能会被修改以符合您的数学。

drop table junk
create table junk(date DATETIME, add_days int)

insert into junk values
('2015-01-01',5  ),       
('2015-01-04',2  ),      
('2015-01-11',7  ),      
('2015-01-20',10 ),      
('2015-01-30',1  )    

;WITH cte as
(
    select ROW_NUMBER() OVER (ORDER BY date) i, date, add_days, ISNULL(DATEDIFF(DAY, LAG(date) OVER (ORDER BY date), date), 0) days_since_prev
    FROM Junk
)
,recursiveCte (i, date, add_days, days_since_prev, days_balance, math)  as
(
    select top 1 
        i, 
        date, 
        add_days, 
        days_since_prev, 
        0 [days_balance], 
        CAST('no math for initial one, just has zero balance' as varchar(max)) [math]
    from cte where i = 1
    UNION ALL --recursive step now
    select 
        curr.i, 
        curr.date, 
        curr.add_days, 
        curr.days_since_prev, 
        prev.days_balance - curr.days_since_prev + prev.add_days [days_balance],
        CAST(prev.days_balance as varchar(max)) + ' - ' + CAST(curr.days_since_prev as varchar(max)) + ' + ' + CAST(prev.add_days as varchar(max)) [math]
    from cte curr
    JOIN recursiveCte prev ON curr.i = prev.i + 1       
)
select i,  DATEPART(day,date) [day], add_days, days_since_prev, days_balance, math
from recursiveCTE
order by date

结果是这样的:

+---+-----+----------+-----------------+--------------+------------------------------------------------+
| i | day | add_days | days_since_prev | days_balance | math                                           |
+---+-----+----------+-----------------+--------------+------------------------------------------------+
| 1 | 1   | 5        | 0               | 0            | no math for initial one, just has zero balance |
| 2 | 4   | 2        | 3               | 2            | 0 - 3 + 5                                      |
| 3 | 11  | 7        | 7               | -3           | 2 - 7 + 2                                      |
| 4 | 20  | 10       | 9               | -5           | -3 - 9 + 7                                     |
| 5 | 30  | 1        | 10              | -5           | -5 - 10 + 10                                   |
+---+-----+----------+-----------------+--------------+------------------------------------------------+

【讨论】:

另外,我觉得这不是一个很好的解决方案,因为递归级别随着每一行而增加,所以只使用一些东西来迭代处理结果可能会更好/更有效(但是最初的问题不是要求那个)。【参考方案3】:

我不太明白您的算法如何返回您的预期结果?但让我分享一个我想出的可能会有所帮助的技术。

这仅在您的数据的最终结果要导出到 Excel 时才有效,即使这样,它也不会在所有情况下都有效,具体取决于您导出数据集的格式,但在这里......

如果您熟悉 Excel 公式,我发现如果您在 SQL 中将 Excel 公式作为另一个字段编写,它会在您导出到 Excel 后立即为您执行该公式(适用于我只是将其复制并粘贴到 Excel 中,因此它不会将其格式化为文本)

因此,对于您的示例,您可以执行以下操作(再次注意我不理解您的算法,所以这可能是错误的,但这只是为了给您一个概念)

SELECT
    date
  , add_days
  , '=INDEX($1:$65536,ROW()-1,COLUMN()-2)'
  ||'+INDEX($1:$65536,ROW()-1,COLUMN()-1)'
  ||'-INDEX($1:$65536,ROW(),COLUMN()-2)'
    AS "days_balance[i]"
  ,'=IF(INDEX($1:$65536,ROW(),COLUMN()-1)>=0'
  ||',INDEX($1:$65536,ROW(),COLUMN()-3)'
  ||'+INDEX($1:$65536,ROW(),COLUMN()-1))'
    AS "date[i]"
FROM
  myTable
ORDER BY /*Ensure to order by whatever you need for your formula to work*/

完成这项工作的关键部分是使用INDEX 公式函数根据当前单元格的位置选择一个单元格。所以ROW()-1 告诉它获取上一条记录的结果,COLUMN()-2 表示从当前记录左侧的两列中获取值。因为您不能使用像A2+B2-A3 这样的单元格引用,因为行号在导出时不会改变,并且它假定列的位置。

我将 SQL 字符串连接与 || 一起使用,以便在屏幕上更容易阅读。

我在excel中试过这个;它与您的预期结果不符。但是,如果这种技术对您有用,那么只需更正 excel 公式以适应。

【讨论】:

以上是关于sql 语句:一个字段,连续几天值大于0,获得天数 怎么解决的?请教的主要内容,如果未能解决你的问题,请参考以下文章

sql 语句:一个字段,连续几天值大于0,获得天数 怎么解决的?请教

利用SQL语句如何获得两个日期之间相差的天数

利用SQL语句如何获得两个日期之间相差的天数

如何用SQL解决连续几天的问题

SQL:连续天数的计算方法

在 SQL 中计算连续班次和天数