Autor Zpráva
Peter123
Profil *
Zdravím,
mám nasledujúcu úlohu: potrebujem vyhladat všetky inzeraty, ktoré majú vybrané nasledujúce parametre:
Lokalita:
[x] v obci
[x] mimo obce
[ ] v meste

Stav
[ ] povodny
[x] novostavba
[x] kompletna rekonstrukcia
[ ] starsia budova

Siete
[x] voda
[ ] plyn
[x] elektrina

ciže: všetky inzeráty, ktoré sú: v obci ALEBO mimo obce A SÚ novostavba ALEBO kompletna rekonstrukcia A SÚ s vodou alebo elektrinou.

1. mám v DB tabulku inzeráty:
-------------------------------------
id, primary, int(11)
title, varchar(100)

2. tabulka inzeraty_paramvalues:
------------------------------------------
inzerat_id, int(11)
paramvalue_id, varchar(20) (napr.: voda, elektrina, novostavba...)

3. dotaz do DB
------------------

select * from inzeraty
where id IN 
  (SELECT inzerat_id FROM inzeraty_paramvalues WHERE paramvalue_id in ('v_obci', 'mimo_obce')
    AND inzerat_id IN
    (
      SELECT inzerat_id FROM inzeraty_paramvalues WHERE paramvalue_id in ('novostavba', 'rekonstrukcia')
      AND inzerat_id IN
      (
        SELECT inzerat_id FROM inzeraty_paramvalues WHERE paramvalue_id in ('voda', 'elektrina')
      )
    )
  )

Problém je v tom, že pri väčšom počte inzerátov a parametrov a aplikuje WHERE na velmi vela záznamov.. Otázka je, či by to šlo nejak zoptimalizovať alebo inak navrhúť štruktúru DB.
mimochodec
Profil
nebylo by jednodušší toto?
WHERE paramvalue_id in ('voda', 'elektrina') AND paramvalue_id in ('novostavba', 'starostavba') ...

Druhá věc je ta konstrukce samotná. Pracovat s řetězci 'v_obci', 'mimo_obce' apod je mnohem náročnější, než si udělat patřičné sloupce a v nich uchovávat integer.
Kajman
Profil
V jaké tabulce si uchováváte rozdělení parametrů na lokalita, stav a siete? S ní by to mělo jít.
Peter123
Profil *
Kajman:
No to je zatiaľ otázka.. Momentálne riešenie je také, že to nebude v DB, ale iba v PHP asociatívnom poli:

<?php

$params = array(
'stav' => array(
  '0' => 'novostavba',
  '1' => 'kompletna rekonstrukcia'
  ....
)
);

?>
Kajman
Profil
Tím přicházíte o možnost ladného a nejspíše i rychlejšího řešení na straně db.

Mohly by fungovat přibližně takovéto dotazy...
SELECT i.*
FROM   inzeraty i
       INNER JOIN (SELECT DISTINCT inzerat_id
                   FROM   inzeraty_paramvalues
                   WHERE  inzerat_id = i.id
                          AND paramvalue_id IN ( 'v_obci', 'mimo_obce' )) p1
               ON i.id = p1.inzerat_id
       INNER JOIN (SELECT DISTINCT inzerat_id
                   FROM   inzeraty_paramvalues
                   WHERE  inzerat_id = i.id
                          AND paramvalue_id IN ( 'novostavba', 'rekonstrukcia' )
                  ) p2
               ON i.id = p2.inzerat_id
       INNER JOIN (SELECT DISTINCT inzerat_id
                   FROM   inzeraty_paramvalues
                   WHERE  inzerat_id = i.id
                          AND paramvalue_id IN ( 'voda', 'elektrina' )) p3
               ON i.id = p3.inzerat_id

SELECT i.*
FROM   inzeraty i
       INNER JOIN (SELECT inzerat_id
                   FROM   (SELECT inzerat_id,
                                  'lokalita' typ
                           FROM   inzeraty_paramvalues
                           WHERE  paramvalue_id IN ( 'v_obci', 'mimo_obce' )
                           UNION ALL
                           SELECT inzerat_id,
                                  'stav' typ
                           FROM   inzeraty_paramvalues
                           WHERE  paramvalue_id IN (
                                  'novostavba', 'rekonstrukcia' )
                           UNION ALL
                           SELECT inzerat_id,
                                  'siete' typ
                           FROM   inzeraty_paramvalues
                           WHERE  paramvalue_id IN ( 'voda', 'elektrina' )) x
                   GROUP  BY inzerat_id
                   HAVING Count(DISTINCT typ) = 3) t
               ON i.id = t.inzerat_id

SELECT i.*
FROM   inzeraty i
WHERE  EXISTS (SELECT inzerat_id
               FROM   inzeraty_paramvalues
               WHERE  inzerat_id = i.id
                      AND paramvalue_id IN ( 'v_obci', 'mimo_obce' ))
       AND EXISTS (SELECT inzerat_id
                   FROM   inzeraty_paramvalues
                   WHERE  inzerat_id = i.id
                          AND paramvalue_id IN ( 'novostavba', 'rekonstrukcia' )
                  )
       AND EXISTS (SELECT inzerat_id
                   FROM   inzeraty_paramvalues
                   WHERE  inzerat_id = i.id
                          AND paramvalue_id IN ( 'voda', 'elektrina' ))  

Těžko říci, co bude u rozsáhlejších dat svižnější. Každopádně je vhodný index na paramvalue_id.

A paramvalue_id by klidně mohlo být číslo z číselníku, který Vám chybí. Ať se nemusí pracovat s řetězcem.
Peter123
Profil *
Kajman:
Hmm, ďakujem, skúsim to otestovať. Každopádne stĺpce inzerat_id a paramvalue_id tvoria primarny kľúč, takže to by mohlo pomôcť, a ten číselník ma tiež napadol.

Ďakujem za pomoc
Kajman
Profil
Peter123:
Každopádne stĺpce inzerat_id a paramvalue_id tvoria primarny kľúč, takže to by mohlo pomôcť

Unikátní index na kombinaci sloupců (inzerat_id, paramvalue_id), v tomto pořadí, není použitelný pro tyto dotazy hledající inzeráty podle hodnoty v paramvalue_id. Proto bude lepší i další index nad druhým sloupcem.

Číselníková tabulka je krok správným směrem.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0