2009-03-26 14 views
34

में सबसे छोटा अप्रयुक्त नंबर खोजें SQL सर्वर कॉलम में सबसे छोटा अप्रयुक्त नंबर आपको कैसे मिलता है?SQL सर्वर

मैं Excel से SQL सर्वर तालिका में मैन्युअल रूप से रिकॉर्ड किए गए रिकॉर्ड की बड़ी संख्या आयात करने जा रहा हूं। उनके पास एक संख्यात्मक आईडी (जिसे दस्तावेज़ संख्या कहा जाता है) है, लेकिन उन्हें उन कारणों के लिए अनुक्रमिक रूप से असाइन नहीं किया गया था जो अब लागू नहीं होते हैं, जिसका अर्थ है कि जब मेरी वेबसाइट एक नया रिकॉर्ड रिकॉर्ड करती है, तो इसे इसे सबसे छोटा संभव दस्तावेज़ संख्या असाइन करने की आवश्यकता होती है (शून्य से अधिक) जो पहले से ही नहीं लिया गया है।

क्या सादा एसक्यूएल के माध्यम से ऐसा करने का कोई तरीका है या यह टीएसक्यूएल/कोड के लिए एक समस्या है?

धन्यवाद! संगामिति के मुद्दे को उठाने के लिए WW को

संपादित

विशेष धन्यवाद। यह देखते हुए कि यह एक वेब ऐप है, यह परिभाषा द्वारा बहु-थ्रेडेड है और किसी भी समस्या का सामना करने वाले किसी भी व्यक्ति को किसी भी समस्या को रोकने के लिए कोड या डीबी स्तर लॉक पर विचार करना चाहिए।

LINQ

FYI करें - यह निम्न कोड के साथ LINQ के माध्यम से पूरा किया जा सकता:

var nums = new [] { 1,2,3,4,6,7,9,10}; 

int nextNewNum = (
    from n in nums 
    where !nums.Select(nu => nu).Contains(n + 1) 
    orderby n 
    select n + 1 
).First(); 

nextNewNum == 5

उत्तर

48

पहली पंक्ति जहां आईडी के साथ एक पंक्ति मौजूद नहीं है का पता लगाएं + 1

SELECT TOP 1 t1.Id+1 
FROM table t1 
WHERE NOT EXISTS(SELECT * FROM table t2 WHERE t2.Id = t1.Id + 1) 
ORDER BY t1.Id 

संपादित करें:

यहाँ विशेष मामले में जहां न्यूनतम मौजूदा आईडी 1 नहीं है संभाल करने के लिए, एक बदसूरत है समाधान:

SELECT TOP 1 * FROM (
    SELECT t1.Id+1 AS Id 
    FROM table t1 
    WHERE NOT EXISTS(SELECT * FROM table t2 WHERE t2.Id = t1.Id + 1) 
    UNION 
    SELECT 1 AS Id 
    WHERE NOT EXISTS (SELECT * FROM table t3 WHERE t3.Id = 1)) ot 
ORDER BY 1 
+0

यह सीमा की शुरुआत से शुरू होने वाले किसी भी संगत ब्लॉक को याद करेगा। उदाहरण के लिए, यदि 'टेबल' में आईडी हैं (5,6,8,9,10) यह 7 लौटाएगा, 1-4 में से कोई भी नहीं। – joshperry

+0

@joshperry आप सही हैं। मैंने सभी आईडी को शून्य से अधिक भरने के बारे में टिप्पणी को याद किया। मैंने एक बदसूरत फिक्स जोड़ा। शायद कोई सुधार का सुझाव देगा। –

+0

+1 बहुत उपयोगी, धन्यवाद! यहां के अधिकांश अन्य उत्तरों "आपको ऐसा करने की आवश्यकता नहीं है, सिस्टम को कुंजी बढ़ाना चाहिए," लेकिन मेरा मामला प्राथमिक कुंजी के लिए नहीं है बल्कि इसके बजाय एक और अद्वितीय संख्यात्मक क्षेत्र है जहां रिक्त स्लॉट एक समस्या है लेकिन कर सकते हैं एक डिलीट के परिणामस्वरूप होता है, न कि होता है। –

2

क्या कोई कारण यह है कि यह सबसे छोटा हो गया है संभव संख्या? आपको छेद भरने की ज़रूरत क्यों है?

उत्तर देने के लिए संपादित करें, क्योंकि यह एक व्यवसाय नियम है।

DECLARE @counter int 
DECLARE @max 
SET @counter = 0 
SET @max = SELECT MAX(Id) FROM YourTable 
WHILE @counter <= @max 
BEGIN 
    SET @counter = @counter + 1 
    IF NOT EXISTS (SELECT Id FROM YourTable WHERE Id = @counter) 
     BREAK 
    END 
END 

(मैं एक डाटाबेस काम नहीं है, तो यह 100% सही नहीं हो सकता है लेकिन आप उसे वहां से प्राप्त करने में सक्षम होना चाहिए)

+0

यह एक व्यापार नियम है। इन दस्तावेज़ संख्याओं को तब उपयोगकर्ताओं को सौंप दिया जाता है और वास्तव में उपयोग किया जाता है। मैंने एक ही सवाल पूछा, लेकिन वे इस पर दृढ़ रह रहे हैं। :) –

+0

यह दुर्भाग्यपूर्ण है ... मुझे पता है कि एकमात्र तरीका उन सभी के माध्यम से लूप होगा जब तक आप एक अप्रयुक्त आईडी नहीं पाते। क्षमा करें अपनी किस्मत झुकाओ। –

3

वहाँ अनुक्रम में अंतराल हैं, तो आप कुछ इस तरह के साथ पहले की खाई को पा सकते हैं:

select top 1 (found.id + 1) nextid from (select id from items union select 0) found 
    where not exists (select * from items blocking 
          where blocking.id = found.id + 1) 
    order by nextid asc 

दूसरे शब्दों में, कम से कम आईडी जिसका उत्तराधिकारी मौजूद नहीं है, और है कि उत्तराधिकारी वापस जाएँ। यदि कोई अंतराल नहीं है, तो यह सबसे बड़ी मौजूदा आईडी से अधिक लौटाता है। 0 के प्लेसहोल्डर आईडी को बीमा करने के लिए डाला गया है कि 1 से शुरू होने वाली आईडी को माना जाता है।

ध्यान दें कि इसमें कम से कम एन लॉग एन समय लगेगा।

माइक्रोसॉफ्ट एसक्यूएल insert कथन में from खंड के उपयोग की अनुमति देता है, इसलिए आपको प्रक्रियात्मक कोड का सहारा लेने की आवश्यकता नहीं हो सकती है।

11

यदि आप उन्हें संख्यात्मक आईडी द्वारा क्रमबद्ध करते हैं, तो आप जिस नंबर की तलाश कर रहे हैं वह पहला होगा जिसके लिए ROW_NUMBER() फ़ंक्शन आईडी के बराबर नहीं है।

+0

+1 अच्छा SQL सर्वर-विशिष्ट चाल। क्या यह पहले गैर-मिलान को चुनने के लिए उप-चयन में किया जा सकता है, फिर अधिकतम (आईडी) +1 के साथ मिलकर इसे एक साथ करने के लिए किया जा सकता है? – bobince

9
SELECT TOP 1 t1.id+1 
FROM mytable t1 
LEFT OUTER JOIN mytable t2 ON (t1.id + 1 = t2.id) 
WHERE t2.id IS NULL 
ORDER BY t1.id; 

यह @Jeffr द्वारा दिए गए सहसंबद्ध सबक्वेरी का उपयोग कर जवाब के लिए एक विकल्प है आई हंटलिन और @ डेरेल मिलर।

हालांकि, आप जिस पॉलिसी का वर्णन कर रहे हैं वह वास्तव में एक अच्छा विचार नहीं है। आईडी मान अद्वितीय होना चाहिए, लेकिन लगातार होने की आवश्यकता नहीं होनी चाहिए।

क्या होता है यदि आप दस्तावेज # 42 के लिंक के साथ किसी को ईमेल करते हैं, और उसके बाद दस्तावेज़ को हटा देते हैं? बाद में, आप एक नए दस्तावेज़ के लिए आईडी # 42 का दोबारा उपयोग करें। अब ईमेल प्राप्तकर्ता गलत दस्तावेज़ के लिंक का पालन करेगा!

+0

गति देने के लिए मौजूद नहीं है, मैं मानता हूं कि यह नहीं मिला 1 का गुम मूल्य 1. हालांकि, यह एक फर्जी समस्या है, जो मेरा असली बिंदु है, इसलिए मुझे समाधान के साथ आने में कोई दिलचस्पी नहीं है! :-P –

+0

दस्तावेज़ संख्या कभी नहीं हटाई जाती है। हालांकि, मैं आपसे सहमत हूं कि यह दस्तावेज़ों की पहचान करने का एक खराब तरीका है। मैं अपनी लड़ाई चुन रहा हूं, हालांकि और तलना करने के लिए बड़ी मछली हैं। –

12

अब तक किसी भी उत्तर में लॉकिंग या समरूपता का कोई उल्लेख नहीं है।

पर लगभग एक ही समय एक दस्तावेज़ जोड़ने के लिए इन दो उन पर विचार करें: -: क) उस त्रुटि संभाल कर रखें और फिर अगले उपलब्ध ईद की तलाश में पाश के आसपास जाने के लिए, या

User 1    User 2 
Find Id    
         Find Id 
Id = 42    
         Id = 42 
Insert (42..) 
         Insert (42..) 
         Error! 

आप या तो करने की जरूरत है बी) प्रक्रिया की शुरुआत में लॉक आउट करें, इसलिए केवल 1 उपयोगकर्ता किसी विशेष समय पर आईडी को ढूंढ रहा है

1

आपको वास्तव में कॉलम को पहचान में बदलने की कोशिश करनी चाहिए। पहले बैकअप पहले दस्तावेज़ आईडी को अपडेट करने के लिए ROW_NUMBER का उपयोग करें ताकि वे 1 से और दस्तावेज़ गणना तक शुरू हो जाएं। आपको उस समय एक WHILE में ऐसा करना चाहिए क्योंकि यदि संख्या कॉलम अन्य तालिकाओं (विदेशी कुंजी) में संदर्भ के रूप में उपयोग किया जाता है तो SQL सर्वर विदेशी कुंजी को अपडेट करने का प्रयास करेगा और शायद विवादों के कारण विफल हो जाएगा। अंत में कॉलम के लिए पहचान विनिर्देशों को सक्षम करें।

:) यह अब और अधिक काम है लेकिन यह आपको बाद में बहुत परेशानी बचाएगा।

2
select 
    MIN(NextID) NextUsableID 
from (
    select (case when c1 = c2 then 0 
      else c1 end) NextID 
    from ( select ROW_NUMBER() over (order by record_id) c1, 
        record_id c2 
      from myTable) 
) 
where NextID > 0 
3
declare @value int 

select @value = case 
        when @value is null or @value + 1 = idcolumn 
        then idcolumn 
        else @value end 
    from table 
    order by idcolumn 

select @value + 1 

से कम 2 एक हैश मैच स्कैन करता है और एक शीर्ष जवाब

+1

बहुत तेज़ तो शीर्ष उत्तर +1! –

2

यहाँ एक सरल तरीका है की तरह शामिल हो करता है बल्कि 1 तालिका स्कैन। यह तेज़ नहीं हो सकता है। शुरुआत में गायब संख्या नहीं मिलेगी।

SELECT MIN(MT1.MyInt+1) 
FROM MyTable MT1 
LEFT OUTER JOIN MyTable MT2 ON (MT1.MyInt+1)=MT2.MyInt 
WHERE MT2.MyInt Is Null 
1

मैं जानता हूँ कि क्या यह उत्तर देर हो चुकी है, लेकिन आप एक पुनरावर्ती तालिका अभिव्यक्ति का उपयोग करके सबसे छोटी अप्रयुक्त नंबर मिल सकता है:

CREATE TABLE Test 
(
    ID int NOT NULL 
) 

--Insert values here 

;WITH CTE AS 
(
    --This is called once to get the minimum and maximum values 
    SELECT nMin = 1, MAX(ID) + 1 as 'nMax' 
    FROM Test 
    UNION ALL 
    --This is called multiple times until the condition is met 
    SELECT nMin + 1, nMax 
    FROM CTE 
    WHERE nMin < nMax 
) 

--Retrieves all the missing values in the table. Removing TOP 1 will 
--list all the unused numbers up to Max + 1 
SELECT TOP 1 nMin 
FROM CTE 
WHERE NOT EXISTS 
(
    SELECT ID 
    FROM Test 
    WHERE nMin = ID 
) 
1

की अपनी आईडी मान हमेशा 1 साथ शुरू करना चाहिए करते हैं:

SELECT MIN(a.id) + 1 AS firstfree 
FROM (SELECT id FROM table UNION SELECT 0) a 
LEFT JOIN table b ON b.id = a.id + 1 
WHERE b.id IS NULL 

यह उन सभी मामलों को संभालता है जिनके बारे में मैं सोच सकता हूं - इसमें कोई मौजूदा रिकॉर्ड भी शामिल नहीं है।

केवल एक चीज मैं इस समाधान के बारे में पसंद नहीं है अतिरिक्त शर्तें, शामिल किया जाना है कि दो बार है कि तरह है:

SELECT MIN(a.id) + 1 AS firstfree 
FROM (SELECT id FROM table WHERE column = 4711 UNION SELECT 0) a 
LEFT JOIN table b ON b.column = 4711 AND b.id = a.id + 1 
WHERE b.id IS NULL 

भी ताला लगा और संगामिति के बारे में टिप्पणी की सूचना दें - आवश्यकता अंतराल को भरने ज्यादातर मामलों में खराब डिजाइन है और समस्याएं पैदा कर सकता है। हालांकि, I के पास ऐसा करने का एक अच्छा कारण था: आईडी मुद्रित और मनुष्यों द्वारा टाइप की जानी चाहिए और हम कुछ समय बाद कई अंकों के साथ आईडी नहीं चाहते हैं, जबकि सभी निम्न नि: शुल्क हैं ...

0

मैं एक ऐसी ही समस्या का सामना करना पड़ा है और इस के साथ आया था:

Select Top 1 IdGapCheck 
From (Select Id, ROW_NUMBER() Over (Order By Id Asc) AS IdGapCheck 
    From dbo.table) F 
Where Id > IdGapCheck 
Order By Id Asc