W moim poprzednim artykule na temat podstawowego operatora przestawnego widzieliśmy, jak można go użyć do konwersji wierszy na kolumny, czego wynikiem są tabele przestawne. Widzieliśmy, że tworzenie tabeli przestawnej składało się z trzech głównych kroków. Pierwszym krokiem było wybranie danych bazowych. Drugim krokiem było przekonwertowanie danych bazowych na wyrażenie z wartościami przechowywanymi w tabeli, a ostatnim krokiem było zastosowanie operatora przestawnego do danych tymczasowych, co zaowocowało powstaniem tabeli przestawnej.
Spójrz na poniższy przykład.
USE schooldb SELECT * FROM (SELECT city, total_score FROM student ) AS StudentTable PIVOT( AVG(total_score) FOR city IN ([London],[Liverpool],[Leeds],[Manchester]) ) AS StudentPivotTable
Uwaga: Aby utworzyć fikcyjną bazę danych i dane, zapoznaj się z poprzednim artykułem na temat operatora przestawnego.
Ograniczenia operatora przestawnego
Istnieją jednak pewne ograniczenia operatora obrotu. Wewnątrz operatora przestawnego musimy określić pole zagregowane i kolumny, na których chcemy przestawić nasze dane. Na koniec musimy również ustawić indywidualne wartości dla nagłówków kolumn, które chcemy utworzyć.
Gdybyśmy wykonali skrypt z poprzedniej sekcji, otrzymalibyśmy następujący wynik:
[identyfikator tabeli=35 /]
Nagłówki kolumn to poszczególne wartości w kolumnie miasta. Określiliśmy te wartości wewnątrz operatora przestawnego w naszym zapytaniu.
Najbardziej żmudną częścią tworzenia tabel przestawnych jest ręczne określanie wartości nagłówków kolumn. Jest to część, która jest podatna na większość błędów, zwłaszcza jeśli zmienią się dane w źródle danych online. Nie możemy być pewni, że wartości określone w operatorze przestawnym pozostaną w bazie danych, dopóki nie utworzymy tej tabeli przestawnej następnym razem.
Na przykład w naszym skrypcie określiliśmy Londyn, Liverpool, Leeds i Manchester jako wartości nagłówków naszej tabeli przestawnej. Te wartości istniały w kolumnie Miasto w tabeli uczniów. Co się stanie, jeśli w jakiś sposób co najmniej jedna z tych wartości zostanie usunięta lub zaktualizowana? W takich przypadkach zostanie zwrócony null.
Lepszym rozwiązaniem byłoby utworzenie dynamicznego zapytania, które zwróci pełny zestaw wartości z kolumny, z której próbujesz wygenerować tabelę przestawną.
Tworzenie dynamicznej tabeli przestawnej
W tej sekcji zobaczymy, jak utworzyć dynamiczną tabelę przestawną.
Oznacza to, że nie będziemy musieli ręcznie określać wartości dla kolumny, z której próbujemy wygenerować naszą tabelę przestawną. Zamiast tego ustawimy te wartości dynamicznie. W tym celu użyjemy funkcji „NAZWA KWOTOWANIA”.
Jak zawsze, upewnij się, że masz dobrą kopię zapasową, zanim zaczniesz eksperymentować z nowym kodem. Zobacz ten artykuł na temat tworzenia kopii zapasowych baz danych MS SQL, jeśli nie masz pewności.
Funkcja QUOTENAME
Funkcja „NAZWA KWOTOWANIA” formatuje wybrane wyniki. Przed wyjaśnieniem dynamicznego obrotu warto przyjrzeć się szybkiemu przykładowi działania funkcji „NAZWA WYWAŻENIA”.
Spójrz na następujące zapytanie.
USE schooldb SELECT QUOTENAME(city)+ ',' FROM student
Domyślnie funkcja „NAZWA KWOTOWANIA” otacza wybrane elementy nawiasami kwadratowymi. Wynik powyższego zapytania wygląda tak:
[identyfikator tabeli=36 /]
Przechowywanie nazw kolumn w zmiennej
Mimo że otoczyliśmy wartości kolumn w nawiasy kwadratowe, musimy określić wartości w operatorze przestawnym w następującym formacie:
„[Leeds],[Liverpool],[Londyn],[Manchester]”
Aby to zrobić, potrzebujemy zmiennej.
USE schooldb DECLARE @CityNames NVARCHAR(MAX) = '' SELECT @CityNames += QUOTENAME(city)+ ',' FROM ( SELECT DISTINCT city FROM student ) AS CITIES PRINT @CityNames
W powyższym zapytaniu zadeklarowaliśmy zmienną „@CityNames” i zainicjalizowaliśmy ją pustym ciągiem. Następnie użyliśmy instrukcji SELECT, aby wybrać różne nazwy miast z kolumny miasto i zapisać je iteracyjnie w zmiennej „@CityNames”. W każdej iteracji do zmiennej „@CityNames” zostanie dodana odrębna wartość w kolumnie miasta wraz z przecinkiem.
Następnie wydrukowaliśmy wartość przechowywaną w tej zmiennej. Wynik powyższego zapytania będzie wyglądał tak:
„[Leeds],[Liverpool],[Londyn],[Manchester]”
Jeśli spojrzysz na dane wyjściowe, po ostatniej wartości znajduje się przecinek. Nie potrzebujemy tego.
Usuwanie końcowego przecinka
Aby usunąć końcowy przecinek, użyjemy funkcji LEFT, która jako pierwszy argument przyjmuje ciąg znaków. Drugim argumentem jest liczba znaków, które mają zostać zwrócone z tego łańcucha, zaczynając od pierwszego znaku. Spójrz na następujące zapytanie:
USE schooldb DECLARE @CityNames NVARCHAR(MAX) = '' SELECT @CityNames += QUOTENAME(city)+ ',' FROM ( SELECT DISTINCT city FROM student ) AS CITIES SET @CityNames = LEFT(@CityNames, LEN(@CityNames)-1) PRINT @CityNames
Tutaj zwróć uwagę na ten wiersz skryptu:
SET @CityNames = LEFT(@CityNames, LEN(@CityNames)-1)
W tej linii skryptu użyliśmy funkcji LEFT, aby pobrać wszystkie znaki po lewej stronie wartości przechowywanej w zmiennej „@CityNames”, zaczynając od pierwszego elementu. W drugim argumencie użyliśmy funkcji LEN do obliczenia liczby elementów wartości przechowywanych w funkcji „@CityNames” i na koniec odjęliśmy od niej 1. Spowoduje to usunięcie końcowego przecinka z ciągu. Wynik będzie wyglądał tak:
[Leeds],[Liverpool],[Londyn],[Manchester]
Konwertowanie zapytania SQL na ciąg
Teraz, miejmy nadzieję, możemy użyć zmiennej „@CityNames” w naszym operatorze przestawnym w ten sposób:
PIVOT( AVG(total_score) FOR city IN ( @CityNames )
Nie możemy jednak użyć zmiennej wewnątrz naszego operatora przestawnego. Alternatywnym podejściem jest przekonwertowanie naszego kompletnego zapytania SQL na łańcuch. Wewnątrz tego ciągu zahaczymy naszą zmienną „@CityNames”.
USE schooldb DECLARE @CityNames NVARCHAR(MAX) = '' DECLARE @Query NVARCHAR(MAX) = '' SELECT @CityNames += QUOTENAME(city)+ ',' FROM ( SELECT DISTINCT city FROM student ) AS CITIES SET @CityNames = LEFT(@CityNames, LEN(@CityNames)-1) SET @Query = 'SELECT * FROM (SELECT city, total_score FROM student ) AS StudentTable PIVOT( AVG(total_score) FOR city IN (' + @CityNames +') ) AS StudentPivotTable' PRINT @Query
Tutaj zadeklarowaliśmy zmienną „@Query” i zapisaliśmy w niej nasze zapytanie SQL. Wewnątrz operatora przestawnego połączyliśmy wartość przechowywaną w zmiennej „@CityNames”. Aby zobaczyć, jak wygląda wykonane zapytanie, wydrukowaliśmy wartość zmiennej „@Query”. Wynikowe zapytanie będzie wyglądać tak na wyjściu:
SELECT * FROM (SELECT city, total_score FROM student ) AS StudentTable PIVOT( AVG(total_score) FOR city IN ([Leeds],[Liverpool],[London],[Manchester]) ) AS StudentPivotTable
To jest dokładnie typ zapytania, które chcemy wykonać. Jest to jednak w formacie String. Ostatnim krokiem jest wykonanie tego zapytania SQL zapisanego jako ciąg tekstowy. W tym celu użyjemy Dynamic SQL.
Wykonywanie dynamicznego SQL
Używamy wbudowanej procedury „sp_executesql” do wykonywania dynamicznego SQL. Użyjemy tej procedury składowanej do wykonania zapytania przechowywanego w zmiennej @Query. Nasze ostatnie zapytanie, które tworzy dynamiczną tabelę przestawną, wygląda tak:
USE schooldb DECLARE @CityNames NVARCHAR(MAX) = '' DECLARE @Query NVARCHAR(MAX) = '' SELECT @CityNames += QUOTENAME(city)+ ',' FROM ( SELECT DISTINCT city FROM student ) AS CITIES SET @CityNames = LEFT(@CityNames, LEN(@CityNames)-1) SET @Query = 'SELECT * FROM (SELECT city, total_score FROM student ) AS StudentTable PIVOT( AVG(total_score) FOR city IN (' + @CityNames +') ) AS StudentPivotTable' EXECUTE sp_executesql @Query
Po wykonaniu powyższego zapytania powinieneś zobaczyć następujący wynik:
[identyfikator tabeli=37 /]
Jednak tym razem nie określiliśmy ręcznie wartości nagłówków tabeli przestawnej. Zamiast tego nagłówki zostały obliczone dynamicznie, co daje dynamiczną tabelę przestawną.