Autor Zpráva
Prochy
Profil
Dobrý večer,

po dlouhé době jsem tu s dotazem ohledně MySQL, který se mi zatím nepodařilo vyřešit. Mám nějaké data, kde jsou řádky, které potřebuji všechny vybrat a vedle toho spočítat počet řádků. Uvedu na příkladě, mám tento dotaz, který mi vybírá řádky klientů a jejich údaje:

SELECT 
 `clients`.`id`,    
 `clients`.`date_reg` as date_reg,
 `clients`.`surname`,
 `clients`.`first_name`,
 `sex_client`.`name`,
 `clients`.`cell_phone`,
 `clients`.`phone`,
 `clients`.`email`,
 `categorie_client`.`name`,
 `source_client`.`name`,
FROM `clients` JOIN `categorie_client` ON (`categorie_client`.`id`=`clients`.`categorie_client`)
JOIN `sex_client` ON (`clients`.`sex_client` = `sex_client`.`id`) 
JOIN `source_client` ON(`clients`.`source_client` = `source_client`.`id`) 
WHERE `clients`.`date_reg` BETWEEN 0 AND 20140224 
ORDER BY date_reg DESC 
LIMIT 50 
OFFSET 0

A teď bych potřeboval nějaký dotaz, který by mi vypsal do řádku sloupec s celkovým počtem vybíraných řádků. Takže např. pro 15 vyfiltrovaných záznamů bude na každým řádku sloupec s count(id)=15. Snažil jsem se na to přijít, ale bohužel bez úspěchu. Našel jsem nějaký příklad s rollupem, ale nějak se mi to nepodařilo rozhodit.

Děkuji za rady
Tori
Profil
Prochy:
A jak pomalé by bylo použít další dotaz se SELECT COUNT(*)? Anebo použít proměnnou?
Jinak: SELECT SUM(1) as soucet, další sloupce atd. GROUP BY date_reg, nějaký jedinečný sloupec WITH ROLLUP - nejsem si jistá úplně tím date_reg v GROUP BY. Záměrem je, aby to řadilo podle data a zároveň rozlišilo jednotlivé řádky, bo ORDER BY se nedá použít zároveň s GROUP BY. Zkuste, jestli to bude fungovat.

Ale u všech těchto možností získáte počet výsledků až v posledním řádku dat. Nemůžete použít nějakou std. funkci num_rows pro danou DB? (Předpokládám, že se chcete vyhnout procházení výsledků vícekrát.)
Prochy
Profil
Tori:
Děkuji za odpověď

bo ORDER BY se nedá použít zároveň s GROUP BY. Zkuste, jestli to bude fungovat.
Ano, tohle nefunguje, zkoušel jsem to. Je to napsaný dokonce v dokumentaci, že není možné použít tyto dvě funkce dohromady.

(Předpokládám, že se chcete vyhnout procházení výsledků vícekrát.)
Ano, přesně tak.

A jak pomalé by bylo použít další dotaz se SELECT COUNT(*)? Anebo použít proměnnou?
Ta proměnná mě nějak nenapadla, bude stačit pravděpodobně výsledná hodnota v posledním řádku.

Jinak je nutné aby to bylo vše v tom jednom dotazu, mimo dotaz už to není možné zpracovat, jelikož je využíváno komponenty jednoho Datagridu, kde se zadává dotaz s vyhledávanými daty. A zásah do toho Datagridu by ovlinil další stránky, které ho využívají, což se mi zdá zbytečně komplikované.
Kajman
Profil
Rollup by dokázal dát součet do řádku navíc, ne do sloupce. Mysql nepodporuje with ... select syntaxi, takže to jde asi opravdu jen zopakováním dotazu...

SELECT `clients`.`id`,
       `clients`.`date_reg`                              AS date_reg,
       `clients`.`surname`,
       `clients`.`first_name`,
       `sex_client`.`name`,
       `clients`.`cell_phone`,
       `clients`.`phone`,
       `clients`.`email`,
       `categorie_client`.`name`,
       `source_client`.`name`,
       `soucet`.`pocet`                                  AS pocet_vsech,
       Greatest(Least(`soucet`.`pocet` - 50 * 0, 50), 0) AS pocet_vypsano
FROM   `clients`
       JOIN `categorie_client`
         ON ( `categorie_client`.`id` = `clients`.`categorie_client` )
       JOIN `sex_client`
         ON ( `clients`.`sex_client` = `sex_client`.`id` )
       JOIN `source_client`
         ON( `clients`.`source_client` = `source_client`.`id` )
       CROSS JOIN (SELECT Count(*) pocet
                   FROM   `clients`
                          JOIN `categorie_client`
                            ON ( `categorie_client`.`id` = `clients`.`categorie_client` )
                          JOIN `sex_client`
                            ON ( `clients`.`sex_client` = `sex_client`.`id` )
                          JOIN `source_client`
                            ON( `clients`.`source_client` = `source_client`.`id` )
                   WHERE  `clients`.`date_reg` BETWEEN 0 AND 20140224)AS soucet
WHERE  `clients`.`date_reg` BETWEEN 0 AND 20140224
ORDER  BY date_reg DESC
LIMIT  50 offset 0  

Osobně bych takovou funkčnost doprogramoval do datagridu.
rafej
Profil
Pokud jsem dobře pochopila, co potřebuješ, hledej v MySQL manuálu COALESCE. Tam je to vysvětleno. Příklad ti nemůžu napsat, protože netuším, u které ze spojovaných tabulek potřebuješ sčítat výsledné řádky.
Kajman
Profil
rafej:

Coalesce vrátí první nenullovou hodnotu ze zadaných argumentů, to mu opravdu nepomůže.
rafej
Profil
Kajman:
Samo o sobě určitě ne, ale s joinem na poddotaz a sečtením hodnot by to pomoct mohlo. Bohužel se čtyřmi tabulkami tohleto dohromady nedám, abych uvedla příklad.
Kajman
Profil
rafej:
Ale on nesčítá hodnoty řádků (sum) ale zajímá se o počet řádků (count) a ten nemůže být nikdy null. Proto mu opravdu coalesce k ničemu není.
rafej
Profil
Kajman:
Velice jsem to zjednodušila pro jednu tabulku, abych ukázala, co myslím.
Dotaz vrátí výpis všech klientů, přidán sloupec počet, který obsahuje na jednom řádku počet vrácených záznamů a na ostatních NULL.
Bohužel nevím, jak bez řazení dle sloupce počet určit, který řádek vrátí požadovanou hodnotu, ale s pomocí COALESCE se ostatní řádky sloupce počet nastaví na NULL, takže lze tento platný řádek s hodnotou najít testem na NULL.

SELECT
 `clients`.`id`,
 `clients`.`surname`,
 `clients`.`first_name`,
  COALESCE(p,NULL) as pocet
FROM `clients` LEFT JOIN (SELECT `clients`.`id`, count(`clients`.`id`) as `p` FROM `clients`) as `o` ON (`clients`.`id`=`o`.`id`)

Pokud to jde ještě nějak líp, a zda to bude řádně fungovat i propojením s ostatními tabulkami, ať si tazatel vyzkouší. Je to jen nástin možného postupu. On musí vědět, zda je to pro něj proveditelné.
Tori
Profil
rafej:
nevím, jak bez řazení dle sloupce počet určit, který řádek vrátí požadovanou hodnotu
Dá se vybrat nejsnáze první nebo poslední řádek (např. změnou id na min(id) as id v poddotazu). Jelikož ale poddotaz vrací jen jeden řádek dat a použila jste left join, tak je coalesce zbytečné - NULL bude v ostatních řádcích automaticky.

Btw - je milé tu vidět další ženu. :)
Prochy
Profil
Nakonec jsem si upravil ten datagrid, ale potřeboval bych s tím trochu pomoct. Zavedu vás trochu víc do problematiky. Mám teda ten datagrid (Tabella k Nette Fram.), který mi vykreslí tabulku s daty. Datagrid má možnost ajaxové filtrace. Já potřebuju zobrazovat nad gridem shrnující údaje, který získám z těch tabulkových dat. Např. momentálně ten celkový počet filtrovaných řádků. To jsem vyřešil, to není problém. Ale chci to mít více flexibilní, aby mi to fungovalo i např. na sumu dat, kde je daná podmínka.

Takže definuji si následujícím způsobem ty pole nad dagridem v konstruktoru:

'addFields' =>array('top'=>array(
                        'style' => "clear:both;width:180px;font-size:12px;font-weight:bold;",
                        array(                            
                            array('text'=>'Přidat klienta',
                                  'tag' =>'a',
                                  'href'=>$this->link('Client:new'),
                                  'style' => 'text-decoration:none;width:80px;'                                
                                . 'margin:0;height:17px;padding-top:6px;'
                                ),
                            array('text'=>'Počet: ',
                                  'row'=>'id',
                                  'sqlFunc'=>'count',
                                  'style' => 'width:100px;height:17px;padding-top:6px;',
                                  ),
                            ),                                                
                        )

Tohle mi momentálně vykreslí následujicí:

Přidat klienta | Počet: count(id)

Je to flexibilní, že tam můžu přidat libovolný počet řádků nebo sloupců. Teď bych k tomu rád dodělal možnost přidání dalších podmínek k těm např. filtrujícím. Např. že by mi to zobrazovalo zvlášť počet žen a mužů, takže by část konstruktoru vypadala následovně:

'addFields' =>array('top'=>array(
                        'style' => "clear:both;width:180px;font-size:12px;font-weight:bold;",
                        array(                            
                            array('text'=>'Přidat klienta',
                                  'tag' =>'a',
                                  'href'=>$this->link('Client:new'),
                                  'style' => 'text-decoration:none;width:80px;'                                
                                . 'margin:0;height:17px;padding-top:4px;'
                                ),
                            array('text'=>'Počet mužů: ',
                                  'row'=>'id',
                                  'sqlFunc'=>'count',
                                  'style' => 'width:100px;height:17px;padding-top:5px;',
                                  'where' => 'sex_client.name=="muž"',
                                  ),
                             array('text'=>'Počet žen: ',
                                  'row'=>'id',
                                  'sqlFunc'=>'count',
                                  'style' => 'width:100px;height:17px;padding-top:5px;',
                                  'where' => 'sex_client.name=="žena"',
                                  ),     
                            ),                                                
                        )

Jinak jakmile tam mám data, kde se využívá nějaké funkce, tak si ty data hodim k sobě a pak projdu dané vyfiltrované řádky a s každým řádkem provádim tu funkci, takže např. s každým řádkem inkrementuju proměnnou, která má nastavenou vlastnost count.

Dejme tomu, že tahla podmínka pro filtrování by byla poměrně jednoduchá. Ale co kdybych chtěl mít těch podmínek víc? Např. následující podmínka:
 'where' => 'method_payment.id=1 || method_payment.id=2 || method payment.id=3',

Kdyby bylo možný převést tento text přímo na podmínku, tak by to asi šlo, ale jinak mě nic nenapadá. Ještě mě napadlo to vyřešit nějak např. následovně.

'where' => array('or'=>array(1 => 'method_payment.id', 2=>'method_payment.id', 3=>'method_payment.id',)),

Ale toto bych sám asi programátorsky nedal.

Programuji občas ve volném čase, takže se nepovažuji za nějaký eso. :-) Tohle mě nějak šikovně nenapadá, jak to vyřešit. Je možné, že to řeším úplně chybně, tak se nebudu zlobit za rady.
rafej
Profil
Co takhle?
'where' => 'method_payment.id IN (1,2,3)'

a nebo přímo v kódu
'where' => "$field_name IN ( ".implode( ',',$array_of_numbers).")"
Prochy
Profil
Právěže já to nespracovávám v MySQL, ale až poté v PHP, takže tenhle způsob nepřichází v úvahu. Takže např. pro tuhle podmínku:

array('or'=>array(1 => 'method_payment.id', 2=>'method_payment.id', 3=>'method_payment.id',)),

Bych potřeboval sestavit něco takového:

if(($row['method_payment.id']==1 || $row['method_payment.id']==2 || $row['method_payment.id']==3))
Tori
Profil
Prochy:
Takže definuji si následujícím způsobem ty pole nad dagridem v konstruktoru:
Ten kód, který jste uvedl v [#11], to je ještě součást Tabelly, anebo to je už vaše vlastní nadstavba / controller? (Nepochopila jsem, jestli se ptáte na správnou syntax k něčemu existujícímu, anebo na názor ohledně kódu, který si teprv píšete.)
Nemohl by se např. do where předávat callback, který dostane aktuální pole (příp. i celý řádek) a vrátí true/false?
Prochy
Profil
Tori:
Je to už nadstavba toho datagridu.

Nemohl by se např. do where předávat callback, který dostane aktuální pole (příp. i celý řádek) a vrátí true/false?

Bohužel callback funkce mi až tolik moc neříkájí.

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: