A probléma
Érdekes problémába futottam bele: use parancs feltételes használata. Valami ilyesmit képzeljetek el, hogy
if exists(select 1 from sys.databases where name='akarmi')
begin
use akarmi
parancsok tömkelege az adott adatbázisra
end
Csak éppen ez így nem működik.
A feladatom azt hiszem egyértelmű: ha egy adott adatbázis létezik, akkor hajtson rajta végre egy rakás parancsot (amiben lehet DML és DDL is), viszont ha nem létezik, akkor ugorja át a parancsokat és ne jelezzen hibát vagy ne csináljon butaságot az éppen aktuális adatbázisban.
Ha megadtok egy olyat, hogy use kakukkmadar
le sem kell futtatnunk, elég ha a parse-t megnyomjuk és máris kiadja, hogy ilyen adatbázis nem létezik (már persze ha nálad nincs kakukkmadar nevű adatbázis teljesen véletlenül).
A USE parancs helpjében szerepel is a kitétel, hogy „USE is executed at both compile and execution time and takes effect immediately.” Vagyis már compile-nál végrehajtja és ezért természetes, hogy az adatbázis hiányt egyből jelzi. De mit tehetünk, ha mégis szükségünk van ilyen funkcionalitásra? Hogyan léphetünk tovább?
Természetesen az nem játszik, hogy execute-tal hajtjuk meg, mint sima string parancs, mert az adatbázis context váltás csak azon a végrehajtáson belül érvényes. Egy use master; execute ('use kakukk;'); create table blabla ...
a master adatbázisban fogja létrehozni a blabla táblát.
Kell nekünk use?
Megpróbálhatjuk kihagyni a use-t az egész játszmából. Átírhatjuk a scripteket, hogy használjunk adatbázisos prefixeket, így az adott parancsot mindig a megfelelő adatbázison hajtjuk végre.
if exists(select 1 from sys.databases where name='akarmi')
begin
create table akarmi.schema.tablanev...
alter table akarmi.schema.tablanev...
update akarmi.schema.tablanev...
és így tovább...
end
Zseniális, megugrottuk a lécet! Ahogy azt az egyszeri SQL-es elképzeli… Egészen addig örülünk, amíg nem kezd el hibákat dobálni a parser:
Msg 166, Level 15, State 1, Line 25967
'DROP FUNCTION' does not allow specifying the database name as a prefix to the object name.
Msg 166, Level 15, State 1, Line 25987
'CREATE/ALTER FUNCTION' does not allow specifying the database name as a prefix to the object name.
Tehát a function manipuláló parancsok nem tudnak adatbázis prefixet fogadni. Meg egyébként a procedure parancsok sem. Ez egyébként szerepel a dokumentációjukban is: a procedure csak schemaname.procedurename-t ismer fel, a function is csak schemaname.functionname-t ismer, míg mondjuk a table ismer databasename.schemaname.tablename-t is.
A megoldás?
Teljesen zátonyra futottunk, csak pislogunk, mint a béka amit Vuk kavicsnak nézett. Illetve mégsem… az elején említettem, hogy az execute-tal meg tudjuk oldani valóban a context váltást, csak éppen nem elég a use parancsot beletenni a végrehajtandó stringbe, hanem az egészet kellene…
declare @comm nvarchar(max);
set @comm = 'use kakukk; create table blabla...';
execute(@comm);
Ez működik. De ezzel a megoldással nem dicsekednék.
Azon kívül, hogy elég barkács, még elvesztünk egy csomó lehetőséget, amiből a legfájóbb az előzetes parse-olás. Nem tudjuk, hogy a script legalább szintaktikailag helyes-e, csak akkor, amikor lefuttattuk az egészet és elkezd hibákat dobálni.
Végszó
Persze lehet kombinálni a kettőt és a stringes végrehajtást csak akkor használni, ha elengedhetetlen. Meg akár release script generáláskor is figyelembe lehetne ezt venni és kicsit automatizálni.
Vannak opciók, de egyik sem tökéletes.