2012-05-22 67 views
6

Trong DB của tôi, tôi có hai bảng Items (Id, ..., ToatlViews int) và ItemViews (id, ItemID, Timestamp)Cập nhật đếm cột từ dữ liệu trong bảng khác

Trong bảng ItemViews tôi lưu trữ tất cả các quan điểm của một mục khi họ đến trang web. Đôi khi tôi muốn gọi một thủ tục lưu sẵn để cập nhật trường Items.ToatlViews. Tôi đã cố gắng để làm điều này SP bằng cách sử dụng một con trỏ ... nhưng tuyên bố cập nhật là sai. Bạn có thể giúp tôi sửa nó không? Tôi có thể làm điều này mà không có con trỏ không?

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 [email protected] 
     FETCH NEXT FROM @currentItemCursor INTO @currentItemId 
    END 
END 
GO 
+0

Tôi khuyên bạn nên cố gắng hết sức để không sử dụng con trỏ khi viết SQL, vì sẽ luôn có một cách 'set' dựa trên để viết những gì bạn muốn đối với cơ sở dữ liệu. Tất nhiên sẽ luôn có một ngoại lệ đối với quy tắc này. –

Trả lời

18

Bạn có thể sử dụng một câu lệnh UPDATE trực tiếp

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

Bạn có thể muốn kiểm tra hiệu suất cho các cách khác nhau để thực hiện điều này , nếu điều đó quan trọng.

+0

+1 cho sự sang trọng. Nói về hiệu suất; Tôi nhớ đọc ở đâu đó rằng 'count (1)' thay vì 'count (id)' là tốt hơn (không phải là bạn có thể sẽ nhận thấy nó). Vì trường id cần được chọn ra như một phần của truy vấn ... –

+1

@mouters là một quan niệm sai lầm. 'COUNT (1)' chỉ tốt hơn 'COUNT (id)' nếu chúng ta nói về độ chính xác, và nó chỉ chính xác hơn (hoặc khác theo bất kỳ cách nào) nếu 'id' là nullable. Nếu bạn thấy điều này được nêu ở đâu đó (ngoài bộ nhớ của bạn), vui lòng chỉ ra nó vì nó cần được sửa chữa hoặc làm rõ. –

+0

Nhận xét công bằng - Tôi nghĩ tôi sẽ gặp khó khăn khi tìm lại bài viết. –

8

Bạn có thể sử dụng update ... from thay vì một con trỏ:

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
;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; 

Nhưng tại sao bạn muốn lưu trữ giá trị này, khi bạn luôn có thể đếm được khi chạy? Bạn sẽ phải chạy câu lệnh cập nhật này mỗi khi bạn chạm vào bảng ItemViews theo bất kỳ cách nào, nếu không thì số đếm được lưu trữ với Các mục sẽ không chính xác.

Những gì bạn có thể xem xét việc làm thay vì đang thiết lập một cái nhìn được lập chỉ mục:

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); 

Bây giờ bạn có thể tham gia vào quan điểm trong các truy vấn của bạn và biết rằng số lượng là luôn được cập nhật (mà không phải trả hình phạt quét cho số lượng của mỗi mục). Nhược điểm của khung nhìn được lập chỉ mục là bạn phải trả chi phí tăng lên khi có chèn/cập nhật/xóa vào bảng ItemViews.

0

Tôi đã tìm thấy câu hỏi/câu trả lời này một năm sau khi được viết và trả lời. câu trả lời là ổn, nhưng sau khi tôi tự động hơn một chút. Tôi đã kết thúc bằng cách viết trình kích hoạt để tự động tính toán lại cột khi một hàng có liên quan trong bảng khác được chèn, xóa hoặc cập nhật.

Tôi nghĩ rằng đó là một giải pháp tốt hơn so với chạy một cái gì đó bằng tay để làm các tính toán lại như không có bất kỳ khả năng ai đó quên để chạy mã:

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 
0

Cùng nhưng khác nhau:

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 
Các vấn đề liên quan