ugrás a tartalomhoz

Infinite sliding window

Poetro · 2012. Feb. 28. (K), 18.35
Sziasztok!

A következő a problémám:
  • Van egy táblám, elemekkel, amelyek meghatározott időrendben vannak, az egyszerűség kedvéért legyen az oszlop neve id és az értékek pedig egész számok.
  • Szeretném lekérdezni a meghatározott elem környezetében levő elemeket egy kis csavarral
  • Ha nincs elég elem a kiválasztott előtt, akkor a lista végéről kellene pótolni az elemeket.
  • Ha nincs elég elem a kiválasztott után, akkor a lista elejéről kellene pótolni.













id 1 3 5 7 9 10 12 14 16 18 21 24

Tegyük fel, hogy a kiválasztott elemünk a 10 és 3 elemnyi a környezet, ekkor a következő értékeket várom: 5, 7, 9, *10*, 12, 14 16.
Ha a kiválasztott elem a 3, akkor a következőket: 21, 24, 1, *3*, 5, 7, 9.
Ha a kiválasztott elem a 18, akkor pedig következőket: 12, 14, 16, *18*, 21, 24, 1. (A *-gal csak azt jelölöm, hogy az a kiválasztott, de a környezetére van csak igazából szükségem).

A fentit meg lehet valósítani 4 lekérdezéssel.
SELECT id FROM `test` WHERE id > X ORDER BY id ASC LIMIT 3;
SELECT id FROM `test` WHERE id < X ORDER BY id DESC LIMIT 3;
SELECT id FROM `test` ORDER BY id DESC LIMIT 3;
SELECT id FROM `test` ORDER BY id ASC LIMIT 3;
És ebből szerver oldalon fel lehet építeni a kívánt listát.

De gondolom van itt valaki aki jobban ért az adatbázisokhoz, mint én, és meg tudja oldani MySQL alatt egy vagy két akár subquery-t használó lekérdezéssel.
 
1

Ha csak a lekérdezések számát…

tiku I tikaszvince · 2012. Feb. 28. (K), 22.11
Ha csak a lekérdezések számát kell csökkenteni, akkor az egyik

SELECT * FROM (
  (SELECT id FROM `test` WHERE id >= X ORDER BY id ASC LIMIT 4)
  UNION
  (SELECT id FROM `test` WHERE id < X ORDER BY id DESC LIMIT 3)
) AS t
ORDER BY id
másik:

(SELECT id FROM `test` ORDER BY id DESC LIMIT 3)
UNION
(SELECT id FROM `test` ORDER BY id ASC LIMIT 3)
3

Nem volt egyszerű

gabesz666 · 2012. Feb. 29. (Sze), 01.47
Nem volt egyszerű menet, de sikerült megoldani :)

SET @row = 0, @rowindex = 0; 

SELECT id 
FROM   (SELECT IF(id = X 
                  AND @rowindex + 3 < (SELECT COUNT(id) 
                                       FROM   test), @rowindex := ROW, @rowindex 
               ) AS 
                      rowindex, 
               ROW, 
               id 
        FROM   ((SELECT @row := @row + 1 AS ROW, 
                        id 
                 FROM   `test`) 
                UNION ALL 
                (SELECT @row := @row + 1, 
                        id AS ROW 
                 FROM   `test`) 
                UNION ALL 
                (SELECT @row := @row + 1, 
                        id AS ROW 
                 FROM   `test`)) AS t1) AS t2 
WHERE  ( ROW >= @rowindex - 3 
         AND ROW <= @rowindex + 3 );
4

OMG

Poetro · 2012. Feb. 29. (Sze), 02.17
Hát ez most sokkal jobb, mint a korábbi verziók, a többiben volt temporary table, meg filesort meg minden... de itt csak Using index, Using where. Persze azt nem mondom, hogy elsőre értem is, mi történik itt.
6

Nem annyira bonyolult

gabesz666 · 2012. Feb. 29. (Sze), 10.58
Nem annyira bonyolult (persze így utólag könnyen mondom :)). A legbelső queryben egymás után 3× összefűzöm a táblát plusz létrehozok egy azonosítót az egyes sorokhoz. Az egyel fentebb levő queryben megkeresem az azonosítóját annak az elemnek amit keresünk (rowIndex). A legkülső queryben pedig leszűröm az eredményhalmazt azokra a sorokra, ahol a sor azonosító legfeljebb 3-al tér el az előbb beállított (rowIndex) azonosítótól.
2

Két lekéréssel...

T.G · 2012. Feb. 29. (Sze), 00.00
Két lekéréssel, az első lekérés fordítottjához hozzá kell fűzni a másodikat:
(SELECT id FROM test WHERE id >= X ORDER BY id ASC LIMIT 4)
UNION
(SELECT id FROM test ORDER BY id ASC LIMIT 4)
LIMIT 4
(SELECT id FROM test WHERE id < X ORDER BY id DESC LIMIT 3)
UNION
(SELECT id FROM test ORDER BY id DESC LIMIT 3)
LIMIT 3
5

Ha csak egy ID kell a

deejayy · 2012. Feb. 29. (Sze), 10.58
Ha csak egy ID kell a táblából, minek erőltetni az SQL-t? Ez egy olyan apró művelet, amit simán el lehet végezni a app oldalon is, és akkor nincsenek unionok, joinok, orderek meg egyéb sql teljesítményromboló kitételek.
7

Ez engem is érdekelne...

H.Z. v2 · 2012. Feb. 29. (Sze), 11.03
Tegnap óta próbálok rájönni, miért jobb szinte megerőszakolni az SQL-t, mint programból intézni a körbejárást.
Viszont Poetrotól még nem láttam, hogy hülyeségeket beszélt volna, ergo kell, hogy legyen valami jó oka.
8

Terheléselosztás?

gabesz666 · 2012. Feb. 29. (Sze), 12.58
Terheléselosztás? (ha külön gépen fut a php és az sql)
9

Lehet...

H.Z. v2 · 2012. Feb. 29. (Sze), 13.17
Ebben a konkrét esetben kicsit túlzásnak érezném, de nem kizárt ez sem.
10

Felhő

Poetro · 2012. Feb. 29. (Sze), 14.36
Mondjuk nekem nem csak az ID kell a táblából, csak nem akartam túlbonyolítani a kérdést. Egyébként az alkalmazás a felhőben fut, ezért meg akarok nézni több alternatívát, hogy melyik fekszik legjobban erre a feladatra, mert természetesen jóval nagyobb mennyiségű adatról van szó, mint a példában. És ha ezek a UNION-ok tényleg rosszul fognak viselkedni, akkor maradok az alkalmazás oldali felépítésnél, csak spórolni akartam az alkalmazás felépítésén, és inkább az adatbázisra akarnám bízni az adatbányászatot, ha már úgyis arra lett kitalálva.

Különben gabesz666 megoldása elég szépen szerepel az EXPLAIN szerint, egyedül a UNION táblák, amik zavarnak, és ezek tényleg sokat ronthatnak a teljesítményen, főleg nagyobb adatmennyiség esetén. Főleg, mivel ezek nem cachelhető UNION-ok, gondolom a változók miatt.

Természetesen rakok ez elé egy cache réteget, de minden egyes új tartalom esetén sajnos törölni kell majd őket, ezért elég gyakran fog lefutni a lekérdezés az előzetes kalkulációk alapján. Egyébként ez egyfajta lapozó lesz, amivel az előző, illetve következő elemek lesznek kilistázva, már ha nem lett volna egyértelmű.
11

Ugye majd elmeséled...

H.Z. v2 · 2012. Feb. 29. (Sze), 20.22
... pár tőmondatban, hogy mire jutottál?
(engem legalábbis nagyon érdekelne)

Különösen azok után, hogy...
- kiderült, a filesort nem egészen az, aminek első ránézésre képzeltem (ahogy a neten keresgéltem, nem vagyok vele egyedül :) )
- felfedeztem, hogy míg a "select ... union all select ..." duó filesortot használ, addig a "select * from (select ... union all select ...)" filesort nélkül működik...
12

+1

Pepita · 2012. Feb. 29. (Sze), 21.41
Ha Poetro esetleg ráér(sz), egy cikk is jó lenne ebből (cache-el, stb. együtt).
13

4 SELECT

Poetro · 2012. Már. 2. (P), 16.15
Végül a 4 SELECT-es változatot választottam, mert ott a kezdet és vég jól cachelhető, és ezért szinte csak 2 SELECT-re redukálódik a probléma (valamint a 4 SELECT is valamivel gyorsabb mint a UNION-os megoldás, és mindenképpen megbízhatóbb). És ez a két SELECT viszont nagyon gyors, és az adatbázismotor is jól tudja cachelni, mivel indexeket használ a rendezéshez és a kiválasztáshoz. De gondolom titeket a kód is érdekel, úgyhogy íme:
/**
 * Fetches the surrounding objects.
 */
function get_surrounding_objects($object, $range = 1) {
  $items = &drupal_static(__FUNCTION__, array());
  $surround = 10;
  $bid = intval($object->bid);
  $table = 'object_data';
  $cid_start = $table . '_start';
  $cid_end = $table . '_end';
  $cid = $table . '_item_' . $bid;
  if (!isset($items[$bid])) {
    // Check if the data is already cached and load it if necessary.
    if ($cache = cache_get($cid)) {
      $items[$bid] = $cache->data;
    }
    else {
      $items[$bid] = array(
        'before' => array(),
        'after' => array(),
      );

      // Get items before.
      $query = db_select($table, 'd');
      $result = $query->fields('d')->condition('d.bid', $bid, '<')->orderBy('d.bid', 'DESC')->range(0, $surround)->execute();
      foreach ($result as $item) {
        $items[$bid]['before'][$item->bid] = $item;
      }

      // Get items after.
      $query = db_select($table, 'd');
      $result = $query->fields('d')->condition('d.bid', $bid, '>')->orderBy('d.bid', 'ASC')->range(0, $surround)->execute();
      foreach ($result as $item) {
        $items[$bid]['after'][$item->bid] = $item;
      }

      // If we have less items before then needed, add items from the end.
      $needed_before = $surround - count($items[$bid]['before']);
      if ($needed_before > 0) {
        // Get items at end.
        $end = array();
        if ($cache = cache_get($cid_end)) {
          $end = $cache->data;
        }
        else {
          $query = db_select($table, 'd');
          $result = $query->fields('d')->orderBy('d.bid', 'DESC')->range(0, $surround)->execute();
          foreach ($result as $item) {
            $end[$item->bid] = $item;
          }
          array_reverse($end, TRUE);
          cache_set($cid_end, $end);
        }

        $items[$bid]['before'] += array_slice($end, -$needed_before, $needed_before, TRUE);
      }

      // If we have less items after then needed, add items from the start.
      $needed_after = $surround - count($items[$bid]['after']);
      if ($needed_after > 0) {
        // Get items at start.
        $start = array();
        if ($cache = cache_get($cid_start)) {
          $start = $cache->data;
        }
        else {
          $query = db_select($table, 'd');
          $result = $query->fields('d')->orderBy('d.bid', 'ASC')->range(0, $surround)->execute();
          foreach ($result as $item) {
            $start[$item->bid] = $item;
          }
          cache_set($cid_start, $start);
        }

        $items[$bid]['after'] += array_slice($start, 0, $needed_after, TRUE);
      }

      // Add the file info if present.
      foreach ($items[$bid] as $position => $elements) {
        foreach ($items[$bid][$position] as &$item) {
          if (!empty($item->fid)) {
            $item->image = file_load($item->fid);
          }
        }
      }

      // Store the items to cache.
      cache_set($cid, $items[$bid]);
    }
  }
  return array(
    'before' => array_slice($items[$bid]['before'], 0, $range, TRUE),
    'after' => array_slice($items[$bid]['after'], 0, $range, TRUE),
    'current' => $object,
  );
}
A függvények Drupal 7 függvények így akit érdekel, melyik mit csinál, ajánlott utánaolvasni az Drupal 7 API-jában. Természetesen át lehetne kicsit szervezni a kódot függvényekbe, hogy kevesebb legyen a redundáns rész, de ezt mindenkinek a saját szorgalmára bízom.
14

kicsit más megközelítés

szabo.b.gabor · 2012. Már. 2. (P), 17.21
lehet nem stimmel amit írok, de.

valahonnan biztosan van egy link erre az elemre. az pedig valószínűleg egy listában szerepel.

tehát ha úgy tudsz megérkezni, hogy tudod hányadik (legyen n) az épp megjelenített elem a listában, akkor 'limit n-3,7' aztán ha nem ennek a listának a közepén van amit kerestél, akkor lehet játszadozni..
15

Nincs

Poetro · 2012. Már. 2. (P), 17.34
Pont ez az, hogy egy ilyen listát akarok készíteni. Maga az oldalam az, aminek az ID-jet tudom, és kell egy lista, ami a környezetét mutatja.