Oznámení
Stejně pojmenované sloupce ve výsledku dotazu
před 9 lety
- rp
- Člen | 14
Lze ve výsledku SQL dotazu zobrazit, ke které tabulce sloupec patří?
SELECT t1.*, t2.*
FROM t1
LEFT JOIN t2 ON t2.id=t1.id
WHERE t1.id=1
Očekávám výsledek: t1.id=1, t2.id=1
Dibi vrátí id=1
(styl zápisu nemá vliv –
fluent
, dibi::
). O které id
se ale
jedná? Náleží k t1
nebo k t2
? Podle všeho
náleží k t1
. Vypadá to, jakoby se vzal první výskyt
id
a na ostatní to už nebere ohled.
Klasický přístup mysql_fetch_array(mysql_query)
vrátí pole,
jehož prvky sice nejsou tak pěkně pojmenovány (t1.id
…), ale
je v něm následující informace:
0=>"1"
id=>"1"
1=>"1"
(id=>"1" <- to tu právě není, ale předchozí řádek naznačuje, že výsledek obsahuje ještě něco)
Příklad jsem jen nastřelil. V praxi mohou být spojené
(join
) tabulky, ve kterých se vícekrát vyskytne stejné jméno
sloupce (např.: name
může být jméno zákazníka, ale i název
zboží).
Chtěl jsem výsledek použít v Nette šabloně a najednou jsem nevěděl,
jak mám k polím přistoupit. Zkoušel jsem to nejprve naslepo pomocí
{$item->t1['id']}
, pak také {$item->t1->id}
,
ale marně. A tak jsem šel nazpět a skončil vlastně u samotného MySQL,
které vrací výsledek v nedostatečném formátu.
Jak si poradit? Tedy kromě nápadu nepojmenovávat sloupce stejně :)
před 9 lety
- rp
- Člen | 14
Ačkoliv jsem hledal před položením dotazu, našel jsem až po položení :)
Zjistil jsem, že jde o vlastnost php funkce
mysql_fetch_array
:
If two or more columns of the result have the same field names, the last column will take precedence. To access the other
column(s) of the same name, you must use the numeric index of the column or make
an alias for the column. For aliased columns, you cannot access the contents
with the original column name.
více informací
V komentářích k php funkci je navrženo řešení.
Pro dibi je také navrženo řešení. Toho jsem si sice všiml již předtím, ale nelíbilo se mi na tom, že při pokládání dotazu musím myslet na to, jestli data z duplicitních sloupců budu potřebovat (pokud ano, musím vytvořit aliasy, pokud ne, můžu se na to vykašlat – ale předem to třeba nevím).
Ještě poznámka pro méně sběhlé v SQL (jako jsem třeba já). Aliasy lze využívat až ve zpracování výsledků dotazu.
Příklad:
<?php
$this->connection->select('customers.*, carts.*, goods.*')
->select(array('customers.id'=>'cid', 'carts.id'=>'caid', 'goods.id'=>'gid'))
->from('[customers]')
->leftJoin('[carts] on [carts].[customer_id]=[customers].[id]')
->leftJoin('[goods] on [goods].[id]=[carts].[goods_id]')
->where('[customers].[id]=%i AND [goods].[active]=%i', $customer_id, '1');
?>
Nemůžu tedy například v klauzuli WHERE
použít místo
[customers].[id]
nadefinovaný alias cid
. Můžu ho
ale použít následně třeba v Nette šabloně {$item->cid}.
před 9 lety
- rp
- Člen | 14
Ještě poznámka. Adminer se chová tak, že názvy duplicitních sloupců zobrazí stejně. Pokud tedy spojím 3 tabulky a každá má sloupec ID, Adminer vypíše 3 sloupce, každý se záhlavím ID. Zde to ale není až takový problém, protože výsledkem dotazu je většinou víc sloupců a tak jsou mezi sloupci ID i jiné. Tak se dá poznat, ke které tabulce které ID patří.
Zaráží mě ale, že když definuji aliasy v SQL dotazu Admineru, tak ve
výsledku jsou sloupce nazvány svými originálními názvy (nejsou použity
aliasy). Možná by stálo za to, implementovat přepínač, kterým bych mohl
nastavit, zda chci výsledky vidět ve formě id, name, description
nebo table.id, table.name, table.description
.
Ale to jsem asi ve špatném fóru… :)
před 9 lety
- vrana
- Člen | 130
Mě tedy Adminer zobrazuje aliasový název sloupce. Uveď prosím, jakou používáš verzi Admineru, MySQL a jakým driverem jsi připojen (zobrazuje se po přihlášení na stránce Vybrat databázi).
Nově jsem doplnil ještě to, že původní název sloupce a tabulka se zobrazuje (pokud je k dispozici) v bublině záhlaví sloupce.
před 9 lety
- paranoiq
- Člen | 388
@rp: dávat id sloupcům ve všech tabulkách jméno ‚id‘ je
vyloženě hloupost. mimo to, že pak máš v názvech sloupců takovýhle
zmatek, se také ochuzuješ o možnost používat třeba tuhle zjednodušenou
syntax: … JOIN customer USING (customer_id)
před 9 lety
- David Grudl
- Nette Core | 6806
V dibi šlo zavolat nad DibiResult metodu setWithTables(TRUE) a stejně pojmenované sloupce bylo možné odlišit pomocí názvu tabulky, ale to bylo tak blbé řešení, že jsem to z poslední verze odstranil. Doporučuju proto použít aliasy nebo jiné pojmenování sloupců. Nebo SQLite ;)
před 9 lety
- rp
- Člen | 14
vrana napsal(a):
Mě tedy Adminer zobrazuje aliasový název sloupce. Uveď prosím, jakou používáš verzi Admineru, MySQL a jakým driverem jsi připojen (zobrazuje se po přihlášení na stránce Vybrat databázi).
Nově jsem doplnil ještě to, že původní název sloupce a tabulka se zobrazuje (pokud je k dispozici) v bublině záhlaví sloupce.
Omlouvám se ti. Je pravda, že Adminer zobrazuje aliasový název sloupce. Nevšiml jsem si toho, protože sloupec byl hodně vpravo (mimo monitor:). Adminer vypisuje výsledné sloupce správně podle požadavku. Pokud alias uvedu až nakonec (což většinou dělám), tak je aliasový sloupec na konci. Typicky:
SELECT *, customers.id as cuid
FROM ...
Vypíše sloupec s aliasem cuid
nakonec. Moje tabulka má
hodně sloupců a výsledek jsem tedy neviděl celý.
Píšeš o bublině v záhlaví sloupce. Předpokládám, že myslíš zobrazení bubliny (tooltip) při umístění kurzoru na název sloupce výsledku. Používám Adminer 2.3.0, MySQL 5.1.32-community-log a zmíněné chování nepozoruji. Co mi ale funguje je zobrazení bubliny při umístění kurzoru nad záhlavím sloupce vypsané tabulky (příkaz „vypsat“). Zobrazí se typ dat uložených ve sloupci.
Co ale nevidím vůbec je možnost zobrazit název sloupce ve formátu
table.column
. Alespoň u sloupců s duplicitními názvy.
Z dalších komentářů (paranoiq, David Grudl) ale usuzuji, že nejspíš
kladu špatný požadavek.
před 9 lety
- rp
- Člen | 14
paranoiq napsal(a):
@rp: dávat id sloupcům ve všech tabulkách jméno ‚id‘ je vyloženě hloupost. mimo to, že pak máš v názvech sloupců takovýhle zmatek, se také ochuzuješ o možnost používat třeba tuhle zjednodušenou syntax:
… JOIN customer USING (customer_id)
Děkuji za příjemné dovzdělání. Snažil jsem se něco si z toho vzít a porovnat to s mými dosavadními zkušenostmi.
Pokud se podíváš na schéma databáze účetního systému POHODA (což můžeš udělat
poměrně snadno), tak zjistíš, že obsahuje 110 tabulek (verze leden
2010). Z nich celkem
110 používá jako název pro id sloupec ID
. Sloupec
s cizím klíčem k propojené tabulce se používá název
Ref[název_propojené_tabulky]
.
Snažil jsem se prostudovat další zdroje a vyplývá mi z toho, že při volbě
primárního klíče je nejlepší sledovat nějaké jednotné pravidlo. Což třeba u MS Access je automaticky
číslované pole ID
.
Dalo by se namítnout, že takovým jednotným pravidlem může být
vytváření primárních klíčů ve tvaru tablename_id
nebo
id_tablename
s efektivním využitím klauzule USING
.
Proti tomu mám vlastně „jen“ principiální námitku. Uvádím znovu informaci, která je již přítomna.
Název sloupce v plném tvaru by pak byl například
customers.customers_id
(když vynechám název databáze). Pokud je
něco přítomno, měl bych to použít a ne to znovu uvádět.
Přirovnal bych to k tomu, že na svém disku nemohu mít dva soubory
stejného jména. I když je umístím do různých adresářů. Anebo si to
lze představit jako nemožnost uvést dva stejnojmenné tagy v XML souboru.
Což samozřejmě jde díky jmenným prostorům. Soubor na disku odkážu
jednoduše uvedením absolutní cesty a tag v XML zase jinak. Naproti tomu v php pohořím, protože budu muset definovat alias pro
každý potenciálně duplicitní sloupec. A budu na to muset setsakra
pamatovat, protože jinak se místo jména výrobku vypíše jméno zákazníka,
který si ho objednal (nejde jen o sloupce ID
, to byl ostatně jen
příklad). Je to potenciální zdroj chyb. U třech tabulek duplicitní názvy
uhlídám, ale dokážu to třeba u 20 nebo 50?
Ideální chování si představuji tak, že na sloupec mohu odkázat ve
tvaru column
nebo table.column
. Podle libosti.
Ještě to budu muset řádně prozkoumat a podívat se, jak to řeší
jiní. Z toho co zatím vidím v jiných databázích je ale zřejmé, že
odkazování sloupce pomocí table.column
je zcela běžné.
před 9 lety
- rp
- Člen | 14
David Grudl napsal(a):
V dibi šlo zavolat nad DibiResult metodu setWithTables(TRUE) a stejně pojmenované sloupce bylo možné odlišit pomocí názvu tabulky, ale to bylo tak blbé řešení, že jsem to z poslední verze odstranil. Doporučuju proto použít aliasy nebo jiné pojmenování sloupců. Nebo SQLite ;)
Děkuji za reakci. Nechápu dvě věci:
- Blbé řešení to bylo proto, že se ti to zdá neužitečné nebo to nebylo dobře naprogramováno?
- Narážku na SQLite jsem se snažil prostudovat, ale v dokumentaci jsem
nenašel žádnou zmínku o problému s duplicitním pojmenováním sloupců.
Znamená to, že SQLite dovoluje odkazovat sloupce ve tvaru
table.column
? Pokud ano, jak potom takový název sloupce uvedu v Nette šabloně (viz. můj původní dotaz)? Ber to obecně – nemyslím, že bych v mém případě mohl nahradit MySQL za SQLite, ale třeba se mi podaří, pomocí řešení naznačeného v předchozím příspěvku, získávat jméno sloupce ve tvarutable.column
.
před 9 lety
- vrana
- Člen | 130
Používám Adminer 2.3.0, MySQL 5.1.32-community-log a zmíněné chování nepozoruji.
Když napíšu „nově jsem doplnil“, tak to znamená v repozitáři.
před 9 lety
- rp
- Člen | 14
vrana napsal(a):
Když napíšu „nově jsem doplnil“, tak to znamená v repozitáři.
Díky za nasměrování. Jenom doplním, že na Adminer jsem si zvykl strašně rychle. Je to skvělý pomocník. Bezvadný nápad je například uchování historie položených dotazů.
Editoval rp (14. 3. 2010 11:09)