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:

  1. Vyberte DISTINCT Hodnoty z tabulky a pořadí
  2. 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.

Výsledky seřazené podle příjmení

Nyní spusťte

SELECT DISTINCT FirstName,
LastName
FROM Person.Person
ORDER BY LastName

A uvidíte, že „Kim Ambercombine“ se zobrazí pouze jednou.

Seznam unikátů seřazený podle LastName

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.

Prvních 10 unikátních řádků seřazených podle LastName

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.

Plán dotazu zobrazující pořadí provádění

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.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.