Databázová vrstva pro Nette: dibi, Doctrine, NotORM?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
vrana
Člen | 131
+
0
-

dakota napsal(a):

	echo $c2c->company->country->name;

Tohle ale přece rozhodně vztah 1:1 není. Více společností bude mít stejný stát, takže při použití JOINu se bude název státu přenášet opakovaně (to sice prakticky zdržovat nebude, ale je dobré si to uvědomit).

Pár dní už jde použít select("company.country.name").

Mam tiež chápať aj to že: poddotaz v MySQL s niekoľko tisíc vrátenými id asi radšej nepoužívať.

Spíš takhle: „Poddotazy v MySQL raději nepoužívat.“ NotORM se tímto pravidlem řídí a poddotazy pro MySQL překládá na IN ($ids). To nijak zvlášť nezdržuje ani při velkém množství záznamů, problém je spíš s množstvím zabrané paměti u velkých výsledků.

dakota
Člen | 148
+
0
-

vrana napsal(a):

dakota napsal(a):

	echo $c2c->company->country->name;

Tohle ale přece rozhodně vztah 1:1 není. Více společností bude mít stejný stát, takže při použití JOINu se bude název státu přenášet opakovaně (to sice prakticky zdržovat nebude, ale je dobré si to uvědomit).

Pár dní už jde použít select("company.country.name").

Asi som trochu v tomto prípade ustrelil. Vyskúšal som tieto dotazy čo som uviedol ešte raz.

V druhom prípade kedy som chcel zlepšiť efektivitu pri získaní nazvu firmy (vzťah 1:1), čiže pri

$company_2_category = $db->table('company_2_category')
                ->where("category_id", 55)
                ->where('company.country_id', 2);

$company_2_category->select('company_2_category.company_id, company.id, company.company_name, company.country_id');

foreach ($company_2_category->limit(500) as $c2c) {
        echo $c2c->company_name;
        echo $c2c->company->country->name;
}

by sa vykonali tieto dotazy:

SELECT company_2_category.company_id, company.id, company.company_name, company.country_id
FROM `company_2_category`
INNER JOIN `company` ON `company_2_category`.`company_id` = `company`.`id`
WHERE (`category_id` = 55) AND (company.country_id = 2)
LIMIT 500

SELECT *
FROM `country`
WHERE (`country`.`id` IN (2))

Trochu ma to prekvapilo, asi sa informácia o country_id získala už zo selectu, myslel som si že sa pre zistenie country_id vykoná další dotaz kde bude v IN (500 ids).

Takže efektívne to je. Len asi nemôžem pri tom využiť cache pri nazvoch stlpcov.

Len mi pride v tomto pripade zbytočne to takto komplikovať len preto že Nette\Database\Selector (NotORM) podporuje len takyto JOIN: SELECT ... FROM company_2_category INNER JOIN company ON ... a nie aj v určitých prípadoch SELECT ... FROM company INNER JOIN company_2_category ON ....

Ak nie je ochota alebo potreba rozšíriť syntax o takyto typ JOIN, tak nebudem namietať. Chcel som len ukázať aj iný pohľad.

Syntax select("company.country.name") nemám vyskušanú.

Editoval dakota (5. 1. 2011 14:00)

vrana
Člen | 131
+
0
-

dakota napsal(a):

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úť.

Proto tento dotaz nesestavuje NotORM. Já postupuji tak, že dotaz pro zjištění celkového počtu vymyslím sám – podle situace v něm zohledním připojené tabulky, group a having a případné další věci. Zobecnit to tak, aby tento dotaz „vymyslelo“ NotORM, si netroufám.

vrana
Člen | 131
+
0
-

dakota napsal(a):

Len mi pride v tomto pripade zbytočne to takto komplikovať len preto že Nette\Database\Selector (NotORM) podporuje len takyto JOIN: SELECT ... FROM company_2_category INNER JOIN company ON ... a nie aj v určitých prípadoch SELECT ... FROM company INNER JOIN company_2_category ON ....

Jaký by se pro to používal zápis? NotORM nezná schéma databáze, takže "$table.$column" se musí používat vždycky pro stejnou věc – konkrétně pro odkazovanou tabulku. Takže pro odkazující tabulku by musela vzniknout nějaká jiná syntaxe (třeba "$table:$column"), což nepovažuji za šťastné.

Len asi nemôžem pri tom využiť cache pri nazvoch stlpcov.

To mě taky trápí. Už před delší dobou jsem přemýšlel o tom, že bych povolil syntaxi $row["$table.$column"], ale to by byl bez keše zabiják výkonnosti (protože by se nejdřív musel položit dotaz bez JOINu a pak ještě znovu s JOINem). Navíc to má akademický problém v tom, že název sloupce může obsahovat tečku, ale ten se projevuje i jinde.

Každopádně jsou JOINy něco, co jsem v NotORM nikdy moc nechtěl a jsou tam jen proto, že se s nimi některé věci dělají pohodlněji. Pokud to jde, tak já osobně se bez nich obcházím – výkonnostně se jimi nic zásadního nezíská.

Cifro
Člen | 245
+
0
-

Nedávno som čítal database-development-mistakes-made-by-application-developers (via @smashingmag). Možno sa to hodí pre inšpiráciu…

David Grudl
Nette Core | 8212
+
0
-

dakota napsal(a):

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?

Zatím bych nepředbíhal. Nejprve chci dokončit a pořádně si vyzkoušet současnou podobu a pak zvažovat, co dál. Nebude existovat důvod, proč při Nette\Database používat i dibi, ale kterou cestou se vydat zatím váhám.

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.

Cílem je co nejvíce zjednodušit často používané operace CRUD. Tedy víc práce bude mimo Nette\Database.

dakota
Člen | 148
+
0
-

David Grudl napsal(a):

Cílem je co nejvíce zjednodušit často používané operace CRUD. Tedy víc práce bude mimo Nette\Database.

Ak je v tomto smere myslená aj validácia na strane modelu (použiteľná napr. aj vo formulároch), tak budem rad a ostatné nesúri.

dakota
Člen | 148
+
0
-

vrana napsal(a):

Každopádně jsou JOINy něco, co jsem v NotORM nikdy moc nechtěl a jsou tam jen proto, že se s nimi některé věci dělají pohodlněji. Pokud to jde, tak já osobně se bez nich obcházím – výkonnostně se jimi nic zásadního nezíská.

Uvediem malý zjednodušený príklad použitia Nette\Database\Selector v modeli (inšpirované príkladom v Nette\Examples), týka sa problému ktorý som uviedol v predošlých príspevkoch:

class Model extends Object
{

	public static $database;

	public static function companies()
	{
		return self::$database->table('companies');
	}
}

$companies = Model::companies();

$filter = $form->getValues();

if (!empty($filter['country_id'])) {
	$companies->where('company.country_id', $filter['country_id']);
}
if (!empty($filter['company_name'])) {
	$companies->where('company.company_name', $filter['company_name']);
}
if (!empty($filter['city'])) {
	$companies->where('company.city', $filter['city']);
}
if (!empty($filter['category_id'])) {
	$companies-> ???
	// poddotaz ??? a čo ked v danej kategorii je 20000 firiem - problem MySQL
	// INNER JOIN company_2_category ???
}

foreach ($companies->limit(50) as $company) {
	echo $company->company_name.'<br>';
	echo $company->city.'<br>';
	...
}

V uvedenom príklade chcem poukázať na: ak by som sa chcel dotazovať na firmy podľa filtra tak takto jednoducho to v Nette\Database\Selector nepôjde a musím vymyslieť iný spôsob.

Jaký by se pro to používal zápis? NotORM nezná schéma databáze, takže „$table.$column“ se musí používat vždycky pro stejnou věc – konkrétně pro odkazovanou tabulku. Takže pro odkazující tabulku by musela vzniknout nějaká jiná syntaxe (třeba „$table:$column“), což nepovažuji za šťastné.

Napadá ma zaviesť pre tabuľky M:N konvenciu pre názov napr. company_2_category čiže nie company_category – odtial zistiť potrebne cudzie kľúče pre pripojenie.

Edit:

Menná konvencia by možno pomohla v prípade company_2_category, ale nepokryla by všetky odkazujúce tabuľky.

Ďalšie možnosti:

  • ponechať „$table.$column“ a riešiť to v DatabaseReflection (v NotORM v Structure) – asi by sa musela definovať určita schéma databázy
  • zaviesť nejakú syntax v metodách where a možno aj order a select, buď namiesto . použiť niečo iné alebo zaviesť syntax v názve tabulky zapísanej v metode
  • doplniť metódu join – to asi nie ale v nutnom pripade by to pomohlo

Možno niekoho napadne niečo iné.

Nette\Database\Selector (NotORM) v určitých prípadoch značne uľahčuje prácu, sprehľadňuje kód a je aj efektívne. Ja osobne ho používať pravdepodobne budem (namiesto dibi a DibiFluent). Ale v určitých prípadoch prácu pri zostavovaní dotazu skôr pridá na rozdiel od DibiFluent.

Editoval dakota (6. 1. 2011 9:14)

vrana
Člen | 131
+
0
-

dakota napsal(a):

	$companies-> ???

Já bych neváhal použít (syntaxe NotORM):

$companies->where("id", $db->company_2_category("category_id", $filter['category_id'])->select("company_id"))
dakota
Člen | 148
+
0
-

vrana napsal(a):

Já bych neváhal použít (syntaxe NotORM):

$companies->where("id", $db->company_2_category("category_id", $filter['category_id'])->select("company_id"))

Aj vtedy keď dopredu neviem koľko firiem je v danej kategorii? Môže tam byť 1 ale aj niekoľko desiatok tísíc. Čiže v prípade MySQL bude v IN toľko id. S pamäťou by nemal byť až taký problém, načítajú sa len id. Skúšal som to nie je to až také pomalé. Len obava to používať.

Edit 10.1.2011:

Od určitého počtu firiem v danej kategórii (myslené 30000, 40000, 50000 a viac, závisí to od nastavenia memory_limit) môže byť problém s pamäťou (nebude stačiť) pri MySQL (kde je poddotaz v Nette\Database\Selector prekládaný na IN ($ids)).

Editoval dakota (10. 1. 2011 17:11)

dakota
Člen | 148
+
0
-

vrana napsal(a):

Proto tento dotaz nesestavuje NotORM. Já postupuji tak, že dotaz pro zjištění celkového počtu vymyslím sám – podle situace v něm zohledním připojené tabulky, group a having a případné další věci. Zobecnit to tak, aby tento dotaz „vymyslelo“ NotORM, si netroufám.

Count by mal zohľadňovať aktuálny stav. Ten sa može meniť v závislosti od where, group, having a limit. Napr. v DibiFluent je to riešené ako SELECT COUNT(*) FROM ( vytvorený dotaz ) as data, preto to v prip. DibiFluent nepoužívam, pretože je to neefektívne a tiež si vytvorím vlastný dotaz na count. Implementácia count v Nette\Database\Selector nie je zlá (pekne zohľadňuje či treba či netreba join) až na problem pri group a having. Implementovať to aby sa dotaz na count vytvoril správne za každých okolnosti je asi ťažké.

Edit:

Tiež si treba davať pozor a nepoužiť count bez parametru pri veľkom počte zaznamov ak ich nechcem vypisať.

Editoval dakota (6. 1. 2011 18:05)

Cifro
Člen | 245
+
0
-

dakota napsal(a):

Napr. v DibiFluent je to riešené ako SELECT COUNT(*) FROM ( vytvorený dotaz ) as data

Takýto select je v prípade MySQL pomalý… Viď https://forum.dibiphp.com/…urce-a-mysql

dakota
Člen | 148
+
0
-

Cifro napsal(a):

dakota napsal(a):

Napr. v DibiFluent je to riešené ako SELECT COUNT(*) FROM ( vytvorený dotaz ) as data

Takýto select je v prípade MySQL pomalý… Viď https://forum.dibiphp.com/…urce-a-mysql

Ved v mojom príspevku je to uvedené ako neefektívne – takto je to zatial riešené v DibiFluent. V Nette\Database\Selector je to riešené oveľa lepšie. Ale treba to vedieť použivať a dávať si pozor na určité nejasnosti.

Editoval dakota (6. 1. 2011 17:59)

Oggy
Člen | 306
+
0
-

Jak by se dal zapsat dotaz z příkladového schématu application .. aplikace nějakého autora, které nemají tag?

pokoušel jsem se to takto ale vygenerovaný dotaz je špatný v tomto joinu:

<?php
INNER JOIN application_tag ON application.application_tag_id = application_tag.id
?>

kde si volí špatné názvy – application.application_tag_id = application_tag.id

<?php
$db->application()->where('author.born <= "1950-01-01"')->where('application_tag.tag_id = NULL')
?>
<?php
SELECT application.* FROM application INNER JOIN author ON application.author_id = author.id INNER JOIN application_tag ON application.application_tag_id = application_tag.id AND (author.born <= '1950-01-01') AND (application_tag.tag_id = NULL)
?>
Oggy
Člen | 306
+
0
-

Teď mě tak napadá … nemohlo by to být nějak takto? je tohle cesta jak podobné situace řešit nebo má Jakub nějaký jiný postup?

<?php
$applications = array();
foreach ($db->application()->where('author.born <= "1950-01-01"') as $application) {
	$tag = $application->application_tag();
        if($tag == NULL) $applications[] = $application;
}
?>

Editoval Oggy (8. 1. 2011 22:25)

dakota
Člen | 148
+
0
-

Oggy napsal(a):

JOINy typu INNER JOIN application_tag ON …, LEFT JOIN application_tag ON … nie sú v Nette\Database\Selector podporované.

Myslím, že by sa dalo použiť:

$apps = $db->table('application')
	->where('author.born <= "1950-01-01"')
	->where('application.id NOT', $db->table('application_tag')->select('application_id'));
	// application.id NOT IN (...)

Nemám to však vyskúšané.

Editoval dakota (9. 1. 2011 12:54)

vaclav
Člen | 4
+
0
-

Narazil jsem na zajimave chovani v soucasne verzi u ktereho nevim jestli se jedna o bug nebo o predpokladane chovani…

Pro vyber zaznamu jen s nulovou hodnotou parent_id jsem pouzil

<?php
$tags = $db->table('tags')->where("parent_id IS NULL");
?>

vytvoreny dotaz vypada tako

SELECT * FROM `page` WHERE (name IS NULL IN (NULL))

ve chvili, kdy pouziji

<?php
$tags = $db->table('tags')->where("parent_id IS ?", NULL);
?>

tak je samozrejme vytvoren spravny dotaz

SELECT * FROM `page` WHERE (name IS NULL)
dakota
Člen | 148
+
0
-

vaclav napsal(a):

U mňa to funguje v poriadku, neviem o akú reviziu Nette Framework 2.0-dev ide, treba skúsiť najnovšiu.
V Nette\Database\Selector som chybu s IS NULL vo where neobjavil.
Nette Framework 2.0-dev, revízia c1c9de0 released on 2011–01–07.

Funguje to takto:

->where('contact_person_id', NULL);
->where('contact_person_id IS NULL');
// WHERE (`contact_person_id` IS NULL)

Editoval dakota (10. 1. 2011 16:22)

vrana
Člen | 131
+
0
-

Oggy napsal(a):

Jak by se dal zapsat dotaz z příkladového schématu application .. aplikace nějakého autora, které nemají tag?

Já bych to zapsal takhle (syntaxe NotORM):

<?php
$db->application("id NOT", $db->application_tag()->select("DISTINCT application_id"));
?>
<?php
$applications = array();
foreach ($db->application()->where('author.born <= "1950-01-01"') as $application) {
	$tag = $application->application_tag();
        if($tag == NULL) $applications[] = $application;
}
?>

To by z databáze zbytečně přenášelo všechny aplikace.

A ještě poznámka – hodnoty neuzavírej do uvozovek, jejich význam se dá v konfiguraci MySQL změnit na ohraničení identifikátorů. Nejlepší je where('author.born <= ?', '1950-01-01').

Oggy
Člen | 306
+
0
-

děkuju :-)

vaclav
Člen | 4
+
0
-

Tak to je zvláštní, děje se mi to v té samé verzi. :)

dakota napsal(a):

vaclav napsal(a):

U mňa to funguje v poriadku, neviem o akú reviziu Nette Framework 2.0-dev ide, treba skúsiť najnovšiu.
V Nette\Database\Selector som chybu s IS NULL vo where neobjavil.
Nette Framework 2.0-dev, revízia c1c9de0 released on 2011–01–07.

Funguje to takto:

->where('contact_person_id', NULL);
->where('contact_person_id IS NULL');
// WHERE (`contact_person_id` IS NULL)
Oggy
Člen | 306
+
0
-

ještě mám spíš takovou prkotinu ..

do šablony si předám z databázového dotazu nějaké záznamy, říkejme jim $entries.

a pak je řádím do různých skupinek a uvádím počet záznamů.

tzn.

<?php
today: {=$entries->where('date = CURDATE()')->count('*')}
tomorrow: {$entries->->where('date = CURDATE()+1')->count('*')}
atd ...
?>

tak NOTORM genereuje dotazy, které se na sebe nabalují.. takže vlastně ke každému výše uvedenému řádku vezme předchozí dotaz a přidá další podmínky.

cesta, která mě napadla, je si před každým dotazem uložit původní entries do jiné proměné a každý jednotlivý dotaz pokládat nad těmi „čistými“ (původndími) entries .. ale děje se to samé ..

<?php
{var today => $entries}
today: {=$today->where('date = CURDATE()')->count('*')}
{var tomorrow => $entries}
tomorrow: {$tomorrow->->where('date = CURDATE()+1')->count('*')}
?>

jak to elegantně vyřešit ? :-)

Jakub Lédl
Člen | 55
+
0
-

clone $entries. Objekty se předávají odkazem.

bojovyletoun
Člen | 667
+
0
-

Měl bych dotaz, jak využít tečkovou notaci.

Schéma (* jsou FK):

  • Tabulka člověk: id, jmeno, clovek_typ_id * – tabulka lidí
  • Tabulka clovek_typ: id, jmeno – nějaká skupina, třeba žid, křesťan, ateista, budhista, šintoista
  • Tabulka viewparam: clovek_typ_id ,* view_id * – obsahuje „připravené pohledy“- např budhista+šintoista
  • Tabulka view: id, jmeno – obsahuje názvy pohledů, v příkladu nebude použita

Mám vstupní hodnotu view_id – budou se tedy zobrazovat např židé a křesťané.
Lámu si hlavu s tím, jak to zapsat (plus půlhodiny hledání, proč to nefunguje )
elegantněji.

zde je můj mezi výsledek: (hlavně ty opakované naázvy se mi nelíbí)
$cond=$db->table("viewparam")->where("viewparam.view_id",$view);$db->table("clovek")->where('clovek_typ_id',$cond->select('clovek_typ_id'))

Psalo se zde:

/---quote
Pár dní už jde použít select(„company.country.name“).
\--

Jde to použít přímo v dotazu nebo až v iteraci?

Jak by šel daný dotaz zapsat?

Šlo by to nějak takhle:?

$cond=$db->table("viewparam")->where("viewparam.view_id",$view);
$cond->select('clovek_typ.clovek.jmeno');

nebo

$db->table('clovek')->where('clovek_typ',$cond);

Editoval bojovyletoun (17. 1. 2011 1:58)

Oggy
Člen | 306
+
0
-

lze nějak docílit LEFT JOIN nebo ho hezky obejít ?

např. dostat nějaký takovýto dotaz:

<?php
SELECT COUNT(*) FROM todo LEFT JOIN project ON todo.project_id = project.id WHERE (project.archived = 0 OR project_id IS NULL)
?>

NOTORM generuje tento dotaz:

<?php
SELECT COUNT(*) FROM todo INNER JOIN project ON todo.project_id = project.id WHERE (project.archived = 0 OR project_id IS NULL)
?>

při

<?php
{=Todo::fetchAll()->where('project.archived = 0 OR project_id IS NULL')->count("*")}
?>

cíl je dostat počet TODO, které patří k nearchivovaném PROEJKTu a nebo k žádnému projektu..

Honza Marek
Člen | 1664
+
0
-

Nemělo by se tohle vlákno zamknout? Poradna jak co napsat v NotORMu přece nepatří do diskuze o vývoji frameworku. Berte ohledy na ty, kdo maj v RSS jen tuhle sekci ;)

vrana
Člen | 131
+
0
-

Oggy napsal(a):

lze nějak docílit LEFT JOIN nebo ho hezky obejít ?

V tomto konkrétním dotazu by se to dalo vyřešit použitím project.id místo project_id nebo by se dokonce celá podmínka dala zjednodušit na 'project.archived <=> 0'. Ale přemýšlím, že bych podporu pro INNER JOIN vyhodil, protože to je jen výkonnostní optimalizace a někdy může být očividně matoucí.

vrana
Člen | 131
+
0
-

Honza Marek napsal(a):

Nemělo by se tohle vlákno zamknout? Poradna jak co napsat v NotORMu přece nepatří do diskuze o vývoji frameworku. Berte ohledy na ty, kdo maj v RSS jen tuhle sekci ;)

Jsem pro. Oni sem lidi asi píšou proto, že na to tady odpovídám. Tak mě pak jen pošlete na nové místo.

Honza Marek
Člen | 1664
+
0
-

Přijde mi vhodná sekce Databáze & ORM. Takže zamykám, další dotazy na Jakuba a jeho NotORM posílejte tam.