2009-12-31 47 views
5

Tôi cần tạo số duy nhất và liên tiếp (để sử dụng trên hóa đơn) một cách nhanh chóng và đáng tin cậy. hiện đang sử dụng một chuỗi Oracle, nhưng trong một số trường hợp các số được tạo ra là không liên tiếp vì các ngoại lệ có thể xảy ra.Cách tốt nhất để tạo số duy nhất và điều tra viên trong Oracle

Tôi nghĩ rằng một vài giải pháp để quản lý vấn đề này, nhưng không ai trong số họ thuyết phục tôi. Bạn đề nghị giải pháp nào?

  1. Sử dụng tối đa chọn()

    SELECT MAX (NVL (doc_num, 0)) +1 FROM invoices 
    
  2. Sử dụng một bảng để lưu trữ các số cuối cùng được tạo ra cho các hóa đơn.

    UPDATE docs_numbers 
        SET last_invoice = last_invoice + 1 
    
  3. Giải pháp khác?

+0

Bạn đã thử tùy chọn 'NOCACHE' ngón chân trên 'CREATE SEQUENCE' chưa? Điều đó sẽ loại bỏ những khoảng trống. – skaffman

+6

@skaffman: Không, 'NOCACHE' chỉ định số lượng giá trị chuỗi sẽ được lưu trữ trong bộ nhớ để truy cập nhanh hơn: http://www.techonthenet.com/oracle/sequences.php –

Trả lời

3

Khoảng trống xuất hiện nếu giao dịch sử dụng số thứ tự nhưng sau đó được cuộn lại.

Có thể câu trả lời không phải là chỉ định số hóa đơn cho đến khi không thể cuộn lại hóa đơn. Điều này giảm thiểu (nhưng có lẽ không loại bỏ) khả năng của những khoảng trống.

Tôi không chắc chắn rằng có bất kỳ cách nhanh chóng hoặc dễ dàng nào để đảm bảo không có khoảng trống trong chuỗi - quét MAX, thêm một và chèn vào đó có lẽ là gần nhất để bảo mật, nhưng không được khuyến nghị vì lý do hiệu suất (và khó khăn với đồng thời) và kỹ thuật sẽ không phát hiện nếu số hóa đơn mới nhất được gán, sau đó bị xóa và được chỉ định lại.

Bạn có thể giải thích các khoảng trống bằng cách nào đó - bằng cách xác định số hóa đơn nào đã 'sử dụng' nhưng 'không được thực hiện vĩnh viễn' bằng cách nào đó? Có thể một giao dịch tự quản giúp đỡ trong việc đó không?


Một khả năng khác - giả định khoảng trống tương đối ít và xa.

Tạo bảng ghi lại số thứ tự phải được sử dụng lại trước khi một giá trị chuỗi mới được lấy. Thông thường, nó sẽ trống rỗng, nhưng một số quá trình chạy mỗi ... phút, giờ, ngày ... kiểm tra các khoảng trống và chèn các giá trị bị nhỡ vào bảng này. Tất cả các quy trình đầu tiên kiểm tra bảng các giá trị bị mất và nếu có bất kỳ giá trị nào, hãy sử dụng giá trị từ đó, thực hiện quá trình cập nhật chậm và xóa hàng mà chúng sử dụng. Nếu bảng trống, sau đó lấy số thứ tự tiếp theo.

Không dễ chịu lắm, nhưng việc tách 'số hóa đơn' từ 'quét các giá trị bị bỏ lỡ' có nghĩa là ngay cả khi quá trình lập hóa đơn không thành công cho một số chuỗi khi sử dụng một trong các giá trị bị thiếu, giá trị đó sẽ được tái khám phá bị thiếu và tái phát hành lần sau - lặp lại cho đến khi một số quy trình thành công với nó.

1

Giữ dòng thứ tự không - bạn có thể sử dụng sau đây để thiết lập lại giá trị đến mức tối đa những gì hiện đang được lưu trữ trong bảng (s):

-- -------------------------------- 
-- Purpose..: Resets the sequences 
-- -------------------------------- 

DECLARE 
    -- record of temp data table 
    TYPE data_rec_type IS RECORD(
    sequence_name VARCHAR2(30), 
    table_name VARCHAR2(30), 
    column_name VARCHAR2(30)); 

    -- temp data table 
    TYPE data_table_type IS TABLE OF data_rec_type INDEX BY BINARY_INTEGER; 

    v_data_table data_table_type; 
    v_index  NUMBER; 
    v_tmp_id  NUMBER; 

    -- add row to temp table for later processing 
    -- 
    PROCEDURE map_seq_to_col(in_sequence_name VARCHAR2, 
          in_table_name VARCHAR2, 
          in_column_name VARCHAR2) IS 
    v_i_index NUMBER; 
    BEGIN 
    v_i_index := v_data_table.COUNT + 1; 
    v_data_table(v_i_index).sequence_name := in_sequence_name; 
    v_data_table(v_i_index).table_name := in_table_name; 
    v_data_table(v_i_index).column_name := in_column_name; 
    END; 

    /************************************************************************** 
     Resets a sequence to a given value 
    ***************************************************************************/ 
    PROCEDURE reset_seq(in_seq_name VARCHAR2, in_new_value NUMBER) IS 

    v_sql  VARCHAR2(2000); 
    v_seq_name VARCHAR2(30) := in_seq_name; 
    v_reset_val NUMBER(10); 
    v_old_val NUMBER(10); 
    v_new_value NUMBER(10); 

    BEGIN 

    -- get current sequence value 

    v_sql := 'SELECT ' || v_seq_name || '.nextval FROM DUAL'; 
    EXECUTE IMMEDIATE v_sql 
     INTO v_old_val; 

    -- handle empty value 
    v_new_value := in_new_value; 
    if v_new_value IS NULL then 
     v_new_value := 0; 
    END IF; 

    IF v_old_val <> v_new_value then  
     IF v_old_val > v_new_value then 
     -- roll backwards 
     v_reset_val := (v_old_val - v_new_value) * -1; 
     elsif v_old_val < v_new_value then 
     v_reset_val := (v_new_value - v_old_val); 
     end if; 

     -- make the sequence rollback to 0 on the next call 
     v_sql := 'alter sequence ' || v_seq_name || ' increment by ' || 
      v_reset_val || ' minvalue 0'; 
     EXECUTE IMMEDIATE (v_sql); 

     -- select from the sequence to make it roll back 
     v_sql := 'SELECT ' || v_seq_name || '.nextval FROM DUAL'; 
     EXECUTE IMMEDIATE v_sql 
     INTO v_reset_val; 

     -- make it increment correctly again 
     v_sql := 'alter sequence ' || v_seq_name || ' increment by 1'; 
     EXECUTE IMMEDIATE (v_sql); 

     -- select from it again to prove it reset correctly. 
     v_sql := 'SELECT ' || v_seq_name || '.currval FROM DUAL'; 
     EXECUTE IMMEDIATE v_sql 
     INTO v_reset_val; 

    END IF; 

    DBMS_OUTPUT.PUT_LINE(v_seq_name || ': ' || v_old_val || ' to ' || 
        v_new_value); 
    END; 

    /********************************************************************************************* 
    Retrieves a max value for a table and then calls RESET_SEQ. 
    *********************************************************************************************/ 
    PROCEDURE reset_seq_to_table(in_sequence_name VARCHAR2, 
           in_table_name VARCHAR2, 
           in_column_name VARCHAR2) IS 

    v_sql_body VARCHAR2(2000); 
    v_max_value NUMBER; 

     BEGIN 

    -- get max value in the table 
    v_sql_body := 'SELECT MAX(' || in_column_name || '+0) FROM ' || 
       in_table_name; 
    EXECUTE IMMEDIATE (v_sql_body) 
     INTO v_max_value; 

    if v_max_value is null then 
     -- handle empty tables 
     v_max_value := 0; 
    end if; 

    -- use max value to reset the sequence 
    RESET_SEQ(in_sequence_name, v_max_value); 

    EXCEPTION 
    WHEN OTHERS THEN 
     DBMS_OUTPUT.PUT_LINE('Failed to reset ' || in_sequence_name || 
         ' from ' || in_table_name || '.' || 
         in_column_name || ' - ' || sqlerrm); 
    END; 

BEGIN 
    --DBMS_OUTPUT.ENABLE(1000000); 

    -- load sequence/table/column associations 

    /***** START SCHEMA CUSTOMIZATION *****/ 
    map_seq_to_col('Your_SEQ', 
       'your_table', 
       'the_invoice_number_column'); 

    /***** END SCHEMA CUSTOMIZATION *****/ 

    -- iterate all sequences that require a reset 
    FOR v_index IN v_data_table.FIRST .. v_data_table.LAST LOOP 

    BEGIN 
     RESET_SEQ_TO_TABLE(v_data_table(v_index).sequence_name, 
         v_data_table(v_index).table_name, 
         v_data_table(v_index).column_name); 
    END; 
    END LOOP; 

END; 
/

-- ------------------------------------------------------------------------------------- 
-- End of Script. 
-- ------------------------------------------------------------------------------------- 

Ví dụ là một sproc nặc danh - thay đổi nó thành các thủ tục thích hợp trong một gói, và gọi nó trước khi chèn một hóa đơn mới để giữ cho việc đánh số nhất quán.

7

Như ông đề nghị, bạn thực sự nên xem xét sự cần thiết cho "không có khoảng trống" yêu cầu

+0

Kế toán Cdn (và có thể là Hoa Kỳ) yêu cầu" không khoảng trống "trong số hóa đơn như một phương tiện phát hiện gian lận. –

+2

Tôi đã nghe điều này là tốt, nhưng như đã chỉ ra trong liên kết không ai dường như có thể trỏ đến bất cứ điều gì những khoảng trống cấm - Tôi tin rằng những khoảng trống chỉ có thể giải thích được. – dpbradley

+0

khoảng trống là một sự trở lại đối với các tài liệu in sẵn như hóa đơn. Đó là một cơ chế kiểm soát thực sự không còn cần thiết cho hầu hết các ứng dụng máy tính. Một ví dụ về nơi nó vẫn sẽ được áp dụng là kiểm tra. – David

1

Nếu bạn muốn số KHÔNG được tăng lên nếu giao dịch của bạn cuối cùng quay trở lại thì SEQUENCE sẽ không hoạt động cho bạn, bởi vì theo như tôi biết, một khi NEXTVAL là yêu cầu từ trình tự chuỗi positio n được tăng lên và rollback sẽ không đảo ngược nó.

Nếu điều này thực sự là một yêu cầu thì bạn có thể phải lưu trữ bộ đếm hiện tại trong một bảng riêng biệt, nhưng hãy cẩn thận cập nhật đồng thời - từ cả hai 'mất cập nhật' và khả năng mở rộng.

0

Bạn có thể phải suy nghĩ lại quá trình của mình một cách nhẹ nhàng và chia nhỏ nó thành nhiều bước hơn. Có một bước không giao dịch tạo hóa đơn giữ chỗ (điều này không có trong giao dịch nên loại bỏ khoảng trống) và sau đó trong giao dịch thực hiện phần còn lại của doanh nghiệp của bạn. Tôi nghĩ đó là cách chúng tôi đã thực hiện nó trong một hệ thống mà tôi đã mắc kẹt nhiều năm trước nhưng tôi không thể nhớ - tôi chỉ nhớ nó là "kỳ lạ".

Tôi muốn nói chuỗi sẽ đảm bảo số duy nhất/liên tiếp nhưng khi bạn ném giao dịch trong danh sách kết hợp không thể đảm bảo trừ khi tạo chuỗi không nằm trong giao dịch đó.

+2

Trình tự Oracle chỉ nên được sử dụng để đảm bảo tính duy nhất không phải là số liên tiếp. –

0

liên kết của dpbradley trong âm thanh số 2 giống như đặt cược tốt nhất của bạn. Tom giữ transactionality với người gọi, nếu bạn không muốn điều đó bạn có thể làm cho nó trở thành một giao dịch tự như vậy:

create or replace 
function getNextInvoiceNumber() 
return number is 
    l_invoicenum  number; 

    pragma autonomous_transaction; 
    begin 
     update docs_numbers 
     set last_invoice = last_invoice + 1 
     returning last_invoice 
     into l_invoicenum; 
     commit; 

     return l_invoicenum; 

    exception 
     when others then 
     rollback; 
     raise; 
end; 
1

Tôi nghĩ rằng bạn sẽ thấy rằng việc sử dụng MAX() của những con số hiện tại là dễ bị một vấn đề mới và thú vị - các bản sao có thể xảy ra nếu nhiều hóa đơn được tạo cùng một lúc. (Đừng hỏi tôi làm sao tôi biết ...).

Một giải pháp khả thi là lấy được khóa chính trên bảng INVOICE của bạn từ một chuỗi, nhưng có NOT này không phải là số hóa đơn. Sau khi tạo hóa đơn chính xác và đúng cách và sau thời điểm ngoại lệ hoặc ý thích của người dùng có thể khiến hóa đơn bị chấm dứt, bạn chuyển đến chuỗi thứ hai để nhận số thứ tự được hiển thị dưới dạng số "hóa đơn" . Điều này có nghĩa là bạn sẽ có hai số duy nhất, không lặp lại trên bảng INVOICE của bạn và số rõ ràng (INVOICE_NO) sẽ không phải là khóa chính (nhưng nó có thể và nên là UNIQUE) để có một chút tà ác, nhưng thay thế - đó là tạo hàng INVOICE với một giá trị trong khóa chính, sau đó thay đổi khóa chính sau khi INVOICE được tạo - chỉ quá ác đối với các từ. :-)

Chia sẻ và thưởng thức.

0

Điều chúng tôi làm là phát hành số thứ tự cho giao dịch và sau đó khi mục chúng tôi đang xử lý được hoàn tất, chúng tôi sẽ phát hành số vĩnh viễn (cũng là một chuỗi). Hoạt động tốt cho chúng tôi.

Trân
K

+0

Vấn đề tương tự, bản cập nhật thứ hai có thể không thành công vì nhiều lý do khác nhau – steve

+0

Có thể, và chúng tôi kiểm tra điều đó. Quá trình gán số vĩnh viễn là nhỏ và xảy ra sau khi xử lý tất cả các dữ liệu khác để cơ hội của bất cứ điều gì không có mỏng. – Khb

1

Nếu bạn thực sự muốn có không có khoảng trống, bạn cần phải hoàn toàn serialize truy cập, nếu không sẽ luôn có khoảng trống. Những lý do cho những khoảng trống là:

  • rollback
  • shutdown abort
1

tôi đã đi qua vấn đề này trước đây. Trong một trường hợp, chúng tôi có thể thuyết phục doanh nghiệp chấp nhận hóa đơn "thực" có thể có khoảng trống và chúng tôi đã viết một công việc chạy hàng ngày để "lấp đầy" khoảng trống với hóa đơn "void" cho mục đích kiểm tra. Trong thực tế, nếu chúng ta đặt NOCACHE theo trình tự, số khoảng trống sẽ là tương đối thấp, do đó, các kiểm toán viên thường sẽ được hạnh phúc miễn là truy vấn của họ trên hóa đơn "void" không trả lại quá nhiều kết quả.

Các vấn đề liên quan