Sql server T-SQL:增加产品视图计数的存储过程
我是sql的初学者,我需要编写一个SP以增加产品视图数。当用户在网站上搜索时,我们希望增加搜索返回的所有产品的计数器。我发现我的SP存在两个问题: 它使用游标 它启动了很多交易 SP由多个线程同时调用。在我实现它之后,我得到了许多超时异常。我的计数表如下所示:Sql server T-SQL:增加产品视图计数的存储过程,sql-server,sql-server-2008,tsql,stored-procedures,transactions,Sql Server,Sql Server 2008,Tsql,Stored Procedures,Transactions,我是sql的初学者,我需要编写一个SP以增加产品视图数。当用户在网站上搜索时,我们希望增加搜索返回的所有产品的计数器。我发现我的SP存在两个问题: 它使用游标 它启动了很多交易 SP由多个线程同时调用。在我实现它之后,我得到了许多超时异常。我的计数表如下所示: CREATE PROCEDURE [dbo].[IncrementProductsViews] -- Add the parameters for the stored procedure here @Produc
CREATE PROCEDURE [dbo].[IncrementProductsViews]
-- Add the parameters for the stored procedure here
@ProductsIds as varchar(max) = '', --CSV ids of products that were returned by search
@ViewType int,
@Timestamp datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @id int
DECLARE idsCursor CURSOR FOR
SELECT Data FROM dbo.Split(@ProductsIds,',')
OPEN idsCursor
FETCH NEXT FROM idsCursor INTO @id
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRAN
UPDATE dbo.ProductsViewsCount SET Count = Count + 1
WHERE ProductId = @id AND ViewType = @ViewType AND Timestamp = @Timestamp
if @@rowcount = 0
BEGIN
INSERT INTO dbo.ProductsViewsCount (ProductId, Timestamp, ViewType, Count)
VALUES (@id, @Timestamp, @ViewType, 1)
END
COMMIT TRAN
FETCH NEXT FROM idsCursor INTO @id
END
CLOSE idsCursor
DEALLOCATE idsCursor
select 1
END
ProductsViewCountProductId int,时间戳datetime,ViewType int,Count int
Tiemstamp列四舍五入到调用SP的.net代码中最接近的小时。基本上,我将按小时计算视图
SP如下所示:
CREATE PROCEDURE [dbo].[IncrementProductsViews]
-- Add the parameters for the stored procedure here
@ProductsIds as varchar(max) = '', --CSV ids of products that were returned by search
@ViewType int,
@Timestamp datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @id int
DECLARE idsCursor CURSOR FOR
SELECT Data FROM dbo.Split(@ProductsIds,',')
OPEN idsCursor
FETCH NEXT FROM idsCursor INTO @id
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRAN
UPDATE dbo.ProductsViewsCount SET Count = Count + 1
WHERE ProductId = @id AND ViewType = @ViewType AND Timestamp = @Timestamp
if @@rowcount = 0
BEGIN
INSERT INTO dbo.ProductsViewsCount (ProductId, Timestamp, ViewType, Count)
VALUES (@id, @Timestamp, @ViewType, 1)
END
COMMIT TRAN
FETCH NEXT FROM idsCursor INTO @id
END
CLOSE idsCursor
DEALLOCATE idsCursor
select 1
END
我可以更有效地执行此操作吗?您可以通过设置操作而不是光标执行此操作:
CREATE PROCEDURE [dbo].[IncrementProductsViews]
-- Add the parameters for the stored procedure here
@ProductsIds as varchar(max) = '', --CSV ids of products that were returned by search
@ViewType int,
@Timestamp datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
;WITH CTE AS
(
SELECT *
FROM dbo.ProductsViewsCount
WHERE ViewType = @ViewType AND [Timestamp] = @Timestamp
)
MERGE CTE AS A
USING (SELECT * FROM dbo.Split(@ProductsIds,',')) B
ON A.ProductId = B.Data
WHEN MATCHED THEN UPDATE SET A.[Count] = B.[Count] + 1
WHEN NOT MATCHED BY TARGET THEN
INSERT(ProductId, [Timestamp], ViewType, [Count])
VALUES(Data, @Timestamp, @ViewType, 1);
SELECT 1 -- I don't know why this is here
END
您可以在集合操作而不是光标上执行此操作:
CREATE PROCEDURE [dbo].[IncrementProductsViews]
-- Add the parameters for the stored procedure here
@ProductsIds as varchar(max) = '', --CSV ids of products that were returned by search
@ViewType int,
@Timestamp datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
;WITH CTE AS
(
SELECT *
FROM dbo.ProductsViewsCount
WHERE ViewType = @ViewType AND [Timestamp] = @Timestamp
)
MERGE CTE AS A
USING (SELECT * FROM dbo.Split(@ProductsIds,',')) B
ON A.ProductId = B.Data
WHEN MATCHED THEN UPDATE SET A.[Count] = B.[Count] + 1
WHEN NOT MATCHED BY TARGET THEN
INSERT(ProductId, [Timestamp], ViewType, [Count])
VALUES(Data, @Timestamp, @ViewType, 1);
SELECT 1 -- I don't know why this is here
END
使用SQL Server 2008,您有了一个不错的新选项http://technet.microsoft.com/en-us/library/bb510625SQL.100.aspx:
with ProductViewsCountSelect as (select * from ProductViewsCount where ViewType = @ViewType and [Timestamp] = @Timestamp)
merge into ProductViewsCountSelect
using (select data, count(*) as cnt from dbo.split('A,B,C,A', ',') group by data) d on ProductViewsCountSelect.ProductId = d.data
when matched
then update set ProductViewsCountSelect.Count = ProductViewsCountSelect.count + cnt
when not matched
then insert (ProductId, [TimeStamp], ViewType, [Count]) values( d.data, @TimeStamp, @ViewType, cnt);
使用SQL Server 2008,您有了一个不错的新选项http://technet.microsoft.com/en-us/library/bb510625SQL.100.aspx:
with ProductViewsCountSelect as (select * from ProductViewsCount where ViewType = @ViewType and [Timestamp] = @Timestamp)
merge into ProductViewsCountSelect
using (select data, count(*) as cnt from dbo.split('A,B,C,A', ',') group by data) d on ProductViewsCountSelect.ProductId = d.data
when matched
then update set ProductViewsCountSelect.Count = ProductViewsCountSelect.count + cnt
when not matched
then insert (ProductId, [TimeStamp], ViewType, [Count]) values( d.data, @TimeStamp, @ViewType, cnt);
我可以避免做交易吗?我的意思是如果我只能锁定我正在更新的行?我假设事务是最昂贵的操作。@RaduD-Ok,我更改了查询,使其使用MERGE语句,将UPDATE和INSERT包装在同一个命令上。这样,你就可以不用进行交易,而风险比两种声明分开时要小;在评估合并时是否会评估CTE?如果从多个线程调用SP,则在计算CTE和计算合并时,SP可能是一个间隙?我会遇到“无法在对象中插入重复键”异常。这意味着MERGE无法正确处理这场比赛condition@RaduD你们从来并没有提到过你们有一个不能在对象中插入重复的密钥,这意味着你们并没有考虑重复的密钥。我可以避免做交易吗?我的意思是如果我只能锁定我正在更新的行?我假设事务是最昂贵的操作。@RaduD-Ok,我更改了查询,使其使用MERGE语句,将UPDATE和INSERT包装在同一个命令上。这样,你就可以不用进行交易,而风险比两种声明分开时要小;在评估合并时是否会评估CTE?如果从多个线程调用SP,则在计算CTE和计算合并时,SP可能是一个间隙?我会遇到“无法在对象中插入重复键”异常。这意味着MERGE无法正确处理这场比赛condition@RaduD您从未说过您有一个无法在对象中插入重复密钥的,这意味着你没有考虑到重复我实际上已经更新了我的答案,所以它使用合并而不是更新和插入一段时间之前我不知道为什么这个脚本只在第一次插入。当视图类型被更改时,我根本没有得到任何插入。没错。因为我上面的回答是错误的:-/如果您已经有了ViewType的条目,那么ProductId上就有一个匹配项。所以你是在比赛的时候。如果由于ViewType零件的原因而不正确,则不会执行“不匹配时”零件。这就是为什么在你接受的答案中使用CTE的原因。接受的答案也有一个问题。。。它给出“无法在对象中插入重复键”。。。基本上,存在一个竞争条件,MERGE命令似乎不能正确处理这个问题。我知道,您的输入字符串中有多次相同的productid。这确实是cte的一个问题。我实际上已经更新了我的答案,所以它使用合并而不是更新和插入。我不知道为什么这个脚本只在第一次插入。当视图类型被更改时,我根本没有得到任何插入。没错。因为我上面的回答是错误的:-/如果您已经有了ViewType的条目,那么ProductId上就有一个匹配项。所以你是在比赛的时候。如果由于ViewType零件的原因而不正确,则不会执行“不匹配时”零件。这就是为什么在你接受的答案中使用CTE的原因。接受的答案也有一个问题。。。它给出“无法在对象中插入重复键”。。。基本上,存在一个竞争条件,MERGE命令似乎不能正确处理这个问题。我知道,您的输入字符串中有多次相同的productid。这确实是cte的一个问题。