我正在解决一个问题,我有几个表格,包括日期范围,开始日期和结束日期。我正在尝试的任务是在TSQL(2008 R2)中获取这些表并将它们组合成一个非规范化表,以便在我们的报告套件中更好地查找。
结果表需要是来自每个组合表的数据,其中日期范围已更改,以便当一个表的数据在另一个表的时间段内发生更改时,将更改第一个表中的日期并创建新记录代表新的范围。
基本上它需要是日期范围分割的组合。
出于这个问题的目的,我将假设我有两个表需要组合,但实际上我必须对几组表进行此操作,每组包含3-6个表。
以下是表A和B的示例:
表A:
IdentityId EmployeeId StartDateId EndDateId Value
---------------------------------------------------------
1 1 1980-01-01 1980-02-01 A
2 1 1980-02-02 1980-03-01 B
3 1 1980-03-01 -1 C
4 2 1980-01-15 1980-02-01 D
5 2 1980-02-02 1980-02-20 E
6 2 1980-02-21 -1 F
表B
IdentityId EmployeeId StartDateId EndDateId Value
---------------------------------------------------------
1 1 1980-01-10 1980-02-01 G
2 1 1980-02-02 1980-03-01 H
3 1 1980-03-02 -1 I
4 2 1980-01-10 1980-02-06 J
5 2 1980-02-07 1980-03-01 K
6 2 1980-03-02 -1 L
组合将是(结果表中的标识列只是一个运行的计数器;它不对应于表A或B中的标识列)
IdentityId EmployeeId StartDateId EndDateId TableA_Value TableB_Value
-------------------------------------------------------------------------------
1 1 1980-01-01 1980-01-10 A G
2 1 1980-01-10 1980-02-01 A G
3 1 1980-02-02 1980-03-01 B H
4 1 1980-03-02 -1 C I
5 2 1980-01-10 1980-01-15 D J
6 2 1980-01-15 1980-02-01 D J
7 2 1980-02-02 1980-02-06 E J
8 2 1980-02-07 1980-02-20 E K
9 2 1980-02-21 1980-03-01 F K
10 2 1980-03-02 -1 F L
一个问题是我不想依赖每个员工都有一组连续日期的事实(即对于有序集中的单个员工ID,后续记录的开始日期是结束上次记录的日期)。在上面的例子中,我试图给出我在数据集中可以想象的所有场景。唯一的假设是源系统有一个“结束日期范围”,这是一个创建的记录,其中员工的最大结束日期为StartDateId,EndDateId为-1。
我遇到麻烦的另一个问题是,当我有结束日期并且开始日期不匹配时。员工2对于第6行和第7行的上述情况显示了这一点。
我的第一种方法是将每个表加入日期维度(基数为一天),如下所示:
SELECT T1.IdentityId AS FactId, DD.DimDateId
FROM
[dbo].[Table_A] T1 JOIN [dbo].[DimDate] DD
ON DD.DimDateId BETWEEN T1.StartDimDateId AND T1.EndDimDateId
然后获取每个结果集并将它们连接在一起。我的想法是,此时我可以看到每组数据重叠的日子。然而,一旦我爆发到个别日子,我无法找到一种回到一系列日期的方法,因为我在每个表中有数十万行,每行的日期范围平均值,所以我达到性能限制一个月。
有人可以告诉我/帮我理解这个问题的解决方案吗?我正在SSIS中解决这个问题,但此时我正在尝试执行SQL,因为我原本以为会有性能优势。
我认为在处理报告数据库和分析解决方案时,这个特殊问题会出现很多问题(这是特定问题试图解决的问题)?
我做了一些关于填充表格的研究。看起来给定员工ID的日期是连续的。
答案 0 :(得分:0)
-- create some tables and data
-- I use the standard convention of using NULL as the end-date
-- for a "still open" interval.
CREATE TABLE tmp.tablea
( id serial NOT NULL
, uid INTEGER NOT NULL
, begin_date DATE NOT NULL
, end_date DATE
, aval CHAR(1)
);
INSERT INTO tmp.tablea (uid, begin_date, end_date, aval) VALUES
(1, '1980-01-01', '1980-02-01', 'A' )
, (1, '1980-02-01', '1980-03-01', 'B' )
, (1, '1980-03-01', NULL, 'C' )
, (2, '1980-01-15', '1980-02-01', 'D' )
, (2, '1980-02-01', '1980-02-20', 'E' )
, (2, '1980-02-20', NULL, 'F' )
;
DROP TABLE tmp.tableb;
CREATE TABLE tmp.tableb
( id serial NOT NULL
, uid INTEGER NOT NULL
, begin_date DATE NOT NULL
, end_date DATE
, bval CHAR(1)
);
INSERT INTO tmp.tableb (uid, begin_date, end_date, bval) VALUES
(1, '1980-01-10', '1980-02-01', 'G')
,(1, '1980-02-01', '1980-03-01', 'H' )
,(1, '1980-03-01', NULL, 'I' )
,(2, '1980-01-10', '1980-02-06', 'J' )
,(2, '1980-02-06', '1980-03-01', 'K' )
,(2, '1980-03-01', NULL, 'L' )
;
SET search_path='tmp';
UPDATE Yet another edit (between is too ugly), fencepost-errors all over the place...
SELECT aa.uid AS uid
, GREATEST (aa.begin_date, bb.begin_date) as the_begin
, LEAST (aa.end_date, bb.end_date) as the_end
, aa.aval
, aa.begin_date AS bega
, aa.end_date AS enda
, bb.bval
, bb.begin_date AS begb
, bb.end_date AS endb
FROM tablea aa
, tableb bb
WHERE aa.uid = bb.uid
AND (0=1
OR (aa.begin_date >= bb.begin_date AND aa.begin_date < bb.end_date )
OR (bb.begin_date >= aa.begin_date AND bb.begin_date < aa.end_date )
OR (bb.end_date > aa.begin_date AND bb.end_date <= aa.end_date )
OR (aa.end_date > bb.begin_date AND aa.end_date <= bb.end_date )
OR (aa.end_date IS NULL AND bb.begin_date >= aa.begin_date)
OR (bb.end_date IS NULL AND aa.begin_date >= bb.begin_date)
)
;
结果:
uid | the_begin | the_end | aval | bega | enda | bval | begb | endb
-----+------------+------------+------+------------+------------+------+------------+------------
1 | 1980-01-10 | 1980-02-01 | A | 1980-01-01 | 1980-02-01 | G | 1980-01-10 | 1980-02-01
1 | 1980-02-01 | 1980-03-01 | B | 1980-02-01 | 1980-03-01 | H | 1980-02-01 | 1980-03-01
1 | 1980-03-01 | | C | 1980-03-01 | | I | 1980-03-01 |
2 | 1980-01-15 | 1980-02-01 | D | 1980-01-15 | 1980-02-01 | J | 1980-01-10 | 1980-02-06
2 | 1980-02-01 | 1980-02-06 | E | 1980-02-01 | 1980-02-20 | J | 1980-01-10 | 1980-02-06
2 | 1980-02-06 | 1980-02-20 | E | 1980-02-01 | 1980-02-20 | K | 1980-02-06 | 1980-03-01
2 | 1980-02-20 | 1980-03-01 | F | 1980-02-20 | | K | 1980-02-06 | 1980-03-01
2 | 1980-03-01 | | F | 1980-02-20 | | L | 1980-03-01 |
(8 rows)