sql >> Database teknologi >  >> RDS >> Oracle

Er brugen af ​​SELECT COUNT(*) før SELECT INTO langsommere end at bruge undtagelser?

Hvis du bruger nøjagtige forespørgsler fra spørgsmålet, så er 1. variant selvfølgelig langsommere, fordi den skal tælle alle poster i tabel, som opfylder kriterierne.

Det skal skrives som

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

eller

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

fordi det er nok til dit formål at kontrollere, om der er registreret eksistens.

Begge varianter garanterer naturligvis ikke, at en anden ikke ændrer noget i foo mellem to udsagn, men det er ikke et problem, hvis denne kontrol er en del af mere komplekse scenarier. Tænk bare på situationen, hvor nogen ændrede værdien af ​​foo.a efter at have valgt dens værdi i var mens du udfører nogle handlinger, der refererer til udvalgte var værdi. Så i komplekse scenarier er det bedre at håndtere sådanne samtidighedsproblemer på applikationslogikniveau.
For at udføre atomoperationer er det bedre at bruge en enkelt SQL-sætning.

Enhver af varianterne ovenfor kræver 2 kontekstskift mellem SQL og PL/SQL og 2 forespørgsler, så den udfører langsommere end enhver variant beskrevet nedenfor i tilfælde, hvor rækken findes i en tabel.

Der er andre varianter til at kontrollere eksistensen af ​​række uden undtagelse:

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

Hvis række_antal =1, opfylder kun én række kriterierne.

Nogle gange er det nok kun at tjekke for eksistensen på grund af en unik begrænsning på foo som garanterer, at der ikke er nogen duplikerede bar værdier i foo . For eksempel. bar er primær nøgle.
I sådanne tilfælde er det muligt at forenkle forespørgslen:

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

eller brug markøren til at behandle værdier:

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

Næste variant er fra link , leveret af @user272735 i hans svar:

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

Fra min erfaring blokerer enhver variant uden undtagelse i de fleste tilfælde hurtigere end en variant med undtagelser, men hvis antallet af udførelser af en sådan blok er lavt, er det bedre at bruge undtagelsesblok med håndtering af no_data_found og too_many_rows undtagelser for at forbedre kodelæsbarheden.

Det rigtige punkt at vælge at bruge undtagelse eller ikke bruge det, er at stille et spørgsmål "Er denne situation normal for anvendelse?". Hvis rækken ikke findes, og det er en forventet situation, som kan håndteres (f.eks. tilføje ny række eller tage data fra et andet sted og så videre), er det bedre at undgå undtagelser. Hvis det er uventet, og der ikke er nogen måde at løse en situation på, så fang undtagelse for at tilpasse fejlmeddelelsen, skriv den til hændelsesloggen og smid den igen, eller fang den slet ikke.

For at sammenligne ydeevne skal du bare lave en simpel testcase på dit system, hvor begge varianter kaldes mange gange og sammenligne.
Sig mere, i 90 procent af applikationerne er dette spørgsmål mere teoretisk end praktisk, fordi der er mange andre kilder til ydeevne spørgsmål, som skal tages i betragtning først.

Opdater

Jeg har gengivet eksempel fra denne side på SQLFiddle-webstedet med lidt rettelser (link ).
Resultater beviser den variant med at vælge fra dual yder bedst:lidt overhead, når de fleste forespørgsler lykkes og laveste ydeevneforringelse, når antallet af manglende rækker øges.
Overraskende nok viste variant med count() og to forespørgsler det bedste resultat i tilfælde af, at alle forespørgsler mislykkedes.

| 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

Nedenfor er en opsætningskode for testmiljø og testscript.

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 - håndtering af undtagelser

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 - markørsløjfe

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 - max()

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 - vælg som felt i vælg fra dobbelt

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 - tjek count() og få derefter værdi

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;
/
 

Testscript:

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. Hvordan tæller man antallet af forekomster af et bestemt ord i en MySQL-blob-tekst?

  2. SQL-forespørgsel til at løse transitive afhængigheder i databasen

  3. Upload flere billeder med PHP, der kun gemmer én filsti til MySQL-databasen

  4. find pladsen optaget af poster i mysql