SQL - združevanje tabel

SQL - združevanje tabel

Avtor: Matija Lokar

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
(slika4.jpg)
  • Pozor! Združena tabela ni taka kot smo želeli (P2) 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)

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

Vsa združevanja

  • Kartezični produkt
  • Notranje
  • Zunanje

    • Levo
    • Desno
    • Polno
  • Število dobljenih vrstic?
  • Zgled

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)

Primer

  • Dve tabeli Naročniki in NarocilaNarocnikov
NIDNImeNPriimek
1DamjanKmetec
2AljažKmetec
3ŠpelaPrezelj


NIDNarociloIDKolicina
112
122
311
NULL12
  • Želimo izpisati skupno količino vseh naročnikov, ki se pišejo Kmetec in ki so naročali več kot enkrat
NIDNImeNPriimekStNarKol
1DamjanKmetec24

SELECT Nar.NID, Nar.NIme, Nar.NPriimek,
    COUNT(NNar.NarociloID) StNar,
    SUM(Nnar.Kolicina) Kol

    FROM Narocniki Nar LEFT OUTER JOIN
    NarocilaNarocnikov NNar ON
    Nar.NarocnikID = NNar.NarocnikID

    WHERE Nar.NarocnikPriimek = 'Kmetec'

    GROUP BY NarocnikID, NarocnikIme , NarocnikPriimek
    HAVING COUNT(NarociloID) > 1

    ORDER BY NarocnikID

Vrstni red izvajanja

(7)SELECT <seznam> (8) DISTINCT (10) TOP
(1) FROM <leva table>
(3) <tip združevanja> JOIN <desna tabela>
(2) ON <pogoji združevanja>
(4) WHERE <omejitveni pogoji>
(5) GROUP BY <razvrščanje v skupine>
(6) HAVING <omejitveni pogoji>
(9) ORDER BY <urejanje>

Prvi korak

FROM Narocniki Nar ... JOIN NarocilaNarocnikov NNar

Nar.NIDNar.NImeNar.NPriimekNNar.NIDNNar.NarociloINNar.Kolicina
1DamjanKmetec112
1DamjanKmetec122
1DamjanKmetec311
1DamjanKmetecNULL12
2AljažKmetec112
2AljažKmetec122
2AljažKmetec311
2AljažKmetecNULL12
3ŠpelaPrezelj112
3ŠpelaPrezelj122
3ŠpelaPrezelj311
3ŠpelaPrezeljNULL12

Drugi korak

  • Filter ON (Nar.NarocnikID = NNar.NarocnikID)
  • Izvedemo primerjanja
Nar.NIDNar.NImeNar.NPriimekNNar.NIDNNar.NarociloINNar.Kolicina
TRUE1DamjanKmetec112
TRUE1DamjanKmetec122
FALSE1DamjanKmetec311
UNKNOWN1DamjanKmetecNULL12
FALSE2AljažKmetec112
FALSE2AljažKmetec122
FALSE2AljažKmetec311
UNKNOWN2AljažKmetecNULL12
FALSE3ŠpelaPrezelj112
FALSE3ŠpelaPrezelj122
TRUE3ŠpelaPrezelj311
UNKNOWN3ŠpelaPrezeljNULL12
  • obdržimo vrstice s TRUE
Nar.NIDNar.NImeNar.NPriimekNNar.NIDNNar.NarociloINNar.Kolicina
TRUE1DamjanKmetec112
TRUE1DamjanKmetec122
TRUE3ŠpelaPrezelj311

Zunanje združevanje

  • Dodamo "leve" oz "desne vrstice"
Nar.NIDNar.NImeNar.NPriimekNNar.NIDNNar.NarociloINNar.Kolicina
1DamjanKmetec112
1DamjanKmetec122
2AljažKmetecNULLNULLNULL
3ŠpelaPrezelj311

Where

  • WHERE Nar.NPriimek = 'Kmetec'
Nar.NIDNar.NImeNar.NPriimekNNar.NIDNNar.NarociloINNar.Kolicina
1DamjanKmetec112
1DamjanKmetec122
2AljažKmetecNULLNULLNULL

Group By

  • GROUP BY NarocnikID, NarocnikIme , NarocnikPriimek
Nar.NIDNar.NImeNar.NPriimekNNar.NIDNNar.NarociloINNar.Kolicina
1DamjanKmetec112
122
2AljažKmetecNULLNULLNULL

S Having izločimo skupine

  • HAVING COUNT(NarociloID) > 1
Nar.NIDNar.NImeNar.NPriimekNNar.NIDNNar.NarociloINNar.Kolicina
1DamjanKmetec112
122

In sedaj so na vrsti stolpci

  • SELECT Nar.NID, Nar.NIme, Nar.NPriimek, COUNT(NNar.NarociloID) StNar, SUM(Nnar.Kolicina) Kol
Nar.NIDNar.NImeNar.NPriimekNNar.NarociloINNar.Kolicina
1DamjanKmetec24
0%
0%