wtorek, 10 grudnia 2013

Usuwanie plików baz danych

Dzisiaj troche hardcorowo. Czasami zdarza się, że na serwerze baz danych z jakiegoś powodu brakuje miejsca na dysku na odtworzenie kolejnej bazy. W dodatku na nieszczęście dostęp do folderów gdzie te miejsce potencjalnie mogłoby się znaleść jest zakazany.

W poszukiwaniu miejsca na dysku zidentyfikowałem ostatnio, że sporo baz danych pomimo tego, że zostało “usuniętych” pozostawiło swoje pliki na serwerze. Szkoda tylko, że nie miałem dostępu a do otrzebnych mi folderów a administrator systemu był zajęty innymi tematami. Z racji tego, że to serwer baz danych deweloperskich to po bandzie czasami można pojechać.

Na serwerach produkcyjnych lub też takich gdzie usuwanie plików może mieć poważne konsekwencje nie polecam tego typu zabaw.

Tak wiec będąc administratorem serwera baz danych mozna włączyć małe co nieco.
Poniższy skrypt włancza opcje zaawansowane oraz command shell
   1: --Enable advanced options
   2: EXEC SP_CONFIGURE 'SHOW ADVANCED OPTIONS', 1;
   3: GO
   4: RECONFIGURE
   5: GO
   6:  
   7: --Enable command shell
   8: EXEC SP_CONFIGURE 'XP_CMDSHELL', 1
   9: GO
  10: RECONFIGURE
  11: GO
No to teraz do dzieła. Usuwamy wszystkie pliki ldf i mdf. A niech sie dzieje. Kod opisany komentarzami dla ulatwienia.
   1: SET NOCOUNT ON
   2:  
   3: DECLARE @DIRCOMMAND VARCHAR (8000)
   4: DECLARE @ROOTPATH VARCHAR (8000)
   5: DECLARE @DATAFOLDERPATH VARCHAR (8000)
   6: DECLARE @BACKUPFOLDERPATH VARCHAR (8000)
   7: DECLARE @FILE VARCHAR (8000)
   8: DECLARE @COMMAND VARCHAR (8000)
   9: DECLARE @THRESHOLDDATE VARCHAR (30)
  10:  
  11: DECLARE @DIRTABLE TABLE
  12: (NAME VARCHAR (8000))
  13:  
  14: SET @ROOTPATH = 'C:\PROGRAM FILES\MICROSOFT SQL SERVER\MSSQL10.SQL2008\MSSQL\'
  15:  
  16: SET @DATAFOLDERPATH = @ROOTPATH + 'DATA\'
  17: SET @BACKUPFOLDERPATH = @ROOTPATH + 'BACKUP\'
  18: --Directory listing command
  19: SET @DIRCOMMAND = 'DIR ' + '"' + @DATAFOLDERPATH + '"'
  20: SET @THRESHOLDDATE = CONVERT( VARCHAR(30 ), GETDATE (), 106)
  21:  
  22: --Delete database backup files
  23: EXECUTE MASTER .DBO. XP_DELETE_FILE 0 , @BACKUPFOLDERPATH, N'BAK', @THRESHOLDDATE
  24:  
  25: --Get all files from Data folder
  26: INSERT @DIRTABLE EXECUTE MASTER. DBO.XP_CMDSHELL @DIRCOMMAND
  27:  
  28: --Remove everything apart from log and data files
  29: DELETE FROM @DIRTABLE WHERE ( NAME NOT LIKE '%.LDF' AND NAME NOT LIKE '%.MDF') OR NAME IS NULL
  30:  
  31: --Iterate through files table
  32: DECLARE GOODBYEFILE CURSOR FAST_FORWARD FOR
  33: SELECT SUBSTRING (NAME, LEN( NAME) - CHARINDEX (' ', REVERSE( NAME))+2 , LEN(NAME )) FROM @DIRTABLE
  34: OPEN GOODBYEFILE
  35:  
  36: FETCH NEXT FROM GOODBYEFILE INTO @FILE
  37: WHILE @@FETCH_STATUS = 0
  38: BEGIN
  39:  
  40: --Delete command
  41: SET @COMMAND = 'DEL "' + @DATAFOLDERPATH + @FILE + '"'
  42:  
  43: --This will remove the specified file as long as it isn't used by any database on the server.
  44: --If the database was detached the file will be gone!!!
  45: EXECUTE MASTER .DBO. XP_CMDSHELL @COMMAND , NO_OUTPUT
  46:  
  47: FETCH NEXT FROM GOODBYEFILE INTO @FILE
  48: END
  49:  
  50: CLOSE GOODBYEFILE
  51: DEALLOCATE GOODBYEFILE
  52: GO
  53: --Job done!
Wykonując ten kod robicie to na własną odpowiedzialność. Można sobie narobić sporo kłopotów jeżeli coś pójdzie nie tak jak powinno.

sobota, 30 listopada 2013

Najczęściej popełniane błędy

Dzisiaj krótko o najczęściej popełnianych błędach podczas pisania skryptów, które udało mi się wychwycić w ostatnim czasie.

U mnie to działa
Jest to dość częste wytłumaczenie programistów. U mnie na środowisku deweloperskim, lub bazie danych, wszystko działa poprawnie. Szkoda tylko, że tego oto środowiska nie można wykorzystać na produkcji. Dlaczego skrypt może wykonywać się bez problemu na jednej bazie danych a na innej już nie, mimo że są na tej samej wersji SQL Servera? Jednym z powodów może być kompatybilność bazy danych. Jeżeli baza jest na SQL 2008 zarówno na środowisku deweloperskim jak i produkcyjnym to obie powinny mieć ten sam poziom kopatybilności. Ostatnio trafiłem na kilka skryptów, które sie nie wykonywały poprawnie z racji tego, że jedna baza miała ustawioną kompatybilność na 90 a druga na 80.

Wstawianie nowego rekordu bez enumeracji kolumn
To jest niestety dość częsty błąd. Wypisanie listy kolumn do których się chce wstawić dane jest tak czasochłonne, że aż pomijane. Niefart pojawia się wtedy, gry ktoś inny napsiał skrypt, który zmienia ilość kolumn w tabeli i ten skrypt z różnych powodów powinien być uruchomiony przed tym, który wstawia nowe rekordy. Co ciekawe, problem wcale nie jest taki skomplikowany i dość łatwo można z nim sobie poradzić używając następującego zapytania.
   1: SELECT name +', ' AS [text()] FROM sys.columns WHERE OBJECT_ID = OBJECT_ID('MYTABLE') FOR XML PATH('')

Powyższe rozwiązuje problem pisania wszystkich nazw kolumn. Wystarczy skopiować odpowiedź, wprowadzić niewielkie modyfikacje (nawiazy, kolumny identity) i problem staje się rozwiązany.

Uruchom i zapomnij
To jest jeden z moich ulubionych błędów. Napisz skrypt, wciśnij F5 i jak nie ma błędu do repozytorium. Szkoda tylko, że po ponownym wykonaniu skryptu rezultat może okazać się diametralnie inny. Defensywne programowanie (sprawdzanie czy obiekty istnieją, czy rekordy o danych kluczach istnieją już w tabeli itp) procentuje spokojem w przyszłości. Ale “niestety” wymaga aż kilku minut wiecej w czasie pisania skryptu.

Jak wspomniałem powyższe są najczęściej spotykanymi błędami, z którymi mam okazje się spotkać analizując lub wykonując kod innych członków zespołu. Uniknąć problemów jest bardzo łatwo, ale należy pamiętać o tych kilku rzeczach, o których wspomniełem powyżej i poświęcić dodatkowe kilka minut dopisując trochę kodu.