Förra månaden gav jag en bakgrund till tabelluttryck i T-SQL. Jag förklarade sammanhanget från relationsteorin och SQL-standarden. Jag förklarade hur en tabell i SQL är ett försök att representera en relation från relationsteori. Jag förklarade också att ett relationsuttryck är ett uttryck som verkar på en eller flera relationer som input och resulterar i en relation. På liknande sätt, i SQL, är ett tabelluttryck ett uttryck som verkar på en eller flera inmatningstabeller och resulterar i en tabell. Uttrycket kan vara en fråga, men behöver inte vara det. Till exempel kan uttrycket vara en tabellvärdekonstruktor, som jag kommer att förklara senare i den här artikeln. Jag förklarade också att jag i den här serien fokuserar på fyra specifika typer av namngivna tabelluttryck som T-SQL stöder:härledda tabeller, vanliga tabelluttryck (CTE), vyer och inline-tabellvärderade funktioner (TVF).
Om du har arbetat med T-SQL ett tag har du förmodligen snubblat in i en hel del fall där du antingen var tvungen att använda tabelluttryck, eller så var det på något sätt bekvämare jämfört med alternativa lösningar som inte använder dem. Här är bara några exempel på användningsfall som kommer att tänka på:
- Skapa en modulär lösning genom att dela upp komplexa uppgifter i steg som vart och ett representeras av olika tabelluttryck.
- Blanda resultat av grupperade frågor och detaljer om du bestämmer dig för att inte använda fönsterfunktioner för detta ändamål.
- Logisk frågebehandling hanterar frågesatser i följande ordning:FROM>WHERE>GROUP BY>HAVING>SELECT>ORDER BY. Som ett resultat, på samma nivå av kapsling, är kolumnalias som du definierar i SELECT-satsen endast tillgängliga för ORDER BY-satsen. De är inte tillgängliga för resten av frågeklausulerna. Med tabelluttryck kan du återanvända alias som du definierar i en inre fråga i valfri sats i den yttre frågan, och på så sätt undvika upprepning av långa/komplexa uttryck.
- Fönsterfunktioner kan endast visas i en frågas SELECT- och ORDER BY-satser. Med tabelluttryck kan du tilldela ett alias till ett uttryck baserat på en fönsterfunktion och sedan använda det aliaset i en fråga mot tabelluttrycket.
- En PIVOT-operatör innefattar tre element:gruppering, spridning och aggregering. Denna operatör identifierar grupperingselementet implicit genom eliminering. Med hjälp av ett tabelluttryck kan du projicera exakt de tre element som ska vara inblandade och låta den yttre frågan använda tabelluttrycket som PIVOT-operatorns inmatningstabell och på så sätt styra vilket element som är grupperingselementet.
- Ändringar med TOP stöder inte en ORDER BY-sats. Du kan styra vilka rader som väljs indirekt genom att definiera ett tabelluttryck baserat på en SELECT-fråga med TOP- eller OFFSET-FETCH-filtret och en ORDER BY-sats, och tillämpa modifieringen mot tabelluttrycket.
Detta är långt ifrån en uttömmande lista. Jag kommer att visa några av ovanstående användningsfall och andra i den här serien. Jag ville bara nämna några användningsfall här för att illustrera hur viktiga tabelluttryck är i vår T-SQL-kod, och varför det är värt att investera i att förstå deras grunder väl.
I denna månads artikel fokuserar jag på den logiska behandlingen av härledda tabeller specifikt.
I mina exempel kommer jag att använda en exempeldatabas som heter TSQLV5. Du kan hitta skriptet som skapar och fyller det här, och dess ER-diagram här.
Härledda tabeller
Termen härledd tabell används i SQL och T-SQL med mer än en betydelse. Så först vill jag göra det klart vilken jag syftar på i den här artikeln. Jag syftar på en specifik språkkonstruktion som du definierar typiskt, men inte bara, i FROM-satsen i en yttre fråga. Jag kommer att tillhandahålla syntaxen för denna konstruktion inom kort.
Den mer allmänna användningen av termen härledd tabell i SQL är motsvarigheten till en härledd relation från relationsteori. En härledd relation är en resultatrelation som härleds från en eller flera indatabasrelationer, genom att tillämpa relationsoperatorer från relationalgebra som projektion, skärningspunkt och andra på dessa basrelationer. På liknande sätt, i allmän mening, är en härledd tabell i SQL en resultattabell som härleds från en eller flera bastabeller, genom att utvärdera uttryck mot dessa indatabastabeller.
För övrigt kollade jag hur SQL-standarden definierar en bastabell och blev direkt ledsen att jag störde mig.
4.15.2 BastabellerEn bastabell är antingen en beständig bastabell eller en tillfällig tabell.
En beständig bastabell är antingen en vanlig beständig bastabell eller en systemversionstabell.
En vanlig bastabell är antingen en vanlig beständig bastabell eller en temporär tabell.”
Tillagt här utan ytterligare kommentarer...
I T-SQL kan du skapa en bastabell med en CREATE TABLE-sats, men det finns andra alternativ, t.ex. SELECT INTO och DECLARE @T AS TABLE.
Här är standardens definition för härledda tabeller i allmän mening:
4.15.3 Härledda tabeller
En härledd tabell är en tabell som härleds direkt eller indirekt från en eller flera andra tabeller genom utvärderingen av ett uttryck, till exempel en
Det finns ett par intressanta saker att notera här om härledda tabeller i allmän mening. Man har att göra med kommentaren om beställning. Jag kommer till detta senare i artikeln. En annan är att en härledd tabell i SQL kan vara ett giltigt fristående tabelluttryck, men det behöver inte vara det. Till exempel representerar följande uttryck en härledd tabell och är anses också vara ett giltigt fristående tabelluttryck (du kan köra det):
Omvänt representerar följande uttryck en härledd tabell, men är det inte ett giltigt fristående tabelluttryck:
T-SQL stöder ett antal tabelloperatorer som ger en härledd tabell, men som inte stöds som fristående uttryck. Dessa är:JOIN, PIVOT, UNPIVOT och APPLY. Du behöver en klausul för att de ska kunna arbeta inom (vanligtvis FROM, men också MERGE-satsens USING-sats), och en värdfråga.
Från och med nu kommer jag att använda termen härledd tabell för att beskriva en mer specifik språkkonstruktion och inte i den allmänna betydelsen som beskrivs ovan.
En härledd tabell kan definieras som en del av en yttre SELECT-sats i dess FROM-sats. Det kan också definieras som en del av DELETE- och UPDATE-satser i deras FROM-sats, och som en del av en MERGE-sats i dess USING-sats. Jag kommer att ge mer information om syntaxen när den används i modifieringssatser senare i den här artikeln.
Här är syntaxen för en förenklad SELECT-fråga mot en härledd tabell:
Den härledda tabelldefinitionen visas där en bastabell normalt kan förekomma, i den yttre frågans FROM-sats. Det kan vara en input till en tabelloperator som JOIN, APPLY, PIVOT och UNPIVOT. När den används som rätt indata till en APPLY-operator, tillåts
Den yttre satsen kan ha alla vanliga frågeelement. I ett SELECT-satsfall:WHERE, GROUP BY, HAVING, ORDER BY och som nämnts, tabelloperatorer i FROM-satsen.
Här är ett exempel på en enkel fråga mot en härledd tabell som representerar kunder i USA:
Den här frågan genererar följande utdata:
Det finns tre huvuddelar att identifiera i ett uttalande som involverar en härledd tabelldefinition:
Tabelluttrycket är tänkt att representera en tabell och måste som sådant uppfylla vissa krav som en normal fråga inte nödvändigtvis behöver uppfylla. Jag kommer att ge detaljerna inom kort i avsnittet "Ett tabelluttryck är en tabell".
När det gäller det målhärledda tabellnamnet; ett vanligt antagande bland T-SQL-utvecklare är att det bara är ett namn eller alias som du tilldelar måltabellen. På samma sätt, överväg följande fråga:
Även här är det vanliga antagandet att AS C bara är ett sätt att byta namn på, eller alias, tabellen Kunder för den här frågan, med början med det logiska frågebearbetningssteget där namnet tilldelas och framåt. Men från relationsteorins synvinkel finns det en djupare mening med vad C representerar. C är vad som kallas en intervallvariabel. C är en härledd relationsvariabel som sträcker sig över tuplarna i indatarelationsvariabeln Kunder. I exemplet ovan sträcker sig C över tuplarna i Kunder och utvärderar predikatlandet =N'USA'. Tupler för vilka predikatet utvärderas till sant blir en del av resultatrelationen C.
Med den bakgrund som jag har gett hittills borde det jag ska förklara härnäst inte vara någon överraskning.
Låt oss bryta ner dessa krav en efter en och diskutera relevansen för både relationsteori och SQL.
Kom ihåg att en relation har en rubrik och en kropp. Rubriken för en relation är en uppsättning attribut (kolumner i SQL). Ett attribut har ett namn och ett typnamn och identifieras med sitt namn. En fråga som inte används som ett tabelluttryck behöver inte nödvändigtvis tilldela namn till alla målkolumner. Betrakta följande fråga som ett exempel:
Den här frågan genererar följande utdata:
Frågeutgången har en anonym kolumn som är resultatet av sammanlänkning av platsattributen med hjälp av CONCAT_WS-funktionen. (Förresten, den här funktionen lades till i SQL Server 2017, så om du kör koden i en tidigare version, ersätt gärna den här beräkningen med en alternativ beräkning som du väljer.) Den här frågan gör det därför inte returnera en tabell, för att inte tala om en relation. Därför är det inte giltigt att använda en sådan fråga som tabelluttrycket/inre frågedelen av en härledd tabelldefinition.
Prova det:
Du får följande felmeddelande:
Lägger du märke till något intressant med felmeddelandet? Den klagar på kolumn 4 och belyser skillnaden mellan kolumner i SQL och attribut i relationsteori.
Lösningen är naturligtvis att se till att du uttryckligen tilldelar namn till kolumner som är resultatet av beräkningar. T-SQL stöder en hel del kolumnnamnstekniker. Jag ska nämna två av dem.
Du kan använda en inline-namngivningsteknik där du tilldelar målkolumnnamnet efter beräkningen och en valfri AS-sats, som i
Den här frågan genererar följande utdata:
Med den här tekniken är det mycket enkelt när man granskar koden för att se vilket målkolumnnamn som är tilldelat vilket uttryck. Dessutom behöver du bara namnge kolumner som inte redan har namn annars.
Du kan också använda en mer extern kolumnnamngivningsteknik där du anger målkolumnnamnen inom parentes direkt efter det härledda tabellnamnet, som så:
Med den här tekniken måste du dock lista namn för alla kolumner – inklusive de som redan har namn. Tilldelningen av målkolumnnamnen görs efter position, från vänster till höger, dvs. det första målkolumnnamnet representerar det första uttrycket i den inre frågans SELECT-lista; det andra målkolumnnamnet representerar det andra uttrycket; och så vidare.
Observera att i händelse av inkonsekvens mellan de inre och yttre kolumnnamnen, t.ex. på grund av en bugg i koden, är omfattningen av de inre namnen den inre frågan – eller, mer exakt, den inre intervallvariabeln (här implicit HR.Employees AS-anställda) – och omfattningen av de yttre namnen är den yttre intervallvariabeln (D i vårt fall). Det är lite mer involverat i omfattningen av kolumnnamn som har att göra med logisk frågebehandling, men det är ett föremål för senare diskussioner.
Potentialen för buggar med den externa namnsyntaxen förklaras bäst med ett exempel.
Undersök resultatet av den föregående frågan, med hela uppsättningen anställda från tabellen HR.Employees. Tänk sedan på följande fråga och försök ta reda på vilka anställda du förväntar dig att se i resultatet innan du kör den:
Om du förväntar dig att frågan ska returnera en tom uppsättning för givna exempeldata, eftersom det för närvarande inte finns några anställda med både ett efternamn och ett förnamn som börjar med bokstaven D, saknar du felet i koden.
Kör nu frågan och undersök den faktiska utdata:
Vad hände?
Den inre frågan anger förnamn som den andra kolumnen och efternamn som den tredje kolumnen i SELECT-listan. Koden som tilldelar den härledda tabellens målkolumnnamn i den yttre frågan anger efternamn andra och förnamn tredje. Kodnamnen förnamn som efternamn och efternamn som förnamn i intervallvariabeln D. I praktiken filtrerar du bara anställda vars efternamn börjar med bokstaven D. Du filtrerar inte anställda med både ett efternamn och ett förnamn som börjar med bokstaven D.
Syntaxen för inline alias är inte benägen för sådana buggar. För det första kallar du normalt inte en kolumn som redan har ett namn du är nöjd med. För det andra, även om du vill tilldela ett annat alias för en kolumn som redan har ett namn, är det inte särskilt troligt att du med syntaxen
Uppenbarligen inte särskilt troligt.
Tillbaka till det faktum att rubriken för en relation är en uppsättning attribut, och givet att ett attribut identifieras med namn, måste attributnamn vara unika för samma relation. I en given fråga kan du alltid referera till ett attribut med ett tvådelat namn med intervallvariabelns namn som kvalificerare, som i
Betrakta följande fristående fråga som ett exempel:
Den här frågan misslyckas inte med ett duplicerat kolumnnamnsfel eftersom en custid-kolumn faktiskt heter C.custid och den andra O.custid inom den aktuella frågans räckvidd. Den här frågan genererar följande utdata:
Försök dock att använda den här frågan som ett tabelluttryck i definitionen av en härledd tabell med namnet CO, så här:
När det gäller den yttre frågan har du en intervallvariabel som heter CO, och omfattningen av alla kolumnnamn i den yttre frågan är den intervallvariabeln. Namnen på alla kolumner i en given intervallvariabel (kom ihåg att en intervallvariabel är en relationsvariabel) måste vara unika. Därför får du följande felmeddelande:
Fixningen är naturligtvis att tilldela olika kolumnnamn till de två custid-kolumnerna när det gäller intervallvariabeln CO, som så:
Den här frågan genererar följande utdata:
Om du följer god praxis listar du uttryckligen kolumnnamnen i den yttersta frågans SELECT-lista. Eftersom det bara finns en intervallvariabel inblandad, behöver du inte använda det tvådelade namnet för de yttre kolumnreferenserna. Om du vill använda det tvådelade namnet, prefixer du kolumnnamnen med det yttre intervallvariabelnamnet CO, så här:
Det finns ganska mycket jag har att säga om namngivna tabelluttryck och ordning – tillräckligt för en artikel i sig – så jag kommer att ägna en framtida artikel till detta ämne. Ändå ville jag beröra ämnet kort här eftersom det är så viktigt. Kom ihåg att kroppen av en relation är en uppsättning tupler, och på samma sätt är kroppen i en tabell en uppsättning rader. Ett set har ingen ordning. Ändå tillåter SQL att den yttersta frågan har en ORDER BY-klausul som tjänar en presentationsordningsinnebörd, vilket följande fråga visar:
Vad du dock behöver förstå är att den här frågan inte returnerar en relation som ett resultat. Även ur SQLs perspektiv returnerar frågan inte en tabell som ett resultat, och därför är det inte betraktas som ett tabelluttryck. Följaktligen är det ogiltigt att använda en sådan fråga som tabelluttrycksdelen av en härledd tabelldefinition.
Testa att köra följande kod:
Du får följande felmeddelande:
Jag tar upp om inte del av felmeddelandet inom kort.
Om du vill att den yttersta frågan ska returnera ett ordnat resultat, måste du ange ORDER BY-satsen i den yttersta frågan, så här:
När det gäller om inte del av felmeddelandet; T-SQL stöder det proprietära TOP-filtret såväl som standardfiltret OFFSET-FETCH. Båda filtren förlitar sig på en ORDER BY-sats i samma frågeomfång för att definiera för dem vilka översta rader som ska filtreras. Detta är tyvärr resultatet av en fälla i utformningen av dessa funktioner, som inte skiljer presentationsbeställning från filterbeställning. Hur som helst, både Microsoft med sitt TOP-filter och standarden med sitt OFFSET-FETCH-filter tillåter att specificera en ORDER BY-sats i den inre frågan så länge som den också specificerar TOP- respektive OFFSET-FETCH-filtret. Så den här frågan är giltig, till exempel:
När jag körde den här frågan på mitt system genererade den följande utdata:
Vad som dock är viktigt att betona är att det enda skälet till att ORDER BY-satsen är tillåten i den inre frågan är att stödja TOP-filtret. Det är den enda garantin att du kommer när det gäller beställning. Eftersom den yttre frågan inte heller har en ORDER BY-klausul, får du ingen garanti för någon specifik presentationsordning från denna fråga, trots det observerade beteendet. Det är både fallet i T-SQL, såväl som i standarden. Här är ett citat från standarden som tar upp denna del:
Som nämnts finns det mycket mer att säga om bordsuttryck och ordning, vilket jag kommer att göra i en framtida artikel. Jag kommer också att ge exempel som visar hur avsaknaden av ORDER BY-klausul i den yttre frågan innebär att du inte får några garantier för presentationsbeställning.
Så ett tabelluttryck, t.ex. en inre fråga i en härledd tabelldefinition, är en tabell. På samma sätt är en härledd tabell (i den specifika meningen) i sig också en tabell. Det är inte ett basbord, men det är ändå ett bord. Detsamma gäller CTE:er, visningar och inline TVF:er. De är inte bastabeller, snarare härledda sådana (i mer allmän mening), men de är ändå tabeller.
Härledda tabeller har två huvudsakliga brister i sin design. Båda har att göra med det faktum att den härledda tabellen är definierad i FROM-satsen i den yttre frågan.
Ett designfel har att göra med det faktum att om du behöver fråga en härledd tabell från en yttre fråga, och i sin tur använda den frågan som ett tabelluttryck i en annan härledd tabelldefinition, slutar du med att kapsla dessa härledda tabellfrågor. Inom datorer tenderar explicit kapsling av kod som involverar flera nivåer av kapsling att resultera i komplex kod som är svår att underhålla.
Här är ett mycket grundläggande exempel som visar detta:
Denna kod returnerar beställningsår och antalet kunder som gjort beställningar under varje år, endast för år där antalet kunder som beställt var fler än 70.
Den främsta motiveringen för att använda tabelluttryck här är att kunna referera till ett kolumnalias flera gånger. Den innersta frågan som används som ett tabelluttryck för den härledda tabellen D1 frågar efter tabellen Sales.Orders och tilldelar kolumnnamnet orderyear till uttrycket YEAR(orderdatum), och returnerar även custid-kolumnen. Frågan mot D1 grupperar raderna från D1 efter orderår och returnerar orderår såväl som det distinkta antalet kunder som lagt beställningar under året i fråga under alias som numcust. Koden definierar en härledd tabell som heter D2 baserat på denna fråga. Den yttersta frågan än frågar D2 och filtrerar endast år där antalet kunder som gjorde beställningar var fler än 70.
Ett försök att granska den här koden eller felsöka den i händelse av problem är knepigt på grund av de flera nivåerna av kapsling. Istället för att granska koden på det mer naturliga sättet från topp till botten, måste du analysera den med början på den innersta enheten och gradvis gå utåt, eftersom det är mer praktiskt.
Hela poängen med att använda härledda tabeller i det här exemplet var att förenkla koden genom att undvika behovet av att upprepa uttryck. Men jag är inte säker på att den här lösningen uppnår detta mål. I det här fallet är det förmodligen bättre att du upprepar vissa uttryck och undviker behovet av att använda härledda tabeller helt och hållet, som så:
Tänk på att jag visar ett mycket enkelt exempel här i illustrationssyfte. Föreställ dig produktionskod med fler nivåer av kapsling och med längre, mer utarbetad kod, så kan du se hur det blir betydligt mer komplicerat att underhålla.
En annan brist i utformningen av härledda tabeller har att göra med fall där du behöver interagera med flera instanser av samma härledda tabell. Betrakta följande fråga som ett exempel:
Den här koden beräknar antalet beställningar som behandlas varje år, såväl som skillnaden från föregående år. Ignorera det faktum att det finns enklare sätt att uppnå samma uppgift med fönsterfunktioner – jag använder den här koden för att illustrera en viss punkt, så själva uppgiften och de olika sätten att lösa den är inte signifikanta.
En join är en tabelloperator som behandlar sina två ingångar som en uppsättning – vilket betyder att det inte finns någon ordning bland dem. De kallas för vänster och höger ingångar så att du kan markera en av dem (eller båda) som en bevarad tabell i en yttre sammanfogning, men det finns fortfarande ingen första och andra bland dem. Du får använda härledda tabeller som join-ingångar, men intervallvariabelnamnet som du tilldelar den vänstra ingången är inte tillgängligt i definitionen av den högra ingången. Det beror på att båda är konceptuellt definierade i samma logiska steg, som vid samma tidpunkt. När du sammanfogar härledda tabeller kan du följaktligen inte definiera två intervallvariabler baserat på ett tabelluttryck. Tyvärr måste du upprepa koden och definiera två intervallvariabler baserat på två identiska kopior av koden. Detta komplicerar förstås kodens underhållbarhet och ökar sannolikheten för buggar. Varje ändring som du gör i ett tabelluttryck måste tillämpas på det andra också.
Som jag kommer att förklara i en framtida artikel, ådrar sig inte CTE:er i sin design dessa två brister som härledda tabeller ådrar sig.
En tabellvärdekonstruktor låter dig konstruera ett tabellvärde baserat på fristående skalära uttryck. Du kan sedan använda en sådan tabell i en yttre fråga precis som du använder en härledd tabell som är baserad på en inre fråga. I en framtida artikel diskuterar jag lateralt härledda tabeller och korrelationer i detalj, och jag kommer att visa mer sofistikerade former av tabellvärdekonstruktörer. In this article, though, I’ll focus on a simple form that is based purely on self-contained scalar expressions.
The general syntax for a query against a table value constructor is as follows:. Ett
SELECT custid, companyname
FROM Sales.Customers
WHERE country = N'USA'
T1 INNER JOIN T2
ON T1.keycol = T2.keycol
Syntax
FRÅN ( -delen av den härledda tabellen ha korrelationer till kolumner från en yttre tabell (mer om detta i en dedikerad framtida artikel i serien). Annars måste tabelluttrycket vara fristående.
SELECT custid, companyname
FROM ( SELECT custid, companyname
FROM Sales.Customers
WHERE country = N'USA' ) AS UC;
custid companyname
------- ---------------
32 Customer YSIQX
36 Customer LVJSO
43 Customer UISOJ
45 Customer QXPPT
48 Customer DVFMB
55 Customer KZQZT
65 Customer NYUHS
71 Customer LCOUJ
75 Customer XOJYP
77 Customer LCYBZ
78 Customer NLTYP
82 Customer EYHKM
89 Customer YBQTI
SELECT custid, companyname
FROM Sales.Customers AS C
WHERE country = N'USA';
Ett tabelluttryck är en tabell
delen av en härledd tabelldefinition är en tabell . Så är fallet även om det uttrycks som en fråga. Kommer du ihåg closure-egenskapen för relationalgebra? Detsamma gäller för resten av de ovannämnda namngivna tabelluttrycken (CTEs, views och inline TVFs). Som du redan har lärt dig, SQLs tabell är motsvarigheten till relationsteorins relation , om än inte en perfekt motsvarighet. Således måste ett tabelluttryck uppfylla vissa krav för att säkerställa att resultatet är en tabell – sådana som en fråga som inte används som ett tabelluttryck inte nödvändigtvis behöver. Här är tre specifika krav:
Alla kolumner måste ha namn
SELECT empid, firstname, lastname,
CONCAT_WS(N'/', country, region, city)
FROM HR.Employees;
empid firstname lastname (No column name)
------ ---------- ---------- -----------------
1 Sara Davis USA/WA/Seattle
2 Don Funk USA/WA/Tacoma
3 Judy Lew USA/WA/Kirkland
4 Yael Peled USA/WA/Redmond
5 Sven Mortensen UK/London
6 Paul Suurs UK/London
7 Russell King UK/London
8 Maria Cameron USA/WA/Seattle
9 Patricia Doyle UK/London
SELECT *
FROM ( SELECT empid, firstname, lastname,
CONCAT_WS(N'/', country, region, city)
FROM HR.Employees ) AS D;
Inget kolumnnamn angavs för kolumn 4 i 'D'. <-uttryck> [AS ]
SELECT empid, firstname, lastname, custlocation
FROM ( SELECT empid, firstname, lastname,
CONCAT_WS(N'/', country, region, city) AS custlocation
FROM HR.Employees ) AS D;
empid firstname lastname custlocation
------ ---------- ---------- ----------------
1 Sara Davis USA/WA/Seattle
2 Don Funk USA/WA/Tacoma
3 Judy Lew USA/WA/Kirkland
4 Yael Peled USA/WA/Redmond
5 Sven Mortensen UK/London
6 Paul Suurs UK/London
7 Russell King UK/London
8 Maria Cameron USA/WA/Seattle
9 Patricia Doyle UK/London
SELECT empid, firstname, lastname, custlocation
FROM ( SELECT empid, firstname, lastname,
CONCAT_WS(N'/', country, region, city)
FROM HR.Employees ) AS D(empid, firstname, lastname, custlocation);
SELECT empid, firstname, lastname, custlocation
FROM ( SELECT empid, firstname, lastname,
CONCAT_WS(N'/', country, region, city)
FROM HR.Employees
WHERE lastname LIKE N'D%' ) AS D(empid, lastname, firstname, custlocation)
WHERE firstname LIKE N'D%';
empid firstname lastname custlocation
------ ---------- --------- ---------------
1 Davis Sara USA/WA/Seattle
9 Doyle Patricia UK/London
SELECT empid, firstname, lastname, custlocation
FROM ( SELECT empid AS empid, firstname AS lastname, lastname AS firstname,
CONCAT_WS(N'/', country, region, city) AS custlocation
FROM HR.Employees
WHERE lastname LIKE N'D%' ) AS D
WHERE firstname LIKE N'D%';
Alla kolumnnamn måste vara unika
SELECT C.custid, O.custid, O.orderid
FROM Sales.Customers AS C
LEFT OUTER JOIN Sales.Orders AS O
ON C.custid = O.custid;
custid custid orderid
----------- ----------- -----------
1 1 10643
1 1 10692
1 1 10702
1 1 10835
1 1 10952
1 1 11011
2 2 10308
2 2 10625
2 2 10759
2 2 10926
...
SELECT *
FROM ( SELECT C.custid, O.custid, O.orderid
FROM Sales.Customers AS C
LEFT OUTER JOIN Sales.Orders AS O
ON C.custid = O.custid ) AS CO;
Kolumnen 'custid' specificerades flera gånger för 'CO'. SELECT *
FROM ( SELECT C.custid AS custcustid, O.custid AS ordercustid, O.orderid
FROM Sales.Customers AS C
LEFT OUTER JOIN Sales.Orders AS O
ON C.custid = O.custid ) AS CO;
custcustid ordercustid orderid
----------- ----------- -----------
1 1 10643
1 1 10692
1 1 10702
1 1 10835
1 1 10952
1 1 11011
2 2 10308
2 2 10625
2 2 10759
2 2 10926
...
SELECT CO.custcustid, CO.ordercustid, CO.orderid
FROM ( SELECT C.custid AS custcustid, O.custid AS ordercustid, O.orderid
FROM Sales.Customers AS C
LEFT OUTER JOIN Sales.Orders AS O
ON C.custid = O.custid ) AS CO;
Ingen beställning
SELECT orderid, val
FROM Sales.OrderValues
ORDER BY val DESC;
SELECT orderid, val
FROM ( SELECT orderid, val
FROM Sales.OrderValues
ORDER BY val DESC ) AS D;
ORDER BY-satsen är ogiltig i vyer, inline-funktioner, härledda tabeller, underfrågor och vanliga tabelluttryck, om inte TOP, OFFSET eller FOR XML också anges. SELECT orderid, val
FROM ( SELECT orderid, val
FROM Sales.OrderValues ) AS D
ORDER BY val DESC;
SELECT orderid, val
FROM ( SELECT TOP (3) orderid, val
FROM Sales.OrderValues
ORDER BY val DESC ) AS D;
orderid val
-------- ---------
10865 16387.50
10981 15810.00
11030 12615.05
Designfel
SELECT orderyear, numcusts
FROM ( SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
FROM ( SELECT YEAR(orderdate) AS orderyear, custid
FROM Sales.Orders ) AS D1
GROUP BY orderyear ) AS D2
WHERE numcusts > 70;
SELECT YEAR(orderdate) AS orderyear, COUNT(DISTINCT custid) AS numcusts
FROM Sales.Orders
GROUP BY YEAR(orderdate)
HAVING COUNT(DISTINCT custid) > 70;
SELECT CUR.orderyear, CUR.numorders,
CUR.numorders - PRV.numorders AS diff
FROM ( SELECT YEAR(orderdate) AS orderyear, COUNT(*) AS numorders
FROM Sales.Orders
GROUP BY YEAR(orderdate) ) AS CUR
LEFT OUTER JOIN
( SELECT YEAR(orderdate) AS orderyear, COUNT(*) AS numorders
FROM Sales.Orders
GROUP BY YEAR(orderdate) ) AS PRV
ON CUR.orderyear = PRV.orderyear + 1;
Tabellvärdekonstruktor