2012-05-22 19 views
6

मेरी DB में मैं दो तालिकाओं आइटम (आईडी, ..., ToatlViews पूर्णांक) और ItemViews (आईडी, Itemid, समय-चिह्न) हैअद्यतन एक और तालिका में डेटा से स्तंभ गणना

ItemViews तालिका में मैं के सभी दृश्यों की दुकान एक वस्तु के रूप में वे साइट पर आते हैं। समय-समय पर मैं आइटम्स.ToatlViews फ़ील्ड को अपडेट करने के लिए संग्रहीत प्रक्रिया को कॉल करना चाहता हूं। मैंने कर्सर का उपयोग करके इस एसपी को करने की कोशिश की ... लेकिन अद्यतन कथन गलत है। क्या आप इसे ठीक करने में मेरी मदद कर सकते हैं? क्या मैं कर्सर के बिना ऐसा कर सकता हूं?

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

मैं अनुशंसा करता हूं कि आप SQL लिखते समय कर्सर का उपयोग न करने के हर प्रयास करें, क्योंकि डेटाबेस के विरुद्ध आप जो लिखना चाहते हैं उसे लिखने के लिए हमेशा 'सेट' आधारित तरीका होगा। निस्संदेह इस नियम के लिए हमेशा अपवाद होगा। लालित्य के लिए –

उत्तर

18

आप एक सीधा अद्यतन कथन का उपयोग कर सकते हैं

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

आप प्रदर्शन का परीक्षण करने के विभिन्न तरीकों से यह करने के लिए के लिए चाहते हो सकता है , अगर यह महत्वपूर्ण है।

+0

+1। प्रदर्शन की बात कर रहे हैं; मुझे कहीं याद है कि 'गिनती (आईडी)' की बजाय 'गिनती (1)' बेहतर है (नहीं कि आप शायद इसे नोटिस करेंगे)। चूंकि आईडी फ़ील्ड को क्वेरी के हिस्से के रूप में चुना जाना आवश्यक है ... –

+1

@ मॉटर जो गलत धारणा है। 'COUNT (1)' 'COUNT (आईडी) 'से बेहतर है, यदि हम सटीकता के बारे में बात कर रहे हैं, और यदि आईडी आईडी शून्य है तो यह केवल अधिक सटीक (या किसी भी तरह से भिन्न) है। यदि आप इसे कहीं कहीं (आपकी याददाश्त के अलावा) देखते हैं, तो कृपया इसे इंगित करें क्योंकि इसे सही या स्पष्ट किया जाना चाहिए। –

+0

उचित टिप्पणी - मुझे लगता है कि मुझे फिर से आलेख खोजने में कठिनाई होगी। –

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

लेकिन आप इस मूल्य को क्यों स्टोर करना चाहते हैं, जब आप हमेशा रनटाइम पर गिनती प्राप्त कर सकते हैं? जब भी आप आइटम दृश्य तालिका को किसी भी तरह स्पर्श करते हैं, तो आपको इस अद्यतन कथन को चलाने की आवश्यकता होगी, अन्यथा आइटम के साथ संग्रहीत गिनती गलत होगी।

क्या आप के बजाय कर रही है पर विचार कर सकते हैं एक अनुक्रमित दृश्य स्थापित कर रही है:

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

अब आप अपने प्रश्नों में देखने के लिए शामिल होने और जानते हैं कि गिनती हमेशा अप टू डेट है सकते हैं (के दंड के भुगतान के बिना प्रत्येक आइटम की गिनती के लिए स्कैनिंग)। अनुक्रमित दृश्य का नकारात्मक पक्ष यह है कि जब आप आइटम दृश्य तालिका में आवेषण/अद्यतन/हटाए जाते हैं तो आप उस लागत का भुगतान करते हैं।

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 
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 
संबंधित मुद्दे