Sql 对包含日期和nvarchar(50)的列使用聚集索引而不是非聚集索引
我有一张名为“GameTransactions”的表格。就性能而言,表工作良好至关重要(当站点开始运行时,表将有数百万条记录)。我想把它编索引。我用于列的列有: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
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的正则索引