SQL - 2. del

SQL - 2. del

Avtor: Matija Lokar

SELECT

  • Najpogosteje uporabljen ukaz
  • Povpraševanje po podatkih
  • Osnovna struktura

    • SELECT seznam podatkov, ki jih želimo videti
      FROM seznam tabel, kjer bomo podatke našli
      WHERE pogoji, ki določajo, katere podatke želimo videti

    • SELECT * FROM knjige
            WHERE cena > 100.00
                 ORDER BY naslov


    • Podatki, ki nastopajo, so lahko stolpci ali pa izrazi!
    • SELECT naslov, round(cena * 1.10, 2) FROM knjige
            WHERE cena * (1 + davek/100.0) > 100.00

Enostavna oblika stavka SELECT

  • SELECT * FROM bbc
  • SELECT name, population FROM bbc
  • SELECT name AS "Ime države" FROM bbc
  • SELECT name AS Ime FROM bbc
         ORDER BY population DESC
  • SELECT DISTINCT region FROM bbc
  • SELECT 15 FROM bbc WHERE region="Europe "
  • SELECT population/area FROM bbc
         WHERE region IN ("Europe", "Asia", "Africa")
               AND area BETWEEN 100000 AND 1000000
  • SELECT name FROM bbc
         WHERE region = "Europe"
               ORDER BY population/area

Poizvedbe s podpoizvedbami

  • Kot določeno vrednost v izrazih lahko uporabimo tudi rezultate ukaza SELECT
  • SELECT name FROM bbc
         WHERE population <=
              (SELECT population FROM bbc
                      WHERE name='Slovenia')
  • Izpiši imena držav, ki imajo manj prebivalcev kot Slovenija
  • SELECT name FROM bbc
         WHERE population BETWEEN
            (SELECT population FROM bbc
                 WHERE name='Canada')
             AND
            (SELECT population FROM bbc
                 WHERE name='Algeria')
  • Izpiši imena držav, ki imajo število prebivalcev med številom prebivalcev Alžirije in Kanade
  • Takim kompleksnim poizvedbam se (vsaj v začetku) izogibajmo!

Poizvedbe s podpoizvedbami II

  • Izpiši imena držav, ki imajo manj prebivalcev kot Slovenija

    • SELECT population FROM bbc
            WHERE name='Slovenia'

      • Dobimo rezultat 2000000
    • SELECT name FROM bbc
            WHERE population <= 2000000
  • Izpiši imena držav, ki imajo število prebivalcev med številom prebivalcev Alžirije in Kanade

    • SELECT population FROM bbc
            WHERE name='Canada'

      • Dobimo rezultat 32000000
    • SELECT population FROM bbc
            WHERE name='Algeria'

      • Dobimo rezultat 32900000
    • SELECT name FROM bbc
            WHERE population BETWEEN 32900000 AND 32000000
  • Podpoizvedbe naj bi vračale le en stolpec z eno vrednostjo!
  • Drugače pride do napake

    • Npr. če bi v našem primeru imeli dve državi (ali več) z imenom Canada.
    • Stolpec ima lahko več vrednosti le, če rezultat poizvedbe uporabimo kjer imamo lahko več vrednosti (seznam)

      • Operator IN

        • IN (SELECT ... )

Združevalne funkcije

  • AVG (stolpec) : povprečna vrednost v stolpcu

    • SELECT AVG(population) FROM bbc
            WHERE region="Europe
      "
    • povprečno število prebivalcev v Evropi
  • COUNT (stolpec) : število vrstic z neničelno vrednostjo v stolpcu

    • SELECT COUNT(gdp) FROM bbc
    • Število držav, kjer je poznan BDP (podatek je v tabeli)
  • COUNT (DISTINCT stolpec) : število različnih vrednosti

    • SELECT COUNT(DISTINCT region) FROM bbc
    • Število regij, ki so v tabeli držav
  • COUNT (*) : število vrstic (zapisov)

    • SELECT COUNT(*) FROM bbc
            WHERE region="Asia
      "
    • Število azijskih držav v tabeli
  • MAX (stolpec) : maksimalna vrednost v stolpcu

    • SELECT MAX(population) FROM bbc
            WHERE region="Africa
      "
    • Maksimalno število prebivalcev afriške države
  • MIN (stolpec) : minimalna vrednost v stolpcu

    • SELECT MIN(area) FROM bbc
    • Najmanjša površina, ki jo ima neka država v tabeli

      • Dobimo 0 ?!
      • SELECT name, area FROM bbc
               WHERE area = 0

        • Vatican, 0
      • SELECT MIN(area) FROM bbc
               WHERE area > 0
  • SUM (stolpec) : vsota vrednost v stolpcu

    • SELECT SUM(gdp) FROM bbc
            WHERE region="Europe
      "
    • Koliko "proizvedemo" v Evropi

Združevalne funkcije in težave

  • Denimo, da nas zanima ime afriške države z največ prebivalci

    • SELECT name, MAX(population) FROM bbc
            WHERE region="Africa
      "
    • sql: Mixing of GROUP columns (MIN(),MAX(),COUNT(),...)
      with no GROUP columns is illegal if there is no GROUP BY clause
  • Zakaj?

    • Če malo pomislimo, je tole čista neumnost
    • Dejansko s tem hočemo izpisati imena vseh afriških držav, zraven vsake pa še maksimalno število prebivalcev, ki jo ima država v Afriki
    • SELECT name, population FROM bbc
            WHERE region="Africa"
              AND population = MAX(population)

      • sql: Invalid use of group function
  • Skupinske funkcije lahko uporabljamo v prvem delu stavka SELECT. Izračunajo neko vrednost prek več vrstic tabele.

Združevalne funkcije in težave II

  • Rešitev?
  • Korak za korakom prosim!

    • SELECT MAX(population) FROM bbc
            WHERE region="Africa
      "

      • Dobimo 130200000
    • SELECT name, population FROM bbc
            WHERE region="Africa" AND population = 130200000

      • Dobimo Nigeria 130200000

Združevalne funkcije in skupine podatkov

  • Denimo, da nas zanima maksimalno število prebivalcev države v vsaki regiji

    • SELECT MAX(population) FROM bbc
            WHERE region = "Europe
      "
    • SELECT MAX(population) FROM bbc
            WHERE region = "Asia
      "
    • ...
    • Moramo poznati vse regije!
    • Cel kup tipkanja
  • GROUP BY

    • Združi podatke glede na vsebino
    • Torej združevalno funkcijo uporabi na vsaki skupini
  • SELECT MAX(population) FROM bbc
         GROUP BY region

    • Malo neinformativno ;-)
  • SELECT region, MAX(population) FROM bbc GROUP BY region
  • A pozor!

    • SELECT name, region, MAX(population) FROM bbc GROUP BY region
    • MySQL veselo izvede stvar in izpiše prvo ime države v vsaki regiji
    • PostgreSQL

      • sql: ERROR: column "bbc.name" must appear in the GROUP BY clause or be used in an aggregate function
    • Oracle

      • sql: ORA-00979: not a GROUP BY expression (DBD ERROR: error possibly near <*> indicator at char 7 in 'SELECT <*>name, region, MAX(population) FROM bbc GROUP BY region ')

SELECT in več tabel

  • "Prave" baze sestavlja več tabel
  • Pogosto želimo kot rezultat dobiti podatke, ki se "skrivajo" v več tabelah
(tabele.jpg)

Združevanje tabel

  • Navedimo več tabel
  • SELECT * FROM zaposleni, naslovi
  • Dobimo kartezični produkt, torej n x m zapisov (če je n število zapisov v prvi, m pa v drugi bazi)

    • V našem primeru 5 x 4 = 20 zapisov!
    • Kot bi vsak zaposleni stanoval na VSEH naslovih!
  • Kako do določenih stolpcev?

    • SELECT ime FROM zaposleni, naslovi
    • Kam "spada" ime?
    • SELECT zaposleni.ime FROM zaposleni, naslovi
    • Torej ime_baze.ime_stolpca
    • Če ni možnosti za zmedo (ime stolpca se pojavi le v eni od navedenih baz), lahko ime baze spustimo.

Združevanje na osnovi ključev

  • Primarni ključ, tuji (zunanji) ključ, ...
  • SELECT * FROM zaposleni, naslovi
         WHERE naslovi.IDZaposlenega = zaposleni.IDZaposlenega
  • Zapisi, kjer se primarni ključ tabele zaposelni ujemo z zunanjim ključem tabele naslovi
(tabele.jpg)
  • Pozor! Združena tabela ni taka kot smo želeli (P10) prej!

Izbor le določenih vrednosti

  • SELECT * FROM zaposleni, naslovi WHERE IDZaposlenega = "MK1 "

    • Napaka! IDZaposlenega je v obeh tabelah!
  • SELECT * FROM zaposleni, naslovi
         WHERE zaposleni.IDZaposlenega = "MK1
    "
  • Koliko zapisov dobimo?

    • 4 (za vsako vrstico iz naslovov)
  • SELECT * FROM zaposleni, naslovi
         WHERE naslovi.IDZaposlenega = "MK1
    "
  • Koliko zapisov dobimo?

    • 5 (za vsako vrstico iz tabele zaposlenih)
  • SELECT * FROM zaposleni, naslovi
         WHERE naslovi.IDZaposlenega = zaposleni.IDZaposlenega = "MK1
    "

    • Seveda narobe!
  • SELECT * FROM zaposleni, naslovi
         WHERE naslovi.IDZaposlenega = zaposleni.IDZaposlenega
              AND naslovi.IDZaposlenega = "MK1
    "
  • Ali ne gre "lepše"?

Notranji stik

  • Dejansko gre le za "lepšo" obliko
  • SELECT * FROM zaposleni
         INNER JOIN  naslovi ON (naslovi.IDZaposlenega = zaposleni.IDZaposlenega)            WHERE  naslovi.IDZaposlenega = "MK1
    "
  • SELECT * FROM zaposleni
         INNER JOIN  naslovi ON (naslovi.IDZaposlenega = zaposleni.IDZaposlenega)
  • Stike običajno izvajamo preko ključev (primarni s tujim)

    • Ni nujno, da se ključa v tabelah imenujeta enako!
  • Če obstajajo zapisi v tabeli zaposlenih, ki nimajo pripadajočega zapisa v tabeli naslovov, jih v tabeli ni.

    • V tabeli zaposlenih je nekdo z IDZaposlenega ML1 . Če v tabeli naslovon ni vrstice, kjer bi bil stolpec IDZaposlenega enak ML1, potem vrstice s "pridruženimi" podatki (praznimi) ni!
  • Opombe

    • INNER lahko spustimo (včasih moramo - RDBMS uporablja drugačen "standard")
    • Včasih je možna le oblika, kot je na prejšnji prosojnici (Oracle 8i in starejši, 9i vpelje to (standardno) obliko)
  • Glejte vaje na http://sqlzoo.net/3a.htm in http://sqlzoo.net/3.htm

Zunanji stik

  • Kaj, če bi želeli dobiti pare, kjer leva (ali desna) tabela nima para

    • V tabeli zaposlenih je nekdo z IDZaposlenega ML1 in v tabeli naslovov ni vrstice, kjer bi bil stolpec IDZaposlenega enak ML1.
    • Želimo dobiti združeno vrstico, kjer so podatki le o ML1 (pripadajočih podatkov o naslovu pa ni)!
(tabele1.jpg)

Oracle

  • SELECT * FROM zaposleni, naslovi
         WHERE zaposleni.IDZaposlenega = naslovi.IDZaposlenega(+)
  • Če desno NI ustreznega ključa, vzamemo polja s samimi praznimi vrednostmi
  • SELECT * FROM zaposleni, naslovi
         WHERE zaposleni.IDZaposlenega(+) = naslovi.IDZaposlenega
  • Če levo NI ustreznega ključa, vzamemo polja s samimi praznimi vrednostmi
  • Do različice 8i
  • Različica 9i (trenutna je 10g) vpelje "standardno" sintakso

Standard

  • SELECT * FROM zaposleni
         LEFT OUTER JOIN  naslovi ON (naslovi.IDZaposlenega = zaposleni.IDZaposlenega)
  • Leva tabela je glavna – vse vrstice iz nje dobijo par! Če v desni ni ustrezne vrstice, se vzame taka z ničelnimi vrednostmi.
  • To smo mi želeli (glede na sliko)!
  • SELECT * FROM zaposleni
         RIGHT OUTER JOIN  naslovi ON (naslovi.IDZaposlenega = zaposleni.IDZaposlenega)
  • Desna tabela je glavna – vse vrstice iz nje dobijo par! Če v levi ni ustrezne vrstice, se vzame taka z ničelnimi vrednostmi.
  • Obstaja tudi FULL OUTER JOIN

    • Unija levega in desnega zunanjega
  • Za zglede glej: http://sqlzoo.net/4.htm, ...

Celoten stavek SELECT

SELECT [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT]
[SQL_BUFFER_RESULT][SQL_CACHE | SQL_NO_CACHE]
[SQL_CALC_FOUND_ROWS] [HIGH_PRIORITY] [DISTINCT | DISTINCTROW |
ALL] select_expression,... [INTO {OUTFILE | DUMPFILE}
'file_name' export_options] [FROM table_references [WHERE
where_definition] [GROUP BY {unsigned_integer | col_name |
formula} [ASC | DESC], ... [WITH ROLLUP]] [HAVING
where_definition] [ORDER BY {unsigned_integer | col_name |
formula} [ASC | DESC] ,...] [LIMIT [offset,] row_count |
row_count OFFSET offset] [PROCEDURE
procedure_name(argument_list)] [FOR UPDATE | LOCK IN SHARE
MODE]]

table_references: table_reference, table_references
table_reference [INNER | CROSS] JOIN table_reference
[join_condition] table_reference STRAIGHT_JOIN table_reference
table_reference LEFT [OUTER] JOIN table_reference
[join_condition] table_reference NATURAL [LEFT [OUTER]] JOIN
table_reference table_reference RIGHT [OUTER] JOIN
table_reference [join_condition] table_reference NATURAL [RIGHT [OUTER]]
JOIN table_reference

table_reference: table_name [[AS] alias] [[USE INDEX (key_list)]
| [IGNORE INDEX (key_list)] | [FORCE INDEX (key_list)]]

join_condition: ON conditional_expr | USING (column_list)
0%
0%