Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server T-SQL:增加产品视图计数的存储过程_Sql Server_Sql Server 2008_Tsql_Stored Procedures_Transactions - Fatal编程技术网

Sql server T-SQL:增加产品视图计数的存储过程

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

我是sql的初学者,我需要编写一个SP以增加产品视图数。当用户在网站上搜索时,我们希望增加搜索返回的所有产品的计数器。我发现我的SP存在两个问题:

它使用游标 它启动了很多交易 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
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的一个问题。