[sql] SQL로 카운터를 실행할 때 “갭”을 어떻게 찾습니까?

SQL 테이블의 카운터 열에서 첫 번째 “간격”을 찾고 싶습니다. 예를 들어 1, 2, 4, 5 값이 있다면 3을 찾고 싶습니다.

물론 값을 순서대로 가져 와서 수동으로 살펴볼 수 있지만 SQL에서 수행하는 방법이 있는지 알고 싶습니다.

또한 다양한 DBMS에서 작동하는 표준 SQL이어야합니다.



답변

에서 MySQLPostgreSQL:

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


답변

첫 번째 값 id = 1이면 모든 답변이 제대로 작동합니다. 그렇지 않으면이 간격이 감지되지 않습니다. 예를 들어 테이블 ID 값이 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) 


답변

이를 수행 하는 매우 표준적인 SQL 방법은 없지만 어떤 형태의 제한 절을 사용하면 할 수 있습니다.

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

(MySQL, PostgreSQL)

또는

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

(신탁)


답변

내 머릿속에 처음으로 떠오른 것. 이런 식으로가는 것이 좋은 생각인지 확실하지 않지만 작동해야합니다. 테이블이 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


답변

이것은 SQL Server에서 작동합니다-다른 시스템에서는 테스트 할 수 없지만 표준으로 보입니다.

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

where 절에 시작점을 추가 할 수도 있습니다.

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

따라서 2003과 2004가 존재하지 않는 2000, 2001, 2002 및 2005가 있다면 2003을 반환합니다.


답변

다음 솔루션 :

  • 테스트 데이터를 제공합니다.
  • 다른 갭을 생성하는 내부 쿼리; 과
  • SQL Server 2012에서 작동합니다.

with “절 에서 순서가 지정된 행에 순차적으로 번호 를 매긴 다음 행 번호의 내부 조인을 사용하여 결과를 두 번 재사용하지만 이전 행과 이후 행을 비교하기 위해 1만큼 오프셋하여 간격이 다음보다 큰 ID를 찾습니다. 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


답변

가능한 모든 값이있는 뷰 또는 시퀀스에 대한 내부 조인.

테이블이 없나요? 테이블을 만드십시오. 나는 항상 이것을 위해 더미 테이블을 가지고 있습니다.

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