2009-06-12 38 views
3

不好意思!如何有效地执行数据库初始查询?

我们有两个数据库表,例如汽车和车轮。他们相关的一个车轮属于一辆车,一辆车有多个车轮。但是,车轮可以在不影响汽车“版本”的情况下进行更改。在不影响车轮版本的情况下(即没有级联更新),汽车的记录可以被更新(例如绘画作业)。

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

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

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

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更换(例如涂漆)。

如何有效地完成作为可以加入到其他表的查询要求?请注意,这是一个新的数据库,我们拥有该模式,并可以更改它或添加审计表以简化查询。我们尝试了一种审计表方法(列:CarId,CarVersion,WheelId,WheelVersion,CarVerTime,WheelVerTime),但它并没有真正改善我们的查询。

实施例的查询:显示汽车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) 

而且,如果你想尝试这则create table和insert记录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 

回答

3

这种表在文献中被称为有效时间状态表。普遍接受的观点是,每一行都应该通过开始日期和结束日期来模拟一段时期。基本上,SQL中的工作单元是行和行应完全定义实体;通过每行只有一个日期,不仅您的查询变得更加复杂,您的设计也会通过将子原子部分分割到不同的行中而受到影响。由欧文斯莫特,对这个主题的权威著作之一提到

由于是:

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

它已绝版,但可以免费下载PDF格式(以上链接)。

我已经看过它并且已经实现了很多概念。大部分文本都在ISO/ANSI标准SQL-92中,尽管有些已经在专有SQL语法中实现,包括SQL Server(也可作为下载文件),但我发现这些概念信息更有用。

Joe Celko还有一本书'Thinking in Sets:SQL中的辅助,时间和虚拟表',主要来源于Snodgrass的工作,尽管我不得不说我认为Snodgrass的方法更可取。

我同意这个问题很难在我们目前拥有的SQL产品中实现。在进行数据处理之前,我们会考虑时间长,如果我们能够脱离“历史”,那么我们就会。 SQL Server中缺少SQL-92中的许多临时功能,例如INTERVAL,OVERLAPS等。为了确保周期不重叠,有些与测序“主键”一样重要的事情无法使用SQL Server中的CHECK约束来实现,因此需要触发器和/或UDF。

斯诺德格拉斯的书是基于他对SQL3工作中,建议延长对标准SQL来对时间数据库提供了更好的支持,但遗憾的是,这似乎搁置年前已经得到有效:(

1

如果每行有开始和结束时间,则查询更容易。在表中存储的结束时间将是最有效的,但如果这是很难的,你可以查询,如:

select 
    ThisCar.CarId 
, StartTime = ThisCar.VersionTime 
, EndTime = NextCar.VersionTime 
from Cars ThisCar 
left join Cars NextCar 
    on NextCar.CarId = ThisCar.CarId 
    and ThisCar.VersionTime < NextCar.VersionTime 
left join Cars BetweenCar 
    on BetweenCar.CarId = BetweenCar.CarId 
    and ThisCar.VersionTime < BetweenCar.VersionTime 
    and BetweenCar.VersionTime < NextCar.VersionTime 
where BetweenCar.CarId is null 

您可以存储此视图中。说的观点被称为vwCars,您可以选择一车一特定日期,如:

select * 
from vwCars 
where StartTime <= '2009-06-12 09:15' 
and ('2009-06-12 09:15' < EndTime or EndTime is null) 

你可以存储在这个价值存储过程中的表,但是,很可能有一个陡峭的性能损失。

+0

您的查询效率更高(表扫描更少),但不执行as-of查询。您的查询只获取最新版本,而不是09:50的版本。我们可能能够从您的查询中获得一些想法,所以谢谢。 – ng5000 2009-06-12 12:34:33

+0

我们将无法使用视图,因为我们需要将查询的时间组件传递到查询中。 SP可能是一个选项,但如果不得不加入其他表格,我们可能需要查看表格功能 – ng5000 2009-06-12 12:39:15

1

根据您的应用程序,你可能要推版本二次审核表,这将同时拥有一个起点和一个可为空的结束日期。我发现在一个高流量的OLTP中,使用版本控制方法会变得相当昂贵,如果大部分读取操作都是最新版本,那么这可能是有益的。

通过使用开始和结束日期,你可以查询寻找一个是开始之间的日期辅助表,并停止或大于启动。

1

存储的结束时间在表中的每个情况使得查询确实更容易表达,但创建保持完整性规则,如对同一辆车“没有两个不同的情况(轮/ ...)可以重叠的问题“(仍然合理可行)和”在任何单个(汽车/车轮/ ...)的不同情况下的时间序列中都不能出现漏洞“(更麻烦)。

对于每种情况,不在表中存储结束时间会迫使您每次需要在唯一隐含的时间间隔上调用Allen运算符(重叠,合并,包含...)时编写自连接你有时间专栏。

SQL只是一个噩梦,如果你需要做这种时间的东西。另外,即使只是用自然语言准确地表达这些查询也是一场噩梦。为了说明:你说你需要“现在”查询,但是你的例子排除了“现在”10:05(第三轮)和10:00(黑色)的情况。尽管事实上这些情况肯定也是“现在的”09:50。

您可能有兴趣阅读“时间数据和关系模型”。请记住,本书中的处理方式完全是抽象的,因为正如书中所说,“这本书不是关于今天任何地方可用的技术”。

关于这个问题的其他标准教科书(我被告知)是由斯诺德格拉斯编写的,但我不知道标题。我被告知这两本书的作者对于解决方案应该采取什么完全相反的立场。

1

这个查询将返回如果您的单行车ID有相同的两个版本时间相同的行,则重复,但这是一个定义您认为在那种情况下是“最新”的行为的问题,我还没有机会对此进行测试,但我认为它会给你你需要的东西,它至少非常接近。

SELECT 
    C.car_id, 
    C.car_version, 
    C.colour, 
    C.version_time AS car_version_time, 
    W.wheel_id, 
    W.wheel_version, 
    W.version_time AS wheel_version_time, 
FROM 
    Cars C 
LEFT OUTER JOIN Cars C2 ON 
    C2.car_id = C.car_id AND 
    C2.version_time <= @as_of_time AND 
    C2.version_time > C.version_time 
LEFT OUTER JOIN Wheels W ON 
    W.car_id = C.car_id AND 
    W.version_time <= @as_of_time 
LEFT OUTER JOIN Wheels W2 ON 
    W2.car_id = C.car_id AND 
    W2.wheel_id = W.wheel_id AND 
    W2.version_time <= @as_of_time AND 
    W2.version_time > W.version_time 
WHERE 
    C.version_time <= @as_of_time AND 
    C2.car_id IS NULL AND 
    W2.wheel_id IS NULL 
相关问题