2009-06-12 33 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 
相關問題