Archive for September, 2010

[PL] SQL Server – Logowanie wejść sysadminów na serwer

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Wstęp

Jakiś czas temu napisałem, dlaczego uważam, że logon trigger nie jest dobrym rozwiązaniem w sytuacji, gdy zależy nam na logowaniu udanych prób logowania do instancji SQL Servera członków roli sysadmin. Napisałem wówczas, że mechanizmem, który nadaje się do takiego zadania, są Extended Events. Ale okazało się, że Extended Events nie dysponują zdarzeniem, które takie logowanie by umożliwiło. W międzyczasie wpadłem na pomysł, by zastosować w takim przypadku events notifications (dostępne od SQL Server 2005).

Rozwiązanie

Zadanie: zapisywać do tabeli wszystkie udane próby logowania członków roli sysadmin.

Rozwiązanie:

-- (0) Wlaczamy Service Brokera, jezeli jest wylaczony
IF EXISTS (SELECT * FROM sys.databases WHERE name = N'msdb' AND is_broker_enabled = 0)
  ALTER DATABASE msdb SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE;
GO

-- (1) Wybieramy baze msdb, bo zawsze jest...
USE msdb;
GO

-- (2) Kasujemy obiekty, ktore zaraz utworzymy
IF EXISTS (SELECT * FROM sys.server_event_notifications WHERE name = N'AuditSysadminLoginNotification')
  DROP EVENT NOTIFICATION AuditSysadminLoginNotification ON SERVER;
GO
IF EXISTS (SELECT * FROM sys.services WHERE name = N'AuditSysadminLoginService')
  DROP SERVICE AuditSysadminLoginService;
GO
IF EXISTS (SELECT * FROM sys.service_queues WHERE name = N'AuditSysadminLoginQueue' AND [schema_id] = 1)
  DROP QUEUE dbo.AuditSysadminLoginQueue;
GO

-- (3) Tworzymy obiekty: kolejke, usluge i powiadomienie o zdarzeniu
CREATE QUEUE dbo.AuditSysadminLoginQueue WITH STATUS = OFF;
GO
CREATE SERVICE AuditSysadminLoginService
ON QUEUE AuditSysadminLoginQueue
(
[http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]
);
GO
CREATE EVENT NOTIFICATION AuditSysadminLoginNotification
ON SERVER
FOR AUDIT_LOGIN
TO SERVICE 'AuditSysadminLoginService', 'current database';
GO

-- (4) Tworzymy tabele na potrzeby logowania
IF OBJECT_ID(N'dbo.SysadminLogins', N'U') IS NOT NULL
  DROP TABLE dbo.SysadminLogins;
GO
CREATE TABLE dbo.SysadminLogins (
  LoginID int NOT NULL IDENTITY(1,1) PRIMARY KEY,
  LoginName sysname NOT NULL,
  ApplicationName nvarchar(256) NULL,
  HostName nvarchar(128),
  LoginDate datetime NOT NULL DEFAULT(GETDATE())
);
GO

-- (5) Tworzymy procedure do logowania
IF  OBJECT_ID(N'dbo.usp_LogSysadminLogin', N'P') IS NOT NULL
  DROP PROCEDURE dbo.usp_LogSysadminLogin
GO
CREATE PROC dbo.usp_LogSysadminLogin
AS
DECLARE
  @ErrorMessage nvarchar(4000),
  @EventData xml,
  @LoginName sysname,
  @ApplicationName nvarchar(256),
  @HostName nvarchar(128),
  @ConversationHandle uniqueidentifier;
WHILE (1=1)
BEGIN
  BEGIN TRAN;
  BEGIN TRY;
    RECEIVE TOP (1)
      @EventData = CAST(message_body AS xml),
      @ConversationHandle = [conversation_handle]
    FROM dbo.AuditSysadminLoginQueue;
    IF @@ROWCOUNT = 0
    BEGIN
      IF @@TRANCOUNT > 0
      BEGIN
          ROLLBACK;
      END;
      BREAK;
    END;
    SELECT
      @LoginName = @EventData.value('(/EVENT_INSTANCE/LoginName/text())[1]', 'sysname'),
      @ApplicationName = @EventData.value('(/EVENT_INSTANCE/ApplicationName/text())[1]', 'nvarchar(256)'),
      @HostName = @EventData.value('(/EVENT_INSTANCE/HostName/text())[1]', 'nvarchar(128)');
    IF IS_SRVROLEMEMBER(N'sysadmin', @LoginName) = 1 BEGIN
      INSERT INTO dbo.SysadminLogins (LoginName, ApplicationName, HostName)
      SELECT @LoginName, @ApplicationName, @HostName;
    END;
    IF @@TRANCOUNT > 0
      COMMIT;
  END TRY
  BEGIN CATCH;
    IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK;
        END CONVERSATION @ConversationHandle;
        BREAK;
    END;
  END CATCH;
END;
GO

-- (6) Przypinamy procedure do kolejki i wlaczamy kolejke
ALTER QUEUE dbo.AuditSysadminLoginQueue
  WITH STATUS = ON,
  RETENTION = OFF,
  ACTIVATION (
    STATUS = ON ,
    PROCEDURE_NAME = msdb.dbo.usp_LogSysadminLogin,
    MAX_QUEUE_READERS = 2,
    EXECUTE AS OWNER
  );
GO

-- (7) Testujemy, czy cos sie zalogowalo
SELECT * FROM msdb.dbo.SysadminLogins;

Komentarz do kodu:

  • jako bazę docelową, w której zapisuję informacje o zdarzeniach, wybieram bazę msdb (zawsze jest na serwerze, domyślnie ma włączonego Service Brokera),
  • w kroku (0) włączam na bazie msdb Service Brokera, jeżeli jeszcze nie jest on włączony,
  • w kroku (2) dbam o to, by obiekty, które zamierzam stworzyć, nie istniały na serwerze i w bazie msdb,
  • w kroku (3) tworzę obiekty niezbędne do asynchronicznego przechwytywania zdarzeń: kolejkę, usługę oraz event notification (na poziomie serwera, przechwytuje zdarzenia AUDIT_LOGIN – logowania do instancji SQL Servera),
  • w kroku (4) tworzę tabelę, do której będą zrzucane informacje o logowaniu sysadminów (login, nazwa użytej aplikacji klienckiej, nazwa maszyny klienckiej oraz data i czas logowania),
  • w kroku (5) tworzę procedurę składowaną, która zostanie użyta do wybrania informacji z tego, co przechwyci event notification i zwróci za pomocą funkcji EVENTDATA, wybrane informacje zostaną zapisane do tabeli utworzonej w kroku (4), do wychwycenia loginów będących członkami roli sysadmin używam funkcji IS_SRVROLEMEMBER,
  • w kroku (6) przypinam procedurę z kroku (5) do kolejki z kroku (3) i włączam kolejkę, od tej pory zdarzenia klasy AUDIT_LOGIN są już przechwytywane, a stosowne informacje zapisywane w tabeli z kroku (4),
  • w kroku (7) prezentuję zapytanie, dzięki któremu możesz sprawdzać, czy jakieś zdarzenie zostało już zalogowane w tabeli z kroku (4).

Przykładowe informacje o zdarzeniach logowania sysadminów:

image

Podsumowanie

Zastosowanie event notification ma kilka zalet: nie ingeruje w proces logowania (nie ma takiej możliwości, by sysadmin nie zalogował się z powodu błędu w procesie logowania zdarzeń, jak to mogło mieć miejsce w przypadku zastosowania logon triggera), asynchroniczność zapewnia niezłą skalowalność i działanie mechanizmu nawet w przypadku dużego natężenia zdarzeń, ewentualne informacje o błędach są do znalezienia w errorlogu instancji SQL Servera (np. błędy wynikające z wykrycia “zatrutej wiadomości” – poison message) lub w widoku systemowym sys.transmission_queue (w kolumnie transmission_status znajdziesz informację, dlaczego komunikat o zdarzeniu utknął w czasie przesyłania). Co ciekawe, wydaje mi się, że można znaleźć naprawdę sporo kategorii zdarzeń, dla przechwytywania których event notifications nadają się idealnie (m.in. DEACLOCK_GRAPH i BLOCKED_PROCESS_REPORT). Używając event notifications możesz zbudować całkiem niezły framework do monitorowania SQL Servera. Polecam zabawę tym mechanizmem.

[PL] SQL Server – Jak przenieść dane geograficzne z bazy do bazy?

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Kolega zapytał mnie dzisiaj, jak przenieść między bazami danych w SQL Server 2008 / 2008 R2 dane geograficzne za pomocą Import/Export Wizarda. Przyznam, że nie znałem odpowiedzi na pytanie, ale z kontekstu pytania domyśliłem się, w czym problem. Otóż wizard ten nie oferuje w ogóle obsługi typów danych zaimplementowanych z użyciem CLR (wyjątkiem jest tu XML, który ma dość oczywistą postać tekstową). Jak sobie zatem poradzić z problemem, gdy chcemy przenieść takie dane między dwoma serwerami (nie między bazami na jednym serwerze – to jest do zrobienia za pomocą prostych poleceń SELECT…INTO lub INSERT INTO…SELECT) ? Rozwiązań jest kilka. Ja pokażę trzy.

Metoda 1. – Generowanie skryptu w Management Studio

W Management Studio (SSMS) w oknie Object Explorer klikamy prawym przyciskiem myszy na bazie danych i wybieramy Tasks – Generate Scripts…

image

W wizardzie do generowania skryptów wybieramy tabelę z danymi geograficznymi i klikamy Next, aż zobaczymy ekran z przyciskiem Advanced…

image

Oczywiście, klikamy na tym przycisku (swoją drogą łatwym do przeoczenia!), by ujrzeć zaawansowaną (?) konfigurację skryptowania. Szukamy pozycji Types of data for script i wybieramy w niej Schema and data. To zapewni nam zeskryptowanie zarówno definicji tabeli, jak i jej danych.

image

W przypadku, gdy zdecydujesz, że wygenerowany kod T-SQL ma być od razu wrzucony do okna nowego skryptu, możesz otrzymać komunikat informujący o dłuuuuugich liniach w tekście. Rozchodzi się o to, że dane binarne (geograficzne) są skryptowane “as is” tj. w postaci heksadecymaliów. Nie trzeba dużej wyobraźni, by domyśleć się, że kształt granic Polski zapisanych za pomocą tysięcy punktów to całkiem duuuże binarium wymagające spoooorej liczby bajtów :-)

image

WAŻNE: Jeżeli odpowiesz “Tak” (a czy masz jakiś wybór? ;-)) na pytanie z okienka powyżej, spodziewaj się, że SSMS zacznie działać zauważalnie wolniej (a czasem po prostu zawiśnie lub zwróci OutOfMemoryException)…

Skrypt zapisujemy (albo i nie), otwieramy połączenie z drugą instancją SQL Server i jesteśmy gotowi do przerzucenia danych.

Metoda 2. – Import/Export Wizard

Druga metoda bazuje na użyciu Import/Export Wizarda i pewnego chwytu, który może wydać się dość rozpaczliwy, ale przy braku obsługi danych geograficznych w owym wizardzie… :-)

A zatem: Start – Uruchom – dtswizard.exe. Otwieramy “czarodzieja”, wybieramy serwer źródłowy i bazę źródłową, serwer docelowy i bazę docelową, a następnie… Skoro wiemy, że wizard nie daje sobie rady z typem geography, trzeba ów typ zmienić. Jak? Po prostu rzutując dane geograficzne na typ binarny varbinary(max).

image

Dalej jest już prosto. Dla tak spreparowanej binarnej kolumny wybieramy jako kolumnę docelową kolumnę typu image lub varbinary(max). A potem? Gdy już dane uda się zaimportować, logujemy się do docelowego serwera i na docelowej tabeli wykonujemy dwie operacje:

  1. Dodajemy kolumnę typu geography.
  2. Kopiujemy dane z kolumny binarnej, w której wylądowały dane geograficzne w procesie przenoszenia danych wizardem, do kolumny utworzonej chwilę wcześniej.

Bez dwóch zdań, wstyd dla Microsoftu, że wizard nie daje sobie rady z typami przestrzennymi (zresztą, to samo jest z typem hierarchyid).

Metoda 3. – Pass-through query

Trzecia metoda oparta jest na mechanizmie serwerów dołączonych (linked servers). Tworzymy na instancji źródłowej, z której chcemy eksportować dane geograficzne, linked server wskazujący na instancję docelową. Na docelowej instancji w odpowiedniej bazie danych zakładamy tabelę, do której dane geograficzne chcemy wrzucić, i konstruujemy tzw. pass-through query, czyli coś w stylu:

INSERT INTO OPENQUERY(
  [ASUS\SQL2008],
  'SELECT CountryName, CountryNameLocal, geom FROM Spatials.dbo.Country_Copy'
)
SELECT CountryName, CountryNameLocal, geom FROM Spatials.dbo.Country;

W powyższym kodzie [ASUS\SQL2008] to nazwa linked servera. Drugim parametrem w powyższym użyciu funkcji OPENQUERY jest zapytanie, którego zadaniem jest wskazanie tabeli i kolumn, do których chcemy wstawić dane. Wygląda niecodziennie, ale najważniejsze, że działa.

A za to nie działają dwa, wydawałoby się bardziej pospolite polecenia:

SELECT * INTO [ASUS\SQL2008].Spatials.dbo.Country_Copy FROM Spatials.dbo.Country;

-- Wynik:
-- Msg 117, Level 15, State 1, Line 1
-- The object name 'ASUS\SQL2008.Spatials.dbo.Country_Copy' 
-- contains more than the maximum number of prefixes. The maximum is 2.

INSERT INTO [ASUS\SQL2008].Spatials.dbo.Country_Copy (CountryName, CountryNameLocal, geom)
SELECT CountryName, CountryNameLocal, geom FROM Spatials.dbo.Country;

-- Wynik:
-- Msg 7325, Level 16, State 1, Line 1
-- Objects exposing columns with CLR types are not allowed 
-- in distributed queries. Please use a pass-through query 
-- to access remote object '"Spatials"."dbo"."Country_Copy"'.

Podsumowanie

Jak widać, przemieszczenie danych geograficznych (lub jakichkolwiek danych zapisanych w kolumnach typów opartych o CLR) nie jest trywialne. Pewnie, można wziąć backup bazy i odtworzyć na docelowym serwerze. Ale jeżeli baza jest duża, a ilość danych do przemieszczenia niewielka, ta metoda już nie wydaje się najlepsza. Poza tym co, jeśli chcemy przenieść dane między SQL Server 2008 R2 a SQL Server 2008? (swoisty downgrade) Wtedy już metoda oparta o backup (czy – równoważnie – o detach/attach) nie wchodzi w grę, bo po prostu nie da się przenieść bazy z wersji wyższej SQL Servera do wersji niższej. O ile wiadomo, że dtswizard.exe to prosta aplikacja, której zadaniem jest dokonywanie najprostszych transferów danych, o tyle brak obsługi danych przestrzennych w SSIS już nieco dziwi. Ale cóż, widać rozwój jednych “ficzerów” SQL Servera jest zbyt szybki i niektóre teamy w Redmond zwyczajnie za nim nie nadążają. Normalna sytuacja w rozbudowanym procesie twórczym skomplikowanego systemu, jakim jest SQL Server :-)

[PL] SQL Server – Indeksy na kolumnach kluczy obcych

VN:F [1.7.9_1023]
Rating: 5.0/5 (1 vote cast)

Tytułem wstępu

Jedną z najlepszych praktyk w relacyjnych bazach danych jest utworzenie indeksów na kolumnach, które wchodzą w skład kluczy obcych. Kolumny takie są często wykorzystywane do łączenia tabel w zapytaniach. Oczywiście nie należy bezwarunkowo zakładać indeksu na każdej kombinacji kolumn, tworzącej klucz obcy, ale przynajmniej należy taką możliwość rozpatrzyć (decyzja zależy również od rozkładu danych).

SQL Server nie tworzy takich indeksów samodzielnie (niektóre systemy potrafią zakładać indeksy automatycznie przy tworzeniu kluczy obcych). Celem niniejszego artykułu jest pokazanie na przykładach, dlaczego indeksowanie kolumn kluczy obcych może być dobrym pomysłem.

Zakładam, że wiesz, czym jest indeks i do czego służy. Jeżeli jest inaczej, zapoznaj się najpierw z tematyką indeksów i wróć do tego wpisu, gdy opanujesz niezbędne podstawy.
Dziękuję Pani Ewie Nowickiej, współpracującej z portalem WSS.pl, za korektę niniejszego tekstu.

Tworzymy poligon

Posłużę się klasycznym przykładem dwóch tabel: tabeli produktów i tabeli kategorii produktów. Tabele są powiązane związkiem binarnym jeden do wielu (jedna kategoria może mieć wiele produktów). W tabeli dbo.Product (zbiór produktów) pojawia się kolumna CategoryID, wskazująca na odpowiednią kategorię w tabeli dbo.Category. Kod do wygenerowania bazy danych i jej zawartości:

CREATE DATABASE Test;
GO
USE Test;
GO
CREATE TABLE dbo.Category (
  CategoryID int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED,
  CategoryName varchar(50) NOT NULL UNIQUE CLUSTERED
);
GO
CREATE TABLE dbo.Product (
  ProductID int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED,
  ProductName varchar(50) NOT NULL UNIQUE CLUSTERED,
  CategoryID int NULL
);
GO
ALTER TABLE dbo.Product
ADD CONSTRAINT FK_Product_Category
FOREIGN KEY (CategoryID)
REFERENCES dbo.Category (CategoryID);
GO
-- Generujemy 1000 rekordów kategorii produktów
INSERT INTO dbo.Category (CategoryName)
SELECT 'Category ' + CONVERT(varchar(4), number)
FROM master.dbo.spt_values
WHERE type = 'P' AND number BETWEEN 1 AND 1000;
GO
-- Generujemy około 248 tys. rekordów produktów
INSERT INTO dbo.Product (ProductName, CategoryID)
SELECT
  'Product ' + CONVERT(varchar(7), Q.n),
  CASE WHEN Q.n % 1000 = 0 THEN 1 ELSE Q.n % 1000 END
FROM (
  SELECT DISTINCT v1.number * v2.number AS n
  FROM master.dbo.spt_values AS v1
  CROSS JOIN master.dbo.spt_values AS v2
  WHERE v1.number BETWEEN 1 AND 1000 AND v1.type = 'P'
  AND v2.number BETWEEN 1 AND 1000 AND v2.type = 'P'
) AS Q
ORDER BY Q.n;
GO

Przypadek 1. – SELECT

Rozpatrzmy następujące zapytanie SELECT:

SELECT c.CategoryName, p.ProductName
FROM dbo.Category AS c
LEFT JOIN dbo.Product AS p
ON c.CategoryID = p.CategoryID
WHERE c.CategoryName = 'Category 1000';

Ma ono za zadanie wybranie wszystkich produktów należących do kategorii o nazwie „Category 1000” (a jeżeli do tej kategorii nie należy żaden produkt, powinien pojawić się jeden rekord z pustą nazwą produktu). Takich produktów w tabeli dbo.Product nie ma. Ale mimo to zapytanie wykonywane jest w zauważalnie długim czasie, ponieważ brak indeksu na kolumnie CategoryID powoduje, że musi nastąpić przeskanowanie (odczyt wszystkich rekordów) tabeli dbo.Product. Plan zapytania wygląda następująco:

clip_image001[7]

Po założeniu, za pomocą poniższego kodu, indeksu na kolumnie CategoryID, w planie nie widzimy już skanowania tabeli produktów wykonywanego w pętli, a samo zapytanie wykonuje się błyskawicznie.

CREATE INDEX IX_Product_CategoryID
ON dbo.Product (CategoryID);

clip_image002[7]

Przypadek 2. – UPDATE

Ale indeks może posłużyć nie tylko do optymalizacji zapytań SELECT. Dzięki niemu można także spowodować, że operacje modyfikacji danych będą wykonywać się szybciej. Weźmy takie polecenie UPDATE:

UPDATE p
SET p.CategoryID = 1
FROM dbo.Category AS c
INNER JOIN dbo.Product AS p
ON c.CategoryID = p.CategoryID
WHERE c.CategoryName = 'Category 1000';

Celem jest przypisanie produktów, dotąd należących do „Category 1000”, do nowej kategorii o identyfikatorze 1. Jeśli na kolumnie CategoryID w tabeli dbo.Product brak indeksu, przy wykynywaniu powyższego polecenia UPDATE praktycznie cały koszt jest generowany przez skanowanie tabeli dbo.Product oraz operatora złączenia typu Nested Loops.

clip_image003[7]

Ale jeżeli dodany zostanie indeks (taki sam, jak w przypadku 1.), skanowanie zastępowane jest przez operację jego przeszukania. Sama modyfikacja danych będzie wykonywana szybciej, na skutek szybszego znalezienia rekordów do zmiany. Odpowiedni fragment planu zapytania po założeniu indeksu:

clip_image004[7]

Przypadek 3. – DELETE

Także operacje kasowania danych mogą być optymalizowane przez założenie indeksu na kolumnie, będącej kluczem obcym. Przykładowe polecenie DELETE:

DELETE FROM dbo.Category
WHERE CategoryName = 'Category 1000';

W planie wykonania, w przypadku braku indeksu na kolumnie CategoryID w tabeli dbo.Product, pojawia się skanowanie całej tabeli w poszukiwaniu rekordów produktów, należących do „Category 1000” (nawet, jeśli takich produktów nie ma, przejrzana zostaje cała tabela). Jest to spowodowane tym, że w tabeli dbo.Product istnieje klucz obcy, odwołujący się do tabeli dbo.Category i uniemożliwiający usuwanie rekordu kategorii, jeżeli ma ona przypisany choć jeden produkt. clip_image006[7]

Jakie korzyści daje założenie indeksu na kolumnie (-ach) klucza obcego w tabeli dbo.Product (takiego samego indeksu, jak w przypadkach 1. i 2.)?

clip_image007[7]

Tym razem operacja wykonuje się szybciej, ponieważ zamiast skanowania tabeli dbo.Product następuje przeszukanie indeksu na kolumnie CategoryID w tej tabeli.

Wnioski daleko idące

Nie zawsze indeksy na kolumnach, będących kluczami obcymi, pozwalają na optymalizację zapytań. Ale na pewno kolumny takie powinny być uwzględniane jako propozycje kluczy indeksów. Przykłady z tego artykułu pokazują, że takim indeksem można także przyspieszyć modyfikacje danych (choć spotyka się ogólne i niezbyt trafne stwierdzenia, że indeksy zawsze powodują spowolnienie takich operacji).

[PL] Konkurs T-SQL – liga piłkarska

VN:F [1.7.9_1023]
Rating: 5.0/5 (6 votes cast)

image

Obiecałem, że jak tylko będę miał możliwość, zorganizuję konkurs T-SQL inspirowany cyklicznymi konkursami T-SQL Challenge. I słowa dotrzymuję :-)

Dane wejściowe

Dane są dwie tabele o strukturach, jak poniżej:

Tabela dbo.Teams:

TeamId      TeamName
----------- --------------------
1           Poland
2           San Marino
3           Ivory Coast
4           Luxemburg

Tabela dbo.Games:

HomeId      VisitorId   HomeGoals   VisitorGoals
----------- ----------- ----------- ------------
1           2           0           0
1           3           2           0
1           4           1           0
2           1           1           1
2           3           1           1
2           4           2           1
3           1           2           0
3           2           1           0
3           4           0           1
4           1           0           1
4           2           0           0
4           3           2           0

Tabela dbo.Teams zawiera identyfikatory (TeamId) oraz nazwy (TeamName) drużyn piłkarskich rywalizujących ze sobą na zasadzie klasycznej ligi (każdy z każdym po dwa mecze – mecz u siebie i mecz na wyjeździe). Tabela dbo.Games zawiera wyniki meczów – identyfikator drużyny gospodarzy (HomeId), identyfikator drużyny gości (VisitorId), liczbę bramek strzelonych przez gospodarzy (HomeGoals) oraz liczbę bramek strzelonych przez gości (VisitorGoals).

Opis zadania

  1. Wynikiem działania zapytania stworzonego w ramach konkursu ma być tabela wyniku rywalizacji drużyn zawierająca kolumny:- Position – pozycja drużyny w tabeli – nie ma miejsc ex-aequo,- Team – nazwa drużyny,- P – liczba rozegranych przez drużynę spotkań,- W – liczba zwycięstw drużyny,- D – liczba remisów drużyny,- L – liczba porażek drużyny,- F – liczba bramek zdobytych przez drużynę,- A – liczba bramek straconych przez drużynę,- Points – liczba punktów zdobytych przez drużynę zgodnie z regułą: 3 punkty za zwycięstwo, 1 punkt za remis oraz 0 punktów za porażkę.
  2. O kolejności drużyn w tabeli decydują kolejno:- liczba punktów – im więcej, tym lepiej,- różnica między bramkami strzelonymi a straconymi – im większa, tym lepiej,- liczba bramek strzelonych – im większa, tym lepiej,- liczba bramek straconych – im mniejsza, tym lepiej,- tabela wyników bezpośrednich spotkań – jeśli dwie lub więcej drużyn mają taką samą pozycję w oparciu o powyższe kryteria, o kolejności decyduje tabela wyników zbudowana wyłącznie o wyniki bezpośredniej rywalizacji między zainteresowanymi drużynami (i stosujemy w tej tabeli takie same kryteria, jak powyżej – czyli zaczynamy od punktów, potem bramki etc.), w tabeli tej bramki strzelone na wyjazdach liczymy podwójnie (ale do liczenia punktów oczywiście bramki liczymy normalnie, bez podwajania tych strzelonych na wyjazdach),- jeżeli dwie lub więcej drużyn po zastosowaniu powyższych kryteriów kwalifikują się do zajęcia tego samego miejsca, o kolejności decyduje TeamId – zespół o mniejszym TeamId w tabeli znajdzie się przed zespołem o większym TeamId (załóżmy, że TeamId to miejsce w rankingu federacji piłkarskiej).

Oczekiwany wynik

Position Team        P W D L F A Points
-------- ----------- - - - - - - ------
1        Poland      6 3 2 1 5 3 11
2        San Marino  6 1 4 1 4 4 7
3        Luxemburg   6 2 1 3 4 4 7
4        Ivory Coast 6 2 1 3 4 6 7

Skypt generujący przykładowe dane

Skrypt do wygenerowania przykładowych danych:

IF OBJECT_ID(N'dbo.Games', 'U') IS NOT NULL
  DROP TABLE dbo.Games;
IF OBJECT_ID(N'dbo.Teams', 'U') IS NOT NULL
  DROP TABLE dbo.Teams;
CREATE TABLE dbo.Teams (
  TeamId int NOT NULL,
  TeamName varchar(20) NOT NULL
);
CREATE TABLE dbo.Games (
  HomeId int NOT NULL,
  VisitorId int NOT NULL,
  HomeGoals int NOT NULL,
  VisitorGoals int NOT NULL
);
INSERT INTO dbo.Teams (TeamId, TeamName)
VALUES (1, 'Poland');
INSERT INTO dbo.Teams (TeamId, TeamName)
VALUES (2, 'San Marino');
INSERT INTO dbo.Teams (TeamId, TeamName)
VALUES (3, 'Ivory Coast');
INSERT INTO dbo.Teams (TeamId, TeamName)
VALUES (4, 'Luxemburg');

INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (1, 2, 0, 0);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (1, 3, 2, 0);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (1, 4, 1, 0);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (2, 1, 1, 1);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (2, 3, 1, 1);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (2, 4, 2, 1);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (3, 1, 2, 0);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (3, 2, 1, 0);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (3, 4, 0, 1);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (4, 1, 0, 1);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (4, 2, 0, 0);
INSERT INTO dbo.Games (HomeId, VisitorId, HomeGoals, VisitorGoals)
VALUES (4, 3, 2, 0);

Zasady konkursu

  1. Rozwiązaniem ma być pojedyncze zapytanie T-SQL rozpoczynające się od słowa “SELECT”, “WITH” lub “;WITH”.
  2. W zapytaniu można używać podzapytań i wszystkich elementów języka T-SQL dostępnych w SQL Server 2008 R2.
  3. W zapytaniu można użyć tabeli liczb, której definicja jest podana tutaj. W skrypcie nie umieszczaj definicji tabeli liczb (załóż, że będzie ona dostępna w bazie testowej).
  4. Zapytania, które przejdą próbę poprawności zwracanego wyniku (testy będą prowadzone na nieco większej ilości danych), będą klasyfikowane wg następujących kryteriów: czas wykonania (duration), zużycie procesora (CPU), ilość odczytów (reads) oraz ilość zapisów (writes). Zwycięży rozwiązanie, które łącznie zdobędzie najwięcej punktów we wszystkich czterech kryteriach.
  5. Rozwiązanie (możesz ich wysłać więcej niż jedno) wyślij mailem w postaci skryptu o rozszerzeniu .sql na adres: pawel.potasinski[małpa]sqlpass.org. W tytule maila wpisz “Konkurs T-SQL”. Otrzymasz ode mnie potwierdzenie (e-mail), jeżeli otrzymam od Ciebie rozwiązanie.
  6. W razie pytań lub uwag użyj podanego powyżej adresu e-mail lub dodaj komentarz do tego wpisu.
  7. Na rozwiązania czekam do 3 października 2010 do końca dnia. Ogłoszenie wyników przewiduję najdalej tydzień później.
  8. Konkurs w całości organizuję sam (sam wymyśliłem zadanie, sam będę testował rozwiązania, sam przyznawał nagrody etc.), a co za tym idzieTwoje ewentualne dane osobowe oglądam wyłącznie ja – Paweł Potasiński (konkurs nie ma sponsorów).

Nagrody

Nagrody to prawdopodobnie najmniej istotna część konkursu, ale chyba dobrze wiedzieć, o co walczysz :-)

1. miejsce – subskrypcja MSDN + dysk przenośny 2,5 cala o pojemności 500 GB

2. miejsce – subskrypcja MSDN

3. miejsce – książka pt. “Serwer SQL 2008. Usługi biznesowe. Analiza i eksploracja danych” z autografem Marcina Szeligi

PS. Zastrzegam sobie prawo do zmiany zestawu nagród bez uprzedzenia – np. w sytuacji, gdy uznam, że należy wyróżnić więcej niż 3 uczestników konursu :-)

Jest o co walczyć? Myślę, że tak! A i tak nie ma to jak satysfakcja z samego działającego rozwiązania! ;-)

Powodzenia i czekam na Wasze rozwiązania!

[EDYCJA: 2010-09-20]

Na prośbę uczestników i osób postronnych załączam bardziej rzeczywiste dane testowe: Primera Division 2009/2010. Visca el Barca!

[/EDYCJA]

[EDYCJA: 2010-09-21]

Udostępniam zestaw danych, który powinien pomóc Wam w przetestowaniu Waszych rozwiązań pod kątem poprawności algorytmu sortowania tabeli wynikowej. Grupa śmierci normalnie :-)

Pobierz skrypt “Tricky data”

[/EDYCJA]

[EDYCJA: 2010-09-22]

Zmyłka ;-) Dzisiejszy alarm podniesiony przeze mnie po interwencji Marka Powichrowskiego (ech, mąciwoda!) okazał się fałszywy. Oczekiwany wynik we wpisie jest OK. W tym miejscu dziękuję Leszkowi Gniadkowskiemu za to, że nie dał się zasugerować ani Markowi, ani mnie, i podjął własne śledztwo :-) Walczymy dalej! Póki co zanotowałem ledwie 4 uczestników. Czyżby wszyscy czytelnicy mojego bloga mieli już własne subskrypcje MSDN i przenośne dyski 500 GB??? ;-)

[/EDYCJA]


[EDYCJA: 2010-09-26 {BASIC TESTING}]

Przeprowadziłem  test basic na wszystkich rozwiązaniach, które dotąd otrzymałem. Poniżej rozwiązania, które przeszły test, a zarazem zwróciły poprawne wyniki dla kilku prostych zestawów danych (darujcie brak polskich liter w nazwiskach):

Gniadkowski_Leszek_v05.sql
Gniadkowski_Leszek_v06.sql
Gniadkowski_Leszek_v07.sql
Gniadkowski_Leszek_v09.sql
Gniadkowski_Leszek_v10.sql
Gniadkowski_Leszek_v12.sql
Grabowska_Katarzyna_v02.sql
Jacewicz_Lukasz_v01.sql
Pakulski_Maciej_v01.sql
Poniatowski_Aleksander_v01.sql
Powichrowski_Marek_v07.sql
Powichrowski_Marek_v10.sql
Przeliorz_Tomek_v01.sql
Waluszko_Bartlomiej_v01.sql

Każdy pewnie wie, ile wersji wysyłał, więc nie powinniście mieć problemów z odnalezieniem tych właściwych :-) Gratuluję tym sześciu osobom, którym się udało, a tych, którzy nie dali rady lub jeszcze nie nadesłali swoich rozwiązań (?), zachęcam do działania. Czasu jeszcze jest sporo ;-) Wszelkich odpowiedzi na Wasze pytania udzielam indywidualnie na maila. Test na “tricky data” już niebawem. Stay tuned!

[/EDYCJA]

[EDYCJA: 2010-09-28 {BASIC TESTING UPDATE}]

Poniżej lista rozwiązań, które pomyślnie przeszły basic testing do dzisiaj (28.09):

Cerekwicki_Cezary_v01.sql
Gniadkowski_Leszek_v05.sql
Gniadkowski_Leszek_v06.sql
Gniadkowski_Leszek_v07.sql
Gniadkowski_Leszek_v09.sql
Gniadkowski_Leszek_v10.sql
Gniadkowski_Leszek_v12.sql
Gniadkowski_Leszek_v13.sql
Grabowska_Katarzyna_v02.sql
Jacewicz_Lukasz_v01.sql
Nowakowski_Marcin_v01.sql
Pakulski_Maciej_v01.sql
Pater_Rafal_v03.sql
Poniatowski_Aleksander_v01.sql
Powichrowski_Marek_v07.sql
Powichrowski_Marek_v10.sql
Powichrowski_Marek_v11.sql
Przeliorz_Tomek_v01.sql
Rozycki_Grzegorz_v01.sql
Sowa_Piotr_v05.sql
Sowa_Piotr_v06.sql
Waluszko_Bartlomiej_v01.sql

[/EDYCJA]


[EDYCJA: 2010-09-29 {TRICKY DATA}]

Panie i Panowie, zaczynamy tricky data testing! Poniżej oddaję w Wasze ręce kolejny zestaw spreparowanych wyników. Tym razem ex-aequo jest sporo i trzeba mieć w kodzie odpowiedzi na wiele pułapek. Sprawdziłem nadesłane do dziś rozwiązania i z niekrytą satysfakcją oznajmiam, że 17 rozwiązań stworzonych przez 9 zawodników spełniło rygorystyczne wymogi tego testu :-) Poniżej lista szczęśliwców. Niewykluczone, że wymyślę jeszcze jakiś zestaw testowy, by dokładnie sprawdzić, czy mnie nie nabieracie w swoich “dziełach sztuki SQL-owej” ;-)

Cerekwicki_Cezary_v01.sql
Gniadkowski_Leszek_v07.sql
Gniadkowski_Leszek_v09.sql
Gniadkowski_Leszek_v10.sql
Gniadkowski_Leszek_v12.sql
Gniadkowski_Leszek_v13.sql
Gniadkowski_Leszek_v15.sql
Grabowska_Katarzyna_v02.sql
Nowakowski_Marcin_v01.sql
Pater_Rafal_v03.sql
Powichrowski_Marek_v07.sql
Powichrowski_Marek_v10.sql
Powichrowski_Marek_v11.sql
Przeliorz_Tomek_v01.sql
Sowa_Piotr_v05.sql
Sowa_Piotr_v06.sql
Waluszko_Bartlomiej_v01.sql

*** Pobierz zestaw danych “tricky data” ***

[/EDYCJA]

[EDYCJA: 2010-09-29 {TRICKY DATA} ]

WAŻNE!!! W tabeli dbo.Teams mogą znaleźć się drużyny, które nie rozegrały żadnego spotkania (ich identyfikatory nie występują w dbo.Games). W takiej sytuacji drużyny te mają pojawić się w tabeli wynikowej z zerami w odpowiednich kolumnach i z zachowaniem kolejności zgodnie z podanym algorytmem (czyli po prostu decyduje ranking). A jeśli takich drużyn bez rozegranego meczu jest więcej niż jedna, o ich kolejności decyduje pozycja w rankingu (TeamId).

Na razie żadne (!) z nadesłanych przez Was rozwiązań nie spełnia tej reguły, więc proszę o szybkie poprawienie kodu!

Zmieniłem w związku z tym ostatnio zamieszczony zestaw TRICKY DATA.

Poniżej będę publikował aktualną listę rozwiązań, które pomyślnie przeszły basic + tricky data test.

Cerekwicki_Cezary_v01.sql
Gailard_Pawel_v02.sql
Gniadkowski_Leszek_v10.sql
Gniadkowski_Leszek_v12.sql
Gniadkowski_Leszek_v15.sql
Grabowska_Katarzyna_v04.sql
Jacewicz_Lukasz_v02.sql
Kulczynski_Przemyslaw_v01.sql
Kulczynski_Przemyslaw_v04.sql
Nowakowski_Marcin_v03.sql
Nowakowski_Marcin_v04.sql
Pakulski_Maciej_v02.sql
Pakulski_Maciej_v03.sql
Pater_Rafal_v03.sql
Pater_Rafal_v04.sql
Poniatowski_Aleksander_v01.sql
Powichrowski_Marek_v12.sql
Powichrowski_Marek_v13.sql
Przeliorz_Tomek_v01.sql
Przeliorz_Tomek_v02.sql
Sliwa_Krzysiek_v02.sql
Sowa_Piotr_v10.sql
Waluszko_Bartlomiej_v02.sql
Zmuda_Katarzyna_v01.sql

[/EDYCJA]

[PL] Skryptowanie w SQL Server 2008 – Więzy DEFAULT i CHECK

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Intro

Kontynuuję serię poświęconą skryptowaniu obiektów w SQL Server 2008. Zapoznaj się także z poprzednimi wpisami z tej serii:

Dziś pokażę, jak można skryptować więzy wartości domyślnych (DEFAULT) i ograniczeń wartości (CHECK). Dla przypomnienia – więzy DEFAULT służą do narzucania kolumnom wartości domyślnych (gdy użytkownik nie wstawia jawnie danych w kolumnę, system wstawia wartość domyślną), zaś więzy CHECK służą do narzucenia ograniczeń wartości, jakie mogą przyjmować dane w kolumnie (stąd więzy te służą m.in. do tworzenia odpowiedników enumeracji w kolumnach w SQL Server).

Skryptowanie więzów DEFAULT

Zadanie: zeskryptować wszystkie więzy DEFAULT w wybranej bazie danych.

Rozwiązanie:

USE AdventureWorks2008R2;
GO
SET NOCOUNT ON;
SELECT
   'IF OBJECT_ID(N''' +
    REPLACE(
      QUOTENAME(SCHEMA_NAME(d.[schema_id])) + '.' + QUOTENAME(d.name),
      '''', ''''''
    ) + ''', N''D'') IS NULL
  ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(d.parent_object_id)) + '.' +
  QUOTENAME(OBJECT_NAME(d.parent_object_id)) + '
  ADD CONSTRAINT ' + QUOTENAME(d.name) + '
  DEFAULT ' + d.definition + ' FOR ' + QUOTENAME(c.name) + ';
GO'
FROM sys.default_constraints AS d
INNER JOIN sys.columns AS c
ON d.parent_object_id = c.[object_id]
AND d.parent_column_id = c.column_id
WHERE is_ms_shipped = 0;
GO

Fragment wyniku:

image

Uwagi: w Management Studio ustaw Results to Text, sugeruję też zwiększenie maksymalnej długości tekstu w jednej kolumnie zwracanej w wynikach zapytań (w menu głównym kliknij Tools – Options – Query Results – SQL Server – Results to Text i w polu Maximum number of characters displayed in each column wpisz 8192, a następnie uruchom ponownie Management Studio).

Komentarz do kodu:

  • metadane więzów DEFAULT zwraca widok systemowy sys.default_constraints,
  • nazwy kolumn zwraca widok systemowy sys.columns (jest też widok sys.all_columns, który zawiera także kolumny obiektów systemowych),
  • funkcja QUOTENAME jak zwykle służy mi do otaczania identyfikatorów nawiasami kwadratowymi,
  • skryptuję tylko obiekty, które zostały stworzone przez użytkowników (filtr na kolumnie is_ms_shipped).

Skryptowanie więzów CHECK

Zadanie: zeskryptować wszystkie więzy CHECK w wybranej bazie danych.

Rozwiązanie:

USE AdventureWorks2008R2;
GO
SET NOCOUNT ON;
SELECT
  'IF OBJECT_ID(N''' +
    REPLACE(
      QUOTENAME(SCHEMA_NAME([schema_id])) + '.' + QUOTENAME(name),
      '''', ''''''
    ) + ''', N''C'') IS NULL
  ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id)) + '.' +
  QUOTENAME(OBJECT_NAME(parent_object_id)) + '
  ADD CONSTRAINT ' + QUOTENAME(name) + '
  CHECK ' + CASE is_not_for_replication WHEN 1 THEN 'NOT FOR REPLICATION ' ELSE '' END +
  definition + ';
GO'
FROM sys.check_constraints
WHERE is_ms_shipped = 0;
GO

Fragment wyniku:

image

Uwagi: w Management Studio ustaw Results to Text, sugeruję też zwiększenie maksymalnej długości tekstu w jednej kolumnie zwracanej w wynikach zapytań (w menu głównym kliknij Tools – Options – Query Results – SQL Server – Results to Text i w polu Maximum number of characters displayed in each column wpisz 8192, a następnie uruchom ponownie Management Studio).

Komentarz do kodu:

  • metadane więzów DEFAULT zwraca widok systemowy sys.check_constraints,
  • funkcja QUOTENAME jak zwykle służy mi do otaczania identyfikatorów nawiasami kwadratowymi,
  • skryptuję tylko obiekty, które zostały stworzone przez użytkowników (filtr na kolumnie is_ms_shipped).

Podsumowanie

Skryptowanie więzów DEFAULT i CHECK jest moim zdaniem stosunkowo proste. Definicje wyrażeń determinujących działanie tych więzów są zapisane w pojedynczych kolumnach. W zasadzie sprawa sprowadza się do zbudowania odpowiedniej składni T-SQL, a to jest bodaj najprostsze. Należy pamiętać, że przed utworzeniem więzów DEFAULT i CHECK musimy zadbać, by w bazie danych istniały wszystkie funkcje skalarne, jakie owe więzy wykorzystują.

[PL] Moje wrażenia z 1st Silesian Code Camp

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Wczoraj spędziłem cały dzień w Katowicach na pierwszej w historii konferencji Silesian Code Camp zorganizowanej przez Śląską Regoinalną Grupę Microsoft (ŚRGM). Czy warto było jechać na Śląśk? Przedstawiam moje wrażenia z konferencji.

Początek konferencji to przywitanie uczestników i prelegentów w wykonaniu Damiana Widery (SQL Server MVP), jednego z liderów ŚRGM i PLSSUG Katowice. Damian w zabawnym stylu wprowadził wszystkich w dobry nastrój i opowiedział o genezie wydarzeń z serii Code Camp oraz o ideach przyświecających grupom pasjonackim w Polsce. Potem było już samo mięsko, czyli cztery sesje techniczne. A zatem po kolei…

Sesja nr 1 – Programowanie predykcyjne (prelegenci: Marcin Szeliga i Daniel Dudek)

IMAG0180

Jak może wyglądać sesja prowadzona przez duet – wybitnego specjalistę d/s baz danych (nie tylko relacyjnych) oraz zaprawionego w bojach programistę aplikacji mobilnych? Marcin Szeliga (SQL Server MVP) i Daniel Dudek nie pozostawili nikomu z obecnych wątpliwości, że taki duet może poprowadzić prezentację ciekawie i na luzie. Doświadczenie Marcina było widać gołym okiem. Daniel dopiero “dociera się” jako prelegent i – moim zdaniem – jeszcze musi trochę popracować nad warsztatem, ale i tak było widać, że pod okiem mistrza (Marcin to wg mnie jeden z lepszych prelegentów w kraju) nabiera pewności siebie i zdobywa szlify jako mówca. Na początku ich sesji trochę się bałem, że pójdą na łatwiznę i pokażą coś oklepanego i bazującego na gotowych i dobrze znanych kontrolkach (sugerowały to pierwsze dema). Na szczęście szybko przekonałem się, że obaj prelegenci wykonali kawał dobrej roboty szykując naprawdę ciekawe dema (zwłaszcza wykrywanie nieprawidłowych wartości atrybutów w aplikacji .NET i “inteligentna wstążka” były przykładami naprawdę efektownymi i oryginalnymi). I już za ten wysiłek oceniam ich prezentację bardzo wysoko. Prosty kod .NET, próba przekonania słuchaczy (chyba udana!), że DMX jest relatywnie prostym językiem, przejrzyste aplikacje – to sprawiło, że Marcina i Daniela oglądałem z dużą przyjemnością.

Ocena sesji: 9/10

Sesja nr 2 – Czy jesteśmy gotowi na SQL Azure? (prelegent: Tobiasz Koprowski)

IMAG0183 Nie ukrywam, że tej sesji bałem się jak ognia. Po pierwsze z uwagi na to, że Tobiasz dostał propozycję wystąpienia na konferencji stosunkowo niedawno, gdy okazało się, że Paula Januszkiewicz do Katowic nie może przyjechać. Po drugie, z uwagi na tematykę. Azure – “chmura”… No właśnie. Nie, żebym wydał wyrok na tę technologię i z góry skazywał ją na porażkę, ale pokazywanie technologii powszechnie uważanej za raczkującą i – póki co – oferującą niewiele możliwości, zawsze jest ryzykowne. Jak dla mnie, Tobiasz dał radę. Dał radę jako prelegent w miarę spokojnie i płynnie poprowadzić prezentację mimo problemów technicznych (woooolno działające połączenie sieciowe). Natomiast kompletnie nie dał rady przekonać mnie (i chyba kogokolwiek), że Azure to obecnie coś więcej niż piękne (?) marzenie. Może byłoby to możliwe, gdyby na wszelki wypadek nagrał sobie przed wystąpieniem demonstracje w postaci filmów. Ale tak naprawdę, to sam nie wiem. Z prezentacji Tobiasza biło po oczach jedno – SQL Azure ma milion ograniczeń i… w zasadzie nie widzę, by dawało cokolwiek w zamian za poświęcenie czasu na poznanie technologii z jej wszystkimi ograniczeniami i uwarunkowaniami (także umownymi i licencyjnymi), za wystawienie danych gdzieś w siną dal, za dokonane opłaty. Jeżeli firma Microsoft chce lansować Azure, chyba lepiej byłoby, gdyby prezentacje na temat “chmury” prowadził ktoś z pracowników korporacji. I szczerze mówiąc, byłem zdziwiony brakiem obecności kogokolwiek z Microsoft Polska na konferencji. Nawet nie tylko dlatego, że mówiono o Azure (podobno włożenie w agendę sesji o Azure było warunkiem otrzymania sponsoringu od Microsoftu???). Bardziej dlatego, że była to jak dotąd największa w tym roku kalendarzowym społecznościowa impreza, w czasie której mówiono praktycznie tylko o technologiach Microsoftu. Czyżby Microsoft w Polsce przestał dostrzegać sens wspierania społeczności? Albo może wyolbrzymiam i trzeba mnie wyprowadzić z błędu?

Ocena sesji: 5/10 (mimo problemów Tobiasz dał radę, było zabawnie, ale duży minus dla Microsoft Polska za nieobecność na konferencji)

Sesja nr 3 – Dotknij Windows (prelegent: Szymon Kobalczyk)

IMAG0190 Czego można się spodziewać, gdy na konferencję jako prelegent przyjeżdża Szymon Kobalczyk (MVP)? Widziałem już pudełka po butach, które w rękach Szymona działały niczym Microsoft Surface. Widzałem zakręcone kontrolery zbudowane z rękawiczek. Co było tym razem? Tym razem Szymon postawił na pełen profesjonalizm ;-) Przywiózł ze sobą kilka ekranów dotykowych i na żywo w bardzo efektownych demonstracjach pokazywał aplikacje dla urządzeń reagujących na dotyk. Przyznaję, że moim oczekiwaniem była właśnie prezentacja ogólna, z niekoniecznie wielką ilością kodu, ale za to z efektami “WOW” i wyjaśnieniami, jak te wszystkie zabawki działają. I dostałem od Szymona to, co chciałem zobaczyć. Po ekranie latały zdjęcia, kulki, buttony i inne elementy interfejsu. Szymon dotykał, przesuwał, macał ;-), rozśmieszał i (jak zwykle) zarażał swoją pasją. Było wszystko, co sprawia, że człowiek nie męczy się słuchając prezentera i ma wrażenie, że prelegent pokazuje coś, co po pierwsze doskonale zna i rozumie, a po drugie – co kocha. Bardzo fajna sesja, a i wyjaśnienie filozofii aplikacji dotykowych bardzo przypadło mi do gustu. Mój wielki szacunek dla Szymona za tę prezentację. Chcę takich więcej w przyszłości.

Ocena sesji: 10/10 (dla mnie TOP 1 konferencji)

Sesja nr 4 – Dziel i zwyciężaj, czyli o architekturze CQRS słów kilka (prelegent: Szymon Pobiega)

IMAG0192 Przyznaję, że temat poruszony przez kolejnego Szymona z Krakowa, Szymona Pobiegę, był mi kompletnie obcy (po pierwsze dlatego, że nie jestem architektem, po drugie z uwagi na moją nieaktualną wiedzę o projektowaniu i implementowaniu aplikacji i systemów w technologii .NET). Niemniej jednak wytrwałem i tę prezentację. I muszę powiedzieć, że miejscami była ona bardzo ciekawa. Podobało mi się między innymi bardzo przejrzyste wyjaśnianie jasnych i ciemnych stron wybranych podejść do projektowania aplikacji (m.in. bardzo ładnie Szymon pokazał metodę testowania opartą o założenie, że nie przeprowadzamy całego procesu, a startujemy w pewnym zadanym stanie). I choć w pewnych sprawach Szymon nie przekonał mnie do prezentowanego podejścia (opartego o rejestrowanie zdarzeń), to i tak odnosiłem wrażenie, że ten człowiek wie, o czym mówi i mówi o tym z dużym przekonaniem (i bez wątpienia z bagażem doświadczeń). Brakowało może takiej “kropki nad i”, która pokazałaby – “zobaczcie, to podejście zostało użyte w tej aplikacji i przyniosło w tym, tym i tamtym miejscu takie korzyści”. Ale sesja zdecydowanie na plus.

Ocena sesji: 8/10 (za to, że przykłady były dostosowane nawet dla laików oraz za pewność siebie i – w większości odpowiedzi – rzeczowe argumenty)

Jak oceniam konferencję? Wystawiam jej ocenę 8/10. Osobiście żałuję, że nie było na niej Pauli Januszkiewicz, ponieważ bardzo chciałem zobaczyć ją “w akcji” (a dotąd mi się nie udało obejrzeć ani jednej jej prezentacji “live”). Organizatorzy stanęli na wysokości zadania – była atmosfera “geek’owska”, smakowita pizza i cola (kalorie w tej ilości? na moją dietę?!), ciekawe prezentacje. I na koniec trochę prywaty – do domu wróciłem bogatszy w dwa gadżety: książkę autorstwa Marcina Szeligi i Danuty Mendrali pt. “Serwer SQL 2008. Usługi Biznesowe. Analiza i Eksploracja Danych” zasponsorowaną przez wydawnictwo Helion (egzemplarz jest unikalny, gdyż zawiera autograf Marcina Szeligi) oraz przenośnym dyskiem 2,5’’ o pojemności 500 GB zasponsorowanym przez firmę Accenture (warto było wypełnić ankietę!). Oba gadżety trafią do puli nagród w konkursie, który już niebawem na moim blogu. W tym miejscu dziękuję mojemu przyjacielowi, Markowi Adamczukowi, za książkę (zdobył ją za aktywność w czasie pierwszej sesji i przekazał mi na potrzeby konkursu) i wspólną podróż w obie strony :-)

Gratuluję ŚRGM dobrej organizacji. Mi się podobało. Za rok, jak zrobicie 2nd Silesian Code Camp, też postaram się do Was przyjechać!

[PL] VirtualStudy Conference 2010 – nagranie z mojej sesji dostępne

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Dzisiaj na portalu VirtualStudy.pl pojawiło się nagranie z mojej prezentacji pt. “Darmowe aplikacje wspierające rozwiązywanie problemów wydajnościowych w SQL Server 2008”, którą poprowadziłem w ramach konferencji wirtualnej VirtualStudy Conference 2010. Aby zobaczyć nagranie, trzeba zarejestrować się na portalu i mieć zainstalowaną wtyczkę Silverlight. Życzę przyjemnego oglądania. Poniżej podaję też odnośnik do materiałów do tej prezentacji.

Zobacz nagranie z prezentacji

Pobierz materiały do prezentacji (560 kB)

[PL] Skryptowanie w SQL Server 2008 – Klucze obce

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Intro

To już trzeci wpis z serii “Skryptowanie w SQL Server 2008″. Dwa poprzednie dostępne są tu:

Tym razem przedstawiam sposób na skryptowanie kluczy obcych. Kiedy takie skryptowanie może się przydać? Na przykład wtedy, gdy chcemy usunąć, a następnie odtworzyć owe klucze (typowy scenariusz: import wyczyszczonych i zwalidowanych danych). W podsumowaniu podam, jakie rozwinięcia mojego kodu można zaimplementować, by za pomocą praktycznie tego samego kodu wykonywać inne zadania związane z kluczami obcymi.

Skryptowanie kluczy obcych

Zadanie: zeskryptować wszystkie klucze obce w bazie danych.

Rozwiązanie:

USE AdventureWorks2008R2;
GO
SET NOCOUNT ON;

-- tu będziemy trzymać generowany kod
DECLARE @SQL TABLE (
  LineId int IDENTITY(1,1) NOT NULL PRIMARY KEY,
  Line nvarchar(4000) NOT NULL
);

DECLARE
  @object_name sysname,
  @object_id int,
  @object_schema_name sysname,
  @parent_object_name sysname,
  @parent_object_schema_name sysname,
  @is_not_for_replication bit,
  @delete_referential_action int,
  @update_referential_action int,
  @referenced_object_schema_name sysname,
  @referenced_object_name sysname,
  @column_name sysname,
  @referenced_column_name sysname,
  @columns nvarchar(4000),
  @referenced_columns nvarchar(4000);

-- kursor do wyciągania pojedynczych kluczy obcych
DECLARE CursorFK CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
  SELECT
    name,
    [object_id],
    SCHEMA_NAME([schema_id]),
    OBJECT_SCHEMA_NAME(parent_object_id),
    OBJECT_NAME(parent_object_id),
    OBJECT_SCHEMA_NAME(referenced_object_id),
    OBJECT_NAME(referenced_object_id),
    is_not_for_replication,
    delete_referential_action,
    update_referential_action
  FROM sys.foreign_keys
  WHERE is_ms_shipped = 0;
OPEN CursorFK;
FETCH NEXT FROM CursorFK
INTO @object_name, @object_id, @object_schema_name,
     @parent_object_schema_name, @parent_object_name,
     @referenced_object_schema_name, @referenced_object_name,
     @is_not_for_replication,
     @delete_referential_action, @update_referential_action;

WHILE @@FETCH_STATUS = 0 BEGIN
  SELECT @columns = '', @referenced_columns = '';

  -- generujemy sprawdzenie, czy klucz nie istnieje
  INSERT INTO @SQL (Line)
  SELECT 'IF OBJECT_ID(N''' +
        REPLACE(
          QUOTENAME(@object_schema_name) + '.' +
          QUOTENAME(@object_name), '''', ''''''
        ) + ''', N''F'') IS NULL';

  -- generujemy polecenie ALTER TABLE ... ADD CONSTRAINT
  INSERT INTO @SQL (Line)
  SELECT '  ALTER TABLE ' +
    QUOTENAME(@parent_object_schema_name) + '.' +
    QUOTENAME(@parent_object_name);
  INSERT INTO @SQL (Line)
  SELECT '  ADD CONSTRAINT ' + QUOTENAME(@object_name);

  -- kursor do wyciągania kolejnych kolumn klucza obcego
  DECLARE CursorFKColumns CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
  FOR
    SELECT
      pc.name,
      rc.name
    FROM sys.foreign_key_columns AS f
    INNER JOIN sys.columns AS pc
    ON f.parent_object_id = pc.[object_id]
    AND f.parent_column_id = pc.column_id
    INNER JOIN sys.columns AS rc
    ON f.referenced_object_id = rc.[object_id]
    AND f.referenced_column_id = rc.column_id
    WHERE f.constraint_object_id = @object_id
    ORDER BY f.constraint_column_id
  OPEN CursorFKColumns;
  FETCH NEXT FROM CursorFKColumns
  INTO
    @column_name,
    @referenced_column_name;
  WHILE @@FETCH_STATUS = 0 BEGIN
    -- doklejamy do zestawów kolumn
    SET @columns = @columns + ', ' + QUOTENAME(@column_name);
    SET @referenced_columns = @referenced_columns + ', ' + QUOTENAME(@referenced_column_name);

    FETCH NEXT FROM CursorFKColumns
    INTO
      @column_name,
      @referenced_column_name;
  END;
  CLOSE CursorFKColumns;
  DEALLOCATE CursorFKColumns;

  -- generujemy kod z listami kolumn
  INSERT INTO @SQL (Line)
  SELECT '  FOREIGN KEY (' + STUFF(@columns, 1, 2, '') + ')';
  INSERT INTO @SQL (Line)
  SELECT '  REFERENCES ' +
    QUOTENAME(@referenced_object_schema_name) + '.' +
    QUOTENAME(@referenced_object_name) + '(' +
    STUFF(@referenced_columns, 1, 2, '') + ')';

  -- akcje klucza przy usuwaniu wierszy
  IF @delete_referential_action <> 0 BEGIN
    INSERT INTO @SQL (Line)
    SELECT '  ON DELETE ' +
      CASE @delete_referential_action
        WHEN 1 THEN 'CASCADE'
        WHEN 2 THEN 'SET NULL'
        WHEN 3 THEN 'SET DEFAULT'
      END;
  END;

  -- akcje klucza przy modyfikacji wierszy
  IF @update_referential_action <> 0 BEGIN
    INSERT INTO @SQL (Line)
    SELECT '  ON UPDATE ' +
      CASE @update_referential_action
        WHEN 1 THEN 'CASCADE'
        WHEN 2 THEN 'SET NULL'
        WHEN 3 THEN 'SET DEFAULT'
      END;
  END;

  -- czy wymuszamy regułę klucza w replikacjach
  IF @is_not_for_replication = 1
    INSERT INTO @SQL (Line)
    SELECT '  NOT FOR REPLICATION';

  UPDATE @SQL
  SET Line = Line + ';'
  WHERE LineId = SCOPE_IDENTITY();

  INSERT INTO @SQL (Line) SELECT 'GO';

  FETCH NEXT FROM CursorFK
  INTO @object_name, @object_id, @object_schema_name,
     @parent_object_schema_name, @parent_object_name,
     @referenced_object_schema_name, @referenced_object_name,
     @is_not_for_replication,
     @delete_referential_action, @update_referential_action;
END;
CLOSE CursorFK;
DEALLOCATE CursorFK;
SELECT Line AS ' ' FROM @SQL ORDER BY LineId;
GO

Fragment wyniku:

Uwagi: w Management Studio ustaw Results to Text, sugeruję też zwiększenie maksymalnej długości tekstu w jednej kolumnie zwracanej w wynikach zapytań (w menu głównym kliknij Tools – Options – Query Results – SQL Server – Results to Text i w polu Maximum number of characters displayed in each column wpisz 8192, a następnie uruchom ponownie Management Studio).

Komentarz do kodu:

  • metadane kluczy obcych zwracają widoki sys.foreign_keys (metadane tabeli, w której tworzymy klucz, opcje dla poleceń DELETE / UPDATE, opcja wyłączenia klucza dla replikacji) oraz sys.foreign_key_columns (metadane kolumn oraz tabeli, do której odwołuje się klucz obcy),
  • funkcja QUOTENAME jak zwykle służy mi do otaczania identyfikatorów nawiasami kwadratowymi i apostrofami,
  • fragmenty generowanego skryptu T-SQL są dodawane w kursorze (iteracja = definicja jednego obiektu) do zmiennej tabelarycznej @SQL (linie są numerowane autonumeracją w kolumnie LineId, zakładam maksymalną długość jednej linii kodu w obiekcie na 4000 znaków),
  • rozwiązanie oparłem o dwa kursory: CursorFK – do iterowania po kluczach obcych oraz zagnieżdżony CursorFKColumns – do iterowania po kolumnach bieżącego klucza,
  • ponieważ nie jestem zwolennikiem wyłączania więzów, moje rozwiązanie nie uwzględnia kolumny is_disabled z widoku sys.foreign_keys (nie generuję klauzuli WITH NOCHECK nawet, gdy klucz jest wyłączony).

Słowo podsumowania

Powyższy kod można rozwinąć:

  • można dołożyć filtrowanie po tabelach (czy to tabeli, w której klucz jest utworzony, czy po tabeli, do której klucz się odwołuje),
  • można generować tylko składnie ALTER TABLE … DROP CONSTRAINT, jeżeli zależy nam tylko na usunięciu wybranych kluczy,
  • można wykorzystać kolumnę is_system_named z widoku sys.foreign_keys, jeżeli chcemy odnaleźć te klucze, które otrzymały nazwy wygenerowane systemowo (jeżeli nie chcemy mieć w bazie danych obiektów nazywanych dość przypadkowo).

Na koniec informacja – w repozytorium kodu umieściłem kod pseudo-systemowej procedury sp_dropforeignkeys, która może posłużyć do wygenerowania kodu usuwającego klucze obce wskazujące na wybraną tabelę. W procedurze tej wykorzystałem te same obiekty systemowe, co w powyższym kodzie, więc komentarz do niej wydaje mi się zbędny. Po stworzeniu w bazie master i oznaczeniu jako obiekt systemowy procedura powinna działać w kontekście dowolnej bazy danych.

[PL] SQL Server – Baza danych z danymi geograficznymi Polski

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Jeżeli chcesz pobawić się trochę danymi geograficznymi i raportami pokazującymi mapy w SQL Server 2008 R2, ten wpis jest specjalnie dla Ciebie. Kiedyś musiałem przygotować prezentację na temat danych przestrzennych i z tamtej prezentacji została mi baza danych Spatials, która w dwóch tabelach przechowuje kształty Polski (tabela dbo.Country) i województw (dbo.Province). Backupy daje się odtworzyć na SQL Server 2008 R2 (jest skompresowany i zajmuje około 1 MB) lub SQL Server 2008 (skompresowany plik o rozmiarze 880 kB). Poniżej przykładowy raport, jaki możesz zbudować w oparciu o dane z tej bazy.

image

Przyjemnej zabawy z danymi geograficznymi :-)

image Pobierz kopię zapasową bazy Spatials dla SQL Server 2008 (880 kB)

image Pobierz kopię zapasową bazy Spatials dla SQL Server 2008R2 (1 MB)

[EDYCJA: 2010-09-08]

Zgodnie z prośbą jednego z czytelników mojego bloga dodałem backup bazy dla SQL Server 2008.

[/EDYCJA]

[PL] Wywiad – nasi na TSQL Challenges

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

image Jakiś czas temu wysłałem rozwiązanie jednej z zagadek Itzika Ben-Gana z serii “T-SQL Puzzle” (zagadki były publikowane na witrynie SQL Server Magazine). Wtedy pomyślałem, że byłoby fajnie, gdyby takie zagadki / konkursy, w których uczestnicy mogliby poćwiczyć szare komórki i rozwijać umiejętności programowania w T-SQL, były organizowane częściej. I oto, za jakiś czas trafiłem na witrynę ByeondRelational.com prowadzoną przez Jacoba Sebastiana (SQL Server MVP) i kilku innych SQL-owych zapaleńców.

Jednym z działów BeyondRelational.com (oprócz licznych blogów i forum) jest dział TSQL Challenges. Czym są TSQL Challenges (dalej używam skrótu TC)? Są to konkursy polegające na rozwiązywaniu problemów za pomocą zapytań w języku T-SQL (nowe zadanie jest ogłaszane raz na dwa tygodnie). Udział może wziąć każdy, kto ma na to ochotę (i zarejestruje się na portalu BeyondRelational.com). Zazwyczaj zadania wymagają rozwiązania postawionego problemu jednym zapytaniem SELECT (choć na ogół dopuszczalne jest używanie podzapytań i CTE), bez użycia tabel tymczasowych i dynamicznego kodu T-SQL. Zwycięża ten uczestnik, który podeśle rozwiązanie generujące poprawny wynik, spełniające podane wymagania i o najlepszych statystykach (liczy się nie tylko czas wykonania, ale także zużycie CPU oraz ilość odczytów i zapisów). Więcej o samych TSQL Challenges i o możliwości nadsyłania własnych propozycji zadań konkursowych czytaj tu: Submit a TSQL Challenge Idea.

Zabawa jest przednia. Sam spróbowałem. Wysłałem nawet ze trzy rozwiązania zadań, ale z uwagi na to, że jak dotąd nie dałem się wciągnąć na dobre w zabawę (co by to było, jakbym jeszcze w TC dał się wciągnąć :-P), moje próby kończą się w przedbiegach po wysłaniu pierwszego rozwiązania, które uznam za “good enough” (a to jest zdecydowanie za mało na takich zawodników, jacy wysyłają swoje rozwiązania na TC) :-) Co lepsi zawodnicy nadsyłają po kilka rozwiązań wymyślając takie rozwiązania, z których można się uczyć wszystkiego, co potrzebne programiście SQL Servera: od algorytmów, przez optymalizację, aż po wymyślne chwyty do zastosowania w kodzie T-SQL. Co ciekawe, jest też wersja “light” konkursu przeznaczona dla początkujących adeptów sztuki programowania w T-SQL – TSQL Beginners Challenges.

Wśród najlepszych uczestników zabawy w “dorosłe” TC znaleźć można kilku Polaków. Dwóch z nich zaprosiłem do rozmowy o TC. Zgodzili się (dzięki, chłopaki!), a zapis wywiadu znajdziesz poniżej.

Moimi rozmówcami są Leszek Gniadkowski (w wywiadzie jako LG) oraz Marek Powichrowski (w wywiadzie jako MP).

Leszek zawodowo pracuje jako członek zespołu administrującego dosyć dużą domeną Active Directory. Specjalizuje się w systemach opartych na MS Windows, choć ma do czynienia również z innymi OS przy okazji integracji AD z usługami opartymi na *NIX/Linux.

Marek zawodowo pracuje jako programista i konsultant systemów ERP. Specjalizuje się w projektowaniu baz danych, głównie SQL Server. Pisze aplikacje w .NET, nie unika ASP.NET. Lubi projektować sałatki śledziowe i piec chleb na własnym zakwasie.

PP: Witajcie. Na początek gratulacje na okazję Waszych sukcesów w konkursach TSQL Challenges!

LG: Witam, dziękujemy, szczęśliwie trwa passa (przyp. PP – Leszek jest ex aequo pierwszy w rankingu TC!) , największym sukcesem jest jednak sama wiedza nabyta przy udziale w TC.

MP: Dzięki, to miłe. Na razie mam 13 SQL Stars i mogę już sobie zrobić T-Shirt’a z logo „TSQL Challenges Winner” :-) A i jeszcze mogę sobie wydrukować certyfikat potwierdzający ten fakt. To takie drobiazgi, ale miłe.

PP: Skąd dowiedzieliście się o konkursach TSQL Challenges i witrynie beyondrelational.com?

LG: W moim przypadku z forum portalu WSS. Dokładniej stąd. Od czasu umieszczenia tego wątku (trwał wtedy TC20), rozpocząłem naukę TSQL i próbowałem sił w następnych TC.

MP: W moim przypadku było dokładnie tak samo. Tamto zadanie zelektryzowało całe forum SQL Server na WSS.pl. Interesujące było to, że trzeba to rozwiązać w jednym zapytaniu. Pamiętam, że podobny problem rzucił kiedyś Maciej Pilecki. Piękne wyzwanie. Wysłałem swoje rozwiązanie i niejakim szokiem było to, że trzeba długo czekać na wyniki rywalizacji. Ale przeszedłem wszystkie fazy (basic test, tricky data i load test) i ostatecznie byłem sklasyfikowany poza pierwszą 10-tką, ale jak na początek to i tak było coś. Potem była przerwa i Leszek odezwał się do mnie przy zadaniu TC28, które miało bardzo mało zgłoszonych rozwiązań. Mi poszło gładko i przeszło dwie pierwsze fazy testów i kiedy już byłem w „ogródku i już witałem się z gąską” przepadłem w load test. To sparzenie się nakazuje mi teraz przyglądać się baczniej wydajności. Ale co ciekawe, Leszek który wygrał tę odsłonę był tym kompletnie zaskoczony bo wstępne porównania statystyk na naszych danych testowych dawały mi przewagę. Ale dane do load test odwróciły tę relację. Także emocje są do samego końca. Od zadania TC28 wciągnąłem się. Wykonałem jeszcze dwa zadania w tył i byłem wśród zwycięzców i od tej pory nie odpuszczam żadnego zadania.

PP: Co takiego jest w TSQL Challenges, że poświęcacie czas na podejmowanie kolejnych „wyzwań”?

MP: To głód tego pięknego momentu olśnienia, gdy znajduje się rozwiązanie proste, z pięknymi statystykami. To głód tych endorfin, które zalewają człowieka powodując, że wszystko wokoło nagle staje się piękne i nic nie jest w stanie tego zakłócić. A poza tym to świetne zajęcie dla umysłu aby przypadkiem nie zachciałoby mu się spuścić z tonu.

LG: Dobra rozrywka umysłowa, sprawdzanie się, rywalizacja. TC w pewnym stopniu uzależniają. Mix samego języka TSQL i zakręconych zadań to mieszanka gwarantująca intelektualną ucztę. Co do samego czasu, jego większość nie jest spędzona bezpośrednio przy komputerze. Nad zadaniem często myślę przy okazji, w czasie wykonywania dowolnej czynności: jazdy samochodem, posiłku, przed snem itd. Samo kodowanie zajmuje już zwykle mniej czasu.

PP: Jak dużo czasu poświęcacie średnio na stworzenie rozwiązania gotowego do zgłoszenia w konkursie i czy zdarza się, że wysyłacie więcej niż jedno rozwiązanie?

LG: Różnie, od 30 minut do dwóch tygodni na stworzenie rozwiązania, które „działa”. Najczęściej parę godzin. Później pracuje się nad ulepszeniem bądź innym podejściem do problemu podkręcającym statystyki. Zdarzało się, że były TC, do których z braku czasu podchodziłem w ostatni dzień przed terminem zakończenia, poświęcając niedzielę. Czasami wysyłam jedną wersję rozwiązania, czasami dwie i więcej. Natomiast prawie zawsze wysyłam poprawione rozwiązania spamując trochę upload.

MP: Różnie z tym bywa. Niektóre pozornie trudne rozwiązują się błyskawicznie w kilku wersjach. Inne kosztują sporo czasu. Jeżeli mam kilka rozwiązań to oczywiście je wysyłam. Trzeba pamiętać, że każde rozwiązanie zadania przechodzi przez trzy fazy weryfikacji: basic test – czyli weryfikacja rozwiązania danymi podanymi przy zadaniu, tricky data – weryfikacja danymi „pokręconymi” w celu testowania odporności rozwiązania na zakłócenia no i na koniec load test, który może wyeliminować z gry. Więc każda dodatkowa wersja rozwiązania to szansa na „przyżycie” w turnieju.

PP: Który TSQLChallenge z dotychczasowych najbardziej przypadł Wam do gustu i dlaczego?

MP: Bezwzględnie TC37! To zadanie najbardziej mnie zmęczyło ale też dało najwięcej radości gdy się wykluło wreszcie z poszukiwań mego umysłu. W samo rozwiązanie mogę się gapić i podziwiać jakie sprytne i szybkie rzeczy można robić w T-SQL-u (nawet bez użycia indeksów). Mam porównanie bo moja pierwsza wersja rozwiązania tego zadania była tragiczna pod względem wydajnościowym i to o kilka rzędów wielkości. Obserwując rozwiązania konkursowe widać, jaki power ma T-SQL, jak bardzo elastycznym jest językiem.

LG: Trudno wybrać, ale wybrałbym TC28 (przyp. PP – Leszek wygrał ten TC!). Za kompletną pustkę w głowie po zapoznaniu się z zadaniem i wiele prób wypełnienia tej pustki kierunkiem zmierzającym do utworzenia sensownej solucji.

PP: Wspominaliście mi, że wzajemnie się „nakręcacie” w czasie konkursów. W jaki sposób?

LG: Wymieniamy się statystykami, uwagami na temat TC, możliwymi „tricky data”. Jeżeli wiem, że Marek zadanie zrobił z lepszymi statystykami niż uzyskałem, mam powód do szukania innego rozwiązania. Korzystamy też z forum, jako źródła statystyk, jak i wielu dodatkowych uwag, czy omawiania samych założeń do TC.

MP: Tak to prawda. Wymieniamy się ze sobą statystykami i powoduje to sportową żyłkę rywalizacyjna między nami. A poza tym przez te kilka miesięcy kontaktów mogliśmy się lepiej poznać nie tylko na polu T-SQL’a ale również zainteresowań w innych technologiach, na przykład w robieniu własnego chleba na własnym zakwasie, piwa, jogurtu, przyrządzaniu sałatek ze śledzi. I to jest ta wartość dodana uczestnictwa w tych zawodach.

PP: Gdybyście mieli mnie zachęcić do udziału w konkursach TSQLChallenge, co byście mi powiedzieli?

MP: Poza zdobywaniem doświadczenia w trudnych wyzwaniach oraz podglądaniem i uczeniem się efektownych technik innych uczestników nie mniej istotna jest żyłka sportowa towarzysząca turniejowi. Co prawda same emocje są rozłożone w czasie tak bardzo, że można je porównać do emocji towarzyszących turniejowi szachowemu, ale jeżeli masz żyłkę sportową, która drga w Tobie, to niewątpliwie Twoje miejsce jest na TC!

LG: TC to dobre miejsce do nauki i rozwijania umiejętności w kodowaniu TSQL. W odróżnieniu od kursów i książek, które często przekazują wiedzę w sposób nudny, schematyczny, TC ukazują problem do rozwiązania, nie podają wiedzy na tacy, tylko zmuszają do samodzielnego jej poszukiwania, intensyfikują proces myślenia i umożliwiają dużo szybsze wchłanianie wiedzy. TC są również znakomitym workiem treningowym dla szarych komórek (ostatni raz ten specyficzny poziom endorfin uzyskałem w szkole średniej rozwiązując trudniejsze całki ;-) ).

PP: Dziękuję za wywiad i powodzenia w kolejnych „wyzwaniach”!

LG: A ja dziękuję za zaproszenie, miłą rozmowę i poparcie w dalszych zmaganiach z tsql’owym puzzle.

MP: Dzięki i do zobaczenia przy okazji kolejnych wyzwań T-SQL z również Twoim udziałem.

A zatem, jeżeli kręci Cię zdrowe współzawodnictwo, chcesz poćwiczyć szare komórki i przy okazji potrenować pisanie kodu T-SQL, spróbuj swoich sił w TSQL Challenges. Obserwując Marka i Leszka muszę Cię jedynie ostrzec, że to wciąga jak diabli :-) Ale jeśli jeszcze nie masz swojego ulubionego uzależnienia… ;-)

I na koniec – chcę wyrazić szacunek dla osób prowadzących portal BeyondRelational.com i konkursy TC. Publikować co dwa tygodnie nowe zadanie, czuwać nad rozwojem sytuacji w każdym konkursie, robić testy nadesłanych rozwiązań i panować nad tym wszystkim, to naprawdę duże wyzwanie dla organizatorów. I jak na razie im się to udaje! Stworzyli atmosferę zdrowej rywalizacji i swego rodzaju społeczność uczestników zabawy. Brawo za ideę i jej realizację.

PS. A już zupełnie na koniec pragnę nadmienić, że ten wywiad jest swego rodzaju zapowiedzią mojego własnego, jednorazowego konkursu a la TC, który zorganizuję na blogu. Szczegóły w drugiej połowie września. Stay tuned!