2011-11-10 27 views
15

Tại nơi làm việc, tôi có một bảng lớn (khoảng 3 triệu hàng, như 40-50 cột). Đôi khi tôi cần phải làm trống một số cột và điền chúng với dữ liệu mới. Những gì tôi không mong đợi làcập nhật x set y = null mất một thời gian dài

UPDATE table1 SET y = null 

mất thời gian nhiều hơn điền vào cột với dữ liệu được tạo ra, ví dụ, trong truy vấn sql từ cột khác của cùng một bảng hoặc truy vấn từ các bảng khác trong một subquery . Nó không quan trọng nếu tôi đi qua tất cả các hàng bảng cùng một lúc (như trong truy vấn cập nhật ở trên) hoặc nếu tôi sử dụng một con trỏ để đi qua hàng của bảng theo hàng (sử dụng pk). Nó không quan trọng nếu tôi sử dụng bảng lớn tại nơi làm việc hoặc nếu tôi tạo một bảng thử nghiệm nhỏ và điền nó với hàng trăm hàng nghìn hàng thử nghiệm. Việc đặt cột thành null luôn luôn mất nhiều thời gian hơn (Trong suốt quá trình kiểm tra, tôi đã gặp phải các yếu tố từ 2 đến 10) so với việc cập nhật cột với một số dữ liệu động (khác với mỗi hàng).

Lý do cho việc này là gì? Oracle làm gì khi thiết lập một cột thành null? Hoặc - lỗi của tôi trong lý luận là gì?

Cảm ơn sự giúp đỡ của bạn!

P.S .: Tôi đang sử dụng oracle 11g2 và đã tìm thấy các kết quả này bằng cách sử dụng cả nhà phát triển plsql và nhà phát triển sql oracle.

+0

Bạn có thể đăng kế hoạch thực hiện/giải thích của bạn? – diagonalbatman

+0

Có mệnh đề 'where' không? – Johan

+0

Nếu tôi đi qua toàn bộ bảng cùng một lúc, không có mệnh đề where. Nếu tôi đi qua hàng của bảng theo hàng, thì có một mệnh đề where tham chiếu đến khóa chính của bảng. Kết quả vẫn giữ nguyên trong cả hai phiên bản. Đối với kế hoạch thực hiện, tôi sẽ chuẩn bị một và một ví dụ từng bước để tái tạo kết quả sau ngày hôm nay. –

Trả lời

4

Tóm tắt

Tôi nghĩ rằng việc cập nhật để null là chậm hơn bởi vì Oracle (không chính xác) cố gắng tận dụng các cách thức mà nó lưu trữ null, khiến nó thường xuyên tổ chức lại các hàng trong khối ("khối đống nén "), tạo thêm nhiều UNDO và REDO.

Điều gì đặc biệt về null?

Từ Oracle Database Concepts:

"Nulls được lưu trữ trong cơ sở dữ liệu nếu họ rơi giữa cột với các giá trị dữ liệu Trong những trường hợp họ yêu cầu 1 byte để lưu trữ theo chiều dài của cột (zero)

..

Việc lưu giữ các giá trị null trong một hàng không yêu cầu lưu trữ vì một tiêu đề hàng mới báo hiệu rằng các cột còn lại trong hàng trước là null, ví dụ, nếu ba cột cuối cùng của bảng là rỗng, thì không có thông tin nào được lưu trữ cho các cột đó. với nhiều cột, các cột có nhiều khả năng chứa null hơn nên được xác định cuối cùng để tiết kiệm dung lượng đĩa. "

thử nghiệm

cập nhật Điểm chuẩn là rất khó khăn vì chi phí thực sự của một bản cập nhật không thể đo lường chỉ từ báo cáo cập nhật. Ví dụ: các công tắc nhật ký sẽ không xảy ra với mọi cập nhật và việc xóa khối bị trễ sẽ xảy ra sau đó. Để kiểm tra chính xác bản cập nhật, cần có nhiều lần chạy, các đối tượng sẽ được tạo lại cho mỗi lần chạy và các giá trị cao và thấp phải được loại bỏ.

Để đơn giản, tập lệnh bên dưới không đưa ra kết quả cao và thấp, đồng thời chỉ kiểm tra bảng có một cột. Nhưng vấn đề vẫn xảy ra bất kể số cột, dữ liệu của chúng và cột nào được cập nhật.

Tôi đã sử dụng tiện ích RunStats từ http://www.oracle-developer.net/utilities.php để so sánh mức tiêu thụ tài nguyên của việc cập nhật thành giá trị với cập nhật-thành-một-không.

create table test1(col1 number); 

BEGIN 
    dbms_output.enable(1000000); 

    runstats_pkg.rs_start; 

    for i in 1 .. 10 loop 
     execute immediate 'drop table test1 purge'; 
     execute immediate 'create table test1 (col1 number)'; 
     execute immediate 'insert /*+ append */ into test1 select 1 col1 
      from dual connect by level <= 100000'; 
     commit; 
     execute immediate 'update test1 set col1 = 1'; 
     commit; 
    end loop; 

    runstats_pkg.rs_pause; 
    runstats_pkg.rs_resume; 

    for i in 1 .. 10 loop 
     execute immediate 'drop table test1 purge'; 
     execute immediate 'create table test1 (col1 number)'; 
     execute immediate 'insert /*+ append */ into test1 select 1 col1 
      from dual connect by level <= 100000'; 
     commit; 
     execute immediate 'update test1 set col1 = null'; 
     commit; 
    end loop; 

    runstats_pkg.rs_stop(); 
END; 
/

quả

Có hàng tá các khác biệt, đây là những bốn tôi nghĩ là phù hợp nhất:

Type Name         Run1   Run2   Diff 
----- ---------------------------- ------------ ------------ ------------ 
TIMER elapsed time (hsecs)    1,269  4,738  3,469 
STAT heap block compress      1  2,028  2,027 
STAT undo change vector size  55,855,008 181,387,456 125,532,448 
STAT redo size      133,260,596 581,641,084 448,380,488 

Solutions?

Giải pháp duy nhất tôi có thể nghĩ là bật tính năng nén bảng. Thủ thuật lưu trữ dấu vết null không xảy ra đối với các bảng nén. Vì vậy, mặc dù số lượng "đống khối nén" thậm chí còn cao hơn cho Run2, từ 2028 đến 23208, tôi đoán nó không thực sự làm bất cứ điều gì. Thời gian làm lại, hoàn tác và thời gian trôi qua giữa hai lần chạy gần giống với tính năng nén bảng được bật.

Tuy nhiên, có rất nhiều nhược điểm tiềm ẩn đối với nén bảng. Việc cập nhật lên một null sẽ chạy nhanh hơn nhiều, nhưng mọi cập nhật khác sẽ chạy ít nhất một chút chậm hơn.

1

Đó là bởi vì nó xóa khỏi chặn dữ liệu đó.

delete là hoạt động khó khăn nhất. Nếu bạn có thể tránh delete, hãy thực hiện.

Tôi khuyên bạn nên tạo một bảng khác với cột đó null (Create table as select chẳng hạn hoặc insert select) và điền vào (cột) với quy trình của bạn. Thả bảng cũ và sau đó đổi tên bảng mới với tên hiện tại.

UPDATE:

Một điều quan trọng là bạn nên cập nhật các cột như là, với các giá trị mới. Nó là vô ích để thiết lập chúng null và sau đó nạp tiền cho họ. Nếu bạn không có giá trị cho tất cả các hàng, bạn có thể làm bản cập nhật như thế này:

udpate table1 
set y = (select new_value from source where source.key = table1.key) 

và sẽ thiết lập để null những hàng đó không tồn tại trong nguồn.

6

Cột Y có được lập chỉ mục không? Nó có thể là thiết lập cột để null có nghĩa là Oracle phải xóa khỏi chỉ mục, thay vì chỉ cập nhật nó. Nếu đúng như vậy, bạn có thể thả và tạo lại nó sau khi cập nhật dữ liệu.

CHỈNH SỬA:

Có phải chỉ là cột Y thể hiện vấn đề hoặc độc lập với cột đang được cập nhật không? Bạn có thể đăng định nghĩa bảng, bao gồm các ràng buộc không?

+0

+1 - Tôi cho rằng đây là vấn đề. –

+0

Xin lỗi, quên nói, không có chỉ mục trên cột đó. –

-3

điều gì cũng có thể giúp tăng tốc độ cập nhật là sử dụng alter table table1 nologging để cập nhật sẽ không tạo lại các bản ghi. khả năng khác là thả cột và thêm lại. vì đây là một hoạt động DDL nó sẽ tạo ra không làm lại cũng như không hoàn tác.

+0

thả cột sẽ làm điều tương tự. Xóa dữ liệu khỏi các khối. Xét về hiệu suất là điều tương tự - cùng thời gian. –

+0

đúng florin, tôi bỏ qua điều đó. tip 'nologging' tuy nhiên vẫn hợp lệ. cảm ơn vì đã sửa! –

+0

có, nologging sẽ tăng tốc truy vấn. –

-1

Tôi sẽ thử những gì Tom Kyte đề xuất về các bản cập nhật lớn. Khi nói đến các bảng lớn, tốt nhất là hãy thực hiện như sau: lấy một vài hàng, cập nhật chúng, lấy thêm một số, cập nhật chúng vv. Đừng cố phát hành bản cập nhật trên tất cả các bảng. Đó là một động thái giết người ngay từ đầu.

Về cơ bản tạo bảng được lập chỉ mục nhị phân, tìm nạp 10 hàng cùng một lúc và cập nhật chúng.

Đây là một đoạn mã mà tôi đã sử dụng các bảng lớn với thành công. Bởi vì im lười biếng và nó giống như 2AM bây giờ bệnh chỉ cần sao chép dán nó ở đây và cho phép bạn con số nó ra, nhưng cho tôi biết nếu bạn cần giúp đỡ:

DECLARE 

    TYPE BookingRecord IS RECORD ( 
     bprice number, 
     bevent_id number, 
     book_id number 
    ); 

    TYPE array is TABLE of BookingRecord index by binary_integer; 
    l_data array; 

CURSOR c1 is 
    SELECT LVC_USD_PRICE_V2(ev.activity_version_id,ev.course_start_date,t.local_update_date,ev.currency,nvl(t.delegate_country,ev.sponsor_org_country),ev.price,ev.currency,t.ota_status,ev.location_type) x, 
     ev.title, 
     t.ota_booking_id 
     FROM [email protected] t, 
      [email protected] ev 
     WHERE t.event_id = ev.event_id 
     and t.ota_booking_id = 
BEGIN 
    open c1; 
     loop 
      fetch c1 bulk collect into l_data limit 20; 

      for i in 1..l_data.count 
       loop 
        update ou_inc_int_t_01 
         set price = l_data(i).bprice, 
          updated = 'Y' 
        where booking_id = l_data(i).book_id; 
       end loop; 

      exit when c1%notfound; 
     end loop; 
     close c1; 
END; 
+1

Tôi mạnh mẽ không đồng ý rằng việc thay thế một câu lệnh SQL lớn bằng nhiều câu lệnh SQL nhỏ sẽ tốt hơn. (Mặc dù trong môi trường nhiều người dùng, đôi khi bạn có thể cần thực hiện điều này để đồng thời hoặc do các tài nguyên hạn chế như một vùng bảng UNDO nhỏ.) Nhiều câu lệnh SQL nhỏ đòi hỏi thời gian để chuyển đổi giữa SQL và PL/SQL. Và một UPDATE duy nhất có thể yêu cầu ít hơn nhiều so với nhiều UPDATE. (FOR FOR thay vì FOR sẽ giúp chuyển ngữ cảnh, nhưng dường như không làm giảm kích thước của UNDO). –

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