Hirdetés
- Magga: PLEX: multimédia az egész lakásban
- sziku69: Fűzzük össze a szavakat :)
- eBay-es kütyük kis pénzért
- Luck Dragon: Asszociációs játék. :)
- sziku69: Szólánc.
- Brogyi: CTEK akkumulátor töltő és másolatai
- SzDavid99: Barangolás egy ünnepi Nintendo-mekkává változott szöuli bevásárlóközpontban
- Sub-ZeRo: Euro Truck Simulator 2 & American Truck Simulator 1 (esetleg 2 majd, ha lesz) :)
- lezso6: Nemzetközi újév visszaszámlátor alkoholistáknak
- ubyegon2: Airfryer XL XXL forrólevegős sütő gyakorlati tanácsok, ötletek, receptek
Új hozzászólás Aktív témák
-
evko
aktív tag
Sziasztok, újra.
Szóval...
Van egy tanár, egy tantárgy és 1 megtartott tábla.
Miután az admin feltöltötte az adatokat a tanár és a tantárgy táblákba, azt kellene elérni, hogy a megtartott táblába hozzá tudjon rendelni 1 tanárhoz tartozó órákat... De pl. 1 matek és fizika órát adó tanárhoz ne lehessen hozzárendelni magyar órát... Csak azokat az órákat, amiket ő tarthat.
Ehhez elég ez a 3 tábla, vagy szükség van még plusz táblára, és hogy lehetne ezt megoldani? -
A 3xx-as HTTP kódoknál nincs konkrét tartalom (mintha csak a HTTP headert kérnéd le) és a Location header mezőben van az URL (abszolút, vagy relatív), ahova a böngészőnek tovább kell dobnia a teljes eredeti kérést. Persze az egyéb header mezőket is fel kell dolgozni (pl SetCookie, de ez a kliensen futó kód szempontjából ireleváns).
Amennyiben pl az általad említett példában az ID alapján is továbbít és nem kell a query, az lehet valami régi logika maradéka, vagy lehet benne plusz infó is, amit pl a redirect során a szerver eltárol (pl referrer link).
A konkrét esetben így néz ki egy kliens-szerver kommunikáció:
1) kliens "betölti" az alábbi oldalt:
https://rd.hirkereso.hu/rd/39891270?place=6544&partner=hirkereso&url=https%3A%2F%2Fprohardver.hu%2Fhir%2Fjon_lg_elso_hibrid_projektora.html
2) "rd.hirkereso.hu" megkapja a kérést és betölti a logikát, ami a "/rd/39891270" path feldolgozásáért felel
3) a betöltött logika megkapja az eredeti kérést a query elemekkel együtt:
/rd/39891270?place=6544&partner=hirkereso&url=https%3A%2F%2Fprohardver.hu%2Fhir%2Fjon_lg_elso_hibrid_projektora.html
4) eltárolja az adatbázisba a megfelelő elemeket, timestamp-ot és betölti annak az oldalnak az URL-jét, ahova redirect-elni kell
5) a logika nem HTTP 200 választ ad, hanem HTTP 3xx-t és a Location headerbe beállítja a redirect URL-t: https://prohardver.hu/hir/jon_lg_elso_hibrid_projektora.html
6) kliens megkapja a 302-es üzenetet és a redirect URL-t: https://prohardver.hu/hir/jon_lg_elso_hibrid_projektora.html
7) a kliens elküldi a kérést redirected URL-nek:
https://prohardver.hu/hir/jon_lg_elso_hibrid_projektora.html
8) a "prohardver.hu" betölti a "hir/jon_lg_elso_hibrid_projektora.html" path feldolgozásához szükséges logikát
9) a betöltött logika legenerálja a felhasználó számára a tartalmat és egy HTTP 200 üzenetben elküldi a kliensnek
10) a klines megjeleníti a betöltött weboldalt (illetve letölti az oldalhoz szükséges többi erőforrást) -
martonx
veterán
Áhá így már értem. Bocs az elején félreértettelek. Mivel SQL-t használsz, jobb módja nincs. Más módok is vannak persze, pl. beüzemelsz egy külön NoSql-t ehhez, inmemory SQL táblát használsz, felhőben valami Queue-ba dobod be az eventeket, és majd onnan egy microservice a maga nyugijában updatelgeti a DB-t, stb...
-
-
Taci
addikt
Végig követtem egy mysql-bin tartalmát, és azt találtam, hogy valamiért az összes cron job-om összes tartalma, rekordonként megismételve szerepel benne...
Tehát ha a full backup és az inkrementális backup között 2000 rekord került be, akkor a mysql-bin-ben 2000-szer szerepel az összes cron job összes tartalma...
A teljes sztorihoz tartozik, hogy anno, amikor elkezdtem ezt az oldalt csinálni, még WordPress-szel kezdtem, és ahhoz raktam fel egy beépülőt (WP Crontrol). A WP már rég nincs használatban, azonban a mai napig ezzel készítettem a cron jobokat, mondván, erre a tesztkörnyezetre ez tökéletesen megfelel, és majd a szolgáltatónál kitapasztalom a normál cron jobok (CPanelből) létrehozását.
Lehetséges, hogy ez a plugin kavar be? Ilyen bejegyzések vannak tonnaszámra a mysql-bin fájlokban:
UPDATE `wp_options`SET `option_value` = 'a:35:{i:1640350382;a:1:{s:17:\"crontrol_cron_job\ ...i:1640350473;a:1:{s:17:\"crontrol_cron_job\ ...i:1640350552;a:1:{s:17:\"crontrol_cron_job\ ...
stb. az összes cron job tartalma.
Aztán ez az egész rekordonként megismételve.Mivel ilyen tábla nem lesz (wp_options, hisz' ez WordPress-hez tartozik), gondolom, éles rendszerben (ahol WP a közelben sem lesz) ez a probléma már nem fog előjönni.
-
Taci
addikt
Azt hiszem, kezdem érteni.
FLUSH LOGS just closes and reopens log filesTehát nem rajta múlik, mi kerül a logokba. Ez csak lezárja az aktuális log fájlt, és nyit egy újat.
Valahol be kell állítanom, hogy ezekbe a logokba csak az adatbázist érintő bejegyzések legyenek elmentve (rekordok, táblák stb. módosításai, létrehozása stb.)
De ha már ezt így eddig összeraktam, hátha meglesz az is.
Persze ha valaki tudja a választ, kérem, írja meg.Bocsánat az előbbi regényért, de nagyon nem akart összeállni, és nem akartam fontos részletet kihagyni a "nyomozásból".
-
Szmeby
tag
Akkor is a saját scriptek tartalmát látod benne, amikor 7 kilo és akkor is, amikor 7 mega? Nem hinném.
Mármint lehet, hogy azt látod, de bizonyára más is van mellette, különben miért is lenne 7 mega.Az adatbázisok nem csak nyers adatokat tárolnak, vannak benne indexek, dolgozik egy rakás átmeneti adattal, metaadattal, millióegy dologgal. Miért ne írná ki ezeket az adatokat is fájlba? Attól még lehet az ebből exportált script töredék méretű, a kettő szerintem nem zárja ki egymást.
(Amúgy semennyire sem értek az adatbázisok lelkivilágához, de számomra így logikus.)
-
martonx
veterán
Szerintem egy kicsit kevered a dolgokat. A backup nem erre való. Backupolni elég naponta egyszer (gondolj bele, amikor X TB-os DB-ket 2 óránként próbálna meg bárki menteni
) . Amire te gondolsz az inkább egyfajta tükrözés, amikor két DB-d van, és időnként a másikba átszinkronizálod az adatokat.
Illetve még olyat szoktak, hogy inkrementálisan backupolnak, így nem foglal annyi helyet. -
martonx
veterán
Mondok egy nem is annyira extrém példát:
Amikor kézzel mókolsz a DB-ben, és te magad hibázol. Vagy ami még rosszabb, kiderül, hogy volt egy programhiba, ami elrontotta az adatokat.
Nem igazán értem, miért neked magadnak kell a backuppal bajlódnod. Jó mondjuk 8 éve csak felhőben dolgozok... -
-
martonx
veterán
"Na kipróbáltam, futott az update(-elő szkript) kb. fél percig, addig mint az őrült kattintgattam a weblapon (ezzel select lekérdezéseket generálva), és nem volt megakadás sehol sem."
Erről beszéltem, hogy igen, ezek a problémák, amik itt felvetődnek jogosak, és léteznek, de a te rendszered mire ide elér, hogy DB szinten lock stratégiákon kell gondolkoznod, és erre optimalizálnod lehet, hogy:
1. sose fog megtörténni
2. ha mégis akkor meg te leszel a következő magyar bank / telko
ez esetben zokszó nélkül fel fogsz tudni venni egy komplett fejlesztő csapatot 
Szóval elvileg nem haszontalan ezeken itt pörögni, gyakorlatilag viszont az

-
martonx
veterán
"Egy index a cikk_id-ra" - itt alapból is kellene indexnek lennie, mivel ez Foreign key. Probléma megoldva

Egyébként meg ez ugyan SQL fórum, de mivel egy web alkalmazásról beszélünk, csomó lehetőséged van tehermentesítened az adatbázist, anélkül, hogy belegörcsölnél az SQL minden mélységébe. Pl. cachelés
-
nyunyu
félisten
Nem tudom ki volt, de szerintem arra gondolhatott, hogy az update, delete az egész táblát szokta lockolni, amíg le nem fut, ha a where feltételeihez nem talál használható indexet.
Ilyenkor az összes többi select, insert addig vár, amíg a tábla fel nem szabadul.Ha van olyan index, ami alapján meg tudja határozni, hogy melyik sorok érintettek az update vagy deleteben, akkor csak azokat a sorokat lockolja a DB, tábla többi része használható marad.
De ez erősen DB motor függő, nem mindegyik kezeli ugyanúgy a tábla meg sor lockokat.
-
nyunyu
félisten
Eddig az hittem, hogy van több kategória táblád, amikben nem biztos, hogy ugyanolyan id tartozik ugyanahhoz a kategórianévhez, ezért verziózod őket.
.
Ekkor logikusan vagy a cikkek-ben kellene tárolnod a kategoria_verzio-t, ha egy cikkhez egyszerre csak az egyik halmazból/táblából tartozhatnak kategóriák.Ha előfordulhat az, hogy adott cikkhez az egyik kategória az egyik halmazból, másik meg a másik halmazból származik, akkor meg a cikk_kategoria-ban van a helye.
Utóbbi esetben ez NEM lenne redundáns!Ezért írtam azt, hogy a mi a francért nem vonod össze a kategória tábláidat, hogy csak egyféle id tartozzon ugyanahhoz a kategória névhez.
De most kiderült, hogy ezekről szó sincs, hanem a kategoria_verzio az ellenőrző szkriptednek egy flag, hogy az adott cikket már ne kelljen vizsgálnia?
Mivel itt 1:1 kapcsolat van a cikkekkel, így szerintem felesleges kitenni új táblába, jó helyen van az a mező ott.Ekkor viszont túlbonyolítottam az előzőekben írt queryket, felesleges volt a sok case when, meg kategoria_v1/v2/v3 join.
Túlzottan beszédes volt a kategoria_verzio név, csak nekem teljesen mást mondott, mint amire a költő eredetileg gondolt.
Igazából neked csak arra van szükséged, hogy néha szemmelverd a cikk-kategória összerendeléseket, aztán ha egy cikkhez rossz kategória van rendelve, akkor vagy törlöd a cikk_kategoria rekordot, vagy megupdateled benne a kategoria_id-t a megfelelő kategóriáéra, ahogy a #5318-ban írtam.
-
Taci
addikt
Átnéztem, köszönöm a belefektetett időt és energiát.
Ami kérdésem még lenne, az csak elméleti, szeretném érteni a dolgokat.
A cikkek és kategóriák kapcsolata egy külön táblában van (az előbbi példákban
cikk_kategoria ck). Itt egy-egy cikkhez több kategória is tartozhat, ekkor ennyi rekord van létrehozva hozzá. (csak az id-k).
Logikailag ide tartozna a korábban tárgyalt verzió (amivel a kategóriák vannak ellenőrizve,kategoria_verzio). Viszont mivel egy cikkhez több rekord is tartozhat, így ha itt lenne a verzió is, akkor ez az adat redundáns lehetne.Megéri ezért ezt egy külön táblába kivinni, ahol csak a
cikk_idés akategoria_verziolenne?
Adatbázis szempontjából melyik a jobb?Illetve még egy kérdésem lenne, amit az eleje óta nem értek:
Ha ez akategoria_verzioacikkek(a fő) táblában maradt volna, az miért lett volna baj? Az miben okozott volna gondot (adatbázis szempontjából), hogy abban a táblában kellett volna azt a mezőt frissítgetni?Csak szeretném érteni.
Köszönöm. -
Taci
addikt
Az első fele meg is van, a GROUP_CONCAT volt a megoldás rá. (Hamarabb is meglettem volna a teszttel, csak GROUP_CONTACT-ot írtam...
)Ez lett végül kb. belőle:
create view cikkek_vw asselect c.id cikk_id,c.cim cim,c.create_date datum,c.creator cikk_iro,GROUP_CONCAT(k.nev) AS kategoriakfrom cikkek cjoin cikk_kategoria ckon c.id = ck.cikk_idJOIN kategoriak AS kON ck.kategoria_id = k.idGROUP BY c.id cikk_id;Köszönöm!
A másik kérdéskörhöz (és a válaszaid feldolgozásához) picit több időre lesz szükségem.
De ott talán nem voltam teljesen egyértelmű azzal, mit szeretnék, miért is volt a verziózás használva.
Ha csak pár cikkről lenne szó, nyilván meg tudnám kézzel is csinálni a módosításokat. De 2 nap alatt kb. 1000 rekordnyi cikk jön, és ezt csak egy karbantartó szkripttel tudom kezelni.
Kategóriát nem nagyon tervezek hozzá adni, de ha úgy alakulna, hogy kell, az nem gond.
Viszont azt, hogy egy-egy cikk milyen kategóriába tartozik, már nem ilyen egyszerű. Ha észreveszek (vagy bejelentenek) egy anomáliát, hogy egy nem megfelelő / többértelmű kategória-szó miatt egy cikk rossz kategóriába is belekerült, azt az összes cikknél ellenőriznem kell. (Mint pl. az előbb láttam: alapból az Ünnepek kategóriába kerül egy cikk, ha a karácsony szó a cikkel kapott kategóriák közt van - viszont most jó pár cikk, ami politikai irányzatú, a szó miatt szintén az Ünnepek kategóriába került, pedig ott nincs helye). Ilyenkor azt a kategória-szót vagy ki kell vennem, vagy egy exclude-tömbbe rakni (HA karácsony ÉS politika, akkor NEM ünnepek). Ezeket nyilván nem lehet egyesével kezelni, visszamenőleg is át kell nézni az összes cikket. Erre van egy szkriptem, szépen működik.Erre írtam, hogy ez a verziót figyeli, mert ami a legfrissebb kategóriatömb-verzióval került az adatbázisba, azt nem kell nézni, mert az már jól van mentve. A többit viszont át kell nézni.
Ez az ellenőrző szkript óránként lefut. Mivel ettől sokkal ritkábban frissítem csak a kategória-tömb tartalmát, ezért a legtöbbször csinál egy gyors ellenőrzést, látja, hogy minden rekord a legfrissebb verziójú kategória-tömbbel van kezelve, úgyhogy nincs dolga.De ha ezt a verziózást kiveszem belőle, akkor óránként végig kell mennie az összes rekordon, ott mindehol legenerálni, hogy az aktuálisan legfrissebb verziójú kategória-tömb szerint milyen kategóriákba kell tartoznia a cikknek, ellenőrizni, hogy úgy van-e elmentve az adatbázisban, és ha nem, cserélni.
Napi kb. 500 új cikk kerül be (és ez csak több lesz), valahogy muszáj vagyok skippelni azokat a rekordokat az ellenőrzésből, amiket nincs értelme ellenőrizni, mert az aktuálisan legfrissebb változat szerint kerültek be. Másképp sosem lenne vége, és a szolgáltató is kidobna, plusz annyi ideig tartana, hogy a max execution time-ot is túllépném bőven.Lehet, már megírtad a jó választ erre a kérdésre is az előbbiekben, még át kell néznem alaposan.
De ha esetleg mégsem, akkor talán mégis az lesz a legegyszerűbb, ahogy most van megcsinálva:
A cikkek táblában egy mező a verziónak, amivel a rekord kategóriái fel lettek töltve, egy másik mező pedig a rekord kategóriáinak, szövegesen.
Ha változik a verzió, mert javítani kellett kategória-szavakat (lásd az előbbi példa), akkor frissítés az összes nem-legfrissebb rekordon ezen a két mezőn.Amúgy az én hibám, visszaolvastam, ezt a verziós dolgot nem írtam az elején, pedig fontosabb, mint az, hogy a kategóriák nevét lássam.
Gondolom, túl nagy gondot nem okoz a rendszernek, hogy két mezőt frissítgetni (UPDATE) kell.
-
nyunyu
félisten
Ja, hogy egységesíteni akarod a cikk_kategoria összerendeléseket?
Akkor
1) öntsd bele egy közös táblába az összes eddigi kategórianevedet (legyen kategoria_uj a példa kedvéért)2) csinálj egy cikk_kategoria_uj táblát, amiben már nincs kategoria_verzio oszlop, többi ugyanaz, mint az eddigi cikk_kategoria-nal.
3) töltsd fel tömegesen a cikk_kategoria_uj táblát:
merge into cikk_kategoria_uj i
using (
select a.cikk_id,
a.cim,
a.kategoria_nev
a.kategoria_verzio,
a.kategoria_id kategoria_id_regi
k.kategoria_id kategoria_id_uj
from (
select c.id cikk_id,
c.cim,
ck.kategoria_id,
ck.kategoria_verzio,
case
when ck.kategoria_verzio = 1 then k1.nev
when ck.kategoria_verzio = 2 then k2.nev
when ck.kategoria_verzio = 3 then k3.nev
end kategoria_nev
from cikkek c
join cikk_kategoria ck
on ck.cikk_id = c.id
left join kategoria_v1 k1
on k1.id = ck.kategoria_id
left join kategoria_v2 k2
on k2.id = ck.kategoria_id
left join kategoria_v3 k3
on k3.id = ck.kategoria_id) a
join kategoria_uj k
on k.kategoria_nev = a.kategoria_nev) x
on (i.cikk_id = x.cikk_id and i.kategoria_id = x.kategoria_id_uj)
when not matched
then insert (cikk_id, kategoria_id)
values (x.cikk_id, x.kategoria_id_uj);4) ELLENŐRIZD az új táblákat:
select c.id,
c.cim,
ck.kategoria_id,
case
when ck.kategoria_verzio = 1 then k1.nev
when ck.kategoria_verzio = 2 then k2.nev
when ck.kategoria_verzio = 3 then k3.nev
end kategoria_nev
from cikkek c
join cikk_kategoria ck
on ck.cikk_id = c.id
left join kategoria_v1 k1
on k1.id = ck.kategoria_id
left join kategoria_v2 k2
on k2.id = ck.kategoria_id
left join kategoria_v3 k3
on k3.id = ck.kategoria_id;
vsselect c.cid,
c.cim,
ck.kategoria_id,
k.nev kategoria_nev
from cikkek c
join cikk_kategoria_uj ck
on ck.cikk_id = c.id
join kategoria_uj k
on k.id = ck.kategoria_id;5) ha egyeznek, akkor átnevezed a régi táblákat valami másra.
ha nem, akkor átgondolod, mit szúrtál el/mi maradt ki.6) új táblákat átrakod a régiek helyére:
rename cikk_kategoria_uj to cikk_kategoria;
rename kategoria_uj to kategoria;7) ha már mindent 3x ellenőriztél, akkor eldobhatod az 5)-nél átnevezett táblákat.
8) itt jön az előző hozzászólásom.
NAGYON bátrak már az 5) pontnál tolhatják drop table-t.
Aztán utólag ne panaszkodjanak, hogy DDLre nincs undo. -
nyunyu
félisten
Nem teljesen értem, mit akarsz feleslegesen verziózni rajta.
Vedd a legfrissebb kategória táblázatodat, aztán annak az ID-it használd minden cikkhez.
Aztán ha jön egy új kategória, akkor csak egy helyre kell beszúrni egy új rekordot, és annak az IDját használod az új cikkhez.Ha meg egy cikk rossz kategóriába került, és utólag kézzel kell javítani?
Akkor átütöd a rossz cikk_kategoria rekordot.De az erősen kézi hajtány:
merge into cikk_kategoria u
using (
select c.id cikk_id,
c.cim cim
k1.id rossz_kategoria_id,
k2.id jo_kategoria_id,
from cikkek c
join kategoria k1
on k1.nev = 'rossz kategória'
join kategoria k2
on k2.nev = 'jó kategória') x
on (u.cikk_id = x.cikk_id and u.kategoria_id = x.rossz_kategoria_id)
when matched
then update
set u.kategoria_id = x.jo_kategoria_id;(nem mertem sima update szintaxissal írni, mert tuti belegabalyodnék és/vagy egy sor helyett a fél táblát updateelné az Oracle
) -
nyunyu
félisten
Valahogy meg lehet csinálni, hogy 1 cikk csak egyszer szerepeljen (ezt a distinct vagy a group by megoldja), és hogy a különböző kategóriák vesszővel elválasztva egy új mezőben legyenek az adott egy darab cikk rekordjában?
Persze, ha a nézetben aggregálod a rekordokat valamilyen függvénnyel:
Oracle alatt valahogy így nézne ki:
create view cikkek_vw as
select c.id cikk_id,
c.cim cim,
c.create_date datum,
c.creator cikk_iro,
listagg(ck.kategoria_id, ', ') within group (order by ck.kategoria_id) kategoria_id,
listagg(k.nev, ', ') within group (order by ck.kategoria_id) kategoria_nev
from cikkek c
join cikk_kategoria ck
on c.id = ck.cikk_id
join kategoriak k
on ck.kategoria_id = k.id
group by c.id, c.cim, c.create_date, c.creator;listagg() függvény nem része az SQL szabványnak, nem tudom, a Te DB motorod alatt van-e hasonló aggregálási lehetőség, illetve milyen szintaxissal.
(MySQL alatt GROUP_CONCAT, MS SQL alatt STRING_AGG)Ilyenkor a végére KELL a group by, mert az fogja megmondani, hogy milyen mezők alapján csoportosítsa/vonja össze a sok találatot egy-egy rekordba.
within group (order by valami) meg azt mondja meg, hogy a vesszővel felsorolt elemek mi szerint legyenek sorbarakva.
(gondolom IDnál és a névnél is ugyanazt a rendezést akarod használni
) -
nyunyu
félisten
create view cikkek_vw as
select c.id cikk_id,
c.cim cim,
c.create_date datum,
c.creator cikk_iro,
k.id kategoria_id,
k.nev kategoria
from cikkek c
join kategoria k
on k.id = c.kategoria_id;Aztán ezt már úgy kérdezed le utólag, ahogy akarod:
select *
from cikkek_vw
where kategoria = 'receptek'
order by cim;Aztán libasorban felsorolja neked a krumplileves, mákos guba, pejsli, töltött paprika receptes cikkek fő adatait.
Az már egyéni ízlés vagy munkahelyi megszokás kérdése, hogy a nézetek elnevezésénél V_ előtagot, vagy _VW utótagot használsz.
-
DeFranco
nagyúr
nem vagyok adatbázisguru csak tanultam alapszinten meg használom, de elvileg nem ismételünk szükségtelenül adatokat, mert ahogy írtad is, redundáns.
erre egy view-t hoznék létre én is, ami lekérdezi és összekapocsolja az adatbázistáblákból az adatokat ott tudod szemrevételezni, hogy minden OK-e
-
-
nevemfel
senior tag
Van értelme annak, hogy a weblappal kapcsolatos műveletekhez (rekordok felvétele, lekérdezése és frissítése) létrehozzak egy másik felhasználót, aminek csak a valóban elengedhetetlen jogokat adom meg?
Van. Security alapelv, hogy pont annyi jogosultságot adsz az adott alkalmazásnak, amennyi a működéséhez szükséges.
-
nyunyu
félisten
Egyelőre szerintem indulj el, aztán ha már úgy látszik, hogy a táblaméretek növekedésével egyre lassabb, akkor ráérsz optimalizálni.
Akkor már úgyis lesz elég adatod a felhasználói szokásokról, és egyértelműbben kirajzolódik, hol van a szűk keresztmetszet.1% sebességnövelésre nem érdemes napokat elszúrni.
Nekem is volt olyan projektem, ahol engem cseszegettek állandóan, hogy túl sokáig fut nagy ügyfelek esetén az adatmigráció teljességét vizsgáló querym, végül már a DBA guruink optimalizálták, de úgy sem lett sokkal jobb a helyzet, talán 10%-ot tudtak nyerni az indexeléssel és egyéb mágiával.
Legnagyobb telefonelőfizető vállalatot 12 óra alatt tudtuk végigkergetni, ebből 2-3 óra volt az adatkonverzió, és 9-10 az adathelyesség+teljesség ellenőrzés.Aztán projekt végén távoztak a főokosok, akik az adatellenőrző motort fejlesztették, és én örököltem meg a kódjukat a következő projekthez, mondván van már elég gyakorlatom a ellenőrző funkciók írásában.
Sikerült beüzemelni, főnököm elment demózni, hogyan lehet egy mozdulattal megszüntetni 120 ezer előfizetést, aztán 20 perc múlva idegesen telefonál, hogy még mindig nem jött be a következő képernyő.
Végül valami 2 óra volt, mire egy táblában szereplő 120 ezer rekordot sikerült kikeresni pár másik táblából, és a demó alkalmazás továbblépett a következő képernyőre.Kézzel megfuttattam ugyanazt az ellenőrző queryt, 3 perc alatt lefutott.

Na, akkor jobban nekiálltam átnézni az ellenőrzéseket futtató motort, és észrevettem, hogy a kolléga minden rekordra, minden egyes query futtatására kurzort használt, így a tömeges adatellenőrzésre szolgáló query annyi példányban futott libasorban, ahány sor volt a táblában

Plusz megfejelve a dinamikusan összerakott SQL futattatásának a hívásonkénti overheadjével (120e rekordnál az bő egy óra!)Utána egy hétig faragtam az örökölt kódot, mire kiírtottam belőle az összes létező kurzort, hogy az összes query az egész adathalmazra egyben fusson, és egyszer legyen csak dinamikus SQL hívva.
Eredmény? 2 óra helyett 3 perc.Ha visszaportoltam volna az újraírt motort a migrációs projektbe, akkor "kedvenc" nagyvállalatunkat 12 helyett 3 óra alatt le tudtuk volna futtatni, ugyanazokkal az ellenőrző querykkel...
Szóval az optimalizálnivaló nem mindig ott van, mint amire először gondolnál!
-
nyunyu
félisten
Indexet csak a leggyakrabban keresett/joinolt oszlopokra érdemes tenni.
Ha a hébe-hóba kérdezett feltételeket is indexeled, azzal többet ártasz, mint használsz, mert az új rekordok beszúrása, illetve régiek törlése is lassul minden egyes plusz indexszel.Törlés+index létrehozás, újraépítés max akkor segít, ha nagyon sok rekordot töröltél a táblából, és emiatt lyukas lesz az index, és nem működik optimálisan.
De ez megint a többmillió soros táblák problémája, alatta jellemzően nem nyersz sokat azzal, hogy újraépíted.Szélsőséges példa: van egy millió soros táblád, ennek az indexe is millió rekordot tartalmaz.
Kitörölsz a táblából 900k rekordot, ekkor az index mérete nem változik, továbbra is 1m helyet foglal, lesz benne 900k lyuk.
Újraépítés után az index mérete lecsökken a maradék 100k-ra, így 10x gyorsabban lehet majd végigmenni rajta, mint újraépítés nélkül.
(B-fáknál log2(10) a gyorsulás?) -
martonx
veterán
1. Ember csináld már meg amit akarsz, ne tökölődj a mi lesz majd 50 év múlva ha nyerek a lottón szintű problémákon.
2. amit linkeltél MS SQL-re vonatkozik, tudtommal te valami játszós DB-t használsz (MariaDB vagy MySQL vagy valami ilyesmit).
3. Egyébként igen, van amikor karban kell tartani az indexeket, nyugodj meg, te sose futsz ilyen esetbe bele, vagy ha igen, addigra már rég milliárdos leszel, és lesz DB admin embered, aki majd elszórakozik az ilyen problémával. -
nyunyu
félisten
Azt neked kell végiggondolni, hogy a táblád legszélesebb oszlopát indexelni akarod-e.
Ha igen, az (közel) megduplázza a tábla helyigényét, de legalább minden beszúrás, törlés művelet sokkal lassabb lesz, hiszen az indexet is karban kell tartania.Kérdés, hogy ez megéri-e azt, hogy néha-néha like '%%'-kal akarj benne keresni.
De mivel egy sokszázezer soros táblára tett többezer karakter széles index sem fog beleférni a memóriába, így szerintem tökmindegy, hogy az eredeti táblán megy a full table scan, vagy a nem sokkal kisebb indexet kell végigolvasnia először, és csak utána éri el a táblát.
Gyors az nem lesz... -
martonx
veterán
Szerintem megválaszoltad magadnak. A hídon majd ráérsz akkor átmenni, amikor odaértél.
Egyébként is van sok lehetőséged, én a helyedben, amikor a mostani megoldás elkezd kevés lenni (ha lesz ilyen), akkor első körben megpróbálnám a felvázolt full text search-öt SQL oldalon megvalósítani.
De ennél is jobb tud lenni, ha beüzemelsz egy ElasticSearch szervert az SQL-ed fölé, és ez szolgálja ki a szöveges kereséseket. Bár ez elég overkill. -
nyunyu
félisten
Ugyanúgy, where alá megy a szűrő alquery:
SELECT f.item_id, f.item_date
FROM
(SELECT item_id, item_date
FROM items
ORDER BY item_date ASC LIMIT 1000) AS f
INNER JOIN items_categories AS fic
ON f.item_id=fic.item_id
WHERE f.item_id not in (select t.item_id
from items_categories t
where t.category_id IN (27))
GROUP BY f.item_id, f.item_date
ORDER BY f.item_date ASC LIMIT 4Tehát a termék-kategória párosokból leválogatod azokat a termékeket, akik a nemszeretem kategóriában vannak, és ezzel szűröd az eredeti terméklistát.
-
nyunyu
félisten
Ugyanezt benéztem nemrég melóban.
Még szerencse, hogy a tesztelők is átnézték a GDPR törlendő szerződések listáját, és kiszúrták, hogy a left join tiltolista + where tiltolista.id is not null átengedte azokat a szerződéseket, ahol csak az egyik ügyfél volt tilitólistás, másik nem.
Írhattam át a queryt where ügyfél not in (select id from tiltolista)-ra. -
Taci
addikt
Saját kútfőből erre jutottam: DB Fiddle
Van esetleg valakinek jobb/másabb ötlete? Valós adatbázison még csak most fogom kipróbálni, nem tudom, mennyire lehet gyors/lassú.
@nyunyu: Most látom csak, hogy írtál, máris nézem, köszönöm.
Oh, valóban, pont ellenkezőleg gondolkodtam... Köszönöm az irányba állítást!
-
nyunyu
félisten
Fordítva gondolkozol.
Nem azokat kell megmutatni, amik nem 27-es kategóriájúak, mert akkor a többszörös kategóriából csak azt az egy példányt zárod ki, nem az összeset.
Helyette azokat a termékeket nem szabad megmutatni, amik 27-esek.
Úgy biztosan kizárja a terméket, akárhány kategóriába tartozik is a 27-esen kívül.SELECT item_id, item_date
FROM items
WHERE
item_id NOT IN (select item_id from items_categories where
category_id in (27))
ORDER BY item_date ASC LIMIT 4 -
nyunyu
félisten
Azt ne felejtsd ki a képletből, hogy a count() lekérdezésnek a végeredménye egy szám, míg a count() nélkülié egy adathalmaz, aminek a DB szerverről letöltése lényegesen tovább tart.
Tegnap kellett lementenem egy 850k soros táblához kapcsolódó adatokat CSVbe, eltartott vagy 20 percig, mire lejött a 160 mega adat a céges VPNről.
Ugyanez select count(*) from (eredeti query)-vel 5 másodpercig tart. -
-
Simán meg lehet trükközni a lekérdezést, hogy pl csak a legfrissebb 1000-ben keresse az első ötöt (persze, ha lesz annyi és nem került be az összes a "rossz" kategóriába):
SELECT i.item_id, i.item_date
FROM (
SELECT item_id, item_date
FROM items
ORDER BY item_date DESC LIMIT 1000) as i INNER JOIN items_categories AS c ON i.item_id=c.item_id
WHERE
c.category_id NOT IN (1,3,13,7,20) AND
i.item_id NOT IN (117,132,145,209,211)
GROUP BY i.item_id, i.item_date
ORDER BY i.item_date DESC LIMIT 4 -
A létrehozott index sokat nem segít, mivel az egy 'compound index' (csak akkor működik, ha egyszerre használod a két mezőt inkluzív keresésre, és egyszerre tudja használni mind a kettőt). Két külön indexszel talán valamit javulna.
Amúgy akármit is csinálsz úgy tűnik a kapcsolótábla nagy mérete miatt - és mivel nem magát a táblát, hanem annak egy előszűrt nézetét használod - szinte biztosan lesz jelentős adatmozgás (ez látszik a "sending data" szekcióban).
Ahogy nézem a mariadb nem igazán tudja rendesen használni az indexeket (pontosabban a MERGE módot), DISTINCT és/vagy GROUP BY kifejezésekkel együtt, mindenféleképp temp táblát szeretne alkalmazni.
Ezért is kisebb az első esetben létrehozott temp tábla (mivel itt csak egy mező jön létre és ezzel hasonlítja össze az item_id-t). A második esetben meg létrehozza a joinolt temp táblát (három mezővel) és mivel utána group by/distinct van így nem MERGE-et használ, hanem csinál egy teljes table dump-ot :/Tényleg meg kell próbálni valami mással, mert még a MySQL doksiban is azt olvastam, hogy még pl a DISTINCT esetében is szépen kell működnie index-szel, ha a sorbarendezett mező sorbarendezett indexet használ.
-
nyunyu
félisten
Ha Azureban gondolkodsz, akkor érdemesebb lehet SQL Server (Express) irányban nézelődni először.
Ha bejön, akkor onnan már minimális kód módosítással igénybe tudod venni az SQL Server felhős változatát. -
Ahogy jobban megnéztem a probléma azzal van, hogy igazából nem sikerült rendes indexet létrehozni.
A primary indexhez lett hozzáadva a dátum, mint key oszlop, pedig saját indexet kellett volna csinálni:--
-- Indexes for table `items`
--
ALTER TABLE `items`
ADD PRIMARY KEY (`item_id`);
ALTER TABLE `items`
ADD INDEX (`item_date` ASC); -
Ha sorba szeretnéd rendezni, akkor majdnem mindegy, hogy ASC-ba, vagy DESC-be rakod az indexet, gyorsan kell működjön, Igazából az ASC azért lenne elméletileg jó a DESC sorbarendezésnél, mert az indexet növekvő sorrendben hozza létre így az index végén levő (legnagyobb értékek) rögtön rendelkezésre kell álljanak. Mondjuk egy-egy execution plan-t jó volna látni mindegyikre...
-
Esetleg:
SELECT i.item_id, i.item_date
FROM items as i INNER JOIN (
SELECT item_id FROM items_categories
WHERE
category_id NOT IN (1,3,13,7,20) AND
item_id NOT IN (117,132,145,209,211)
GROUP BY item_id
) AS c ON i.item_id=c.item_id
ORDER BY i.item_date DESC LIMIT 4Ennél már nem tudom jobban bonyolítani

De egy item_date Sorted Index-szel szerintem többre mennél, ha mindenféleképp sorba szeretnél rendezni:
https://www.mssqltips.com/sqlservertip/1337/building-sql-server-indexes-in-ascending-vs-descending-order/ -
Ráadásul: "amire amúgy már az elején, az első Select-nél is meg lehetne csinálni, mert úgyis csak abban van a mező, ami alapján rendez, így kár a kibővített találati táblán rendezgetni."
Nincs olyan, hogy első Select. Amit te elsőnek nézel, az a külső select, tehát az hajtódik végre utoljára, tehát subselect először, külső select másodszor.Join+GroupBy nekem több rápróbálás után kb ugyanúgy (~500ms) működik, mint a subselect:
SELECT i.item_id, max(i.item_date) as max_date
FROM items as i INNER JOIN items_categories c on i.item_id=c.item_id
WHERE
c.category_id not in (1,3,13,7,20) and
c.item_id not in (117,132,145,209,211)
group by i.item_id
ORDER BY max_date DESC LIMIT 4 -
Az orderby csak a fő selectre hat, a subselect egyszer legenerálódik és azután csak újrahasznosítódik.
Az utolsó kérdésre:
A query mindig a motor optimális futása alapján történink, nincs értelme order by-nak belül (ha nincs limit is mellé), mert a subquery/temp tábla lekérdezése nem garantálja a rekordok sorrendjét. -
Taci
addikt
Meg lehet azt valahogy csinálni, hogy ne a subquery-ben lévő Select-re (select item_id) alkalmazott fő Select-re (select item_id, item_date) alkalmazza az Order By-t, hanem csak a fő Select-re?
Ahogy írtam is a bejegyzésben, amire itt válaszolok most, külön-külön gyorsak a lekérdezés részei:
select item_idfrom items_categorieswherecategory_id not in (1,3,13,7,20) anditem_id not in (117,132,145,209,211)
Showing rows 0 - 24 (768981 total, Query took 0.0232 seconds.)SELECT item_id, item_dateFROM itemsORDER BY item_date DESC LIMIT 4
Showing rows 0 - 3 (4 total, Query took 0.0057 seconds.)És ha egyben van, akkor pedig az egészre nézve az Order By lassítja le:
Order By benne:
SELECT item_id, item_dateFROM itemsWHEREitem_id IN (select item_id from items_categories wherecategory_id not in (1,3,13,7,20) anditem_id not in (117,132,145,209,211))ORDER BY item_date DESC LIMIT 4Showing rows 0 - 3 (4 total, Query took 0.5749 seconds.)
Order By nékül:
SELECT item_id, item_dateFROM itemsWHEREitem_id IN (select item_id from items_categories wherecategory_id not in (1,3,13,7,20) anditem_id not in (117,132,145,209,211))LIMIT 4Showing rows 0 - 3 (4 total, Query took 0.0295 seconds.)
Ezért gondoltam arra, ha a Group By-t "le lehetne tudni hamarabb", akkor már kellően gyors lehetne az egész lekérdezés. Mert így a join-olt (ez így subquery-vel amúgy Join-nak számít?) táblán sokat időzik az Order By miatt - amire amúgy már az elején, az első Select-nél is meg lehetne csinálni, mert úgyis csak abban van a mező, ami alapján rendez, így kár a kibővített találati táblán rendezgetni.
Így lehetne optimális:
SELECT item_id, item_dateFROM itemsORDER BY item_date DESC -- <----WHEREitem_id IN (select item_id from items_categories wherecategory_id not in (1,3,13,7,20) anditem_id not in (117,132,145,209,211))LIMIT 4Persze ez itt a szintaktika nem helyes. Ahogy nézegettem, kb. ilyesmi módon lehetne helyesen lekérdezni:
SELECT item_id, item_dateFROM(SELECT item_id, item_dateFROM itemsORDER BY item_date DESC) AS iWHEREitem_id IN (select item_id from items_categories wherecategory_id not in (1,3,13,7,20) anditem_id not in (117,132,145,209,211)) LIMIT 4És ez már elég gyors is:
Showing rows 0 - 3 (4 total, Query took 0.0156 seconds.)Viszont itt van egy furcsaság:
Nálam a saját gépemen futtatva más eredményt ad a 2 változat. Az első (ahol a Group By a végén van) adja a helyes eredményeket, ez a legutóbbi pedig teljesen más rekordokat mutat, és nincsenek item_date alapján rendezve sem.VISZONT
DB Fiddle-ben ugyanazok az eredmények, tehát ott meg elvileg jó az új lekérdezés:
- Eredeti: [link] (item_id: 213, 212, 210,208)
- Új: [link] (item_id: 213, 212, 210,208)Erre az új fajta lekérdezésre, és a különböző eredményekre tudtok mondani valamit?
Esetleg finomítani ezen a változaton? (Ha pl. nem raktam bele az "AS i"'-t, akkor szólt, hogy "Every derived table must have its own alias". Így viszont a Where után lehet, hogy i.item_id kellene? Bár nem változtat az eredményen. Csak hátha ti láttok még benne valamit, azért írtam ezt a példát, hogy ez nekem csak Google-keresés eredménye. Hátha lehet csiszolni.) -
nyunyu
félisten
SQL99 óta azt javasolja a szabvány, hogy csoportosításnál, rendezéskor írd ki a teljes mezőt, függvényt, akármit.
Oszlopra sorszámmal hivatkozás az ennél régebbi szintaxis maradvány, már nem szabványos.Eddig csak Oracle kódokban láttam használatban, de ahogy nézem jó pár DB kezelő ismeri ezt a szintaxist.
-
Taci
addikt
Még annyi, hogy külön-külön a rész-lekérdezések gyorsak:
select item_idfrom items_categorieswherecategory_id not in (1,3,13,7,20) anditem_id not in (117,132,145,209,211)
Showing rows 0 - 24 (768981 total, Query took 0.0232 seconds.)SELECT item_id, item_dateFROM itemsORDER BY item_date DESC LIMIT 4
Showing rows 0 - 3 (4 total, Query took 0.0057 seconds.)Szóval nem tudom/értem, összekapcsolva hogyan lesz ebből 0.8 mp.
-
további problémának látom a felesleges joinokat.
mivel az items_categories táblában a category_id egyenlő a categories táblában a category_id-vel, ezért csak abból a célból, hogy szűrni lehessen rá, tök felesleges összejoinolni a kettőt. az items_categories táblában pont ugyanúgy lehet szűrni a category_id-re, mint a categories táblában.pontosan ugyanez igaz az item_id-re.
tehát azt kellene csinálni, hogy az items_categories táblából leválogatod azt, ami kell, berakod egy subselectbe (ha a mysql vagymi tud olyat, nem ismerem), és utána ahhoz joinolod hozzá a végén a két plusz táblát.
ekkor a két plusz tábla joinját már csak a leszűrt termékekre csinálja meg, és sokkal gyorsabb lesz.valami ilyesmi postgresben az elmélet:
with tetelek as (select * from items_categories wherecategory_id not in (1,3,13,7,20) anditem_id not in (117,132,145,209,211)) select * from items,tetelek,categories where
tetelek.item_id = items.item_id and
tetelek.category_id = categories.category_id;a lényeg, hogy a valódi szűrést a tetelek selectjében érdemes megcsinálni.
-
-
-
-
martonx
veterán
Észrevételeim:
1. Select *-ot el kellene felejteni, és ki kellene írni azokat mezőket amiket ki szeretnél listázni.
2. Group By-nál szépen leírja, hogy mi a baja: bele kell venni a többi listázandó mezőt is (érdemes utána járnod, hogy mi is az a group by, mysql, mariadb specialitás, hogy a példádban szereplő szintaktikailag helytelen group by egyáltalán futni tud bizonyos helyezetekben).
3. Önszopatás a táblák mezőit a táblanévvel kezdődően elnevezni. Ha van egy táblád, aminek categories a neve, akkor annak id, és name mezői legyenek, ne pedig category_id, category_name.
4. Nekem ez 4 ms alatt lefut, bár nyilván több szemszögből sem lehet összehasonlítani a te adataiddal (eltérő adat mennyiség, és MySql vs MariaDB, localhostos erős géped, vs. valami ingyenes osztott hosting a dbfiddle alatt).
SELECT DISTINCT *
FROM items AS i
JOIN items_categories AS ic
ON i.item_id = ic.item_id
JOIN categories AS c
ON c.category_id = ic.category_id
AND c.category_id NOT IN (1,3,13,7,20)
WHERE i.item_id NOT IN (117,132,145,209,211)
ORDER BY i.item_date DESC5. Az Item nevű tábláktól idegrángást kapok. Légyszi nevezzük már el normálisan a táblákat. Jó, hogy nem fiszfasz, meg izé nevű tábláid vannak fiszfasz_izé nevű kapcsolótáblákkal. Aztán amikor 2 év múlva ránézel, te se fogod érteni, hogy mit is akartál az egyes táblákkal leképezni.
-
martonx
veterán
Nem tennél fel ide DB sémát, és adatokat? DB Fiddle - SQL Database Playground (db-fiddle.com)
Mert így leginkább csak magaddal tudsz társalogni. -
nyunyu
félisten
nekem az kellene, hogy a feed_id-kra legyen vonatkoztatva, tehát a képernyőfotós példában a 100111 csak egyszer szerepeljen.
Ha kiveszed a lekérdezésből a category_id-t, akkor minden feed_id csak egyszer fog szerepelni.
DISTINCT mindig a lekérdezésben szereplő összes oszlopra nézi az egyediséget!
-
-
Taci
addikt
Nagyon bénának és kapkodósnak érzem magam, pedig nem vagyok (kapkodós).
De ahogy most újra lefuttattam a lekérdezést (JOIN, amiben ott van az ORDER BY) is, láttam, hogy hiába van benne az elején a DISTINCT, a 4 találatból 3-nak ugyanaz a feed_id-ja.
Aztán fogtam, kiszedtem a DISTINCT-et (az ORDER BY ugyanúgy benne maradt), és a korábbi 19 mp-es lekérdezésből hirtelen 0.0615 mp-es lett... ugyanazzal az eredménnyel...

-
nyunyu
félisten
Sokszorozás:
- Végigmész egy kurzorral a termék táblán, megjegyzed az eredeti adatait.
- kiosztasz neki egy új azonosítót
- beszúrsz egy új termék rekordot az eredeti adataival, csak id helyére az újat írod
- leválogatod a termék_kategoria táblából az eredeti terméked kategória értékeit
- beszúrod a termék_kategóriába az új termék_id, régi_kategória_id párosokat
- tetszés szerint ismétled-- sokszorozás
declare
regi_id number;
regi_name varchar2(100);
uj_id number;
i number;
cursor cur is
select p.id,
p.name
from product p
where instr(p.name,'_') = 0;
begin
open cur;
loop
fetch cur into regi_id, regi_name;
exit when cur%NOTFOUND;
for i in 1..1000
loop
uj_id := product_seq.nextval;
insert into product (id, name)
values (uj_id, regi_name || '_' || to_char(i));
insert into product_category (id, product_id, category_id)
select product_category_seq.nextval,
uj_id,
pc.category_id
from product_category pc
where pc.product_id = regi_id;
end loop;
end loop;
close cur;
end;Próbáltam SQLFiddlében összerakni a múltkori gyümölcsös példámat, de valamiért nem tetszik neki a szintaxis.
Hús-vér Oracle alatt persze simán legenerál 1000 új példányt minden termékből.
Ha a DB kezelőd automatikusan inkrementálja az id mezőt (kb. mindenki, kivéve Oracle
), akkor a for ciklus belsejében először beszúrod az új termék példányt, aztán leselectálod az id-jét az uj_id változóba, és úgy használod a termék_kategória beszúrásnál.Pl. SQL Serveren valahogy így
...
while @i <= 1000
begin
insert into product (name)
values (regi_name || '_' || convert(varchar(10),i));
select p.id
into @uj_id
from product p
where p.name = regi_name || '_' || convert(varchar(10),i));
insert into product_category (product_id, category_id)
select
@uj_id,
pc.category_id
from product_category pc
where pc.product_id = regi_id;
set @i = @i +1;
end;
..(Még jó, hogy mindenki másképp írja a szabvány SQLen felüli részt.)
-
nyunyu
félisten
Nem karbantartható.
Ahányszor jönne egy új kategória, annyiszor felvennél kézzel egy új oszlopot a táblába, és megupdatelnéd az összes rekordot 0 értékkel?
Plusz az összes lekérdezést átírnád, hogy az új oszlopot is figyelje?Külön kategória táblában akárhány kategória elfér, csak egy új rekordot kell beszúrni, aztán annak az ID-ját hozzárendelni a termékhez.
Nem jár adatszerkezet és kód módosítással! -
Taci
addikt
(Mivel egyelőre még nem tudom kipróbálni a megoldást, így csak agyban tudok a témán "dolgozni", és az jutott eszembe, hogy) mi lenne, ha a ~30 kategóriának csinálnék egyszerűen mezőket a jelenlegi táblában? Lenne category1, category2 stb. nevű mező, értékként 0, ha a rekordhoz nem tartozik, 1, ha igen.
Így nem kellene LIKE-ot sem használni már, az eredeti lekérdezésSELECT * FROM tableWHERE channel_idIN ('id1','id2','id3','id4')AND(category LIKE '%category1%'OR category LIKE '%category2%'OR category LIKE '%category3%'OR category LIKE '%category4%'OR category LIKE '%category5%'OR category LIKE '%category6%')AND(category NOT LIKE '%category7%'AND category NOT LIKE '%category8%'AND category NOT LIKE '%category9%')ORDER BY date DESC LIMIT 4nézhetne ki így is:
SELECT * FROM tableWHERE channel_idIN ('id1','id2','id3','id4')AND(category7 = 0AND category8 = 0AND category9 = 0)ORDER BY date DESC LIMIT 4(Mert most látom csak, hogy feleslegesen szűrtem arra is, hogy milyen kategóriákat listázzon, ha egyszer már ott van az is, hogy miket NE, és csak 2 állása van (vagy benne van, vagy nincs)).
Ez lenne olyan hatékony, mint a 3 táblás JOIN-olás? Vagy még hatékonyabb, esetleg kevésbé?
(Most kíváncsi lennék, az EXPLAIN erre mit mondana.) -
nyunyu
félisten
És ha jól értem, ugye azt írod, hogy csináljak egy product_category táblát, amibe úgy kerülnének bele a rekordok, hogy ha a fő táblámban (product) mondjuk van 2 millió rekord, és mindegyikhez tartozik 2-4 (átlagban 3) kategória (category). Akkor e szerint a product_category táblában jelen állás szerint 6 millió rekord lenne.
Nem ez a lényeg, hanem az, hogy a kategória táblád párszáz, ezer rekordos lesz csak.
Ezen a string műveletek időigénye még nem tragikus, gyorsan le tudja kérdezni kategórianevekhez tartozó ID-t.
Aztán abból már könnyen joinolja a többmilliós product_category táblát (ha tettél indexet a category_id-re), így hamar megvan a product_id, ami meg szintén külső kulcs, a product tábla elsődleges kulcsára mutat, ami megint csak indexelt, aztán máris megvannak a megfelelő termék rekordjaid.Feltételezem, hogy valami webshop motort hegesztesz, ahol a vevő a termék kategóriára is tudna szabadszövegesen keresni, ezért kell a kategória névre string illesztés.
-
nyunyu
félisten
N:M kapcsolatnak pont az a lényege, hogy külön-külön lekérdezhető mindegyik variációja.
Teszemazt van egy product táblád:
id name
1 alma
2 körte
3 banán
4 szilva
5 narancsvan egy categoryd:
id name
1 piros
2 sárga
3 zöld
4 kék
5 narancssárgaEzeket összerendelő product_category táblád:
product_id category_id
1 1
1 2
1 3
2 2
3 2
4 4
5 1
5 5Ha erre ráuszítod az előző querymet c.name like '%sár%'-ral, akkor ki fogja neked listázni az almát, körtét, banánt, narancsot, mert azok SÁRga vagy narancsSÁRga kategóriásak.
Ha azt akarod kérdezni, hogy melyik az a termék, amiből van sárga és piros is, akkor kétszer kell a product_category-t és a categoryt joinolni, és azokat ANDdal kérdezni:
select p.*
from product p
join product_category pc1
on pc1.product_id = p.id
join category c1
on c1.id = pc1.category_id
join product_category pc2
on pc2.product_id = p.id
join category c2
on c2.id = pc2.category_id
where c1.name = 'sárga' and c2.name = 'piros'
order by p.date desc;Ez már csak az almát találná meg.
Ha ezt írnád:
where c.name = 'sárga'
or c.name = 'piros'
or c.name = 'kék'
vagy az ezzel ekvivalenswhere c.name in ('sárga','piros','kék')
feltételt, akkor az összes sárga vagy piros vagy kék gyümölcs lejönne (alma, körte, banán, szilva)
Narancs nem, mert itt kategórianévre teljes egyezés a feltétel! -
martonx
veterán
"És ha jól értem, ugye azt írod, hogy csináljak egy product_category táblát, amibe úgy kerülnének bele a rekordok, hogy ha a fő táblámban (product) mondjuk van 2 millió rekord, és mindegyikhez tartozik 2-4 (átlagban 3) kategória (category). Akkor e szerint a product_category táblában jelen állás szerint 6 millió rekord lenne."
Ez azért tud gyors lenni, mert ezek mind Foreign Key-ek, azaz indexeltek. Itt tenném még hozzá, hogy sokkal tisztább érzés lenne Kategória ID-re szűrni Name helyett.
-
nyunyu
félisten
DB teljesítményhez két dolog szükséges: sok RAM, meg sok, gyors diszk.
Több procimagot is meghálálja, de arra nem annyira érzékeny, mint a kevés memóriára.Tényleg, nem valami ingyenes DB licenszet használsz, ami 1 magra és 1GB RAMra van korlátozva?
(Pl. Oracle XE, SQL Server Express) -
nyunyu
félisten
Jaj, itt már a relációs adatmodell alapjai is hiányoznak.
Ahogy tm5 írja, ki kéne tenni a kategóriákat egy külön táblába, amiben van egy category_id, és egy name mező.
Mivel ez pártíz-száz különböző értéket fog tartalmazni, ezen akár még a lájk is működhetne gyorsan, nem fájna annyira, mint egy nagyonnagy táblán.Mivel egy termékhez több kategóriát is szeretnél tárolni, illetve egy kategóriába több termék is eshet, így N:M reláció lesz a termék és a kategória között.
Ennek leképezése úgy történik, hogy csinálsz egy termék_kategória táblát, amibe beleteszed a termék azonosítóját, és a kategória azonosítóját.
Ahány kategóriába tartozik, annyiszor veszed fel ide a terméket, mindig a következő kategória azonosítójával.Lekérdezéskor meg joinolod az id-k mentén a három táblát, valahogy így:
select p.*
from product p
join product_category pc
on pc.product_id = p.id
join category c
on c.id = pc.category_id
where c.name like '%akármi%'
order by p.date desc; -
tm5
tag
Szerintem le kellene ülni és összeszedni, hogy mik az elvárások és az alapján tervezni egy adatbázist, mert most minden posztodban kiderül valami újabb dolog.
A category oszlopot inkább kiraknám egy külön táblába, mondjuk úgy, hogy ha van egy category szótárod (cat_id, cat_name) akkor lenne egy un. junction táblád (tabla_id, cat_id)
és akkor cat_id alapján gyorsan tudnál keresni. Ez esetben lehetne az IN operátort is használni. Kerüljük a redundanciát ha lehet. Egy Microsoft SQL-es MVP már 15 éve azt írta, hogy egy rendes 3. normálformájú adatbázis sokkal jobban teljesít, mint egy redundanciával teli.Én amúgy szeretek kompozit indexek helyett külön indexet használni leggyakrabban keresett oszlopokra. Esetleg megpróbálhatod ezt is.
-
nyunyu
félisten
A probléma leginkább azzal van, hogy szótöredékre próbálsz keresni
like '%valami%', emiatt nem nagyon tudja hatékonyan használni a category_id-re tett indexet.Azért egy próbát megérhet egy (channel_id, category_id) összetett index, hátha segít valamit.
Ha szó elejére keresnél
like 'valami%', akkor könnyen meg tudná mondani az indexből, hogy melyik rekordok kellhetnek.
Új hozzászólás Aktív témák
- Tablet felvásárlás!! Apple iPad, iPad Mini, iPad Air, iPad Pro
- GYÖNYÖRŰ iPhone 13 Mini 128GB Blue -1 ÉV GARANCIA -Kártyafüggetlen, MS3886
- Azonnali készpénzes GAMER / üzleti notebook felvásárlás személyesen / csomagküldéssel korrekt áron
- Bomba ár! Lenovo ThinkPad L13 G3 - i5-1245U I 16GB I 256SSD I 13,3" FHD Touch I NBD Gari!
- Eladó Nvidia GTX 650 1GB videokártya
Állásajánlatok
Cég: Laptopszaki Kft.
Város: Budapest
Cég: PCMENTOR SZERVIZ KFT.
Város: Budapest



)



