sql - 如何有效地做数据库as-of查询?

标签 sql database database-design

请问这么长的问题!

我们有两个数据库表,例如汽车和车轮。它们的关系在于一个轮子属于一辆车,一辆车有多个轮子。然而,可以在不影响汽车“版本”的情况下更换车轮。可以在不影响车轮版本的情况下更新汽车的记录(例如油漆工作)(即无级联更新)。

例如,Car 表目前看起来是这样的:

CarId, CarVer, VersionTime, Colour
   1      1       9:00       Red
   1      2       9:30       Blue
   1      3       9:45       Yellow
   1      4      10:00       Black

Wheels 表看起来像这样(这辆车只有两个轮子!)

WheelId, WheelVer, VersionTime, CarId
   1         1           9:00     1
   1         2           9:40     1
   1         3          10:05     1
   2         1           9:00     1

因此,这款两轮车有 4 个版本。它的第一个轮子 (WheelId 1) 没有改变。第二个轮子在 10 点 05 分更换(例如绘制(paint))。

我如何有效地执行可以根据需要连接到其他表的查询?请注意,这是一个新数据库,我们拥有该架构,可以更改它或添加审计表以使此查询更容易。我们尝试了一种审计表方法(包含列:CarId、CarVersion、WheelId、WheelVersion、CarVerTime、WheelVerTime),但它并没有真正改善我们的查询。

示例查询:按原样显示 Car ID 1,包括其截至 9:50 的车轮记录。此查询应导致返回这两行:

WheelId, WheelVer, WheelVerTime, CarId, CarVer, CarVerTime, CarColour
   1         2         9:40        1       3       9:45      Yellow
   2         1         9:00        1       3       9:45      Yellow

我们能想到的最好的查询是这样的:

select c.CarId, c.VersionTime, w.WheelId,w.WheelVer,w.VersionTime,w.CarId
from Cars c, 
(    select w.WheelId,w.WheelVer,w.VersionTime,w.CarId
    from Wheels w
    where w.VersionTime <= "12 Jun 2009 09:50" 
     group by w.WheelId,w.CarId
     having w.WheelVer = max(w.WheelVer)
) w
where c.CarId = w.CarId
and c.CarId = 1
and c.VersionTime <= "12 Jun 2009 09:50" 
group by c.CarId, w.WheelId,w.WheelVer,w.VersionTime,w.CarId
having c.CarVer = max(c.CarVer)

而且,如果您想尝试这个,那么创建表和插入记录的 SQL 就在这里:

create table Wheels
(
WheelId int not null,
WheelVer int not null,
VersionTime datetime not null,
CarId int not null,
 PRIMARY KEY  (WheelId,WheelVer)
)
go

insert into Wheels values (1,1,'12 Jun 2009 09:00', 1)
go
insert into Wheels values (1,2,'12 Jun 2009 09:40', 1)
go
insert into Wheels values (1,3,'12 Jun 2009 10:05', 1)
go
insert into Wheels values (2,1,'12 Jun 2009 09:00', 1)
go


create table Cars
(
CarId int not null,
CarVer int not null,
VersionTime datetime not null,
colour varchar(50) not null,
 PRIMARY KEY  (CarId,CarVer)
)
go

insert into Cars values (1,1,'12 Jun 2009 09:00', 'Red')
go
insert into Cars values (1,2,'12 Jun 2009 09:30',  'Blue')
go
insert into Cars values (1,3,'12 Jun 2009 09:45',  'Yellow')
go
insert into Cars values (1,4,'12 Jun 2009 10:00',  'Black')
go

最佳答案

这种表在文献中称为有效时间状态表。普遍接受的是,每一行都应该通过具有开始日期和结束日期来模拟一个时期。基本上,SQL 中的工作单元是行,行应该完整地定义实体;通过每行只有一个日期,不仅您的查询会变得更加复杂,而且您的设计也会因为将子原子部分拆分到不同的行而受到损害。

正如 Erwin Smout 所提到的,关于该主题的权威书籍之一是:

理查德·斯诺德格拉斯 (1999)。 Developing Time-Oriented Database Applications in SQL

它已绝版,但很高兴可以免费下载 PDF(上面的链接)。

我确实读过它并实现了许多概念。大部分文本采用 ISO/ANSI 标准 SQL-92 格式,尽管有些已使用专有 SQL 语法实现,包括 SQL Server(也可下载),但我发现概念性信息更有用。

Joe Celko 也有一本书,“Thinking in Sets: Auxiliary, Temporal, and Virtual Tables in SQL”,主要源自 Snodgrass 的著作,但我不得不说出两者的不同之处,我认为 Snodgrass 的方法更可取。

我同意这些东西很难在我们目前拥有的 SQL 产品中实现。在使数据具有时间性之前,我们经过深思熟虑;如果我们能摆脱仅仅是“历史”的后果,那么我们会的。 SQL-92 中的许多时间功能在 SQL Server 中缺失,例如INTERVAL、OVERLAPS 等。在 SQL Server 中使用 CHECK 约束无法实现一些基本的东西,如排序的“主键”以确保周期不重叠,需要触发器和/或 UDF。

Snodgrass 的书基于他对 SQL3 的工作,SQL3 是对标准 SQL 的拟议扩展,旨在为时态数据库提供更好的支持,但遗憾的是,这似乎在多年前就被有效地搁置了:(

关于sql - 如何有效地做数据库as-of查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/986259/

相关文章:

sql - 如何从表中获取几个最高值?

database - Hibernate HQL查询分析工具?

database - Elmah 从电子邮件中过滤 404 错误但记录到数据库

database - 多次加入日期维度? - Kimball 关于数据仓库和维度建模的书

sql - Oracle SQL 可以在找到字符之前修剪字符吗?

sql - 可以从 t-sql 中远程执行查询吗?

sql - 具体复杂的SQL查询和Django ORM?

mysql - 我需要为新语言准备一个新表吗?

sql - 如何在数据库中实现元数据目录和自动化 SQL?

database-design - 为我的网站建立数据库