2008-10-06 23 views
12

Gần đây tôi đã kế thừa cơ sở dữ liệu mà một trong các bảng có khóa chính bao gồm các giá trị được mã hóa (Part1 * 1000 + Part2).
Tôi đã chuẩn hóa cột đó, nhưng tôi không thể thay đổi các giá trị cũ. Vì vậy, bây giờ tôi cóCách tìm "lỗ" trong bảng

select ID from table order by ID 
ID 
100001 
100002 
101001 
... 

Tôi muốn tìm ra "lỗ hổng" trong bảng (chính xác hơn, các "lỗ" đầu tiên sau khi 100000) cho các hàng mới.
Tôi đang sử dụng lựa chọn sau đây, nhưng có cách nào tốt hơn để làm điều đó không?

select /* top 1 */ ID+1 as newID from table 
where ID > 100000 and 
ID + 1 not in (select ID from table) 
order by ID 

newID 
100003 
101029 
... 

Cơ sở dữ liệu là Microsoft SQL Server 2000. Tôi đồng ý với việc sử dụng các tiện ích mở rộng SQL.

+0

Just ra khỏi tò mò, động lực cho việc tìm kiếm những "lỗ" là gì? Đây có phải là chìa khóa thông minh không? –

+0

Cơ sở dữ liệu dành cho hệ thống kiểm soát truy cập tại nơi làm việc của tôi. Trong cơ sở dữ liệu cũ Part1 là một mã công ty và Part2 là một mã nhân viên. Nếu một người chuyển công ty, anh ta phải được cấp một thẻ mới. Hệ thống đọc thẻ cần 6 chữ số, vì vậy thẻ mới bắt đầu ở 100000, không bao gồm thẻ hiện tại. – pmg

Trả lời

12
select ID +1 From Table t1 
where not exists (select * from Table t2 where t1.id +1 = t2.id); 

không chắc chắn nếu phiên bản này sẽ nhanh hơn so với một bạn đề cập ban đầu.

+1

Tôi thích giao diện của phiên bản của bạn. Tôi sẽ kiểm tra Kế hoạch thực hiện vào sáng mai và cho bạn biết. – pmg

+1

Tôi không biết về dữ liệu của bạn, nhưng trên bảng tương tự của tôi (25.000 mục, lỗ đầu tiên ở mức 3.000 điểm), phiên bản nối trái mất khoảng một giây đồng hồ, cái này chạy quá lâu tôi đã giết sau 2 phút. –

+1

Bảng dữ liệu thực của tôi có 17500 bản ghi và rất nhiều lỗ. Lựa chọn của tôi và IronGoofy thực hiện chính xác như nhau; Phiên bản của Santiago chậm hơn một chút - nhưng tất cả 3, trên hệ thống của tôi, trả lại các lỗ trong nháy mắt. – pmg

7
SELECT (ID+1) FROM table AS t1 
LEFT JOIN table as t2 
ON t1.ID+1 = t2.ID 
WHERE t2.ID IS NULL 
+2

Tính năng này hoạt động. Cảm ơn Santiago. Trình phân tích truy vấn, trong Kế hoạch thực hiện, cho biết phiên bản chọn lựa là tốt hơn vì nó sử dụng Tham gia hợp nhất thay vì kết hợp băm. – pmg

+1

Có, tốt hơn là tránh truy vấn phụ nếu có thể. Vui mừng nó giúp! –

+1

Điều này không hiệu quả đối với tôi trong PostgreSQL, có thể là do cách chọn "id + 1" hoạt động trong đó, hoặc có thể vì nó không phải là khóa chính của tôi. –

4

Giải pháp này sẽ cung cấp cho bạn giá trị ID đầu tiên và cuối cùng của "lỗ" bạn đang tìm kiếm. Tôi sử dụng điều này trong Firebird 1.5 trên một bảng 500K hồ sơ, và mặc dù nó mất một chút thời gian, nó mang lại cho tôi những gì tôi muốn.

SELECT l.id + 1 start_id, MIN(fr.id) - 1 stop_id 
FROM (table l 
LEFT JOIN table r 
ON l.id = r.id - 1) 
LEFT JOIN table fr 
ON l.id < fr.id 
WHERE r.id IS NULL AND fr.id IS NOT NULL 
GROUP BY l.id, r.id 

Ví dụ, nếu dữ liệu của bạn trông như thế này:

ID 
1001 
1002 
1005 
1006 
1007 
1009 
1011 

Bạn sẽ nhận được điều này:

start_id stop_id 
1003  1004 
1008  1008 
1010  1010 

Tôi ước gì tôi có thể mất tín dụng đầy đủ cho giải pháp này, nhưng tôi tìm thấy tại số Xaprb.

+0

Mất một lúc để chạy trên một bảng lớn qua kết nối chậm SQL2005, nhưng chờ đợi cũng có giá trị nó! –

0

Giải pháp này không cung cấp tất cả các lỗ trong bảng, chỉ những số miễn phí tiếp theo + số thứ tự tối đa đầu tiên trên bảng - hoạt động nếu bạn muốn lấp đầy khoảng trống trong id-es, + nhận số id miễn phí nếu bạn không có một khoảng trống ..

chọn tê + 1 từ temp trừ chọn tê từ nhiệt độ;

0

từ How do I find a "gap" in running counter with SQL?

select 
    MIN(ID) 
from (
    select 
     100001 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

Cách tốt nhất là xây dựng một bảng tạm thời với tất cả các ID

Thần làm cho một trái tham gia.

declare @maxId int 
select @maxId = max(YOUR_COLUMN_ID) from YOUR_TABLE_HERE 


declare @t table (id int) 

declare @i int 
set @i = 1 

while @i <= @maxId 
begin 
    insert into @t values (@i) 
    set @i = @i +1 
end 

select t.id 
from @t t 
left join YOUR_TABLE_HERE x on x.YOUR_COLUMN_ID = t.id 
where x.YOUR_COLUMN_ID is null 
0

này sẽ cung cấp cho bạn bức tranh hoàn chỉnh, nơi 'dưới' là viết tắt của khoảng cách bắt đầu'Top' là viết tắt của khoảng cách cuối:

select * 
    from 
    ( 
     (select <COL>+1 as id, 'Bottom' AS 'Pos' from <TABLENAME> /*where <CONDITION*/> 
     except 
     select <COL>, 'Bottom' AS 'Pos' from <TABLENAME> /*where <CONDITION>*/) 
    union 
     (select <COL>-1 as id, 'Top' AS 'Pos' from <TABLENAME> /*where <CONDITION>*/ 
     except 
     select <COL>, 'Top' AS 'Pos' from <TABLENAME> /*where <CONDITION>*/) 
    ) t 
    order by t.id, t.Pos 

Lưu ý: Đầu tiênCuối cùng kết quả là WRONG và không nên được xem xét, nhưng việc loại bỏ chúng sẽ làm cho truy vấn này phức tạp hơn nhiều, vì vậy điều này sẽ làm ngay bây giờ.

1

đã nghĩ về câu hỏi này thời gian gần đây, và trông giống như đây là cách thanh lịch nhất để làm điều đó:

SELECT TOP(@MaxNumber) ROW_NUMBER() OVER (ORDER BY t1.number) 
FROM master..spt_values t1 CROSS JOIN master..spt_values t2 
EXCEPT 
SELECT Id FROM <your_table> 
0

Nhiều người trong số các câu trả lời trước là khá tốt. Tuy nhiên tất cả họ đều bỏ lỡ để trả lại giá trị đầu tiên của chuỗi và/hoặc bỏ lỡ để xem xét giới hạn dưới 100000. Tất cả đều trả về các lỗ trung gian nhưng không phải là lỗ đầu tiên (100001 nếu thiếu).

Một giải pháp đầy đủ cho câu hỏi là một sau:

select id + 1 as newid from 
    (select 100000 as id union select id from tbl) t 
    where (id + 1 not in (select id from tbl)) and 
      (id >= 100000) 
    order by id 
    limit 1; 

Số 100000 là để được sử dụng nếu số đầu tiên của dãy là 100001 (như trong câu hỏi ban đầu); nếu không nó là phải được sửa đổi cho phù "giới hạn 1" được sử dụng để có chỉ số đầu tiên có sẵn thay vì trình tự đầy đủ

0

Đối với những người sử dụng Oracle, sau đây có thể được sử dụng:

select a, b from (
    select ID + 1 a, max(ID) over (order by ID rows between current row and 1 following) - 1 b from MY_TABLE 
) where a <= b order by a desc; 
Các vấn đề liên quan