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

SQL avanzato:APPLICAZIONE CROSS e APPLICAZIONE ESTERNA

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:

  1. ID sessione
  2. Tempo CPU
  3. Tipo di attesa
  4. ID database
  5. Legge (fisico)
  6. Scrive (fisiche)
  7. Letture logiche
  8. Gestione SQL
  9. Maniglia del piano
  10. Stato della query
  11. Comando
  12. 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:

  1. ID database
  2. ID oggetto
  3. È crittografato
  4. 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:

  1. ID database
  2. ID oggetto
  3. È crittografato
  4. 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:

  1. ID database
  2. ID oggetto
  3. ID indice
  4. Numero partizione
  5. 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.