SQL日期查询 - 此条件为真有效

时间:2009-03-11 16:07:44

标签: sql database datetime sybase

问题是这些客户在任何特定日期都会有多长时间。

我正在反对Sybase

对于表history_data的简化表结构

table: history_of_jerkiness
processing_date  name  is_jerk
---------------  ----- -------
20090101         Matt  true
20090101         Bob   false        
20090101         Alex  true        
20090101         Carol true        
20090102         Matt  true        
20090102         Bob   true        
20090102         Alex  false        
20090102         Carol true        
20090103         Matt  true        
20090103         Bob   true        
20090103         Alex  true        
20090103         Carol false        

第三名的报告应该表明,马特一直是个混蛋,亚历克斯刚刚变成了一个混蛋,而鲍勃已经是个混蛋2天了。

name    days jerky
-----   ----------
Matt    3
Bob     2
Alex    1

我想动态地找到这些时间跨度,所以如果我运行第二个报告,我会得到不同的结果:

name    days_jerky
-----   ----------
Matt    2
Bob     1
Carol   2

这里的关键是尝试仅查找比特定日期更早的连续跨度。我找到了一些线索,但这似乎是一个问题,那里会有非常聪明的棘手解决方案。

4 个答案:

答案 0 :(得分:2)

我的SQL Server解决方案 - 和Dems一样,但我自己放入了一个min基线。 它假定没有间隙 - 即每个人每天都有一个条目。 如果不是这样,那么我必须循环。

DECLARE @run_date datetime
DECLARE @min_date datetime

SET @run_date = {d '2009-01-03'}

-- get day before any entries in the table to use as a false baseline date
SELECT @min_date = DATEADD(day, -1, MIN(processing_date)) FROM history_of_jerkiness

-- get last not a jerk date for each name that is before or on the run date
-- the difference in days between the run date and the last not a jerk date is the number of days as a jerk
SELECT [name], DATEDIFF(day, MAX(processing_date), @run_date)
FROM (
     SELECT processing_date, [name], is_jerk
     FROM history_of_jerkiness
     UNION ALL
     SELECT DISTINCT @min_date, [name], 0
     FROM history_of_jerkiness ) as data
WHERE is_jerk = 0
  AND processing_date <= @run_date
GROUP BY [name]
HAVING DATEDIFF(day, MAX(processing_date), @run_date) > 0

我使用以下内容创建了测试表:

CREATE TABLE history_of_jerkiness (processing_date datetime, [name] varchar(20), is_jerk bit)

INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Matt', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Bob', 0)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Alex', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Carol', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Matt', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Bob', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Alex', 0)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Carol', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Matt', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Bob', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Alex', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Carol', 0) 

答案 1 :(得分:1)

如果您构建数据以符合以下条件,这可以变得简单......

  

所有人都必须有一个他们不是混蛋的初始记录

你可以做点像......

SELECT
   name,
   MAX(date)   last_day_jerk_free
FROM
   jerkiness AS [data]
WHERE
   jerk = 'false'
   AND date <= 'a date'
GROUP BY
   name

你已经知道基准日期是什么('一个日期'),现在你知道它们不是混蛋的最后一天。我不知道sybase,但我确信你可以使用命令来获取'a data'和'last_day_jerk_free'之间的天数

编辑:

有多种方法可以人为地创建初始化“非生涩”的记录。 Will Rickards建议的那个使用包含union的子查询。但是,这样做有两个不利方面...... 1.子查询屏蔽可能已经使用过的任何索引 2.它假设所有人都有从同一点开始的数据

或者,采用Will Rickard的建议并将聚合从外部查询移动到内部查询(因此最大化索引的使用),并与通用的第二个子查询联合以创建起始的jerky = false记录...

SELECT name, DATEDIFF(day, MAX(processing_date), @run_date) AS days_jerky
FROM (

    SELECT name, MAX(processing_date) as processing_date
    FROM history_of_jerkiness
    WHERE is_jerk = 0 AND processing_date <= @run_date
    GROUP BY name

    UNION

    SELECT name, DATEADD(DAY, -1, MIN(processing_date))
    FROM history_of_jerkiness
    WHERE processing_date <= @run_date
    GROUP BY name

    ) as data
GROUP BY
   name

外部查询仍然必须在没有索引的情况下执行max,但是记录数量减少(每个名称2个,而不是每个名称n个)。通过不要求每个名称具有每个使用日期的值,也减少了记录的数量。还有很多其他方法可以做到这一点,有些可以在我的编辑历史中看到。

答案 2 :(得分:1)

“如果您构建数据以符合以下条件,这可以变得简单......

所有人都必须有一个他们不是混蛋的初始记录“

数据应该和不应该满足的标准取决于用户,而不是开发人员。

答案 3 :(得分:0)

这个怎么样:

select a.name,count(*) from history_of_jerkiness a
left join history_of_jerkiness b
on a.name = b.name 
and a.processing_date >= b.processing_date
and a.is_jerk = 'true'
where not exists
( select * from history_of_jerkiness c
  where a.name = c.name
  and c.processing_date between a.processing_date and b.processing_date
  and c.is_jerk = 'false'
)
and a.processing_date <= :a_certain_date;