Ve svém předchozím článku jsem vysvětlil základy operátorů množin, jejich typy a předpoklady pro jejich použití. Mluvil jsem také o operátorech UNION a UNION ALL, jejich použití a rozdílech.
V tomto článku se naučíme následující:
- Operátory EXCEPT a INTERSECT.
- Rozdíl mezi INTERSECT a INNER JOIN.
- Podrobné vysvětlení INTERSECT a EXCEPT s příkladem.
Operátory EXCEPT a INTERSECT byly zavedeny v SQL Server 2005. Oba jsou množinové operátory používané ke kombinaci sad výsledků generovaných dvěma dotazy a získání požadovaného výstupu.
Co je operátor INTERSECT
INTERSECT se používá k získání záznamů společných pro všechny datové sady načtené z více dotazů nebo tabulek. Zde je vizualizace:
Syntaxe operátoru INTERSECT je následující:
SELECT COLUMN1, COLUMN2, COLUMN3, COLUMN4..FROM TABLE1 INTERSECT SELECT COLUMN1, COLUMN2, COLUMN3, COLUMN4..FROM TABLE2
Co je operátor EXCEPT
EXCEPT se používá k načtení záznamů, které byly nalezeny v jednom dotazu, ale ne v jiném dotazu. Jinými slovy, vrací záznamy, které jsou jedinečné pro jednu sadu výsledků. Takto to vypadá ve vizualizaci:
Syntaxe operátoru EXCEPT je následující:
SELECT COLUMN1, COLUMN2, COLUMN3, COLUMN4..FROM TABLE1 EXCEPT SELECT COLUMN1, COLUMN2, COLUMN3, COLUMN4..FROM TABLE2
Pojďme vytvořit ukázkové nastavení, abychom ukázali, jak lze tyto operátory používat.
Nastavení ukázky
Abych předvedl INTERSECT a EXCEPT, vytvořil jsem dvě tabulky s názvem Zaměstnanec a Stážista .
Provedením následujícího dotazu vytvořte tyto tabulky:
CREATE TABLE [DBO].[EMPLOYEE] ( [NAME] [NVARCHAR](250) NOT NULL, [BUSINESSENTITYID] [INT] NOT NULL, [NATIONALIDNUMBER] [NVARCHAR](15) NOT NULL, [LOGINID] [NVARCHAR](256) NOT NULL, [BIRTHDATE] [DATE] NOT NULL, [MARITALSTATUS] [NCHAR](1) NOT NULL, [GENDER] [NCHAR](1) NOT NULL ) ON [PRIMARY] CREATE TABLE [DBO].[TRAINEE] ( [NAME] [NVARCHAR](250) NOT NULL, [BUSINESSENTITYID] [INT] NOT NULL, [NATIONALIDNUMBER] [NVARCHAR](15) NOT NULL, [BIRTHDATE] [DATE] NOT NULL, [GENDER] [NCHAR](1) NOT NULL ) ON [PRIMARY]
Nyní do položky Zaměstnanec vložíme nějaká fiktivní data tabulky provedením následujícího dotazu:
INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'KEN SÁNCHEZ', 1, N'295847284', N'ADVENTURE-WORKS\KEN0', CAST(N'1969-01-29' AS DATE), N'S', N'M') GO INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'TERRI DUFFY', 2, N'245797967', N'ADVENTURE-WORKS\TERRI0', CAST(N'1971-08-01' AS DATE), N'S', N'F') GO INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'ROBERTO TAMBURELLO', 3, N'509647174', N'ADVENTURE-WORKS\ROBERTO0', CAST(N'1974-11-12' AS DATE), N'M', N'M') GO INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'ROB WALTERS', 4, N'112457891', N'ADVENTURE-WORKS\ROB0', CAST(N'1974-12-23' AS DATE), N'S', N'M') GO INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'GAIL ERICKSON', 5, N'695256908', N'ADVENTURE-WORKS\GAIL0', CAST(N'1952-09-27' AS DATE), N'M', N'F') GO INSERT [DBO].[EMPLOYEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [LOGINID], [BIRTHDATE], [MARITALSTATUS], [GENDER]) VALUES (N'JOSSEF GOLDBERG', 6, N'998320692', N'ADVENTURE-WORKS\JOSSEF0', CAST(N'1959-03-11' AS DATE), N'M', N'M')
Dále uděláme totéž pro Účastníka tabulka:
INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'JOHN WOOD', 18, N'222969461', CAST(N'1978-03-06' AS DATE), N'M') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'MARY DEMPSEY', 19, N'52541318', CAST(N'1978-01-29' AS DATE), N'F') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'WANIDA BENSHOOF', 20, N'323403273', CAST(N'1975-03-17' AS DATE), N'F') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'KEN SÁNCHEZ', 1, N'295847284', CAST(N'1969-01-29' AS DATE), N'M') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'TERRI DUFFY', 2, N'245797967', CAST(N'1971-08-01' AS DATE), N'F') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'ROBERTO TAMBURELLO', 3, N'509647174', CAST(N'1974-11-12' AS DATE), N'M') GO
Nyní pomocí INTERSECT načteme seznam zaměstnanců, kteří jsou společné pro obě tabulky. Chcete-li to provést, spusťte následující dotaz:
SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM EMPLOYEE INTERSECT SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM TRAINEE
Výstup tohoto dotazu by měl být následující:
Jak můžete vidět na obrázku výše, dotaz vrátil pouze záznamy, které jsou společné pro obě tabulky.
INNER JOIN vs. INTERSECT
Ve většině případů INTERSECT a INNER JOIN vrátí stejný výstup, ale existují výjimky. To nám pomůže pochopit jednoduchý příklad.
Pojďme přidat nějaké duplicitní záznamy do tabulky Trainee. Proveďte následující dotaz:
INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'TERRI DUFFY', 2, N'245797967', CAST(N'1971-08-01' AS DATE), N'F') GO INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (N'ROBERTO TAMBURELLO', 3, N'509647174', CAST(N'1974-11-12' AS DATE), N'M') GO
Nyní se pokusíme vygenerovat požadovaný výstup pomocí INTERSECT.
SELECT NAME,BUSINESSENTITYID,NATIONALIDNUMBER,BIRTHDATE,GENDER FROM EMPLOYEE INTERSECT SELECT NAME,BUSINESSENTITYID,NATIONALIDNUMBER,BIRTHDATE,GENDER FROM TRAINEE
Toto je výstup, který dostáváme:
Nyní zkusme použít INNER JOIN.
SELECT A.NAME, A.BUSINESSENTITYID, A.NATIONALIDNUMBER, A.BIRTHDATE, A.GENDER FROM EMPLOYEE A INNER JOIN TRAINEE B ON A.NAME = B.NAME
Výstup, který v tomto případě získáme, je následující:
Nyní, jak můžete vidět na obrázku výše, INNER JOIN načte záznamy, které jsou společné pro obě tabulky. Vyplní všechny záznamy z pravé tabulky. Proto můžete vidět duplicitní záznamy.
Nyní přidejte klíčové slovo DISTINCT do dotazu INNER JOIN a podívejme se, co to udělá:
SELECT DISTINCT A.NAME, A.BUSINESSENTITYID, A.NATIONALIDNUMBER, A.BIRTHDATE, A.GENDER FROM EMPLOYEE A INNER JOIN TRAINEE B ON A.NAME = B.NAME
Výstup by měl vypadat takto:
Jak můžete vidět na snímku obrazovky výše, duplicitní záznamy byly odstraněny.
INTERSECT a INNER JOIN zacházejí s hodnotami NULL odlišně. U INNER JOIN jsou dvě hodnoty NULL různé, takže existuje šance, že je přeskočí při spojování dvou stolů.
Na druhou stranu INTERSECT považuje dvě hodnoty NULL za stejné, takže záznamy, které mají hodnoty NULL, nebudou odstraněny. Abyste tomu lépe porozuměli, podívejme se na příklad.
Nejprve do Tréninku přidejte některé hodnoty NULL a Zaměstnanec tabulky provedením následujícího dotazu:
INSERT [DBO].[TRAINEE] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER], [BIRTHDATE], [GENDER]) VALUES (NULL, 3, N'509647174', CAST(N'1974-11-12' AS DATE), N'M') GO INSERT [DBO].[Employee] ([NAME], [BUSINESSENTITYID], [NATIONALIDNUMBER],[LOGINID], [BIRTHDATE],[MARITALSTATUS], [GENDER]) VALUES (NULL, 3, N'509647174','ADVENTURE-WORKS\TERRI0', CAST(N'1974-11-12' AS DATE), N'M',N'M') GO
Nyní se pokusíme získat záznamy společné pro tyto dvě tabulky pomocí INTERSECT a INNER JOIN. Budete muset provést následující dotaz:
/*QUERY WITH INTERSECT*/ SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM EMPLOYEE INTERSECT SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM TRAINEE /*QUERY WITH INNER JOIN*/ SELECT A.NAME, A.BUSINESSENTITYID, A.NATIONALIDNUMBER, A.BIRTHDATE, A.GENDER FROM EMPLOYEE A INNER JOIN TRAINEE B ON A.NAME = B.NAME
Toto je výstup, který bychom jako výsledek měli dostat:
Jak můžete vidět výše, sada výsledků generovaná pomocí INTERSECT obsahuje hodnoty NULL, zatímco INNER JOIN přeskočil záznamy, které mají hodnoty NULL.
KROMĚ operátora
Abychom předvedli operátor EXCEPT v akci, podívejme se na případ použití. Chci například vyplnit podrobnosti o zaměstnankyních z tabulky Zaměstnanec. K tomu nám pomůže následující dotaz:
SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM EMPLOYEE WHERE GENDER = 'F' EXCEPT SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM EMPLOYEE WHERE GENDER = 'M'
Toto je výstup, který dostáváme:
Jak můžete vidět výše, dotaz vyplnil pouze údaje o zaměstnankyních.
Sadu výsledků můžete také naplnit pomocí dílčího dotazu:
SELECT NAME, BUSINESSENTITYID, NATIONALIDNUMBER, BIRTHDATE, GENDER FROM EMPLOYEE AS M WHERE GENDER = 'F' AND GENDER NOT IN (SELECT GENDER FROM EMPLOYEE AS F WHERE GENDER = 'M')
Omezení INTERSECT a EXCEPT
- Nemůžeme použít EXCEPT a INTERSECT v definicích distribuovaného rozděleného pohledu s klauzulemi COMPUTE a COMPUTE BY.
- EXCEPT a INTERSECT lze použít v rychlých kurzorech pouze vpřed a ve statických kurzorech.
- EXCEPT a INTERSECT lze použít v distribuovaných dotazech, ale lze je spustit pouze na místním serveru. Nemůžete je spustit na vzdáleném serveru.
Shrnutí
V tomto článku jsem se zabýval:
- Operátory EXCEPT a INTERSECT.
- Rozdíl mezi INTERSECT a INNER JOIN.
- Podrobné vysvětlení operátorů INTERSECT a EXCEPT s příkladem.