FROM – określanie źródeł danych

Nie bez powodu, klauzula FROM jest pierwszym punktem w procesie przetwarzania zapytania. Silnik bazodanowy, musi przecież wiedzieć, z jakich zbiorów będzie pobierał dane aby wykonać na nich operacje np. filtrowania rekordów (WHERE), grupowania (GROUP BY) czy sortowania (ORDER BY), zdefiniowane w kwerendzie. Musi także poznać sposób, jak wymienione zbiory ze sobą łączyć.

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

W klauzuli FROM, określane są zatem trzy bardzo istotne elementy zapytań.
Przede wszystkim, wyszczególniane są zbiory (źródła) danych (np. tabele, widoki) do których się odnosimy.

Jeśli zbiorów jest więcej niż jeden (zapytania do wielu tabel), określany jest typ relacji pomiędzy nimi a także warunki na jakich zasadach te relacje budujemy.

Ten artykuł opisuje wszystkie możliwe obiekty, które można traktować jako źródła danych i odwoływać się do nich w klauzuli FROM. Informacje o typach i warunkach połączeń źródeł (tabel, widoków etc.) znajdziesz w rozdziale o pisaniu zapytań do wielu tabel (łączenie).


Źródła danych

W klauzuli FROM, określamy źródło lub źródła danych, do których chcemy się odnieść w jakikolwiek sposób w zapytaniu. W oparciu o teorię zbiorów, przedstawię wszystkie typy źródeł, do których można się tutaj odwoływać.

Co może być źródłem danych

Źródłem danych może być dowolny, w pełni określony zbiór elementów. Czyli taki, który posiada nazwę, a jego elementy opisane są za pomocą unikalnych nazw atrybutów.
Ważne jest abyśmy tak właśnie postrzegali obiekty występujące w klauzuli FROM – jako zbiory danych – nieważne czy jedno czy wieloelementowe. Podobnie elementy tych zbiorów, mogą być opisane przez jeden lub wiele atrybutów.
Może to być zatem np. zbiór jednoelementowy :

SELECT * FROM ( SELECT 1 AS Kol1 ) AS Tabelka

FROM_01
Jak widać, zbiór został nazwany jako Tabelka. Elementy tego zbioru, określone są poprzez unikalne atrybuty (tu w zasadzie jest tylko jeden – Kol1). Zbiór jest w postaci tabeli pochodnej (derived table) czyli jako wynik podzapytania (w literaturze określany również jako tabela wirtualna). Ponieważ sam w sobie nie posiada nazwy własnej (tak jak regularne tabele), musimy mu ją nadać poprzez alias (słowo kluczowe AS).
Inny przykład zbioru :

SELECT * FROM (

	VALUES (1, 'pierwszy'), (2, 'drugi'), (3, 'trzeci') 

) AS Tabelka(Id,Nazwa)

FROM_02
Tym razem nasz zbiór zawiera 3 elementy określone dwoma atrybutami – Id oraz Nazwa. Zbiór jest nazwany – Tabelka – zatem spełnia wszystkie wymogi, aby móc go użyć w klauzuli FROM.
Operacje na podzbiorach mogą być również bardziej złożone np.

SELECT * 
FROM (
	SELECT 'raz' AS kolA
	UNION
	SELECT 'dwa'  ) AS Tabelka

FROM_03
W tym przypadku łączymy dwa bardzo proste zapytania (równie dobrze, mogłyby być bardziej skomplikowane) operatorem UNION (który dodaje dwa zbiory do siebie – wyjaśnione jest to tutaj). W wyniku będziemy mieli zbiór o nazwie Tabelka z dwoma elementami o wartościach 1 i 2. Czyli znów wszystko jest zgodne z definicją i możliwościami FROM.
Mam nadzieję, że widzisz jak wiele wspólnego ma SQL z teorią zbiorów.

Tabele, widoki

Najczęściej we FROM odwołujemy się do tabel lub widoków. Tu warto zaznaczyć, że nazwy obiektów w bazie składają się z kilku członów. Pełna nazwa każdego (full qualified name) obiektu wygląda następująco:

<nazwa_serwera>.<nazwa_bazy>.<schemat>.<nazwa_obiektu>

Trzeba pamiętać o tym, że pisząc dowolne polecenia SQL, np. kwerendę pobierającą dane z różnych tabel, powinniśmy odwoływać się po nazwie dwuczłonowej (jeśli jest to kwerenda odwołująca się do obiektów w ramach jednej bazy).

Jest to związane z możliwością ustawienia różnych schematów domyślnych dla użytkowników i jeśli chcemy, aby nasza kwerenda zwracała poprawne dane, dla każdego kto będzie ją wykonywał – musimy zadbać o jednoznaczne identyfikowanie obiektów do których się odwołujemy.

-- odwoływanie się do tabeli lub widoku po nazwie 2 członowej
-- schemat.nazwa_obiektu
select FirstName, LastName from Person.Person

-- odwoływanie się do tabeli za pomocą nazwy 3 członowej 
-- z innej bazy na tym samym serwerze
select FirstName, LastName from AdventureWorks2008.Person.Person

-- odwoływanie się do tabeli za pomocą nazwy 4 członowej 
-- do serwera zdalnego - Linked Server - nazwany HeadQuarter
select FirstName, LastName from HeadQuarter.AdventureWorks2008.Person.Person

Stosowanie w pełni kwalifikowanej, 4 członowej nazwy do określanie obiektów na serwerze lokalnym nie jest zalecane. Stosujemy je tylko wtedy, gdy faktycznie chcemy odwołać się do obiektów, będących na innym serwerze, poprzez nazwę określoną w definicji serwera powiązanego (Linked Server).

Podzapytania

Każda kwerenda w swoim wyniku zwraca zbiór elementów. Jeśli więc zadbamy, aby taki zbiór wynikowy, był określony (nazwany), z unikalnie nazwanymi atrybutami – możemy go też stosować w określeniu źródła. Jest to podstawowa i bardzo często używana w praktyce właściwość SQL.

Na przykład zapytanie z zapytania – czemu nie ? Przecież przed chwilą powiedzieliśmy, że każda kwerenda (tu zapytanie wewnętrzne), zwraca nam zbiór :

select * from (
	select * from HumanResources.Department
	) a

FROM_04
Takie zapytanie, oczywiście nie ma zbyt dużego sensu, ale chodzi o ideę.

W takim razie może bardziej sensowny przykład. Chcemy np. uzyskać informacje o 3 najdroższych zleceniach dla każdego ze sprzedawców (o ile w ogóle coś sprzedali). Wykorzystam do realizacji tego zadania funkcję szeregującą ROW_NUMBER. Za jej pomocą ponumeruję zlecenia według ich wartości, dla każdego ze sprzedawców osobno. Następnie w zapytaniu zewnętrznym, bazującym na zbiorze zwróconym przez tą kwerendę, odfiltruję tylko 3 najdroższe zamówienia per sprzedawca :

USE AdventureWorks2008
Go

SELECT *
FROM 
   (  -- zbiór elementów jako wynik kwerendy (podzapytania)
   
    SELECT o.TotalDue, p.BusinessEntityID, p.LastName, p.FirstName , 
     ROW_NUMBER() OVER(Partition by p.BusinessEntityID Order by o.TotalDue DESC) as BestNo
   
    FROM [Person].[Person] p inner join [Sales].[SalesOrderHeader] o 
	on p.BusinessEntityID= o.SalesPersonID

   ) as TabelaPosrednia

WHERE BestNo<4

FROM_05
Sposobów na rozwiązanie tego samego problemu, bywa wiele. Są lepsze, gorsze, alternatywne.

W tym przypadku moglibyśmy równie dobrze jak podzapytania, użyć np. wyrażenia tablicowego CTE. Chodzi o to abyś zapamiętał fakt, że we FROM, możesz odwoływać się do dowolnych nazwanych zbiorów, będących np. wynikiem podzapytania.

Funkcje tabelaryczne

To obiekty programistyczne, które na podstawie przyjmowanych argumentów (parametrów), zwracają określony zbiór elementów w postaci tabeli wynikowej (VT). Zatem również pasują do naszej definicji zbiorów i możemy ich używać tak jak zwykłe tabele.
W SQL Server mamy dostępne funkcje tabelaryczne systemowe np. OPENROWSET – jest to funkcja do czytania zewnętrznych źródeł danych takich jak plik Excel (opisuję jej możliwości w tym zakresie w artykule import Excela do bazy za pomocą T-SQL), OPENQUERY itp..

Możemy również tworzyć własne funkcje UDF (User Defined Function), zwracające w wyniku tabelę – opisane są one w rozdziale 4.

Przykład odwołania się do funkcji tabelarycznej użytkownika w klauzuli FROM:

USE AdventureWorks2008
Go

select * from  dbo.ufnGetContactInformation(273)

FROM_06
Tutaj funkcja użyta jest jak widok sparametryzowany, na podstawie numeru, zwraca pewne informacje. Z pewnością można by było zastąpić taką funkcję widokiem, ale bywają sytuacje, w których chcemy wykorzystać rozszerzone możliwości programistyczne, jakie oferują funkcje. Wynik działania takiej funkcji, traktujemy jak zwykły zbiór danych (tak jak zwykłą tabelę).

Rozszerzenia funkcjonalne FROM

Istnieje jeszcze kilka innych funkcjonalności, stosowanych jako rozszerzenia klauzuli FROM.
Są to np. polecenia PIVOT / UNPIVOT wprowadzone w wersji 9 (SQL Server 2005) służące do tworzenia / przekształcania tabel przestawnych.

Podsumowanie

W artykule tym, chciałem zwrócić Twoją uwagę na fakt, że we FROM, możesz używać różnych obiektów, będącymi zbiorami elementów, nie tylko nazw tabel czy widoków.
Wyjście poza standardy, już na etapie budowania podstawowej wiedzy, pozwala szybciej zrozumieć prawdziwy sens języka SQL. Podkreślam wiele razy w tym kursie, że jeśli będziesz patrzył na SQL, jak na język operacji na zbiorach – pisanie zapytań stanie się prostsze i będzie intuicyjne !

Jest to jednak preludium do pełnego zrozumienia FROM. Esencją realcyjnych baz jest łączenie danych z różnych zbiorów (tabel, widoków i innych opisanych tutaj obiektów).
Wyjaśnienie tych zagadnień, umieściłem w dalszej części tego kursu, czyli w artykule o łączeniu danych z różnych tabel (zbiorów).

8 Responses

  • Przy wyjaśnieniu działania operatora UNION jest taki zapis:
    …(który dodaje dwa zbiory do siebie – wyjaśnione jest to tutaj)

    Zakładam, że w słowie „tutaj” miał być link 🙂

  • Cześć,

    1. Super kurs. Z góry dzięki!
    2. Jestem totalnym laikiem z programowania, dlatego pytam:

    Co oznacza mnożnik pomiędzy select a from (select*from)?

  • To nie ma wiele wspólnego z T-SQL-em, chodzi o czysty standard SQL i wprowadzenie konstrukcji istniejącej w standardzie od dawna.

    • To prawda, że nie wszystkie konstrukcje standardu są wdrażane w dialektach. Czasem są stosowane znacznie później…. a czasem wcale.

      Będąc ściśłym, jeśli chodzi o VALUES jako definicji elementów zbioru, miałem na myśli ewolucję dialektu (nie standardu) T-SQL i jego implementacji w wersjach 9 i 10. To wiąże się bezpośrednio z T-SQL 🙂

      Wszystkie przykłady, artykuły w tym serwisie, bazują nie na czystym standardzie a na konkretnych wersjach dialektu T-SQL.

  • To zapytanie:
    SELECT * FROM (VALUES (1, 'pierwszy’), (2, 'drugi’), (3, 'trzeci’) ) AS Tabelka(Id,Nazwa)
    nie działa w sql 2005
    działa w sql 2008
    Dlaczego tak się dzieje?

    • Jest to tak naprawdę drobna zmiana w implementacji T-SQL. Podobnie jak funkcjonalności kolejnych odsłon SQL Server rosną, zmienia się także sam dialekt. Tego typu niuansów jest znacznie więcej i wynikają one z ewolucji standardu SQL.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.