এসকিউএল সহ কাউন্টার চালানোর ক্ষেত্রে আমি কীভাবে একটি "ফাঁক" খুঁজে পাব?


106

আমি এসকিউএল টেবিলের কাউন্টার কলামে প্রথম "ফাঁক" খুঁজে পেতে চাই। উদাহরণস্বরূপ, যদি মানগুলি 1,2,4 এবং 5 হয় তবে আমি 3 খুঁজে বের করতে চাই।

আমি অবশ্যই মানগুলি যথাযথভাবে পেতে পারি এবং এটি নিজে নিজেই যেতে পারি তবে এসকিউএল-তে এটি করার কোনও উপায় আছে কিনা তা জানতে চাই I'd

এছাড়াও, এটি বিভিন্ন স্ট্যান্ডার্ড এসকিউএল হওয়া উচিত, বিভিন্ন ডিবিএমএসের সাথে কাজ করা।


এসকিএল সার্ভার ২০০৮ এবং তারপরে আপনি ক্লজ LAG(id, 1, null)সহ ফাংশনটি ব্যবহার করতে পারেন OVER (ORDER BY id)
আজিহ

উত্তর:


184

ইন MySQLএবং PostgreSQL:

SELECT  id + 1
FROM    mytable mo
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    mytable mi 
        WHERE   mi.id = mo.id + 1
        )
ORDER BY
        id
LIMIT 1

ইন SQL Server:

SELECT  TOP 1
        id + 1
FROM    mytable mo
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    mytable mi 
        WHERE   mi.id = mo.id + 1
        )
ORDER BY
        id

ইন Oracle:

SELECT  *
FROM    (
        SELECT  id + 1 AS gap
        FROM    mytable mo
        WHERE   NOT EXISTS
                (
                SELECT  NULL
                FROM    mytable mi 
                WHERE   mi.id = mo.id + 1
                )
        ORDER BY
                id
        )
WHERE   rownum = 1

ANSI (সর্বত্র কার্যকর, সর্বনিম্ন দক্ষ):

SELECT  MIN(id) + 1
FROM    mytable mo
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    mytable mi 
        WHERE   mi.id = mo.id + 1
        )

স্লাইডিং উইন্ডো ফাংশন সমর্থনকারী সিস্টেমগুলি:

SELECT  -- TOP 1
        -- Uncomment above for SQL Server 2012+
        previd
FROM    (
        SELECT  id,
                LAG(id) OVER (ORDER BY id) previd
        FROM    mytable
        ) q
WHERE   previd <> id - 1
ORDER BY
        id
-- LIMIT 1
-- Uncomment above for PostgreSQL

39
@ ভলকানিনো: দয়া করে এডেন্টেশন সংরক্ষণ করতে বলুন। এছাড়াও দয়া করে নোট করুন যে সৃজনশীল কমন্স লাইসেন্সের জন্য আমার নিক এবং প্রশ্নটিও ট্যাটু URLকরা উচিত, যদিও এটি আমার মনে হয় কিউআর কোডড হতে পারে।
কাসনোই

4
এটি দুর্দান্ত, তবে যদি আমি থাকতাম [1, 2, 11, 12]তবে এটি কেবল খুঁজে পাওয়া যেত 3। পরিবর্তে 3-10 এটির আমি খুঁজে পেতে যা পছন্দ করব তা হল - মূলত প্রতিটি ব্যবধানের শুরু এবং শেষ। আমি বুঝতে পেরেছি যে আমার নিজের পাইথন স্ক্রিপ্ট লিখতে হবে যা এসকিউএল (আমার ক্ষেত্রে মাইকিউএসএল) -এর ক্ষতিসাধন করে, তবে এসকিউএল আমাকে যা চাইবে তার কাছাকাছি পেতে পারলে ভাল লাগবে (আমার কাছে 2 মিলিয়ন সারি ফাঁকযুক্ত একটি টেবিল রয়েছে, সুতরাং আমার এটি আরও ছোট ছোট টুকরো করে কাটাতে হবে এবং এতে কিছু এসকিউএল চালাতে হবে)। আমি মনে করি আমি একটি ফাঁকির প্রারম্ভ খুঁজে পেতে একটি কোয়েরি চালাতে পারি, তারপরে আরেকটি ফাঁকের সমাপ্তি খুঁজে পেতে এবং সেগুলি দুটি সিকোয়েন্সগুলিকে "মার্জ্ট সাজ্ট" করে।
হামিশ গ্রুবিজান

1
@ হামিশগ্রুবিজান: দয়া করে এটি অন্য একটি প্রশ্ন হিসাবে পোস্ট করুন
কাসনসুই

2
@Malkocoglu: আপনি পাবেন NULLনা 0যদি টেবিল খালি। এটি সমস্ত ডাটাবেসের ক্ষেত্রে সত্য।
কাসনসুই

5
এটি প্রাথমিক ফাঁকগুলি সঠিকভাবে খুঁজে পাবে না। যদি আপনার 3,4,5,6,8 থাকে। এই কোডটি 7 টি প্রতিবেদন করবে, কারণ এটির সাথে চেক করারও কোনও 1 নেই। সুতরাং আপনি যদি শুরুর সংখ্যাগুলি অনুপস্থিত থাকেন তবে আপনাকে এটির জন্য পরীক্ষা করতে হবে।
ttomsen

12

আপনার উত্তরগুলি সমস্ত ঠিক আছে যদি আপনার প্রথম মান আইডি = 1 থাকে তবে অন্যথায় এই ফাঁকটি সনাক্ত করা যাবে না। উদাহরণস্বরূপ, যদি আপনার টেবিলের আইডি মানগুলি 3,4,5 হয়, তবে আপনার অনুসন্ধানগুলি 6 ফিরে আসবে।

আমি এরকম কিছু করেছি

SELECT MIN(ID+1) FROM (
    SELECT 0 AS ID UNION ALL 
    SELECT  
        MIN(ID + 1)
    FROM    
        TableX) AS T1
WHERE
    ID+1 NOT IN (SELECT ID FROM TableX) 

এটি প্রথম ফাঁকটি খুঁজে পাবে। আপনার যদি আইডি 0, 2,3,4 থাকে। উত্তরটি হল ১। আমি বৃহত্তম ফাঁক খুঁজে পাওয়ার জন্য একটি উত্তর খুঁজছিলাম। ক্রমটি 0,2,3,4, 100,101,102 বলুন। আমি 4-99 এর ফাঁক খুঁজতে চাই।
কেমিন

8

এটি করার জন্য একটি অত্যন্ত স্ট্যান্ডার্ড এসকিউএল উপায় নেই, তবে কিছু ধারা সীমাবদ্ধ করে আপনি এটি করতে পারেন

SELECT `table`.`num` + 1
FROM `table`
LEFT JOIN `table` AS `alt`
ON `alt`.`num` = `table`.`num` + 1
WHERE `alt`.`num` IS NULL
LIMIT 1

(মাইএসকিউএল, পোস্টগ্র্যাস এসকিউএল)

অথবা

SELECT TOP 1 `num` + 1
FROM `table`
LEFT JOIN `table` AS `alt`
ON `alt`.`num` = `table`.`num` + 1
WHERE `alt`.`num` IS NULL

(SQL সার্ভার)

অথবা

SELECT `num` + 1
FROM `table`
LEFT JOIN `table` AS `alt`
ON `alt`.`num` = `table`.`num` + 1
WHERE `alt`.`num` IS NULL
AND ROWNUM = 1

(ওরাকল)


যদি কোনও ফাঁক সীমা থাকে তবে রেঞ্জের প্রথম সারিটিই আপনার পোস্টগ্রোসের ক্যোয়ারির জন্য ফিরে আসবে।
জন হাগল্যান্ড

এটি আমার কাছে সবচেয়ে বেশি অর্থবোধ করে, একটি জয়েন ব্যবহার করা আপনাকে আরও শীর্ষস্থানীয় ফলাফলগুলি দেখানোর জন্য আপনার শীর্ষ মান পরিবর্তন করতে দেয়।
এজে_

1
ধন্যবাদ, এটি খুব ভালভাবে কাজ করে এবং আপনি যদি সমস্ত পয়েন্ট দেখতে চান যেখানে কোনও ফাঁক রয়েছে, আপনি সীমাটি সরাতে পারেন।
mekbib.awoke

8

আমার মাথায় প্রথম জিনিসটি এসেছিল। মোটেও এই পথে যাওয়া ভাল ধারণা কিনা তা নিশ্চিত নন, তবে কাজ করা উচিত। ধরুন টেবিলটি হ'ল tএবং কলামটি হ'ল c:

SELECT t1.c+1 AS gap FROM t as t1 LEFT OUTER JOIN t as t2 ON (t1.c+1=t2.c) WHERE t2.c IS NULL ORDER BY gap ASC LIMIT 1

সম্পাদনা: এটি দ্রুত একটি টিক (এবং সংক্ষিপ্ত!) হতে পারে:

SELECT min(t1.c)+1 AS gap FROM t as t1 LEFT OUTER JOIN t as t2 ON (t1.c+1=t2.c) WHERE t2.c IS NULL


বাম আউটয়ার যোগ দিন t ==> বাম আউটয়ার যোগ দিন t2
ইমন নের্বোন

1
না-না, ইমন, LEFT OUTER JOING t2আপনার t2টেবিল থাকা দরকার, যা কেবল একটি উপনাম।
মাইকেল ক্রেলিন - হ্যাকার

6

এটি এসকিউএল সার্ভারে কাজ করে - অন্যান্য সিস্টেমে এটি পরীক্ষা করতে পারে না তবে এটি স্ট্যান্ডার্ড বলে মনে হচ্ছে ...

SELECT MIN(t1.ID)+1 FROM mytable t1 WHERE NOT EXISTS (SELECT ID FROM mytable WHERE ID = (t1.ID + 1))

আপনি যেখানে ধারাটিতে একটি সূচনা পয়েন্ট যোগ করতে পারেন ...

SELECT MIN(t1.ID)+1 FROM mytable t1 WHERE NOT EXISTS (SELECT ID FROM mytable WHERE ID = (t1.ID + 1)) AND ID > 2000

সুতরাং আপনার যদি 2000, 2001, 2002 এবং 2005 থাকে যেখানে 2003 এবং 2004 এর অস্তিত্ব ছিল না, এটি 2003 এ ফিরে আসবে।


3

নিম্নলিখিত সমাধান:

  • পরীক্ষার তথ্য সরবরাহ করে;
  • একটি অভ্যন্তরীণ কোয়েরি যা অন্যান্য ফাঁক তৈরি করে; এবং
  • এটি এসকিউএল সার্ভার 2012 এ কাজ করে।

" সহ " ধারাটিতে ক্রমযুক্ত ক্রমগুলি চিহ্নিত করুন এবং তারপরে সারি সংখ্যার অভ্যন্তরীণ জোড়ায় ফলাফলটি দু'বার পুনরায় ব্যবহার করেছেন, তবে 1 দিয়ে অফসেট করুন যাতে সারিটির পরে সারিটির আগে তুলনা করতে, এর চেয়ে বেশি ফাঁক দিয়ে আইডি সন্ধান করুন 1. চেয়ে বেশি চেয়ে বেশি কিন্তু ব্যাপকভাবে প্রযোজ্য।

create table #ID ( id integer );

insert into #ID values (1),(2),    (4),(5),(6),(7),(8),    (12),(13),(14),(15);

with Source as (
    select
         row_number()over ( order by A.id ) as seq
        ,A.id                               as id
    from #ID as A WITH(NOLOCK)
)
Select top 1 gap_start from (
    Select 
         (J.id+1) as gap_start
        ,(K.id-1) as gap_end
    from       Source as J
    inner join Source as K
    on (J.seq+1) = K.seq
    where (J.id - (K.id-1)) <> 0
) as G

অভ্যন্তরীণ কোয়েরি উত্পাদন করে:

gap_start   gap_end

3           3

9           11

বাহ্যিক ক্যোয়ারী উত্পন্ন করে:

gap_start

3

2

সমস্ত সম্ভাব্য মান রয়েছে এমন একটি দর্শন বা ক্রমটিতে অভ্যন্তরীণ যোগদান করুন।

টেবিল নেই? একটি টেবিল তৈরি করুন। আমি সবসময় কেবল এই জন্য একটি ডামি টেবিল রাখি।

create table artificial_range( 
  id int not null primary key auto_increment, 
  name varchar( 20 ) null ) ;

-- or whatever your database requires for an auto increment column

insert into artificial_range( name ) values ( null )
-- create one row.

insert into artificial_range( name ) select name from artificial_range;
-- you now have two rows

insert into artificial_range( name ) select name from artificial_range;
-- you now have four rows

insert into artificial_range( name ) select name from artificial_range;
-- you now have eight rows

--etc.

insert into artificial_range( name ) select name from artificial_range;
-- you now have 1024 rows, with ids 1-1024

তারপর,

 select a.id from artificial_range a
 where not exists ( select * from your_table b
 where b.counter = a.id) ;

2

জন্য PostgreSQL

এমন একটি উদাহরণ যা পুনরাবৃত্তির ক্যোয়ারী ব্যবহার করে।

আপনি যদি একটি নির্দিষ্ট পরিসরে ফাঁক খুঁজে পেতে চান তবে এটি কার্যকর হতে পারে (টেবিলটি খালি থাকলেও এটি কাজ করবে, অন্য উদাহরণগুলি এটি ব্যবহার করবে না)

WITH    
    RECURSIVE a(id) AS (VALUES (1) UNION ALL SELECT id + 1 FROM a WHERE id < 100), -- range 1..100  
    b AS (SELECT id FROM my_table) -- your table ID list    
SELECT a.id -- find numbers from the range that do not exist in main table
FROM a
LEFT JOIN b ON b.id = a.id
WHERE b.id IS NULL
-- LIMIT 1 -- uncomment if only the first value is needed


1

এটি এখন পর্যন্ত উল্লিখিত সমস্ত কিছুর জন্য অ্যাকাউন্ট করে। এটিতে শুরুর পয়েন্ট হিসাবে 0 অন্তর্ভুক্ত রয়েছে, যা কোনও মানের অস্তিত্ব না থাকলে এটি ডিফল্ট হবে। আমি একটি বহু-মান কী এর অন্যান্য অংশগুলির জন্য উপযুক্ত অবস্থানগুলিও যুক্ত করেছি। এটি কেবল এসকিউএল সার্ভারে পরীক্ষা করা হয়েছে।

select
    MIN(ID)
from (
    select
        0 ID
    union all
    select
        [YourIdColumn]+1
    from
        [YourTable]
    where
        --Filter the rest of your key--
    ) foo
left join
    [YourTable]
    on [YourIdColumn]=ID
    and --Filter the rest of your key--
where
    [YourIdColumn] is null

1

আমি এটি করার একটি দ্রুত উপায় লিখেছিলাম। নিশ্চিত না যে এটি সর্বাধিক দক্ষ, তবে কাজটি সম্পন্ন করে। মনে রাখবেন যে এটি আপনাকে ফাঁকটি বলে না, তবে ফাঁকের আগে এবং পরে আপনাকে আইডি বলে দেয় (মনে রাখবেন ফাঁকটি একাধিক মান হতে পারে, উদাহরণস্বরূপ 1,2,4,7,11 ইত্যাদি)

আমি উদাহরণ হিসাবে স্ক্লাইট ব্যবহার করছি

এটি যদি আপনার টেবিল কাঠামো হয়

create table sequential(id int not null, name varchar(10) null);

এবং এগুলি আপনার সারি

id|name
1|one
2|two
4|four
5|five
9|nine

কোয়েরিটি হ'ল

select a.* from sequential a left join sequential b on a.id = b.id + 1 where b.id is null and a.id <> (select min(id) from sequential)
union
select a.* from sequential a left join sequential b on a.id = b.id - 1 where b.id is null and a.id <> (select max(id) from sequential);

https://gist.github.com/wkimeria/7787ffe84d1c54216f1b320996b17b7e


0
select min([ColumnName]) from [TableName]
where [ColumnName]-1 not in (select [ColumnName] from [TableName])
and [ColumnName] <> (select min([ColumnName]) from [TableName])

0

এখানে স্ট্যান্ডার্ড একটি এসকিউএল সমাধান রয়েছে যা কোনও পরিবর্তন ছাড়াই সমস্ত ডাটাবেস সার্ভারে চলে:

select min(counter + 1) FIRST_GAP
    from my_table a
    where not exists (select 'x' from my_table b where b.counter = a.counter + 1)
        and a.counter <> (select max(c.counter) from my_table c);

জন্য ক্রিয়া দেখুন;


0

এটি খালি টেবিলগুলির জন্য বা negativeণাত্মক মানগুলির সাথেও কাজ করে। সবেমাত্র এসকিউএল সার্ভার 2012 এ পরীক্ষা করা হয়েছে

 select min(n) from (
select  case when lead(i,1,0) over(order by i)>i+1 then i+1 else null end n from MyTable) w

0

আপনি যদি ফায়ারবার্ড 3 ব্যবহার করেন তবে এটি সবচেয়ে মার্জিত এবং সহজ:

select RowID
  from (
    select `ID_Column`, Row_Number() over(order by `ID_Column`) as RowID
      from `Your_Table`
        order by `ID_Column`)
    where `ID_Column` <> RowID
    rows 1

0
            -- PUT THE TABLE NAME AND COLUMN NAME BELOW
            -- IN MY EXAMPLE, THE TABLE NAME IS = SHOW_GAPS AND COLUMN NAME IS = ID

            -- PUT THESE TWO VALUES AND EXECUTE THE QUERY

            DECLARE @TABLE_NAME VARCHAR(100) = 'SHOW_GAPS'
            DECLARE @COLUMN_NAME VARCHAR(100) = 'ID'


            DECLARE @SQL VARCHAR(MAX)
            SET @SQL = 
            'SELECT  TOP 1
                    '+@COLUMN_NAME+' + 1
            FROM    '+@TABLE_NAME+' mo
            WHERE   NOT EXISTS
                    (
                    SELECT  NULL
                    FROM    '+@TABLE_NAME+' mi 
                    WHERE   mi.'+@COLUMN_NAME+' = mo.'+@COLUMN_NAME+' + 1
                    )
            ORDER BY
                    '+@COLUMN_NAME

            -- SELECT @SQL

            DECLARE @MISSING_ID TABLE (ID INT)

            INSERT INTO @MISSING_ID
            EXEC (@SQL)

            --select * from @MISSING_ID

            declare @var_for_cursor int
            DECLARE @LOW INT
            DECLARE @HIGH INT
            DECLARE @FINAL_RANGE TABLE (LOWER_MISSING_RANGE INT, HIGHER_MISSING_RANGE INT)
            DECLARE IdentityGapCursor CURSOR FOR   
            select * from @MISSING_ID
            ORDER BY 1;  

            open IdentityGapCursor

            fetch next from IdentityGapCursor
            into @var_for_cursor

            WHILE @@FETCH_STATUS = 0  
            BEGIN
            SET @SQL = '
            DECLARE @LOW INT
            SELECT @LOW = MAX('+@COLUMN_NAME+') + 1 FROM '+@TABLE_NAME
                    +' WHERE '+@COLUMN_NAME+' < ' + cast( @var_for_cursor as VARCHAR(MAX))

            SET @SQL = @sql + '
            DECLARE @HIGH INT
            SELECT @HIGH = MIN('+@COLUMN_NAME+') - 1 FROM '+@TABLE_NAME
                    +' WHERE '+@COLUMN_NAME+' > ' + cast( @var_for_cursor as VARCHAR(MAX))

            SET @SQL = @sql + 'SELECT @LOW,@HIGH'

            INSERT INTO @FINAL_RANGE
             EXEC( @SQL)
            fetch next from IdentityGapCursor
            into @var_for_cursor
            END

            CLOSE IdentityGapCursor;  
            DEALLOCATE IdentityGapCursor;  

            SELECT ROW_NUMBER() OVER(ORDER BY LOWER_MISSING_RANGE) AS 'Gap Number',* FROM @FINAL_RANGE

0

পাওয়া গেছে বেশিরভাগ পদ্ধতির খুব খুব ধীর গতিতে চলে mysql। এখানে আমার সমাধান mysql < 8.0। সমাপ্তির জন্য ~ 1 সেকেন্ডের কাছাকাছি একটি ফাঁক দিয়ে 1 এম রেকর্ডে পরীক্ষিত। এটি অন্যান্য এসকিউএল স্বাদে ফিট করে কিনা তা নিশ্চিত নন।

SELECT cardNumber - 1
FROM
    (SELECT @row_number := 0) as t,
    (
        SELECT (@row_number:=@row_number+1), cardNumber, cardNumber-@row_number AS diff
        FROM cards
        ORDER BY cardNumber
    ) as x
WHERE diff >= 1
LIMIT 0,1
আমি অনুমান করি যে ক্রমটি শুরু হয় `1` থেকে`

0

যদি আপনার কাউন্টারটি 1 থেকে শুরু হয় এবং আপনি খালি অবস্থায় প্রথম সংখ্যা (1) তৈরি করতে চান তবে ওরাকলের জন্য প্রথম উত্তরটির কোডের সংশোধিত টুকরাটি এখানে:

SELECT
  NVL(MIN(id + 1),1) AS gap
FROM
  mytable mo  
WHERE 1=1
  AND NOT EXISTS
      (
       SELECT  NULL
       FROM    mytable mi 
       WHERE   mi.id = mo.id + 1
      )
  AND EXISTS
     (
       SELECT  NULL
       FROM    mytable mi 
       WHERE   mi.id = 1
     )  

0
DECLARE @Table AS TABLE(
[Value] int
)

INSERT INTO @Table ([Value])
VALUES
 (1),(2),(4),(5),(6),(10),(20),(21),(22),(50),(51),(52),(53),(54),(55)
 --Gaps
 --Start    End     Size
 --3        3       1
 --7        9       3
 --11       19      9
 --23       49      27


SELECT [startTable].[Value]+1 [Start]
     ,[EndTable].[Value]-1 [End]
     ,([EndTable].[Value]-1) - ([startTable].[Value]) Size 
 FROM 
    (
SELECT [Value]
    ,ROW_NUMBER() OVER(PARTITION BY 1 ORDER BY [Value]) Record
FROM @Table
)AS startTable
JOIN 
(
SELECT [Value]
,ROW_NUMBER() OVER(PARTITION BY 1 ORDER BY [Value]) Record
FROM @Table
)AS EndTable
ON [EndTable].Record = [startTable].Record+1
WHERE [startTable].[Value]+1 <>[EndTable].[Value]

0

কলামের সংখ্যাগুলি যদি ইতিবাচক পূর্ণসংখ্যা হয় (1 থেকে শুরু হয়) তবে সহজেই কীভাবে এটি সমাধান করা যায় তা এখানে। (ধরে নিচ্ছেন আইডি আপনার কলামের নাম)

    SELECT TEMP.ID 
    FROM (SELECT ROW_NUMBER() OVER () AS NUM FROM 'TABLE-NAME') AS TEMP 
    WHERE ID NOT IN (SELECT ID FROM 'TABLE-NAME')
    ORDER BY 1 ASC LIMIT 1

এটি 'সারণী-নাম' -তে "নির্বাচন করুন ROW_NUMBER () ওভার () হিসাবে" টেবিল-নাম "হিসাবে NUM সারিগুলির সংখ্যার
অবধি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.