了解主键如何工作&如何使用它

时间:2015-08-24 12:27:41

标签: sql-server database-design sql-server-2012 primary-key

我正在使用SQL Server并创建一个表(示例位于此问题的最底部)。但是我有一些问题需要了解主键实际上是如何工作的。如何正确使用它们。

所以我知道主键确保表中的所有行都是唯一的,并且主键不能为空。我还阅读了关于索引的index basics - simple talk页面以及如何在b树结构中组织索引。

所以在我的表中有一个具有唯一值的行,我将不得不使用前3列(UploadDate,SecID& FundCode类型为datetime,varchar(12)& varchar(6))。此表格仅使用选择查询。 where子句将使用刚刚提到的三个字段中的一个或多个。

所以我知道可以在多个列上创建主键,因此在我的情况下,它将是上面的3个。如何在我的表上使用主键有助于提高选择查询的性能?所以我认为主键创建了一个索引或某种类型的列的值(或者在我的情况下是3列),我不知道这将如何帮助,因为我的价值将是一个日期时间&两位文字?

有人提到我应该创建一个带有递增数字的整数列并将其作为主键 - 我无法看到在运行选择查询时这有什么帮助,因为新字段不会有任何帮助含义&不会在查询的任何选择查询或where子句中使用?

b tree

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

1 个答案:

答案 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列作为复合(群集)主键,如果我知道没有子表,那么这就是我所有的选择查询仍然需要我选择UploadedDateSecIDFundCode。如果您在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同意,然后答案真的必须是个人偏好),以及你的确切情况,在某些情况下你会想要一个复合主键,在某些情况下你会想要一个代理键,在某些情况下你会想要你的主键和你的聚类键是相同的,在其他情况下你不会。