2012-05-22 13 views
6

W moim DB mam dwie tabele egzemplarze (Id, ..., ToatlViews int) i ItemViews (id, ItemId, Datownik)liczyć Aktualizacja kolumny z danych w innej tabeli

W tabeli ItemViews przechowywać wszystkie widoki przedmiot, gdy przychodzą na stronę. Od czasu do czasu chcę wywołać procedurę przechowywaną, aby zaktualizować pole Items.ToatlViews. Próbowałem zrobić to SP za pomocą kursora ... ale instrukcja aktualizacji jest niepoprawna. Czy możesz mi pomóc to poprawić? Czy mogę to zrobić bez kursora?

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

Zalecam, abyś dołożył wszelkich starań, aby nie używać kursorów podczas pisania SQL, ponieważ zawsze będzie oparty na "zestawie" sposób zapisywania tego, co chcesz, w bazie danych. Oczywiście zawsze będzie wyjątek od tej zasady. –

Odpowiedz

18

Można użyć bezpośredniego UPDATE

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

Może chcesz przetestować działanie na różne sposoby, aby to zrobić , jeśli to ważne.

+0

+1 dla elegancji. Mówienie o wydajności; Pamiętam, że czytałem gdzieś, że 'count (1)' zamiast 'count (id)' jest lepsze (nie, że prawdopodobnie zauważysz to). Ponieważ pole identyfikatora musi zostać wybrane jako część zapytania ... –

+1

@mouters to błędne przekonanie. "COUNT (1)" jest tylko lepsze niż "COUNT (id)", jeśli mówimy o dokładności, i jest ono dokładniejsze (lub w jakikolwiek inny sposób), jeśli 'id' jest zerowalne. Jeśli widzisz to gdzieś (poza twoją pamięcią), proszę wskazać to, ponieważ powinno ono zostać poprawione lub wyjaśnione. –

+0

Komentarz targowy - Myślę, że będę miał trudności ze znalezieniem tego artykułu. –

8

Można użyć update ... from zamiast kursora:

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; 

Ale dlaczego chcesz zachować tę wartość, gdy zawsze możesz uzyskać liczenie w czasie wykonywania? Będziesz musiał uruchomić tę instrukcję aktualizacji za każdym razem, gdy dotkniesz tabeli ItemViews w jakikolwiek sposób, w przeciwnym razie licznik przechowywany z Elementami będzie niepoprawny.

Co można rozważyć robi zamiast jest utworzenie indeksowany widok:

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

Teraz możesz dołączyć do widoku w zapytaniach i wiem, że licznik jest zawsze na bieżąco (bez płacenia kary skanowanie pod kątem liczby sztuk każdego przedmiotu). Wadą indeksowanego widoku jest to, że płacisz ten koszt narastająco, gdy istnieją inserty/aktualizacje/usunięcia do tabeli ItemViews.

0

Znalazłem to pytanie/odpowiedź rok po napisaniu i odebraniu odpowiedzi. odpowiedź była w porządku, ale szukałem czegoś bardziej automatycznego. Skończyłem na pisaniu spustu, aby automatycznie przeliczyć kolumnę, kiedy odpowiedni wiersz w drugiej tabeli został wstawiony, usunięty lub zaktualizowany.

myślę, że to lepsze rozwiązanie niż bieganie coś ręcznie wykonać przeliczenie jak nie ma żadnej możliwości ktoś zapominając uruchomić kod:

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

samo, ale inaczej:

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 
Powiązane problemy