Grafikus C# – Windows Forms tanfolyam › 3.12 lecke Windows Forms · 3.12 lecke · összesítések Eddig az adatbázisból sorokat kértél. Most azt tanulod meg, hogyan számoltasd ki az adatbázissal az összeget, az átlagot, a legnagyobbat, és hogyan kapsz csoportonként (például részlegenként) külön eredményt. Ezt a tudást használják a kimutatások, a statisztikák és a grafikonok is.Összesítések SQL-ben: SUM, GROUP BY, COALESCE

A lecke végére
- a
COUNT,SUM,AVG,MINésMAXfüggvényekkel egyetlen számot számoltatsz ki a táblából; - érted, mi a
NULL, és aCOALESCE-szel kezeled; - a
GROUP BY-jal csoportonként összesítesz, és sorba rendezed az eredményt; - az eredményt Windows Forms alkalmazásban megjeleníted: egy számot
ExecuteScalar-ral, egy kis táblátDataGridView-ban; - felismered a leggyakoribb összesítős hibákat.
Mielőtt elkezded
Ehhez a leckéhez kell a 3.1 lecke (DB Browser, COUNT(*)), a 3.2 lecke (DB.cs, ExecuteScalar), a 3.5 lecke (WHERE, ORDER BY, LIMIT), a 3.7 lecke (paraméterek) és a 3.8 lecke (DataTable, SQLiteDataAdapter).
Gyakorló tábla: dolgozók és fizetések
Az összesítést olyan táblán érdemes megtanulni, ahol számolni is lehet, ezért készítünk egy új, kis gyakorló adatbázist. A DB Browser programban (3.1 lecke) hozz létre egy új adatbázist gyakorlo.db néven, nyisd meg az Execute SQL fület, másold be az alábbi kódot, futtasd (Ctrl+Enter), végül mentsd a változásokat (Write Changes, Ctrl+S).
CREATE TABLE IF NOT EXISTS Dolgozok (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nev TEXT NOT NULL,
reszleg TEXT NOT NULL,
fizetes INTEGER NOT NULL,
premium INTEGER
);
INSERT INTO Dolgozok (nev, reszleg, fizetes, premium) VALUES
('Kiss Anna', 'Iroda', 420000, 50000),
('Nagy Béla', 'Iroda', 380000, NULL),
('Tóth Cili', 'Raktár', 310000, NULL),
('Szabó Dénes', 'Raktár', 330000, 20000),
('Horváth Eszter', 'Raktár', 295000, NULL),
('Varga Feri', 'Műhely', 450000, 80000),
('Kovács Gréta', 'Műhely', 410000, NULL),
('Farkas Hugó', 'Iroda', 365000, 30000);NOT NULL, mert nem mindenki kap prémiumot. Ahol nincs prémium, a cella NULL.A táblában nyolc ember van három részlegen: az Irodában három, a Raktárban három, a Műhelyben két ember dolgozik.
Egy szám az egész táblából: összesítő függvények
Az összesítő függvény sok sort egyetlen értékké von össze. Eddig csak a COUNT(*)-ot láttad (3.1). A többi is hasonlóan működik:
| Függvény | Mit számol? |
|---|---|
COUNT(*) | hány sor van |
SUM(oszlop) | az oszlop értékeinek összege |
AVG(oszlop) | az átlag |
MIN(oszlop) | a legkisebb érték |
MAX(oszlop) | a legnagyobb érték |
Jósolj!
Mit fog adni ez a lekérdezés a nyolc dolgozóra? Gondold végig a számokat, mielőtt lefuttatod: hány dolgozó van, mekkora az összes fizetés, mennyi az átlag, mennyi a legkisebb és a legnagyobb fizetés?
SELECT COUNT(*) AS db,
SUM(fizetes) AS osszeg,
AVG(fizetes) AS atlag,
MIN(fizetes) AS legkisebb,
MAX(fizetes) AS legnagyobb
FROM Dolgozok;Jóslat ellenőrzése
Az eredmény egyetlen sor: db = 8, osszeg = 2 960 000, atlag = 370 000, legkisebb = 295 000, legnagyobb = 450 000. Az összesítő függvény az egész táblát egy sorba vonja össze, ezért itt nem látsz külön dolgozókat.
AS nevet ad az eredmény oszlopának (alias). Nélküle az oszlop neve ilyesmi lenne: SUM(fizetes). A névvel a DB Browser, a DataGridView fejléce és a ORDER BY is egyszerűbb.WHERE előbb kiválasztja a sorokat, és az összesítés csak azokra számol. Például SELECT SUM(fizetes) FROM Dolgozok WHERE reszleg = 'Iroda'; csak az iroda összegét adja: 1 165 000.Hiányzó értékek: NULL és COALESCE
A prémium oszlopban nem mindenkinek van érték. Nézzük meg, mit csinálnak az összesítő függvények a NULL értékekkel:
SELECT COUNT(*) AS mindenki,
COUNT(premium) AS premiumosok,
SUM(premium) AS premiumOsszeg
FROM Dolgozok;Jósolj!
A nyolc dolgozóból hányan kapnak prémiumot a táblázat szerint? Mit ad vajon a COUNT(premium), és mit a COUNT(*)?
Jóslat ellenőrzése
Az eredmény: mindenki = 8, premiumosok = 4, premiumOsszeg = 180 000. A COUNT(*) minden sort számol, a COUNT(oszlop) viszont csak azokat, ahol az oszlop nem NULL. A SUM, az AVG, a MIN és a MAX is átugorja a NULL értékeket.
Amikor az összeg is NULL
Ha egy összesítésnél egyetlen sor sem felel meg a feltételnek, a SUM eredménye nem nulla, hanem NULL. Próbáld ki:
SELECT SUM(premium) FROM Dolgozok WHERE reszleg = 'Nincs ilyen';A DB Browser egy NULL cellát mutat. Egy C# programban ez gondot okoz, mert a NULL értéket nem lehet számmá alakítani (lásd a Gyakori hibák szakaszt). A megoldás a COALESCE:
SELECT COALESCE(SUM(premium), 0) FROM Dolgozok WHERE reszleg = 'Nincs ilyen';a NULL, akkor a b-t.A COALESCE a sorok szintjén is hasznos. Ha a fizetéshez hozzá akarod adni a prémiumot, a NULL prémiumot nullának kell venni, különben az egész összeg NULL lenne:
SELECT nev, fizetes + COALESCE(premium, 0) AS teljes
FROM Dolgozok
ORDER BY teljes DESC;Csoportonként: GROUP BY
Az eddigi lekérdezések az egész táblát egyetlen sorba vonták össze. A GROUP BY azt mondja: csoportosítsd a sorokat egy oszlop értéke szerint, és minden csoportra külön számolj. Így kapsz részlegenként összegeket.
SELECT reszleg,
COUNT(*) AS fo,
SUM(fizetes) AS osszeg
FROM Dolgozok
GROUP BY reszleg;Jósolj!
Hány sora lesz az eredménynek? Mennyi lesz az Iroda, a Raktár és a Műhely fizetéseinek összege? (Segítség: az iroda összegét az előbb már láttad.)
Jóslat ellenőrzése
Három sor lesz, részlegenként egy: Iroda: 3 fő, 1 165 000; Műhely: 2 fő, 860 000; Raktár: 3 fő, 935 000. A három összeg együtt 2 960 000, ugyanannyi, mint a teljes táblára számolt összeg.
| reszleg | fo | osszeg |
|---|---|---|
| Iroda | 3 | 1165000 |
| Műhely | 2 | 860000 |
| Raktár | 3 | 935000 |
Mi kerülhet a SELECT-be?
Csoportosított lekérdezésben a SELECT után két fajta elem állhat: maga a csoportosító oszlop (itt a reszleg) és összesítő függvények. Minden más oszlop (például a nev) értelmetlen, mert egy csoportban több név van.
A lekérdezés sorai: WHERE, GROUP BY, ORDER BY
A három záradék mindig ebben a sorrendben áll: először a WHERE szűr, utána a GROUP BY csoportosít, végül az ORDER BY rendez. Például a legalább 350 000 Ft-ot keresők összege részlegenként, a legnagyobb összeggel kezdve:
SELECT reszleg, SUM(fizetes) AS osszeg
FROM Dolgozok
WHERE fizetes >= 350000
GROUP BY reszleg
ORDER BY osszeg DESC;Az eredmény két sor: Iroda 1 165 000, Műhely 860 000. A Raktárban senki sem keres ennyit, ezért az a csoport meg sem jelenik.
Rendezés összeg szerint, döntetlennel
Az ORDER BY az összesítés eredményére is hivatkozhat az aliasával. Ha két csoport összege egyenlő, érdemes egy második rendezési szempontot is adni, hogy a sorrend mindig ugyanaz legyen:
SELECT reszleg, COUNT(*) AS fo
FROM Dolgozok
GROUP BY reszleg
ORDER BY fo DESC, reszleg;Az Iroda és a Raktár is három fős, ezért a második szempont (a név betűrendje) dönt: Iroda 3, Raktár 3, Műhely 2.
Kipróbálás
Futtasd le mindegyik lekérdezést a DB Browserben, és ellenőrizd, hogy a saját számolásod egyezik-e az eredménnyel. Utána próbáld ki, mi történik, ha a GROUP BY reszleg helyett GROUP BY nev-et írsz: hány sort kapsz, és miért?
Összesítés Windows Forms alkalmazásban
Most vigyük át ugyanezt egy programba. Két módon kapsz eredményt: egy egyetlen számot (összeg) az ExecuteScalar ad vissza, egy kis táblát (részlegenként) pedig az SQLiteDataAdapter tölt be, mint a 3.8 leckében.
Előkészítés
- Hozz létre egy új Windows Forms (.NET Framework) projektet
Osszesitesnéven, és telepítsd aSystem.Data.SQLitecsomagot a NuGettel (3.2 lecke). - Add hozzá a
DB.csosztályt a 3.2 leckéből, de az adatbázis fájlneve legyen"gyakorlo.db". - Másold a DB Browserben mentett
gyakorlo.dbfájlt a projektbin\Debugmappájába. - A Formra tegyél egy
Button-t (btnOsszeg), egyLabel-t (lblEredmeny), egy másikButton-t (btnReszlegek), és egyDataGridView-t (dgvEredmeny,ReadOnly = true).
Egy szám: ExecuteScalar
private void btnOsszeg_Click(object sender, EventArgs e)
{
try
{
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
{
kapcsolat.Open();
using (SQLiteCommand parancs = new SQLiteCommand("SELECT COALESCE(SUM(fizetes), 0) FROM Dolgozok", kapcsolat))
{
int osszeg = Convert.ToInt32(parancs.ExecuteScalar());
lblEredmeny.Text = "Összes fizetés: " + osszeg + " Ft";
}
}
}
catch (Exception ex)
{
MessageBox.Show("Hiba: " + ex.Message);
}
}object típusban. Összesítésnél pontosan egy ilyen érték van.object-et egész számmá alakítja. Ez azért kell, mert az SQLite ezt az értéket nagyobb számtípusban adja vissza.SUM NULL lenne, és a Convert.ToInt32 hibával leállna. A COALESCE miatt ilyenkor 0 a válasz.Egy kis tábla: részlegenként
private void btnReszlegek_Click(object sender, EventArgs e)
{
string sql = "SELECT reszleg AS Reszleg, COUNT(*) AS Fo, SUM(fizetes) AS Osszeg " +
"FROM Dolgozok GROUP BY reszleg ORDER BY Osszeg DESC, Reszleg";
try
{
DataTable tabla = new DataTable();
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql, kapcsolat))
{
adapter.Fill(tabla);
}
dgvEredmeny.DataSource = tabla;
}
catch (Exception ex)
{
MessageBox.Show("Hiba: " + ex.Message);
}
}A táblázat fejlécei az SQL-ben megadott aliasok (Reszleg, Fo, Osszeg). Az eredmény három sor: Iroda 3 fő, 1 165 000; Raktár 3 fő, 935 000; Műhely 2 fő, 860 000.
Paraméterrel szűrt összeg
A szűrő értékét (például egy NumericUpDown-ból) paraméterként add át, ahogy a 3.7 leckében tanultad:
private void btnSzurt_Click(object sender, EventArgs e)
{
try
{
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
{
kapcsolat.Open();
using (SQLiteCommand parancs = new SQLiteCommand("SELECT COALESCE(SUM(fizetes), 0) FROM Dolgozok WHERE fizetes >= @kuszob", kapcsolat))
{
parancs.Parameters.AddWithValue("@kuszob", (int)nudKuszob.Value);
int osszeg = Convert.ToInt32(parancs.ExecuteScalar());
lblEredmeny.Text = "A küszöb feletti fizetések összege: " + osszeg + " Ft";
}
}
}
catch (Exception ex)
{
MessageBox.Show("Hiba: " + ex.Message);
}
}Ha a nudKuszob értéke 350000 (állítsd be a Maximum-ot elég nagyra, például 1000000-ra), az eredmény 2 025 000 Ft: az Iroda 1 165 000 és a Műhely 860 000 összege.
A leggyakoribb hibák
1. NULL összeget próbálsz számmá alakítani
A SUM üres halmazon NULL-t ad, a Convert.ToInt32 pedig nem tud NULL-ból számot csinálni.InvalidCastException: Object cannot be cast from DBNull to other types.
Javítás: COALESCE(SUM(...), 0) az SQL-ben.
2. Nem csoportosított oszlop a SELECT-ben
A SELECT nev, SUM(fizetes) FROM Dolgozok GROUP BY reszleg az SQLite-ban nem ad hibaüzenetet, hanem a csoport egyik tetszőleges nevét írja ki. Az eredmény félrevezető.Hiba nélkül lefut, de a név nem a csoport „nyertesének” a neve
Javítás: a SELECT-ben csak a csoportosító oszlop és összesítő függvények szerepeljenek.
3. WHERE a GROUP BY után
A záradékok sorrendje kötött: WHERE, GROUP BY, ORDER BY. Ha a sorrendet felcseréled, szintaktikai hibát kapsz.SQLiteException: near "WHERE": syntax error
4. Hiányzó adatbázisfájl a bin\Debug mappában
Ha a gyakorlo.db nem került a program mellé, a DB.UjKapcsolat() a 3.2 leckében látott módon hibát jelez.FileNotFoundException: Nem található az adatbázis
Alkoss: önálló feladatok
Előbb próbáld meg magad, aztán nyisd le a megoldást. Mindegyik a Dolgozok táblát használja.
1. Legmagasabb fizetés részlegenként
Írj lekérdezést, amely részlegenként megmutatja a legmagasabb fizetést, részleg szerint betűrendben. Elvárt eredmény: Iroda 420 000, Műhely 450 000, Raktár 330 000.Megoldás
SELECT reszleg, MAX(fizetes) AS legtobb
FROM Dolgozok
GROUP BY reszleg
ORDER BY reszleg;
2. Ki keres a legtöbbet?
Csak egyetlen sort kérj: a legtöbbet kereső dolgozó nevét és a fizetését. Elvárt: Varga Feri, 450 000. (Segítség: a 3.5 lecke A rendezés a legnagyobb fizetéssel kezd, a ORDER BY és LIMIT kulcsszavai.)Megoldás
SELECT nev, fizetes
FROM Dolgozok
ORDER BY fizetes DESC, nev
LIMIT 1;LIMIT 1 az első sort tartja meg. A második rendezési szempont (nev) azért kell, hogy egyenlő fizetés esetén is mindig ugyanaz a név legyen az első.
3. Teljes költség prémiummal
Számold ki részlegenként, mennyibe kerül a cégnek a fizetés és a prémium együtt, a legdrágább részleggel kezdve. Elvárt: Iroda 1 245 000, Raktár 955 000, Műhely 940 000. A Megoldás
SELECT reszleg, SUM(fizetes + COALESCE(premium, 0)) AS teljes
FROM Dolgozok
GROUP BY reszleg
ORDER BY teljes DESC, reszleg;COALESCE nélkül a NULL prémiumú sorok teljes értéke is NULL lenne, és az összeg kevesebb sort számolna.
4. A programban: hányan kapnak prémiumot részlegenként?
Tegyél a Formra egy „Prémiumosok” gombot, amely a A dgvEredmeny-ben részlegenként megmutatja, hány dolgozó kap prémiumot. Elvárt: Iroda 2, Műhely 1, Raktár 1.Megoldás
private void btnPremiumosok_Click(object sender, EventArgs e)
{
string sql = "SELECT reszleg AS Reszleg, COUNT(premium) AS Premiumosok " +
"FROM Dolgozok GROUP BY reszleg ORDER BY Reszleg";
try
{
DataTable tabla = new DataTable();
using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql, kapcsolat))
{
adapter.Fill(tabla);
}
dgvEredmeny.DataSource = tabla;
}
catch (Exception ex)
{
MessageBox.Show("Hiba: " + ex.Message);
}
}COUNT(premium) csak azokat a sorokat számolja, ahol a prémium nem NULL, ezért kapjuk a prémiumosok számát.
Mit tudsz most?
- A
COUNT,SUM,AVG,MINésMAXfüggvényekkel egy számot számoltatsz ki az adatbázissal. - Tudod, hogy az összesítések átugorják a NULL értékeket, és a
COALESCE-szel adsz nekik alapértéket. - A
GROUP BY-jal csoportonként összesítesz, és aWHERE,GROUP BY,ORDER BYsorrendet tartod. - Az eredményt
ExecuteScalar-ral vagySQLiteDataAdapter-rel hozod be a programba.
Tovább a tanmenetre
Az összesítésekre épül a 9.11 lecke is, amelyben a grafikon már az adatbázis számaiból rajzol, és a 16. fejezet összetett gyakorlófeladatai is.
