SELECT Explanation, Example FROM Pro.Knowledge
FacebookRSS

Funkcje konwersji typów, CAST i CONVERT

Jednymi z częściej stosowanych skalarnych funkcji wbudowanych są CAST oraz CONVERT. Służą one do konwersji typów danych na inne (np. liczby na ciąg znaków). Szczególnie pomocne, gdy chcesz np. dokonać konkatenacji (złączenia) atrybutów w ciągi znakowe (stringi) z wartościami typu data czy liczbami.

W T-SQL trzeba zadbać o sensowność operacji pod względem typów danych na któych operujemy. Trudno porównać wartość tekstową ‘Seattle’ z liczbą 2. Poniższe dwa zapytania zawierają typowe błędy, związane ze znaczącą różnicą porównywnaych typów czy przekształcacnych danych (składanie stringu).

use northwind
go
 
-- błąd konwersji w warunku WHERE 
-- porównanie wartości tekstowej z liczbą
select * from dbo.Employees
where City = 2
 
-- błąd konwersji w SELECT
-- próba połączenia stringów z typem danych date/time
select LastName + ' urodzony : ' + BirthDate
from dbo.Employees
Msg 245, Level 16, State 1, Line 3
Conversion failed when converting the nvarchar value 'Seattle' to data type int.

Msg 241, Level 16, State 1, Line 3
Conversion failed when converting date and/or time from character string.

W niektórych przypadkach, można działać na różniących się typach z tej samej rodziny. Wtedy gdy możliwa jest niejawna konwersja – np. wartość liczbowa typu całkowitego oraz zmiennoprzecinkowa. Jeśli jednak chcemy być pewni wyniku, powinniśmy zawsze zatroszczyć się o pełną zgodność typów. Funkcje CAST oraz CONVERT służą właśnie do osiągnięcia tego celu.


CAST(wartość_konwertowana AS typ_danych) jest podstawową funkcją konwersji zgodną ze standardem ANSI. Jej działanie ogranicza się do bezpośredniej konwersji danej wartości na inny typ danych podany jako drugi parametr funkcji (po słowie kluczowym AS). Z uwagi na jej kompatybilność z ANSI, powinna być stosowana zawsze, gdy nie potrzeba określać styli (np. w konwersji dat).

Jej możliwości są jednak nieco ograniczone w stosunku do CONVERT, ale w wielu sytuacjach nie ma to znaczenia. Możemy stosować je wymiennie.

Konwersję używamy np. do łączenia danych różnego typu, chcąc uzyskać w rezultacie ciąg znakowy.

USE Northwind
GO
 
SELECT LastName + ' hired : ' +  CAST (HireDate as varchar(100)) as EmpInfo,
       HireDate
FROM dbo.Employees;

FN_CAST_01
Zauważ, że w tym przypadku wartość daty została przekonwertowana na określony styl. Funkcja CAST nie posiada możliwości wyboru stylu, dlatego jeśli chcemy otrzymać samą datę (w formacie YYYY-MM-DD), powinniśmy w tej sytuacji skorzystać z funkcji CONVERT ze wskazanym stylem.


CONVERT (typ_danych, wartość_konwertowana, opcjonalnie_styl) potrafi wszystko to co CAST, dodatkowo możemy określić styl typu danych. Ma on znaczenie zwłaszcza w przypadku konwersji na typy danych związanych z datą i czasem, a także liczbowe (określenie precyzji), XML i binarne. Szczegółowy opis styli znajdziesz tutaj (MSDN).

USE Northwind
GO
 
SELECT 'Hired : ' +  CAST (HireDate as varchar(10)) as konwersjaCast,
        CONVERT(varchar(10),HireDate ,120) as KonwersjaConvertStyles, 
        CAST(CAST (HireDate as date) as varchar(10)) as DblCast,
        HireDate
FROM dbo.Employees

FN_CAST_02
Podobne operacje musimy wykonać jeśli chcemy łączyć ze sobą wartości liczbowe i tekstowe. Poniższy przykład obnaża po raz kolejny ograniczenia CAST (tym razem związane z zaokrąglaniem wartości).

USE AdventureWorks2008
GO
 
SELECT salesOrderId, 'Cast  = ' + CAST(TotalDue as varchar(100)) + 
       '; Convert = ' + CONVERT(varchar(100),TotalDue,2) as Summary, 
       TotalDue
FROM Sales.SalesOrderHeader
WHERE TotalDue BETWEEN 123 AND 124

FN_CAST_CONVERT_03
Trzeba pamiętać, że w ten sposób wykonywana konwersja, musi być możliwa dla wszystkich wartości w danej kolumnie. W przeciwnym razie, jeśli chodzi dla jednej wartości nie będzie możliwa zmiany typu danych – zapytanie zakończy się niepowodzeniem. Zdarza się tak zazwyczaj w „zabrudzonych” bazach, w których dane są niespójne na wskutek np. importów z pominięciem reguł itp. akcji.


W SQL Server 2012 wprowadzono nowe funkcje związane z konwersją typów danych. Szczególnie przydatne to – TRY_CAST oraz TRY_CONVERT. Ich pojawienie upraszcza „czyszczenie danych”.
Funkcje te działają identycznie do CAST oraz CONVERT z tym że dla danych, dla których konwersja nie jest możliwa, zamiast błędu, zwracana jest wartość NULL.

Funkcje matematyczne w SQL

Wbudowane, skalarne funkcje matematyczne, służą do operacji na liczbach. Poniżej znajdziesz opis kilku wybranych, najczęściej używanych.

ROUND ( wartość_liczbowa, precyzja ) – zaokrągla wartość liczbową, zmiennoprzecinkową do zadenj precyzji.

USE AdventureWorks2008
GO
 
SELECT SalesOrderId, TotalDue, 
       ROUND(TotalDue,2) as TotalDueRounded 
FROM Sales.SalesOrderHeader
WHERE TotalDue BETWEEN 123 AND 124

FN_Maths_01_ROUND


RAND () – funkcja pseudolosowa, zwraca liczbę typu float z zakresu 0-1. Łatwo w oparciu o nią stworzyć generator liczb pseudolosowych zwracający liczby losowe z określonego przedziału.

-- przedział 10 - 20
SELECT 10 + CONVERT(INT, (20-10+1) * RAND())
-- Przedział 0-10
SELECT 0 + CONVERT(INT, (10-0+1) * RAND())

Poniżej przykład skryptu w którym celem sprawdzenia działania generatora, wrzucam do tabeli tymczasowej #test, 1000 losowych wartości z przedziału 1-100. Na koniec sprawdzam MAX, MIN i średnią z tych wartości.
Średnia powinna dążyć do 51 – im więcej rekordów tym bliżej tej wartości (zakładając rozkład jednorodny).

IF (OBJECT_ID('tempdb.dbo.#test') is not null ) drop table dbo.#test;
 
CREATE TABLE dbo.#test
( 
	wart int
)
GO
 
SET NOCOUNT ON
GO
 
DECLARE @min int,@max int
-- określenie zakresu przedziału
SELECT @min = 1, @max =100
 
INSERT INTO dbo.#test(wart)  
-- losujemy liczby całkowite z zakresu 1-100
 
SELECT @min + CONVERT(INT, (@max - @min +1) * RAND())
GO 1000
 
SELECT MAX(wart) as MaxValue, MIN(wart) as MinValue, 
	AVG(cast(wart as float)) as Avarage, COUNT(*) as IleLiczb 
FROM dbo.#test

FN_Maths_02_RAND

Istotną cechą funkcji RAND() jest jej determinizm. Wywołana w pojedynczym batchu (logicznej jednostce), zwraca wartość losową identyczną dla każdego wiersza (podobnie jak getdate()).

Funkcja RAND() może przyjmować parametr, będący “ziarnem losowości”. Jeśli potrzebujesz wygenerować wartość losową dla KAŻDEGO wiersza w jednym zapytaniu, trzeba sprawić aby funkcja stała się niedeterministyczna.

with tabelka as
(
	select 1 as kol
	union
	select 2 
	union 
	select 3 
)
SELECT kol, RAND() as Rnd, getdate() as Dt, RAND(CHECKSUM(NEWID())) as RowRand
FROM tabelka

Niedeterministryczne_RAND

Temat liczb pseudolosowych jest szeroki i informacje tu zawarte są jedynie prostym przykładem ich zastosowania.


FLOOR( liczba_ułamkowa ) – to zaokrąglenie dowolnej liczby zmiennoprzecinkowej w dół – biorąc pod uwagę jej wartość ułamkową. Analogicznie działa CEILING( liczba_ułamkowa ) – zaokrągla zawsze do pełnej wartości w górę, niezależnie od wartości części po przecinku.

SELECT FLOOR (123.78) , FLOOR (-123.78) , CEILING(123.78),  CEILING(-123.78)

FN_Maths_02_FLOOR


POWER (liczba, potęga) oraz SQRT(liczba) to funkcje potęgowania oraz pierwiastkowania (kwadratowego)


Poza wyżej wymienionymi funkcjami, SQL udostępnia także wbudowane funkcje trygonometryczne, logarytmiczne i kilka innych o których możesz poczytać w np. w BOL czy na stronach MSDN.

Istotne jest aby pamiętać, że SQL to język operowania na zbiorach danych. Jeśli potrzebujesz wykonywać skomplikowane obliczenia – powinieneś wykorzystać do tego celu możliwości języków programowania do tego celu stworzonych. Dzięki integracji CLR w SQL, możesz tworzyć funkcje, procedury w środowisku .NET i wywoływać je bezpośrednio z kodu T-SQL.

Funkcje daty i czasu

Kolejną istotną grupą wbudowanych funkcji skalarnych są obiekty związane z przetwarzaniem typów danych daty i czasu.

YEAR ( data ), MONTH ( data ), DAY ( data ) – dokonują ekstraktu z daty, odpowiednio roku, miesiąca oraz dnia.

-- ekstrakt poszczególnych części daty
 
SELECT  YEAR ( '2013-02-12' ) as Rok,
        MONTH( '2013-02-12' ) as Miesiac,
        DAY  ( '2013-02-12' ) as Dzien

FN_datetime_01


Najczęściej stosowane funkcje zwracające date i czas systemowy to GETDATE() oraz SYSDATETIME(). Są to funkcje nie przyjmujące żadnych argumentów i zwracają po prostu bieżącą datę i czas systemowy.

-- funkcje zwracające aktualny czas i datę systemową
 
SELECT SYSDATETIME(), 
       SYSDATETIMEOFFSET(),
       GETDATE(),
       GETUTCDATE()

FN_datetime_02


DATEADD ( datepart, liczba, data ) – dodaje (lub odejmuje) liczbę jednostek daty/czasu określonych za pomocą datepart np dni (day, dd, d), lat (years,yy,yyyy), miesięcy (month,mm,m), minut (minute,mi,n) etc. do zadanej daty. Jednostki określone mogą być za pomocą pełnej nazwy, lub skrótu. Pełny ich opis znajdziesz tutaj. Stosowana często w warunkach filtracji, np. wszystkie zlecenia z ostatnich 14 dni. Funkcja DATEADD, jest też bardzo użyteczna w określaniu zakresów

-- DATEADD - dodawanie/odejmowanie jednostek określonego typu z zadanej daty
 
SELECT  DATEADD ( dd,-DAY( GETDATE()-1 ), GETDATE() ) as FirstDayCurrMonth,
	DATEADD ( dd,-DAY( GETDATE() ), GETDATE() ) as LastDayPrevMonth

FN_datetime_03


DATEDIFF ( datepart, startdate, enddate ) – różnica pomiędzy dwiema datami (end – start) wyrażona w jednostkach określonych przez datepart. Wiek pracowników :

USE Northwind
GO
 
-- DATEDIFF - określanie różnicy wyrażonej w konkretnych jednostkach 
-- pomiędzy dwiema datami
 
SELECT  FirstName, LastName, BirthDate, 	
	DATEDIFF ( yy , BirthDate , GETDATE() )  as Age
FROM dbo.Employees

FN_datetime_04


DATEPART( datepart, data ) – wyciąga określoną parametrem datepart, jednostkę podanej daty.

-- DATEPART - ekstrakt określonej części daty /czasu
 
SELECT  DATEPART( yy, GETDATE() ) as CurrentYear,
	DATEPART( mm, GETDATE() ) as CurrentMonth,
	DATEPART( dd, GETDATE() ) as CurrentDay,
	DATEPART( ww, GETDATE() ) as CurrentWeek

FN_datetime_05


DATENAME ( datepart, data ) – pododbna do DATEPART, zwraca wartość znakowa, określonej parametrem datepart, części daty w tym nazwę dnia tygodnia, miesiąca zgodnie z ustawieniami @@LANGID (bieżący język dla sesji)

SELECT  DATENAME(dw, GETDATE() ) as DzienTygodnia,
	DATENAME(mm, GETDATE() ) as Miesiac

FN_datetime_06


Funkcje tekstowe w SQL

Przekształcenia ciągów znakowych (stringów) w SQL

Funkcje operujące na ciągach znaków, są jednymi z najczęściej stosowanych. Ich nazewnictwo jest intuicyjne i często bardzo podobne lub nawet identyczne, ze stosowanym w innych językach programowania.

W tym artykule przedstawiam najczęściej używane funkcje związane z przetwarzaniem wartości ciągów znakowych typu (‘n)char/varchar.


LEFT( exp, n ) oraz RIGHT( exp, n ) – to jedne z najprostszych funkcji tekstowych. Zwracają n znaków od lewej lub prawej z wyrażenia exp na którym działa.

USE Northwind
GO
 
-- LEFT/RIGHT - n znaków z lewej / prawej strony
 
SELECT LEFT(FirstName,1) + '. ' + LastName as FullName,  
       RIGHT(HomePhone,8) as ShortPhone,
       FirstName, HomePhone 
FROM dbo.Employees

FN_Strings_01_LEFT
W tym przykładzie funkcja LEFT(FirstName,1) zwraca zawsze sensowną wartość – inicjał od imienia. Z kolei RIGHT (HomePhone,8) – zwróci zawsze tylko ostatnie 8 znaków. Jeśli byśmy chcieli wyciągnąć zmienną liczbę znaków np. od lewej (numer kierunkowy) – potrzebujemy wsparcia kolejnej funkcji, która nam namierzy ten zmienny punkt.


CHARINDEX ( exp1 , exp2 , start_location ) – szuka pierwszego wystąpienia ciągu znaków exp1 w wartości znakowej podanej jako argument exp2. Rozpoczyna poszukiwania od pozycji 1 (jeśli nie jest podana) lub innej określonej przez start_location. Zwraca numer znaku w którym rozpoczyna się pierwsze wystąpienie poszukiwanego stringu.

Połączmy więc wiedzę dotyczącą funkcji LEFT oraz CHARINDEX, do wyciągniecia informacji o numerze kierunkowym z kolumny HomePhone :

-- CHARINDEX namierza pierwsze wystąpienie znaku w stringu (zwraca numer)
 
SELECT CHARINDEX (' ', HomePhone) as FirstSpace,
       LEFT(HomePhone, CHARINDEX (' ', HomePhone)-1 ) as Direction,
       HomePhone
FROM dbo.Employees

FN_Strings_02_charindex
W zagnieżdzaniu funkcji trzeba pamiętać o jednej zasadzie. Funkcja zagnieżdzona musi zwrócić odpowiedni typ danych dla argumentu funkcji zewnętrzenej. W tym przypadku funkcja CHARINDEX – zwróciła wartość typu integer, która jest oczekiwanym typem danych, drugiego argumentu funkcji LEFT (określającym liczbę znaków).


UPPER (string) oraz LOWER(string) zamienia wszystkie litery na duże lub małe.


LEN( exp ) – funkcja zwraca wartość typu integer, równej liczbie znaków (długości) wyrażenia będącego jej argumentem. Często stosowana z innymi funkcjami – np. SUBSTRING.

-- UPPER / LOWER  - WIELKIE/ male litery, LEN - długość stringu
 
SELECT ProductName, UPPER( ProductName ) as UpperName, 
	LOWER( ProductName ) as LowerName, 
        LEN( ProductName ) NameLength
FROM dbo.Products
WHERE LEN( ProductName ) < 10

FN_Strings_03_UPPER
Zauważ, że zastosowałem tutaj funkcję LEN m.in. w warunku filtracji WHERE. Trzeba być świadomym, że takie zastosowanie, spowoduje jej uruchomienie dla każdego zwróconego rekordu dwukrotnie. Po raz pierwszy, zostanie ona wykonana dla każdego wierszy tabeli dbo.Products (w WHERE). Drugi – tylko dla tych których nazwa jest krótsza niż 10 znaków (w SELECT).

Stosowanie funkcji w warunkach filtracji może mieć znaczący wpływ na wydajność zapytań (co nie oznacza, że nie możemy ich tu stosować) – więcej o tym w ostatnim rozdziale tego kursu.


SUBSTRING ( exp , start_location , n ) – zwraca fragment tekstu, liczbę n znaków z wyrażenia podanego w parametrze exp, startując od zadanego miejsca start_location. Napiszmy zapytanie wyciągające tylko nazwę domeny z pełnego adresu URL. W tym celu musimy wyciągnąć fragment tekstu z pominięciem http:// (od ósmego znaku) do pierwszego wystąpienia znaku / (licząc również od ósmej pozycji)

-- SUBSTRING - wyciąga konrketny framgent stringu  
 
SELECT  WebPage, 
	SUBSTRING(WebPage,8, CHARINDEX('/',webPage,8)-8 ) as OnlyDomain
FROM 
(
        -- potraktuj to jak tabelę z dwoma wierszami ;)
	SELECT '/pisanie-zapytan-w-jezyku-sql-kurs/' as WebPage
	UNION
	SELECT 'https://sqlpedia.pl/kurs-sql/' 
 
) as Pages

FN_Strings_04_SUBSTRING
Łącząc ze sobą podstawowe funkcje, można osiągnąć całkiem skomplikowane przekształcenia.


RTRIM ( exp ) oraz LTRIM ( exp ) – obcięcie z prawej/lewej znaków spacji danego wyrażenia exp. Szczególnie często spotykane przy łączeniu stringów typu CHAR o stałej długości.

CREATE TABLE #tabelka
(
	opis char(10)
);
 
INSERT INTO #tabelka VALUES('jeden'),('dwa'),(' trzy'),(' cztery ');
 
-- LTRIM / RTRIM usuwa spacje z lewej / prawej strony
 
SELECT '!' + opis + '!' as Laczenie, 
       '!' + LTRIM( RTRIM(opis) ) + '!'  as Trimowane
FROM #tabelka;
 
DROP TABLE  #tabelka;

FN_Strings_05_TRIM


PATINDEX ( wzorzec, exp ) – wyszukiwanie pierwszego wystąpienia doapsowania maski – określonej wzorcem w danym wyrażeniu exp. Maska jest tworzona na tej samej zasadzie co porównywanie ciągów znakowych za pomocą operatora LIKE. Napiszmy zapytanie, wyciągające kod pocztowy ze stringu.

-- PATINDEX wyszukuje dopasowania wzorca w stringu 
-- i zwraca numer znaku, pierwszego wystąpienia
 
SELECT Adres, 
	   PATINDEX('%[0-9][0-9]-[0-9][0-9][0-9]%',Adres) as KodPocztowyStart,
	   SUBSTRING(Adres,PATINDEX('%[0-9][0-9]-[0-9][0-9][0-9]%',Adres), 6 ) as KodPocztowy
FROM
(
	-- potraktuj to jak regularną tabelę ;)
	SELECT '60-144 Poznań' as Adres 
	UNION
	SELECT 'Poznań, 60-186'
	UNION 
	SELECT 'Kod pocztowy 61-698, Poznań' 
	UNION 
	SELECT 'Poznań 61-698, Jana Pawła 16' 
 
) as Adresy

FN_Strings_06_PATINDEX
Pełną składnię tworzenia wzorców w LIKE, znajdziesz w artykule na temat filtrowania wartości znakowych w WHERE.


REPLACE ( exp , old_substring , new_substring ) – podmienia każde wystąpienie ciągu znaków old_substring na new_substring w przeszukiwanym wyrażeniu exp. Poniższy przykład, zamieni wszystkie odnalezione wystąpienia znaku „-„ na ciąg pusty czyli „”. Zauważ, że argumentem wejściowym funkcji REPLACE, będzie wynik działania RIGHT – czyli wartość kolumny HomePhone, obcięta do ośmiu znaków od prawej.

-- REPLACE podmienia wszystkie wystąpienia szukanego ciągu znaków, na inny
 
SELECT HomePhone , REPLACE ( RIGHT(HomePhone,8) , '-', '') as ShortPhone
FROM dbo.Employees

FN_Strings_07_REPLACE


REVERSE ( exp ) – odbicie lustrzane stringu czyli ostatni będą pierwszymi.

-- REVERSE - lustro
 
SELECT REVERSE('aidepLQS') as Mirror

FN_Strings_08_REPLACE


STUFF ( exp1, start, n, exp2 ) –usuwa n znaków wyrażenia exp1 zaczynając od pozycji określonej parametrem start. Następnie wrzuca określony ciąg znakowy exp2 do wyrażenia exp1, również od pozycji określonej w start.

-- STUFF kasuje wskazany fragment ciągu znakowego i wkleje zadany wewnątrz innego
 
SELECT STUFF('Agnieszka Maria Kwiatkowska', 3, 5, 'XXXXX');

FN_Strings_09_STUFF

HAVING – filtrowanie grup

Jest to trzeci i ostatni krok, w którym możemy filtrować elementy zbioru wynikowego. Różni się zasadniczo od poznanych do tej pory, związanych z selekcją wierszy we FROM (gdzie filtrem są warunki złączeń z innymi tabelami) oraz WHERE.

(5)     SELECT
(1)	FROM
(2)	WHERE
(3)	GROUP BY
(4)     HAVING(6)     ORDER BY

Operacja grupowania opisana w artykule na temat GROUP BY, wprowadza pewne ograniczenia. W każdym kolejnym kroku po GROUP BY, odwoływać się możemy bezpośrednio tylko do atrybutów (kolumn) sekcji grupującej. Do pozostałych kolumn (sekcji danych surowych) tylko za pośrednictwem funkcji agregujących.

HAVING jest kolejnym krokiem po GROUP BY – działamy zatem na całych grupach wierszy. Jest to tak zwana selekcja pozioma grup wierszy. Warunki określone w WHERE, traktujemy jako selekcję poziomą pojedynczych rekordów.


Sposób działania HAVING

Najlepiej pokazać to na przykładzie. W naszym scenariuszu, będziemy działać na bazie Northwind. Załóżmy że potrzebujemy wyciągnąć informacje o miastach w Brazylii, w których mamy więcej niż jednego Klienta (z tabeli dbo.Customers).

Pierwszym krokiem, będzie selekcja pozioma wierszy (WHERE) do odfiltrowania wszystkich Klientów z Brazylii. Działanie tego filtra jest proste, wybieramy precyzyjnie tylko te pojedyncze rekordy, których Country = ’Brazil’. Fragment działania filtra w WHERE obrazuje poniższy zrzut :
Having_01
Otrzymamy wyselekcjonowane wiersze zgodnie z definicją filtra w WHERE.

select Country, CIty, CustomerID , ContactName, CompanyName  
from dbo.Customers
WHERE Country = 'Brazil'

Having_02
Kolejnym krokiem analizy naszych danych, niech będzie wyciągnięcie informacji o liczbie Klientów z danego miasta w Brazylii.

select CIty, COUNT(CustomerID) as CustQty
from dbo.Customers
WHERE Country = 'Brazil'
GROUP BY City

Having_03
Filtrowanie w HAVING, polega na filtrowaniu całych grup rekordów. Zgodnie z zasadą opisaną na początku artykułu, możemy filtrować po kolumnach grupujących lub pozostałych, za pośrednictwem funkcji agregujących. W tym momencie, chcemy właśnie filtrować grupy rekordów, ze względu na ilość elementów (liczby klientów) w ich ramach.

Cel ten zrealizuje filtrowanie za pomocą HAVING. Filtrem będzie wynik funkcją agregującej COUNT(), wybierający tylko te grupy, dla których ilość wierszy (Klientów) będzie większa od 1.

select City, COUNT(CustomerID) as CustQty
from dbo.Customers
WHERE Country = 'Brazil'
GROUP BY City
HAVING COUNT(CustomerID)>1

Having_04
Tworzenia filtrów w HAVING podobnie jak w WHERE, umożliwia łączenie wielu warunków ze sobą, za pomocą operatorów logicznych AND i OR. Powyższe zapytanie, moglibyśmy również zapisać w ten sposób :

select Country,City, COUNT(CustomerID) as CustQty
from dbo.Customers
GROUP BY Country, City
HAVING Country = 'Brazil' AND COUNT(CustomerID) >1

Wynik działania będzie identyczny. Zauważ jednak, że istnieje różnica logiczna w jego przetworzeniu. Przynajmniej teoretycznie, w tym przypadku, całość filtracji odbędzie się tylko w kroku HAVING.
W praktyce optymalizator i tak zastosuje filtrację w pierwszym kroku przetwarzania, minimalizując liczbę rekordów, które będzie przetwarzał w kolejnych etapach. Zobaczyć można to na planie wykonania. Obydwa zapytania posiadają identyczny plan.
Having_06
Having_05


Podsumowanie

Ważne jest abyś dobrze zrozumiał, w jaki sposób wykonywane są zapytania. Pozwala to zapobiec popełnianiu, najtrudniej wykrywalnych błędów logicznych.

Pamiętać należy również, że funkcje agregujące pomijają w kalkulacjach wartości null. Więcej na temat grupowania, having oraz stosowania funkcji agregujacych znajdziesz w rozdziale opisującym szerzej, praktyczne aspekty pisania zapytań.