Sql 对包含日期和nvarchar(50)的列使用聚集索引而不是非聚集索引

Sql 对包含日期和nvarchar(50)的列使用聚集索引而不是非聚集索引,sql,sql-server,Sql,Sql Server,我有一张名为“GameTransactions”的表格。就性能而言,表工作良好至关重要(当站点开始运行时,表将有数百万条记录)。我想把它编索引。我用于列的列有: UserID [int], TransactionID [nvarchar(50)] ProviderID [int] TransactionTimeStamp [datetime] 关于我如何使用表格的一些上下文 在SQL操作开始时,我检查同一用户的事务ID是否存在 SELECT COUNT(1) FROM Game

我有一张名为“GameTransactions”的表格。就性能而言,表工作良好至关重要(当站点开始运行时,表将有数百万条记录)。我想把它编索引。我用于列的列有:

UserID [int],
TransactionID [nvarchar(50)]
ProviderID [int]
TransactionTimeStamp [datetime]
关于我如何使用表格的一些上下文

在SQL操作开始时,我检查同一用户的事务ID是否存在

   SELECT COUNT(1) 
    FROM GameTransactions WITH(NOLOCK)
    WHERE 
    UserID=@UserID AND
    TransactionID=@TransactionID 
    AND ProviderID=@ProviderID 
    AND TransactionTimeStamp>DATEADD(MONTH,-1,GETUTCDATE())
如果请求在数据库中不存在,我将插入它

我选择使用以下索引

CREATE CLUSTERED INDEX IX_GameTransactions_UserID_TransactionID_ProviderID_TransactionTimeStamp
ON dbo.GameTransactions (UserID,TransactionID,ProviderID,TransactionTimeStamp);   
我在这篇文章中读到:

datetime作为聚集索引中的一列可以实现良好的性能。我不关心聚集索引将占用的磁盘空间,我更关心的是速度性能

我还考虑了另一种解决方案

 CREATE NONCLUSTERED INDEX IX_GameTransactions_UserID_TransactionID_ProviderID_TransactionTimeStamp
 ON dbo.GameTransactions (UserID, Month, Year,ProviderID)
 INCLUDE (TransactionID);
我可以添加两个额外的列-月和年。并使用INT而不是date。请记住,“TransactionID”字段必须是nvarchar(50)。没有办法解决这个问题

我还有一个自动递增的Id列。这样的解决方案行得通吗

  CONSTRAINT PK_GameTransactions PRIMARY KEY CLUSTERED (
      UserID
    , TransactionID
    , ProviderID
    , TransactionTimeStamp
, Id
)

使用
EXISTS
而不是
COUNT
有条件地插入行。这将更有效,因为不需要计数。确保索引是唯一的,以确保不可能重复

对于时间戳条件,请使用
=
而不是
,这样具有相同时间戳的两个会话不会同时插入同一行,尽管如果存在唯一索引或约束,其中一个会话将出错

此外,考虑删除<代码> NoLooC.<代码>,以确保并发会话不会为TransactionTimeStamp日期范围内的相同USEID/TraceActudio/PosivRID ID插入行。为此,我建议

SERIALIZABLE
。下面的示例DDL,查询封装在下面的存储过程中,利用主键索引实现性能和数据完整性

CREATE TABLE dbo.GameTransactions(
      UserID int
    , TransactionID nvarchar(50)
    , ProviderID int
    , TransactionTimeStamp datetime
    CONSTRAINT PK_GameTransactions PRIMARY KEY CLUSTERED (
          UserID
        , TransactionID
        , ProviderID
        , TransactionTimeStamp
    )
);
GO

CREATE PROCEDURE dbo.InsertGameTransactions
      @UserID int
    , @TransactionID nvarchar(50)
    , @ProviderID int
AS
DECLARE @TransactionTimeStamp datetime = GETUTCDATE();
INSERT INTO dbo.GameTransactions (
      UserID
    , TransactionID 
    , ProviderID 
    , TransactionTimeStamp
)
SELECT
      @UserID
    , @TransactionID 
    , @ProviderID 
    , @TransactionTimeStamp
WHERE NOT EXISTS(
    SELECT 1
    FROM dbo.GameTransactions WITH(SERIALIZABLE)
    WHERE 
        UserID=@UserID AND
        TransactionID=@TransactionID 
        AND ProviderID=@ProviderID 
        AND TransactionTimeStamp >= DATEADD(MONTH,-1,@TransactionTimeStamp)
    );
GO

使用
EXISTS
而不是
COUNT
有条件地插入行。这将更有效,因为不需要计数。确保索引是唯一的,以确保不可能重复

对于时间戳条件,请使用
=
而不是
,这样具有相同时间戳的两个会话不会同时插入同一行,尽管如果存在唯一索引或约束,其中一个会话将出错

此外,考虑删除<代码> NoLooC.<代码>,以确保并发会话不会为TransactionTimeStamp日期范围内的相同USEID/TraceActudio/PosivRID ID插入行。为此,我建议

SERIALIZABLE
。下面的示例DDL,查询封装在下面的存储过程中,利用主键索引实现性能和数据完整性

CREATE TABLE dbo.GameTransactions(
      UserID int
    , TransactionID nvarchar(50)
    , ProviderID int
    , TransactionTimeStamp datetime
    CONSTRAINT PK_GameTransactions PRIMARY KEY CLUSTERED (
          UserID
        , TransactionID
        , ProviderID
        , TransactionTimeStamp
    )
);
GO

CREATE PROCEDURE dbo.InsertGameTransactions
      @UserID int
    , @TransactionID nvarchar(50)
    , @ProviderID int
AS
DECLARE @TransactionTimeStamp datetime = GETUTCDATE();
INSERT INTO dbo.GameTransactions (
      UserID
    , TransactionID 
    , ProviderID 
    , TransactionTimeStamp
)
SELECT
      @UserID
    , @TransactionID 
    , @ProviderID 
    , @TransactionTimeStamp
WHERE NOT EXISTS(
    SELECT 1
    FROM dbo.GameTransactions WITH(SERIALIZABLE)
    WHERE 
        UserID=@UserID AND
        TransactionID=@TransactionID 
        AND ProviderID=@ProviderID 
        AND TransactionTimeStamp >= DATEADD(MONTH,-1,@TransactionTimeStamp)
    );
GO

首先,聚集索引对比较没有好处

其次,我非常同意Dan的观点,即如果您关心性能,您应该使用
EXISTS
而不是
SELECT COUNT(*)

第三,你从博客上获取了错误的信息。聚集索引的问题是数据在数据页上按顺序存储。当您有一个聚集索引时,当您必须在其他行之间插入行时,您可能会有一个很大的性能瓶颈

因此,通常的建议是使用
identity
列作为聚集索引键(顺便说一句,这是默认值)。这是一个很好的建议,但也有其他情况。例如,
newsequentialid()
是一个生成适用于聚集索引的GUI的函数,因为它们(几乎总是)在增加


在您的情况下,索引中的第一列不是日期/时间。因此,在使用这样的聚集索引时,您可能会遇到大量的碎片问题。对于要执行的操作,没有理由在数据页上排序数据。只需将所有需要的列作为键使用常规索引。

首先,聚集索引对比较没有好处

其次,我非常同意Dan的观点,即如果您关心性能,您应该使用
EXISTS
而不是
SELECT COUNT(*)

第三,你从博客上获取了错误的信息。聚集索引的问题是数据在数据页上按顺序存储。当您有一个聚集索引时,当您必须在其他行之间插入行时,您可能会有一个很大的性能瓶颈

因此,通常的建议是使用
identity
列作为聚集索引键(顺便说一句,这是默认值)。这是一个很好的建议,但也有其他情况。例如,
newsequentialid()
是一个生成适用于聚集索引的GUI的函数,因为它们(几乎总是)在增加


在您的情况下,索引中的第一列不是日期/时间。因此,在使用这样的聚集索引时,您可能会遇到大量的碎片问题。对于要执行的操作,没有理由在数据页上排序数据。只需使用一个常规索引,将所需的所有列作为键即可。

“当您必须在“其他行”之间插入行时,可能会遇到很大的性能瓶颈。”-我从不需要在其他行之间插入行。”。只需使用一个常规索引,将所有需要的列作为键即可。-那么,你是说我应该使用这些列作为主键,而不是使用聚集索引。为什么在where子句中为几列创建集群或非集群不能提高性能,为什么我的解决方案是错误的,我应该只使用一个自动递增主键?“当您必须在“其他行”之间插入行时,您可能会遇到很大的性能瓶颈。-我从不需要在其他行之间插入行。”。只需使用一个包含所有t的正则索引