1

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

  1. 它使用光标
  2. 它开始了很多交易

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

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

4

2 回答 2

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
于 2012-06-08T20:00:50.210 回答
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);
于 2012-06-08T20:29:29.297 回答