AdamátorZápiskyHlášky

Aplikace SQL ⬩ 18SQL

PřednášejícíMgr. Dana Majerová, Ph.D.
Semestrzima 2025
  1. Databázové jazyky
    1. Jazyk SQL
      1. Datové typy
        1. Konstanty a literály
          1. Operátory
          2. Domény
            1. Tabulky
              1. Manipulace s daty
                1. Vkládání dat do tabulky
                  1. Změna dat v tabulce
                    1. Mazání dat v tabulce
                    2. Výběr dat z tabulky
                      1. Spojování
                        1. Kartézský součin
                          1. Vnitřní spojení
                            1. Vnější spojení
                            2. Seskupování záznamů
                              1. Poddotazy
                                1. Sjednocení výsledků
                                2. Pohledy

                                  V této konzoli si můžete zkoušet SQLite příkazy.

                                  Zaškrtnutím pole 📌 ji můžete připnout k horní části obrazovky.

                                  Je předvyplněna databáze her s tabulkami game_type, game, player, score (viz níže).

                                  Podmínkou pro získání zápočtu je vypracovat protokol obsahující:

                                  Databázové jazyky

                                  Databázové jazyky mohou sloužit pro

                                  Databázový jazyk musí obsahovat:

                                  Jazyk SQL

                                  V historii měl hodně verzí. Mezi implementacemi se liší.

                                  Všechno je strukturováno ve formě tabulek. Data jsou uložena nezávisle na formě tabulek.

                                  Datové typy

                                  Celá čísla
                                  INTEGER, BIGINT, SMALLINT, TINYINT, …
                                  Čísla s pohyblivou čárkou
                                  FLOAT, DOUBLE PRECISION, …
                                  Logické
                                  BOOLEAN
                                  Znakové
                                  CHAR(n), VARCHAR(n), …
                                  Datum a čas
                                  DATE, TIME, DATETIME, TIMESTAMP, …
                                  Rozsáhlý text
                                  TEXT, TINYTEXT, MEDIUMTEXT, LONGTEXT
                                  Binární data
                                  BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB

                                  Konstanty a literály

                                  Prázdná hodnota
                                  NULL
                                  Čísla
                                  28, 31, 123, …
                                  Řetězce
                                  'Lenka', 'Nováková', …
                                  Datum a čas
                                  '2013-02-25', '12:45:01', …

                                  Operátory

                                  Aritmetické
                                  +, -, *, /
                                  Relační
                                  =, >, <, >=, <=, <>/!=, IS NULL, IS NOT NULL, BETWEEN AND, NOT BETWEEN AND, IN, NOT IN, LIKE, NOT LIKE
                                  Logické
                                  NOT, AND, OR
                                  Řetězcové
                                  ||

                                  Přednost operátorů:

                                  1. ||
                                  2. - (unární)
                                  3. *, /
                                  4. +, -
                                  5. =, <>, >, <, >=, <=
                                  6. NOT
                                  7. AND
                                  8. OR

                                  Domény

                                  V některých dialektech SQL je možné vytvářet domény: vlastní datové typy určené omezením nějakého jiného typu. Vytváří se příkazem:

                                  CREATE DOMAIN name
                                    AS type
                                    [DEFAULT value]
                                    [NOT NULL]
                                    [CHECK(condition)]
                                    [COLLATE collation];
                                  Příklad povinně zadávaná kladná celá čísla
                                  CREATE DOMAIN identification AS INTEGER NOT NULL CHECK(VALUE > 0);
                                  Příklad povinně zadávaný výběr z možností M/F
                                  CREATE DOMAIN sex AS CHAR(1) NOT NULL CHECK(VALUE IN ('M', 'F'));
                                  Příklad nepovinně zadávaný šedesátiznakový Unicode řetězec, který nesmí začínat ani končit mezerou
                                  CREATE DOMAIN my_text AS VARCHAR(60) CHARACTER SET UTF8
                                    CHECK(VALUE NOT LIKE ' %' AND VALUE NOT LIKE '% ')
                                    COLLATE UTF8;

                                  Domény jde také měnit (ALTER DOMAIN) a mazat (DROP DOMAIN).

                                  Tabulky

                                  Tabulky se vytváří příkazem:

                                  CREATE TABLE name (
                                    column_definitions
                                    [CONSTRAINT constraint] PRIMARY KEY (columns)
                                    [[CONSTRAINT constraint] UNIQUE (columns)]*
                                    [[CONSTRAINT constraint] FOREIGN KEY (columns)
                                      REFERENCES table [(columns)]
                                      [ON DELETE action]
                                      [ON UPDATE action]]*
                                    [, [CONSTRAINT constraint] CHECK column_constraint]*
                                  );

                                  Sloupce jsou specifikovány jménem a typem/doménou. Místo typu je možné použít COMPUTED BY value, což vypočte hodnotu sloupce na základě jiných sloupců. Dají se přidat další věci:

                                  DEFAULT value
                                  určí výchozí hodnotu
                                  NOT NULL
                                  zakáže prázdnost
                                  PRIMARY KEY
                                  nastaví jako primární klíč (může být jen u jednoho sloupce)
                                  REFERENCES table [column]
                                  nastaví jako cizí klíč
                                  CHECK (condition)
                                  nastaví podmínku
                                  UNIQUE
                                  sloupec musí obsahovat různé hodnoty

                                  Část PRIMARY KEY specifikuje, které sloupce jsou primární klíč.

                                  Části UNIQUE specifikují, které skupiny sloupců musí obsahovat dohromady různé hodnoty.

                                  Tabulky jde také upravovat příkazem ALTER TABLE nebo odstraňovat příkazem DROP TABLE.

                                  Příklad specifikace databáze počítačových her
                                  Firebird
                                  CREATE DOMAIN identification AS INTEGER NOT NULL CHECK(VALUE > 0);
                                  CREATE DOMAIN valid_date AS DATE NOT NULL CHECK (VALUE >= '1900-01-01');
                                  CREATE DOMAIN text35 AS VARCHAR(35) CHARACTER SET UTF8
                                    NOT NULL CHECK(VALUE NOT LIKE ' %' AND VALUE NOT LIKE '% ') COLLATE UTF8;
                                  CREATE DOMAIN text20 AS VARCHAR(20) CHARACTER SET UTF8
                                    NOT NULL CHECK(VALUE NOT LIKE ' %' AND VALUE NOT LIKE '% ') COLLATE UTF8;
                                  CREATE TABLE game_type (
                                    rowid identification,
                                    name text35,
                                    CONSTRAINT game_type_pk PRIMARY KEY(rowid),
                                    CONSTRAINT game_type_uniq_name UNIQUE (name)
                                  );
                                  CREATE TABLE game (
                                    rowid identification,
                                    name text35,
                                    version text20,
                                    released valid_date,
                                    type identification,
                                    CONSTRAINT game_pk PRIMARY KEY (rowid),
                                    CONSTRAINT game_uniq UNIQUE (name, version)
                                  );
                                  CREATE TABLE score (
                                    player identification,
                                    game identification,
                                    day valid_date,
                                    order identification,
                                    score identification,
                                    CONSTRAINT score_pk PRIMARY KEY (player, game, day, order)
                                  );
                                  CREATE TABLE player (
                                    rowid identification,
                                    name text20,
                                    surname text35,
                                    birthday valid_date,
                                    CONSTRAINT player_pk PRIMARY KEY (rowid)
                                  );
                                  ALTER TABLE game ADD CONSTRAINT game_fk_type
                                    FOREIGN KEY (type) REFERENCES game_type(rowid) ON UPDATE CASCADE;
                                  ALTER TABLE score ADD CONSTRAINT score_fk_player
                                    FOREIGN KEY (player) REFERENCES player(rowid) ON UPDATE CASCADE;
                                  ALTER TABLE score ADD CONSTRAINT score_fk_game
                                    FOREIGN KEY (game) REFERENCES game(rowid) ON UPDATE CASCADE;
                                  SQLite
                                  CREATE TABLE game_type (name, CONSTRAINT game_type_uniq_name UNIQUE (name));
                                  CREATE TABLE game (name, version, released, type, CONSTRAINT game_uniq UNIQUE (name, version), CONSTRAINT game_fk_type FOREIGN KEY (type) REFERENCES game_type(rowid) ON UPDATE CASCADE);
                                  CREATE TABLE score (player, game, day, ord, score, CONSTRAINT score_pk PRIMARY KEY (player, game, day, ord), CONSTRAINT score_fk_game FOREIGN KEY (game) REFERENCES game(rowid) ON UPDATE CASCADE, CONSTRAINT game_fk_player FOREIGN KEY (player) REFERENCES player(rowid) ON UPDATE CASCADE);
                                  CREATE TABLE player (name, surname, birthday);

                                  Manipulace s daty

                                  Vkládání dat do tabulky

                                  INSERT INTO table [(columns)] VALUES (values);

                                  Pokud není uveden seznam sloupců, berou se v pořadí, v jakém byly deklarovány. Pokud má sloupec definovanou výchozí hodnotu nebo povoluje prázdné hodnoty, je možné ho vynechat.

                                  Vložení selže, pokud by byla porušena doménová, entitní nebo referenční integrita.

                                  V některých implementacích je také možné „nasypat“ data do tabulky z jiné tabulky (místo VALUES se napíše příkaz SELECT) nebo přidat více řádků najednou (více závorek za VALUES oddělených čárkou).

                                  Některé implementace umožňují automatickou generaci primárního klíče zvyšováním čísla.

                                  Příklad vkládání dat do databáze her
                                  Firebird
                                  CREATE SEQUENCE g_player;
                                  CREATE SEQUENCE g_game;
                                  CREATE SEQUENCE g_game_type;
                                  INSERT INTO game_type VALUES (NEXT VALUE FOR g_game_type, 'puzzle');
                                  INSERT INTO game_type VALUES (NEXT VALUE FOR g_game_type, 'action');
                                  INSERT INTO game_type VALUES (NEXT VALUE FOR g_game_type, 'strategy');
                                  INSERT INTO game_type VALUES (NEXT VALUE FOR g_game_type, 'adventure');
                                  INSERT INTO game VALUES (NEXT VALUE FOR g_game, 'Tic-Tac-Toe', '8.4', '2009-06-23', 1);
                                  INSERT INTO game VALUES (NEXT VALUE FOR g_game, 'Zoo Tycoon', '2', '2008-04-17', 3);
                                  INSERT INTO game VALUES (NEXT VALUE FOR g_game, 'Sudoku', '1.01', '2008-05-02', 1);
                                  INSERT INTO game VALUES (NEXT VALUE FOR g_game, 'Doom', '1.9', '2002-09-02', 2);
                                  INSERT INTO game VALUES (NEXT VALUE FOR g_game, 'Paintball', '2', '2006-07-29', 2);
                                  INSERT INTO game VALUES (NEXT VALUE FOR g_game, 'Age of Empires', '2', '2000-12-06', 3);
                                  INSERT INTO player VALUES (NEXT VALUE FOR g_player, 'Petr', 'Synek', '2000-11-20');
                                  INSERT INTO player VALUES (NEXT VALUE FOR g_player, 'Ivana', 'Malá', '1999-10-10');
                                  INSERT INTO player VALUES (NEXT VALUE FOR g_player, 'René', 'Nový', '2002-05-31');
                                  INSERT INTO player VALUES (NEXT VALUE FOR g_player, 'Pavel', 'Srb', '2003-10-30');
                                  INSERT INTO player VALUES (NEXT VALUE FOR g_player, 'Petr', 'Klíč', '2001-11-16');
                                  INSERT INTO player VALUES (NEXT VALUE FOR g_player, 'Zita', 'Stará', '2001-11-03');
                                  INSERT INTO score VALUES (1, 3, CAST('today' AS DATE), 1, 4021);      -- OK
                                  INSERT INTO score VALUES (1, 3, CAST('today' AS DATE), 2, 4093);      -- OK
                                  INSERT INTO score VALUES (3, 8, CAST('today' AS DATE) - 4, 1, 3245);  -- chyba referenční integrity
                                  INSERT INTO score VALUES (3, 3, 2010, 4, 4213);                       -- chyba doménové integrity
                                  INSERT INTO score VALUES (1, 4, '2022-02-30', 1, 17850);              -- chyba doménové integrity
                                  INSERT INTO score VALUES (1, 4, CAST('today' AS DATE) - 1, 1, 17850); -- OK
                                  INSERT INTO score VALUES (1, 4, CAST('today' AS DATE) - 1, 1, 25321); -- chyba entitní integrity
                                  INSERT INTO score VALUES (1, 4, CAST('today' AS DATE) - 1, 2, 25321); -- OK
                                  INSERT INTO score VALUES (3, 3, CAST('today' AS DATE), 1, 3245);      -- OK
                                  INSERT INTO score VALUES (3, 3, CAST('today' AS DATE), 2, 4137);      -- OK
                                  INSERT INTO score VALUES (3, 3, CAST('today' AS DATE), 3, 3981);      -- OK
                                  INSERT INTO score VALUES (6, 3, CAST('today' AS DATE) - 3, 1, 3401);  -- OK
                                  INSERT INTO score VALUES (6, 3, CAST('today' AS DATE) - 3, 2, 3998);  -- OK
                                  INSERT INTO score VALUES (6, 3, CAST('today' AS DATE) - 3, 3, 3728);  -- OK
                                  INSERT INTO score VALUES (6, 3, CAST('today' AS DATE) - 3, 4, 4137);  -- OK
                                  INSERT INTO score VALUES (2, 3, CAST('today' AS DATE) - 5, 1, 1891);  -- OK
                                  INSERT INTO score VALUES (2, 3, CAST('today' AS DATE) - 5, 2, 4150);  -- OK
                                  SQLite
                                  INSERT INTO game_type VALUES ('puzzle');
                                  INSERT INTO game_type VALUES ('action');
                                  INSERT INTO game_type VALUES ('strategy');
                                  INSERT INTO game_type VALUES ('adventure');
                                  INSERT INTO game VALUES ('Tic-Tac-Toe', '8.4', '2009-06-23', 1);
                                  INSERT INTO game VALUES ('Zoo Tycoon', '2', '2008-04-17', 3);
                                  INSERT INTO game VALUES ('Sudoku', '1.01', '2008-05-02', 1);
                                  INSERT INTO game VALUES ('Doom', '1.9', '2002-09-02', 2);
                                  INSERT INTO game VALUES ('Paintball', '2', '2006-07-29', 2);
                                  INSERT INTO game VALUES ('Age of Empires', '2', '2000-12-06', 3);
                                  INSERT INTO player VALUES ('Petr', 'Synek', '2000-11-20');
                                  INSERT INTO player VALUES ('Ivana', 'Malá', '1999-10-10');
                                  INSERT INTO player VALUES ('René', 'Nový', '2002-05-31');
                                  INSERT INTO player VALUES ('Pavel', 'Srb', '2003-10-30');
                                  INSERT INTO player VALUES ('Petr', 'Klíč', '2001-11-16');
                                  INSERT INTO player VALUES ('Zita', 'Stará', '2001-11-03');
                                  INSERT INTO score VALUES (1, 3, DATE('now'), 1, 4021);
                                  INSERT INTO score VALUES (1, 3, DATE('now'), 2, 4093);
                                  INSERT INTO score VALUES (1, 4, DATE('now', '-1 day'), 1, 17850);
                                  INSERT INTO score VALUES (1, 4, DATE('now', '-1 day'), 2, 25321);
                                  INSERT INTO score VALUES (3, 3, DATE('now'), 1, 3245);
                                  INSERT INTO score VALUES (3, 3, DATE('now'), 2, 4137);
                                  INSERT INTO score VALUES (3, 3, DATE('now'), 3, 3981);
                                  INSERT INTO score VALUES (6, 3, DATE('now', '-3 day'), 1, 3401);
                                  INSERT INTO score VALUES (6, 3, DATE('now', '-3 day'), 2, 3998);
                                  INSERT INTO score VALUES (6, 3, DATE('now', '-3 day'), 3, 3728);
                                  INSERT INTO score VALUES (6, 3, DATE('now', '-3 day'), 4, 4137);
                                  INSERT INTO score VALUES (2, 3, DATE('now', '-5 day'), 1, 1891);
                                  INSERT INTO score VALUES (2, 3, DATE('now', '-5 day'), 2, 4150);

                                  Změna dat v tabulce

                                  UPDATE table SET column1=value1 [, columnK=valueK]* [WHERE condition]

                                  Použijeme-li WHERE, změní se jen ty řádky, které splňují podmínku. Pokud podmínku neuvedeme, změní se všechny řádky tabulky.

                                  Mazání dat v tabulce

                                  DELETE FROM table [WHERE condition]

                                  Smažou se všechny řádky vyhovující podmínce. Vynecháme-li podmínku, smažou se úplně všechny.

                                  Výběr dat z tabulky

                                  SELECT [DISTINCT | ALL] projection
                                    FROM source
                                    [WHERE restriction]
                                    [GROUP BY aggregation [HAVING restriction]]
                                    [ORDER BY criterion]
                                    [row_limit];

                                  Výsledkem dotazu je vždy tabulka. Počet sloupců závisí na projekci, počet řádků na restrikcích.

                                  Specifikujeme-li DISTINCT, duplicitní záznamy jsou vynechány.

                                  Příklad výpis dnešního data
                                  Firebird
                                  SELECT DISTINCT CAST('today' AS DATE) FROM game_type AS today;
                                  SQLite
                                  SELECT DATE('now') AS today;
                                  Příklad výpis různých jmen všech hráčů
                                  Firebird/SQLite
                                  SELECT DISTINCT name FROM player ORDER BY name;
                                  Příklad výpis všech informací o hráčích seřazený podle příjmení
                                  Firebird/SQLite
                                  SELECT * FROM player ORDER BY surname;
                                  Příklad výpis všech informací o hráčích seřazený od nejmladšího a následně podle příjmení
                                  Firebird/SQLite
                                  SELECT * FROM player ORDER BY birthday DESC, surname;
                                  Příklad výpis roku narození, jména a příjmení seřazený od nejmladšího a následně podle příjmení
                                  Firebird
                                  SELECT EXTRACT(YEAR FROM birthday) AS birth_year, name, surname FROM player ORDER BY birthday DESC, surname;
                                  SQLite
                                  SELECT strftime('%Y', birthday) AS birth_year, name, surname FROM player ORDER BY birthday DESC, surname;
                                  Příklad výpis věku, jména a příjmení seřazený od nejmladšího a následně podle příjmení
                                  Firebird
                                  SELECT (CAST('today' AS DATE) - birthday)/365.25 AS age, name, surname FROM player ORDER BY birthday DESC, surname;
                                  SQLite
                                  SELECT (julianday() - julianday(birthday))/365.25 AS age, name, surname FROM player ORDER BY birthday DESC, surname;
                                  Příklad výběr ID hráčů, kteří hráli alespoň jednu hru
                                  Firebird/SQLite
                                  SELECT DISTINCT player FROM score ORDER BY player;
                                  Příklad výběr ID her, které hrál alespoň jeden hráč
                                  Firebird/SQLite
                                  SELECT DISTINCT game FROM score ORDER BY game;
                                  Příklad výběr prvního dne, kdy někdo hrál nějakou hru
                                  Firebird/SQLite
                                  SELECT MIN(day) AS first_day FROM score;
                                  Příklad výběr posledního dne, kdy někdo hrál nějakou hru
                                  Firebird/SQLite
                                  SELECT MAX(day) AS first_day FROM score;
                                  Příklad výběr názvů, verze a ID všech her, které jsou nejdéle z roku 2006, seřazené od nejnovějších
                                  Firebird/SQLite
                                  SELECT name, version, rowid FROM game WHERE released >= '2006-01-01' ORDER BY released DESC;
                                  Příklad výběr názvů, verze, ID a typu všech logických her, seřazeno podle názvu hry
                                  Firebird/SQLite
                                  SELECT name, version, rowid, type FROM game WHERE type = 1 ORDER BY name DESC;

                                  Spojování

                                  Kartézský součin

                                  table0 CROSS JOIN table1

                                  Vnitřní spojení

                                  table0 [INNER] JOIN table1 ON condition

                                  Podmínka slouží k realizaci vztahu mezi tabulkami.

                                  Lze spojit jen dvě tabulky, při větším množství je potřeba více JOIN klauzulí.

                                  Systém si sám vybere, jaký algoritmus pro spojení použije. V případě, že je to nutné specifikovat ručně, existuje rozšířená syntaxe.

                                  Vnější spojení

                                  table0 (LEFT | RIGHT | FULL) [OUTER] JOIN table1 ON condition
                                  Příklad výpis id a názvů her, které hrál hráč s ID 1, seřazené dle názvu
                                  Firebird/SQLite
                                  SELECT DISTINCT game.rowid, game.name FROM game JOIN score ON score.game = game.rowid WHERE player = 1 ORDER BY game.name;
                                  Příklad výpis všech informací o hráčích, kteří zatím nehráli žádnou hru, řazeno podle příjmení, jména a data narození
                                  Firebird/SQLite
                                  SELECT player.* FROM player LEFT JOIN score ON score.player = player.rowid WHERE score.game IS NULL ORDER BY player.surname, player.name, player.birthday;
                                  Příklad výpis všech informací o hrách, které jsou starší než z roku 2008 a zatím je nikdo nehrál
                                  Firebird/SQLite
                                  SELECT game.* FROM game LEFT JOIN score ON score.game = game.rowid WHERE score.player IS NULL AND game.released < '2008-01-01';
                                  Příklad výpis všech her spolu s názvem jejich typu, seřazený podle typu a dále podle názvu hry
                                  Firebird/SQLite
                                  SELECT game.*, game_type.name FROM game JOIN game_type ON game.type = game_type.rowid;
                                  Příklad výpis skóre spolu s názvem hry a jménem a příjmením hráče, seřadit dle data hraní hry a dále podle čísla hráče a počtu
                                  Firebird/SQLite
                                  SELECT score, game.name, player.name, player.surname FROM score JOIN game ON score.game = game.rowid JOIN player ON score.player = player.rowid ORDER BY score.day, score.player, score.ord;
                                  Příklad výpis všech hráčů (id, jméno, příjmení) a her (id, název), které každý hráč hrál (pokud hráč nic nehrál, bude id hry i název prázdné), seřazeno podle příjmení a dále jména hráče
                                  Firebird/SQLite
                                  SELECT DISTINCT player.rowid, player.name, player.surname, game.rowid, game.name FROM player LEFT JOIN score ON score.player = player.rowid LEFT JOIN game ON score.game = game.rowid ORDER BY player.surname, player.name;

                                  Agregační funkce

                                  Agregační funkce pracují s celou množinou záznamů a vracejí jednu hodnotu. Prázdné hodnoty jsou ignorovány (s výjimkou funkce COUNT). Pro prázdný vstup vracejí NULL.

                                  Použitím modifikátoru DISTINCT je možné zpracovat pouze různé hodnoty.

                                  func([DISTINCT | ALL] expr)
                                  COUNT
                                  počet hodnot
                                  SUM
                                  součet hodnot
                                  AVG
                                  aritmetický průměr hodnot
                                  MIN
                                  nejmenší hodnota
                                  MAX
                                  největší hodnota
                                  Příklad

                                  Mějme tabulku zaměstnanců, kde jsou uvedeny jejich platy s hodnotami 12540, 13820, 11958, 13820, 14051, 19085, 20340, 13820, 14051:

                                  SQLite
                                  CREATE TABLE employee(name, salary);
                                  INSERT INTO employee VALUES ('Agáta', 12540), ('Bořek', 13820), ('Cindy', 11958), ('David', 13820), ('Ema', 14051), ('Filip', 19085), ('Gabriela', 20340), ('Hynek', 13820), ('Iva', 14051);

                                  Poté na ní můžeme vyzkoušet různé agregační funkce:

                                  SQLite
                                  SELECT COUNT(salary), COUNT(DISTINCT salary), SUM(salary), SUM(DISTINCT salary), AVG(salary), AVG(DISTINCT salary), MIN(salary), MIN(DISTINCT salary), MAX(salary), MAX(DISTINCT salary) FROM employee;

                                  Agregační funkce se dají využívat:

                                  Seskupování záznamů

                                  Klauzule GROUP BY vytvoří „souhrnné“ řádky, kde každý obsahuje informace o všech původních řádcích, které mají nějaké dané atributy stejné.

                                  K získání skalární hodnoty v ostatních sloupcích je nutné použít agregační funkce.

                                  Podmínka s použitím WHERE se týká původních dat. Pokud chceme nějak filtrovat podle seskupených dat, musíme použít HAVING.

                                  Příklad vypsání celkového počtu záznamů v každé tabulce
                                  Firebird/SQLite
                                  SELECT (SELECT COUNT(*) FROM game_type) as game_type_count, (SELECT COUNT(*) FROM game) as game_count, (SELECT COUNT(*) FROM player) as player_count, (SELECT COUNT(*) FROM score) as score_count;
                                  Příklad vypsání ID hráců, kteří hráli alespoň dvě různé hry
                                  Firebird/SQLite
                                  SELECT player FROM score GROUP BY player HAVING COUNT(DISTINCT game) >= 2;
                                  Příklad vypsání ID, jména a příjmení hráčů, a počtu různých her, které někdy hráli, bez hráčů, kteří nic nehráli, seřazeno podle počtu her sestupně a podle příjmení hráče
                                  Firebird/SQLite
                                  SELECT player.rowid, player.name, player.surname, COUNT(DISTINCT game) as game_count FROM player JOIN score ON score.player = player.rowid GROUP BY player.rowid ORDER BY game_count DESC, surname;
                                  Příklad vypsání ID, jména a příjmení hráčů, a počtu různých her, které někdy hráli, včetně hráčů, kteří nic nehráli, seřazeno podle počtu her sestupně a podle příjmení hráče
                                  Firebird/SQLite
                                  SELECT player.rowid, player.name, player.surname, COUNT(DISTINCT game) as game_count FROM player LEFT JOIN score ON score.player = player.rowid GROUP BY player.rowid ORDER BY game_count DESC, surname;
                                  Příklad vypsání ID, jména a příjmení hráčů, ID a názvu hry a počet dní, ve kterých hru hrál, bez hráčů, kteří nic nehráli, seřazeno podle příjmení hráče a názvu hry
                                  Firebird/SQLite
                                  SELECT player.rowid, player.name, player.surname, game.rowid, game.name, COUNT(DISTINCT day) AS day_count FROM player JOIN score ON score.player = player.rowid JOIN game ON score.game = game.rowid GROUP BY player.rowid, game.rowid ORDER BY player.surname, game.name;
                                  Příklad to samé, ale ještě k tomu poslední den, kdy hrál
                                  Firebird/SQLite
                                  SELECT player.rowid, player.name, player.surname, game.rowid, game.name, COUNT(DISTINCT day) AS day_count, MAX(day) AS last_day FROM player JOIN score ON score.player = player.rowid JOIN game ON score.game = game.rowid GROUP BY player.rowid, game.rowid ORDER BY player.surname, game.name;
                                  Příklad ID a název her, které byly hrány alespoň pětkrát, seřazeno podle názvu, včetně počtu hraní
                                  Firebird/SQLite
                                  SELECT game.rowid, game.name, COUNT(*) AS play_count FROM game JOIN score ON score.game = game.rowid GROUP BY game.rowid HAVING play_count >= 5 ORDER BY game.name;
                                  Příklad ID a název hry, kterou hráli alespoň dva různí hráči, seřazeno podle názvu hry, včetně počtu různých hráčů
                                  Firebird/SQLite
                                  SELECT game.rowid, game.name, COUNT(DISTINCT player) AS player_count FROM game JOIN score ON score.game = game.rowid GROUP BY game.rowid HAVING player_count >= 2 ORDER BY game.name;
                                  Příklad ID a název hry, ID hráče a počet hraní hry daným hráčem, pod podmínkou, že hrál hru alespoň dvakrát, seřazeno podle názvu hry a ID hráče
                                  Firebird/SQLite
                                  SELECT game.rowid, game.name, score.player, COUNT(*) AS play_count FROM game JOIN score ON score.game = game.rowid GROUP BY game.rowid, score.player HAVING play_count >= 2 ORDER BY game.name, score.player;
                                  Příklad dny, kdy se hrála nějaká hra, a počet různých her v daný den, seřazeno podle data sestupně
                                  Firebird/SQLite
                                  SELECT day, COUNT(DISTINCT game) as game_count FROM score GROUP BY day ORDER BY day DESC;
                                  Příklad ID, jméno, příjmení a datum narození každého hráče, ID a název hry, kterou někdy hrál, a průměrné skóre v dané hře
                                  Firebird/SQLite
                                  SELECT player.rowid, player.name, player.surname, player.birthday, game.rowid, game.name, AVG(score.score) AS average_score FROM player LEFT JOIN score ON score.player = player.rowid LEFT JOIN game ON score.game = game.rowid GROUP BY player.rowid, game.rowid;

                                  Poddotazy

                                  Do jistých příkazů je možné vnořit příkaz SELECT. Konkrétně pokud vnořujeme do dalšího příkazu SELECT, můžeme ho použít v podmínce WHERE, v projekci nebo ve zdroji dat (odvozená tabulka). Poddotazy podporují jen některé systémy.

                                  Příklad výpis hráčů, kteří nic nehráli
                                  Firebird/SQLite
                                  SELECT rowid, name, surname FROM player WHERE rowid NOT IN (SELECT player FROM score) ORDER BY surname, name, birthday;
                                  Příklad vložení logické hry do tabulky her
                                  Firebird
                                  INSERT INTO game VALUES (NEXT VALUE FOR g_hra, 'Zuma Deluxe', '1.0', '2010-07-07', (SELECT rowid FROM game_type WHERE name='puzzle'));
                                  SELECT * FROM game;
                                  SQLite
                                  INSERT INTO game VALUES ('Zuma Deluxe', '1.0', '2010-07-07', (SELECT rowid FROM game_type WHERE name='puzzle'));
                                  SELECT * FROM game;

                                  Co můžeme s výsledkem poddotazu dělat, závisí na jeho tvaru:

                                  Prázdná množina:
                                  Neexistenci můžeme zjistit pomocí operátorů EXISTS a NOT EXISTS.
                                  Jeden řádek, jeden sloupec:
                                  Výsledek můžeme používat jako skalár.
                                  Jeden sloupec:
                                  Na výsledek lze aplikovat operace ANY, SOME, IN, NOT IN, ALL, EXISTS, NOT EXISTS. Například 0 IN (SELECT something) je ekvivalentní 0 = SOME(SELECT something).
                                  Jeden řádek:
                                  V projekci nebo WHERE nejde použít.
                                  Tabulka:
                                  V projekci nebo WHERE nejde použít.

                                  Poddotaz musí být v kulatých závorkách. Uvnitř WHERE se musí psát napravo od operátoru. Některé poddotazy lze přeformulovat jako spojení, což většinou zvýší efektivitu.

                                  Poddotaz může používat atributy z vnějšího dotazu, čímž vznikne korelovaný poddotaz. Ten ovšem může být hodně pomalý, protože se musí provádět zvlášť pro každý řádek vnějšího dotazu. Zato vnější dotaz nesmí používat atributy z vnitřního dotazu. Dřív byly korelované dotazy hodně významné, ale dnes má SQL nové funkcionality, které většinou umožňují se jim vyhnout.

                                  Příklad korelovaný poddotaz v projekci
                                  Firebird/SQLite
                                  SELECT rowid, name, surname, (SELECT COUNT(DISTINCT game) FROM score WHERE player = player.rowid) AS game_count FROM player ORDER BY surname, name, birthday;
                                  Všimněme si, že dotaz by se dal přeformulovat pomocí JOIN a GROUP BY.
                                  Příklad hry, které zatím nikdo nehrál
                                  Firebird/SQLite
                                  SELECT * FROM game WHERE rowid NOT IN (SELECT game FROM score) ORDER BY name;
                                  Také by to šlo pomocí vnějšího spojení, což ale může být pomalejší:
                                  Firebird/SQLite
                                  SELECT game.* FROM game LEFT JOIN score ON score.game = game.rowid WHERE player IS NULL;
                                  Příklad ID, název a datum nejstarší hry (popřípadě, je-li jich víc, seřazeno podle názvu)
                                  Firebird/SQLite
                                  SELECT rowid, name, released FROM game WHERE released = (SELECT MIN(released) FROM game) ORDER BY name;
                                  Příklad ID a název typu, pod který jsou zařazeny alespoň tři hry
                                  Firebird/SQLite
                                  SELECT rowid, name FROM game_type WHERE 3 <= (SELECT COUNT(*) FROM game WHERE type = game_type.rowid) ORDER BY name;

                                  Sjednocení výsledků

                                  select1 UNION [ALL] select2 (UNION [ALL] selectk)*

                                  Není-li použito ALL, sjednocení vynechá duplicitní řádky.

                                  Všechny dotazy musí mít stejný počet a typy sloupců. Názvy se řídí podle prvního dotazu.

                                  Sjednocení lze seřadit přidáním ORDER BY za poslední dotaz.

                                  Pohledy

                                  CREATE VIEW name [(columns)]
                                  AS select_query
                                  [WITH CHECK OPTION];

                                  Jméno pohledu musí být unikátní v rámci tabulek a pohledů.

                                  Neuvedeme-li názvy sloupců, použijí se ty z dotazu. Je nutné je uvést, pokud v dotazu chybí aliasy pro některé vypočtené výrazy.

                                  V pohledu není efektivní používat ORDER BY. Lepší je seřadit až výsledek po použití pohledu.

                                  Klauzuli WITH CHECK OPTION můžeme použít u aktualizovatelných pohledů, pokud nechceme, aby do tabulky vkládaly řádky, které nesplňují vyhledávací podmínku.

                                  Definici pohledu lze měnit:

                                  ALTER VIEW name [(columns)]
                                  AS select_query
                                  [WITH CHECK OPTION];

                                  Pohled lze také odstranit příkazem DROP VIEW name;.

                                  Pohled je aktualizovatelný (lze ho použít také k úpravě tabulky), pokud

                                  Příklad megatabulka pro databázi her
                                  SQLite
                                  CREATE VIEW score_all
                                  AS SELECT player.name AS player_name, surname, birthday, game.name AS game_name, game.version, game.released, game_type.name AS type, score.day, score.ord, score.score
                                  FROM score JOIN player ON score.player = player.rowid JOIN game ON score.game = game.rowid JOIN game_type ON game.type = game_type.rowid;
                                  SELECT * FROM score_all ORDER BY player_name, game_name, day, ord;
                                  Příklad statistiky her
                                  SQLite
                                  CREATE VIEW game_stats
                                  AS SELECT game.name, game.version, game.released, game_type.name AS type, MAX(score) AS max_score, MIN(score) AS min_score, AVG(score) AS avg_score, COUNT(*) as play_count, COUNT(DISTINCT player) AS player_count, MIN(day) AS first_played, MAX(day) AS last_played
                                  FROM game JOIN score ON score.game = game.rowid JOIN game_type ON game.type = game_type.rowid
                                  GROUP BY game;
                                  SELECT * FROM game_stats ORDER BY name;