我有一个ETL过程,它从输入表中获取值,该输入表是一个键值表,每行都有一个字段ID,并将其转换为更加非规范化的表,其中每一行都包含所有值。具体来说,这是输入表:
StudentFieldValues (
FieldId INT NOT NULL,
StudentId INT NOT NULL,
Day DATE NOT NULL,
Value FLOAT NULL
)
FieldId
是表Field
中的外键,Day
是表Days
中的外键。 PK是前3个领域。目前有188个不同的领域。输出表格如下:
StudentDays (
StudentId INT NOT NULL,
Day DATE NOT NULL,
NumberOfClasses FLOAT NULL,
MinutesLateToSchool FLOAT NULL,
... -- the rest of the 188 fields
)
PK是前两个字段。
目前填充输出表的查询会自动加入StudentFieldValues
188次,每个字段一次。每个联接等同于StudentId
和Day
,并采用不同的FieldId
。具体做法是:
SELECT Students.StudentId, Days.Day,
StudentFieldValues1.Value NumberOfClasses,
StudentFieldValues2.Value MinutesLateToSchool,
...
INTO StudentDays
FROM Students
CROSS JOIN Days
LEFT OUTER JOIN StudentFieldValues StudentFieldValues1
ON Students.StudentId=StudentFieldValues1.StudentId AND
Days.Day=StudentFieldValues1.Day AND
AND StudentFieldValues1.FieldId=1
LEFT OUTER JOIN StudentFieldValues StudentFieldValues2
ON Students.StudentId=StudentFieldValues2.StudentId AND
Days.Day=StudentFieldValues2.Day AND
StudentFieldValues2.FieldId=2
... -- 188 joins with StudentFieldValues table, one for each FieldId
我担心这个系统不会扩展,因为更多的日子,学生和领域(特别是字段)被添加到系统中。已经有188个连接,我一直在阅读,如果你有一个查询数量的连接,你做错了什么。所以我基本上都在问:这件事很快会在我的脸上爆炸吗?有没有更好的方法来实现我想要做的事情?重要的是要注意这个查询记录最少,而且如果我一个接一个地添加字段,就不会有这样的事情。
更多详情:
答案 0 :(得分:2)
是的,这会爆炸。你有"标准化的定义"和#34;非规范化"向后。字段/值表设计不是关系设计。它是entity-attribute-value设计的变体,有各种各样的问题。
我建议您不要尝试在SQL查询中透视数据。它不会那么好地扩展。 Instea,您需要将其作为一组行进行查询,因为它存储在数据库中,并将结果集提取到应用程序中。在那里你编写代码来逐行读取数据,并应用"字段"到对象的字段或散列图或其他东西。
答案 1 :(得分:2)
考虑使用条件聚合来做到这一点:
SELECT s.StudentId, d.Day,
max(case when sfv.FieldId = 1 then sfv.Value end) as NumberOfClasses,
max(case when sfv.FieldId = 2 then sfv.Value end) as MinutesLateToSchool,
...
INTO StudentDays
FROM Students s CROSS JOIN
Days d LEFT OUTER JOIN
StudentFieldValues sfv
ON s.StudentId = sfv.StudentId AND
d.Day = sfv.Day
GROUP BY s.StudentId, d.Day;
这具有易于扩展的优点。您可以添加数百个字段,处理时间应与较少的字段相当(更长,但可比较)。添加新字段也更容易。
编辑:
此查询的更快版本将使用子查询而不是聚合:
SELECT s.StudentId, d.Day,
(SELECT TOP 1 sfv.Value FROM StudentFieldValues WHERE sfv.FieldId = 1 and sfv.StudentId = s.StudentId and sfv.Day = sfv.Day) as NumberOfClasses,
(SELECT TOP 1 sfv.Value FROM StudentFieldValues WHERE sfv.FieldId = 2 and sfv.StudentId = s.StudentId and sfv.Day = sfv.Day) as MinutesLateToSchool,
...
INTO StudentDays
FROM Students s CROSS JOIN
Days d;
对于性能,您需要StudentFieldValues(StudentId, day, FieldId, Value)
上的复合索引。
答案 2 :(得分:0)
我认为这里可能会有一些试验和错误,看看有什么用,但这里有一些你可以尝试的事情:
以上内容取自msdn post,其中某人正在做与您相似的事情。