Database
 sql >> Baza danych >  >> RDS >> Database

Kontynuacja Summer Performance Palooza 2013

27 czerwca PASS Performance Virtual Chapter zorganizował Summer Performance Palooza 2013 – rodzaj zmniejszonych 24 godzin PASS, ale skupionych wyłącznie na tematach związanych z wydajnością. Prowadziłem sesję zatytułowaną „10 złych nawyków, które mogą zabić wydajność”, omawiając 10 następujących pojęć:

  1. WYBIERZ *
  2. Ślepe indeksy
  3. Brak przedrostka schematu
  4. Domyślne opcje kursora
  5. przedrostek sp_
  6. Zezwalanie na rozrost pamięci podręcznej
  7. Szerokie typy danych
  8. Domyślne ustawienia serwera SQL
  9. Nadużywanie funkcji
  10. „Działa na moim komputerze”

Być może pamiętasz niektóre z tych tematów z takich prezentacji, jak mój wykład „Złe nawyki i najlepsze praktyki” lub nasze cotygodniowe seminaria internetowe dotyczące dostrajania zapytań, które prowadzę z Kevinem Klinem od początku czerwca do tego tygodnia. (Nawiasem mówiąc, te 6 filmów będzie dostępnych na początku sierpnia w YouTube).

Na moją sesję wzięło udział 351 osób i otrzymałem świetne opinie. Chciałem poruszyć niektóre z nich.

Po pierwsze, kwestia konfiguracji:używałem zupełnie nowego mikrofonu i nie miałem pojęcia, że ​​każde naciśnięcie klawisza będzie brzmiało jak grzmot. Rozwiązałem ten problem, poprawiając rozmieszczenie moich urządzeń peryferyjnych, ale chcę przeprosić wszystkich, których to dotyczy.

Następnie pliki do pobrania; pokład i próbki są publikowane na miejscu imprezy. Znajdują się na dole strony, ale możesz je również pobrać tutaj.

Na koniec poniżej znajduje się lista pytań, które zostały zadane podczas sesji i chciałem się upewnić, że odpowiedziałem na te, na które nie udzielono odpowiedzi podczas pytań i odpowiedzi na żywo. Przepraszam, że wcisnąłem to w niecały miesiąc , ale były dużo pytań i nie chciałem publikować ich w częściach.

P:Jeśli masz procedurę, która może mieć bardzo różne wartości wejściowe dla danych parametrów i w rezultacie w większości przypadków buforowany plan nie jest optymalny, najlepiej jest utworzyć procedurę WITH RECOMPILE i wziąć małą wydajność osiągana przy każdym uruchomieniu?

O: Będziesz musiał podejść do tego indywidualnie dla każdego przypadku, ponieważ będzie to naprawdę zależeć od wielu czynników (w tym złożoności planu). Należy również zauważyć, że można przeprowadzić ponowną kompilację na poziomie instrukcji w taki sposób, że tylko instrukcje, których dotyczy problem, muszą przyjąć trafienie, w przeciwieństwie do całego modułu. Paul White przypomniał mi, że ludzie często „naprawiają” wąchanie parametrów za pomocą RECOMPILE , ale zbyt często oznacza to WITH RECOMPILE w stylu 2000 zamiast znacznie lepszej OPTION (RECOMPILE) , który nie tylko ogranicza się do instrukcji, ale także umożliwia osadzanie parametrów, które WITH RECOMPILE nie. Tak więc, jeśli zamierzasz użyć RECOMPILE aby udaremnić podsłuchiwanie parametrów, dodaj je do instrukcji, a nie do modułu.

P:Jeśli użyjesz opcji rekompilacji na dynamicznym sql, czy zobaczysz duży hit wydajności?

O: Jak wyżej, będzie to zależeć od kosztów i złożoności planów i nie ma sposobu, aby powiedzieć „Tak, zawsze będzie duży hit wydajności”. Musisz także porównać to z alternatywą.

P:Jeśli istnieje indeks klastrowy w dniu insertdate, później, gdy pobieramy dane, używamy funkcji konwersji, jeśli używasz bezpośredniego porównania, data zapytania nie jest czytelna, w świecie rzeczywistym, jaki jest lepszy wybór?

O: Nie jestem pewien, co oznacza „czytelny w prawdziwym świecie”. Jeśli masz na myśli, że chcesz uzyskać dane wyjściowe w określonym formacie, zwykle lepiej jest przekonwertować na ciąg po stronie klienta. C# i większość innych języków, których prawdopodobnie używasz w warstwie prezentacji, są w stanie sformatować dane wyjściowe daty/godziny z bazy danych w dowolnym formacie regionalnym.

P:Jak określić, ile razy używany jest plan w pamięci podręcznej – czy istnieje kolumna z tą wartością lub jakieś zapytania w Internecie, które dadzą tę wartość? Na koniec, czy takie liczby będą miały znaczenie tylko od ostatniego ponownego uruchomienia?

O: Większość DMV jest ważna dopiero od ostatniego uruchomienia usługi, a nawet inne można opróżniać częściej (nawet na żądanie – zarówno nieumyślnie, jak i celowo). Pamięć podręczna planu jest oczywiście w ciągłym ruchu, a plany AFAIK, które wypadają z pamięci podręcznej, nie zachowują poprzedniej liczby, jeśli wrócą. Więc nawet gdy widzisz plan w pamięci podręcznej, nie jestem w 100% przekonany, że możesz uwierzyć w znalezioną liczbę zastosowań.

To powiedziawszy, prawdopodobnie szukasz sys.dm_exec_cached_plans.usecounts możesz również znaleźć sys.dm_exec_procedure_stats.execution_count aby pomóc w uzupełnieniu informacji o procedurach, w których poszczególne instrukcje w ramach procedur nie znajdują się w pamięci podręcznej.

P:Jakie są problemy podczas uaktualniania silnika bazy danych do nowej wersji, ale pozostawiania baz danych użytkowników w starszych trybach zgodności?

O: Głównymi problemami związanymi z tym jest możliwość użycia określonej składni, takiej jak OUTER APPLY lub zmienne z funkcją wycenianą w tabeli. Nie są mi znane przypadki, w których używanie niższej kompatybilności ma jakikolwiek bezpośredni wpływ na wydajność, ale kilka rzeczy zwykle zalecanych to przebudowa indeksów i aktualizacja statystyk (oraz aby dostawca jak najszybciej wspierał nowszy poziom kompatybilności). Widziałem, jak rozwiązało to nieoczekiwane pogorszenie wydajności w znacznej liczbie przypadków, ale słyszałem też opinie, że nie jest to konieczne, a może nawet nierozsądne.

P:Przy * czy ma to znaczenie, kiedy robisz klauzulę exist

O: Nie, przynajmniej pod względem wydajności, jeden wyjątek, w którym SELECT * nie ma znaczenia, gdy jest używany wewnątrz EXISTS klauzula. Ale dlaczego miałbyś używać * tutaj? Wolę używać EXISTS (SELECT 1 ... – optymalizator potraktuje je tak samo, ale w pewien sposób sam dokumentuje kod i zapewnia, że ​​czytelnicy rozumieją, że podzapytanie nie zwraca żadnych danych (nawet jeśli przegapią duże EXISTS na zewnątrz). Niektórzy używają NULL , i nie mam pojęcia, dlaczego zacząłem używać 1, ale znalazłem NULL trochę nieintuicyjne.

*Uwaga* musisz być ostrożny, jeśli spróbujesz użyć EXISTS (SELECT * wewnątrz modułu, który jest powiązany ze schematem:

CREATE VIEW dbo.ThisWillNotWork
WITH SCHEMABINDING
AS
  SELECT BusinessEntityID
    FROM Person.Person AS p
	WHERE EXISTS (SELECT * FROM Sales.SalesOrderHeader AS h
	  WHERE h.SalesPersonID = p.BusinessEntityID);

Pojawia się ten błąd:

Msg 1054, Poziom 15, Stan 6, Procedura ThisWillNotWork, Wiersz 6
Składnia '*' nie jest dozwolona w obiektach powiązanych ze schematem.

Jednak zmieniając go na SELECT 1 działa dobrze. Może to kolejny argument za unikaniem SELECT * nawet w tym scenariuszu.

P:Czy jest jakiś link do zasobów dla najlepszych standardów kodowania?

O: Prawdopodobnie istnieją setki w różnych językach. Podobnie jak konwencje nazewnictwa, standardy kodowania są bardzo subiektywne. Tak naprawdę nie ma znaczenia, która konwencja, którą uznasz, będzie dla Ciebie najlepsza; jeśli lubisz tbl przedrostki, zwariuj! Wolisz Pascala od bigEndianu. Chcesz poprzedzić nazwy kolumn typem danych, np. intCustomerID , nie zamierzam cię zatrzymywać. Ważniejsze jest to, że definiujesz konwencję i stosujesz ją *konsekwentnie.*

To powiedziawszy, jeśli chcesz moich opinii, nie brakuje mi ich.

P:Czy XACT_ABORT może być używany w SQL Server 2008 i nowszych?

O: Nie znam żadnych planów wycofania XACT_ABORT więc powinno nadal działać dobrze. Szczerze mówiąc, nie widzę tego zbyt często, ponieważ mamy TRY / CATCH (i THROW od SQL Server 2012).

P:W jaki sposób funkcja tabeli wbudowanej na krzyżu ma zastosowanie w porównaniu z funkcją skalarną, która została nazwana 1000x?

O: Nie testowałem tego, ale w wielu przypadkach zastąpienie funkcji skalarnej wbudowaną funkcją z wartością tabelaryczną może mieć duży wpływ na wydajność. Problem, który znalazłem, polega na tym, że wprowadzenie tego przełącznika może wymagać znacznej ilości pracy w systemie, który został napisany przed APPLY istniały lub nadal są zarządzane przez osoby, które nie przyjęły tego lepszego podejścia.

P:Mam zapytanie, które za pierwszym razem działa bardzo wolno (~1min) i szybko (~3sekundy) za każdym razem. Od czego zacząć szukać źródła problemu z wydajnością po raz pierwszy?

O: Przychodzą mi na myśl dwie rzeczy:(1) opóźnienie ma związek z czasem kompilacji lub (2) opóźnienie ma związek z ilością danych, które są ładowane w celu zaspokojenia zapytania i kiedy po raz pierwszy muszą pochodzić z dysku a nie pamięć. W przypadku (1) możesz wykonać zapytanie w Eksploratorze planów SQL Sentry, a pasek stanu pokaże czas kompilacji dla pierwszego i kolejnych wywołań (chociaż minuta wydaje się dość przesadna i mało prawdopodobna). Jeśli nie znajdziesz żadnej różnicy, może to być po prostu natura systemu:niewystarczająca pamięć do obsłużenia ilości danych, które próbujesz załadować za pomocą tego zapytania w połączeniu z innymi danymi, które już znajdowały się w puli buforów. Jeśli nie uważasz, że którykolwiek z tych problemów stanowi problem, sprawdź, czy te dwie różne egzekucje faktycznie dają różne plany — jeśli są jakieś różnice, opublikuj plany na adres Answers.sqlperformance.com, a z przyjemnością się przyjrzymy . W rzeczywistości przechwytywanie rzeczywistych planów dla obu wykonań przy użyciu Eksploratora planów w każdym przypadku może również poinformować o wszelkich różnicach we/wy i może prowadzić do tego, że SQL Server spędza swój czas przy pierwszym, wolniejszym uruchomieniu.

P:Otrzymuję podsłuchiwanie parametrów za pomocą sp_executesql, czy Optimize dla obciążeń ad hoc rozwiąże ten problem, ponieważ tylko fragment planu znajduje się w pamięci podręcznej?

O: Nie, nie sądzę, aby ustawienie Optymalizuj dla obciążeń ad hoc pomogło w tym scenariuszu, ponieważ podsłuchiwanie parametrów oznacza, że ​​kolejne wykonania tego samego planu są używane dla różnych parametrów i ze znacznie różnymi zachowaniami wydajnościowymi. Optymalizacja pod kątem obciążeń ad hoc służy do minimalizowania drastycznego wpływu na pamięć podręczną planu, który może mieć miejsce w przypadku dużej liczby różnych instrukcji SQL. Więc jeśli nie mówisz o wpływie na pamięć podręczną planu wielu różnych instrukcji, które wysyłasz do sp_executesql – co nie byłoby scharakteryzowane jako wąchanie parametrów – myślę, że eksperymentując z OPTION (RECOMPILE) może mieć lepszy wynik lub, jeśli znasz wartości parametrów, które *dają* dobre wyniki w różnych kombinacjach parametrów, użyj OPTIMIZE FOR . Ta odpowiedź Paula White'a może zapewnić znacznie lepszy wgląd.

P:Czy istnieje sposób na uruchomienie dynamicznego SQL i NIE zapisywanie planu zapytań?

O: Jasne, po prostu dołącz OPTION (RECOMPILE) w dynamicznym tekście SQL:

DBCC FREEPROCCACHE;
 
USE AdventureWorks2012;
GO
SET NOCOUNT ON;
GO
 
EXEC sp_executesql 
  N'SELECT TOP (1) * INTO #x FROM Sales.SalesOrderHeader;';
GO
EXEC sp_executesql 
  N'SELECT TOP (1) * INTO #x FROM Sales.SalesOrderDetail OPTION (RECOMPILE);'
GO
 
SELECT t.[text], p.usecounts
FROM sys.dm_exec_cached_plans AS p
CROSS APPLY sys.dm_exec_sql_text(p.[plan_handle]) AS t
WHERE t.[text] LIKE N'%Sales.' + 'SalesOrder%';

Wyniki:1 wiersz pokazujący Sales.SalesOrderHeader zapytanie.

Teraz, jeśli jakakolwiek instrukcja w partii NIE zawiera OPTION (RECOMPILE) , plan może być nadal przechowywany w pamięci podręcznej, po prostu nie można go ponownie wykorzystać.

P:Czy możesz zamiast tego użyć BETWEEN w dacie z nr 9, jeśli>=i

O: Cóż, BETWEEN nie jest semantycznie równoważny z >= AND < , ale raczej >= AND <= , optymalizuje i działa dokładnie w ten sam sposób. W każdym razie celowo nie używam BETWEEN w zapytaniach dotyczących zakresu dat – zawsze – ponieważ nie ma sposobu, aby był to zakres otwarty. Z BETWEEN , oba końce są inkluzywne, co może być bardzo problematyczne w zależności od bazowego typu danych (teraz lub z powodu jakiejś przyszłej zmiany, o której możesz nie wiedzieć). Tytuł może wydawać się nieco surowy, ale szczegółowo omówię to w następującym poście na blogu:

Co łączy MIĘDZY diabłem i diabłem?

P:Co naprawdę robi „local fast_forward” w kursorze?

O: FAST_FORWARD jest w rzeczywistości krótką formą READ_ONLY i FORWARD_ONLY . Oto, co robią:

  • LOCAL sprawia, że ​​zewnętrzne zakresy (domyślnie kursor to GLOBAL chyba że zmieniłeś opcję na poziomie instancji).
  • READ_ONLY sprawia, że ​​nie można bezpośrednio zaktualizować kursora, np. używając WHERE CURRENT OF .
  • FORWARD_ONLY uniemożliwia przewijanie m.in. za pomocą FETCH PRIOR lub FETCH ABSOLUTE zamiast FETCH NEXT .

Ustawienie tych opcji, jak zademonstrowałem (i pisałem o tym na blogu), może mieć znaczący wpływ na wydajność. Bardzo rzadko widzę kursory w środowisku produkcyjnym, które faktycznie muszą odbiegać od tego zestawu funkcji, ale zazwyczaj są one napisane tak, aby i tak akceptowały znacznie droższe wartości domyślne.

P:co jest bardziej wydajne, kursor czy pętla while?

O: WHILE pętla będzie prawdopodobnie bardziej wydajna niż równoważny kursor z domyślnymi opcjami, ale podejrzewam, że nie zauważysz różnicy, jeśli użyjesz LOCAL FAST_FORWARD . Ogólnie rzecz biorąc, WHILE pętla *jest* kursorem, którego nie nazywa się kursorem, a w zeszłym roku rzuciłem wyzwanie kilku cenionym kolegom, aby udowodnili, że się mylę. Ich WHILE pętle nie radziły sobie tak dobrze.

P:Nie polecasz prefiksu usp dla procedur przechowywanych przez użytkownika, czy ma to taki sam negatywny wpływ?

O: usp_ prefiks (lub dowolny prefiks inny niż sp_ , lub brak prefiksu w tej sprawie) *nie* ma takiego samego wpływu, jaki zademonstrowałem. Uważam jednak, że używanie prefiksu w procedurach składowanych jest mało wartościowe, ponieważ bardzo rzadko pojawia się wątpliwość, że kiedy znajdę kod, który mówi EXEC something , że coś jest procedurą składowaną – więc nie ma tam wartości (w przeciwieństwie do, powiedzmy, przedrostków widoków w celu odróżnienia ich od tabel, ponieważ można ich używać zamiennie). Nadanie każdej procedurze tego samego prefiksu znacznie utrudnia również znalezienie poszukiwanego obiektu w, powiedzmy, Eksploratorze obiektów. Wyobraź sobie, że każde nazwisko w książce telefonicznej ma przedrostek LastName_ – w jaki sposób ci to pomaga?

P:Czy istnieje sposób na wyczyszczenie planów z pamięci podręcznej, w których jest wiele kopii?

O: Tak! Cóż, jeśli korzystasz z SQL Server 2008 lub nowszego. Gdy zidentyfikujesz dwa identyczne plany, nadal będą miały oddzielny plan_handle wartości. Więc zidentyfikuj ten, którego *nie* chcesz zachować, skopiuj jego plan_handle i umieść go w tym DBCC polecenie:

DBCC FREEPROCCACHE(0x06.....);
P:Czy użycie if else itp. w proc powoduje złe plany, czy jest on optymalizowany dla pierwszego uruchomienia i optymalizowany tylko dla tej ścieżki? Czy sekcje kodu w każdym IF muszą być podzielone na osobne procedury?

O: Ponieważ SQL Server może teraz przeprowadzać optymalizację na poziomie instrukcji, ma to obecnie mniej drastyczny efekt niż w przypadku starszych wersji, w których cała procedura musiała zostać ponownie skompilowana jako jedna jednostka.

P:Czasami zauważyłem, że pisanie dynamicznego sql może być lepsze, ponieważ eliminuje problem podsłuchiwania parametrów dla sp. Czy to prawda ? Czy w przypadku tego scenariusza należy dokonać kompromisów lub innych rozważań?

O: Tak, dynamiczny SQL może często udaremniać podsłuchiwanie parametrów, szczególnie w przypadku, gdy masowe zapytanie „ulew kuchenny” zawiera wiele opcjonalnych parametrów. W powyższych pytaniach omówiłem kilka innych kwestii.

P:Gdybym miał kolumnę obliczeniową w mojej tabeli jako DATEPART(mojakolumna, rok) i w jej indeksie, czy serwer SQL używałby tego z SEEK?

O: Powinno, ale oczywiście zależy to od zapytania. Indeks może nie być odpowiedni do pokrycia kolumn wyjściowych lub spełniania innych filtrów, a używany parametr może nie być wystarczająco selektywny, aby uzasadnić wyszukiwanie.

P:czy plan jest generowany dla KAŻDEGO zapytania? Czy plan jest generowany nawet dla trywialnych?

O: O ile wiem, plan jest generowany dla każdego prawidłowego zapytania, nawet trywialnych planów, chyba że wystąpi błąd uniemożliwiający wygenerowanie planu (może się to zdarzyć w wielu scenariuszach, takich jak nieprawidłowe podpowiedzi). To, czy są przechowywane w pamięci podręcznej, czy nie (i jak długo pozostają w pamięci podręcznej), zależy od wielu innych czynników, z których niektóre omówiłem powyżej.

P:Czy wywołanie sp_executesql generuje (i ponownie wykorzystuje) plan z pamięci podręcznej?

O: Tak, jeśli wyślesz dokładnie ten sam tekst zapytania, nie ma znaczenia, czy wyślesz je bezpośrednio, czy wyślesz je przez sp_executesql , SQL Server zapisze i ponownie użyje planu.

P:Czy można wymusić regułę (dla środowiska deweloperskiego), w której wszystkie maszyny deweloperskie używają natychmiastowej inicjalizacji plików?

O: Nie rozumiem, dlaczego nie. Jedyną obawą, jaką mam, jest to, że przy natychmiastowej inicjalizacji plików programiści mogą nie zauważyć dużej liczby zdarzeń autowzrostu, co może odzwierciedlać słabe ustawienia autowzrostu, które mogą mieć bardzo różny wpływ na środowisko produkcyjne (zwłaszcza jeśli którykolwiek z tych serwerów *nie * mieć włączone IFI).

P:Czy w przypadku funkcji w klauzuli SELECT słuszne byłoby stwierdzenie, że lepiej jest zduplikować kod?

O: Osobiście powiedziałbym tak. Osiągnąłem dużo wydajności dzięki zastąpieniu funkcji skalarnych w SELECT lista z wbudowanym odpowiednikiem, nawet w przypadkach, gdy muszę powtórzyć ten kod. Jak wspomniano powyżej, w niektórych przypadkach może się okazać, że zastąpienie tego wbudowaną funkcją z wartością tabeli może dać ci możliwość ponownego wykorzystania kodu bez nieprzyjemnych spadków wydajności.

P:Czy możemy użyć generatorów danych, aby uzyskać ten sam rozmiar danych do użytku programistycznego, zamiast używać (trudnych do zdobycia) danych produkcyjnych? Czy pochylenie danych jest ważne dla wynikowych planów?

O: Pochylenie danych może mieć czynnik i podejrzewam, że zależy to od rodzaju danych, które generujesz/symulujesz i jak daleko może być pochylenie. Jeśli masz, powiedzmy, kolumnę varchar(100), która w środowisku produkcyjnym ma zwykle długość 90 znaków, a generowanie danych generuje dane o średniej wartości 50 (co zakłada SQL Server), zobaczysz znacznie inny wpływ na liczbę stron i optymalizacji oraz prawdopodobnie niezbyt realistyczne testy.

Ale będę szczery:ten konkretny aspekt nie jest czymś, w co zainwestowałem dużo czasu, ponieważ zwykle potrafię zmusić mnie do zdobycia prawdziwych danych. :-)

P:Czy wszystkie funkcje tworzone są jednakowo podczas badania wydajności zapytań? Jeśli nie, czy istnieje lista znanych funkcji, których powinieneś unikać, gdy to możliwe?

O: Nie, nie wszystkie funkcje są sobie równe pod względem wydajności. Istnieją trzy różne typy funkcji, które możemy tworzyć (na razie ignorując funkcje CLR):

  • Wielozdaniowe funkcje skalarne
  • Wieloinstancyjne funkcje z wartościami tabelarycznymi
  • Inline funkcje z wartościami tabelarycznymi
    Inline funkcje skalarne są wymienione w dokumentacji, ale są mitem i, od SQL Server Przynajmniej rok 2014 można również wymienić obok Sasquatcha i potwora z Loch Ness.

Ogólnie rzecz biorąc, gdybym mógł, umieściłbym to czcionką 80pt, wbudowane funkcje z wartościami tabelarycznymi są dobre, a innych należy unikać, jeśli to możliwe, ponieważ są znacznie trudniejsze do optymalizacji.

Funkcje mogą również mieć różne właściwości, które wpływają na ich wydajność, na przykład to, czy są deterministyczne i czy są powiązane ze schematem.

W przypadku wielu wzorców funkcji zdecydowanie należy wziąć pod uwagę wydajność, a także należy zdawać sobie sprawę z tego elementu Connect, który ma na celu ich rozwiązanie.

P:Czy możemy nadal uruchamiać podsumowania bez kursorów?

O: Tak możemy; istnieje kilka metod innych niż kursor (jak opisano w moim poście na blogu, Najlepsze podejścia do obliczania sum – aktualizacja dla SQL Server 2012):

  • Podzapytanie na liście SELECT
  • Rekurencyjne CTE
  • Samozłączenie
  • "Dziwaczna aktualizacja"
  • Tylko SQL Server 2012+:SUM() OVER() (przy użyciu domyślnej / RANGE)
  • Tylko SQL Server 2012+:SUM() OVER() (przy użyciu ROWS)

Ostatnia opcja jest zdecydowanie najlepszym podejściem, jeśli korzystasz z SQL Server 2012; jeśli nie, istnieją ograniczenia dotyczące innych opcji bez kursora, które często sprawiają, że kursor jest bardziej atrakcyjnym wyborem. Na przykład dziwaczna metoda aktualizacji jest nieudokumentowana i nie ma gwarancji, że będzie działać w oczekiwanej kolejności; rekursywne CTE wymaga, aby nie było przerw w jakimkolwiek sekwencyjnym mechanizmie, którego używasz; a podzapytania i podejścia do samodzielnego łączenia po prostu się nie skalują.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak utworzyć tabelę tymczasową w SQL?

  2. Przywracanie przykładowej bazy danych DW AdventureWorksDW2019

  3. SQL między operatorem

  4. Testowanie warstwy ODBC

  5. Unikanie sortowania za pomocą łączenia łączenia przez scalanie