Databázová vrstva pro Nette: dibi, Doctrine, NotORM?
- vrana
- Člen | 131
Doplním, že implementace v Nette nedovoluje rozlišit mezi tím, když
chci získat hodnotu sloupce (číslo) a celý navázaný řádek v případě,
že se vazební sloupec jmenuje stejně jako tabulka (tedy ne
author_id
, ale jen author
). To jsme s Davidem
probírali a vyhodnotili jako celkem nepodstatný problém z těchto
důvodů:
- Nette může doporučit (kromě dokumentace také např. prostřednictvím skeletonu), aby takový název sloupce lidi nepoužívali.
- Získat samotné číslo je potřeba celkem zřídka.
- Ono to stejně bude fungovat, protože vrácený řádek prostřednictvím
__toString()
vrací právě svoje ID, jen se ten řádek získá zbytečně.
Převážila výhoda v tom, že se nemusí přemýšlet, kdy použít
->
a kdy [""]
.
P.S. V NotORM to měnit nebudu.
- bene
- Člen | 82
Vypadá to hezky.
Pár otázek, poznatků:
- Lze někde nadefinovat, aby se pro nějakou tabulku použila má třída dědící z Row? Abych to ale nemusel vždy napsat v dotaze, ale dělo se to automaticky na základě konfigurace.
table($table)->get($id)
– předpokládám že pro prim. klíč složený ze dvou sloupců se použije zápistable($table)->get(array('article_id' => 1, 'tag_id' => 2))
.- Funguje to stejně jako v NotORM, že při získávání navázaných tabulek se provádějí dotazy s IN (id, id, id)?
- Plyne z otázky č. 3 – Pokud ano, lze docílit, aby když vytáhnu třeba 1000 záznamů, se to neprovedlo, ale normálně bych mohl iterovat, aniž bych načetl do paměti naráz 1000 řádků. Zajímalo by mě, jestli tohle nějak řeší i NotORM.
- Persistování, mazání – plánují se nějaké události (before/afterDelete/Update/Create)? Pokud ano, pak nebude potřeba už žádne „ORM“.
- Docela často potřebuji vytáhnout nějaký sloupec a uložit jej do pole,
k tomu by mělo sloužit fetchPairs, ovšem existuje jeden velmi elgantní
přístup
where(xxx)->user_id
vrátí pole ideček. – návrh, pokud to implementováno není. - Plánuje se stejně jako v NotORM „předbíhání budoucnosti“?
Díky
Editoval bene (17. 12. 2010 11:39)
- vrana
- Člen | 131
bene napsal(a):
Pár otázek, poznatků:
- Na rozdíl od NotORM to nejde.
- Ne. V takovém případě je potřeba použít
table($table)->where('article_id', 1)->where('tag_id', 2)->fetch()
. - Ano.
- Ani v jedné knihovně to nejde. Doporučený způsob je omezit počet
ručně pomocí
limit(1000)
. To je ostatně vhodné i při běžnémmysql_query()
. - Metody
insert
,update
adelete
přítomné už jsou. - To David nejspíš bude považovat za magii a ani já to do NotORM zařazovat nebudu.
- To už by mělo fungovat teď –
Connection::$cache
může obsahovatNette\Caching\Cache
.
- dakota
- Člen | 148
Po skopirovaní najnovšej prefixovanej verzie to hlási: Fatal Error – Call to undefined method NConnection::onQuery()
Všimol som si rozdiel pri offset
// $article = $db->table('article')->get(1);
photos: {foreach $article->related('article_photo')->limit(3, 2) as $photo}{$photo->src}, {/foreach}
vykona sa dvakrat offset – použije sa offset v sql LIMIT 3 OFFSET 2 – čo je spravne ale vykona sa offset aj v php, takže to zobrazi ako keby bol offset 4 a nie 2
// $articles = $db->table('article');
{foreach $articles as $article}
photos: {foreach $article->related('article_photo')->limit(3, 2) as $photo}{$photo->src}, {/foreach}
{/foreach}
tu je to dobre – tu sa vykona offset iba v php, pretože sa neda použiť v sql LIMIT 3 OFFSET 2
Tiež sa stracaju hodnoty pri použiti cache Connection::$cache v iteracii
{foreach $article->related('article_2_tag') as $article_tag}{$article_tag->tag->name}, {/foreach}
prvykrat to zobrazi spravne, ale ak sa použije cache tak už nie – hlasi to notice a warning
Editoval dakota (18. 12. 2010 12:35)
- vrana
- Člen | 131
dakota napsal(a):
vykona sa dvakrat offset – použije sa offset v sql LIMIT 3 OFFSET 2 – čo je spravne ale vykona sa offset aj v php, takže to zobrazi ako keby bol offset 4 a nie 2
Už jsem to jednou psal, ale asi jsem to zapomněl odeslat – chybu jsem opravil v Nette i v NotORM.
Ostatní připomínky jsem neřešil.
- vrana
- Člen | 131
norbe napsal(a):
vrana napsal(a):
5. Metodyinsert
,update
adelete
přítomné už jsou.Myslím že dotaz nebyl na existenci metod
insert
,update
adelete
ale na existenci událostí, které se spouštějí před a po těchto akcích.
Aha. A k čemu se to používá? Věci specifické pro entitu patří do
modelu, logování zajišťuje onQuery
(v NotORM
debug
). V NotORM lze navíc vytvořit potomka
NotORM_Row
, který může tyto metody předefinovat.
- dakota
- Člen | 148
David Grudl napsal(a):
Na to jsem nenarazil. Můžeš mi poslat nějaký příklad?
Fatal Error – Call to undefined method NConnection::onQuery() mi vypisuje v sandboxe najnovšej verzie Nette už pri $db = new NConnection(…) v renderDefault, nič som v sandboxe nemenil. Zatiaľ som to vyriešil zakomentovaním riadku $this->connection->onQuery($this, $params); v Nette\Database\Statement.
Nette\Database a NotORM su vynikajúce nástroje, páči sa mi automatický left join pri použití where, order zo súvisiacej tabuľky.
V rámci Nette\Database ma napadajú ešte:
- možnosť použiť vlastný DatabaseReflection – napr. ak sa mi nevyhovuje používať názvy tabuliek a cudzích kľúčov v tvare tabulka – tabulka_id, prip. množné číslo pri názve tabuľky a jednotné pri cudzom kľúči
- možnosť použiť viac cudzích kľúčov na tu istú súvisiacu tabuľku napr. from_country_id, to_country_id, bolo to už preberané na: http://php.vrana.cz/notorm.php#…, myslim ze v NotORM sa to da
- automaticky previesť $article = $db->table(‚article‘)->select(‚title, author.name, category.name‘) na $article->title, $article->author->name, $article->category->name pomocou left join (tz. aby sa nevykonali dalšie sql na súviciace tabuľky)
Editoval dakota (20. 12. 2010 21:53)
- norbe
- Backer | 405
vrana napsal(a): A k čemu se to používá?
Dají se tím vytvořit třídy obstarávající různé typy chování (např. traverzování kolem stromu, verzování db).
Osobně jsem celkem zvědavý jak se tahle část nette vyvine. Doctrine mi přijde zbytečně složitý, NotORM si zase neumim moc představit v situaci, kde mám na několika místech aplikace zobrazovat hodnoty závislé na více db tabulkách.
- vrana
- Člen | 131
dakota napsal(a):
Chybu onQuery
jsem neřešil.
- možnosť použiť vlastný DatabaseReflection – napr. ak sa mi nevyhovuje používať názvy tabuliek a cudzích kľúčov v tvare tabulka – tabulka_id, prip. množné číslo pri názve tabuľky a jednotné pri cudzom kľúči
- možnosť použiť viac cudzích kľúčov na tu istú súvisiacu tabuľku napr. from_country_id, to_country_id, bolo to už preberané na: http://php.vrana.cz/notorm.php#…, myslim ze v NotORM sa to da
V kódu je to označené jako TODO, takže trpělivost.
3. automaticky previesť
$article = $db->table('article')->select('title, author.name, category.name')
na$article->title
,$article->author->name
,$article->category->name
pomocou left join (tz. aby sa nevykonali dalšie sql na súviciace tabuľky)
Tohle do NotORM rozhodně dělat nebudu, jde to proti jeho duchu. NotORM
s minimálním úsilím programátora vytváří limitně optimální dotazy
(někdy dokonce ty úplně nejlepší možné). Takováto konstrukce jednak
přidělává práci programátorovi a jednak může výkonnost zhoršit
(přenáší se více dat, které zaberou více paměti, navíc to zhoršuje
využitelnost query cache MySQL). Nicméně jde napsat
select('title, author.name AS author_name')
a tím to dostat na
stejnou úroveň, většinou to ale ničemu neprospěje.
- dakota
- Člen | 148
vrana napsal(a):
Tohle do NotORM rozhodně dělat nebudu, jde to proti jeho duchu.
Bol to len nápad, myslel som to ako alternatívu pri načitaní jedneho riadku pomocou ->get(id) kde je niekedy vyhodnejšie použiť left join, na author.name as author_name a automaticky left join som prišiel až po odoslaní príspevku, čo je postačujúce.
- dakota
- Člen | 148
Ak mám napr. katalog firiem – štruktúra tabuliek
company: id, company_name, ...
category: id, name
company_2_category: company_id, category_id
tz. firmu možem zaradiť do viacerých kategorii
da sa nejako pomocou Nette\Database alebo NotORM efektívne získať z databázy firmy ktoré su zaradené v určitej kategorii, čiže niečo také:
SELECT DISTINCT company.id, company.company_name
FROM company, company_2_category
WHERE company.id = company_2_category.company_id AND company_2_category.category_id = ?
- dakota
- Člen | 148
dakota napsal(a):
Ak mám napr. katalog firiem – štruktúra tabuliek
company: id, company_name, ... category: id, name company_2_category: company_id, category_id
tz. firmu možem zaradiť do viacerých kategorii
da sa nejako pomocou Nette\Database alebo NotORM efektívne získať z databázy firmy ktoré su zaradené v určitej kategorii
$companies = $db->table('company')->where('country.name = ?', 'Slovenská republika')->limit(10);
// vytvori sql: SELECT company.* FROM company INNER JOIN country ON company.country_id = country.id WHERE (country.name = ?) LIMIT 10;
$companies = $db->table('company')->where('company_2_category.category_id = ?', 5)->limit(10);
// vyhodi chybu, čo je zatial logické
// mohlo by to však vytvoriť sql: SELECT company.* FROM company INNER JOIN company_2_category ON company.id = company_2_category.company_id WHERE company_2_category.category_id = ? LIMIT 10;
Editoval dakota (22. 12. 2010 12:09)
- vrana
- Člen | 131
dakota napsal(a):
da sa nejako pomocou Nette\Database alebo NotORM efektívne získať z databázy firmy ktoré su zaradené v určitej kategorii:
Nejsnáze takto (NotORM syntaxe):
<?php
foreach ($db->company_2_category("category_id", $category_id) as $company_2_category) {
echo $company_2_category->company["company_name"];
}
?>
Editoval vrana (22. 12. 2010 15:29)
- dakota
- Člen | 148
takže da sa použiť
foreach ($db->table('company_2_category')->where("category_id = ?", ...)->where('company.country_id = ?', ...)->limit(10) as $company_2_category) {
echo $company_2_category->company->company_name;
}
pričom to vytvorí
SELECT company_2_category.*
FROM company_2_category INNER JOIN company ON company_2_category.company_id = company.id
WHERE (category_id = ...) AND (company.country_id = ...)
LIMIT 10;
SELECT * FROM company WHERE (company.id IN (...));
Je to trochu iný sposob ako, na ktorý som zvyknutý, hlavne ak raz
vypisujem všetky firmy, potom ich mam možnosť filtrovať podla kategorie,
štátu … ,
pri všetkych firmách som použil SELECT * FROM company, pri vypise určitej
kategorie som použil
INNER JOIN company_2_category ON company.id = company_2_category.company_id
a podmienku, pripadne ďalšie podmienky napr. štát, … .
Editoval dakota (22. 12. 2010 18:30)
- bojovyletoun
- Člen | 667
<mg src=http://www.notorm.com/static/classes.png
tip
akorád nevím zda jde o nette či notorm
foreach($db->firmy() as $firma){
foreach($firma)->firmy_2_kategorie() as $i){
echo $i->kategorie['jmeno']
}
}
ještě se chci zeptat, asi mi to uniklo, ale nette\Db bude mít stejnou syntaxi jako notorm (základ) nebo jinou?
- dakota
- Člen | 148
NotORM a Nette\Database maju odlišnú syntax v zápise, napr. NotORM $db->tablename(), Nette\Database $db->table(tablename).
Kategórie sa daju vypisať takto:
foreach ($company_2_category->company->related('company_2_category') as $category) {
echo $category->category->name.', ';
}
Myslel som si že sa da použiť zápis, kde by som na zaklade formulára pridaval jednotlivé podmienky
$companies = $db->table('company');
// všetky firmy
$companies = $db->table('company')
->where('company_2_category.category_id = ?', 5); // toto nie je možné
// firmy, ktoré su zaradené v kategorii 5
$companies = $db->table('company')
->where('company_2_category.category_id = ?', 5) // toto nie je možné
->where('company.country_id = ?', 22);
// firmy, ktoré su zaradené v kategorii 5 a sú z krajiny 22
// dalšie podmienky
v šablone potom:
{foreach $companies->order('company.company_name')->limit(50) as $company}
<div>
<h1>{$company->company_name}</h1>
<p>{$company->country->name}</p>
<ul>
{foreach $company->related('company_2_category') as $category}
<li>{$category->category->name}</li>
{/foreach}
</ul>
</div>
{/foreach}
a že to vytvorí sql (firmy, ktoré su zaradené v kategorii 5 a sú z krajiny 22):
SELECT company.* FROM company
INNER JOIN company_2_category ON company.id = company_2_category.company_id
WHERE company_2_category.category_id = 5
AND company.country_id = 22
ORDER BY company.company_name
LIMIT 50;
Editoval dakota (23. 12. 2010 8:54)
- bojovyletoun
- Člen | 667
Nevíte jak v pomocí tohoto nástroje vypsat „kontigenční tabulku“?
Příklad:
tabulka člověk: jméno, id.
tabulka země: jméno, id.
tabulka vazba: clovk_id, zeme_ id.
Zo brazí se tablulka asi taková
X | Počet | Josef | Eva | Pavla |
Počet | X | 2 | 0 | 3 |
USA | 2 | Y | N | Y |
China | 2 | Y | N | Y |
Japan | 1 | N | N | Y |
V dibi jsem to vyřešil pomocí tří dotazů (sice krkolomných
Tyto tři dotazy(jejich výsledky) v šabloně byli proměnné: pozn. první a druhý dotaz jsou totožné
dibi::select("jmeno,id, count(id)as ucast")->
from("lidi")->leftJoin("vazba")->on("clovek_id=id")->groupBy("id")->fetchAll();
dibi::select("jmeno,id,count(id) as ucast")->
from("zeme")->leftJoin('vazba')->on("zeme_id=id")->groupBy("zeme.id")->fetchAll();
template->exists=dibi::select("clovek_id,zeme_id, 1 as m")->from("vazba")->fetchAssoc("clovek_id|zeme_id=m");
v šabloně klasicky foreache a :
- $zeme->jmeno, $clovek->jmeno, nadpisy
- $akce->ucast , $clovek->ucast pro sumy
- isset(exists[$clovek->id][$zeme->id]?„ano“:„ne“ pro jednotlivé bunky
otázka je, jak toto zapsat pro \Nette\Db? (aniž bych měl 47 dotazů)
(mimochodem toto se mi stalo
pokud nepoužiji na konci fetchall)
Editoval bojovyletoun (24. 12. 2010 18:09)
- Filip Procházka
- Moderator | 4668
tohle je velice specifická potřeba a myslím si, že tvoje řešení je asi nejoptimálnější
- Tharos
- Člen | 1030
Bug (tentokrát již ne PHP): zde
jsou prohozené parametry funkce strpos
, takže momentálně
příkazy, které mají jít přes preprocessor jenom kvůli substitucím, přes
preprocessor vůbec nejdou.
Související issue: když se pořadí parametrů opraví, na preprocessor se
posílají už i příkazy generované v konstruktoru driveru (například
u MySQL driveru příkaz SET time_zone='+01:00'
, protože obsahuje
dvojtečku). V době, kdy se ale instance driveru vytváří, ještě není
u connection preprocessor k dispozici. Rychlý workaround: tohle
by mělo být na řádku 52. :)
- dakota
- Člen | 148
Ja sa zase pridávam k väčšej podpore spájania tabuliek pre použitie podmienok vo where, napr.
$companies = $db->table('company')
->where('company_2_category.category_id = ?', 5) // toto nie je možné
->where('company.country_id = ?', 22);
Tiež k lepšej efektivite pri zistení počtu.
$company_2_category = $db->table('company_2_category')
->where("category_id = ?", ..)
->where('company.country_id = ?', ...);
$totalCount = $company_2_category->count();
// načíta z databázy údaje aby sa zistil počet
$totalCount = $company_2_category->count('company_2_category.company_id');
// sa nedá použiť, nezohľadňuje where z cudzej tabuľky
foreach ($company_2_category->limit(50, 100) as $c2c) {
echo $c2c->company->company_name;
}
Editoval dakota (27. 12. 2010 10:25)
- vrana
- Člen | 131
dakota napsal(a):
Ja sa zase pridávam k väčšej podpore spájania tabuliek pre použitie podmienok vo where, napr.
Tečková notace se používá pro připojení tabulek ve vztahu 1:N. Nejde ji tedy použít pro připojení tabulek ve vztahu M:N. Je potřeba změnit uvažování, jak už jsem popisoval.
Tiež k lepšej efektivite pri zistení počtu.
Pro použití agregační funkce je potřeba předat metodě parametr, např.
tedy count("*")
.
- dakota
- Člen | 148
vrana napsal(a):
Tečková notace se používá pro připojení tabulek ve vztahu 1:N. Nejde ji tedy použít pro připojení tabulek ve vztahu M:N. Je potřeba změnit uvažování, jak už jsem popisoval.
V príklade ktorý som uviedol – ak chcem získať firmy z určitej kategórie (firmy môžu byť zaradené vo viacerých kategóriach) a súčasne z určitého štátu môžem v Nette\Database použiť tento postup príp. nejaky iný?
$company_2_category = $db->table('company_2_category')
->where("category_id = ?", ...)
->where('company.country_id = ?', ...);
foreach ($company_2_category->limit(50, 100) as $c2c) {
echo $c2c->company->company_name;
}
Pro použití agregační funkce je potřeba předat metodě parametr, např. tedy
count("*")
.
Možno som sa zle vyjadril, chcel som tým poukazať na: ak pripojím tabuľku uvedením tabuľky vo where, tak pre zistenie počtu nemožem použiť count s parametrom pretože to vyhodi chybu (v dotaze pre zistenie počtu nedojde k pripojeniu tabuľky pričom vo where je uvedená), v tomto pripade funguje len count() bez parametru.
Editoval dakota (28. 12. 2010 13:16)
- David Grudl
- Nette Core | 8218
Trošku se nám to tu zvrhává v mišmaš.
- prosím tedy příspěvky k Nette\Database do fóra Databáze & ORM či přímo Nette\Database: představení
- příspěvky týkající se NotORM asi nejlépe na http://forum.php7.org (nebo existuje fórum pro NotORM?)
- příspěvky týkající se vývoje klidně sem
Tady bych to asi trošku s dovolením promazal.
Ještě k problému s PHP 5.2 a onQuery: považoval jsem ho za vyřešený od 21. prosince, ale jak se zdá, ve verzí 5.2.6 a nižších to stále nefunguje, kvůli jinému bugu v PHP. Fixed.
- David Grudl
- Nette Core | 8218
Tharos napsal(a):
zde jsou prohozené parametry funkce
strpos
…
Související issue…
fixed
- dakota
- Člen | 148
dakota napsal(a):
Pro použití agregační funkce je potřeba předat metodě parametr, např. tedy
count("*")
.Možno som sa zle vyjadril, chcel som tým poukazať na: ak pripojím tabuľku uvedením tabuľky vo where, tak pre zistenie počtu nemožem použiť count s parametrom pretože to vyhodi chybu (v dotaze pre zistenie počtu nedojde k pripojeniu tabuľky pričom vo where je uvedená), v tomto pripade funguje len count() bez parametru.
napišem to ešte tu v podstate to len zopakujem – možte to potom
zmazať, vidím chybu v count ak je pripojená tabulka pomocou where, tak
count('*')
nefunguje – týka sa fungujúceho zápisu
v Nette\Database.
napr.
$company_2_category = $db->table('company_2_category')
->where("category_id = ?", ...)
->where('company.country_id = ?', ...);
$company_2_category->count('*') // vyhodí chybu
Editoval dakota (30. 12. 2010 14:17)
- Aurielle
- Člen | 1281
David Grudl napsal(a):
- příspěvky týkající se NotORM asi nejlépe na http://forum.php7.org (nebo existuje fórum pro NotORM?)
Whois píše Domain is for sale + je tam jen defaultní hostmonsterovská stránka…
- gawan
- Člen | 110
vrana napsal(a):
Konečně jsem to pochopil a opravil v NotORM (v Nette ne).
chcem sa vás (Jakub, David) opýtať akým spôsobom budete udržiavať NotORM a Nette\Database? Opravy a nové vlastnosti budete commitovať do oboch alebo \Nette\Database bude len „chodobnejší príbuzný“ resp. oklieštená verzia NotORM?
- vrana
- Člen | 131
gawan napsal(a):
chcem sa vás (Jakub, David) opýtať akým spôsobom budete udržiavať NotORM a Nette\Database? Opravy a nové vlastnosti budete commitovať do oboch alebo \Nette\Database bude len „chodobnejší príbuzný“ resp. oklieštená verzia NotORM?
Mě se moc nechce udržovat něco, co sám nepoužívám. A
Nette\Database
se alespoň prozatím používat nechystám. Takže
já se budu nadále soustředit na NotORM a je možné, že se oba projekty
časem více rozejdou. Nevylučuji, že některé změny v NotORM budu dělat
i do Nette\Database
, ale jako svou „práci“ to nechápu.
- David Grudl
- Nette Core | 8218
gawan napsal(a):
chcem sa vás (Jakub, David) opýtať akým spôsobom budete udržiavať NotORM a Nette\Database? Opravy a nové vlastnosti budete commitovať do oboch alebo \Nette\Database bude len „chodobnejší príbuzný“ resp. oklieštená verzia NotORM?
Bugfixy v NotORM budu portovat do Nette\Database, snad jich moc nebude :-)
Každopádně Nette\Database\Selector nepovažuju za chudobného príbuzného NotORM, ba právě naopak, mělo by jít o jakési „bestof“ z NotORM a dibi. Takže Selector umí navíc automaticky ošetřovat identifikátory, má (z mého pohledu) srozumitelnější API, integrovaný Debug panel atd.
- dakota
- Člen | 148
V prípade štruktúry tabuliek:
offer: id, company_id, contact_person_id, title, ...
contact_person: id, company_id, first_name, surname, ...
company: id, company_name, ...
Uživateľ može ale nemusí uviesť pri ponuke kontaktnú osobu.
V prípade uvedenia kontaktnej osoby sa v tabuľke offer v contact_person_id
uloží id kontaktnej osoby (dátový typ int).
V prípade neuvedenia kontaktnej osoby bude v contact_person_id 0
(alebo NULL).
$offer = $this->db->table('offer')->get(199);
echo 'Offer: ' . $offer->title.'<br>';
echo 'Company: ' . $offer->company->company_name.'<br>';
if ($offer->contact_person) {
echo 'Contact person: ' . $offer->contact_person->first_name . ' ' . $offer->contact_person->surname.'<br>';
}
1. možnosť – neuvedená kontaktná osoba – v contact_person_id je
NULL (nastavené default NULL
pri contact_person_id), vykoná
sa dotaz:
SELECT `id`, `surname`, `first_name` FROM `contact_person` WHERE (`contact_person`.`id` IN (''))
// v IN je ''
2. možnosť – neuvedená kontaktná osoba – v contact_person_id je
uvedené 0
, vykoná sa dotaz:
SELECT `id`, `surname`, `first_name` FROM `contact_person` WHERE (`contact_person`.`id` IN (0))
// v IN je 0
Chcem sa spýtať či sa v tomto príklade v prípade neuvedenia kontaktnej osoby musí v Nette\Database vykonať dotaz na zistenie údajov o kontaktnej osobe pre danú položku (ponuka), keď už z contact_person_id je patrné že daná položka (ponuka) kontaktnú osobu nemá.
Tiež sa chcem spýtať, síce to nesúvisí s Nette\Database, čo je
lepšie ukladať do contact_person_id (dátový typ int), ak neuvediem
kontaktnú osobu pri danej položke: 0
alebo NULL (neznáma
hodnota). V súčasnosti používam 0
, pretože sa mi to zdá
správnejšie.
Uvedenú štruktúru tabuliek je síce možné zmeniť na (z pohľadu normalizácie je správnejšia, ale zložitejšia):
offer: id, company_id, title, ...
contact_person: id, company_id, first_name, surname, ...
offer_contact_person: offer_id, contact_person_id
company: id, company_name, ...
Editoval dakota (4. 1. 2011 15:18)
- Tharos
- Člen | 1030
dakota napsal(a):
Tiež sa chcem spýtať, síce to nesúvisí s Nette\Database, čo je lepšie ukladať do contact_person_id (dátový typ int), ak neuvediem kontaktnú osobu pri danej položke: 0 alebo NULL (neznáma hodnota). V súčasnosti používam 0, pretože sa mi to zdá správnejšie.
Správnější je samozřejmě null
. Kontaktní osoba prostě
není nadefinována. 0
znamená, že se záznamem souvisí
kontaktní osoba s ID 0
.
Edit: Zvláště pak při použití cizích klíčů bys s 0
použitou tímto způsobem už vůbec nepochodil.
Editoval Tharos (4. 1. 2011 13:11)
- dakota
- Člen | 148
Tharos napsal(a):
Správnější je samozřejměnull
. Kontaktní osoba prostě není nadefinována.0
znamená, že se záznamem souvisí kontaktní osoba s ID0
.
Vďaka za radu asi to upravím na NULL. ID 0
sa mi zdá
vzláštne ale možné to je.
Pri NULL to pre získanie údajov o kontaktnej osobe v Nette\Database položí dotaz:
SELECT `id`, `surname`, `first_name` FROM `contact_person` WHERE (`contact_person`.`id` IN (''))
pripadne ak viac kontaktných osôb
SELECT `id`, `surname`, `first_name` FROM `contact_person` WHERE (`contact_person`.`id` IN ('', 5, 8 ))
neviem či je to v poriadku alebo nie
Editoval dakota (4. 1. 2011 15:15)
- dakota
- Člen | 148
bojovyletoun napsal(a):
- jestli to správně chápu, tak pro referenced row se používá $offer->ref(‚company‘) A nebo $offer->company se stejným významem? (z metody __&get v selectoru)
Myslím si že $offer->ref(‚company‘) a $offer->company majú rovnaký význam
Edit: Prečo ma metóda ref() tak krátky názov? Nebolo by vystižnejšie referenced() alebo referencedRow()?
Editoval dakota (4. 1. 2011 14:59)
- vrana
- Člen | 131
dakota napsal(a):
Chcem sa spýtať či sa v tomto príklade v prípade neuvedenia kontaktnej osoby musí v Nette\Database vykonať dotaz na zistenie údajov o kontaktnej osobe pre danú položku (ponuka), keď už z contact_person_id je patrné že daná položka (ponuka) kontaktnú osobu nemá.
Dobrý postřeh. Upravil jsem NotORM i Nette\Database tak, aby se na odkaz
na hodnotu NULL
neptaly.
- dakota
- Člen | 148
vrana napsal(a):
Dobrý postřeh. Upravil jsem NotORM i Nette\Database tak, aby se na odkaz na hodnotuNULL
neptaly.
Vďaka za rýchlu reakciu. Chcel by som ešte upozorniť na:
Chyba v count() pri neexistujúcom zázname – https://forum.nette.org/…ase-selector
Nefungujúci JOIN na základe určitého zápisu where – https://forum.nette.org/…ase-selector
Neviem či je chyba aj v NotORM alebo len v Nette\Database.
- vrana
- Člen | 131
dakota napsal(a):
Chyba v count() pri neexistujúcom zázname – https://forum.nette.org/…ase-selector
Díky za report. Opravil jsem to v NotORM, v Nette ne.
Nefungujúci JOIN na základe určitého zápisu where – https://forum.nette.org/…ase-selector
V NotORM se to neprojevuje, v Nette jsem to nezkoušel.
- dakota
- Člen | 148
Ako mám správne postupovať v prípade použitia GROUP pri zistení celkového počtu pre stránkovanie?
Napríklad:
$cs = $this->db->table('company_2_category')
->group('company_id')
->select('company_id, COUNT(category_id) AS category_count');
$totalCount = $cs->count('*');
foreach($cs->limit(30, 60) as $c) {
echo $c->company->company_name.' - '.$c->category_count.'<br>';
};
Použitie count() bez parametru – je neefektívne pri veľkom počte záznamov.
Pri count('*')
sa vykoná dotaz:
SELECT COUNT(*) FROM `company_2_category`
Možno by sa dala metoda count upraviť aby v prípade GROUP použila tento dotaz aj ked nie je až taký efektívny ale iný ma nenapadá:
SELECT COUNT(*) FROM (SELECT company_id, COUNT(category_id) FROM company_2_category GROUP BY company_id) AS tmp
Ešte je tu možnosť v tomto príklade ukladať do tabuľky company počet kategorii – využiteľné pri zotriedení podľa počtu kategorii.
- dakota
- Člen | 148
David Grudl napsal(a):
Každopádně Nette\Database\Selector nepovažuju za chudobného príbuzného NotORM, ba právě naopak, mělo by jít o jakési „bestof“ z NotORM a dibi. Takže Selector umí navíc automaticky ošetřovat identifikátory, má (z mého pohledu) srozumitelnější API, integrovaný Debug panel atd.
Mám zopár otázok ohľadom budúceho vývoja Nette\Database.
Bude sa Nette\Database príp. Nette\Database\Selector v blízkej budúcnosti rozširovať o nejaké dalšie metódy, príp. možnosti, nástroje – napr. o niektoré veci z dibi?
Uvažuje sa napr. o doplnenie manuálneho JOIN v rámci Nette\Database\Selector (popri automatickom JOIN) alebo o rozšírení automatického JOIN o tabulky vo vzťahu M:N?. V rámci testovania by som v určitých prípadoch použil iné spojenie tabuliek aké mi to Nette\Database\Selector automaticky vytvorí, napr. v rámci pridavania podmienok na základe určitého filtra – uvádzal som to už v predošlých príspevkoch.
Uvažuje sa o prepacovaní, zefektívnení a zaradení DibiFluent (príp. inej podoby skladania dotazov) do Nette\Database? Aby sa v určitých prípadoch nemuselo popri Nette\Database používať aj dibi. Zaujímavá by bola napr. kombinácia DibiFluent (resp. kombinácia DibiFluent a Nette\Database\Selector) pri skladaní dotazu a Nette\Database\Selector pri získavaní súvisiacich záznamov.
Je možné konkrétnejšie uviesť aká ďalšia podpora modelu sa popri Nette\Database v Nette pripravuje? V úvodnom príspevku je slabo načrtnuté čo sa v rámci podpory modelu pripravuje.
Editoval dakota (4. 1. 2011 22:22)
- dakota
- Člen | 148
Chcem sa tiež spýtať na efektivitu dotazov pri Nette\Database\Selector.
Vo väščine prípadov sa mi zdá Nette\Database\Selector (NotORM) efektívne a pri skúšaní som nenarazil na žiadny väčší problém týkajúci sa efektívnosti a možné problémy plynuli skôr z neznalosti použitia napr. pri count. Tiež sa mi parkrát stalo že mi pri výpise väčšieho počtu záznamov zabralo dosť z pamäti príp. mi pamäť nestačila.
Sú nejaké limity, pri ktorých môže mať Nette\Database\Selector (NotORM) problem s efektívnosťou? Alebo sú nejaké odporúčania kedy Nette\Database\Selector (NotORM) použiť s opatrnosťou?
Napríklad
- vzťah 1:1 alebo vzťah ktorý sa blíži k 1:1, niekedy sa mi zdá že by bolo v tomto prípade lepšie načitať data už pomocou JOIN
- poddotazy v MySQL – tie sa pri MySQL prevádzajú na IN ( id1, id2, … ). Ak by poddotaz vrátil niekoľko stoviek id možem to v MySQL použiť?
- výpis veľkého počtu záznamov – odporúča sa limit(1000)
- opakované použitie IN ( … ) s niekoľko desiatkami id pre získanie rôznych údajov
- možná zvýšena réžia na strane php pri získavaní mnohých súvisiacich záznamov
- väčšie množstvo previazaných tabuliek, niekoľko stotisíc záznamov v tabuľkách
Neistota sa však týka hlavne klauzuly IN a vzťahov blížiacich sa k 1:1. Je nejaký limit počtu id v IN pokiaľ to je ešte efektívne? Žiadne rapidne zhoršenie efektívnosti som zatiaľ pri skúšaní nespozoroval.
Výhody Nette\Database\Selector (NotORM) týkajúce sa efektívnosti sa uvádzajú: Nette\Database\Selector (NotORM) kladie konštantný počet dotazov (záznamy získa pomocou IN), kladie jednoduché dotazy, využíva sa databazová cache, neprenášajú sa zbytočne duplicitné záznamy atď. Výhoda je aj to že sa nemusia ručne písať dotazy na získanie dát zo spojených tabuliek.
Nette\Database\Selector (NotORM) by mohol tiež poskytnúť možnosť nejakym spôsobom upraviť správanie v prípadoch i mierne zhoršenej efektívnosti v určitých prípadoch a tým zvýšiť efektívnosť.
Editoval dakota (5. 1. 2011 10:54)
- vrana
- Člen | 131
dakota napsal(a):
Tiež sa mi parkrát stalo že mi pri výpise väčšieho počtu záznamov zabralo dosť z pamäti príp. mi pamäť nestačila.
Obrovské výsledky je vhodné stahovat postupně, např. pomocí
limit(1000)
. To je ostatně vhodné i při použití běžných
dotazů, protože i u nich se nejprve celý výsledek přenese a uloží do
paměti (kterou PHP nezapočítává do limitu, takže to není tak dobře
vidět, ale zabraná paměť to je). Viz What about memory
consumption?.
vzťah 1:1 alebo vzťah ktorý sa blíži k 1:1, niekedy sa mi zdá že by bolo v tomto prípade lepšie načitať data už pomocou JOIN
Samozřejmě, k tomu se dá použít select("tab.col")
. Jen by
někdy mohl být problém s názvy sloupců, to se dá v NotORM vyřešit
vlastní Structure.
poddotazy v MySQL – tie sa v MySQL prevádzajú na IN ( id1, id2, … ). Ak by poddotaz vrátil niekoľko stoviek id možem to v MySQL použiť?
Ano, bohužel to je pořád lepší, než přímé vložení poddotazu.
možná zvýšena réžia na strane php pri získavaní mnohých súvisiacich záznamov
V tomhle by k problémům dojít nemělo. Související záznamy se ukládají do PHP polí, která mají konstantní čas přístupu při libovolném počtu záznamů.
Nette\Database\Selector (NotORM) by mohol tiež poskytnúť možnosť nejakym spôsobom upraviť správanie v prípadoch i mierne zhoršenej efektívnosti v určitých prípadoch a tým zvýšiť efektívnosť.
Máš na mysli něco konkrétního? I tak se do toho asi pouštět nebudu, protože chci, aby chování zůstalo předvídatelné, NotORM nemá být blackbox.
- dakota
- Člen | 148
vrana napsal(a):
Nette\Database\Selector (NotORM) by mohol tiež poskytnúť možnosť nejakym spôsobom upraviť správanie v prípadoch i mierne zhoršenej efektívnosti v určitých prípadoch a tým zvýšiť efektívnosť.
Máš na mysli něco konkrétního? I tak se do toho asi pouštět nebudu, protože chci, aby chování zůstalo předvídatelné, NotORM nemá být blackbox.
V tomto prípade som mal na mysli napr. toto:
$company_2_category = $db->table('company_2_category')
->where("category_id", ...)
->where('company.country_id', ...);
foreach ($company_2_category->limit(500) as $c2c) {
echo $c2c->company->company_name;
echo $c2c->company->country->name;
}
vidím to ako vzťah 1:1 – čiže z prveho dotazu sa 500 id premietne do IN ( 500 id ), zámerne som uviedol väčší počet ale rovnako je to aj pri limit(50), tak to pre lepšiu efektivitu zapišem takto:
$company_2_category = $db->table('company_2_category')
->where("category_id", ...)
->where('company.country_id', ...);
$company_2_category->select('company_2_category.company_id, company.company_name, company.country_id');
foreach ($company_2_category->limit(500) as $c2c) {
echo $c2c->company_name;
// chcem zapisať aj názov krajiny ale nemam ako jedine znovu použiť
echo $c2c->company->country->name;
}
V tomto prípade mi fakt chýba pripojenie tabuľky typu
SELECT ... FROM company INNER JOIN company_2_category ON ...
alebo
nejaký iný spôsob ako zvýšiť efektivitu. Ak je to len môj iný pohľad
alebo iný spôsob myslenia tak to považuj za bezpredmetné. Sú to možno
špecifické potreby ale môžu sa vyskytnúť.
Mam tiež chápať aj to že: poddotaz v MySQL s niekoľko tisíc vrátenými id asi radšej nepoužívať.
Editoval dakota (5. 1. 2011 11:15)
- vrana
- Člen | 131
dakota napsal(a):
Ako mám správne postupovať v prípade použitia GROUP pri zistení celkového počtu pre stránkovanie?
Nejefektivnější je v takovém případě položit extra dotaz, v tvém
případě
table("company_2_category")->count("DISTINCT company_id")
.
Zkratku pro to asi dělat nebudu, protože by se to poněkud zkomplikovalo
třeba v případě count("DISTINCT x")
nebo dokonce
max("tab2.y")
a ještě víc u MultiResult
.
- dakota
- Člen | 148
vrana napsal(a):
Nejefektivnější je v takovém případě položit extra dotaz, v tvém případě
table("company_2_category")->count("DISTINCT company_id")
.
len malá poznámka: distinct som vyskúšal funguje v tomto prípade rovnako, ale týmto sa v podstate ignoruje GROUP BY … HAVING …, možno tie konštrukcie nie sú časté ale môžu sa vyskytnúť.
Edit:
Ono vo všeobecnosti je niekedy problém s efektivitou pri GROUP BY a tiež DISTINCT napr. pri zistení celkového počtu. Možno by som to nechal tak ako je to teraz implementované, neviem či však s tým nebudú niekedy problémy alebo nejasnosti.
Editoval dakota (5. 1. 2011 12:01)