10

在我的数据库中,我有两个表 Items(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
4

6 回答 6

26

您可以使用直接的 UPDATE 语句

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

如果这很重要,您可能想要测试执行此操作的各种方法的性能。

于 2012-05-22T11:54:17.247 回答
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
于 2012-05-22T11:50:22.293 回答
3
;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 表进行插入/更新/删除时,您会逐步支付该成本。

于 2012-05-22T11:56:03.703 回答
1

对于谁也需要包括零计数

UPDATE Items as i,
    (SELECT 
        i.Id as Id, COUNT(iv.ItemId) AS c
    FROM
        Items AS i
    LEFT JOIN ItemViews AS iv ON i.Id = iv.ItemId
    GROUP BY i.Id) AS ic 
SET 
    i.TotalViews = ic.c
WHERE
    i.Id = ic.Id
于 2022-01-20T10:59:12.010 回答
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
于 2013-05-15T22:19:45.270 回答
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
于 2015-07-14T13:02:43.597 回答