用纯SQL可行吗?或者是需要的程序?

时间:2010-12-12 11:01:17

标签: sql db2

(这是在DB2 V9.5中)

假设我有一个包含四列的表:

C1: [EmployeeId]
C2: [StartDate] yyyy-mm-dd format
C3: [EndDate]   yyyy-mm-dd format
C4: [Age]

组合[EmployeeId + StartDate]构成主键 现在考虑以下示例行:

1     2010-01-16     2010-04-16     29
2     2010-02-16     2010-03-16     33
3     2010-05-16     2010-05-16     31

可以保证所有DAYS在所有日期的所有日期都是第16天。别无他法。

我正在尝试编写一个查询,它将执行以下操作:

对于起始月份小于结束月份的任何行(如第1行),复制其间的每个月的行,包括最后一行。因此,在运行查询后,第1行的新行将为:

1     2010-01-16     2010-04-16     29
1     2010-02-16     2010-04-16     29
1     2010-03-16     2010-04-16     29
1     2010-04-16     2010-04-16     29

对于起始月份和结束月份相同的行,不执行任何操作。对于结束月份是开始月份之后的月份的行,将插入一个新行(请参阅上面的最后一行以了解原因)。

问题:

  1. 这在纯SQL中是否可行?或者需要程序吗?
  2. 如果是,那怎么样? (提示,代码,任何东西)
  3. 感谢您的阅读。问题陈述很复杂,我希望我能做一个公平的工作来解释它。任何歧义或不一致:请指出,我会尽快编辑。

2 个答案:

答案 0 :(得分:1)

我认为应该这样做:

SELECT e.employee_id, 
       date(extract(year from startdate)||'-'||mm.month_nr||'-'||extract(day from startdate)) as stardate,
       first_value(e.enddate) over (partition by employee_id order by e.enddate) as enddate
FROM (
  VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)
) mm (month_nr)
JOIN emp_test e
     ON mm.month_nr = extract(month from e.startdate)
ORDER BY e.employee_id, 
         e.startdate, 
         e.enddate

这当然假设startdate和enddate的数据类型是DATE 这只有在所有日期都在一年内才有效。

我只用DB2 9.7对它进行了测试,但我认为它也适用于9.5。

我认为年度交叉问题有一个解决方案:

WITH yrs (year_month) AS
(
  SELECT DATE(year_nr||'-'||month_nr||'-16')
  FROM (
   VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)
  ) mon (month_nr)
  CROSS JOIN (
    VALUES (2007), (2008), (2009), (2010), (2011), (2012), (2013)
  ) yr (year_nr)
) 
SELECT e.employee_id, 
       yrs.year_month as startdate,
       first_value(e.enddate) over (partition by employee_id order by e.enddate) as enddate
FROM emp_test e
  JOIN yrs ON yrs.year_month between e.startdate and e.enddate

正如您所看到的那些年份被“硬编码”到CTE(公用表表达式)中。最简单的解决方案是生成一个包含所有可能值的“月/年”表 - 基本上是CTE生成的数据。

答案 1 :(得分:0)

FWIW一组属性和元组约束会在某种程度上简化:

CHECK (EXTRACT(DAY FROM StartDate) = 16),
CHECK (EXTRACT(DAY FROM EndDate) = 16),
CHECK (StartDate < EndDate)