Diskuse vs. SQL - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:



PHP

Diskuse vs. SQL

8. srpna 2002, 00.00 | Podívejme se na již otřepané téma diskusního fóra, tentokráte z jiného pohledu. Oním pohledem bude SQL se zaměřením se
na množství příkazů na databázový stroj.

Dnešním tématem bych se rád věnoval problematice Diskusního fóra. Jistě mnozí z vás okamžitě namítnou, že o tomto tématu bylo již napsáno spousty článků na mnoha serverech. Proto bych vás rád upokojil tím, že se zde nebudu zabývat kompletním řešením diskusního fóra natož kompletními zdroji1.

Mým cílem je zobrazit diskusní fórum z pohledu SQL, bez kterého se asi žádné fórum neobejde. Především se chci zaměřit na minimalizaci počtu příkazů nutných pro vypsání stromu odpovědí diskuse.

Na webu jsem se často setkal s diskusními fóry, založeným na přístupu, že každý příspěvek obsahuje odkaz na předcházející příspěvek. To umožňuje vytvářet téměř neomezenou stromovou strukturu, ale váže to k sobě i jeden problém. Jakým způsobem ho vypsat? Většina těchto fór používá pro vypsání funkci, která se postupně volá s jednotlivými odkazy a tím vzniká strom.

Tento způsob však nepovažuji do dostatečně účinný. Protože vypsání ne moc složitého stromu, může dosáhnout i 50 příkazů na databázový stroj. Určitě by tento problém šel řešit použitím nějakého netriviálního příkazu z SQL, v takovém případě už sice nebudete muset volat příkaz SQL tak často, ale doba odezvy se dosti prodlouží. V podstatě to ani není řešením problému, pouze ho přesouváte na databázový stroj.

Moje řešení je vcelku prosté a spočívá v pouhém abecedním seřazení. Výpis celého stromu příspěvků se realizuje pouhým jedním SQL příkazem, který, jak jsem již řekl, obsahuje pouze ORDER BY, případně ještě LIMIT. Takovýto příkaz samozřejmě je mnohem více náročný než příkaz pro vypsání jednoho příspěvku, ale vsadil bych se, že celkový čas všech jednotlivých příkazů bude delší než na tento jeden.

Nesouhlasíte? Dobře děláte. Logicky dokážete odvodit, že pro rozsáhlou databázi bude ORDER BY trvat déle. Bohužel tomu, tak nebude. V tip je v tom, že moje řešení přináší i jednu nevýhodu, která je současně i jistou výhodou.

Mluvil-li jsem o tom, že se výpis provádí přes ORDER BY dokážete odvodit, že se bude abecedně řadit nějaký sloupec ID. Aby pak bylo řazení funkční ID bude nabývat různých textových hodnot různé délky. Datový typ tohoto sloupce nakonec způsobí to, že strom bude moci existovat pouze do určité hloubky, stejně jako počet hlavních příspěvků bude omezen. To byla nevýhoda. Výhoda spočívá tom, že při správném nastavení hloubky stromu a množství hlavních příspěvků se o celé fórum nemusíte starat, staré příspěvky, které by stejně někdo nečetl se budou při zadávání nových postupně mazat.

Praktické řešení

Už dost teorie! Nyní se budu zabývat přímo tím, jak to napsat.

Vytvoříme si tabulku:

CREATE TABLE phorum_pa (
     id_key varchar(255) NOT NULL default '',
     id varchar(255) NOT NULL default '',
     date int(10) NOT NULL default '0',
     subject varchar(255) NOT NULL default '',
     text text NOT NULL,
     nickname varchar(50) NOT NULL default '',
     email varchar(255) default NULL,
     PRIMARY KEY (id),
     KEY id_key (id_key,date),
     FULLTEXT KEY subject (subject,text,nickname)
) TYPE=MyISAM;

Každý si jistě všimnul sloupců id a id_key. Co je jejich obsahem? Musím připomenout, že lze nastavovat hloubka a šířka databáze2. Základně používám délku 2, ta mi umožňuje vložit 100 hlavních příspěvků3 (00 až 99) a hloubku 127 úrovní (255 / 2). Stejně dobře bych mohl používat délku 3, kde bych získal až 1000 hlavních příspěvků s maximální hloubkou 85 úrovní.4

Co ale obsahují tyto dva sloupce doopravdy? Nejdříve se podíváme na sloupec id. Jeho obsah je jednoduchý obsahuje vždy číslo příspěvku5, které se postupně zvětšuje. Za tímto číslem může následovat další úroveň, která se zapisuje stejně. Těchto úrovní zde může být uvedeno, tolik až do maximální hloubky.

Zatímco sloupec id_key obsahuje pouze číslo příspěvku v první úrovni tj. první dva znaky ze sloupce id. Je to taková malá vzpoura proti, pravidlům databází :-).

Asi to není tolik jasné i když je to strašně jednoduché viz. příklad:

id_key

id

Můj popis

00 00 první hlavní příspěvek
00 0000 první odpověď na první hlavní příspěvek
00 0001 druhá odpověď na první hlavní příspěvek
00 0002 třetí odpověď na první hlavní příspěvek
00 000200 první odpověď na třetí odpověď na první hlavní příspěvek
01 01 druhý hlavní příspěvek
02 02 třetí hlavní příspěvek
02 0200 první odpověď na třetí hlavní příspěvek

Neobjasnil jsem, proč vlastně jsem do tabulky zahrnul sloupec id_key, když je obsažen v id. Je to prosté. Většina diskusí co znám řadí hlavní příspěvky od nejnovějších, zatímco při zobrazení odpovědí se provádí výpis od nejstarších, abyste postupovali od známého k neznámému. To vyžaduje dvojité seřazení descendentální podle id_key a současně ascendentální podle id6.

Obyčejný SELECT může vypadat např. takto:

SELECT * FROM `phorum_pa` ORDER BY `id_key` DESC, `id` ASC LIMIT ...;

Při výpisu poznáte aktuální hloubku aktuálního příspěvku podle délky jeho id. Díky tomu, že je máte seřazené stačí otevírat či zavírat např. <blockquote> pouze podle toho, zda předtím byla úroveň nižší či vyšší. Narazíte však, ještě najeden problémeček. Při používání LIMIT určujete kolik se má vypsat příspěvků v jakékoliv úrovni, nemůžete se tedy divit, že se poslední strom příspěvku může pokračovat na další stránce. Osobně si však myslím, že to není tak velký problém.

Fórum není těžké předělat do formy jako je např. tady na Builderu tj. v podobě, kdy se nejdříve zobrazí jen hlavní příspěvky a teprve po kliknutí se zobrazí daný strom. Mohlo by to být přes SELECTy:

SELECT * FROM `phorum_pa` WHERE LENGTH(id) = 2 ORDER BY `id` DESC; // výpis hlavních příspěvků

SELECT * FROM `phorum_pa` WHERE id LIKE '[id_key příspěvku]%' ORDER BY `id` ASC; // výpis stromu jednoho hlavního příspěvku

Sledování nových příspěvků

K pokročilejší funkci fóra je sledování nových odpovědí. Stačí založit novou tabulku:

CREATE TABLE watch_pa (
     id int(10) NOT NULL auto_increment,
     id_key varchar(255) default NULL,
     email varchar(255) NOT NULL default '',
     PRIMARY KEY (id),
     KEY id_key (id_key),
     FULLTEXT KEY id_key_2 (id_key),
     KEY email (email)
) TYPE=MyISAM;

A můžeme sledovat hned několika způsoby:

  • sledování všech nových příspěvků ve všech diskusích
     INSERT INTO `watch_pa` VALUES ('', '%', '$email');
  • sledování všech nových příspěvků ve vybrané diskusi
     INSERT INTO `watch_pa` VALUES ('', '[id_key]%', '$email');
  • sledování všech nových příspěvků pouze ve vybrané větvi stromu
     INSERT INTO `watch_pa` VALUES ('', '[id]%', '$email');

Po zařazení nového příspěvku stačí projít tabulku watch následujícím příkazem a získáme seznam e-mailů, na které má být odesláno oznámení.

     SELECT DISTINCT(email), id FROM `watch_pa` WHERE [new_id] LIKE id_key;

A to je asi vše. S případnými dotazy se na mě obracejte přes diskusní fórum tohoto článku.

V experimentální formě toto fórum běží na pomalém serveru http://svecpetr.crolink.cz/police/phorum_pa/. Do konce srpna roku 2002 si s ním můžete dělat co chcete. Po tomto datu, prosím, do něj nevkládejte nesmyslné příspěvky, protože bude součástí komerčního webu.


1. – Přestože kompletní zdrojové kódy vlastním.
2. – Ta navíc nemusí být symetrická jako je v následujících příkladech.
3. – Každý příspěvek může mít opět 100 odpovědí.
4. – Šířka a hloubka lze zvýšit tím, že se nebudou používat pouze čísla 0-9, ale přidají se i písmena a-z. Případně zvětšením velikosti sloupce id a id_key.
5. – uložené ve formátu např. 00 až 99.
6. – id obsahuje id_key z důvodu aby bylo unikátní a mohlo tvořit primární klíč.

Tématické zařazení:

 » Rubriky  » PHP  

 » Rubriky  » Web  

 » Rubriky  » ASP  

 » Rubriky  » HTML  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: