sql >> Database teknologi >  >> RDS >> Database

Avanceret SQL:CROSS APPLY og OUTER APPLY

I denne artikel vil vi se nærmere på operatoren "APPLY" og dens variationer - CROSS APPLY og OUTER APPLY sammen med eksempler på, hvordan de kan bruges.

Vi vil især lære:

  • forskellen mellem CROSS APPLY og JOIN-klausulen
  • hvordan man forbinder output fra SQL-forespørgsler med tabel-evaluerede funktioner
  • hvordan man identificerer ydeevneproblemer ved at forespørge på dynamiske administrationsvisninger og dynamiske administrationsfunktioner.

Hvad APPLY-klausulen er

Microsoft introducerede APPLY-operatoren i SQL Server 2005. APPLY-operatoren ligner T-SQL JOIN-klausulen, da den også giver dig mulighed for at forbinde to tabeller – for eksempel kan du forbinde en ydre tabel med en indre tabel. APPLY-operatoren er en god mulighed, når vi på den ene side har et tabel-evalueret udtryk, som vi ønsker at evaluere for hver række fra den tabel, vi har på den anden side. Så tabellen i højre side behandles for hver række i tabellen i venstre side. Tabellen i venstre side evalueres først, og derefter evalueres tabellen i højre side mod hver række i tabellen i venstre side for at generere det endelige resultatsæt. Det endelige resultatsæt inkluderer alle kolonner fra begge tabeller.

APPLY-operatøren har to varianter:

  • KRYDSANSØG
  • YDRE ANVENDELSE

KRYDSANSØG

CROSS APPLY ligner INNER JOIN, men kan også bruges til at forbinde tabel-evaluerede funktioner med SQL-tabeller. CROSS APPLYs endelige output består af poster, der matcher output fra en tabel-evalueret funktion og en SQL-tabel.

YDRE ANVENDELSE

OUTER APPLY minder om LEFT JOIN, men har en evne til at forbinde tabel-evaluerede funktioner med SQL-tabeller. OUTER APPLY's endelige output indeholder alle poster fra venstre-side-tabellen eller tabel-evaluerede funktion, selvom de ikke matcher med posterne i højre-side-tabellen eller tabel-vurderede funktion.

Lad mig nu forklare begge variationer med eksempler.

Eksempler på brug

Forberedelse af demoopsætningen

For at forberede en demo-opsætning skal du oprette tabeller med navnet "Medarbejdere" og "Afdeling" i en database, vi kalder "DemoDatabase". For at gøre det skal du køre følgende kode:

BRUG DEMODATABASE GO OPRET TABEL [DBO].[Medarbejdere] ( [EMPLOYEENAME] [VARCHAR](MAX) NULL, [FØDSELSDATO] [DATETIME] NULL, [JOBTITLE] [VARCHAR](150) NULL, [EMAILID] [ VARCHAR](100) NULL, [TELEFONNUMMER] [VARCHAR](20) NULL, [HIREDATE] [DATETIME] NULL, [DEPARTMENTID] [INT] NULL ) GÅ OPRET TABEL [DBO].[AFDELING] ( [DEPARTMENTID] INT IDENTITY (1, 1), [DEPARTMENTNAME] [VARCHAR](MAX) NULL ) GO

Indsæt derefter nogle dummy-data i begge tabeller. Følgende script vil indsætte data i "Medarbejder s ” tabel:

[expand title =”FULD FORESPØRGSEL “]

INDSÆT [DBO].[ANSATTE] ([EMPLOYEENAVN], [FØDSELSDATO], [JOBTITLE], [EMAILID], [TELEFONNUMMER], [UDLEJDAT], [DEPARTMENTID]) VÆRDI (N'KEN J SÁNCHEZ', CAST (N'1969-01-29T00:00:00.000' AS DATETIME), N'CHIEF EXECUTIVE OFFICER', N'[email protected]', N'697-555-0142', CAST(N'2009-01- 14T00:00:00.000' AS DATETIME), 1), (N'TERRI LEE DUFFY', CAST(N'1971-08-01T00:00:00.000' AS DATETIME), N'VICE PRESIDENT OF ENGINEERING', N'eksempel @sqldat.com', N'819-555-0175', CAST(N'2008-01-31T00:00:00.000' AS DATETIME), NULL), (N'ROBERTO TAMBURELLO', CAST(N'1974-11) -12T00:00:00.000' AS DATETIME), N'ENGINEERING MANAGER', N'[email protected]', N'212-555-0187', CAST(N'2007-11-11T00:00:00.000' AS DATETIME), NULL), (N'ROB WALTERS', CAST(N'1974-12-23T00:00:00.000' AS DATETIME), N' SENIOR TOOL DESIGNER', N'[email protected]', N'612-555-0100', CAST(N'2007-12-05T00:00:00.000' AS DATETIME), NULL), (N'GAIL A ERICKSON ', CAST(N'1952-09-27T00:00:00.000' AS DATETIME), N'DESIGN ENGINEER', N'[email protected]', N'849-555-0139', CAST(N'2008- 01-06T00:00:00.000' AS DATETIME), NULL), (N'JOSSEF H GOLDBERG', CAST(N'1959-03-11T00:00:00.000' AS DATETIME), N'DESIGN ENGINEER', N'eksempel @sqldat.com', N'122-555-0189', CAST(N'2008-01-24T00:00:00.000' AS DATETIME), NULL), (N'DYLAN A MILLER', CAST(N'1987- 02-24T00:00:00.000' AS DATETIME), N'RESEARCH AND DEVELOPMENT MANAGER', N'[email protected]', N'181-555-0156', CAST(N'2009-02-08T00:00:00.000' AS DATETIME), 3), (N'DIANE L MARGHEIM', CAST(N'1986-06-05T00:00:00.000' AS DATETIME), N'RESEARCH AND DEVELOPMENT ENGINEER', N'[email protected]', N'815-555-0138', CAST(N'2008-12-29T00:00:00.000' AS DATETIME), 3), (N'GIGI N MATTHEW', CAST(N) '1979-01-21T00:00:00.000' AS DATETIME), N'RESEARCH AND DEVELOPMENT ENGINEER', N'[email protected]', N'185-555-0186', CAST(N'2009-01-16T00 :00:00.000' AS DATETIME), 3), (N'MICHAEL RAHEEM', CAST(N'1984-11-30T00:00:00.000' AS DATETIME), N'RESEARCH AND DEVELOPMENT MANAGER', N'example@sqldat .com', N'330-555-2568', CAST(N'2009-05-03T00:00:00.000' AS DATETIME), 3)

[/udvid]

For at tilføje data til vores "Afdeling ” tabel, skal du køre følgende script:

INDSÆT [DBO].[AFDELING] ([AFDELINGSID], [AFDELINGSNAVN]) VÆRDIER (1, N'IT'), (2, N'TECHNICAL'), (3, N'RESEARCH AND DEVELOPMENT')

Nu, for at bekræfte dataene, skal du udføre koden, du kan se nedenfor:

VÆLG [ANSATNAVN], [FØDSELSDATO], [JOBTITLE], [EMAILID], [TELEFONNUMMER], [UDLEJDAT], [AFDELINGSID] FRA [ANSATTE] GÅVÆLG [AFDELINGS-ID], [AFDELINGSNAVN] FRA [AFDELING] GO 

Her er det ønskede output:

Oprettelse og test af en tabel-evalueret funktion

Som jeg allerede har nævnt, "ANVENDELSE PÅ KRYDS ” og “YDRE ANVENDELSE ” bruges til at forbinde SQL-tabeller med tabel-evaluerede funktioner. For at demonstrere det, lad os oprette en tabel-evalueret funktion ved navn "getEmployeeData ." Denne funktion vil bruge en værdi fra Afdelings-ID kolonne som inputparameter og returnere alle medarbejdere fra korrespondentafdelingen.

For at oprette funktionen skal du køre følgende script:

OPRET FUNKTION Getemployeesbydepartment (@DEPARTMENTID INT) RETURER @EMPLOYEES TABLE ( MEDARBEJDERNAVN VARCHAR (MAX), FØDSELSDATO DATETIME, JOBTITLE VARCHAR(150), EMAILID VARCHAR(100), PHONEDATE NUMBER TIME 0,VARCHARMENT )) SOM BEGIN INDSÆT I @EMPLOYEES VÆLG A.EMPLOYEENAME, A.BIRTHDATE, A.JOBTITLE, A.EMAILID, A.PHONENUMBER, A.HIREDATE, A.DEPARTMENTID FRA [EMPLOYEES] A WHERE A.DEPARTMENTID =@DEPARTMENTID RETURN END 

Nu, for at teste funktionen, sender vi "1 " som "afdelings-ID ” til “Getemployeesby-afdelingen " funktion. For at gøre dette skal du udføre scriptet nedenfor:

BRUG DEMODATABASEGOSELECT MEDARBEJDERNAVN, FØDSELSDATO, JOBTITLE, EMAILID, TELEFONNUMMER, UDLEJDAT, AFDELINGIDFRA GETEMPLOYEESBYDEPARTMENT (1)

Outputtet skal være som følger:

Tilslutning til en tabel med en tabel-evalueret funktion ved hjælp af CROSS APPLY

Lad os nu prøve at slutte os til tabellen Medarbejdere med "Getemployeesbydepartment ” tabel-evalueret funktion ved hjælp af CROSS APPLY . Som jeg nævnte, er KRYDS ANVENDELSE operatør ligner Join-klausulen. Det vil udfylde alle poster fra "Medarbejder ” tabel, for hvilken der er matchende rækker i outputtet af “Getemployeesbydepartment ”.

Kør følgende script:

VÆLG A.[EMPLOYEENAME], A.[FØDSELSDATO], A.[JOBTITLE], A.[EMAILID], A.[TELEFONNUMMER], A.[UDLEJDAT], B.[AFDELINGSNAVN] FRA AFDELING B KRYDS ANSØG GETEMPLOYEESBYDEPARTMENT(B.DEPARTMENTID) A

Outputtet skal være som følger:

Tilslutning til en tabel med en tabel-evalueret funktion ved hjælp af OUTER APPLY

Lad os nu prøve at slutte os til tabellen Medarbejdere med "Getemployeesbydepartment ” tabel-evalueret funktion ved hjælp af OUTER APPLY . Som jeg nævnte før, er YDRE APPLY operatøren ligner "YDRE JOIN ” klausul. Den udfylder alle poster fra "Medarbejder ”-tabellen og output fra “Getemployeesbydepartment ” funktion.

Kør følgende script:

VÆLG A.[EMPLOYEENAVN], A.[FØDSELSDATO], A.[JOBTITLE], A.[EMAILID], A.[TELEFONNUMMER], A.[UDLEJDAT], B.[AFDELINGSNAVN] FRA AFDELING B YDRE ANSØG GETEMPLOYEESBYDEPARTMENT(B.DEPARTMENTID) A

Her er det output, du skal se som et resultat:

Identifikation af ydeevneproblemer ved at bruge dynamiske administrationsfunktioner og visninger

Lad mig vise dig et andet eksempel. Her vil vi se, hvordan du får en forespørgselsplan og den tilsvarende forespørgselstekst ved at bruge dynamiske administrationsfunktioner og dynamiske administrationsvisninger.

Til demonstrationsformål har jeg lavet en tabel med navnet "SmokeTestResults " i "DemoDatabasen". Den indeholder resultater af en applikationsrøgtest. Lad os forestille os, at en udvikler ved en fejltagelse udfører en SQL-forespørgsel for at udfylde dataene fra "SmokeTestResults ” uden at tilføje et filter, hvilket reducerer databasens ydeevne betydeligt.

Som DBA skal vi identificere den ressourcetunge forespørgsel. For at gøre dette bruger vi "sys.dm_exec_requests "-visningen og "sys.dm_exec_sql_text ” funktion.

"Sys.dm_exec_requests ” er en dynamisk administrationsvisning, som giver følgende vigtige detaljer, vi kan bruge til at identificere den ressourcekrævende forespørgsel:

  1. Sessions-id
  2. CPU-tid
  3. Ventetype
  4. Database-id
  5. Læser (fysisk)
  6. Skriver (fysisk)
  7. Logiske læsninger
  8. SQL-håndtag
  9. Planhåndtag
  10. Forespørgselsstatus
  11. Kommando
  12. Transaktions-id

"sys.dm_exec_sql_text ” er en dynamisk administrationsfunktion, som accepterer et SQL-håndtag som inputparameter og giver følgende detaljer:

  1. Database-id
  2. Objekt-id
  3. Er krypteret
  4. SQL-forespørgselstekst

Lad os nu køre følgende forespørgsel for at generere noget stress på ASAP-databasen. Udfør følgende forespørgsel:

BRUG ASAP GO VÆLG TSID, USERID, EXECUTIONID, EX_RESULTFILE, EX_TESTDATAFILE, EX_ZIPFILE, EX_STARTTIME, EX_ENDTIME, EX_REMARKS FRA [ASAP].[DBO].[SMOKETESTRESULTS]

SQL Server tildeler et sessions-id "66" og starter udførelse af forespørgslen. Se følgende billede:

For nu at fejlfinde problemet kræver vi Database ID, Logical Reads, SQL Forespørgsel, kommando, sessions-id, ventetype og SQL-håndtag . Som jeg nævnte, kan vi få Database ID, Logical Reads, Command, Session ID, wait Type og SQL-håndtag fra "sys.dm_exec_requests." For at få SQL-forespørgsel , skal vi bruge "sys.dm_exec_sql_text. ” Det er en dynamisk administrationsfunktion, så du skal deltage i “sys.dm_exec_requests ” med “sys.dm_exec_sql_text ” ved at bruge CROSS APPLY.

Kør følgende forespørgsel i vinduet Ny forespørgselseditor:

VÆLG B.TEXT, A.WAIT_TYPE, A.LAST_WAIT_TYPE, A.COMMAND, A.SESSION_ID, CPU_TIME, A.BLOCKING_SESSION_ID, A.LOGICAL_READS FRA SYS.DM_EXEC_REQUESTS A CROSS APPLY SYS.QL_EXEC_SYS.QL_EXEC_H /pre> 

Det skulle producere følgende output:

Som du kan se på ovenstående skærmbillede, returnerede forespørgslen alle nødvendige oplysninger for at identificere ydeevneproblemet.

Ud over forespørgselsteksten ønsker vi nu at få den eksekveringsplan, som blev brugt til at udføre den pågældende forespørgsel. For at gøre dette bruger vi "sys.dm_exec_query_plan" funktion.

"sys.dm_exec_query_plan ” er en dynamisk administrationsfunktion, som accepterer et planhåndtag som inputparameter og giver følgende detaljer:

  1. Database-id
  2. Objekt-id
  3. Er krypteret
  4. SQL-forespørgselsplan i XML-format

For at udfylde forespørgselsudførelsesplanen skal vi bruge CROSS APPLY til at deltage i "sys.dm_exec_requests ” og “sys.dm_exec_query_plan.

Åbn vinduet Ny forespørgselseditor, og udfør følgende forespørgsel:

VÆLG B.TEXT, A.WAIT_TYPE, A.LAST_WAIT_TYPE, A.COMMAND, A.SESSION_ID, CPU_TIME, A.BLOCKING_SESSION_ID, A.LOGICAL_READS, C.QUERY_PLAN FRA SYS.DM_EXEC_REQUESTS A CROSSQ.DM_APPLY SYS.L. SQL_HANDLE) B CROSS APPLY SYS.DM_EXEC_QUERY_PLAN (A.PLAN_HANDLE) C

Outputtet skal være som følger:

Nu, som du kan se, genereres forespørgselsplanen som standard i XML-format. For at åbne det som en grafisk repræsentation skal du klikke på XML-outputtet i query_plan kolonne som vist på billedet ovenfor. Når du klikker på XML-outputtet, vil eksekveringsplanen blive åbnet i et nyt vindue som vist på følgende billede:

Få en liste over tabeller med stærkt fragmenterede indekser ved at bruge dynamiske administrationsvisninger og funktioner

Lad os se endnu et eksempel. Jeg ønsker at få en liste over tabeller med indekser, der har 50% eller mere fragmentering i en given database. For at hente disse tabeller skal vi bruge "sys.dm_db_index_physical_stats "-visningen og "sys.tables ” funktion.

"Sys.tables ” er en dynamisk administrationsvisning, som udfylder en liste over tabeller på den specifikke database.

"sys.dm_db_index_physical_stats ” er en dynamisk administrationsfunktion, som accepterer følgende inputparametre:

  1. Database-id
  2. Objekt-id
  3. Indeks-id
  4. Partitionsnummer
  5. Tilstand

Det returnerer detaljerede oplysninger om den fysiske status for det angivne indeks.

For nu at udfylde listen over fragmenterede indekser skal vi tilslutte os "sys.dm_db_index_physical_stats ” og “sys.tables ” ved at bruge CROSS APPLY. Kør følgende forespørgsel:

SELECT TABLES.NAME, INDEXSTATISTICS.ALLOC_UNIT_TYPE_DESC, CONVERT(NUMERIC(10, 2), INDEXSTATISTICS.AVG_FRAGMENTATION_IN_PERCENT) AS PERCENTAGEFRAGMENTATION, INDEXSTATISTICS.PAGE_COUNT SBROSS_ APPYSTAT_SOBYS.PH. , NULL, NULL, NULL) SOM INDEXSTATISTICS HVOR INDEXSTATISTICS.DATABASE_ID =DB_ID() OG AVG_FRAGMENTATION_IN_PERCENT>=50 ORDEN EFTER INDEXSTATISTICS.AVG_FRAGMENTATION_IN_PERCENT DESCENT 

Forespørgslen skal producere følgende output:

Oversigt

I denne artikel dækkede vi APPLY-operatøren, dens variationer - CROSS APPLY og OUTER APPLY og hvordan dit arbejde. Vi har også set, hvordan du kan bruge dem til at identificere SQL-ydeevneproblemer ved hjælp af dynamiske administrationsvisninger og dynamiske administrationsfunktioner.


  1. PostgreSQL-gabfri sekvenser

  2. Hvordan får man alle kolonnenavne til alle tabellerne i MySQL?

  3. SQL - Hvordan transponeres?

  4. MySQL SELECT DISTINCT flere kolonner