SQL nad Chinook databází pro MSSQL, Oracle a MySQL/MariaDB

Jeden SQL příklad, tři databázové platformy

Tahle stránka ukazuje SQL příklady nad testovací databází Chinook. U důležitých dotazů najdeš variantu pro SQL Server, Oracle i MySQL/MariaDB.

Mini ukázka: TOP / FETCH / LIMIT

MSSQL
SELECT TOP 10
	Name AS TrackName
	,UnitPrice
FROM Track
ORDER BY Name;
Oracle
SELECT
	Name AS TrackName
	,UnitPrice
FROM Track
ORDER BY Name
FETCH FIRST 10 ROWS ONLY;
MySQL/MariaDB
SELECT
	Name AS TrackName
	,UnitPrice
FROM Track
ORDER BY Name
LIMIT 10;

Co je Chinook databáze?

Chinook je testovací databáze digitálního hudebního obchodu. Hodí se pro výuku SELECTu, JOINů, agregací, filtrování i práce s datem.

Hudební katalog

Tabulky Artist, Album, Track, Genre, MediaType, Playlist a PlaylistTrack.

Zákazníci

Tabulka Customer obsahuje zákazníky, kontakty, adresy, zemi a obchodního zástupce.

Faktury

Tabulky Invoice a InvoiceLine umožňují řešit tržby, počty prodejů a položky faktur.

Zaměstnanci

Tabulka Employee ukazuje zaměstnance, nadřízené a obchodní zástupce zákazníků.

Nejdůležitější vazby

Artist.ArtistId = Album.ArtistId
Album.AlbumId = Track.AlbumId
Track.GenreId = Genre.GenreId
Track.MediaTypeId = MediaType.MediaTypeId

Customer.CustomerId = Invoice.CustomerId
Invoice.InvoiceId = InvoiceLine.InvoiceId
Track.TrackId = InvoiceLine.TrackId

Employee.EmployeeId = Customer.SupportRepId
Employee.EmployeeId = Employee.ReportsTo

Playlist.PlaylistId = PlaylistTrack.PlaylistId
Track.TrackId = PlaylistTrack.TrackId

Nejčastější rozdíly syntaxe

Toto jsou rozdíly, které uživatelé nejčastěji potkají při přechodu mezi MSSQL, Oracle a MySQL/MariaDB.

Situace MSSQL Oracle MySQL/MariaDB
Prvních 10 řádků SELECT TOP 10 FETCH FIRST 10 ROWS ONLY LIMIT 10
Spojení textu FirstName + ' ' + LastName FirstName || ' ' || LastName CONCAT(FirstName, ' ', LastName)
Aktuální datum GETDATE() SYSDATE NOW()
Zaokrouhlení ROUND(value, 2) ROUND(value, 2) ROUND(value, 2)
Alias sloupce AS AliasName AS AliasName AS AliasName
Podmínka textu LIKE '%Rock%' LIKE '%Rock%' LIKE '%Rock%'
Datum z faktury YEAR(InvoiceDate) EXTRACT(YEAR FROM InvoiceDate) YEAR(InvoiceDate)
Praktická poznámka: SELECT, FROM, JOIN, WHERE, GROUP BY, HAVING a ORDER BY vypadají velmi podobně. Nejvíc se liší omezení počtu řádků, práce s textem, datumové funkce a některé systémové funkce.

SQL příklady nad Chinook

Každý blok obsahuje variantu pro MSSQL, Oracle a MySQL/MariaDB.

1. Prvních 10 skladeb

Zobraz sloupečky TrackId, Name pojmenovaný jako TrackName a UnitPrice. Zobraz prvních 10 řádků z tabulky Track.

MSSQL

TOP
SELECT TOP 10
	TrackId
	,Name AS TrackName
	,UnitPrice
FROM Track
ORDER BY Name;

Oracle

FETCH
SELECT
	TrackId
	,Name AS TrackName
	,UnitPrice
FROM Track
ORDER BY Name
FETCH FIRST 10 ROWS ONLY;

MySQL/MariaDB

LIMIT
SELECT
	TrackId
	,Name AS TrackName
	,UnitPrice
FROM Track
ORDER BY Name
LIMIT 10;

2. Celé jméno zákazníka

Zobraz sloupečky CustomerId, celé jméno zákazníka pojmenované jako CustomerName a Country. Výsledek seřaď podle celého jména zákazníka.

MSSQL

+
SELECT
	CustomerId
	,FirstName + ' ' + LastName AS CustomerName
	,Country
FROM Customer
ORDER BY CustomerName;

Oracle

||
SELECT
	CustomerId
	,FirstName || ' ' || LastName AS CustomerName
	,Country
FROM Customer
ORDER BY CustomerName;

MySQL/MariaDB

CONCAT
SELECT
	CustomerId
	,CONCAT(FirstName, ' ', LastName) AS CustomerName
	,Country
FROM Customer
ORDER BY CustomerName;
Důležitý rozdíl: spojování textů patří mezi první oblasti, kde se syntaxe jednotlivých databází liší. SQL Server používá operátor +, Oracle používá || a MySQL/MariaDB používá funkci CONCAT().

3. Skladby včetně alba, interpreta a žánru

Zobraz název skladby jako TrackName, název alba jako AlbumTitle, název interpreta jako ArtistName a název žánru jako GenreName. Výsledek omez na 50 řádků a seřaď podle interpreta, alba a skladby.

MSSQL

JOIN
SELECT TOP 50
	t.Name AS TrackName
	,al.Title AS AlbumTitle
	,ar.Name AS ArtistName
	,g.Name AS GenreName
FROM Track t
INNER JOIN Album al ON t.AlbumId = al.AlbumId
INNER JOIN Artist ar ON al.ArtistId = ar.ArtistId
INNER JOIN Genre g ON t.GenreId = g.GenreId
ORDER BY
	ar.Name
	,al.Title
	,t.Name;

Oracle

JOIN
SELECT
	t.Name AS TrackName
	,al.Title AS AlbumTitle
	,ar.Name AS ArtistName
	,g.Name AS GenreName
FROM Track t
INNER JOIN Album al ON t.AlbumId = al.AlbumId
INNER JOIN Artist ar ON al.ArtistId = ar.ArtistId
INNER JOIN Genre g ON t.GenreId = g.GenreId
ORDER BY
	ar.Name
	,al.Title
	,t.Name
FETCH FIRST 50 ROWS ONLY;

MySQL/MariaDB

JOIN
SELECT
	t.Name AS TrackName
	,al.Title AS AlbumTitle
	,ar.Name AS ArtistName
	,g.Name AS GenreName
FROM Track t
INNER JOIN Album al ON t.AlbumId = al.AlbumId
INNER JOIN Artist ar ON al.ArtistId = ar.ArtistId
INNER JOIN Genre g ON t.GenreId = g.GenreId
ORDER BY
	ar.Name
	,al.Title
	,t.Name
LIMIT 50;
Poznámka: syntaxe JOINů je v tomto příkladu mezi databázemi téměř stejná. Rozdíl je hlavně ve způsobu omezení počtu vrácených řádků.

4. Počet skladeb podle žánru

Zobraz název žánru jako GenreName a počet skladeb v daném žánru jako TrackCount. Výsledek seřaď od největšího počtu skladeb.

MSSQL

GROUP BY
SELECT
	g.Name AS GenreName
	,COUNT(t.TrackId) AS TrackCount
FROM Genre g
INNER JOIN Track t ON g.GenreId = t.GenreId
GROUP BY g.Name
ORDER BY TrackCount DESC;

Oracle

GROUP BY
SELECT
	g.Name AS GenreName
	,COUNT(t.TrackId) AS TrackCount
FROM Genre g
INNER JOIN Track t ON g.GenreId = t.GenreId
GROUP BY g.Name
ORDER BY TrackCount DESC;

MySQL/MariaDB

GROUP BY
SELECT
	g.Name AS GenreName
	,COUNT(t.TrackId) AS TrackCount
FROM Genre g
INNER JOIN Track t ON g.GenreId = t.GenreId
GROUP BY g.Name
ORDER BY TrackCount DESC;
Společná syntaxe: tento dotaz má stejnou podobu pro SQL Server, Oracle i MySQL/MariaDB. Základní práce s GROUP BY je proto dobře přenositelná mezi databázemi.

5. Žánry s více než 100 skladbami

Zobraz název žánru jako GenreName a počet skladeb jako TrackCount. Zobraz pouze žánry, které mají více než 100 skladeb.

MSSQL

HAVING
SELECT
	g.Name AS GenreName
	,COUNT(t.TrackId) AS TrackCount
FROM Genre g
INNER JOIN Track t ON g.GenreId = t.GenreId
GROUP BY g.Name
HAVING COUNT(t.TrackId) > 100
ORDER BY TrackCount DESC;

Oracle

HAVING
SELECT
	g.Name AS GenreName
	,COUNT(t.TrackId) AS TrackCount
FROM Genre g
INNER JOIN Track t ON g.GenreId = t.GenreId
GROUP BY g.Name
HAVING COUNT(t.TrackId) > 100
ORDER BY TrackCount DESC;

MySQL/MariaDB

HAVING
SELECT
	g.Name AS GenreName
	,COUNT(t.TrackId) AS TrackCount
FROM Genre g
INNER JOIN Track t ON g.GenreId = t.GenreId
GROUP BY g.Name
HAVING COUNT(t.TrackId) > 100
ORDER BY TrackCount DESC;
Častá chyba: agregační funkce jako COUNT() nelze běžně filtrovat pomocí WHERE. Pro filtrování výsledků po seskupení slouží HAVING.

6. Tržby podle zákazníka

Zobraz celé jméno zákazníka jako CustomerName, zemi zákazníka a celkové tržby jako TotalSales. Výsledek seřaď podle tržeb od nejvyšších.

MSSQL

+
SELECT
	c.FirstName + ' ' + c.LastName AS CustomerName
	,c.Country
	,SUM(i.Total) AS TotalSales
FROM Customer c
INNER JOIN Invoice i ON c.CustomerId = i.CustomerId
GROUP BY
	c.FirstName
	,c.LastName
	,c.Country
ORDER BY TotalSales DESC;

Oracle

||
SELECT
	c.FirstName || ' ' || c.LastName AS CustomerName
	,c.Country
	,SUM(i.Total) AS TotalSales
FROM Customer c
INNER JOIN Invoice i ON c.CustomerId = i.CustomerId
GROUP BY
	c.FirstName
	,c.LastName
	,c.Country
ORDER BY TotalSales DESC;

MySQL/MariaDB

CONCAT
SELECT
	CONCAT(c.FirstName, ' ', c.LastName) AS CustomerName
	,c.Country
	,SUM(i.Total) AS TotalSales
FROM Customer c
INNER JOIN Invoice i ON c.CustomerId = i.CustomerId
GROUP BY
	c.FirstName
	,c.LastName
	,c.Country
ORDER BY TotalSales DESC;

7. Tržby podle interpreta

Zobraz název interpreta jako ArtistName a celkové tržby za jeho skladby jako TotalSales. Výsledek seřaď podle tržeb od nejvyšších.

MSSQL

SUM
SELECT
	ar.Name AS ArtistName
	,SUM(il.UnitPrice * il.Quantity) AS TotalSales
FROM InvoiceLine il
INNER JOIN Track t ON il.TrackId = t.TrackId
INNER JOIN Album al ON t.AlbumId = al.AlbumId
INNER JOIN Artist ar ON al.ArtistId = ar.ArtistId
GROUP BY ar.Name
ORDER BY TotalSales DESC;

Oracle

SUM
SELECT
	ar.Name AS ArtistName
	,SUM(il.UnitPrice * il.Quantity) AS TotalSales
FROM InvoiceLine il
INNER JOIN Track t ON il.TrackId = t.TrackId
INNER JOIN Album al ON t.AlbumId = al.AlbumId
INNER JOIN Artist ar ON al.ArtistId = ar.ArtistId
GROUP BY ar.Name
ORDER BY TotalSales DESC;

MySQL/MariaDB

SUM
SELECT
	ar.Name AS ArtistName
	,SUM(il.UnitPrice * il.Quantity) AS TotalSales
FROM InvoiceLine il
INNER JOIN Track t ON il.TrackId = t.TrackId
INNER JOIN Album al ON t.AlbumId = al.AlbumId
INNER JOIN Artist ar ON al.ArtistId = ar.ArtistId
GROUP BY ar.Name
ORDER BY TotalSales DESC;
Důležité: při výpočtu tržeb podle skladby, alba, interpreta nebo žánru je potřeba vycházet z tabulky InvoiceLine. Tabulka Invoice obsahuje souhrn za celou fakturu, která může mít více položek.

8. Tržby podle roku faktury

Zobraz rok faktury jako InvoiceYear a součet tržeb jako TotalSales. Výsledek seřaď podle roku.

MSSQL

YEAR()
SELECT
	YEAR(InvoiceDate) AS InvoiceYear
	,SUM(Total) AS TotalSales
FROM Invoice
GROUP BY YEAR(InvoiceDate)
ORDER BY InvoiceYear;

Oracle

EXTRACT
SELECT
	EXTRACT(YEAR FROM InvoiceDate) AS InvoiceYear
	,SUM(Total) AS TotalSales
FROM Invoice
GROUP BY EXTRACT(YEAR FROM InvoiceDate)
ORDER BY InvoiceYear;

MySQL/MariaDB

YEAR()
SELECT
	YEAR(InvoiceDate) AS InvoiceYear
	,SUM(Total) AS TotalSales
FROM Invoice
GROUP BY YEAR(InvoiceDate)
ORDER BY InvoiceYear;
Rozdíl v práci s datem: i jednoduché získání roku z datového sloupce se může mezi databázemi lišit. SQL Server a MySQL/MariaDB používají YEAR(), Oracle používá EXTRACT().

9. CASE výraz pro délku skladby

Zobraz název skladby jako TrackName, její délku v milisekundách a nově vytvořenou kategorii délky jako TrackLengthCategory. Výsledek seřaď od nejdelších skladeb a zobraz pouze 30 řádků.

MSSQL

CASE
SELECT TOP 30
	Name AS TrackName
	,Milliseconds
	,CASE
		WHEN Milliseconds < 180000 THEN 'Krátká skladba'
		WHEN Milliseconds BETWEEN 180000 AND 300000 THEN 'Běžná skladba'
		ELSE 'Dlouhá skladba'
	END AS TrackLengthCategory
FROM Track
ORDER BY Milliseconds DESC;

Oracle

CASE
SELECT
	Name AS TrackName
	,Milliseconds
	,CASE
		WHEN Milliseconds < 180000 THEN 'Krátká skladba'
		WHEN Milliseconds BETWEEN 180000 AND 300000 THEN 'Běžná skladba'
		ELSE 'Dlouhá skladba'
	END AS TrackLengthCategory
FROM Track
ORDER BY Milliseconds DESC
FETCH FIRST 30 ROWS ONLY;

MySQL/MariaDB

CASE
SELECT
	Name AS TrackName
	,Milliseconds
	,CASE
		WHEN Milliseconds < 180000 THEN 'Krátká skladba'
		WHEN Milliseconds BETWEEN 180000 AND 300000 THEN 'Běžná skladba'
		ELSE 'Dlouhá skladba'
	END AS TrackLengthCategory
FROM Track
ORDER BY Milliseconds DESC
LIMIT 30;
CASE výraz je dobře přenositelná konstrukce. Ve všech třech databázích umožňuje vytvářet vlastní textové nebo číselné kategorie podle podmínek.

10. Zaměstnanec a jeho nadřízený

Zobraz celé jméno zaměstnance jako EmployeeName, jeho pracovní pozici jako EmployeeTitle a celé jméno jeho nadřízeného jako ManagerName. Použij propojení tabulky Employee sama na sebe.

MSSQL

Self JOIN
SELECT
	e.FirstName + ' ' + e.LastName AS EmployeeName
	,e.Title AS EmployeeTitle
	,manager.FirstName + ' ' + manager.LastName AS ManagerName
FROM Employee e
LEFT JOIN Employee manager ON e.ReportsTo = manager.EmployeeId
ORDER BY EmployeeName;

Oracle

Self JOIN
SELECT
	e.FirstName || ' ' || e.LastName AS EmployeeName
	,e.Title AS EmployeeTitle
	,manager.FirstName || ' ' || manager.LastName AS ManagerName
FROM Employee e
LEFT JOIN Employee manager ON e.ReportsTo = manager.EmployeeId
ORDER BY EmployeeName;

MySQL/MariaDB

Self JOIN
SELECT
	CONCAT(e.FirstName, ' ', e.LastName) AS EmployeeName
	,e.Title AS EmployeeTitle
	,CONCAT(manager.FirstName, ' ', manager.LastName) AS ManagerName
FROM Employee e
LEFT JOIN Employee manager ON e.ReportsTo = manager.EmployeeId
ORDER BY EmployeeName;
Self JOIN znamená, že se tabulka propojí sama se sebou. Typicky se používá pro hierarchie, například zaměstnanec → nadřízený.

11. INSERT, UPDATE, DELETE - testovací interpret

Vlož testovacího interpreta do tabulky Artist, následně uprav jeho název a nakonec testovací záznam smaž.

MSSQL

DML
INSERT INTO Artist (
	Name
)
VALUES (
	'Test Artist'
);

UPDATE Artist
SET Name = 'Test Artist Updated'
WHERE Name = 'Test Artist';

DELETE FROM Artist
WHERE Name = 'Test Artist Updated';

Oracle

DML
INSERT INTO Artist (
	Name
)
VALUES (
	'Test Artist'
);

UPDATE Artist
SET Name = 'Test Artist Updated'
WHERE Name = 'Test Artist';

DELETE FROM Artist
WHERE Name = 'Test Artist Updated';

MySQL/MariaDB

DML
INSERT INTO Artist (
	Name
)
VALUES (
	'Test Artist'
);

UPDATE Artist
SET Name = 'Test Artist Updated'
WHERE Name = 'Test Artist';

DELETE FROM Artist
WHERE Name = 'Test Artist Updated';
Poznámka k právům: příkazy INSERT, UPDATE a DELETE vyžadují práva pro zápis. Uživatelé s oprávněním pouze pro čtení obvykle pracují hlavně s příkazy typu SELECT.

Rychlý tahák pro výuku

Stručné shrnutí, co se mezi databázemi mění a co zůstává stejné.

Většinou stejné

  • SELECT, FROM, WHERE
  • INNER JOIN, LEFT JOIN
  • GROUP BY, HAVING
  • ORDER BY
  • COUNT(), SUM(), AVG(), MIN(), MAX()
  • CASE WHEN ... THEN ... ELSE ... END
  • INSERT, UPDATE, DELETE v základní podobě

Často rozdílné

  • Omezení počtu řádků: TOP, FETCH, LIMIT
  • Spojování textu: +, ||, CONCAT()
  • Datumové funkce: GETDATE(), SYSDATE, NOW()
  • Získání roku z data: YEAR() vs. EXTRACT()
  • Automatické číslování ID
  • Procedury a funkce: T-SQL, PL/SQL, SQL/PSM nebo specifická syntaxe

Doporučený výklad pro studenty

Téma Jednoduché vysvětlení Příklad nad Chinook
SELECT Vyberou se konkrétní sloupce z tabulky. Skladby z tabulky Track.
WHERE Filtrují se řádky před seskupením. Zákazníci z Czech Republic.
JOIN Spojují se tabulky podle společného klíče. Track → Album → Artist.
GROUP BY Vytváří se souhrn podle vybraného sloupce. Počet skladeb podle žánru.
HAVING Filtrují se až hotové skupiny. Jen žánry s více než 100 skladbami.
CASE Vytváří se vlastní kategorie podle podmínky. Krátká, běžná a dlouhá skladba.
Datum Každá databáze může mít trochu jinou syntaxi. Tržby podle roku faktury.
DML Příkazy pro vložení, úpravu a smazání dat. Testovací interpret v Artist.
Doporučené členění: pro přehledné studium je vhodné rozdělit SQL látku na základní dotazy, rozdíly mezi databázemi a praktické úlohy nad konkrétní testovací databází.