sql >> Database >  >> RDS >> Database

Geavanceerde SQL:CROSS APPLY en OUTER APPLY

In dit artikel bekijken we de operator "APPLY" en zijn variaties - CROSS APPLY en OUTER APPLY, samen met voorbeelden van hoe ze kunnen worden gebruikt.

We zullen in het bijzonder leren:

  • het verschil tussen CROSS APPLY en de JOIN-clausule
  • hoe de uitvoer van SQL-query's samen te voegen met door tabellen geëvalueerde functies
  • hoe prestatieproblemen te identificeren door dynamische beheerweergaven en dynamische beheerfuncties op te vragen.

Wat de APPLY-clausule is

Microsoft heeft de APPLY-operator in SQL Server 2005 geïntroduceerd. De APPLY-operator is vergelijkbaar met de T-SQL JOIN-component, omdat u hiermee ook twee tabellen kunt samenvoegen - u kunt bijvoorbeeld een buitenste tabel samenvoegen met een binnenste tabel. De APPLY-operator is een goede optie wanneer we aan de ene kant een door een tabel geëvalueerde uitdrukking hebben die we willen evalueren voor elke rij van de tabel die we aan de andere kant hebben. De tabel aan de rechterkant wordt dus verwerkt voor elke rij van de tabel aan de linkerkant. De tabel aan de linkerkant wordt eerst geëvalueerd en vervolgens wordt de tabel aan de rechterkant geëvalueerd met elke rij van de tabel aan de linkerkant om de uiteindelijke resultatenset te genereren. De uiteindelijke resultatenset bevat alle kolommen uit beide tabellen.

De APPLY-operator heeft twee varianten:

  • KRUIS TOEPASSEN
  • BUITENSTE TOEPASSING

KRUIS TOEPASSEN

CROSS APPLY lijkt op INNER JOIN, maar kan ook worden gebruikt om tabel-geëvalueerde functies samen te voegen met SQL-tabellen. De uiteindelijke uitvoer van CROSS APPLY bestaat uit records die overeenkomen tussen de uitvoer van een door een tabel geëvalueerde functie en een SQL-tabel.

BUITENSTE TOEPASSING

OUTER APPLY lijkt op LEFT JOIN, maar heeft de mogelijkheid om tabel-geëvalueerde functies samen te voegen met SQL-tabellen. De uiteindelijke uitvoer van OUTER APPLY bevat alle records uit de tabel aan de linkerkant of de tabel-geëvalueerde functie, zelfs als ze niet overeenkomen met de records in de tabel aan de rechterkant of de functie met tabelwaarde.

Laat me nu beide varianten uitleggen met voorbeelden.

Gebruiksvoorbeelden

De demo-instelling voorbereiden

Om een ​​demo-configuratie voor te bereiden, moet u tabellen met de namen "Werknemers" en "Afdeling" maken in een database die we "DemoDatabase" noemen. Voer hiervoor de volgende code uit:

GEBRUIK DEMODATABASE GA TABEL MAKEN [DBO].[WERKNEMERS] ( [NAAM WERKNEMER] [VARCHAR](MAX) NULL, [GEBOORTEDATUM] [DATUMTIJD] NULL, [JOBTITLE] [VARCHAR](150) NULL, [EMAILID] [ VARCHAR](100) NULL, [PHONENUMBER] [VARCHAR](20) NULL, [HIREDATE] [DATETIME] NULL, [DEPARTMENTID] [INT] NULL ) GO CREATE TABLE [DBO].[AFDELING] ( [DEPARTMENTID] INT IDENTITY (1, 1), [AFDELINGSNAAM] [VARCHAR](MAX) NULL ) GO

Voeg vervolgens enkele dummy-gegevens in beide tabellen in. Het volgende script zal gegevens invoegen in de "Werknemer s ” tabel:

[titel uitbreiden =”VOLLEDIGE QUERY "]

INSERT [DBO].[WERKNEMERS] ([NAAM WERKNEMER], [GEBOORTEDATUM], [JOBTITLE], [EMAILID], [TELEFOONNUMMER], [HIREDATE], [AFDELINGS-ID]) WAARDEN (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'example @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'voorbeeld @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)

[/uitbreiden]

Gegevens toevoegen aan onze "Afdeling ” tabel, voer het volgende script uit:

INSERT [DBO].[AFDELING] ([AFDELING-ID], [AFDELINGNAAM]) WAARDEN (1, N'IT'), (2, N'TECHNICAL'), (3, N'RESEARCH AND DEVELOPMENT')

Voer nu de onderstaande code uit om de gegevens te verifiëren:

SELECTEER [EMPLOYEENAME], [BIRTHDATE], [VACATURE], [EMAILID], [PHONENUMBER], [HIREDATE], [AFDELING-ID] VAN [WERKNEMERS] GOSELECT [AFDELINGS-ID], [AFDELINGSNAAM] VAN [AFDELING] GO

Dit is de gewenste uitvoer:

Een tabel-geëvalueerde functie maken en testen

Zoals ik al zei, "CROSS APPLY ” en “BUITENSTE TOEPASSING ” worden gebruikt om SQL-tabellen samen te voegen met door tabellen geëvalueerde functies. Laten we om dat te demonstreren een door een tabel geëvalueerde functie maken met de naam "getEmployeeData .” Deze functie gebruikt een waarde uit de DepartmentID kolom als invoerparameter en retourneer alle medewerkers van de corresponderende afdeling.

Voer het volgende script uit om de functie te maken:

FUNCTIE MAKEN Getemployeesbydepartment (@DEPARTMENTID INT) RETURNS @EMPLOYEES TABLE ( WERKNEMERSNAAM VARCHAR (MAX), GEBOORTEDATUM DATETIME, JOBTITLE VARCHAR(150), EMAILID VARCHAR(100), TELEFOONNUMMER VARCHAR(20), VERHURENDATUM VARCHAR )) ALS BEGIN INVOEREN IN @EMPLOYEES SELECTEER A.EMPLOYEENAME, A.BIRTHDATE, A.JOBTITLE, A.EMAILID, A.PHONENUMBER, A.HIREDATE, A.DEPARTMENTID VAN [WERKNEMERS] EEN WAAR A.DEPARTMENTID =@DEPARTMENTID RETOUR 

Nu, om de functie te testen, zullen we "1 . doorgeven ” als “afdelings-ID ” naar de “Getemployeesbydepartment ” functie. Voer hiervoor het onderstaande script uit:

GEBRUIK DEMODATABASEGOSELECT WERKNEMERNAAM, BIRTHDATE, JOBTITLE, EMAILID, TELEFOONNUMMER, HIREDATE, DEPARTMENTIDFROM GETEMPLOYEESBYDEPARTMENT (1)

De uitvoer zou als volgt moeten zijn:

Deelnemen aan een tabel met een tabel-geëvalueerde functie met behulp van CROSS APPLY

Laten we nu proberen om deel te nemen aan de tabel Werknemers met de "Getemployeesbydepartment ” tabel-geëvalueerde functie met behulp van CROSS APPLY . Zoals ik al zei, de CROSS APPLY operator is vergelijkbaar met de Join-clausule. Het zal alle records van de "Werknemer . vullen ” tabel waarvoor er overeenkomende rijen zijn in de uitvoer van “Getemployeesbydepartment ”.

Voer het volgende script uit:

SELECTEER A.[NAAM WERKNEMER], A.[GEBOORTEDATUM], A.[VACATURE], A.[EMAILID], A.[TELEFOONNUMMER], A.[HIREDATE], B.[AFDELINGSNAAM] VANAF AFDELING B KRUIS TOEPASSEN GETEMPLOYEESBYDEPARTMENT(B.DEPARTMENTID) A

De uitvoer zou als volgt moeten zijn:

Deelnemen aan een tabel met een tabel-geëvalueerde functie met OUTER APPLY

Laten we nu proberen om deel te nemen aan de tabel Werknemers met de "Getemployeesbydepartment ” tabel-geëvalueerde functie met behulp van OUTER APPLY . Zoals ik eerder al zei, de OUTER APPLY operator lijkt op de "OUTER JOIN ” clausule. Het vult alle records van de "Werknemer ” tabel en de output van de “Getemployeesbydepartment ” functie.

Voer het volgende script uit:

SELECTEER A.[NAAM WERKNEMER], A.[GEBOORTEDATUM], A.[VACATURE], A.[EMAILID], A.[TELEFOONNUMMER], A.[HIREDATE], B.[AFDELINGSNAAM] VANAF AFDELING B BUITEN TOEPASSING GETEMPLOYEESBYDEPARTMENT(B.DEPARTMENTID) A

Dit is de uitvoer die u als resultaat zou moeten zien:

Prestatieproblemen identificeren met behulp van dynamische beheerfuncties en weergaven

Laat me je een ander voorbeeld laten zien. Hier zullen we zien hoe u een queryplan en de bijbehorende querytekst kunt krijgen met behulp van dynamische beheerfuncties en dynamische beheerweergaven.

Voor demonstratiedoeleinden heb ik een tabel gemaakt met de naam "SmokeTestResults ” in de “DemoDatabase”. Het bevat resultaten van een toepassingsrooktest. Laten we ons voorstellen dat een ontwikkelaar per ongeluk een SQL-query uitvoert om de gegevens van "SmokeTestResults ” zonder een filter toe te voegen, wat de databaseprestaties aanzienlijk vermindert.

Als DBA moeten we de resource-zware query identificeren. Om dit te doen, gebruiken we de "sys.dm_exec_requests ” weergave en de “sys.dm_exec_sql_text ” functie.

"Sys.dm_exec_requests ” is een dynamische beheerweergave die de volgende belangrijke details biedt die we kunnen gebruiken om de resource-intensieve zoekopdracht te identificeren:

  1. Sessie-ID
  2. CPU-tijd
  3. Wachttype
  4. Database-ID
  5. Lees (fysiek)
  6. Schrijft (fysiek)
  7. Logische leest
  8. SQL-handvat
  9. Greep plannen
  10. Opvraagstatus
  11. Opdracht
  12. Transactie-ID

sys.dm_exec_sql_text ” is een dynamische beheerfunctie die een SQL-handle . accepteert als invoerparameter en geeft de volgende details:

  1. Database-ID
  2. Object-ID
  3. Is versleuteld
  4. SQL-querytekst

Laten we nu de volgende query uitvoeren om wat stress op de ASAP-database te genereren. Voer de volgende vraag uit:

GEBRUIK ASAP GA SELECT TSID, USERID, EXECUTIONID, EX_RESULTFILE, EX_TESTDATAFILE, EX_ZIPFILE, EX_STARTTIME, EX_ENDTIME, EX_REMARKS FROM [ASAP].[DBO].[SMOKETESTRESULTS]

SQL Server wijst een sessie-ID "66" toe en start de uitvoering van de query. Zie de volgende afbeelding:

Om het probleem op te lossen, hebben we de Database-ID, logische leesbewerkingen, nodig SQL Query, Commando, Sessie-ID, Wachttype en SQL-handvat . Zoals ik al zei, kunnen we Database-ID, Logical Reads, Command, Session ID, wait Type krijgen en SQL-handle van "sys.dm_exec_requests." De SQL-query . ophalen , moeten we "sys.dm_exec_sql_text. . gebruiken ” Het is een dynamische beheerfunctie, dus zou moeten deelnemen aan “sys.dm_exec_requests ” met “sys.dm_exec_sql_text ” door CROSS APPLY te gebruiken.

Voer in het venster Nieuwe query-editor de volgende query uit:

SELECT B.TEXT, A.WAIT_TYPE, A.LAST_WAIT_TYPE, A.COMMAND, A.SESSION_ID, CPU_TIME, A.BLOCKING_SESSION_ID, A.LOGICAL_READS FROM SYS.DM_EXEC_REQUESTS EEN CROSS APPLY SYS.DM_EXEC_SQLQ_LQ /pre> 

Het zou de volgende output moeten produceren:

Zoals je kunt zien in de bovenstaande schermafbeelding, heeft de query alle informatie geretourneerd die nodig is om het prestatieprobleem te identificeren.

Nu willen we, naast de querytekst, het uitvoeringsplan krijgen dat is gebruikt om de betreffende query uit te voeren. Om dit te doen, gebruiken we het "sys.dm_exec_query_plan" functie.

"sys.dm_exec_query_plan ” is een dynamische beheerfunctie die een planhandvat accepteert als invoerparameter en geeft de volgende details:

  1. Database-ID
  2. Object-ID
  3. Is versleuteld
  4. SQL-queryplan in XML-indeling

Om het uitvoeringsplan voor query's te vullen, moeten we CROSS APPLY gebruiken om deel te nemen aan "sys.dm_exec_requests ” en “sys.dm_exec_query_plan.

Open het venster Nieuwe query-editor en voer de volgende query uit:

SELECT 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 VAN SYS.DM_EXEC_REQUESTS A CROSS EXELY SYS.DM_ SQL_HANDLE) B CROSS APPLY SYS.DM_EXEC_QUERY_PLAN (A.PLAN_HANDLE) C

De uitvoer zou als volgt moeten zijn:

Zoals u kunt zien, wordt het queryplan nu standaard in XML-indeling gegenereerd. Om het als een grafische weergave te openen, klikt u op de XML-uitvoer in het query_plan kolom zoals weergegeven in de bovenstaande afbeelding. Zodra u op de XML-uitvoer klikt, wordt het uitvoeringsplan geopend in een nieuw venster, zoals weergegeven in de volgende afbeelding:

Een lijst met tabellen met zeer gefragmenteerde indices krijgen met behulp van dynamische beheerweergaven en -functies

Laten we nog een voorbeeld bekijken. Ik wil een lijst met tabellen krijgen met indices die 50% of meer fragmentatie hebben in een bepaalde database. Om deze tabellen op te halen, moeten we de "sys.dm_db_index_physical_stats gebruiken ” weergave en de “sys.tables ” functie.

"Sys.tables ” is een dynamische beheerweergave die een lijst met tabellen in de specifieke database vult.

"sys.dm_db_index_physical_stats ” is een dynamische beheerfunctie die de volgende invoerparameters accepteert:

  1. Database-ID
  2. Object-ID
  3. Index-ID
  4. Partitienummer
  5. Modus

Het geeft gedetailleerde informatie over de fysieke status van de opgegeven index.

Om nu de lijst met gefragmenteerde indices te vullen, moeten we ons aansluiten bij "sys.dm_db_index_physical_stats ” en “sys.tables ” met CROSS APPLY. Voer de volgende query uit:

SELECT TABLES.NAME, INDEXSTATISTICS.ALLOC_UNIT_TYPE_DESC, CONVERT(NUMERIC(10, 2), INDEXSTATISTICS.AVG_FRAGMENTATION_IN_PERCENT) ALS PERCENTAGEFRAGMENTATION, INDEXSTATISTICS.PAGE_COUNT FROM SYS.S.TABLES ALS T , NULL, NULL, NULL) AS INDEXSTATISTICS WAAR INDEXSTATISTICS.DATABASE_ID =DB_ID() AND AVG_FRAGMENTATION_IN_PERCENT>=50 BESTELLEN DOOR INDEXSTATISTICS.AVG_FRAGMENTATION_IN_PERCENT DESC

De query moet de volgende uitvoer opleveren:

Samenvatting

In dit artikel hebben we de APPLY-operator behandeld, de variaties ervan - CROSS APPLY en OUTER APPLY en hoe u werkt. We hebben ook gezien hoe u ze kunt gebruiken om problemen met SQL-prestaties te identificeren met behulp van dynamische beheerweergaven en dynamische beheerfuncties.


  1. DB-testgegevens genereren

  2. Is het beter om meerdere databases met elk één schema te gebruiken, of één database met meerdere schema's?

  3. Quickbooks ODBC-stuurprogramma

  4. Hoe rangschikkingsposities van rijen in SQL toe te voegen met RANK()