PHP fóra: Builder | JakPsatWeb | Webtrh
Nejste přihlášen(a)
Který z následujících návrhů je lepší pro eshop, kde si prodejce může sám volit, jaká další data chce k nějakému záznamu ukládat (ke zboží typu „knížka“ může být potřeba zapsat ISBN, k „bazarovým automobilům“ navíc počet najetých kilometrů atd.)?
Jedinou podmínkou je, že údaje stejného typu (kniha->ISBN, jetina->kilometry) musí být možné porovnávat a selektovat v rámci databáze, bez nutnosti vstupu php kódu – což by myslím neměl být velký problém
Varianta A
Zbozi [idZbozi, idKategorie, ...]
Uloziste_Metadata[idMeta, idKategorie, Nazev, idDatatypu]
Uloziste_Datatypy[idDatatypu, Nazev, CallbackOnRead, CastType]
Uloziste_Data[idMeta, hodnota::TEXT]
všechny datové typy ukládat do tabulky Uloziste_Data jako typ TEXT. Pamatujeme si zároveň zamýšlený datový typ a můžeme ho v SQL dotazu pomocí SQL funkce CAST() správně vyvolat (Uloziste_Datatypy.CastType) pro porovnávání apod.
Varianta B
Zbozi [idZbozi, idKategorie, ...]
Kategorie[idKategorie, nazevTabulky, definicePoli]
Kat_nazevTabulky1[idZbozi, ISBN::TEXT]
Kat_nazevTabulky2[idZbozi, kilometry::BIGINT]
...
pro každý takový případ vytvořit vlastní tabulku, kde jsou data uloženy ve svém, správném, datovém typu. Sami cítíte, že něco není správně. Stovky kategorií = stovky tabulek. A provozní nutnost používat DDL příkazy. (schema je trochu zjednodušeno).
Nebo existuje Varianta C?
Ještě jsem tohle nikde neviděl řešit, nebo googloval nesprávné fráze. Už je to třetí (absolutně nesouvisející) projekt, kde to musím řešit, a stále si nejsem jistý, jestli a jak to dělat dobře.
tohle jsem taky jednou řešil, ale po nacenění zákazník z požadavku ustoupil :-)
Varianta A – podle mne ti CAST nepomůže – respektive databáze to sice schroustá, ale určitě ne optimálním způsobem. Představ si, že buudeš chtít řadit podle INT, ale všechno bude uloženo jako TEXT – databáze musí přečíst všechny řádky, provést na nich přetypování a pak to podle toho seřadit. Stejně tak nad TEXT nikdy neuděláš číselný index. No a naposled – TEXT položky s sebou afaik, alespoň v MySQL, nesou dost výkonnostních omezení (nelze dělat temporary tabulky v paměti, nelze mít pevnou velikost záznamu atp.)
Varianta B – to budeš mít SELECT třeba s 20 JOINy? To mi připadá dost vražedné.
Varianta C
Udělal bych tabulku Zbozi, do ktere bych dal co nejvic beznych polozek, ktere
jsou u vsech kategorii (nazev, cena, popis, obrázek,…)
a pro každou kategorii bych dělal jednu extra tabulku ZboziDetailXXX, ve
které by byl zbytek položek.
hledáme zkušené programátory v PHP / Nette: http://www.twobits.cz/volna-mista/
mě připadá to co popisuje Wosonj jako C úplně stejné jako B ^_^
pro připojení detailů se dá použít místo JOINů také dědičnost tabulek. pokud to ovšem umí db stroj na kterém to pojede – např: http://www.postgresql.org/…ritance.html
je to rychlejší a jednodušší na správu
___________
doplnění: navíc pokud a tabulky T (hlavní) zdědíte TC1 a TC2 (kategorie 1 a 2) a přes obě přistupujete k témuž záznamu, může být v podstatě tento záznam zároveň v obou kategoriích. problém je pak jen v aplikaci, kde jaksi nemůže být objekt instancí dvou sourozeneckých tříd zároveň. něco takového jsem ale naštěstí zatím nepotřeboval :]
Editoval paranoiq (19. 2. 2009 11:29)
podívej se, jak to má řešený e-shop magento, ve svých app jsem to
používal podobně
tj tabulka atributy, tabulka spojení atribut_produkt, tabulka
atribut_hodnoty_int, tabulka atribut_hodnoty_decimal, tabulka
atribut_hodnoty_text
Díky za komentáře! Mrknu na řešení od Kevujina.
Stav po prvním kole:
Varianta A – NE
důvody: dobře napsal Wosonj
Varianta B – nerozhodně
Varianta C od Wosonj je moje B. Společnou slabinou je elegance. V počátku
bude těch kategorií pár desítek, v budoucnu možná pár set. To znamená
pár set dynamicky migrujících tabulek po databázi, spolu se php kódem,
který tabulky vytváří, mění a maže. Tomu říkám binec! :)
Dejme tomu, že separujeme tyto tabulky v rámci MySQL do vlastní databáze a
použijeme db motor InnoDB, u kterého nehrozí kapacitní omezení množství
tabulek v databázi tak nízko, jako při MyISAM. Ale stále to ještě
není ono.
Možná kdyby se Varianta A trochu rozšířila, a inteligentně ukládala data do správných (a indexovatelných) datových typů… takhle:
Varianta A+
...
Uloziste_Data[idMeta, idZbozi, hodnotaText::TEXT, hodnotaInt::BIGINT, hodnotaFloat::FLOAT, hodnotaDate::DATETIME]
Plus znamená i optimalizaci: všechna hodnotová pole DEFAULT NULL
a nad tabulkou indexy vždy ve dvojici (idMeta, hodnotovePole), takže v rámci
jedné kategorie zboží půjde selektovat i řadit podle indexu nad
optimálním datovým typem.
Nevýhoda: menší plýtvání místem v db. u ISBN se např. použije na
řádku jen pole hodnotaText, u kilometrů jen hodnotaInt. Ostatní sloupce
(vyjma id) zůstanou NULL.
Zní to líp?
Editoval petrs (19. 2. 2009 12:03)
ad oddělení do vlastní databáze – to je nesmysl, neuděláš JOIN přes databáze. Varianta A+ mi připadá už relativně rozumná, i když osobně bych se jí bránil z obav ze škálovatelnosti. Představ si, že tam budeš mít 10.000 produktů, u každého bude v průměru třeba 10 položek (a to bude určitě víc) a už máš 100k řádků. Těžko odhadnout průměrnou délku řádku, ale pokud tam budou i popisky a další, dost snadno se dostaneš na 100MB tabulku… Prostě se bojím výkonu…
Vůbec, co to bude za eshop, že tam bude potřeba pár set kategorií s vlastním vyhledáváním? Já to chápu tak, že eshop má kategorie ve stromu, kde potřeba mít vlastní položky je tak do 2. úrovně max…
Podle mne bys to měl klientovi v první řadě rozmluvit :-) Jestli plánuje, že tam bude ty políčka spravovat sám, dopadne to jako vyhledávání na http://www.pcexpert.cz/hledejp/?…. Myslím, že méně je v tomto případě podstatně více…
hledáme zkušené programátory v PHP / Nette: http://www.twobits.cz/volna-mista/
Osobne bych u Variantu A+ rozdelil tabulku Uloziste_Data na 4 tabulky. Uloziste_Data_Text, Uloziste_Data_Bigint, …
Tak jako tak v aplikaci musis clenit ktery sloupecek vybrat. Tak proc to neclenit na urovni tabulky. Neebude se tak zbytecne zabirat misto u prazdnych hodnot.
ad oddělení do vlastní databáze – to je nesmysl, neuděláš JOIN přes databáze.
V MySQL JOIN mezi databázemi jde použít (SELECT * FROM db1.texty
LEFT JOIN db2.autori ON ...)
Co se týče škálovatelnosti, tak jsem nad tím dumal, ale nakonec mě
vysvobodila odpověď phx.
Tabulka se rozdělí na další podtabulky, čímž vznikne řešení
pojmenované třeba A++.
A já asi mám favorita :)
Editoval petrs (20. 2. 2009 22:24)
Doporučuju to co navrhuje phx, ale A++ už si to asi nepojmenuješ, tomuhle modelu přístupu k DB se říká EAV (entitÿ-attribute-value) a je tu asi od 70. let minulého století :)
EAV, takhle se to teda jmenuje :)
Pár zajímavých odkazů (ať to kdyžtak nemusíte hledat):
A ještě pro zajímavost:
Tedy zůstaly dva možné způsoby řešení zadání, EAV a rozšířující tabulky (ala varianta B). Každý má své klady a zápory, záleží na konkrétní situaci. Kdyby vás ještě něco napadlo, dejte vědět, jinak bych toto téma s dovolením (a poděkováním) uzavřel.
Ještě jednou díky!