Oracle
 sql >> Teknologi Basis Data >  >> RDS >> Oracle

Apakah penggunaan SELECT COUNT(*) sebelum SELECT INTO lebih lambat daripada menggunakan Pengecualian?

Jika Anda menggunakan kueri yang tepat dari pertanyaan, maka varian pertama tentu saja lebih lambat karena harus menghitung semua catatan dalam tabel yang memenuhi kriteria.

Itu harus ditulis sebagai

SELECT COUNT(*) INTO row_count FROM foo WHERE bar = 123 and rownum = 1;

atau

select 1 into row_count from dual where exists (select 1 from foo where bar = 123);

karena memeriksa keberadaan catatan sudah cukup untuk tujuan Anda.

Tentu saja, kedua varian tidak menjamin bahwa orang lain tidak mengubah sesuatu di foo antara dua pernyataan, tetapi itu bukan masalah jika pemeriksaan ini adalah bagian dari skenario yang lebih kompleks. Pikirkan saja situasi ketika seseorang mengubah nilai foo.a setelah memilih nilainya menjadi var saat melakukan beberapa tindakan yang merujuk pada var . yang dipilih nilai. Jadi dalam skenario kompleks lebih baik untuk menangani masalah konkurensi seperti itu pada tingkat logika aplikasi.
Untuk melakukan operasi atom lebih baik menggunakan pernyataan SQL tunggal.

Setiap varian di atas memerlukan 2 sakelar konteks antara SQL dan PL/SQL dan 2 kueri sehingga kinerjanya lebih lambat daripada varian apa pun yang dijelaskan di bawah jika baris ditemukan dalam tabel.

Ada varian lain untuk memeriksa keberadaan baris tanpa kecuali:

select max(a), count(1) into var, row_count 
from foo 
where bar = 123 and rownum < 3;

Jika row_count =1 maka hanya satu baris yang memenuhi kriteria.

Terkadang cukup untuk memeriksa keberadaan hanya karena batasan unik pada foo yang menjamin bahwa tidak ada bar yang terduplikasi nilai dalam foo . Misalnya. bar adalah kunci utama.
Dalam kasus seperti itu, kueri dapat disederhanakan:

select max(a) into var from foo where bar = 123;
if(var is not null) then 
  ...
end if;

atau gunakan kursor untuk memproses nilai:

for cValueA in ( 
  select a from foo where bar = 123
) loop
  ...  
end loop;

Varian berikutnya adalah dari link , disediakan oleh @user272735 dalam jawabannya:

select 
  (select a from foo where bar = 123)
  into var 
from dual;

Dari pengalaman saya, varian apa pun tanpa pengecualian blok dalam banyak kasus lebih cepat daripada varian dengan pengecualian, tetapi jika jumlah eksekusi blok tersebut rendah maka lebih baik menggunakan blok pengecualian dengan penanganan no_data_found dan too_many_rows pengecualian untuk meningkatkan keterbacaan kode.

Poin yang tepat untuk memilih menggunakan pengecualian atau tidak menggunakannya, adalah mengajukan pertanyaan "Apakah situasi ini normal untuk aplikasi?". Jika baris tidak ditemukan dan itu adalah situasi yang diharapkan yang dapat ditangani (misalnya menambahkan baris baru atau mengambil data dari tempat lain dan seterusnya) lebih baik untuk menghindari pengecualian. Jika tidak terduga dan tidak ada cara untuk memperbaiki situasi, maka tangkap pengecualian untuk menyesuaikan pesan kesalahan, tulis ke log peristiwa dan lempar ulang, atau jangan tangkap sama sekali.

Untuk membandingkan kinerja, buat saja kasus uji sederhana pada sistem Anda dengan kedua varian dipanggil berkali-kali dan bandingkan.
Katakan lebih banyak, dalam 90 persen aplikasi, pertanyaan ini lebih teoretis daripada praktis karena ada banyak sumber kinerja lain masalah yang harus diperhitungkan terlebih dahulu.

Perbarui

Saya mereproduksi contoh dari halaman ini di situs SQLFiddle dengan sedikit koreksi (tautan ).
Hasil membuktikan varian tersebut dengan memilih dari dual berkinerja terbaik:sedikit overhead saat sebagian besar kueri berhasil dan penurunan kinerja terendah saat jumlah baris yang hilang meningkat.
Yang mengejutkan, varian dengan count() dan dua kueri menunjukkan hasil terbaik jika semua kueri gagal.

| FNAME | LOOP_COUNT | ALL_FAILED | ALL_SUCCEED | variant name |
----------------------------------------------------------------
|    f1 |       2000 |       2.09 |        0.28 |  exception   |
|    f2 |       2000 |       0.31 |        0.38 |  cursor      |
|    f3 |       2000 |       0.26 |        0.27 |  max()       |
|    f4 |       2000 |       0.23 |        0.28 |  dual        |
|    f5 |       2000 |       0.22 |        0.58 |  count()     |

-- FNAME        - tested function name 
-- LOOP_COUNT   - number of loops in one test run
-- ALL_FAILED   - time in seconds if all tested rows missed from table
-- ALL_SUCCEED  - time in seconds if all tested rows found in table
-- variant name - short name of tested variant

Di bawah ini adalah kode penyiapan untuk lingkungan pengujian dan skrip pengujian.

create table t_test(a, b)
as
select level,level from dual connect by level<=1e5
/
insert into t_test(a, b) select null, level from dual connect by level < 100
/

create unique index x_text on t_test(a)
/

create table timings(
  fname varchar2(10), 
  loop_count number, 
  exec_time number
)
/

create table params(pstart number, pend number)
/
-- loop bounds
insert into params(pstart, pend) values(1, 2000)
/

-- f1 - penanganan pengecualian

create or replace function f1(p in number) return number
as
  res number;
begin
  select b into res
  from t_test t
  where t.a=p and rownum = 1;
  return res;
exception when no_data_found then
  return null;
end;
/

-- f2 - lingkaran kursor

create or replace function f2(p in number) return number
as
  res number;
begin
  for rec in (select b from t_test t where t.a=p and rownum = 1) loop
    res:=rec.b;
  end loop;
  return res;
end;
/

-- f3 - maks()

create or replace function f3(p in number) return number
as
  res number;
begin
  select max(b) into res
  from t_test t
  where t.a=p and rownum = 1;
  return res;
end;
/

-- f4 - pilih sebagai bidang di pilih dari ganda

create or replace function f4(p in number) return number
as
  res number;
begin
  select
    (select b from t_test t where t.a=p and rownum = 1)
    into res
  from dual;
  return res;
end;
/

-- f5 - centang count() lalu dapatkan nilai

create or replace function f5(p in number) return number
as
  res number;
  cnt number;
begin
  select count(*) into cnt
  from t_test t where t.a=p and rownum = 1;

  if(cnt = 1) then
    select b into res from t_test t where t.a=p;
  end if;

  return res;
end;
/

Skrip pengujian:

declare
  v       integer;
  v_start integer;
  v_end   integer;

  vStartTime number;

begin
  select pstart, pend into v_start, v_end from params;

  vStartTime := dbms_utility.get_cpu_time;

  for i in v_start .. v_end loop
    v:=f1(i);
  end loop;

  insert into timings(fname, loop_count, exec_time) 
    values ('f1', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100) ;
end;
/

declare
  v       integer;
  v_start integer;
  v_end   integer;

  vStartTime number;

begin
  select pstart, pend into v_start, v_end from params;

  vStartTime := dbms_utility.get_cpu_time;

  for i in v_start .. v_end loop
    v:=f2(i);
  end loop;

  insert into timings(fname, loop_count, exec_time) 
    values ('f2', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100) ;
end;
/

declare
  v       integer;
  v_start integer;
  v_end   integer;

  vStartTime number;

begin
  select pstart, pend into v_start, v_end from params;

  vStartTime := dbms_utility.get_cpu_time;

  for i in v_start .. v_end loop
    v:=f3(i);
  end loop;

  insert into timings(fname, loop_count, exec_time) 
    values ('f3', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100) ;
end;
/

declare
  v       integer;
  v_start integer;
  v_end   integer;

  vStartTime number;

begin
  select pstart, pend into v_start, v_end from params;

  vStartTime := dbms_utility.get_cpu_time;

  for i in v_start .. v_end loop
    v:=f4(i);
  end loop;

  insert into timings(fname, loop_count, exec_time) 
    values ('f4', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100) ;
end;
/

declare
  v       integer;
  v_start integer;
  v_end   integer;

  vStartTime number;

begin
  select pstart, pend into v_start, v_end from params;
  --v_end := v_start + trunc((v_end-v_start)*2/3);

  vStartTime := dbms_utility.get_cpu_time;

  for i in v_start .. v_end loop
    v:=f5(i);
  end loop;

  insert into timings(fname, loop_count, exec_time) 
    values ('f5', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100) ;
end;
/

select * from timings order by fname
/


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Pernyataan 'LIKE' Dinamis dalam SQL (Oracle)

  2. Bagaimana cara membuat tabel yang tidak dimiliki oleh sys di Oracle?

  3. Bagaimana cara mendapatkan parameter output prosedur tersimpan yang merupakan array agar berfungsi?

  4. Oracle JDBC ReadyStatement Abaikan Trailing Spaces

  5. Panda ke Oracle melalui SQL Alchemy:UnicodeEncodeError:codec 'ascii' tidak dapat menyandikan karakter