从另一个表中的数据更新计数列

时间:2012-05-22 11:47:51

标签: sql-server stored-procedures

在我的数据库中我有两个表项(Id,...,ToatlViews int)和ItemViews(id,ItemId,Timestamp)

在ItemViews表中,我存储项目进入网站时的所有视图。我不时想调用存储过程来更新Items.ToatlViews字段。我尝试使用游标执行此SP ...但更新语句错误。你能帮我纠正一下吗?我可以不用光标吗?

CREATE PROCEDURE UpdateItemsViews
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @currentItemId int
    DECLARE @currentItemCursor CURSOR
    SET @currentItemCursor = CURSOR FOR SELECT Id FROM dbo.Items

    OPEN @currentItemCursor
    FETCH NEXT FROM @currentItemCursor INTO @currentItemId
    WHILE @@FETCH_STATUS = 0
    BEGIN
        Update dbo.Items set TotalViews = count(*) 
              from dbo.ItemViews where ItemId=@currentItemId
        FETCH NEXT FROM @currentItemCursor INTO @currentItemId
    END   
END
GO

5 个答案:

答案 0 :(得分:21)

您可以使用直接UPDATE语句

update Items set TotalViews = 
     (select COUNT(id) from ItemViews where ItemViews.ItemId = Items.Id)

如果这很重要,您可能希望测试各种方法的性能。

答案 1 :(得分:8)

您可以使用update ... from代替光标:

update  i
set     TotalViews = iv.cnt
from    dbo.Item i
join    (
        select  ItemId
        ,       count(*) as cnt
        from    dbo.ItemViews
        group by
                ItemId
        ) iv
on      i.Id = iv.ItemId

答案 2 :(得分:2)

;WITH x AS 
(
  SELECT ItemID, c = COUNT(*) 
  FROM dbo.ItemViews
  GROUP BY ItemID
)
UPDATE i
SET TotalViews = x.c
FROM dbo.Items AS i
INNER JOIN x
ON x.ItemID = i.ItemID;

但是,为什么要在运行时始终获得计数时,为什么要存储此值?每次以任何方式触摸ItemViews表时,您都必须运行此更新语句,否则与Items一起存储的计数将不正确。

您可能会考虑做的是设置索引视图:

CREATE VIEW dbo.ItemViewCount
WITH SCHEMABINDING
AS
    SELECT ItemID, ItemCount = COUNT_BIG(*)
      FROM dbo.ItemViews
      GROUP BY ItemID;
GO
CREATE UNIQUE CLUSTERED INDEX x ON dbo.ItemViewCount(ItemID);

现在,您可以加入查询中的视图,并知道计数始终是最新的(无需支付扫描每个项目计数的罚款)。索引视图的缺点是,当对ItemViews表进行插入/更新/删除时,您将逐步支付该成本。

答案 3 :(得分:0)

我在撰写和回答一年后发现了这个问题/答案。答案还可以,但我更自动了。我最后写了一个触发器,当插入,删除或更新另一个表中的相关行时自动重新计算列。

我认为这比手动运行重新计算更好,因为没有人忘记运行代码:

CREATE TRIGGER [dbo].[TriggerItemTotalViews] 
   ON  [dbo].[ItemViews]
   AFTER INSERT, DELETE, UPDATE
AS 
BEGIN
SET NOCOUNT ON;

UPDATE [Items] 
SET [TotalViews] = 
    (
    SELECT COUNT(id) 
    FROM [ItemViews] 
    WHERE [ItemViews].[ItemId] = [Items].[ItemId]
    )
WHERE [Items].[ItemId] IN
    (
    SELECT [ItemId] FROM [INSERTED] 
    UNION 
    SELECT [ItemId] FROM [DELETED]
    )
END

答案 4 :(得分:0)

相同但不同:

declare @productId int = 24;
declare @classificationTypeId int = 86;

update s
set CounterByProductAndClassificationType = row_num
from Samples s
join
(
    select row_number() over (order by (select Id)) row_num, Id
    from Samples
    where 
        ProductId = @productId and
        ClassificationTypeId = @classificationTypeId
) s_row on s.Id = s_row.Id