2012-06-10 38 views
13

Tôi đang làm một dự án tạo ra một hệ thống tuyển sinh cho một trường đại học; các công nghệ là Java và Oracle.Làm cách nào để kiểm tra bất kỳ số bị thiếu nào từ một chuỗi số?

Trong một trong các bảng, số sê-ri được tạo trước được lưu trữ. Sau đó, đối với các số sê-ri đó, dữ liệu biểu mẫu của người nộp đơn sẽ được nhập. Yêu cầu của tôi là khi quá trình nhập hoàn tất, tôi sẽ phải tạo ra một báo cáo rất khôn ngoan. Nếu trong quá trình cho ăn các số sê-ri được tạo trước, bất kỳ số thứ tự nào bị thiếu. Ví dụ, nói trong một bảng, các số thứ tự là 7001, 7002, 7004, 7005, 7006, 7010. Từ chuỗi trên, rõ ràng là từ 7001 đến 7010 số mất tích là 7003, 7007, 7008 và 7009

Có chức năng DBMS nào trong Oracle để tìm ra các số này hoặc nếu bất kỳ thủ tục được lưu trữ nào có thể thực hiện mục đích của tôi thì vui lòng đề xuất một thuật toán.

Tôi có thể tìm thấy một số kỹ thuật trong Java nhưng đối với tốc độ, tôi muốn tìm giải pháp trong Oracle.

+0

tôi thêm thẻ lỗ hổng-and-đảo. tìm kiếm nó có thể sẽ mang lại một lượng đầy đủ các nghệ thuật trước, bao gồm các truy vấn đệ quy. – wildplasser

+0

Xem [Tìm phạm vi giá trị bị thiếu trong một chuỗi số hoặc ngày] (http://lalitkumarb.wordpress.com/2015/07/22/find-range-of-missing-values-in-a-sequence-of -numbers-or-date /) –

Trả lời

33

Một giải pháp mà không thể xác định rõ 9:

select min_a - 1 + level 
    from (select min(a) min_a 
       , max(a) max_a 
       from test1 
     ) 
    connect by level <= max_a - min_a + 1 
    minus 
    select a 
    from test1 

Kết quả:

MIN_A-1+LEVEL 
------------- 
     7003 
     7007 
     7008 
     7009 

4 rows selected. 
+5

Điều đó làm cho câu trả lời của tôi trông phức tạp quá phức tạp! +1 – Ben

+0

Tôi đã tự mình khám phá logic một thời gian và quyết định không cần thiết lãng phí thời gian như thế này. Tôi đoán tôi nên làm cho Google trở thành một thực hành tốt. Do đó +1 cho câu trả lời này. –

0

Một cách đơn giản để có được câu trả lời của bạn cho kịch bản của mình là thế này:

create table test1 (a number(9,0)); 

insert into test1 values (7001); 
insert into test1 values (7002); 
insert into test1 values (7004); 
insert into test1 values (7005); 
insert into test1 values (7006); 
insert into test1 values (7010); 
commit; 

select n.n from (select ROWNUM + 7001 as n from dual connect by level <= 9) n 
    left join test1 t on n.n = t.a where t.a is null; 

Các chọn sẽ cung cấp cho bạn câu trả lời từ ví dụ của bạn. Điều này chỉ có ý nghĩa, nếu bạn biết trước, trong đó phạm vi số của bạn và phạm vi không nên quá lớn. Số đầu tiên phải là giá trị bù trừ trong phần ROWNUM và độ dài của chuỗi là giới hạn cho mức trong phần connect by.

+0

Bạn cần phải biết rằng giá trị là 9. Bạn biết điều này như thế nào? – Ben

+0

Đó là những gì tôi đã viết: Bạn cần biết phạm vi chuỗi của bạn. Nếu tôi hiểu nhiệm vụ một cách chính xác, điều này có thể được biết đến. Hay tôi đã hiểu lầm bạn? – Stefan

1

Tôi đã đề xuất connect by levelStefan has done, tuy nhiên, bạn không thể sử dụng truy vấn phụ trong tuyên bố này, điều đó có nghĩa là nó không thực sự phù hợp với bạn khi bạn cần biết giá trị tối đa và tối thiểu của trình tự của bạn là.

Tôi sẽ đề xuất một pipe-lined table function có thể là cách tốt nhất để tạo các số bạn cần thực hiện. Để cho tiện làm việc bạn cần một đối tượng trong cơ sở dữ liệu của bạn để trả lại giá trị cho:

create or replace type t_num_array as table of number; 

Sau đó các chức năng:

create or replace function generate_serial_nos return t_num_array pipelined is 

    l_first number; 
    l_last number; 

begin 

    select min(serial_no), max_serial_no) 
    into l_first, l_last 
    from my_table 
      ; 

    for i in l_first .. l_last loop 
     pipe row(i); 
    end loop; 

    return; 

end generate_serial_nos; 
/

Sử dụng chức năng này sau đây sẽ trả về một danh sách các sê-ri số, giữa mức tối thiểu và tối đa.

select * from table(generate_serial_nos); 

Có nghĩa là truy vấn của bạn để tìm ra các số sê-ri đang thiếu trở thành:

select serial_no 
    from (select * 
      from table(generate_serial_nos) 
       ) generator 
    left outer join my_table actual 
    on generator.column_value = actual.serial_no 
where actual.serial_no is null 
0

Điều này làm việc nhưng chọn chuỗi đầu tiên (giá trị bắt đầu) vì nó không có tiền thân.Thử nghiệm trong SQL Server nhưng nên làm việc trong Oracle

SELECT 
    s.sequence FROM seqs s 
WHERE 
    s.sequence - (SELECT sequence FROM seqs WHERE sequence = s.sequence-1) IS NULL 

Đây là một kết quả xét nghiệm

Table 
    ------------- 
    7000 
    7001 
    7004 
    7005 
    7007 
    7008 

    Result 
    ---------- 
    7000 
    7004 
    7007 

Để có được chuỗi unassigned, chỉ cần làm value[i] - 1 nơi tôi lớn hàng đầu tiên ví dụ (7004 - 1 = 7003 and 7007 - 1 = 7006) đó là chuỗi sẵn

Tôi nghĩ bạn có thể cải thiện truy vấn đơn giản này

+0

Điều này trước khi giả sử một bảng với tất cả các số thứ tự được lưu trữ. Không cần phải làm điều này trong Oracle. – Ben

+0

Oracle sẽ 'kết nối bởi' thực hiện tốt hơn điều này? – codingbiz

+0

câu trả lời với số phiếu bầu cao nhất ở đây sử dụng 2 chức năng tổng hợp - về hiệu suất? – codingbiz

-1

này hoạt động trên postgres> = 8.4. Với một số sửa đổi nhỏ cho cú pháp CTE, nó có thể được thực hiện để làm việc cho oracle và microsoft, quá.

-- EXPLAIN ANALYZE 
WITH missing AS (
    WITH RECURSIVE fullhouse AS (
     SELECT MIN(num)+1 as num 
     FROM numbers n0 
     UNION ALL SELECT 1+ fh0.num AS num 
     FROM fullhouse fh0 
     WHERE EXISTS (
       SELECT * FROM numbers ex 
       WHERE ex.num > fh0.num 
       ) 
     ) 
     SELECT * FROM fullhouse fh1 
     EXCEPT (SELECT num FROM numbers nx) 
     ) 
SELECT * FROM missing; 
+0

Để trình gỡ xuống: hãy giải thích. Câu hỏi này được gắn thẻ 'sql' là sql chuẩn (phải là). CTE là một phần của điều đó. – wildplasser

+2

Đó không phải là tôi đã bỏ phiếu bình chọn nhưng công bằng với họ nó cũng được gắn thẻ Oracle và cú pháp này là không chính xác. – Ben

+0

Tôi đã được thông báo rằng CTE được thực hiện trong oracle xem: http: //stackoverflow.com/questions/6064970/oracle-cte-merge. Tất nhiên cấu trúc kết nối bởi/trước đã tồn tại trong một vài năm, nhưng cú pháp CTE ít nhất là một phần của tiêu chuẩn, và luôn có một lý do cho sự đa dạng, ngay cả khi nó là tiêu chuẩn. Như tôi đã nói trong câu trả lời của tôi: các khác biệt nhỏ về cú pháp (chẳng hạn như bỏ qua từ khóa RECURSIVE) có thể tồn tại. Và cuối cùng: ít nhất là các truy vấn làm việc cho tôi (mebe với một vài thay đổi cho những người khác, quá). Đã có những câu trả lời khác được đăng ở đây mà * dont * làm việc. – wildplasser

11

Hãy thử điều này:

SELECT t1.SequenceNumber + 1 AS "From", 
     MIN(t2.SequenceNumber) - 1 AS "To" 
FROM MyTable t1 
JOIN MyTable t2 ON t1.SequenceNumber < t2.SequenceNumber 
GROUP BY t1.SequenceNumber 
HAVING t1.SequenceNumber + 1 < MIN(t2.SequenceNumber) 

Đây là kết quả cho chuỗi 7001, 7002, 7004, 7005, 7006, 7010:

From To 
7003 7003 
7007 7009 
-2
select A.ID + 1 As ID 
From [Missing] As A 
Where A.ID + 1 Not IN (Select ID from [Missing]) 
And A.ID < n 

Data: ID 
1 
2 
5 
7 
Result: ID 
3 
4 
6 
+0

Thiếu số thứ tự. –

+0

Vui lòng chỉnh sửa bài đăng của bạn để cung cấp ngữ cảnh cho câu trả lời của bạn. Câu trả lời chỉ có mã chỉ hữu ích một phần: http://stackoverflow.com/help/how-to-answer –

0

Dưới đây là một giải pháp mà :

  • Dựa vào chức năng LAG Oracle
  • Không đòi hỏi kiến ​​thức về trình tự đầy đủ (nhưng do đó không phát hiện nếu số đầu tiên hoặc cuối cùng trong chuỗi đã bị bỏ lỡ)
  • Danh sách các giá trị xung quanh danh sách mất tích của số
  • liệt kê những danh sách mất tích các số như nhóm tiếp giáp (có lẽ thuận tiện cho việc báo cáo)
  • thật là bi kịch thất bại cho các danh sách rất lớn của số mất tích, do hạn chế về listagg

SQL:

WITH MentionedValues /*this would just be your actual table, only defined here to provide data for this example */ 
     AS (SELECT * 
       FROM ( SELECT LEVEL + 7000 seqnum 
          FROM DUAL 
        CONNECT BY LEVEL <= 10000) 
      WHERE seqnum NOT IN (7003,7007,7008,7009)--omit those four per example 
             ), 
    Ranges /*identifies all ranges between adjacent rows*/ 
     AS (SELECT seqnum AS seqnum_curr, 
        LAG (seqnum, 1) OVER (ORDER BY seqnum) AS seqnum_prev, 
        seqnum - (LAG (seqnum, 1) OVER (ORDER BY seqnum)) AS diff 
       FROM MentionedValues) 
SELECT Ranges.*, 
     ( SELECT LISTAGG (Ranges.seqnum_prev + LEVEL, ',') WITHIN GROUP (ORDER BY 1) 
       FROM DUAL 
     CONNECT BY LEVEL < Ranges.diff) "MissingValues" /*count from lower seqnum+1 up to lower_seqnum+(diff-1)*/ 
    FROM Ranges 
WHERE diff != 1 /*ignore when diff=1 because that means the numers are sequential without skipping any*/ 
; 

Output:

SEQNUM_CURR SEQNUM_PREV DIFF MissingValues 
7004  7002  2 "7003" 
7010  7006  4 "7007,7008,7009"     
Các vấn đề liên quan