2009-08-21 30 views
72

Tôi muốn tìm "khoảng cách" đầu tiên trong cột truy cập trong bảng SQL. Ví dụ: nếu có giá trị 1,2,4 và 5, tôi muốn tìm hiểu 3.Làm cách nào để tìm "khoảng trống" trong bộ đếm đang chạy với SQL?

Tôi có thể lấy giá trị theo thứ tự và thực hiện theo cách thủ công, nhưng tôi muốn biết sẽ có một cách để làm điều đó trong SQL.

Ngoài ra, nó phải là SQL khá chuẩn, làm việc với các DBMS khác nhau.

+0

trong SQL Server 2008 và lên bạn có thể sử dụng 'LAG (id, 1, null)' hàm với mệnh đề 'OVER (ORDER BY id)'. – ajeh

Trả lời

133

Trong 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 

Trong 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 

Trong 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 (làm việc ở khắp mọi nơi, hiệu quả nhất):

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

Hệ thống hỗ trợ trượt chức năng cửa sổ:

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 
+12

siêu hữu ích, tôi sẽ hình thành mã này :) – vulkanino

+30

@vulkanino: hãy yêu cầu họ giữ lại phần đầu dòng. Ngoài ra xin lưu ý rằng giấy phép commons sáng tạo yêu cầu bạn phải đánh dấu nick của tôi và câu hỏi 'URL' là tốt, mặc dù nó có thể được mã hóa QR tôi nghĩ. – Quassnoi

+3

Điều này thật tuyệt, nhưng nếu tôi có '[1, 2, 11, 12]', thì điều này sẽ chỉ tìm thấy '3'. Những gì tôi muốn nó tìm thấy là 3-10 thay vào đó - về cơ bản là sự bắt đầu và kết thúc của mọi khoảng trống. Tôi hiểu rằng tôi có thể phải viết kịch bản python của riêng tôi để tận dụng SQL (trong trường hợp MySql), nhưng sẽ tốt hơn nếu SQL có thể đưa tôi đến gần hơn với những gì tôi muốn (tôi có một bảng với 2 triệu hàng có khoảng trống, vì vậy tôi sẽ cần phải cắt nó thành các phần nhỏ hơn và chạy một số SQL trên đó). Tôi cho rằng tôi có thể chạy một truy vấn để tìm sự bắt đầu của một khoảng trống, sau đó một truy vấn khác để tìm kết thúc của một khoảng trống và chúng "hợp nhất sắp xếp" hai chuỗi. –

7

Điều đầu tiên xuất hiện trong đầu tôi. Không chắc chắn nếu đó là một ý tưởng tốt để đi theo cách này ở tất cả, nhưng nên làm việc. Giả sử bảng là t và cột là 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

Edit: Cái này có thể là một đánh dấu nhanh hơn (và ngắn hơn!):

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

+0

LEFT OUTER JOIN t ==> LEFT OUTER JOIN t2 –

+1

Không, không, Eamon, 'LEFT OUTER JOING t2' sẽ yêu cầu bạn có bảng' t2', đó chỉ là một bí danh. –

6

này hoạt động trong SQL Server - không thể kiểm tra nó trong các hệ thống khác nhưng có vẻ như tiêu chuẩn ...

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

Bạn cũng có thể thêm một điểm khởi đầu để mệnh đề where ...

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

Vì vậy, nếu bạn có 2000, 2001, 2002, và 2005 ở đâu 2003 và 2004 không tồn tại, nó sẽ quay trở lại năm 2003.

2

Tham gia bên trong vào chế độ xem hoặc chuỗi có tất cả các giá trị có thể có.

Không có bảng? Làm một cái bàn. Tôi luôn giữ một cái bàn giả chỉ để làm việc này.

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 

Sau đó,

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

Tôi đoán:

SELECT MIN(p1.field) + 1 as gap 
FROM table1 AS p1 
INNER JOIN table1 as p3 ON (p1.field = p3.field + 2) 
LEFT OUTER JOIN table1 AS p2 ON (p1.field = p2.field + 1) 
WHERE p2.field is null; 
4

Không có thực sự là một cực kỳ tiêu chuẩn đường SQL để làm điều này, nhưng với một số hình thức hạn chế khoản bạn có thể làm

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)

hoặc

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

(SQL Server)

hoặc

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

(Oracle)

+0

nếu có khoảng cách, chỉ có hàng đầu tiên phạm vi sẽ được trả về cho truy vấn postgres của bạn. –

8

câu trả lời của bạn tất cả các hoạt động tốt nếu bạn có giá trị đầu tiên id = 1, nếu không khoảng cách này sẽ không được phát hiện. Ví dụ nếu giá trị id bảng của bạn là 3,4,5, truy vấn của bạn sẽ trở lại 6.

tôi đã làm một cái gì đó như thế này

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

Cái này chiếm tất cả mọi thứ đề cập đến nay. Nó bao gồm 0 là điểm bắt đầu, nó sẽ mặc định nếu không có giá trị nào tồn tại. Tôi cũng đã thêm các vị trí thích hợp cho các phần khác của khóa đa giá trị. Điều này chỉ được thử nghiệm trên SQL Server.

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 
0
select min([ColumnName]) from [TableName] 
where [ColumnName]-1 not in (select [ColumnName] from [TableName]) 
and [ColumnName] <> (select min([ColumnName]) from [TableName]) 
1

Đối PostgreSQL

Một ví dụ mà làm cho sử dụng các truy vấn đệ quy.

Điều này có thể hữu ích nếu bạn muốn tìm một khoảng trống trong một phạm vi cụ thể (nó sẽ làm việc ngay cả khi bảng là trống rỗng, trong khi các ví dụ khác sẽ không)

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 
0

Dưới đây là tiêu chuẩn một SQL giải pháp chạy trên tất cả các máy chủ cơ sở dữ liệu mà không thay đổi:

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

Xem hoạt động;

0

Nó hoạt động cho các bảng trống hoặc với các giá trị âm là tốt.Chỉ cần thử nghiệm trong SQL Server 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

Nếu Bạn sử dụng Firebird 3 này là thanh lịch nhất và đơn giản:

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 
Các vấn đề liên quan