tiny ‘n’ smart
database layer

Odkazy: dibi | API reference

Oznámení

Omlouváme se, provoz fóra byl ukončen

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:

  1. Blbé řešení to bylo proto, že se ti to zdá neužitečné nebo to nebylo dobře naprogramováno?
  2. 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 tvaru table.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)