I sidste måned gav jeg en baggrund til tabeludtryk i T-SQL. Jeg forklarede konteksten ud fra relationsteorien og SQL-standarden. Jeg forklarede, hvordan en tabel i SQL er et forsøg på at repræsentere en relation fra relationsteori. Jeg forklarede også, at et relationelt udtryk er et udtryk, der opererer på en eller flere relationer som input og resulterer i en relation. På samme måde i SQL er et tabeludtryk et udtryk, der opererer på en eller flere inputtabeller og resulterer i en tabel. Udtrykket kan være en forespørgsel, men behøver ikke at være det. For eksempel kan udtrykket være en tabelværdikonstruktør, som jeg vil forklare senere i denne artikel. Jeg forklarede også, at jeg i denne serie fokuserer på fire specifikke typer af navngivne tabeludtryk, som T-SQL understøtter:afledte tabeller, almindelige tabeludtryk (CTE'er), visninger og indlejrede tabelværdier (TVF'er).
Hvis du har arbejdet med T-SQL i noget tid, er du sikkert stødt ind i en del tilfælde, hvor du enten skulle bruge tabeludtryk, eller det var på en eller anden måde mere bekvemt sammenlignet med alternative løsninger, der ikke bruger dem. Her er blot nogle få eksempler på use cases, der kommer til at tænke på:
- Skab en modulopbygget løsning ved at opdele komplekse opgaver i trin, der hver repræsenteres af et forskelligt tabeludtryk.
- Blanding af resultater af grupperede forespørgsler og detaljer, hvis du beslutter dig for ikke at bruge vinduesfunktioner til dette formål.
- Logisk forespørgselsbehandling håndterer forespørgselsklausuler i følgende rækkefølge:FROM>WHERE>GROUP BY>HAVING>SELECT>ORDER BY. Som følge heraf er kolonnealiasser, som du definerer i SELECT-udtrykket, kun tilgængelige for ORDER BY-udtrykket på samme niveau af indlejring. De er ikke tilgængelige for resten af forespørgselsklausulerne. Med tabeludtryk kan du genbruge aliaser, som du definerer i en indre forespørgsel i en hvilken som helst klausul af den ydre forespørgsel, og på denne måde undgå gentagelse af lange/komplekse udtryk.
- Vinduesfunktioner kan kun vises i en forespørgsels SELECT- og ORDER BY-klausuler. Med tabeludtryk kan du tildele et alias til et udtryk baseret på en vinduesfunktion og derefter bruge dette alias i en forespørgsel mod tabeludtrykket.
- En PIVOT-operator involverer tre elementer:gruppering, spredning og aggregering. Denne operatør identificerer grupperingselementet implicit ved eliminering. Ved hjælp af et tabeludtryk kan du projicere præcis de tre elementer, der skal være involveret, og få den ydre forespørgsel til at bruge tabeludtrykket som PIVOT-operatørens inputtabel og dermed kontrollere, hvilket element der er grupperingselementet.
- Ændringer med TOP understøtter ikke en ORDER BY-klausul. Du kan kontrollere, hvilke rækker der vælges indirekte ved at definere et tabeludtryk baseret på en SELECT-forespørgsel med TOP- eller OFFSET-FETCH-filteret og et ORDER BY-udtryk og anvende modifikationen mod tabeludtrykket.
Dette er langt fra en udtømmende liste. Jeg vil demonstrere nogle af ovenstående use cases og andre i denne serie. Jeg ville bare nævne nogle use cases her for at illustrere, hvor vigtige tabeludtryk er i vores T-SQL-kode, og hvorfor det er umagen værd at investere i at forstå deres grundlæggende principper godt.
I denne måneds artikel fokuserer jeg specifikt på den logiske behandling af afledte tabeller.
I mine eksempler vil jeg bruge en prøvedatabase kaldet TSQLV5. Du kan finde scriptet, der opretter og udfylder det her, og dets ER-diagram her.
Afledte tabeller
Udtrykket afledt tabel bruges i SQL og T-SQL med mere end én betydning. Så først vil jeg gøre det klart, hvilken jeg henviser til i denne artikel. Jeg henviser til en specifik sprogkonstruktion, som du typisk definerer, men ikke kun, i FROM-klausulen i en ydre forespørgsel. Jeg vil snart give syntaksen for denne konstruktion.
Den mere generelle brug af begrebet afledt tabel i SQL er modstykket til en afledt relation fra relationsteori. En afledt relation er en resultatrelation, der er afledt af en eller flere inputbaserelationer ved at anvende relationelle operatorer fra relationel algebra som projektion, skæringspunkt og andre til disse basisrelationer. På samme måde i generel forstand er en afledt tabel i SQL en resultattabel, der er afledt fra en eller flere basistabeller ved at evaluere udtryk mod disse inputbasetabeller.
Som en sidebemærkning tjekkede jeg, hvordan SQL-standarden definerer en basistabel, og jeg var straks ked af, at jeg generede det.
4.15.2 BasistabellerEn basistabel er enten en vedvarende basistabel eller en midlertidig tabel.
En persistent basistabel er enten en almindelig persistent basistabel eller en systemversionstabel.
En almindelig basistabel er enten en almindelig vedvarende basistabel eller en midlertidig tabel.”
Tilføjet her uden yderligere kommentarer...
I T-SQL kan du oprette en basistabel med en CREATE TABLE-sætning, men der er andre muligheder, f.eks. SELECT INTO og DECLARE @T AS TABLE.
Her er standardens definition for afledte tabeller i generel betydning:
4.15.3 Afledte tabeller
En afledt tabel er en tabel, der er afledt direkte eller indirekte fra en eller flere andre tabeller ved evalueringen af et udtryk, såsom en
Der er et par interessante ting at bemærke her om afledte tabeller i generel forstand. Man har at gøre med kommentaren om bestilling. Jeg kommer til denne senere i artiklen. En anden er, at en afledt tabel i SQL kan være et gyldigt selvstændigt tabeludtryk, men det behøver ikke at være det. For eksempel repræsenterer følgende udtryk en afledt tabel, og er betragtes også som et gyldigt selvstændigt tabeludtryk (du kan køre det):
Omvendt repræsenterer følgende udtryk en afledt tabel, men er det ikke et gyldigt selvstændigt tabeludtryk:
T-SQL understøtter en række tabeloperatorer, der giver en afledt tabel, men som ikke understøttes som selvstændige udtryk. Disse er:JOIN, PIVOT, UNPIVOT og APPLY. Du har brug for en klausul, som de kan fungere inden for (typisk FROM, men også MERGE-sætningens USING-klausul) og en værtsforespørgsel.
Herefter vil jeg bruge udtrykket afledt tabel til at beskrive en mere specifik sprogkonstruktion og ikke i den generelle betydning beskrevet ovenfor.
En afledt tabel kan defineres som en del af en ydre SELECT-sætning i dens FROM-sætning. Det kan også defineres som en del af DELETE- og UPDATE-sætningerne i deres FROM-sætning og som en del af en MERGE-sætning i dens USING-sætning. Jeg vil give flere detaljer om syntaksen, når den bruges i modifikationserklæringer senere i denne artikel.
Her er syntaksen for en forenklet SELECT-forespørgsel mod en afledt tabel:
Den afledte tabeldefinition vises, hvor en basistabel normalt kan forekomme, i den ydre forespørgsels FROM-klausul. Det kan være et input til en tabeloperator som JOIN, APPLY, PIVOT og UNPIVOT. Når den bruges som det rigtige input til en APPLY-operator, tillades
Den ydre sætning kan have alle de sædvanlige forespørgselselementer. I et tilfælde af SELECT-sætning:WHERE, GROUP BY, HAVING, ORDER BY og som nævnt tabeloperatorer i FROM-sætningen.
Her er et eksempel på en simpel forespørgsel mod en afledt tabel, der repræsenterer kunder i USA:
Denne forespørgsel genererer følgende output:
Der er tre hoveddele at identificere i en sætning, der involverer en afledt tabeldefinition:
Tabeludtrykket formodes at repræsentere en tabel og skal som sådan opfylde visse krav, som en normal forespørgsel ikke nødvendigvis behøver at opfylde. Jeg vil snart give detaljerne i afsnittet "Et tabeludtryk er en tabel".
Hvad angår det målafledte tabelnavn; en almindelig antagelse blandt T-SQL-udviklere er, at det blot er et navn eller et alias, som du tildeler måltabellen. På samme måde kan du overveje følgende forespørgsel:
Også her er den almindelige antagelse, at AS C blot er en måde at omdøbe, eller alias, tabellen Kunder med henblik på denne forespørgsel, startende med det logiske forespørgselsbehandlingstrin, hvor navnet tildeles, og fremefter. Men fra relationsteoriens synspunkt er der en dybere mening med, hvad C repræsenterer. C er det, der er kendt som en intervalvariabel. C er en afledt relationsvariabel, der går over tuplerne i inputrelationsvariablen Kunder. I ovenstående eksempel går C over tuplerne i Kunder og evaluerer prædikatet land =N'USA'. Tupler, for hvilke prædikatet vurderes til at være sandt, bliver en del af resultatrelationen C.
Med den baggrund, som jeg har givet indtil videre, burde det, jeg nu skal forklare, ikke være overraskende.
Lad os nedbryde disse krav en efter en og diskutere relevansen for både relationsteori og SQL.
Husk, at en relation har en overskrift og en krop. Overskriften på en relation er et sæt attributter (kolonner i SQL). En attribut har et navn og et typenavn og identificeres ved sit navn. En forespørgsel, der ikke bruges som et tabeludtryk, behøver ikke nødvendigvis at tildele navne til alle målkolonner. Overvej følgende forespørgsel som et eksempel:
Denne forespørgsel genererer følgende output:
Forespørgselsoutputtet har en anonym kolonne, der er et resultat af sammenkædningen af lokationsattributterne ved hjælp af CONCAT_WS-funktionen. (Denne funktion blev i øvrigt tilføjet i SQL Server 2017, så hvis du kører koden i en tidligere version, er du velkommen til at erstatte denne beregning med en alternativ beregning efter eget valg.) Denne forespørgsel gør det derfor ikke returnere en tabel, for ikke at tale om en relation. Derfor er det ikke gyldigt at bruge en sådan forespørgsel som tabeludtrykket/den indre forespørgselsdel af en afledt tabeldefinition.
Prøv det:
Du får følgende fejlmeddelelse:
Som en sidebemærkning, bemærker du noget interessant ved fejlmeddelelsen? Den klager over kolonne 4 og fremhæver forskellen mellem kolonner i SQL og attributter i relationsteori.
Løsningen er selvfølgelig at sikre, at du eksplicit tildeler navne til kolonner, der er resultatet af beregninger. T-SQL understøtter en del kolonnenavngivningsteknikker. Jeg vil nævne to af dem.
Du kan bruge en indlejret navngivningsteknik, hvor du tildeler målkolonnenavnet efter beregningen og en valgfri AS-klausul, som i
Denne forespørgsel genererer følgende output:
Ved at bruge denne teknik er det meget nemt, når man gennemgår koden, at fortælle hvilket målkolonnenavn der er tildelt hvilket udtryk. Du behøver også kun at navngive kolonner, der ellers ikke allerede har navne.
Du kan også bruge en mere ekstern kolonnenavngivningsteknik, hvor du angiver målkolonnenavnene i parentes lige efter det afledte tabelnavn, som sådan:
Med denne teknik skal du dog angive navne for alle kolonner - inklusive dem, der allerede har navne. Tildelingen af målkolonnenavnene udføres efter position fra venstre mod højre, dvs. det første målkolonnenavn repræsenterer det første udtryk i den indre forespørgsels SELECT-liste; det anden målkolonnenavn repræsenterer det andet udtryk; og så videre.
Bemærk, at i tilfælde af uoverensstemmelse mellem de indre og ydre kolonnenavne, f.eks. på grund af en fejl i koden, er omfanget af de indre navne den indre forespørgsel – eller mere præcist den indre områdevariabel (her implicit HR.Employees AS Employees) - og omfanget af de ydre navne er den ydre områdevariabel (D i vores tilfælde). Der er lidt mere involveret i omfanget af kolonnenavne, der har at gøre med logisk forespørgselsbehandling, men det er et punkt til senere diskussioner.
Potentialet for fejl med den eksterne navngivningssyntaks forklares bedst med et eksempel.
Undersøg resultatet af den forrige forespørgsel med det fulde sæt af medarbejdere fra HR.Employees-tabellen. Overvej derefter følgende forespørgsel, og før du kører den, prøv at finde ud af, hvilke medarbejdere du forventer at se i resultatet:
Hvis du forventer, at forespørgslen returnerer et tomt sæt for de givne eksempeldata, da der i øjeblikket ikke er nogen medarbejdere med både et efternavn og et fornavn, der starter med bogstavet D, mangler du fejlen i koden.
Kør nu forespørgslen, og undersøg det faktiske output:
Hvad skete der?
Den indre forespørgsel angiver fornavn som anden kolonne og efternavn som tredje kolonne i SELECT-listen. Koden, der tildeler den afledte tabels målkolonnenavne i den ydre forespørgsel, angiver efternavn andet og fornavn tredje. Kodenavnene fornavn som efternavn og efternavn som fornavn i intervalvariablen D. Faktisk filtrerer du bare medarbejdere, hvis efternavn starter med bogstavet D. Du filtrerer ikke medarbejdere med både et efternavn og et fornavn, der starter med bogstavet D.
Den inline aliasing syntaks er ikke tilbøjelig til sådanne fejl. For det første kalder du normalt ikke en kolonne, der allerede har et navn, du er tilfreds med. For det andet, selvom du ønsker at tildele et andet alias til en kolonne, der allerede har et navn, er det ikke særlig sandsynligt, at du med syntaksen
Det er åbenbart ikke særlig sandsynligt.
Tilbage til det faktum, at overskriften på en relation er et sæt attributter, og givet at en attribut er identificeret ved navn, skal attributnavne være unikke for den samme relation. I en given forespørgsel kan du altid henvise til en attribut ved hjælp af et todelt navn med områdevariabelnavnet som kvalifikation, som i
Overvej følgende selvstændige forespørgsel som et eksempel:
Denne forespørgsel mislykkes ikke med en dublet kolonnenavnsfejl, da den ene custid-kolonne faktisk hedder C.custid og den anden O.custid inden for den aktuelle forespørgsels rækkevidde. Denne forespørgsel genererer følgende output:
Prøv dog at bruge denne forespørgsel som et tabeludtryk i definitionen af en afledt tabel med navnet CO, som sådan:
For så vidt angår den ydre forespørgsel, har du én områdevariabel ved navn CO, og omfanget af alle kolonnenavne i den ydre forespørgsel er den områdevariabel. Navnene på alle kolonner i en given intervalvariabel (husk, en intervalvariabel er en relationsvariabel) skal være unikke. Derfor får du følgende fejl:
Rettelsen er selvfølgelig at tildele forskellige kolonnenavne til de to custid-kolonner, hvad angår intervalvariablen CO, som sådan:
Denne forespørgsel genererer følgende output:
Hvis du følger god praksis, angiver du eksplicit kolonnenavnene i den yderste forespørgsels SELECT-liste. Da der kun er én intervalvariabel involveret, behøver du ikke bruge det todelte navn til de ydre kolonnereferencer. Hvis du ønsker at bruge det todelte navn, skal du præfikse kolonnenavnene med det ydre områdevariabelnavn CO, sådan:
Der er ret meget, jeg har at sige om navngivne tabeludtryk og rækkefølge - nok til en artikel i sig selv - så jeg vil dedikere en fremtidig artikel til dette emne. Alligevel ville jeg kort berøre emnet her, da det er så vigtigt. Husk, at kroppen af en relation er et sæt af tupler, og på samme måde er kroppen af en tabel et sæt rækker. Et sæt har ingen rækkefølge. Alligevel tillader SQL, at den yderste forespørgsel har en ORDER BY-klausul, der tjener en præsentationsordringsbetydning, som følgende forespørgsel demonstrerer:
Hvad du dog skal forstå er, at denne forespørgsel ikke returnerer en relation som et resultat. Selv fra SQLs perspektiv returnerer forespørgslen ikke en tabel som et resultat, og derfor er det ikke betragtes som et tabeludtryk. Det er derfor ugyldigt at bruge en sådan forespørgsel som tabeludtryksdelen af en afledt tabeldefinition.
Prøv at køre følgende kode:
Du får følgende fejlmeddelelse:
Jeg tager fat på medmindre del af fejlmeddelelsen snart.
Hvis du ønsker, at den yderste forespørgsel skal returnere et ordnet resultat, skal du angive ORDER BY-klausulen i den yderste forespørgsel, sådan:
Med hensyn til medmindre del af fejlmeddelelsen; T-SQL understøtter det proprietære TOP-filter samt standard OFFSET-FETCH-filteret. Begge filtre er afhængige af en ORDER BY-klausul i det samme forespørgselsomfang for at definere for dem, hvilke øverste rækker der skal filtreres. Dette er desværre resultatet af en fælde i designet af disse funktioner, som ikke adskiller præsentationsbestilling fra filterbestilling. Hvorom alting er, både Microsoft med dets TOP-filter og standarden med dets OFFSET-FETCH-filter tillader at specificere en ORDER BY-klausul i den indre forespørgsel, så længe den også specificerer henholdsvis TOP- eller OFFSET-FETCH-filteret. Så denne forespørgsel er gyldig, for eksempel:
Da jeg kørte denne forespørgsel på mit system, genererede den følgende output:
Det, der dog er vigtigt at understrege, er, at den eneste grund til, at ORDER BY-klausulen er tilladt i den indre forespørgsel, er at understøtte TOP-filteret. Det er den eneste garanti for, at du når, hvad bestilling angår. Da den ydre forespørgsel ikke også har en ORDER BY-klausul, får du ikke garanti for nogen specifik præsentationsbestilling fra denne forespørgsel, uanset hvad den observerede adfærd er. Det er både tilfældet i T-SQL såvel som i standarden. Her er et citat fra standarden, der omhandler denne del:
Som nævnt er der meget mere at sige om bordudtryk og rækkefølge, hvilket jeg vil gøre i en fremtidig artikel. Jeg vil også give eksempler, der viser, hvordan manglen på ORDER BY-klausul i den ydre forespørgsel betyder, at du ikke får nogen præsentationsbestillingsgarantier.
Så et tabeludtryk, f.eks. en indre forespørgsel i en afledt tabeldefinition, er en tabel. På samme måde er en afledt tabel (i den specifikke betydning) i sig selv også en tabel. Det er ikke et basisbord, men det er ikke desto mindre et bord. Det samme gælder for CTE'er, views og inline TVF'er. De er ikke basistabeller, snarere afledte (i mere generel forstand), men de er ikke desto mindre tabeller.
Afledte tabeller har to hovedmangler i deres design. Begge har at gøre med det faktum, at den afledte tabel er defineret i FROM-delen af den ydre forespørgsel.
En designfejl har at gøre med det faktum, at hvis du har brug for at forespørge en afledt tabel fra en ydre forespørgsel, og til gengæld bruge den forespørgsel som et tabeludtryk i en anden afledt tabeldefinition, ender du med at indlejre disse afledte tabelforespørgsler. Inden for databehandling har eksplicit indlejring af kode, der involverer flere niveauer af indlejring, tendens til at resultere i kompleks kode, som er svær at vedligeholde.
Her er et meget grundlæggende eksempel, der viser dette:
Denne kode returnerer ordreår og antallet af kunder, der afgav ordrer i løbet af hvert år, kun for år, hvor antallet af kunder, der afgav ordrer, var større end 70.
Hovedmotivationen for at bruge tabeludtryk her er for at kunne henvise til et kolonnealias flere gange. Den inderste forespørgsel, der bruges som et tabeludtryk for den afledte tabel D1, forespørger i Sales.Orders-tabellen, og tildeler kolonnenavnet ordreår til udtrykket YEAR(orderdate), og returnerer også custid-kolonnen. Forespørgslen mod D1 grupperer rækkerne fra D1 efter ordreår og returnerer ordreår samt det distinkte antal kunder, der har afgivet ordrer i løbet af det pågældende år, kaldet numcust. Koden definerer en afledt tabel kaldet D2 baseret på denne forespørgsel. Den yderste forespørgsel end forespørgsler D2 og filtrerer kun år, hvor antallet af kunder, der afgav ordrer, var større end 70.
Et forsøg på at gennemgå denne kode eller fejlfinde den i tilfælde af problemer er vanskelig på grund af de flere niveauer af indlejring. I stedet for at gennemgå koden på den mere naturlige top-til-bund måde, ser du ud til, at du skal analysere den, begyndende med den inderste enhed og gradvist gå udad, da det er mere praktisk.
Hele pointen med at bruge afledte tabeller i dette eksempel var at forenkle koden ved at undgå behovet for at gentage udtryk. Men jeg er ikke sikker på, at denne løsning når dette mål. I dette tilfælde er det nok bedre at gentage nogle udtryk og helt undgå behovet for at bruge afledte tabeller, som sådan:
Husk, at jeg viser et meget simpelt eksempel her til illustrationsformål. Forestil dig produktionskode med flere niveauer af indlejring og med længere, mere udførlig kode, og du kan se, hvordan det bliver væsentligt mere kompliceret at vedligeholde.
En anden fejl i designet af afledte tabeller har at gøre med tilfælde, hvor du skal interagere med flere forekomster af den samme afledte tabel. Overvej følgende forespørgsel som et eksempel:
Denne kode beregner antallet af ordrer, der behandles i hvert år, samt forskellen fra det foregående år. Ignorer det faktum, at der er enklere måder at opnå den samme opgave med vinduesfunktioner - jeg bruger denne kode til at illustrere et bestemt punkt, så selve opgaven og de forskellige måder at løse den på er ikke væsentlige.
En joinforbindelse er en tabeloperator, der behandler sine to input som et sæt - hvilket betyder, at der ikke er nogen rækkefølge blandt dem. De omtales som venstre og højre input, så du kan markere en af dem (eller begge) som en bevaret tabel i en ydre sammenføjning, men alligevel er der ingen første og anden blandt dem. Det er tilladt at bruge afledte tabeller som join-input, men det områdevariabelnavn, som du tildeler det venstre input, er ikke tilgængeligt i definitionen af det højre input. Det er fordi begge er konceptuelt defineret i det samme logiske trin, som om det var på samme tidspunkt. Når du forbinder afledte tabeller, kan du derfor ikke definere to intervalvariabler baseret på ét tabeludtryk. Desværre er du nødt til at gentage koden og definere to intervalvariabler baseret på to identiske kopier af koden. Dette komplicerer naturligvis vedligeholdelsen af koden og øger sandsynligheden for fejl. Hver ændring, du foretager i et tabeludtryk, skal også anvendes på det andet.
Som jeg vil forklare i en fremtidig artikel, pådrager CTE'er i deres design ikke disse to fejl, som afledte tabeller pådrager sig.
En tabelværdikonstruktør giver dig mulighed for at konstruere en tabelværdi baseret på selvstændige skalarudtryk. Du kan derefter bruge en sådan tabel i en ydre forespørgsel, ligesom du bruger en afledt tabel, der er baseret på en indre forespørgsel. I en fremtidig artikel diskuterer jeg laterale afledte tabeller og korrelationer i detaljer, og jeg vil vise mere sofistikerede former for tabelværdikonstruktører. I denne artikel vil jeg dog fokusere på en simpel form, der udelukkende er baseret på selvstændige skalarudtryk.
The general syntax for a query against a table value constructor is as follows:. Et
SELECT custid, companyname
FROM Sales.Customers
WHERE country = N'USA'
T1 INNER JOIN T2
ON T1.keycol = T2.keycol
Syntaks
FRA ( 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';
Et tabeludtryk er en tabel
delen af en afledt tabeldefinition er en tabel . Det er tilfældet, selvom det er udtrykt som en forespørgsel. Kan du huske lukningsegenskaben ved relationel algebra? Det samme gælder for resten af de førnævnte navngivne tabeludtryk (CTE'er, views og inline TVF'er). Som du allerede har lært, SQLs tabel er modstykket til relationsteoriens relation , omend ikke et perfekt modstykke. Et tabeludtryk skal således opfylde visse krav for at sikre, at resultatet er en tabel – dem, som en forespørgsel, der ikke bruges som et tabeludtryk, ikke nødvendigvis skal. Her er tre specifikke krav:
Alle kolonner skal have navne
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;
Intet kolonnenavn blev angivet for kolonne 4 i 'D'. <-udtryk> [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%';
Alle kolonnenavne skal være unikke
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;
Kolonnen 'custid' blev angivet flere gange for '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 ordre
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-sætningen er ugyldig i visninger, inline-funktioner, afledte tabeller, underforespørgsler og almindelige tabeludtryk, medmindre TOP, OFFSET eller FOR XML også er angivet. 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
Designfejl
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;
Tabelværdikonstruktør