2009-04-13 28 views
4

假設的例子:在關係數據庫中存儲(和訪問)歷史1:M關係的最佳方式是什麼?

我有車和業主。每輛車在給定時間屬於一個(且只有一個)車主,但所有權可能會轉移。業主可以隨時擁有零或更多的汽車。我想要的是將歷史關係存儲在MySQL數據庫中,在給定任意時間的情況下,我可以查看Cars to Owners的當前任務。

I.e.在時間X(其中X可以是現在或過去的任何時間):

  • 誰擁有汽車Y?
  • 擁有者Z擁有哪輛車(如果有的話)?

創建M:N表中的SQL(帶有時間戳)是很簡單的,但我想,以避免相關子查詢作爲該表將得到大(和,因此,性能將受到影響)。有任何想法嗎?我有一種感覺,有辦法通過自己加入這樣一張表來實現這一點,但我對數據庫並不熟悉。

更新:我想避免每行都使用「start_date」和「end_date」字段,因爲每次插入新行時都需要(可能)昂貴的查找。 (另外,這是多餘的)。

回答

9

製作第三張名爲CarOwners的表,用於carid,ownerid的字段,並開始_日期和結束日期爲_。 購買汽車時,請填寫前三位並查看錶格以確保沒有其他人被列爲車主。如果存在,則用該數據更新記錄,作爲結束日期_。

要查找當前所有者:

select carid, ownerid from CarOwner where end_date is null 

要找到一個點所有者時間:

select carid, ownerid from CarOwner where start_date < getdate() 
and end_date > getdate() 

GETDATE()是MS SQL Server特定的,但每個數據庫都有一些函數,返回當前日期 - 只需替換。

當然,如果你還想從其他表中獲得更多信息,你也可以加入他們。

select co.carid, co.ownerid, o.owner_name, c.make, c.Model, c.year 
from CarOwner co 
JOIN Car c on co.carid = c.carid 
JOIN Owner o on o.ownerid = co.ownerid 
where co.end_date is null 
+0

這個問題是它需要一個(可能是昂貴的)插入查找。我寧願不使用「end_date」字段出於這個原因(也是多餘的)。 – 2009-04-13 20:45:58

0

汽車表可以稱爲OWNERID一個id,那麼你可以簡單地

從汽車內1.select車車主一起在car.ownerid = owner.ownerid其中OWNERID = Y

2.select車來自汽車的所有者= z

不是確切的語法,而是簡單的僞代碼。

+0

這很好的狀態的現在,但如果我想在一週前看到這個作業,那該怎麼辦? – 2009-04-13 20:31:44

0

鑑於你的業務規則,每輛車屬於至少一個所有者(即。存在業主,他們被分配到前車AA)和您的運營約束,該表可能變大,如下我設計的架構:

(一般SQL 92語法:)

CREATE TABLE Cars 
(
CarID integer not null default autoincrement, 
OwnerID integer not null, 
CarDescription varchar(100) not null, 
CreatedOn timestamp not null default current timestamp, 
Primary key (CarID), 
FOREIGN KEY (OwnerID) REFERENCES Owners(OwnerID) 

) 

CREATE TABLE Owners 
(
OwnerID integer not null default autoincrement, 
OwnerName varchar(100) not null, 
Primary key(OwnerID) 
) 

CREATE TABLE HistoricalCarOwners 
(
CarID integer not null, 
OwnerID integer not null, 
OwnedFrom timestamp null, 
Owneduntil timestamp null, 
primary key (cardid, ownerid), 
FOREIGN KEY (OwnerID) REFERENCES Owners(OwnerID), 
FOREIGN KEY (CarID) REFERENCES Cars(CarID) 
) 

我個人不會碰到我的客戶端應用程序中的第三張表格,但只需讓數據庫完成工作並保持數據完整性 - 每當汽車改變所有者時(即,只要UPDATE是UPDATE,汽車表格上的ON UPDATEON DELETE觸發器就會填充HistoricalCarOwners表格在OwnerId列上承諾)或汽車被刪除。

通過上述模式,選擇目前的車主是微不足道的,選擇歷史車主是一個簡單的

select ownerid, ownername from owners o inner join historicalcarowners hco 
             on hco.ownerid = o.ownerid 
             where hco.carid = :arg_id and 
               :arg_timestamp between ownedfrom and owneduntil 
order by ... 

HTH,文斯

0

如果你真的不希望有一個開始和結束日期,您可以只使用一個日期並執行類似下面的查詢。

SELECT * FROM CarOwner co 
WHERE co.CarId = @CarId 
     AND co.TransferDate <= @AsOfDate 
     AND NOT EXISTS (SELECT * FROM CarOwner co2 
       WHERE co2.CarId = @CarId 
        AND co2.TransferDate <= @AsOfDate 
        AND co2.TransferDate > co.Transferdate) 

或略有變化

SELECT * FROM Car ca 
    JOIN CarOwner co ON ca.Id = co.CarId 
    AND co.TransferDate = (SELECT MAX(TransferDate) 
        FROM CarOwner WHERE CarId = @CarId 
              AND TransferDate < @AsOfDate) 
WHERE co.CarId = @CarId 

這些解決方案在功能上等同於哈維爾的建議,但具體取決於所使用一種解決方案可能比其他快數據庫。

但是,根據您的讀寫比例,如果冗餘更新關聯實體中的結束日期,您可能會發現性能更好。

2

我發現處理這種需求的最好方法就是維護一個VehicleEvent的日誌,其中一個是ChangeOwner。在實踐中,您可以從這裏提出的所有問題的答案 - 至少與您收集事件一樣準確。

每條記錄​​都有一個時間戳,指示事件發生的時間。

這樣做的一個好處是可以在每個事件中添加最少量的數據,但有關車輛的信息可以積累和發展。

而且,與時間戳,可事後當事件發生時添加的事件(只要時間戳準確反映。

試圖保持我有任何其他方式這樣的事情歷史狀態試圖導致瘋狂(也許我還在恢復中:d)。

順便說一句,這裏的顯着特徵可能是,它是一個時間序列或事件日誌,而不是它的1:M

1

爲什麼不有一個交易表?它將包含汽車ID,FROM所有者,TO所有者和交易發生的日期。

然後,您所要做的就是在所需日期之前查找汽車的第一筆交易。

要查找3月1日由業主253擁有的汽車:

SELECT * FROM transactions WHERE ownerToId = 253和date> '2009-03-01'

相關問題