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

时间:2012-06-08 19:56:41

标签: sql-server sql-server-2008 tsql stored-procedures transactions

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

  1. 它使用光标
  2. 它启动了很多交易
  3. 许多线程同时调用SP。实现之后,我遇到了很多超时异常。我的计数表看起来像这样:

    ProductsViewsCount(ProductId int,Timestamp 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
    

    我能以更有效的方式做到这一点吗?

2 个答案:

答案 0 :(得分:3)

您可以在设置操作而不是光标上执行此操作:

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

答案 1 :(得分:1)

使用SQL Server 2008,您有一个不错的新选项(http://technet.microsoft.com/en-us/library/bb510625(SQL.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);