SQL Server
Wirtualna premiera SQL Server 2012
Jan 23rd
Właśnie dowiedziałem się, będąc w Redmond na szkoleniu Bare Metal SQL Server 2012, że 7 marca będzie miała miejsce wirtualna premiera SQL Server 2012! Jeżeli interesuje Cię nowa wersja SQL Servera, chcesz obejrzeć 30+ sesji prowadzonych przez czołowych specjalistów z całego świata i pracowników Microsoft, nie przegap tego wydarzenia i rejestruj się już dzisiaj na witrynie http://www.sqlserverlaunch.com. Na zachętę dodam, że w ramach premiery będzie miał miejsce mały konkurs z ciekawymi nagrodami. Po szczegóły odsyłam już na witrynę wydarzenia. Ja już się zarejestrowałem, a teraz wracam do tworzenia wirtualnych maszyn na potrzeby labów ;-)
Ważne! Wspomniana wirtualna premiera będzie wyłącznie wydarzeniem promującym najnowszą wersję SQL Servera i nie należy jej traktować jako oficjalnej premiery produktu. Nie oczekujcie więc, że 7 marca będziemy w stanie pobrać wersję RTM produktu :-)
Materiały z dwóch edycji SQLDay Workshop
Jan 7th
Ostatnio informowałem o trzeciej edycji warsztatów SQLDay Workshop. Miejsca na warsztaty rozeszły się błyskawicznie, co tylko potwierdza, jak potrzebne są podobne przedsięwzięcia.
Miło mi poinformować za Tobiaszem Koprowskim, aktualnym prezesem Polskiej Grupy Użytkowników SQL Server (PLSSUG), że na kanale YouTube PLSSUG dostępne są nagrania wideo z dwóch poprzednich edycji warsztatów.
Osoby zainteresowane materiałami zapraszam na podane poniżej strony.
SQLDay Workshop 2009 PlayList: http://www.youtube.com/playlist?list=PL6FF436F4330A598C&feature=plcp
SQLDay Workshop 2010 PlayList: http://www.youtube.com/playlist?list=PLB07B560412F478F6&feature=plcp
Polecam też fanpage PLSSUG na Facebooku, gdzie można śledzić aktualne wydarzenia i działania grupy.
[PL] Konferencje, prelekcje, wydarzenia
Nov 24th
Szykuje się sporo atrakcji dla osób zainteresowanych bazami danych, w szczególności tymi implementowanymi w systemie Microsoft SQL Server. Pomyślałem, że dobrze by było zebrać informacje o nadchodzących wydarzeniach i wyostrzyć czujność pasjonatów baz danych.
Już za tydzień, w czwartek 1 grudnia, o godzinie 18:00 w siedzibie firmy Microsoft w Warszawie przy Al. Jerozolimskich 195A rozpocznie się spotkanie warszawskiego oddziału Polskiej Grupy Użytkowników SQL Server (PLSSUG). Z czystym sumieniem polecam obu prelegentów, którzy wystąpią w ramach tego spotkania. Marek Adamczuk, ekspert w dziedzinie architektury rozwiązań i programowania w języku T-SQL, opowie o kursorach – często znienawidzonym i jeszcze częściej niedocenianym elemencie języka T-SQL. Z kolei Włodek Bielski, znakomity specjalista z zakresu hurtowni danych i Business Intelligence, zaprezentuje PowerView – nowe narzędzie do prezentacji danych dostępne w SQL Server 2012. Naprawdę polecam obie ciekawie zapowiadające się sesje i obu prelegentów, których mam przyjemność znać osobiście i z którymi mam lub miałem okazję blisko współpracować.
Jeśli ktoś jest zainteresowany budowaniem rozwiązań analitycznych w oparciu o SQL Server i przy okazji jest studentem WWSI (Warszawska Wyższa Szkoła Informatyki), może być zainteresowany konferencją organizowaną przez tę uczelnię. Konferencja ta – odbywająca się co roku pod szyldem "Od studenta do specjalisty Business Intelligence" – jest skierowana głównie do studentów, ale gdy spojrzeć na sylwetki prelegentów – Marcin Szeliga (SQL Server MVP), Grzegorz Stolecki (SQL Server MVP) czy wspomniany Włodek Bielski, wydaje mi się, że każdy entuzjasta hurtowni, BI i data miningu może znaleźć tam coś dla siebie. Ja również będę miał przyjemność wystąpienia na tej konferencji. Zaprezentuję temat "Władcy Danych – wykorzystanie Master Data Services do zapanowania nad wszechobecnym chaosem". Prezentacja będzie poświęcona nowemu narzędziu do zarządzania różnymi reprezentacjami danych w rozproszonych środowiskach – Master Data Services. Konferencja odbędzie się 8 grudnia w budynku uczelni. Więcej informacji na stronie pod linkiem powyżej. Niestety, z tego, co wiem, konferencja jest przeznaczona wyłącznie dla studentów WWSI. Spróbuję się zorientować, czy będą jakieś nagrania z prezentacji i ew. materiały udostępnione publicznie.
I wreszcie wisienka na torcie. SQLDay 2012 czyli doroczna konferencja organizowana przez PLSSUG we Wrocławiu. Znamy już daty – 25 i 26 maja przyszłego roku – a więc warto zarezerwować sobie te dwa dni (a najlepiej także 24 maja). Dwa dni, a więc coś nowego (do tej pory wszystkie edycje były jednodniowe). Co więcej, od obecnego prezesa PLSSUG, Tobiasza "Anoraka" Koprowskiego, wiem, że swój udział zapowiedziało kilku zagranicznych prelegentów, wybitnych specjalistów w zakresie systemu SQL Server (naprawdę głośne nazwiska, sądzę, że niebawem pojawią się na witrynie konferencji). Zapowiada się znakomity zlot pasjonatów, geeków i innych SQL-owych zboczeńców :-)
To tyle informacji, a zarazem mojego blogowania na dzisiaj. A teraz czas zaprzyjaźnić się z SQL Server 2012 RC0, którego wczoraj udało mi się po walce zainstalować! ;-)
[PL] Najlepsze praktyki dla programisty T-SQL – collation i "obce znaczki"
Nov 6th
Na tegorocznej konferencji Microsoft Technology Summit (MTS) przedstawiłem wraz z Markiem Adamczukiem prezentację pt. "Najlepsze praktyki dla programisty Transact-SQL". Po tej sesji sporo osób prosiło mnie o materiały, a głównie demonstracje. Dobra informacja jest taka, że materiały te można już pobrać ze strony konferencji. Najwięcej dyskusji po prezentacji dotyczyło fragmentu poświęconego collation i pracy z danymi tekstowymi pochodzącymi z różnych języków. Dlatego, po rozmowie z Markiem, postanowiłem napisać niniejszy artykuł, w którym omówię wybrane najlepsze praktyki posługując się kodem zaprezentowanym na MTS.
Nvarchar – N ma znaczenie
Typy danych nchar i nvarchar umożliwiają przechowywanie w bazie danych tekstów zawierających znaki charakterystyczne dla różnych języków. Przykładowo, mamy bazę w collation "rosyjskim", a w jednej z kolumn w tabeli w tej bazie chcemy przechować tekst w języku polskim. Wówczas typ danych nvarchar doskonale nadaje się do przechowywania danych (co ciekawe, kolumna tego typu ma nadal przypisane collation "rosyjskie"). Co jest jednak istotne w takim przypadku? Ważne jest, by przy używaniu literałów tekstowych dawać systemowi SQL Server znać, że podany tekst ma być traktowany jako Unicode. Do tego służy prefiks N umieszczany bezpośrednio przed literałem (wielkość litery ma znaczenie). Poniższy przykład dobrze obrazuje tę zasadę. Bez prefiksu N polskie znaki w napisie, który otrzymuje collation bazy danych, po prostu znikają (w niektórych przypadkach zamiast znaków charakterystycznych dla różnych alfabetów pojawiają się znaki zapytania lub inne "krzaczki"). Po dodaniu prefiksu N polskie znaki są wstawiane w kolumnę typu nvarchar bez problemu, mimo że collation bazy i kolumny jest "rosyjskie".
--Tworzymy "rosyjską" bazę USE master; GO IF DB_ID('RussianDB') IS NOT NULL BEGIN ALTER DATABASE RussianDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE RussianDB; END; GO CREATE DATABASE RussianDB COLLATE Cyrillic_General_CI_AS; GO USE RussianDB; GO --Tabela z kolumną nvarchar CREATE TABLE dbo.Tab (c nvarchar(20) NULL); GO --Wstawiamy dane INSERT INTO dbo.Tab (c) SELECT 'ąęśćłóżźń'; GO --Test SELECT * FROM dbo.Tab; GO --A teraz zróbmy to, jak trzeba INSERT INTO dbo.Tab (c) SELECT N'ąęśćłóżźń'; GO --Test SELECT * FROM dbo.Tab; GO
Długa czy krótka kolacja?
W SQL Server mamy bogactwo możliwych do wyboru collation. Zawsze, gdy tworzymy nową bazę danych, stajemy przed wyborem, jakie collation nadać tworzonej bazie? Pierwsza myśl – niech baza ma takie collation, jak bazy systemowe (czyli takie collation, jakie wybraliśmy przy instalacji SQL Servera). A jeśli ma mieć inne collation? Co wybierać – collation SQL-owe (prefiks SQL_ – zwane dalej "długim collation") czy raczej collation rodem z systemu Windows (np. Polish_CI_AS – zwane dalej "krótkim collation")? Istnieje kilka przesłanek przemawiających na korzyść collation "Windowsowych". Pierwsza przesłanka – niejawne rzutowania w przypadku, gdy do kolumny o "długim collation" spróbujemy wstawić dane typu nvarchar. Wówczas niejawna konwersja kolumny na nvarchar spowoduje niemożliwość wykorzystania ewentualnego indeksu (i odbędzie się skanowanie tabeli). W przypadku kolumny z "krótkim collation" problem znika – dane Unicode są rzutowane do typu varchar i indeks może zostać wykorzystany.
--Tabela z "długim" collation CREATE TABLE #t1 ( c1 varchar(128) COLLATE SQL_Polish_CP1250_CS_AS NOT NULL ); CREATE CLUSTERED INDEX IX_1 ON #t1 (c1); GO --Tabela z "krótkim" collation CREATE TABLE #t2 ( c2 varchar(128) COLLATE Polish_CI_AS NOT NULL ); CREATE CLUSTERED INDEX IX_2 ON #t2 (c2); GO --Dane INSERT INTO #t1 (c1) SELECT name FROM sys.all_objects; GO INSERT INTO #t2 (c2) SELECT name FROM sys.all_objects; GO --Test SELECT * FROM #t1 WHERE c1 = N'objects'; SELECT * FROM #t2 WHERE c2 = N'objects'; GO
Innym zachowaniem "długich collation", które może być powodem do zaniechania ich wykorzystywania, jest sortowanie tekstów, w których pojawiają się myślniki. W zasadzie komentarz jest zbędny – wystarczy rzut oka na wyniki zapytania z sortowaniem, by stwierdzić, że wynik dla "długiego collation" jest dość nieoczekiwany.
--Zmieńmy zawartość TRUNCATE TABLE #t1; TRUNCATE TABLE #t2; INSERT INTO #t1 (c1) VALUES ('a'), ('a-'), ('a-a'); INSERT INTO #t2 (c2) VALUES ('a'), ('a-'), ('a-a'); GO --Problem sortowania i myślników SELECT * FROM #t1 ORDER BY c1; SELECT * FROM #t2 ORDER BY c2; GO
Collation a tabele tymczasowe
Jeśli już zdecydujemy się na wybór dla bazy danych collation innego niż collation serwera, musimy liczyć się z tym, że nasza nowo utworzona baza będzie miała inne collation niż baza tempdb. Co za tym idzie? Gdy będziemy używali tabel tymczasowych w kodzie proceduralnym w naszej bazie, możemy napotkać na konflikty collation w sytuacjach, gdy będą wykonywane operacje porównywania czy sortowania tekstu.
USE RussianDB; GO IF OBJECT_ID('dbo.T11', 'U') IS NOT NULL DROP TABLE dbo.T11; GO CREATE TABLE dbo.T11 ( c11 varchar(128) NOT NULL ); GO INSERT INTO dbo.T11 (c11) VALUES ('MTS'); GO IF OBJECT_ID('tempdb.dbo.#T11', 'U') IS NOT NULL DROP TABLE #T11; GO CREATE TABLE #T11 ( c11 varchar(128) NOT NULL ); GO -- Test - konflikt collation SELECT * FROM dbo.T11 UNION SELECT * FROM #T11; GO
Msg 468, Level 16, State 9, Line 3 Cannot resolve the collation conflict between "Polish_CI_AS" and "Cyrillic_General_CI_AS" in the UNION operation.
Rozwiązanie połowiczne – użyć składni SELECT INTO do stworzenia tabeli tymczasowej jako kopii istniejącej tabeli. Wówczas collation kolumn tekstowych są identyczne z tymi z tabeli źródłowej.
IF OBJECT_ID('tempdb.dbo.#T11', 'U') IS NOT NULL DROP TABLE #T11; GO SELECT TOP 0 * INTO #T11 FROM dbo.T11; GO -- Test: ciut lepiej, bo działa SELECT * FROM dbo.T11 UNION SELECT * FROM #T11; GO
Nie zawsze jednak mamy tabelę wzorcową, na której możemy oprzeć tabelę tymczasową. Dlatego najlepszym rozwiązaniem wydaje się być użycie klauzuli COLLATE DATABASE_DEFAULT. Wówczas kolumna tekstowa otrzymuje collation identyczne z tym, jakie zostało przypisane bazie danych, w kontekście której uruchamiamy kod.
IF OBJECT_ID('tempdb.dbo.#T11', 'U') IS NOT NULL DROP TABLE #T11; GO CREATE TABLE #T11 ( c11 varchar(128) COLLATE DATABASE_DEFAULT NOT NULL ); GO -- Test: jest dobrze :) SELECT * FROM dbo.T11 UNION SELECT * FROM #T11; GO
Podsumowanie
Powyższe przykłady ilustrują pewne wybrane przeze mnie i Marka problemy dotyczące danych tekstowych w SQL Server. Oczywiście, podobnych problemów jest więcej (np. jak zmienić collation istniejącej bazy?), ale wydawało nam się, że przedstawione dema pozwolą na ominięcie najczęściej występujących pułapek. No i druga sprawa – mieliśmy tylko 60 minut na prezentację :-) W tym miejscu pragnę podziękować wszystkim, którzy byli na naszej sesji na MTS i wypełnili ankietę dotyczącą naszej prezentacji. Jest mi też niezmiernie miło, że nasz przekaz spotkał się z bardzo pozytywnym odbiorem. Gdyby była potrzeba opisania w kolejnym artykule jakiegoś fragmentu kodu z naszych demonstracji, dajcie znać, a chętnie opublikuję kolejny fragment tekstu na blogu.
[PL] SQL Server MVP Deep Dives vol. 2 już jest!
Sep 27th
Miło mi poinformować, że wydawnictwo Manning rozpoczęło przyjmowanie zamówień na książkę SQL Server MVP Deep Dives vo. 2, której jestem współautorem. Oficjalna premiera książki będzie miała miejsce w Redmond w czasie konferencji PASS Summit 2011. Dla osób, które nie zetknęły się z pierwszą częścią książki – SQL Server MVP Deep Dives to książka pisana przez specjalistów zajmujących się systemem SQL Server. Większość autorów to ludzie nagrodzeni przez firmę Microsoft tytułem Microsoft Most Valuable Professional. Cały dochód ze sprzedaży idzie na cel charytatywny, w przypadku drugiej odsłony książki – na organizację Operation Smile zajmującą się operowaniem rozszczepów podniebienia u dzieci. To dla mnie wielki zaszczyt być częścią tego przedsięwzięcia i szczytnej idei, jaką jest wspieranie organizacji niosących pomoc dzieciom na całym świecie. Cena książki to 47,99 dolarów US za wersję elektroniczną lub 59,99 dolarów US za wersję drukowaną. Książkę polecam każdemu, kto interesuje się zagadnieniami związanymi z SQL Serverem lub po prostu ma ochotę i środki by wesprzeć szczytny cel tego wydawnictwa.
[PL] Denali CTP3 – Nowe lepsze DBCC IND?
Jul 26th
Niedawno ukazała się nowa publiczna wersja Community Preview 3 (CTP3) systemu SQL Server vNext (codename Denali). Okoliczności ukazania się tej wersji były dość ciekawe, ponieważ wersja ukazała się dość niespodziewanie szybciej, niż ktokolwiek mógł podejrzewać. Jednocześnie firma Microsoft zarzuciła użytkowników bogactwem nowych wersji SQL Server 2008 R2 (Service Pack1) i 2008 (kilka Cumulative Update’ów). Jest więc co eksplorować, choć zalecam ostrożność, ponieważ z zaufanych źródeł wiem, że zwłaszcza Service Pack 1 do R2 jest źródłem licznych problemów (włącznie z tym, że dość trudno go poprawnie zainstalować).
Dopiero kilka dni temu znalazłem czas, by sprokurować sobie maszynę wirtualną do Denali CTP3. Maszyna działa pod kontrolą Windows Server 2008 SP1. Do tego trzeba było doinstalować parę rzeczy, ale na szczęście instalator wszystko dość jasno opisuje i nie ma problemów ze znalezieniem w sieci brakujących dodatków do systemu operacyjnego.
Jedną z pierwszych czynności po zainstalowaniu każdej nowej wersji SQL Servera jest w moim przypadku sprawdzenie, czy nie pojawiły się nowe widoki i funkcje dynamiczne. Uruchamiam zatem zapytanie:
SELECT 'sys.' + name FROM sys.system_objects WHERE [schema_id] = 4 AND name LIKE 'dm[_]%';
Przejrzenie listy w przypadku Denali CTP3 zaowocowało znalezieniem kilku ciekawych obiektów, ale najciekawszym z mojego punktu widzenia wydaje się być sys.dm_db_database_page_allocations. Jest to funkcja dynamiczna, która przyjmuje sporo parametrów: identyfikator bazy, identyfikator tabeli, identyfikator indeksu, numer partycji oraz rodzaj widoku. Lista parametrów przypomina widok sys.dm_db_index_physical_stats. Jednak nowa funkcja zwraca informacje prawie takie same, jakie dotąd można było uzyskać za pomocą polecenia DBCC IND. Możemy zatem obejrzeć, które strony wchodzą w skład konkretnego indeksu czy sprawdzić, z ilu poziomów stron dany indeks jest złożony. Przykładowe odwołanie do tej funkcji:
SELECT * FROM sys.dm_db_database_page_allocations( DB_ID(), NULL, NULL, NULL, 'DETAILED' );
Przykładowy wynik (lista stron zaalokowanych dla całej bazy, widok o największej liczbie detali):
Nie muszę tłumaczyć, o ile lepiej byłoby mieć w systemie widok dynamiczny, na dodatek w pełni wspierany i udokumentowany. Do szczęścia jeszcze poproszę jakiś oficjalny zamiennik dla DBCC PAGE :-)
[PL] SQL Server – Przerwany łańcuch backupów różnicowych
Jun 3rd
Ostatnio po raz pierwszy w życiu spotkałem się z przypadkiem przerwanego łańcucha backupów różnicowych w SQL Server. Zjawisko może i ciekawe, ale potrafi mocno zestresować DBA. Jak to wygląda i w czym problem?
Wyobraźmy sobie scenariusz. Masz hurtownię danych, VLDB o rozmiarze ponad 1TB. Baza pracuje w SIMPLE recovery model. Wykonanie jej pełnego backupu zajmuje jakieś 2-3 godziny. Backup pełen bazy został zrobiony kilka godzin wcześniej. Teraz po dokonaniu pewnych zmian w konfiguracji bazy decydujesz się na wykonanie backupu różnicowego (differential backup). Ale przy próbie wykonania takiego backupu pojawia się komunikat:
Msg 3035, Level 16, State 1, Line 1 Cannot perform a differential backup for database "DataWarehouse", because a current database backup does not exist. Perform a full database backup by reissuing BACKUP DATABASE, omitting the WITH DIFFERENTIAL option. Msg 3013, Level 16, State 1, Line 1 BACKUP DATABASE is terminating abnormally.
Ale jak to? Przecież Ty wiesz, że pełen backup bazy został wykonany i leży sobie na dysku! Co się wydarzyło przez te parę godzin, że SQL Server nie pozwala Ci wykonać kopii różnicowej? Pierwszą podpowiedź znajdziesz w errorlogu SQL Servera. W czasie, gdy wykonywał się pełen backup zapewne pojawiły się w errorlogu komunikaty w stylu:
I/O is frozen on database DataWarehouse. No user action is required. However, if I/O is not resumed promptly, you could cancel the backup.
Powyżej pewnie zobaczysz (pewnie nawet zalogowane w tej samej sekundzie):
I/O was resumed on database DataWarehouse. No user action is required.
Hmmm… Pierwsza myśl – coś z dyskiem :-) Ale to nie to.
Sprawdźmy historię backupów naszej bazy danych (nie pamiętam, skąd wziąłem skrypt, ale w razie czego można posłużyć się fajnym kawałkiem kodu z bloga Roberta Kubalskiego):
SELECT bs.backup_finish_date, DATEDIFF(second, bs.backup_start_date, bs.backup_finish_date) AS time_taken_sec, CASE bs.type WHEN 'D' THEN 'Full' WHEN 'L' THEN 'Log' WHEN 'I' THEN 'Differential' ELSE '' END AS backup_type, CONVERT(decimal(15, 2), bs.backup_size / (1024. * 1024)) AS backup_size_mb, bmf.physical_device_name FROM msdb.dbo.backupmediafamily as bmf INNER JOIN msdb.dbo.backupset as bs ON bmf.media_set_id = bs.media_set_id WHERE (CONVERT(datetime, bs.backup_start_date, 102) >= GETDATE() - 7) AND bs.database_name = N'DataWarehouse' ORDER BY bs.backup_finish_date DESC;
Przykładowy wynik:
Co widzimy? Ano, zapewne wśród wyświetlonych informacji pojawią się backupy, które w kolumnie physical_device_name będą miały dziwaczne “szlaczki” w stylu {CBE47A8D-BF0C-4C9E-B3D4-841E16793FA8}10. Ponadto, owe podejrzane backupy będą miały równie podejrzany maleńki rozmiar (w prezentowanym powyżej przykładowym wyniku pełen backup bazy o rozmiarze 1GB zajął ponoć 0.01MB). O czym świadczą te objawy (komunikaty w errorlogu, dziwne wpisy w historii backupów oraz problem z wykonaniem backupu differential)?
Problem okazał się powszechnie (?) znany i dość wiekowy, bo nawet doczekał się swojego KB: http://support.microsoft.com/kb/903643 (a jak poszukać, to jest pewnie sporo pokrewnych KB, np. http://support.microsoft.com/kb/937683). A więc wszystkiemu winien jest działający spod systemu operacyjnego program do robienia kopii zapasowych plików, który potrafi używać Volume Shadow Copy do backupowania plików baz danych nawet, gdy są one używane przez SQL Server. Niestety, robi to kosztem backupów natywnych SQL Servera. Jeśli zdarzy się, że oba backupy – natywny backup w SQL Serverze i backup z poziomu systemu operacyjnego (niekoniecznie ntbackup, bo okazuje się, że narzędzia firm trzecich też używają tego samego mechanizmu) – wykonują się w tym samym czasie, może zostać przerwany łańcuch backupów różnicowych i nie będziesz w stanie zrobić backupu differential. A to może być czasem fatalne w skutkach. Rozwiązaniem oczywistym jest – bazy danych SQL Servera backupować wyłącznie natywnym mechanizmem (z kodu T-SQL) lub narzędziami dedykowanymi do backupowania baz SQL Servera. Jednak czasem nie da się wytłumaczyć administratorowi sieci korporacyjnej, że nie powinien używać innych narzędzi do backupowania plików baz danych. Wówczas pomocne może okazać się wyłączenie usługi SQL Server VSS Writer, dzięki której możliwe jest właśnie backupowanie plików działających baz przez “windowsowe” narzędzia.
Na koniec wypadałoby napisać, jak wykrywać takie anomalie. Próbowałem przeglądać strony DIFF bazy potraktowanej narzędziem NTBackup, ale nie znalazłem żadnej (nie-)prawidłowości, która sugerowałaby, że łańcuch backupów differential jest przerwany. Jedyne, co przychodzi mi więc do głowy, to sprawdzać w historii backupów danej bazy, czy physical_device_name jednego z ostatnich backupów zawiera coś innego niż ścieżkę do pliku. Jeśli zamiast ścieżki widzisz wspomniane wyżej “krzaki”, to może się okazać, że właśnie dotknął Cię opisany problem. Jeśli ktoś ma lepszy pomysł na detektor zerwanego łańcucha backupów differential, niech się podzieli :-)
[PL] SQL Server – Jak optymalizator robi ze mnie głupca
May 31st
Dawno, dawno temu dowiedziałem się z bloga Tibora Karasziego (SQL Server MVP), że optymalizator może wykorzystać obiekty constraint, takie jak CHECK czy FOREIGN KEY (klucz obcy) do optymalizacji określonych klas zapytań.
Jakiś czas temu próbowałem pokazać koledze z firmy, jak to działa i przy okazji dowiedziałem się ciekawej rzeczy, na którą dotąd nie natrafiłem. A w zasadzie, można powiedzieć, normalnie aż zgłupiałem :-)
Na początek weźmy tabelę:
USE tempdb; GO IF OBJECT_ID('dbo.Employee') IS NOT NULL DROP TABLE dbo.Employee; CREATE TABLE dbo.Employee ( EmployeeId int IDENTITY(1,1) NOT NULL PRIMARY KEY, FirstName varchar(50) NOT NULL, LastName varchar(50) NOT NULL, Income money NOT NULL DEFAULT (0) ); GO ALTER TABLE dbo.Employee ADD CONSTRAINT CK_Employee_Income CHECK (Income >= 0); GO INSERT INTO dbo.Employee (FirstName, LastName, Income) SELECT NEWID(), NEWID(), RAND(CHECKSUM(NEWID())) * v1.number + v2.number FROM master.dbo.spt_values AS v1, master.dbo.spt_values AS v2 WHERE v1.type = 'P' AND v2.type = 'P' AND v1.number BETWEEN 1 AND 1000 AND v2.number BETWEEN 1 AND 1000; GO
Tabela dość typowa. I zawiera milion rekordów. To, co ważne, to fakt, że założyłem w niej warunek ograniczający możliwe wartości kolumny Income (muszą być nieujemne). Świeżo założony w powyższy sposób warunek CHECK jest “trusted” (patrz wpis na blogu Tibora). Weźmy teraz zapytanie:
SELECT * FROM dbo.Employee WHERE Income < 0; GO
Plany wykonania na mojej maszynie i przy konfiguracji serwera z MAXDOP = 0 wygląda u mnie tak:
Hę? Spodziewałem się, że zobaczę operator Constant Scan i… tyle! A tymczasem jakiś skan, z jakimś parallelismem… Horror!
Pobawiłem się trochę tym zapytaniem. Pierwsza myśl – wyeliminować parallelism:
SELECT * FROM dbo.Employee WHERE Income < 0 OPTION (MAXDOP 1); GO
Plan wykonania:
Noooo, to lubimy :-) Żadnego zbędnego zaglądania do tabeli, po prostu od razu na podstawie zaufanego warunku CHECK generowana jest odpowiedź, że nie ma rekordów spełniających warunek z zapytania.
A teraz deser – przyznaję, że sam tego nie wymyśliłem (zbyt fikuśne :-)), choć testowałem różne śmieszne warianty zapytań (m.in. dodawałem grupowania po wszystkich kolumnach tabeli – pomagało i wymuszało Constant Scan). Maciek Pilecki, z którym ten problem przedyskutowałem i który, podobnie jak ja, ma wrażenie, że coś tu nie gra, wypróbował taki kwiatek:
SELECT * FROM dbo.Employee WHERE Income < 0 OPTION (MAXDOP 0); GO
Efekt? Sami sprawdźcie, ale gwarantuję pełne zaskoczenie :-) Oczywiście, Constant Scan! I o so chosi?
Wygląda na to, że optymalizator pozostawiony samemu sobie i stojący przed “trudnym” wyborem (nie wpisując hinta powodujemy, że optymalizator ma do dyspozycji wszystkie możliwe ścieżki optymalizacji zapytania) robi głupotę. A może to nowy bug? Ale plan dla zapytania z MAXDOP = 0 powoduje już moją konsternację. Trzeba będzie z kimś z grupy produktowej o tym porozmawiać, bo bardzo nie lubię tłumaczyć się przed kolegami, którym demonstruję “niby dobrze znane” triki optymalizacyjne, a tymczasem optymalizator krzyżuje moje plany i wychodzę na głupca :-)
Wszelkie przemyślenia własne na temat powyższego mile widziane. Moje przemyślenie – nigdy nie bądź niczego pewnym na 100% bez sprawdzenia organoleptycznie :-)
Dodam, że przełączenie MADXOP = 1 na poziomie serwera nie zmienia sytuacji – dalej mamy skan całej tabeli, jeśli nie dodamy hinta lub nie skomplikujemy zapytania! O co tutaj chodzi??? :-)
I na sam koniec – kupa śmiechu. Jaki jest bodaj najprostszy możliwy hint wymuszający Constant Scan w omawianym przypadku? Opadają mi ręce, gdy pomyślę o mojej SQL-owej niewiedzy…
SELECT * FROM dbo.Employee WHERE Income < 0 AND 1=1; GO
[EDIT] Jak słusznie zauważył Patrik Třeštik komentując ten wpis, problem jest najpewniej spowodowany przez parametryzację zapytania (i zarazem zjawisko parameter sniffing). Jakimś cudem zero w warunku WHERE przestaje być stałą i optymalizator zaczyna traktować je jak wartość nieznaną, a więc jednocześnie nie może porównać jej do wartości zaszytej w ograniczeniu CHECK. Tylko powstaje pytanie – po co ta parametryzacja? :-) [/EDIT]
[PL] SQL Server – {Workbench} Funkcje i ich “gotchas”
May 4th
Od jakiegoś czasu chodził za mną pomysł, żeby publikować na blogu obszerne fragmenty kodu, które wraz z komentarzami w nich zawartymi opisywałyby się same. Pomysł zaczerpnąłem z serii, którą dawno temu śledziłem na portalu Simple Talk (przykład). Dzisiaj pierwszy workbench na moim blogu – poświęcony pułapkom w systemowych funkcjach skalarnych (kod trzymam od dawna, więc pomyślałem, że czas go opublikować). Prawie na pewno nie będzie to ostatni workbench, jaki opublikuję. A gdyby komuś przyszło do głowy, co by można było dopisać do listy poniższych “gotchas”, śmiało dodawajcie w komentarzach albo ślijcie na maila (najlepiej od razu w takiej postaci, jak poniżej). Creditsy gwarantowane :-)
/* Workbench: Funkcje i ich "gotchas" Cel: Pokazanie pułapek ukrytych w systemowych funkcjach skalarnych w SQL Server. Założenie: Znajomość przedstawionych funkcji i umiejętność znalezienia pomocy do nich w BOL Uwagi: kod zawiera tylko "gotchas", nie podaję gotowych obejść problemów */ ------------------------------------------------------------ -- Funkcja : DATEDIFF -- Gotcha : Niedziela zawsze pierwszym dniem tygodnia -- Rozwiązanie : Własna funkcja ------------------------------------------------------------ -- Niedziela pierwszym dniem tygodnia SET DATEFIRST 7 -- Wynik: oczekiwane 0 SELECT DATEDIFF(week, '20110501', '20110502') -- Poniedziałek pierwszym dniem tygodnia SET DATEFIRST 1 -- Wynik: nieoczekiwane 0 SELECT DATEDIFF(week, '20110501', '20110502') ------------------------------------------------------------ -- Funkcja : ISNULL -- Gotcha : Typ danych narzucany przez pierwszy parametr -- Rozwiązanie : Jawna konwersja lub użycie COALESCE zamiast ISNULL ------------------------------------------------------------ -- Wynik: Ala m - bo typ wynikowy to char(5) SELECT ISNULL(CONVERT(char(5), NULL), 'Ala ma kota') ------------------------------------------------------------ -- Funkcja : ISNUMERIC -- Gotcha : Nie zawsze mówi prawdę -- Rozwiązanie : Własna funkcja (CLR?) ------------------------------------------------------------ -- Wynik: 0 i 0, czyli '' nie jest liczbą, -- ale daje się rzutować na typ liczbowy SELECT ISNUMERIC(''), CONVERT(int, '') ------------------------------------------------------------ -- Funkcja : LEN -- Gotcha : Przed zliczaniem znaków ucina końcowe spacje -- Rozwiązanie : Własna funkcja ------------------------------------------------------------ -- Wynik: 3 (zamiast 5) SELECT LEN('abc ') ------------------------------------------------------------ -- Funkcja : QUOTENAME -- Gotcha : Zwraca NULL dla parametrów dłuższych niż 128 znaków -- Rozwiązanie : Własna funkcja ------------------------------------------------------------ -- Wynik: NULL SELECT QUOTENAME(REPLICATE('a', 129)) ------------------------------------------------------------ -- Funkcja : REPLICATE -- Gotcha : Typ danych prawie narzucany przez pierwszy parametr -- Rozwiązanie : Jawna konwersja pierwszego parametru ------------------------------------------------------------ -- Wynik: 7998, bo kolejna trójka nie mieści się w 8000, -- a typ varchar(max)/nvarchar(max) musi być wskazany jawnie SELECT LEN(REPLICATE(CONVERT(char(3), 'abc'), 3000)) ------------------------------------------------------------ -- Funkcja : SERVERPROPERTY -- Gotcha1 : Dla "nieznanej" wartości parametru zwraca NULL -- Gotcha2 : Zwraca wartość typu sql_variant (jak inne funkcje *PROPERTY) -- Rozwiązania : Uważać na parametr + jawna konwersja wyniku ------------------------------------------------------------ -- Wynik: NULL i nazwa_maszyny SELECT SERVERPROPERTY('MachinName'), SERVERPROPERTY('MachineName') -- Wynik: Błąd -- Msg 402, Level 16, State 1, Line 1 -- The data types varchar and sql_variant are incompatible in the add operator. SELECT 'Nazwa maszyny: ' + SERVERPROPERTY('MachineName') ------------------------------------------------------------ -- Funkcja : STR -- Gotcha : Czasem zwraca "gwiazdki" :-) -- Rozwiązania : Podawać dwa parametry lub nie używać STR ------------------------------------------------------------ -- Wynik: ********** i 12345678901 -- Pierwsze wyrażenie zwraca wartość typu char(10), -- a gwiazdki to wynik zwracany przez STR przy przekroczeniu -- długości określonej przez drugi opcjonalny parametr (domyślnie 10) SELECT STR(12345678901), STR(12345678901, 11)



Nazywam się Paweł Potasiński i pracuję w polskim oddziale Microsoft w dziale Small and Midmarket Solutions & Partners (SMS&P) jako Partner Technology Advisor.




