owned this note
owned this note
Published
Linked with GitHub
---
tags: vaje, opb, view, select, gnezdenje
hackmd: https://hackmd.io/Bpt5AAqLRUOeS2Qou0e27w
---
# Osnove podatkovnih baz - vaje 10.3.2022
---
## Pogledi (`VIEW`), gnezdene poizvedbe (sub`SELECT`s), zahtevnejši `SELECT` stavki
Še naprej delamo na tabelah s traktorji.
Če v nalogi ni čisto točno določeno, kaj je treba izpisati (npr. ime osebe ali ID osebe), v glavnem velja, da se izogni tovrstnim tehnikalijam, reši lažjo varianto in raje porabi čas za še druge naloge.
Naloge poleg določil `JOIN` in `GROUP BY` večinoma zahtevajo še uporabo kakšnega od naslednjih pristopov/ukazov/določil:
* [`CREATE VIEW`](http://www.postgresql.org/docs/current/static/sql-createview.html)
* gnezdene poizvedbe (subqueries)
* [`UNION [ALL]`, `INTERSECT [ALL]`, `EXCEPT [ALL]`](http://www.postgresql.org/docs/current/static/queries-union.html)
* [`EXISTS`, `ANY`, `ALL`](http://www.postgresql.org/docs/current/static/functions-subquery.html)
* [`WITH`](http://www.postgresql.org/docs/current/static/queries-with.html)
---
```sql
CREATE VIEW pogled AS
SELECT ...
```
---
### Naloge
1. Vsak traktor je vreden 10000 dolarjev, vsak rezervni del pa 100.
* Sestavi pogled `traktor_premozenje` s stolpcema `lastnik` in `vrednost`, ki za vsako osebo, ki ima v lasti kakšen traktor, pove, koliko so vredni njegovi traktorji.
* Sestavi pogled `deli_premozenje(lastnik, vrednost)`, ki poda podobno informacijo za rezervne dele.
* Z uporabo zgornjih dveh pogledov sestavi poizvedbo, ki za vsako osebo izpiše, koliko ima premoženja.
* Sestavi isto poizvedbo še brez uporabe pogledov, z gnezdenimi `SELECT`i.
```sql
CREATE VIEW traktor_premozenje AS
SELECT lastnik, 10000 * count(*) AS vrednost
FROM traktor
GROUP BY lastnik;
CREATE VIEW deli_premozenje AS
SELECT lastnik, 100 * sum(stevilo) AS vrednost
FROM deli
GROUP BY lastnik;
SELECT lastnik, coalesce(traktor_premozenje.vrednost, 0) + coalesce(deli_premozenje.vrednost, 0) AS vrednost
FROM traktor_premozenje FULL JOIN deli_premozenje USING (lastnik);
SELECT lastnik, sum(vrednost) AS vrednost FROM (
SELECT lastnik, vrednost FROM deli_premozenje
UNION ALL
SELECT lastnik, vrednost FROM traktor_premozenje
) AS t
GROUP BY lastnik;
SELECT lastnik, sum(vrednost) AS vrednost FROM (
SELECT lastnik, 100 * stevilo AS vrednost
FROM deli
UNION ALL
SELECT lastnik, 10000 AS vrednost
FROM traktor
) AS t
GROUP BY lastnik;
```
2. Izpiši ljudi, ki nimajo nobenega otroka z rdečim traktorjem.
```sql
SELECT * FROM oseba
WHERE NOT EXISTS (
SELECT * FROM otroci
JOIN traktor ON lastnik = otrok
WHERE barva = 'rdeca' AND stars = oseba.id
);
```
3. Za vsako osebo izpiši, koliko ima takšnih rezervnih delov, ki jih ne more uporabiti na nobenem od svojih traktorjev. Oseb, za katere bi bila ta vrednost 0, ni treba izpisovati.
```sql
SELECT lastnik, sum(stevilo) FROM deli
WHERE znamka NOT IN (
SELECT znamka FROM traktor
WHERE lastnik = deli.lastnik
)
GROUP BY lastnik;
```
4. Izpiši ime osebe, ki ima v lasti največ različnih znamk traktorjev.
```sql
SELECT lastnik FROM traktor
GROUP BY lastnik
HAVING count(DISTINCT znamka) >= ALL (
SELECT count(DISTINCT znamka) FROM traktor
GROUP BY lastnik
);
WITH razlicne_znamke AS (
SELECT lastnik, count(DISTINCT znamka) AS znamke
FROM traktor
GROUP BY lastnik
)
SELECT lastnik FROM razlicne_znamke
WHERE znamke >= ALL (
SELECT znamke FROM razlicne_znamke
);
```
5. Izpiši osebe, katerih otroci so vsi lastniki traktorja iste znamke (t.j., obstaja znamka traktorja, ki jo imajo vsi otroci te osebe). Oseb brez otrok ne izpisuj.
```sql
SELECT * FROM oseba
WHERE EXISTS (
SELECT * FROM znamka
WHERE NOT EXISTS (
SELECT otrok FROM otroci
WHERE stars = oseba.id
EXCEPT
SELECT lastnik FROM traktor
WHERE znamka = znamka.id
)
)
AND id IN (
SELECT stars FROM otroci
);
```
6. Za vsako osebo izpiši, koliko rezervnih delov ima na razpolago. "Imeti na razpolago" ni isto kot "imeti v lasti"; za potrebe te naloge veljajo naslednja pravila:
* rezervni deli so na razpolago lastniku
* staršu so na razpolago vsi rezervni deli, ki jih imajo v lasti njegovi otroci
* če ima nek del na razpolago *N* ljudi, vsakemu priznavamo le 1/*N* razpoložljivosti. Če ima nekdo npr. v lasti 3 rezervne dele, v bazi pa nastopa tudi njegov oče (ki sam ni lastnik nobenega rezervnega dela), potem naj poizvedba tako za sina kot za očeta izpiše, da imata na razpolago 1.5 dela.
```sql
WITH druzine AS (
SELECT stars AS id, otrok AS clan FROM otroci
UNION
SELECT id, id AS clan FROM oseba
)
SELECT id, sum(stevilo / velikost::real) AS razpolozljivost FROM druzine
JOIN deli ON lastnik = clan
JOIN (
SELECT clan, count(*) AS velikost
FROM druzine
GROUP BY clan
) AS v USING (clan);
GROUP BY id;
```
7. Izpiši vse osebe, ki imajo v lasti vsaj dva traktorja. Prepovedana je uporaba `GROUP BY` in funkcije `count`.
```sql
SELECT DISTINCT lastnik FROM traktor AS t1
JOIN traktor AS t2 USING (lastnik)
WHERE t1.id <> t2.id;
```