SQL DISTINCT a TOP ve stejném dotazu
Tento článek je inspirován sérií otázek, které mi nedávno zaslala jedna z mých čtenářek, Nan, ohledně DISTINCT, TOP a ORDER BY.
Všechny příklady v této lekci jsou založeny na Microsoft SQL Server Management Studio a databázi AdventureWorks2012. S těmito bezplatnými nástroji můžete začít pracovat pomocí mé příručky Začínáme používat SQL Server.
Jak spolupracují modifikátory SQL Top a Distinct SELECT při tvorbě výsledků?
Tady je otázka, kterou mi původně poslala Nan:
Mám trochu zmatek ohledně SELECT DISTINCT a SELECT. Například,
SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName
Je to hledání různých křestních jmen? Odlišná kombinovaná jména a příjmení? Jak rozlišujeme mezi sloupci použitými pro distinktivní vyhodnocení a sloupci, které chceme pouze zobrazit ve výstupu?“
Co třeba
Select Distinct TOP 10 LastName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY LastName
Myslel jsem si, že by všichni rádi znali odpověď, a tak vytvářím příspěvek na blogu.
DISTINCT a TOP – co je dřív?“
Podívejme se na první příkaz, jehož účelem je vrátit unikátní seznam pětic a příjmení.
SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName;
TOP 10 vrátí prvních deset položek z uspořádané množiny a DISTINCT odstraní případné duplicity. Otázka zní, co se stane jako první:
- Je tabulka seřazena podle Příjmení, vybráno prvních deset položek a poté odstraněny duplicity?
- Nebo jsou odstraněny duplicity a poté položky seřazeny a zobrazeno prvních deset položek?
Než odpovíme na tuto otázku, mějte na paměti, že DISTINCT pracuje se všemi sloupci a výrazy v klauzuli SELECT. V tomto případě tedy příkaz vrátí odlišné řádky pro FirstName a LastName.
Naneštěstí neexistuje přímý způsob, jak použít DISTINCT na jednu sadu polí a zobrazit ostatní. Jakmile do příkazu SELECT přidáte sloupce, stanou se pod vlivem operátoru DISTINCT. Říkám přímo, protože byste mohli získat odlišný seznam a pak použít INNER JOIN pro vtažení dalších sloupců. To má však svá rizika, protože spojením se mohou znovu objevit duplicity.
Zajímavé je přidání klauzule TOP k operátoru DISTINCT. Nebyl jsem si jistý, co se stane, ale trochu jsem experimentoval s databází AdventureWorks a zjistil jsem, že pořadí zpracování probíhá nějak takto:
- Vyberte DISTINCT Hodnoty z tabulky a pořadí
- Vyberte TOP x řádků z výsledků v kroku 1 a zobrazte.
Pokud to chcete zkusit sami, začněte s
SELECT FirstName,
LastName
FROM Person.Person
ORDER BY LastName
A všimněte si výsledků. Sledujte „Kim Ambercombie“. Všimněte si, že pro její jméno existují tři záznamy.
![](https://www.essentialsql.com/wp-content/uploads/2015/05/OrderedResult.png)
Nyní spusťte
SELECT DISTINCT FirstName,
LastName
FROM Person.Person
ORDER BY LastName
A uvidíte, že „Kim Ambercombine“ se zobrazí pouze jednou.
![](https://www.essentialsql.com/wp-content/uploads/2015/05/DistinctOrderedResults.png)
Teď spusťte
SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName
A uvidíte, že to vrátí prvních 10 unikátních jmen a příjmení seřazených podle LastName.
![](https://www.essentialsql.com/wp-content/uploads/2015/05/OrderedResultDistinct10.png)
Pokud vás zajímá, co se stane dříve, zda operace DISTINCT nebo TOP 10, pak porovnejte výsledky posledních dvou dotazů.
Všimněte si, že dotaz „DISTINCT TOP 10“ zahrnuje prvních 10 řádků z dotazu z dotazu „DISTINCT“.
Z toho víme, že se nejprve vytvoří seznam DISTINCT a poté se vrátí TOP 10 položek.
![](https://www.essentialsql.com/wp-content/uploads/2015/05/QueryPlan.png)
To můžete potvrdit i zobrazením plánu dotazu. Chcete-li tak učinit, vyberte před provedením dotazu z nabídky možnost Query -> Include Actual Query Plan.
Ikona „Stream Aggregate“ je určena pro operaci DISTINCT a „Top“ pro operaci TOP 10.
Může se zdát poněkud neintuitivní, že v rámci příkazu SELECT je jako první uveden DISTINCT. Jen mějte na paměti, že SQL není nutně zpracováváno v pořadí, v jakém by ho četl člověk zleva doprava.
DISTINCT a TOP s výrazy seznamu SELECT
Druhá část Naniny otázky se týkala toho, jak se zachází s výrazy s operátorem DISTINCT.
Výrazy jsou ohledně DISTINCT a TOP zpracovávány stejně jako sloupce. Začněme příkazem select pro získání křestního jména i celého, které vytvoříme přičtením LastName ke FirstName.
Při použití ORDER BY mějte také na paměti, že při použití operátoru Distinct se musí v seznamu select objevit položky ORDER BY. Vzhledem k tomu musím upravit příkaz uvedený v původním dotazu:
SELECT DISTINCT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY LastName
Neběží, protože LastName není v seznamu SELECT. Ano, je součástí výrazu v seznamu select, ale samo o sobě tam není. Platí uspořádání podle FullName.
Toto uspořádání použijeme v následujících příkladech.
Příkaz
SELECT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName
Vrátí 19972 řádků. Když přidáme Distinct
SELECT DISTINCT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName
Pak se vrátí 19516 řádků. Nakonec přidáme Top 10, vrátí se prvních 10 odlišných kombinací jmen.
SELECT DISTINCT TOP 10 FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName
Zkuste tyto dotazy spustit na databázi AdventureWork a sami uvidíte, že chování je stejné, jako když pracujeme výhradně se sloupci.