2009-08-03 21 views
6

के बिना SQL सर्वर तालिका से रिकॉर्ड्स हटाना मैं कर्सर के माध्यम से लूप किए बिना SQL सर्वर 2005 तालिका से रिकॉर्ड्स को चुनने का प्रयास कर रहा हूं। तालिका में कई रिकॉर्ड हो सकते हैं (कभी-कभी> 500,000) तो लूपिंग बहुत धीमी है।कर्सर

डाटा:

ID, UnitID, Day, Interval, Amount 

1 100  10 21  9.345 

2 100  10 22  9.367 

3 200  11 21  4.150 

4 300  11 21  4.350 

5 300  11 22  4.734 

6 300  11 23  5.106 

7 400  13 21  10.257 

8 400  13 22  10.428 

कुंजी है: आईडी, UnitID, दिन, अंतराल।

इस उदाहरण में मैं रिकॉर्ड्स 2, 5 और 8 को हटाना चाहता हूं - वे मौजूदा रिकॉर्ड (कुंजी के आधार पर) के निकट हैं।

नोट: रिकॉर्ड 6 हटाया नहीं जाएगा क्योंकि एक बार 5 चला गया है, यह अब आसन्न नहीं है।

क्या मैं बहुत ज्यादा पूछ रहा हूं?

+1

मैं सकारात्मक नहीं हूँ, लेकिन एक सेट है कि मैं क्या सेट सिद्धांत से याद से "निकटता" को नहीं समझता। इसे कर्सर के साथ करने की आवश्यकता हो सकती है। –

+1

आप कैसे तय करते हैं कि कौन सी पंक्तियां हटाना है? क्या फ़ील्ड (ओं) के आधार पर मानदंड क्या है? –

+0

यह कुंजी के आधार पर "क्रमिक क्रम" प्रतीत होता है। –

उत्तर

1

मुझे नहीं लगता कि आप जो पूछ रहे हैं वह संभव है - लेकिन आप करीब पहुंचने में सक्षम हो सकते हैं। ऐसा लगता है कि लगभग एक साथ रिकॉर्ड का पता लगाकर यह कर सकते हैं इस तरह आत्म में शामिल होने:

SELECT t1.id 
FROM 
    table t1 JOIN table t2 ON (
    t1.unitid = t2.unitid AND 
    t1.day = t2.day AND 
    t1.interval = t2.interval - 1 
) 

लेकिन समस्या यह है, कि आईडी = 6 रूप में अच्छी तरह मिल जाएगा। हालांकि, यदि आप इस डेटा से एक अस्थायी तालिका बनाते हैं, तो यह आपके मूल डेटा से अधिक हो सकता है, और इस प्रकार कर्सर के साथ स्कैन करने के लिए बहुत तेज़ (आईडी = 6 समस्या को ठीक करने के लिए) हो सकता है। फिर आप पंक्तियों को मारने के लिए DELETE FROM table WHERE id IN (SELECT id FROM tmp_table) कर सकते हैं।

आईडी = 6 समस्या w/o कर्सर को ठीक करने का कोई तरीका हो सकता है, लेकिन यदि ऐसा है, तो मुझे यह नहीं दिखाई देता है।

+1

यदि आप पहले ही मूल डाल चुके हैं तालिका चर में चयन करें, फिर आप आईडी = 6 समस्या को ठीक करने के लिए कर्सर के बजाय WHILE का उपयोग कर सकते हैं। –

0

WHILE statement है, जो कर्सर का विकल्प है। table variables के साथ संयुक्त होने से आप एक ही काम को बाध्य कर सकते हैं जिसमें आप ठीक हैं।

0
DECLARE @Table TABLE (ID INT, UnitID INT, [Day] INT, Interval INT, Amount FLOAT) 

INSERT INTO @Table VALUES (1, 100, 10, 21, 9.345) 
INSERT INTO @Table VALUES (2, 100, 10, 22, 9.367) 
INSERT INTO @Table VALUES (3, 200, 11, 21, 4.150) 
INSERT INTO @Table VALUES (4, 300, 11, 21, 4.350) 
INSERT INTO @Table VALUES (5, 300, 11, 22, 4.734) 
INSERT INTO @Table VALUES (6, 300, 11, 23, 5.106) 
INSERT INTO @Table VALUES (7, 400, 13, 21, 10.257) 
INSERT INTO @Table VALUES (8, 400, 13, 22, 10.428) 

DELETE FROM @Table 
WHERE ID IN (
    SELECT t1.ID 
    FROM @Table t1 
     INNER JOIN @Table t2 
      ON t2.UnitID = t1.UnitID 
       AND t2.Day = t1.Day 
       AND t2.Interval = t1.Interval - 1 
     LEFT OUTER JOIN @Table t3 
      ON t3.UnitID = t2.UnitID 
       AND t3.Day = t2.Day 
       AND t3.Interval = t2.Interval - 1 
    WHERE t3.ID IS NULL) 

SELECT * FROM @Table 
+0

यह केवल पहले आसन्न मूल्य को हटा देगा। अगर हमारे पास '21',' 22', '23' और' 24', यह '22' को हटा देगा लेकिन' 24' छोड़ देगा। – Quassnoi

+0

आप सही हैं। इसे बनाते समय (चुनें ...) हटाएं ... –

4

प्रदर्शन विस्तार के लिए अपने ब्लॉग में इन लेखों देखें:


नीचे क्वेरी के लिए मुख्य विचार है कि हम सभी को नष्ट करना चाहिए अंतराल की निरंतर श्रृंखला से भी पंक्तियां।

यही है, अगर दिया के लिए (unitId, Day) हम निम्नलिखित intervals है:

1 
2 
3 
4 
6 
7 
8 
9 

, हम दो निरंतर पर्वतमाला है:

1 
2 
3 
4 

और

6 
7 
8 
9 

, और हम ऐसा करना चाहिए हर पंक्ति को भी हटाएं:

1 
2 -- delete 
3 
4 -- delete 

और

6 
7 -- delete 
8 
9 -- delete 

, ताकि हम पाते हैं:

1 
3 
6 
8 

ध्यान दें कि "यहां तक ​​कि पंक्तियों" का अर्थ है "भी प्रति-सीमा ROW_NUMBER() रों" यहाँ, नहीं "भी interval के मूल्यों "।

क्वेरी है:

DECLARE @Table TABLE (ID INT, UnitID INT, [Day] INT, Interval INT, Amount FLOAT) 

INSERT INTO @Table VALUES (1, 100, 10, 21, 9.345) 
INSERT INTO @Table VALUES (2, 100, 10, 22, 9.345) 
INSERT INTO @Table VALUES (3, 200, 11, 21, 9.345) 
INSERT INTO @Table VALUES (4, 300, 11, 21, 9.345) 
INSERT INTO @Table VALUES (5, 300, 11, 22, 9.345) 
INSERT INTO @Table VALUES (6, 300, 11, 23, 9.345) 
INSERT INTO @Table VALUES (7, 400, 13, 21, 9.345) 
INSERT INTO @Table VALUES (8, 400, 13, 22, 9.345) 
INSERT INTO @Table VALUES (9, 400, 13, 23, 9.345) 
INSERT INTO @Table VALUES (10, 400, 13, 24, 9.345) 
INSERT INTO @Table VALUES (11, 400, 13, 26, 9.345) 
INSERT INTO @Table VALUES (12, 400, 13, 27, 9.345) 
INSERT INTO @Table VALUES (13, 400, 13, 28, 9.345) 
INSERT INTO @Table VALUES (14, 400, 13, 29, 9.345) 

;WITH rows AS 
     (
     SELECT *, 
       ROW_NUMBER() OVER 
       (
       PARTITION BY 
         (
         SELECT TOP 1 qi.id AS mint 
         FROM @Table qi 
         WHERE qi.unitid = qo.unitid 
           AND qi.[day] = qo.[day] 
           AND qi.interval <= qo.interval 
           AND NOT EXISTS 
           (
           SELECT NULL 
           FROM @Table t 
           WHERE t.unitid = qi.unitid 
             AND t.[day] = qi.day 
             AND t.interval = qi.interval - 1 
           ) 
         ORDER BY 
           qi.interval DESC 
         ) 
       ORDER BY interval 
       ) AS rnm 
     FROM @Table qo 
     ) 
DELETE 
FROM rows 
WHERE rnm % 2 = 0 

SELECT * 
FROM @table 

अद्यतन:

यहाँ एक अधिक कुशल क्वेरी है:

DECLARE @Table TABLE (ID INT, UnitID INT, [Day] INT, Interval INT, Amount FLOAT) 

INSERT INTO @Table VALUES (1, 100, 10, 21, 9.345) 
INSERT INTO @Table VALUES (2, 100, 10, 22, 9.345) 
INSERT INTO @Table VALUES (3, 200, 11, 21, 9.345) 
INSERT INTO @Table VALUES (4, 300, 11, 21, 9.345) 
INSERT INTO @Table VALUES (5, 300, 11, 22, 9.345) 
INSERT INTO @Table VALUES (6, 300, 11, 23, 9.345) 
INSERT INTO @Table VALUES (7, 400, 13, 21, 9.345) 
INSERT INTO @Table VALUES (8, 400, 13, 22, 9.345) 
INSERT INTO @Table VALUES (9, 400, 13, 23, 9.345) 
INSERT INTO @Table VALUES (10, 400, 13, 24, 9.345) 
INSERT INTO @Table VALUES (11, 400, 13, 26, 9.345) 
INSERT INTO @Table VALUES (12, 400, 13, 27, 9.345) 
INSERT INTO @Table VALUES (13, 400, 13, 28, 9.345) 
INSERT INTO @Table VALUES (14, 400, 13, 29, 9.345) 

;WITH source AS 
     (
     SELECT *, ROW_NUMBER() OVER (PARTITION BY unitid, day ORDER BY interval) rn 
     FROM @Table 
     ), 
     rows AS 
     (
     SELECT *, ROW_NUMBER() OVER (PARTITION BY unitid, day, interval - rn ORDER BY interval) AS rnm 
     FROM source 
     ) 
DELETE 
FROM rows 
WHERE rnm % 2 = 0 

SELECT * 
FROM @table 
+0

यदि आपकी अंतराल सीमा अंतराल 2, 3, 4, 5 है - आपको बाधाओं को दूर करने की जरूरत है और यहां तक ​​कि शाम भी नहीं। – Matt

+0

@ मैट: यहां तक ​​कि पंक्तियों को हटाने की जरूरत है, मूल्य भी नहीं। इंटर वैल '3' में आपके उदाहरण में' 2' का 'ROW_NUMBER()' होगा। – Quassnoi

+0

मुझे अब मिल गया है। ऐसा लगता है कि इस के लिए जाने के लिए रास्ता/ROW_NUMBER है – Matt

0

Lieven इतने करीब है - यह परीक्षण सेट के लिए काम किया है, लेकिन अगर मैं कुछ और रिकॉर्ड जोड़ता हूं जो कुछ याद करने लगते हैं।

हम किसी भी विषम/यहां तक ​​कि मानदंडों का उपयोग नहीं कर सकते - हमें नहीं पता कि डेटा कैसे गिरता है।

इस डेटा जोड़ें और पुन: प्रयास:

INSERT @Table VALUES (9, 100,  10, 23,  9.345) 

INSERT @Table VALUES (10, 100,  10, 24,  9.367) 

INSERT @Table VALUES (11, 100,  10, 25,  4.150) 

INSERT @Table VALUES (12, 100,  10, 26,  4.350) 

INSERT @Table VALUES (13, 300,  11, 25,  4.734) 

INSERT @Table VALUES (14, 300,  11, 26,  5.106) 

INSERT @Table VALUES (15, 300,  11, 27,  10.257) 

INSERT @Table VALUES (16, 300,  11, 29,  10.428) 
+0

@ इयान: पुनः प्रयास किया गया, यह पंक्तियां '9',' 11', '13',' 15' और '16' छोड़ देता है,' 10', '12' हटा देता है और '14'। क्या आप यह नहीं चाहते हैं? – Quassnoi