In questo articolo, esamineremo l'operatore "APPLICA" e le sue variazioni:APPLICAZIONE CROSS e APPLICAZIONE ESTERNA insieme a esempi di come possono essere utilizzati.
In particolare impareremo:
- la differenza tra CROSS APPLY e la clausola JOIN
- come unire l'output di query SQL con funzioni valutate da tabella
- come identificare i problemi di prestazioni interrogando viste di gestione dinamica e funzioni di gestione dinamica.
Cos'è la clausola APPLY
Microsoft ha introdotto l'operatore APPLY in SQL Server 2005. L'operatore APPLY è simile alla clausola T-SQL JOIN in quanto consente anche di unire due tabelle, ad esempio è possibile unire una tabella esterna con una tabella interna. L'operatore APPLY è una buona opzione quando, da un lato, abbiamo un'espressione valutata da una tabella che vogliamo valutare per ogni riga della tabella che abbiamo su un altro lato. Quindi, la tabella di destra viene elaborata per ogni riga della tabella di sinistra. Viene prima valutata la tabella di sinistra, quindi la tabella di destra viene valutata rispetto a ciascuna riga della tabella di sinistra per generare il set di risultati finale. Il set di risultati finale include tutte le colonne di entrambe le tabelle.
L'operatore APPLY ha due varianti:
- APPLICAZIONE INCROCIATA
- APPLICAZIONE ESTERNA
APPLICAZIONE INCROCIATA
CROSS APPLY è simile a INNER JOIN, ma può essere utilizzato anche per unire funzioni valutate da tabella con tabelle SQL. L'output finale di CROSS APPLY è costituito da record corrispondenti tra l'output di una funzione valutata da una tabella e una tabella SQL.
APPLICAZIONE ESTERNA
OUTER APPLY assomiglia a LEFT JOIN, ma ha la capacità di unire funzioni valutate da tabella con tabelle SQL. L'output finale di OUTER APPLY contiene tutti i record della tabella di sinistra o della funzione valutata in tabella, anche se non corrispondono ai record della tabella di destra o della funzione valutata in tabella.
Ora, lascia che ti spieghi entrambe le varianti con esempi.
Esempi di utilizzo
Preparazione della configurazione demo
Per preparare una configurazione demo, dovrai creare tabelle denominate "Employees" e "Department" in un database che chiameremo "DemoDatabase". Per farlo, esegui il seguente codice:
USE DEMODATABASE GO CREATE TABLE [DBO].[EMPLOYEES] ( [EMPLOYEENAME] [VARCHAR](MAX) NULL, [BIRTHDATE] [DATETIME] NULL, [JOBTITLE] [VARCHAR](150) NULL, [EMAILID] [VARCHAR](100) NULL, [PHONENUMBER] [VARCHAR](20) NULL, [HIREDATE] [DATETIME] NULL, [DEPARTMENTID] [INT] NULL ) GO CREATE TABLE [DBO].[DEPARTMENT] ( [DEPARTMENTID] INT IDENTITY (1, 1), [DEPARTMENTNAME] [VARCHAR](MAX) NULL ) GO
Quindi, inserisci alcuni dati fittizi in entrambe le tabelle. Il seguente script inserirà i dati nel "Dipendente s ” tabella:
[expand title =”QUERY COMPLETA “]
INSERT [DBO].[EMPLOYEES] ([EMPLOYEENAME], [BIRTHDATE], [JOBTITLE], [EMAILID], [PHONENUMBER], [HIREDATE], [DEPARTMENTID]) VALUES (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'[email protected]', 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'[email protected]', 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'[email protected]', N'330-555-2568', CAST(N'2009-05-03T00:00:00.000' AS DATETIME), 3)
[/espandi]
Per aggiungere dati al nostro “Reparto ” tabella, esegui il seguente script:
INSERT [DBO].[DEPARTMENT] ([DEPARTMENTID], [DEPARTMENTNAME]) VALUES (1, N'IT'), (2, N'TECHNICAL'), (3, N'RESEARCH AND DEVELOPMENT')
Ora, per verificare i dati, esegui il codice che puoi vedere di seguito:
SELECT [EMPLOYEENAME], [BIRTHDATE], [JOBTITLE], [EMAILID], [PHONENUMBER], [HIREDATE], [DEPARTMENTID] FROM [EMPLOYEES] GO SELECT [DEPARTMENTID], [DEPARTMENTNAME] FROM [DEPARTMENT] GO
Ecco l'output desiderato:
Creazione e test di una funzione valutata da tabella
Come ho già detto, “CROSS APPLY ” e “APPLICAZIONE ESTERNA ” vengono utilizzati per unire tabelle SQL con funzioni valutate da tabella. Per dimostrarlo, creiamo una funzione valutata da una tabella denominata "getEmployeeData .” Questa funzione utilizzerà un valore da DepartmentID colonna come parametro di input e restituisce tutti i dipendenti del reparto corrispondente.
Per creare la funzione, eseguire il seguente script:
CREATE FUNCTION Getemployeesbydepartment (@DEPARTMENTID INT) RETURNS @EMPLOYEES TABLE ( EMPLOYEENAME VARCHAR (MAX), BIRTHDATE DATETIME, JOBTITLE VARCHAR(150), EMAILID VARCHAR(100), PHONENUMBER VARCHAR(20), HIREDATE DATETIME, DEPARTMENTID VARCHAR(500)) AS BEGIN INSERT INTO @EMPLOYEES SELECT A.EMPLOYEENAME, A.BIRTHDATE, A.JOBTITLE, A.EMAILID, A.PHONENUMBER, A.HIREDATE, A.DEPARTMENTID FROM [EMPLOYEES] A WHERE A.DEPARTMENTID = @DEPARTMENTID RETURN END
Ora, per testare la funzione, passeremo "1 ” come “ID reparto ” al “Getemployeesbydepartment " funzione. Per fare ciò, esegui lo script fornito di seguito:
USE DEMODATABASE GO SELECT EMPLOYEENAME, BIRTHDATE, JOBTITLE, EMAILID, PHONENUMBER, HIREDATE, DEPARTMENTID FROM GETEMPLOYEESBYDEPARTMENT (1)
L'output dovrebbe essere il seguente:
Unire una tabella con una funzione valutata da una tabella utilizzando CROSS APPLY
Ora, proviamo a unirci alla tabella Dipendenti con il "Getemployeesbydepartment ” funzione valutata in tabella utilizzando CROSS APPLY . Come ho già detto, la CROSS APPLY operatore è simile alla clausola Join. Popolerà tutti i record del "Dipendente ” per la quale sono presenti righe corrispondenti nell'output di “Getemployeesbydepartment ”.
Esegui il seguente script:
SELECT A.[EMPLOYEENAME], A.[BIRTHDATE], A.[JOBTITLE], A.[EMAILID], A.[PHONENUMBER], A.[HIREDATE], B.[DEPARTMENTNAME] FROM DEPARTMENT B CROSS APPLY GETEMPLOYEESBYDEPARTMENT(B.DEPARTMENTID) A
L'output dovrebbe essere il seguente:
Unire una tabella con una funzione valutata da una tabella utilizzando OUTER APPLY
Ora, proviamo a unirci alla tabella Employees con il "Getemployeesbydepartment ” funzione valutata in tabella utilizzando APPLICAZIONE ESTERNA . Come ho detto prima, l'APPLICAZIONE ESTERNA è simile all'operatore "OUTER JOIN clausola. Popola tutti i record di "Dipendente ” e l'output del “Getemployeesbydepartment funzione ".
Esegui il seguente script:
SELECT A.[EMPLOYEENAME], A.[BIRTHDATE], A.[JOBTITLE], A.[EMAILID], A.[PHONENUMBER], A.[HIREDATE], B.[DEPARTMENTNAME] FROM DEPARTMENT B OUTER APPLY GETEMPLOYEESBYDEPARTMENT(B.DEPARTMENTID) A
Ecco l'output che dovresti vedere come risultato:
Identificazione dei problemi di prestazioni utilizzando funzioni e viste di gestione dinamica
Lascia che ti mostri un esempio diverso. Qui vedremo come ottenere un piano di query e il testo della query corrispondente utilizzando le funzioni di gestione dinamica e le viste a gestione dinamica.
A scopo dimostrativo, ho creato una tabella denominata "SmokeTestResults ” nel “DemoDatabase”. Contiene i risultati di un test del fumo dell'applicazione. Immaginiamo che, per errore, uno sviluppatore esegua una query SQL per popolare i dati da “SmokeTestResults ” senza aggiungere un filtro, che riduce notevolmente le prestazioni del database.
Come DBA, dobbiamo identificare la query pesante. Per fare ciò, utilizzeremo "sys.dm_exec_requests ” e la vista “sys.dm_exec_sql_text funzione ".
“Sys.dm_exec_requests ” è una visualizzazione a gestione dinamica che fornisce i seguenti dettagli importanti che possiamo utilizzare per identificare la query che consuma risorse:
- ID sessione
- Tempo CPU
- Tipo di attesa
- ID database
- Legge (fisico)
- Scrive (fisiche)
- Letture logiche
- Gestione SQL
- Maniglia del piano
- Stato della query
- Comando
- ID transazione
“sys.dm_exec_sql_text ” è una funzione di gestione dinamica che accetta un handle SQL come parametro di input e fornisce i seguenti dettagli:
- ID database
- ID oggetto
- È crittografato
- Testo query SQL
Ora, eseguiamo la seguente query per generare un po' di stress sul database ASAP. Esegui la seguente query:
USE ASAP GO SELECT TSID, USERID, EXECUTIONID, EX_RESULTFILE, EX_TESTDATAFILE, EX_ZIPFILE, EX_STARTTIME, EX_ENDTIME, EX_REMARKS FROM [ASAP].[DBO].[SMOKETESTRESULTS]
SQL Server assegna un ID di sessione "66" e avvia l'esecuzione della query. Vedi l'immagine seguente:
Ora, per risolvere il problema, abbiamo bisogno dell'ID database, letture logiche, SQL Query, comando, ID sessione, tipo di attesa e Gestore SQL . Come ho già detto, possiamo ottenere ID database, letture logiche, comando, ID sessione, tipo di attesa e handle SQL da "sys.dm_exec_requests". Per ottenere la Query SQL , dobbiamo usare “sys.dm_exec_sql_text. ” È una funzione di gestione dinamica, quindi è necessario unirsi a “sys.dm_exec_requests ” con “sys.dm_exec_sql_text ” utilizzando APPLICAZIONE CROSS.
Nella finestra Nuovo editor di query, esegui la seguente query:
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 A CROSS APPLY SYS.DM_EXEC_SQL_TEXT(A.SQL_HANDLE) B
Dovrebbe produrre il seguente output:
Come puoi vedere nello screenshot sopra, la query ha restituito tutte le informazioni necessarie per identificare il problema di prestazioni.
Ora, oltre al testo della query, vogliamo ottenere il piano di esecuzione utilizzato per eseguire la query in questione. Per fare ciò, utilizzeremo "sys.dm_exec_query_plan" funzione.
“sys.dm_exec_query_plan ” è una funzione di gestione dinamica che accetta un handle del piano come parametro di input e fornisce i seguenti dettagli:
- ID database
- ID oggetto
- È crittografato
- Piano di query SQL in formato XML
Per popolare il piano di esecuzione della query, dobbiamo utilizzare CROSS APPLY per unirci a "sys.dm_exec_requests " e "sys.dm_exec_query_plan. ”
Apri la finestra dell'editor Nuova query ed esegui la seguente query:
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 FROM SYS.DM_EXEC_REQUESTS A CROSS APPLY SYS.DM_EXEC_SQL_TEXT(A.SQL_HANDLE) B CROSS APPLY SYS.DM_EXEC_QUERY_PLAN (A.PLAN_HANDLE) C
L'output dovrebbe essere il seguente:
Ora, come puoi vedere, il piano di query viene generato in formato XML per impostazione predefinita. Per aprirlo come rappresentazione grafica, fai clic sull'output XML nel query_plan colonna come mostrato nell'immagine sopra. Dopo aver fatto clic sull'output XML, il piano di esecuzione verrà aperto in una nuova finestra come mostrato nell'immagine seguente:
Ottenere un elenco di tabelle con indici altamente frammentati utilizzando viste e funzioni a gestione dinamica
Vediamo un altro esempio. Voglio ottenere un elenco di tabelle con indici che hanno una frammentazione del 50% o più in un determinato database. Per recuperare queste tabelle, dovremo utilizzare "sys.dm_db_index_physical_stats ” e la vista “sys.tables funzione ".
“Sys.tables ” è una vista a gestione dinamica che popola un elenco di tabelle sul database specifico.
“sys.dm_db_index_physical_stats ” è una funzione di gestione dinamica che accetta i seguenti parametri di input:
- ID database
- ID oggetto
- ID indice
- Numero partizione
- Modalità
Restituisce informazioni dettagliate sullo stato fisico dell'indice specificato.
Ora, per popolare l'elenco degli indici frammentati, dobbiamo unirci a “sys.dm_db_index_physical_stats " e "sys.tables ” utilizzando APPLICAZIONE INCROCIATA. Esegui la seguente query:
SELECT TABLES.NAME, INDEXSTATISTICS.ALLOC_UNIT_TYPE_DESC, CONVERT(NUMERIC(10, 2), INDEXSTATISTICS.AVG_FRAGMENTATION_IN_PERCENT) AS PERCENTAGEFRAGMENTATION, INDEXSTATISTICS.PAGE_COUNT FROM SYS.TABLES AS TABLES CROSS APPLY SYS.DM_DB_INDEX_PHYSICAL_STATS (DB_ID(), TABLES.OBJECT_ID, NULL, NULL, NULL) AS INDEXSTATISTICS WHERE INDEXSTATISTICS.DATABASE_ID = DB_ID() AND AVG_FRAGMENTATION_IN_PERCENT >= 50 ORDER BY INDEXSTATISTICS.AVG_FRAGMENTATION_IN_PERCENT DESC
La query dovrebbe produrre il seguente output:
Riepilogo
In questo articolo, abbiamo trattato l'operatore APPLICA, le sue variazioni – APPLICAZIONE CROSS e APPLICAZIONE ESTERNA e come il tuo lavoro. Abbiamo anche visto come utilizzarli per identificare problemi di prestazioni SQL utilizzando le viste a gestione dinamica e le funzioni di gestione dinamica.