我正在使用SQL Server并创建一个表(示例位于此问题的最底部)。但是我有一些问题需要了解主键实际上是如何工作的。如何正确使用它们。
所以我知道主键确保表中的所有行都是唯一的,并且主键不能为空。我还阅读了关于索引的index basics - simple talk页面以及如何在b树结构中组织索引。
所以在我的表中有一个具有唯一值的行,我将不得不使用前3列(UploadDate,SecID& FundCode类型为datetime,varchar(12)& varchar(6))。此表格仅使用选择查询。 where子句将使用刚刚提到的三个字段中的一个或多个。
所以我知道可以在多个列上创建主键,因此在我的情况下,它将是上面的3个。如何在我的表上使用主键有助于提高选择查询的性能?所以我认为主键创建了一个索引或某种类型的列的值(或者在我的情况下是3列),我不知道这将如何帮助,因为我的价值将是一个日期时间&两位文字?
有人提到我应该创建一个带有递增数字的整数列并将其作为主键 - 我无法看到在运行选择查询时这有什么帮助,因为新字段不会有任何帮助含义&不会在查询的任何选择查询或where子句中使用?
type column name
------------- ------------
datetime UploadDate
varchar(12) SecID
varchar(6) FundCode
varchar(100) Name
float Price
float Nominal
int SourceCode
datetime PriceDate
某些行的示例
UploadDate SecID FundCode Name Price Nominal SourceCode PriceDate
2015-08-20 A045 ABCVPL Joe 1.3434 1000.33 3
2015-08-20 A563 ABCVPL Bob 1.5961 10.33 3
2015-08-20 A045 DEFGHJ Joe 1.3434 856.41 3
2015-08-20 XC45 PLMNOI Pip 2.3654 25.52 3
2015-08-20 KMM5 ABCVPL Nit 6.9565 1532 3
2015-08-21 A045 ABCVPL Joe 4.3434 1112 3
2015-08-21 GH45 DEFGHJ Joe 3.3434 16532 3
2015-08-21 PL34 DEFGHJ Joe 7.3434 635 3
2015-08-21 ER33 ABCVPL Joe 8.3434 6320 3
答案 0 :(得分:3)
这个问题似乎混淆了两个不同的概念。第一个是主键,第二个是聚簇索引。第一个是逻辑概念,后者是物理概念,指的是数据的实际存储方式。在某些情况下,解耦主键和群集键很有用,但大多数情况下它们是同一个,默认情况下,主键将是您的群集键。尽管如此,这是一个重要的区别。
我认为人们可以(并且已经)争论过,直到奶牛回家询问是否use a natural or surrogate primary key。我没有太多关注这个问题,但基本就是你在建议使用3列时会建议一个唯一的行是一个自然键(即已经存在于你的数据中),另一种方法是使用一个标识列,它将为每一行提供一个唯一值,这是一个代理键,因为除了唯一标识您的行之外,它没有任何实际意义。
所以我知道可以在多个列上创建主键,因此在我的情况下,它将是上面的3个。怎么会有一个 我的表上的主键有助于提高选择查询的性能?
根据您的查询,索引可能会有所帮助。给定正确的索引,数据库引擎能够直接导航到所需的数据。
有人提到我应该创建一个带有递增数字的整数列并将其作为主键 - 我无法看到在运行选择查询时这有什么帮助,因为新字段不会有任何帮助含义&不会在查询的任何选择查询或where子句中使用?
这是群集密钥的理想选择。根据金伯利·特里普索引的女王clustered index should be:
你已经勾选了这个独特的方框,你的3列,这不是那么窄,但绝不宽。第二个我不能回答,如果UploadDate
是在创建时输入的默认值,那么你可能会有一个不断增加的模式,我不知道你的三列是静态的还是它们可能会改变。如果最后两个中的任何一个都为真,那么无论如何都应该使用代理标识列进行聚类。
我个人可能已经将其作为基于with(26字节)的聚类键的候选者而消除了。在聚簇索引中每行额外增加4个字节,但在所有后续索引中每行可节省22个字节。
因此,在10,000,000行的表中,由于标识列,您获得额外的38.1 MB,但是每个非聚集索引获得209.8 MB,尽管磁盘空间很便宜,但这并不是不必要地浪费它的理由。它不仅仅是获得这22个字节的所有索引,它还引用了具有外键的表,这导致了我的下一点,即编写查询时的便利性。你真的想在每次引用密钥时输入这个连接:
SELECT *
FROM Parent AS p
INNER JOIN Child AS c
ON c.UploadDate = p.UploadDate
AND c.SecID = p.SecID
AND c.FundCode = p.FundCode;
或者你愿意简单地写一下:
SELECT *
FROM Parent AS p
INNER JOIN Child AS c
ON c.ParentID = p.ParentID;
由于这个原因,即使我已经确定逻辑上主键不适合聚类键,我仍然倾向于使聚类键成为关键表中易于引用的主键。例如,我有一个外部API,用XML发送订单详细信息:
<orders>
<order ID="12B47EF2-B9F5-4CD7-811F-2E7EC1A67E59">
<orderdetail>
<product>Some Product</product>
<quantity>1</quantity</quantity>
</orderdetail>
<orderdetail>
<product>Some Other Product</product>
<quantity>2</quantity</quantity>
</orderdetail>
</order>
<order ID="3A819217-49CA-4B4C-8AD5-CAD297FCA3F3">
<etc />
</order>
</orders>
如果我正在设置我的表来存储它,虽然来自XML的ID将是我的Orders
表的逻辑主键,但它将是一个可怕的聚类键,所以我将添加一个代理身份字段避免与GUID上的群集相关联的碎片:
CREATE TABLE dbo.Orders
(
OrderID INT IDENTITY NOT NULL,
SupplierOrderID UNIQUEIDENTIFIER NOT NULL,
CONSTRAINT PK_Orders__SupplierOrderID PRIMARY KEY NONCLUSTERED (SupplierOrderID)
);
CREATE UNIQUE CLUSTERED INDEX UQ_Orders__OrderID ON dbo.Orders (OrderID);
GUID仍然是主键,所以我的订单明细表可以参考这个,但我一般认为如果我不认为密钥足够集中,为什么我会把相同的密钥放在一起作为外键进入另一个表。我已经在OrderID
中定义了一个更窄的密钥,为什么不在订单详细信息中使用它作为我的外键,并保存自己12个字节。所以我最终得到:
CREATE TABLE dbo.Orders
(
OrderID INT IDENTITY NOT NULL,
SupplierOrderID UNIQUEIDENTIFIER NOT NULL,
CONSTRAINT PK_Orders__OrderID PRIMARY KEY CLUSTERED (OrderID)
);
CREATE UNIQUE NONCLUSTERED INDEX UQ_Orders__SupplierOrderID ON dbo.Orders (SupplierOrderID);
与所有内容一样,也有例外情况,在某些情况下我会选择3列作为复合(群集)主键,如果我知道没有子表,那么这就是我所有的选择查询仍然需要我选择UploadedDate
,SecID
和FundCode
。如果您在Name
上有索引,例如:
CREATE NONCLUSTERED INDEX IX_YourTable__Name ON dbo.YourTable (Name);
SELECT UploadDate, SecID, FundCode, Name
FROM dbo.YourTable
WHERE Name = 'Bob';
如果您有代理键,那么您将通过名称索引寻找并仅在第2行找到Bob,然后在聚集索引上查找第2行以获取UploadedDate
的相应值,{{1} }和SecID
。如果这三列是您的群集键,那么您不需要查找,因为您已经拥有FundCode
索引中的数据。每个索引额外的209.8MB可能值得避免这些查找操作。
总结(通常情况下),这取决于你的个人偏好(我相信Aaron Bertrand和Joe Celko在自然与代理关键辩论方面仍然存在争执,如果这两位伟大的思想能够和他们相提并论。 t同意,然后答案真的必须是个人偏好),以及你的确切情况,在某些情况下你会想要一个复合主键,在某些情况下你会想要一个代理键,在某些情况下你会想要你的主键和你的聚类键是相同的,在其他情况下你不会。