我有一个名为“ 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);
我在这篇文章中读到
https://sqlstudies.com/2014/12/01/using-a-date-or-int-column-as-the-clustered-index/
使用datetime作为聚簇索引中的一列可以实现良好的性能。我不在乎聚集索引将要占用的磁盘空间,我更关心速度性能。
我还考虑了替代解决方案
CREATE NONCLUSTERED INDEX IX_GameTransactions_UserID_TransactionID_ProviderID_TransactionTimeStamp
ON dbo.GameTransactions (UserID, Month, Year,ProviderID)
INCLUDE (TransactionID);
我可以添加2列-月份和年份。并使用整数而不是日期。请记住,“ TransactionID”字段必须为nvarchar(50)。无法解决它。
我还有一个ID列,它是自动递增的。这样的解决方案行得通吗?
CONSTRAINT PK_GameTransactions PRIMARY KEY CLUSTERED (
UserID
, TransactionID
, ProviderID
, TransactionTimeStamp
, Id
)
答案 0 :(得分:1)
使用EXISTS
代替COUNT
有条件地插入行。由于不需要计数,因此效率更高。确保索引是唯一的,以确保不可能重复。
使用>=
代替>
作为时间戳记标准,以使2个具有相同时间戳记的会话都不会插入同一行,尽管如果存在唯一索引或约束,则会出错。 / p>
此外,考虑删除NOLOCK
以确保并发会话在TransactionTimeStamp日期范围内不会为相同的UserID / TransactionID / ProviderID插入行。为此,我建议使用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
答案 1 :(得分:1)
首先,聚集索引对您的比较没有好处。
第二,我非常同意Dan,如果您关心性能,应该使用EXISTS
而不是SELECT COUNT(*)
。
第三,您正在从博客中获取错误消息。聚集索引的问题是数据按顺序存储在数据页上。如果具有聚集索引,则必须在其他行之间插入行时会遇到很大的性能瓶颈。
因此,通常的建议是使用identity
列作为聚集索引键(顺便说一下,这是默认值)。这是一个很好的建议,但也有其他情况。例如,newsequentialid()
是一种函数,它生成适合于聚集索引的GUID,因为它们(几乎总是)在增加。
在您的情况下,索引中的第一列不是日期/时间。因此,在使用这种聚集索引时,您可能会遇到很多碎片问题。对于您想做的事情,没有理由在数据页上排序数据。只需将常规索引与所有需要的列用作键。