Avoiding dynamic SQL when sorting dynamic

For et par dager siden utførte jeg et kode review på en SQL pakke. Denne koden kom definitivt ikke igjennom nåløyet. Koden bygde opp diverse SQL strenger dynamisk, og gjorde blant annet ikke bruk av bind variabler. Dvs det var bind noen steder, og andre steder ikke. En av årsakene til at dynamisk SQL ble benyttet var behovet for å sortere på ulike kolonner på bakgrunn av input-parametere. I etterkant av dette har jeg prøvd å google for å se om det finnes løsninger på å løse denne type sorteringer uten bruk av dynamisk SQL. Det eneste fornuftige jeg fant var diverse linker som pekte mot det faktum at man kan bruke subqueries i order by klausulen. Derfor bestemte jeg meg for å prøve ut en løsning på egen hånd.

Jeg har laget en liten test case som jeg ønsker å utforske litt videre. Hvis noen av dere har kommentarer, eller har erfaring med noe lignende, så kommenter gjerne på denne artikkelen.

Test:

-- create test table, and insert some data
drop table t;
create table t as select object_id id, object_name  my_text from all_objects where rownum<=10;
insert into t values (200,'TEST');
commit;

var setting varchar2(30)
exec :setting:='id'

select * from t
order by
(case :setting when 'id' then to_char(lpad(t.id,17,0))
when 'my_text'  then T.TEKST
else null end) ;

ID MY_TEXT
---------- ------------------------------
100 ORA$BASE
116 DUAL
117 DUAL
200 TEST
280 MAP_OBJECT
365 SYSTEM_PRIVILEGE_MAP
367 SYSTEM_PRIVILEGE_MAP
368 TABLE_PRIVILEGE_MAP
370 TABLE_PRIVILEGE_MAP
371 STMT_AUDIT_OPTION_MAP
373 STMT_AUDIT_OPTION_MAP

11 rows selected.

exec :setting:='my_text'

select * from t
order by
(case :setting when 'id' then to_char(lpad(t.id,17,0))
when 'my_text'  then T.TEKST
else null end) ;

ID MY_TEXT
---------- ------------------------------
116 DUAL
117 DUAL
280 MAP_OBJECT
100 ORA$BASE
371 STMT_AUDIT_OPTION_MAP
373 STMT_AUDIT_OPTION_MAP
367 SYSTEM_PRIVILEGE_MAP
365 SYSTEM_PRIVILEGE_MAP
368 TABLE_PRIVILEGE_MAP
370 TABLE_PRIVILEGE_MAP
200 TEST

11 rows selected.

exec :setting:=''

select * from t
order by
(case :setting when 'id' then to_char(lpad(t.id,17,0))
when 'my_text'  then T.TEKST
else null end) ;

ID MY_TEXT
---------- ------------------------------
100 ORA$BASE
116 DUAL
117 DUAL
280 MAP_OBJECT
365 SYSTEM_PRIVILEGE_MAP
200 TEST
368 TABLE_PRIVILEGE_MAP
370 TABLE_PRIVILEGE_MAP
371 STMT_AUDIT_OPTION_MAP
373 STMT_AUDIT_OPTION_MAP
367 SYSTEM_PRIVILEGE_MAP

11 rows selected.

Er det grunner til at du ikke bør løse sorterings problemet på denne måten? Eller finnes det andre bedre måter å løse dette problemet på?  Gi gjerne en kommentar!

Den orginale koden utførte noe lignende:


set serveroutput on
declare
  my_sql varchar2(2000);
  type rcur_type is ref cursor;
  rcur rcur_type;
  myrec t%rowtype;
  setting varchar2(10);
begin
  setting:='id';
  my_sql:='select * from t';

  if (setting='id') then
    my_sql:=my_sql ||' order by id';
  else
    if (setting='my_text') then
      my_sql:=my_sql ||' order by my_text';
    end if;
  end if;
  dbms_output.put_line('SQL> '|| my_sql);

  open rcur for my_sql;
  loop
    fetch rcur into myrec;
    exit when rcur%notfound;
    dbms_output.put_line(lpad(myrec.id,10,' ') || ' : '|| myrec.my_text);
  end loop;
  close rcur;
end;
/

Når spørringer blir komplekse liker jeg definitivt ikke å bygge opp spørringene på denne måten.
Dynamisk SQL øker sjansen for bugs i koden, og jeg ønsker derfor å finne alternative måter å gjøre dette på.

Post a Comment

Your email is never published nor shared. Required fields are marked *