Grafikus C# – Windows Forms tanfolyam › 3.5 lecke

Windows Forms · 3.5 lecke · lekérdezések

SELECT, WHERE, ORDER BY és rekordok rendezése

Az adatbázisok igazi ereje a lekérdezésekben van: nem az egész táblát kell megnézned, hanem csak azt, amire szükséged van, a kívánt sorrendben. Ebben a leckében az SQL WHERE, LIKE, BETWEEN, ORDER BY, DISTINCT és LIMIT kulcsszavait tanulod meg, majd egy dolgozói keresőt építesz, amelynek szűrői és rendezése együtt működik.

  • kb. 100 perc
  • közepes szint
  • szűrés és rendezés
Anime stílusú fiú és lány egy C# programozási feladatot beszél meg

A lecke végére

  • a WHERE feltételeit (=, <>, <, >, BETWEEN, IN, LIKE, IS NULL) használod, és összekapcsolod AND, OR és zárójelek segítségével;
  • az ORDER BY kulcsszóval több szempont szerint rendezel;
  • a DISTINCT és a LIMIT használatával egyedi értékeket és „első néhány” sort kérdezel le;
  • választható szűrőket építesz egyetlen paraméteres lekérdezésbe;
  • érted, miért nem paraméterezhető az ORDER BY, és hogyan rendezel biztonságosan;
  • ismered az SQLite LIKE ékezetes betűkkel kapcsolatos korlátját.

Mielőtt elkezded

Ehhez a leckéhez kell a 3.4 lecke projektje (DB.cs, kapcsolat, DataTable és DataGridView), és a belepes.db adatbázisban az adatok tábla a 3.1 lecke mini projektjéből (id, vnev, knev, fizetes, cim).

3.4 Regisztráció és DataGridView →

WHERE: a sorok szűrése

A SELECT alapból az összes sort adja. A WHERE után megadott feltétel csak azokat hagyja meg, amelyekre igaz. A leggyakoribb feltételek:

FeltételJelentésePélda
=, <>egyenlő, nem egyenlőWHERE cim = 'Győr'
<, <=, >, >=összehasonlításWHERE fizetes >= 400000
BETWEEN a AND bkét érték között (a határokat is beleértve)WHERE fizetes BETWEEN 300000 AND 450000
IN (…)a felsorolt értékek valamelyikeWHERE cim IN ('Győr', 'Pécs')
LIKEmintaillesztés (%: bármennyi karakter, _: pontosan egy)WHERE vnev LIKE 'K%'
IS NULLnincs érték (üres)WHERE cim IS NULL
AND, OR, NOTfeltételek összekapcsolása, tagadásWHERE cim = 'Győr' AND fizetes > 300000

Az AND erősebb, mint az OR

Ha AND és OR együtt szerepel, előbb az AND értékelődik ki. A WHERE cim = 'Győr' OR cim = 'Pécs' AND fizetes > 400000 azt jelenti: Győrben bárki, vagy Pécsett aki 400 000 fölött keres. Ha mást szeretnél, használj zárójelet: WHERE (cim = 'Győr' OR cim = 'Pécs') AND fizetes > 400000.

Jósolj!

A táblában az egyik dolgozónak nincs megadva a címe (NULL). Szerepel-e az eredményben a SELECT * FROM adatok WHERE cim <> 'Győr' lekérdezésnél?

Jóslat ellenőrzése

Nem. A NULL az SQL-ben „ismeretlen érték”, és minden hagyományos összehasonlítás vele (=, <>, >) eredménye sem igaz, sem hamis, ezért a sor kimarad. Ha a címnélküli dolgozót is látni szeretnéd, ezt külön kell kérni: WHERE cim <> 'Győr' OR cim IS NULL. A hiányzó értéket mindig az IS NULL, illetve az IS NOT NULL vizsgálja, a = NULL nem működik.

ORDER BY, DISTINCT és LIMIT

A sorok sorrendjét az ORDER BY adja meg. Az ASC növekvő (ez az alapértelmezett), a DESC csökkenő sorrend. Több szempontot vesszővel sorolhatsz fel: az első szerint rendez, az azonosak között a második dönt.

SQLSQLite
-- név szerint, ábécében
SELECT * FROM adatok ORDER BY vnev, knev;

-- legjobban fizetettek elöl
SELECT vnev, knev, fizetes FROM adatok ORDER BY fizetes DESC;

-- városonként, azon belül fizetés szerint csökkenően
SELECT cim, vnev, fizetes FROM adatok ORDER BY cim, fizetes DESC;

-- melyik városokban laknak dolgozóink? (minden város csak egyszer)
SELECT DISTINCT cim FROM adatok ORDER BY cim;

-- a három legjobban fizetett dolgozó
SELECT vnev, knev, fizetes FROM adatok ORDER BY fizetes DESC LIMIT 3;
ORDER BY oszlopA rendezés alapja. Szöveg esetén ábécé, szám esetén nagyság szerint rendez.
ASC / DESCNövekvő, illetve csökkenő sorrend. Oszloponként külön megadható.
DISTINCTAz ismétlődő sorokat egyszer adja. Hasznos az egyedi értékek listázásához.
LIMIT nLegfeljebb n sort ad. Az ORDER BY után áll, így „a legjobb n” lekérdezhető.

Jósolj!

Mit ad a SELECT vnev FROM adatok LIMIT 3, ha nincs ORDER BY? Ugyanazt kapod-e, ha a tábla sorait másik sorrendben vitték fel?

Jóslat ellenőrzése

Három sort ad, de nincs garantált sorrend: az adatbázis tetszőleges három sort adhat (jellemzően a felvétel sorrendjében, de erre nem szabad építeni). Ha más sorrendben vitték fel a sorokat, más három sort kaphatsz. A LIMIT-et ezért mindig ORDER BY-jal együtt használd, különben nem tudod, melyik n sort kapod.

Gyakorlóadatok és kipróbálás a DB Browserben

Hogy a szűrőknek legyen mit szűrniük, bővítsd az adatok táblát további sorokkal (az Execute SQL lapon, majd Ctrl+S). Az utolsó sor szándékosan hiányos: nincs címe.

SQLSQLite
INSERT INTO adatok (vnev, knev, fizetes, cim) VALUES ('Kiss', 'Márta', 380000, 'Győr');
INSERT INTO adatok (vnev, knev, fizetes, cim) VALUES ('Varga', 'Dávid', 450000, 'Pécs');
INSERT INTO adatok (vnev, knev, fizetes, cim) VALUES ('Kelemen', 'Zita', 330000, 'Veszprém');
INSERT INTO adatok (vnev, knev, fizetes, cim) VALUES ('Árpád', 'Lili', 410000, 'Győr');
INSERT INTO adatok (vnev, knev, fizetes) VALUES ('Molnár', 'Gábor', 360000);

Próbáld ki a leckében látott lekérdezéseket, és figyeld meg az eredményt:

LekérdezésMit várj?
SELECT * FROM adatok WHERE cim = 'Győr'Három sor: Szabó Kata, Kiss Márta, Árpád Lili.
SELECT * FROM adatok WHERE fizetes BETWEEN 300000 AND 400000A határok is beleszámítanak.
SELECT * FROM adatok WHERE vnev LIKE 'K%'Kovács, Kiss, Kelemen.
SELECT * FROM adatok WHERE cim IS NULLMolnár Gábor.
SELECT * FROM adatok WHERE cim <> 'Győr'Molnár Gábor nem szerepel (NULL).

A LIKE és az ékezetes betűk

Az SQLite LIKE operátora alapból a kis- és nagybetűt csak az angol (ASCII) betűknél tekinti azonosnak: a 'k%' megtalálja a Kovács nevet, de a 'á%' nem találja meg az Árpád nevet, mert az á és az Á ékezetes betű különbözőnek számít. Magyar szövegeknél ezért a kisbetű–nagybetű keresés a LIKE-kal nem teljes értékű. A kereső készítésekor gondolj erre, és tesztelj ékezetes nevekkel is.

📷 Képernyőkép helye

Mit tegyél ide: a DB Browser Execute SQL lapja a WHERE vnev LIKE 'K%' lekérdezéssel és az eredményt mutató táblával.Javasolt fájlnév: winforms-3-5-like.webp · Alt szöveg: LIKE lekérdezés eredménye a DB Browserben

A dolgozói kereső: választható szűrők egy lekérdezésben

Építsünk ablakos keresőt. Az ablakon (új projekt a 3.2–3.4 szerint, a belepes.db-vel és a DB.cs osztállyal) ezek lesznek:

txtKeresesA név részlete (vezeték- vagy keresztnév).
cmbVarosA város (DropDownList). Az első elem: (összes).
nudMinFizetesA legkisebb fizetés (Maximum: 1000000, Increment: 10000).
cmbRendezesA rendezés módja (DropDownList).
dgvDolgozok, lblDarabA találatok táblázata és a darabszám.

A legfontosabb ötlet: nem írunk különböző lekérdezést minden szűrőkombinációhoz. Egyetlen lekérdezés minden szűrőt tartalmaz, és az üres szűrő mindent átenged:

DB.cs (a DB osztályba; a fájl elején legyen using System.Data;)C#
public static DataTable Dolgozok(string nevresz, int minFizetes, string varos, int rendezes)
{
// az ORDER BY a program rögzített szövegeiből áll össze, soha nem a felhasználó szövegéből
string sorrend = "vnev, knev";

if (rendezes == 1)
{
sorrend = "fizetes DESC";
}
else if (rendezes == 2)
{
sorrend = "fizetes ASC";
}
else if (rendezes == 3)
{
sorrend = "cim, vnev";
}

string sql = "SELECT id, vnev, knev, fizetes, cim FROM adatok "
+ "WHERE (vnev LIKE @minta OR knev LIKE @minta) "
+ "AND fizetes >= @min "
+ "AND (@varos = '' OR cim = @varos) "
+ "ORDER BY " + sorrend;

DataTable tabla = new DataTable();

using (SQLiteConnection kapcsolat = UjKapcsolat())
{
kapcsolat.Open();

using (SQLiteCommand parancs = new SQLiteCommand(sql, kapcsolat))
{
parancs.Parameters.AddWithValue("@minta", "%" + nevresz + "%");
parancs.Parameters.AddWithValue("@min", minFizetes);
parancs.Parameters.AddWithValue("@varos", varos);

using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(parancs))
{
adapter.Fill(tabla);
}
}
}

return tabla;
}

public static DataTable Varosok()
{
DataTable tabla = new DataTable();

using (SQLiteConnection kapcsolat = UjKapcsolat())
{
kapcsolat.Open();

using (SQLiteDataAdapter adapter = new SQLiteDataAdapter("SELECT DISTINCT cim FROM adatok WHERE cim IS NOT NULL ORDER BY cim", kapcsolat))
{
adapter.Fill(tabla);
}
}

return tabla;
}
(vnev LIKE @minta OR knev LIKE @minta)A név bármelyik részére keres. Üres beírásnál a minta %%, amely minden szöveget elfogad. A zárójel azért kell, hogy az OR ne keveredjen a többi feltétellel.
fizetes >= @minA kezdőérték 0, így mindenki átmegy. (A NULL fizetésű dolgozó kimarad: lásd a gyakorlótárat.)
(@varos = '' OR cim = @varos)Üres szöveg esetén az első rész igaz, tehát a város nem szűr. Ha van város, a második rész dönt. Ez a „választható szűrő” mintája.
"%" + nevresz + "%"A % jeleket a C# kód fűzi a beírt szöveg köré, és az egész minta megy paraméterként. A felhasználó szövege itt sem kerül az SQL szövegébe (3.3 lecke).
DISTINCTA Varosok() minden várost egyszer ad vissza, a NULL címet kihagyja.
SQLiteDataAdapter(parancs)A kész parancsot kapja, így a paraméterek is érvényesek.

Miért nem paraméter az ORDER BY? A felület bekötése

Az oszlopnév vagy a rendezés iránya nem adható át paraméterként. A paraméter mindig érték, nem a lekérdezés szerkezetének része: ha az ORDER BY @oszlop parancsba a "fizetes" szöveget adnád, az adatbázis egy állandó szöveg szerint rendezne, vagyis valójában nem rendezne semmit. Ezért építünk a rendezésnél a program rögzített szövegeiből választva (a DB.Dolgozok if–else lánca), és a felhasználó csak egy sorszámot (SelectedIndex) ad meg. Így sosem kerülhet idegen szöveg az SQL szerkezetébe.

Szabály az SQL-ben összefűzhető részekre

Összefűzni csak olyan szöveget szabad az SQL-be, amelyet a program rögzített listájából választottál ki (oszlopnevek, rendezési irány). A felhasználó beírt szövegét mindig paraméterként add át.

Az ablak kódja minden szűrő változásakor újra lekérdez. Mivel az ablak betöltésekor a listák feltöltése is kiváltja a változás eseményeit, egy jelzőváltozó (mező) védi a korai lefutást (mint a 2.11 leckében):

Form1.csC#
private bool betoltve = false;

private void Form1_Load(object sender, EventArgs e)
{
cmbRendezes.Items.Add("Név szerint");
cmbRendezes.Items.Add("Fizetés szerint, csökkenő");
cmbRendezes.Items.Add("Fizetés szerint, növekvő");
cmbRendezes.Items.Add("Város szerint");
cmbRendezes.SelectedIndex = 0;

try
{
cmbVaros.Items.Add("(összes)");

foreach (DataRow sor in DB.Varosok().Rows)
{
cmbVaros.Items.Add(sor["cim"].ToString());
}

cmbVaros.SelectedIndex = 0;
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült betölteni a városokat: " + ex.Message);
}

betoltve = true;
Frissit();
}

private void Frissit()
{
if (!betoltve)
{
return;
}

try
{
string varos = cmbVaros.SelectedIndex <= 0 ? "" : cmbVaros.SelectedItem.ToString();

DataTable tabla = DB.Dolgozok(txtKereses.Text.Trim(), (int)nudMinFizetes.Value, varos, cmbRendezes.SelectedIndex);

dgvDolgozok.DataSource = tabla;
dgvDolgozok.Columns["vnev"].HeaderText = "Vezetéknév";
dgvDolgozok.Columns["knev"].HeaderText = "Keresztnév";
dgvDolgozok.Columns["fizetes"].HeaderText = "Fizetés (Ft)";
dgvDolgozok.Columns["cim"].HeaderText = "Város";

lblDarab.Text = tabla.Rows.Count + " találat";
}
catch (Exception ex)
{
MessageBox.Show("Adatbázis-hiba: " + ex.Message);
}
}

// ugyanezt hívja: txtKereses.TextChanged, cmbVaros.SelectedIndexChanged,
// nudMinFizetes.ValueChanged, cmbRendezes.SelectedIndexChanged
private void SzuroValtozott(object sender, EventArgs e)
{
Frissit();
}
betoltveAmíg hamis, a Frissit() kilép. Így a lista feltöltésekor kiváltott események még nem futtatnak lekérdezést félkész állapotban.
SzuroValtozottEgy eseménykezelő mind a négy szűrő változásához: a tervezőben mindegyik eseményénél ezt válaszd a legördülő listából.
SelectedIndex <= 0Ha nincs kiválasztott elem (-1) vagy az első, (összes) elem, a város szűrője kikapcsol.
Columns["vnev"].HeaderTextAz SQL oszlopnév helyett szép fejléc. Minden új DataSource után újra be kell állítani.

Jósolj!

A név mezőbe az a betűt írod, a város (összes), a minimális fizetés 0, a rendezés „Fizetés szerint, csökkenő”. Milyen sorrendben jönnek a találatok? Szerepel-e a címnélküli Molnár Gábor?

Jóslat ellenőrzése

A találatok a fizetés szerint csökkenően jönnek, legnagyobbal kezdve. Molnár Gábor szerepel, mert a város szűrője ((összes)) ilyenkor üres szöveget ad, és az (@varos = '' OR ...) feltétel első fele igaz, így a NULL cím nem akadályozza. A fizetes >= 0 igaz a 360 000 Ft-ra is. A név találat: a „Molnár Gábor” nevekben szerepel az a betű. (A NULL fizetésű sor viszont kimaradna, mert a fizetes >= 0 vele nem igaz.)

📷 Képernyőkép helye

Mit tegyél ide: a futó dolgozói kereső: a szűrők a tetején, a találatok táblázata a darabszámmal („5 találat”).Javasolt fájlnév: winforms-3-5-kereso.webp · Alt szöveg: A dolgozói kereső szűrőkkel és találati táblázattal

Hibakeresés: te vagy a hibadetektív

Mi a baj, és hogyan javítod?

Gondold végig az okot, és csak utána nyisd le a megoldást.

1. A rendezés nem működik

string sql = "SELECT * FROM adatok ORDER BY @oszlop";
parancs.Parameters.AddWithValue("@oszlop", "fizetes");
Hiba és javítás

A paraméter értéke egy állandó szöveg (fizetes), amely szerint az adatbázis rendez, de mivel minden sorban ugyanaz, a sorrend nem változik. Az oszlopnév nem paraméterezhető. A javítás: az ORDER BY után a program rögzített szövegeiből választott részt fűzd az SQL-hez ("... ORDER BY " + sorrend), a sorrend értéke pedig a SelectedIndex alapján választódjon ki.

2. A LIKE nem talál semmit

parancs.Parameters.AddWithValue("@minta", nevresz);
Hiba és javítás

A LIKE a mintát a teljes szöveggel hasonlítja össze. % nélkül csak a pontosan azonos szöveget találja meg (a ko nem találja a Kovács nevet). Fűzd a mintát: "%" + nevresz + "%".

3. Az ékezetes név nem található

Az árpád keresés nem találja meg az Árpád nevet, pedig az angol betűs nevek keresésénél a kis- és nagybetű nem számít.

Ok és javítás

Az SQLite LIKE csak az ASCII betűknél hagyja figyelmen kívül a kis- és nagybetű különbségét; az á és az Á eltér. A megoldás lehetőségei: (1) kérd meg a felhasználót, hogy pontosan írja be a nevet, (2) a kereséskor mindkét változatot (kis- és nagybetűs első betű) is keresed, (3) az adatok mellé tárolsz egy kisbetűs, ékezet nélküli keresőoszlopot, vagy (4) a szűrést a program oldalán végzed a DataTable sorain. Ezek közül a kezdő szinten a 4. a legegyszerűbb, és a 3.8 leckében ezt is megnézzük.

4. Hiányzó zárójel

WHERE vnev LIKE @minta OR knev LIKE @minta AND fizetes >= @min
Hiba és javítás

Az AND erősebb, ezért ez azt jelenti: vnev LIKE @minta, vagy (knev LIKE @minta és a fizetés elég magas). A vezetéknévre keresésnél tehát a fizetési szűrő nem érvényesül. Zárójelezd: (vnev LIKE @minta OR knev LIKE @minta) AND fizetes >= @min.

5. NullReferenceException az ablak indulásakor

string varos = cmbVaros.SelectedItem.ToString();
Hiba és javítás

Ha a Frissit() még azelőtt lefut, hogy a cmbVaros kapott volna kiválasztott elemet (SelectedItem értéke null), a ToString() hibát okoz. Védekezz kétféleképpen: a betoltve jelzővel kilépsz a korai futásból, és a SelectedIndex <= 0 feltétellel kezeled a hiányzó kiválasztást.

6. A NULL fizetésű dolgozó eltűnik

Egy dolgozónak nincs megadva a fizetése, és a kereső soha nem mutatja, pedig a minimum 0.

Ok és javítás

A fizetes >= 0 a NULL értékre nem igaz, ezért a sor kimarad. Ha mutatni szeretnéd, ezt kérni kell: AND (fizetes >= @min OR fizetes IS NULL). A jobb megoldás: az adatbázisban a fizetés legyen NOT NULL és alapértelmezett 0 (DEFAULT 0), ezzel kizárod a hiányzó értéket.

Mini projekt: dolgozói kereső

Építsd meg a keresőt, és bővítsd egy extra szűrővel:

  1. Az adatok tábla legyen feltöltve a leckében látott 9 sorral (köztük a címnélkülivel).
  2. A DB.Dolgozok és a DB.Varosok metódus, az ablak a négy szűrővel és a találati táblázattal.
  3. Új szűrő: egy nudMaxFizetes mező a legnagyobb fizetéshez (alapérték 1 000 000), amely a BETWEEN segítségével szűr a két határ között.
  4. A lblDarab mutassa a találatok számát, és ha nincs találat, üzenet: Nincs a feltételeknek megfelelő dolgozó.
  5. Teszteld: ékezetes név, nagybetűs keresés, üres név, nulla találat, a szűrők együttes használata.
Megoldás megnyitása (próbáld ki előbb önállóan!)

A DB.Dolgozok kap egy ötödik paramétert (int maxFizetes), a lekérdezés pedig a fizetes >= @min AND fizetes <= @max feltételt (vagy BETWEEN @min AND @max) használja. A nudMaxFizetes.ValueChanged esemény is a SzuroValtozott metódust hívja. A hibás határokat (a legkisebb nagyobb a legnagyobbnál) érdemes külön jelezni.

DB.cs és Form1.csC#
// DB.cs: a lekérdezés új feltétele
+ "AND fizetes BETWEEN @min AND @max "

parancs.Parameters.AddWithValue("@max", maxFizetes);

// Form1.cs: a Frissit() metódusban
if (nudMinFizetes.Value > nudMaxFizetes.Value)
{
lblDarab.Text = "A legkisebb fizetés nem lehet nagyobb a legnagyobbnál!";
return;
}

dgvDolgozok.DataSource = tabla;

if (tabla.Rows.Count == 0)
{
lblDarab.Text = "Nincs a feltételeknek megfelelő dolgozó.";
}
else
{
lblDarab.Text = tabla.Rows.Count + " találat";
}
📷 Képernyőkép helye

Mit tegyél ide: a kész kereső a két fizetéshatárral, a „Győr” város szűrőjével és a rendezéssel, néhány találattal.Javasolt fájlnév: winforms-3-5-kesz.webp · Alt szöveg: A kész dolgozói kereső több szűrővel

Három önálló gyakorlófeladat

Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.

1. Város szerinti csoportosítás

Az ablakon egy gomb írja ki, hány dolgozó él az egyes városokban (Győr: 3), egy lstStat ListBoxban.

Egy lehetséges megoldás

A GROUP BY csoportokba rendezi a sorokat, a COUNT(*) csoportonként számol. A NULL cím külön csoportot alkot, ezért kihagyjuk.

Form1.csC#
private void btnStat_Click(object sender, EventArgs e)
{
lstStat.Items.Clear();

using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
{
kapcsolat.Open();

using (SQLiteCommand parancs = new SQLiteCommand("SELECT cim, COUNT(*) AS darab FROM adatok WHERE cim IS NOT NULL GROUP BY cim ORDER BY cim", kapcsolat))
using (SQLiteDataReader olvaso = parancs.ExecuteReader())
{
while (olvaso.Read())
{
lstStat.Items.Add(olvaso["cim"] + ": " + olvaso["darab"]);
}
}
}
}

Az ExecuteReader sorról sorra olvas: a Read() addig igaz, amíg van következő sor. (Hibakezelést a try–catch blokkal érdemes hozzátenni.)

2. A legjobban fizetett három

Egy gomb jelenítse meg a táblázatban a három legjobban fizetett dolgozót.

Egy lehetséges megoldás

Az ORDER BY ... DESC LIMIT 3 pontosan ezt adja. A lekérdezés rögzített szöveg, nincs benne felhasználói bevitel.

Form1.csC#
DataTable tabla = new DataTable();

using (SQLiteConnection kapcsolat = DB.UjKapcsolat())
{
kapcsolat.Open();

using (SQLiteDataAdapter adapter = new SQLiteDataAdapter("SELECT vnev, knev, fizetes FROM adatok ORDER BY fizetes DESC LIMIT 3", kapcsolat))
{
adapter.Fill(tabla);
}
}

dgvDolgozok.DataSource = tabla;

3. Fizetési sáv választó

Egy cmbSav lista három sávot kínál: 300 000 alatt, 300 000 – 400 000, 400 000 fölött. A kiválasztott sáv szerint szűrjön a program.

Egy lehetséges megoldás

A három sávhoz három rögzített feltétel-szöveg tartozik, amelyek közül a sorszám alapján választunk (nem a felhasználó szövegéből építjük). Az alsó határ a BETWEEN miatt része a sávnak: ha ezt nem szeretnéd, használj > és < jeleket.

Form1.csC#
string feltetel = "fizetes < 300000";

if (cmbSav.SelectedIndex == 1)
{
feltetel = "fizetes BETWEEN 300000 AND 400000";
}
else if (cmbSav.SelectedIndex == 2)
{
feltetel = "fizetes > 400000";
}

string sql = "SELECT vnev, knev, fizetes FROM adatok WHERE " + feltetel + " ORDER BY fizetes";

Gyakorlótár: 6 további feladat

Három szinten gyakorolhatsz: alap, közepes és kihívás. Az első négyet a DB Browser Execute SQL lapján oldd meg, a többihez a dolgozói kereső projektje kell. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Veszprémi dolgozók névsorban

Kérdezd le a veszprémi dolgozók vezeték- és keresztnevét, vezetéknév szerint ábécébe rendezve.

Megoldás megnyitása

A szűrés a WHERE, a rendezés az ORDER BY dolga. A kért oszlopokat a SELECT után soroljuk fel.

SQLSQLite
SELECT vnev, knev
FROM adatok
WHERE cim = 'Veszprém'
ORDER BY vnev;

2. Fizetési sáv

Írd ki azokat a dolgozókat, akiknek a fizetése 350 000 és 420 000 Ft között van (a határokat is beleértve), fizetés szerint növekvő sorrendben.

Megoldás megnyitása

A BETWEEN mindkét határt tartalmazza. Ugyanez >= és <= jelekkel is megírható.

SQLSQLite
SELECT vnev, knev, fizetes
FROM adatok
WHERE fizetes BETWEEN 350000 AND 420000
ORDER BY fizetes;

Közepes szint

3. Több város és kizárás

Írd ki a győri és a pécsi dolgozókat (IN), majd azokat, akik nem ezekben a városokban laknak (NOT IN). Mi történik a címnélküli dolgozóval a második lekérdezésben?

Megoldás megnyitása

A NOT IN a NULL értékű sort is kihagyja, mert a NULL-lal végzett összehasonlítás nem igaz. Ha a címnélküli sor is kell, az OR cim IS NULL feltételt hozzá kell tenni.

SQLSQLite
SELECT vnev, knev, cim FROM adatok WHERE cim IN ('Győr', 'Pécs');

SELECT vnev, knev, cim FROM adatok WHERE cim NOT IN ('Győr', 'Pécs');

SELECT vnev, knev, cim FROM adatok WHERE cim NOT IN ('Győr', 'Pécs') OR cim IS NULL;

4. A legkisebb fizetés

Add meg egyetlen lekérdezéssel a legkisebb fizetést kapó dolgozó nevét. Két megoldást írj: MIN nélkül (ORDER BY és LIMIT), és MIN segítségével (al-lekérdezéssel).

Megoldás megnyitása

Az első megoldás a legegyszerűbb: növekvő rendezés, az első sor. A második egy al-lekérdezést (zárójelben álló lekérdezést) használ: előbb kiszámolja a legkisebb fizetést, majd azt keresi a táblában. A második megoldás több sort is ad, ha többen keresnek ugyanannyit.

SQLSQLite
SELECT vnev, knev, fizetes FROM adatok ORDER BY fizetes ASC LIMIT 1;

SELECT vnev, knev, fizetes FROM adatok
WHERE fizetes = (SELECT MIN(fizetes) FROM adatok);

Kihívás szint

5. Kereső fizetéssávval

Bővítsd a dolgozói keresőt: két mező (nudMinFizetes, nudMaxFizetes) szabja meg a sávot, és ha a minimum nagyobb a maximumnál, a program ne kérdezzen, hanem jelezzen.

Megoldás megnyitása

A határok ellentmondását az adatbázis-hívás előtt ellenőrizzük. A hibás sávnál a lekérdezés üres eredményt adna, de a felhasználónak érdemes megmondani, miért.

Form1.csC#
private void Frissit()
{
if (!betoltve)
{
return;
}

if (nudMinFizetes.Value > nudMaxFizetes.Value)
{
lblDarab.Text = "A legkisebb fizetés nem lehet nagyobb a legnagyobbnál!";
return;
}

// ... a lekérdezés a DB.Dolgozok(...) hívással, a max értékkel is ...
}

6. A kereső címnélküli sorokkal

Szeretnéd, hogy a kereső a NULL fizetésű vagy NULL című dolgozókat is mutassa, amikor a szűrők alaphelyzetben vannak (a városszűrő (összes), a minimum 0). Hogyan módosítod a lekérdezést?

Megoldás megnyitása

A hiányzó értékeket külön kell engedélyezni az IS NULL segítségével. Fontos, hogy a három feltétel zárójelezése helyes legyen, különben az OR a többi szűrőt is felborítja. A város szűrőjénél a (@varos = '' OR cim = @varos) már minden sort átenged üres szűrőnél, így a NULL című sor ott nem akad el. A fizetésnél kell az új feltétel.

DB.csC#
string sql = "SELECT id, vnev, knev, fizetes, cim FROM adatok "
+ "WHERE (vnev LIKE @minta OR knev LIKE @minta) "
+ "AND (fizetes >= @min OR (fizetes IS NULL AND @min = 0)) "
+ "AND (@varos = '' OR cim = @varos) "
+ "ORDER BY " + sorrend;

Gyors önellenőrzés

Válaszolj fejben, és csak utána nyisd le a választ.

1. Mit csinál a WHERE?

Szűri a sorokat: csak azok kerülnek az eredménybe, amelyekre a feltétel igaz.

2. Mire való a LIKE, és mit jelent a % jel?

Mintaillesztésre. A % bármennyi (akár nulla) karaktert jelent, a _ pontosan egyet.

3. Miért nem működik a = NULL?

Mert a NULL ismeretlen érték, és a vele végzett összehasonlítás sem igaz, sem hamis. A hiányzó értéket az IS NULL vizsgálja.

4. Mi a különbség az ASC és a DESC között?

Az ASC növekvő, a DESC csökkenő sorrend. Az ASC az alapértelmezett.

5. Miért kell a LIMIT mellé ORDER BY?

Mert ORDER BY nélkül nincs garantált sorrend, így nem tudod, melyik n sort kapod.

6. Miért nem lehet az oszlopnevet paraméterként átadni?

A paraméter mindig érték, nem a lekérdezés szerkezetének része. Az oszlopnevet a program rögzített szövegei közül választva fűzzük az SQL-hez, soha nem a felhasználó beírt szövegéből.

7. Hogyan lesz egy szűrő választható egyetlen lekérdezésben?

Az (@szuro = '' OR oszlop = @szuro) mintával: üres szűrőnél az első rész igaz, így a feltétel mindent átenged.

Mit tudsz már?

  • Használod a WHERE feltételeit, és helyesen zárójelezed az AND és OR kapcsolatokat.
  • Tudod, hogyan viselkedik a NULL az összehasonlításoknál.
  • Rendezel az ORDER BY, szűrsz a DISTINCT és a LIMIT segítségével.
  • Választható szűrőket építesz egyetlen paraméteres lekérdezésbe.
  • Tudod, miért nem paraméterezhető a rendezés, és hogyan oldod meg biztonságosan.

Következő lecke: INSERT, UPDATE és DELETE

Eddig adatokat olvastál. A következő leckében módosítod is őket: új sort veszel fel, meglévőt javítasz, és törölsz.