我目前正在开发一个数据库,需要一个日志来跟踪一堆不同的数据变化。价格变化,项目状态变化等等。为了实现这一点,我制作了不同的“日志”表,用于存储需要保存的数据。
举一个可靠的例子,为了跟踪需要订购的零件的价格变化,我创建了一个名为Part_Price_Log
的表。主键是由修改零件价格的日期和Parts
表上零件唯一ID的外键组成的组合。
我的逻辑是,如果您需要查找零件的当前价格,您只需要找到该零件ID的最新条目。但是,我被告知不要以这种方式实现它,因为使用Date作为主键的一部分是一种简单的方法来获取数据中的错误。
所以我的问题就是这样。
使用Date列作为复合主键的一部分有哪些优缺点?有哪些更好的选择?
答案 0 :(得分:3)
一般来说,我认为最好的主键是合成自动增量键。这些都有一定的优势:
根据您的数据建议,其中第四个是包含大量插入的数据库中的一个非常大的问题。
复合主键没有先验错误。它们有时很有用。但这不是我想要的方向。
答案 1 :(得分:1)
在不了解您的域名的情况下,建议真的很难。您如何识别现实世界中的一部分?我们假设您使用EAN。这是你的“自然钥匙”。现在,每次价格变化时,一部分是否会获得新的EAN?可能不是,在这种情况下,零件价格的真实世界标识符是其EAN与该价格有效的时间段的组合。
我认为关于“一种简单的方法来获取数据错误”的评论指的是时间数据库不仅本质上更复杂(它们还有一个额外的维度 - 时间),对时间功能的支持是缺乏大多数SQL DBMS。
例如,您选择的SQL产品是否具有interval
数据类型,或者您是否需要使用一对start_date
和end_date
列来推广自己的产品?您选择的SQL产品是否具有表内约束的能力,例如防止同一部分的重叠或非并发间隔?您的SQL产品是否具有轻松查询时态数据的时态函数?
答案 2 :(得分:1)
根据性能要求以及查询此表的频率,优缺点会有所不同。
作为第一个例子,请考虑以下事项:
GHCi> foo (\m -> m (show . not))
"False"
GHCi> bar (\m -> m (show . not))
"False"
如果CREATE TABLE Part_Price_Log (
ModifiedDate DATE,
PartID INT,
PRIMARY KEY (ModifiedDate, PartID))
是第一个并且这是一个只有插入行的日志表,那么每个新行都将放在最后,这很好(减少了碎片)。如果您希望按ModifiedDate
或ModifiedDate
+ ModifiedDate
直接过滤,则此方法也很有用,因为PartID
是主键中的第一列。这里的con将是ModifiedDate
搜索,因为主键的聚集索引将无法直接搜索PartID
。
第二个例子是相同但反向的主键排序:
PartID
这适用于CREATE TABLE Part_Price_Log (
ModifiedDate DATE,
PartID INT,
PRIMARY KEY (PartID, ModifiedDate))
的查询,但PartID
直接查询的数据不多。首先使用ModifiedDate
会使插入替换数据页,因为插入的PartID
低于最大PartID
(这会增加碎片)。
最后一个例子是使用代理主键,如PartID
。
IDENTITY
这将使所有插入最后一次并减少碎片,但您需要一个额外的索引来查询您的数据,例如:
CREATE TABLE Part_Price_Log (
LogID BIGINT IDENTITY PRIMARY KEY,
ModifiedDate DATE,
PartID INT)
关于最后一个的问题是插入操作将花费更长时间(因为索引也必须更新),并且由于索引,表的大小将增加。
另请注意,如果您的数据允许同一天的同一部分进行多次更新,那么使用复合CREATE NONCLUSTERED INDEX NCI_Part_Price_Log_Date_PartID ON Part_Price_Log (ModifiedDate, PartID)
CREATE NONCLUSTERED INDEX NCI_Part_Price_Log_PartID_Date ON Part_Price_Log (PartID, ModifiedDate)
会使第二次更新失败。您在此处的选择是使用代理键,使用PRIMARY KEY
代替DATETIME
(将为您提供更多的更新余量),或使用DATE
而不使用CLUSTERED INDEX
或PRIMARY KEY
约束。
我建议做以下事情。您只保留一个索引(实际表格,因为它是群集的),订单始终是插入的,您不必担心重复的UNIQUE
具有相同的ModifiedDate
,您的查询将按日期进行快。
PartID
答案 3 :(得分:0)
我同意在这种情况下最好将标识列/ uniqueidentifier保留为主键,如果将partid和date设置为复合主键,则在两个并发用户尝试更新的情况下,它将失败在这种情况下,主要密钥将失败。因此,更好的方法是将标识列作为主键并继续转储日志表中的更改。如果您稍后遇到一些性能障碍你可以明智地对你的桌子进行分区,并且可以克服性能挑战。