SQL: vypis susedov

hknmtt

  • ****
  • 281
    • Zobrazit profil
    • E-mail
SQL: vypis susedov
« kdy: 23. 04. 2025, 14:44:38 »
Mam na webe obycajne strankovanie vysledkov, ktore mi moze vratit nieco ako [1,2,3,4,5,6,7,8,10..] a povedzme, ze si kliknem na cislo 6 a zobrazim si celu entitu.

Chcel by som co najefektivnejsie najst susedov, aby som sa mohol preklikat na predosly alebo nasledovny vysledok. Cize ak som na obsahu s ID=6 tak chcem 5 a 7.

Chcel by som vsak predist tomu, aby som opakoval strankovaciu query a len pouzit aktualne id, 5, ako limitus pre najdenie predosleho a nasledovneho id.

Je to mozne spravit nejak jednoducho, bez nutnosti dvoch query, alebo dvoch sub-query, a vlastne len spusti zase tu strankovaciu query s tym, ze poviem ze 5 je "ciel" a chcem len susedov?


Re:SQL: vypis susedov
« Odpověď #1 kdy: 23. 04. 2025, 15:01:56 »
a nechceš napsat aspoň, kterou databázi používáš?

Zpravidla tohle nejde snadno a třeba v případě mysql to znamená si tu stejnou tabulku tam připojit dvakrát znovu a najít předchozí a následující řádek podle aktuálního řazení.

hknmtt

  • ****
  • 281
    • Zobrazit profil
    • E-mail
Re:SQL: vypis susedov
« Odpověď #2 kdy: 23. 04. 2025, 15:04:32 »
Maria/Mysql.

Re:SQL: vypis susedov
« Odpověď #3 kdy: 23. 04. 2025, 15:39:06 »
Moc jste nepopsal, co vlastně chcete. Pokud je to opravdu tak, jak popisujete na příkladu, tedy že máte vzestupně řazená ID a jde vám jen o to získat sousední ID, tak prostě uděláte SELECT id FROM tbl WHERE id > $selectedID ORDER BY id LIMIT 1 pro následující ID, nebo SELECT MIN(id) FROM tbl WHERE id > $selectedID – normální databáze by měla pochopit, že jde o to samé a vybrat stejný prováděcí plán, u MariaDB/MySQL bych radši porovnal obě varianty, možná jedna z nich bude lepší. Tím získáte ID následujícího záznamu, pro získání ID předchozího záznamu si to snad upravíte sám. A pokud to chcete jedním dotazem, spojíte ty dva SELECTy pomocí UNION.

Re:SQL: vypis susedov
« Odpověď #4 kdy: 23. 04. 2025, 15:40:11 »
Možná mohou pomoci funkce LAG a LEAD, ty ti mohou dát hodnotu v vybraném sloupci z předchozího nebo následujícího záznamu.
https://dev.mysql.com/doc/refman/8.4/en/window-function-descriptions.html#function_lag
https://dev.mysql.com/doc/refman/8.4/en/window-function-descriptions.html#function_lead



hknmtt

  • ****
  • 281
    • Zobrazit profil
    • E-mail
Re:SQL: vypis susedov
« Odpověď #5 kdy: 23. 04. 2025, 15:43:50 »
Možná mohou pomoci funkce LAG a LEAD, ty ti mohou dát hodnotu v vybraném sloupci z předchozího nebo následujícího záznamu.
https://dev.mysql.com/doc/refman/8.4/en/window-function-descriptions.html#function_lag
https://dev.mysql.com/doc/refman/8.4/en/window-function-descriptions.html#function_lead

Ale to asi len dava hodnotu v kontexte tabulky odkial sa selectuje, ale nie v kontexte query ktora vracia finalne vysledky, nie?

Re:SQL: vypis susedov
« Odpověď #6 kdy: 23. 04. 2025, 15:49:52 »
Možná mohou pomoci funkce LAG a LEAD, ty ti mohou dát hodnotu v vybraném sloupci z předchozího nebo následujícího záznamu.
https://dev.mysql.com/doc/refman/8.4/en/window-function-descriptions.html#function_lag
https://dev.mysql.com/doc/refman/8.4/en/window-function-descriptions.html#function_lead

Ale to asi len dava hodnotu v kontexte tabulky odkial sa selectuje, ale nie v kontexte query ktora vracia finalne vysledky, nie?
Nevím jak v mysql, ale v např oracle je to v kontextu query.
Pokusil jse se udělat příklad a při filtraci nad tabulkou to funguje v kontextu query.
https://sqlfiddle.com/mysql/online-compiler?id=9ecc9f50-2620-480d-ad12-9186f69576b1


Re:SQL: vypis susedov
« Odpověď #7 kdy: 23. 04. 2025, 21:30:34 »
Pak je tu ještě možnost že to je opravdu průchod výsledkovou sadou toho vyhledávání (podle konkrétních kritérií včetně nějakého řazení), a ne celou tabulkou.  Pak se může hodit i mít tu celou výsledkovou sadu někde vedle uloženou (ve struktuře typu (id_vysledkove_sady, poradi_vysledku_v_sade, id_vyhledaneho_objektu) - a pro procházení tou sadou ve stylu next/previous (případně i stránkování) si předávat všechny tři (next znamená např. "jsem na výsledkové sadě id 1234 na položce č. 3 což je entita s id 9999 - zmáčknu next - dám požadavek že chci ze sady č. 1234 položku č. 4 - a k tomu se pak rychle dohledá že to je entita třeba s id 1645").
Je to o výběru jestli chci mít stabilní výsledkovou sadu (a jistotu že X*next + X*prev mě vždzycky vrátí tam kde jsem začal, i když se něco mezi přidá/smaže/změní podmínky takže to vypadne z výsledků vyhledávání/...) nebo to nepotřebuju (a můžu mít jednodušší implementaci).

I stránkování se pak dělá jenom tak že si z té odložené sady řeknu o položky od nějaké pozice ( (číslo stránky - 1) x (velikost stránky) + 1) do nějaké pozice ( (číslo stránky) x (velikost stránky) ).

LAG a LEAD mi můžou při zobrazení detailu jedné entity najít ID těch sousedů, ale po přechodu na další musím udělat znova vyhledávací dotaz abych mohl najít next/prev k nové entitě ve stejném pořadí (a ještě ve výsledku hledat kde mám aktuální entitu abych věděl ke komu najít toho předchozího a následujícího? No to můžu rovnou kouknout na předchozí a následující řadek toho co mi vrátila databáze ...).

Udělat dva selekty na "největší menší" a "nejměnší větší" id co jsou v té výsledkové sadě je lepší, ale pokud se to má procházet se složitou vyhledávací podmínkou a jěště nějak seřazené tak to taky rozhodně může skončit dost pomalé.

hknmtt

  • ****
  • 281
    • Zobrazit profil
    • E-mail
Re:SQL: vypis susedov
« Odpověď #8 kdy: 23. 04. 2025, 22:04:33 »
Hm, ono ide o to, ze ja si sice mozem spravit "cache" tabulku s vysledkami a spravit jednoduchu query bez joinov a filtrov, len som hladal nieco univerzalne, lebo nie vzdy je to mozne urobit a teda potom neostava nic ine len pouzit dve query/sub-query na najdenie susedov.

Re:SQL: vypis susedov
« Odpověď #9 kdy: 24. 04. 2025, 08:56:16 »
Od verze 8.0 by melo MySQL mit alespon castecnou podporu ISO SQL:2008, konretne windowing functions LAG a LEAD.

Re:SQL: vypis susedov
« Odpověď #10 kdy: 24. 04. 2025, 09:05:31 »
Na subquery není nic špatného, je to univerzální a je to naprosto v pořádku. Navíc to, co popisujete vy, je to, že chcete získat jeden záznam a ID ze dvou okolních záznamů. Takže triviální implementace je získat tři záznamy a z těch dvou krajních použít jen ID. Nesnažte se optimalizovat databázové dotazy, když nevíte, zda v nich je nějaký problém.

Navíc jste pořád nepopsal, jaký konkrétní problém řešíte, tak těžko dostanete konkrétnější odpověď. A to, co jste zatím popsal, moc smysl nedává. Vypadá to, že máte nějaké master–detail zobrazení, tj. máte sadu záznamů a prohlížíte po jednom detaily záznamu. V takové situaci chce uživatel procházet fixní sadu záznamů – takže dotaz na výběr záznamů dle podmínek se udělá jen jednou, zapamatují se ID záznamů a uživatel pak prochází ten předem daný seznam ID. Podle vašeho dotazu to asi bude nějaká webová aplikace, když vás nenapadlo rovnou tu sadu výsledků si pamatovat. Ale když to má procházet uživatel po jednom, těch záznamů stejně budou maximálně desítky, při nejhorším stovky, takže není problém si to zapamatovat i ve webové aplikaci.

To, že se to dělá nad fixní sadou záznamů, je proto, že pro uživatele by to bylo matoucí, kdyby ten dotaz byl živý. Pak půjde uživatel na třetí stránku, bude tam mít záznam 4, přejde jinam, vrátí se na stranu tři a bude tam mít záznam 6. Nebo půjde na stranu čtyři, bude tam mít záznam 5, půjde na předchozí stranu a znovu tam bude mít záznam 5. To by pro uživatele bylo matoucí, proto se ta sada záznamů vybere jednou a pak se uživatel pohybuje v ní. Když jde na konkrétní záznam, stáhnou se aktuální data, může se tam zobrazit, že už záznam neodpovídá původním podmínkám, ale to je vše.

Dovedu si představit i případ, že by uživatel zpracovával výsledky živého dotazu, ale pak bude postupovat jen dopředu.

Re:SQL: vypis susedov
« Odpověď #11 kdy: 24. 04. 2025, 09:08:52 »
Od verze 8.0 by melo MySQL mit alespon castecnou podporu ISO SQL:2008, konretne windowing functions LAG a LEAD.

A mariadb myslím od verze 10.2.

Kloním se k tomu co píše Filip nademnou, pusuň offset dozadu, ztrojnásobi limit a načti si prostě 3 stránky najednou. Pak v aplikaci vykreslíš jen tu prostředí část, tak se tyhle preloady obvykle dělají. Bude to stokrát levnější než LAG/LEAD.

LAG/LEAD se hodí na to, když máš nějaké uid řádku a potřebuješ podle nějakého řazení ty předchozí, další, ale pokud máš stránkování, tj. neznáš konkrétní řádky, ale posouváš se seznamem, můžeš prostě rozšířit oblast načtení...

Re:SQL: vypis susedov
« Odpověď #12 kdy: 24. 04. 2025, 18:42:34 »
Nacti si 3 stranky najednou. Tedy 5,6,7 a zobraz jen tu 6.
Coz ale neresi problem kdyz se presunes na stranku 5. Pak ti bude chybet stranka 4.
Asi budes muset udelat cache na nejaky pocet stranek a nekdy ho budes muset nacist znova.

Re:SQL: vypis susedov
« Odpověď #13 kdy: 24. 04. 2025, 20:17:35 »
Udržiavať si stav na backende asi nie je riešenie. Predpokladám, že stránkovanie môže vracať rôzne výsledky v závislosti od nastavenia filtrov a zoradenia. Teda jeden používateľ môže mať otvorených viac tabov a v každom vidieť inak stránkované záznamy. Takže z pohľadu backendu by to malo byť stateless.

Vytiahnuť si susedov hneď s daným záznamom tiež nerieši problém úplne. Ako poznamenal Zdeno Sekerák vyššie, len po posunie problém o krok (n-krokov) ďalej.

Chcelo by to storage, ktorý je "tab-local". Ak tých záznamov nie je veľa, možno by sa celá sekvencia ID-čiek dala poslať a uložiť na frontend. Znie to obludne, ale myslím, že array s pár tisícami čísel by v praxi fungoval ok. V prípade, že ide o old school web, kde sa stránka stále načítava odznova, dalo by sa to "uložiť" ako URL query parameter ("records.html?filters=...&order=...&ids=1,2,3,4,..."). Priznávam, ešte väčšia obludnosť, ale funkčná :)

Ak je tých záznamov naozaj veľa a platí môj úvodný predpoklad, tak dodatočnej query by som sa asi nevedel vyhnúť.

Re:SQL: vypis susedov
« Odpověď #14 kdy: 25. 04. 2025, 08:39:20 »
Udržiavať si stav na backende asi nie je riešenie. Predpokladám, že stránkovanie môže vracať rôzne výsledky v závislosti od nastavenia filtrov a zoradenia. Teda jeden používateľ môže mať otvorených viac tabov a v každom vidieť inak stránkované záznamy. Takže z pohľadu backendu by to malo byť stateless.
Udržovat si stav na backendu je řešení. Věci jako session už byly vynalezeny. Každá záložka v prohlížeči si může pamatovat identifikátor své sady záznamů uložené na serveru.

Pokud s tím má uživatel nějak rozumně pracovat, budou těch záznamů desítky, v extrémním případě stovky. Takže si je může pamatovat i prohlížeč.

Vytiahnuť si susedov hneď s daným záznamom tiež nerieši problém úplne. Ako poznamenal Zdeno Sekerák vyššie, len po posunie problém o krok (n-krokov) ďalej.
Řeší to problém úplně, protože při přechodu na jinou stránku a pošle požadavek na server a načte se nová trojice.

Ak je tých záznamov naozaj veľa a platí môj úvodný predpoklad, tak dodatočnej query by som sa asi nevedel vyhnúť.
Pokud těch záznamů budou tisíce, tak je stejně nedokáže zpracovat uživatel.

Stránkování v aplikacích už bylo vyřešeno milionkrát a použitelná řešení už tu byla popsána několikrát. Jaké konkrétní řešení použít nemůžeme poradit, protože nevíme, jaký konkrétní problém hknmtt řeší (jako obvykle).