Database
 sql >> Base de données >  >> RDS >> Database

SQL avancé :CROSS APPLY et OUTER APPLY

Dans cet article, nous examinerons l'opérateur "APPLY" et ses variantes - CROSS APPLY et OUTER APPLY, ainsi que des exemples d'utilisation.

Nous apprendrons notamment :

  • la différence entre CROSS APPLY et la clause JOIN
  • comment joindre la sortie des requêtes SQL avec des fonctions évaluées par table
  • Comment identifier les problèmes de performances en interrogeant les vues de gestion dynamique et les fonctions de gestion dynamique

Ce qu'est la clause APPLY

Microsoft a introduit l'opérateur APPLY dans SQL Server 2005. L'opérateur APPLY est similaire à la clause T-SQL JOIN car il vous permet également de joindre deux tables - par exemple, vous pouvez joindre une table externe avec une table interne. L'opérateur APPLY est une bonne option lorsque, d'un côté, nous avons une expression évaluée par table que nous voulons évaluer pour chaque ligne de la table que nous avons de l'autre côté. Ainsi, la table de droite est traitée pour chaque ligne de la table de gauche. La table de gauche est évaluée en premier, puis la table de droite est évaluée par rapport à chaque ligne de la table de gauche pour générer le jeu de résultats final. Le jeu de résultats final inclut toutes les colonnes des deux tables.

L'opérateur APPLY a deux variantes :

  • CANDIDATURE CROISÉE
  • APPLICATION EXTÉRIEURE

CANDIDATURE CROISÉE

CROSS APPLY est similaire à INNER JOIN, mais peut également être utilisé pour joindre des fonctions évaluées par table avec des tables SQL. La sortie finale de CROSS APPLY consiste en des enregistrements correspondants entre la sortie d'une fonction évaluée par table et une table SQL.

APPLICATION EXTÉRIEURE

OUTER APPLY ressemble à LEFT JOIN, mais a la capacité de joindre des fonctions évaluées par table avec des tables SQL. La sortie finale de OUTER APPLY contient tous les enregistrements de la table de gauche ou de la fonction évaluée par table, même s'ils ne correspondent pas aux enregistrements de la table de droite ou de la fonction table.

Maintenant, laissez-moi vous expliquer les deux variantes avec des exemples.

Exemples d'utilisation

Préparer la configuration de démonstration

Pour préparer une configuration de démonstration, vous devrez créer des tables nommées "Employés" et "Département" dans une base de données que nous appellerons "DemoDatabase". Pour ce faire, exécutez le code suivant :

UTILISER 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 ) ALLER

Ensuite, insérez des données factices dans les deux tables. Le script suivant insère des données dans le champ "Employee s ” tableau :

[expand title =»REQUETE COMPLÈTE “]

INSERT [DBO].[EMPLOYEES] ([EMPLOYEENAME], [BIRTHDATE], [JOBTITLE], [EMAILID], [PHONENUMBER], [HIREDATE], [DEPARTMENTID]) VALEURS (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'example @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'INGÉNIEUR RECHERCHE ET DÉVELOPPEMENT', 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'INGÉNIEUR RECHERCHE ET DÉVELOPPEMENT', 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)

[/expand]

Pour ajouter des données à notre "Département ", exécutez le script suivant :

INSERT [DBO].[DEPARTMENT] ([DEPARTMENTID], [DEPARTMENTNAME]) VALUES (1, N'IT'), (2, N'TECHNICAL'), (3, N'RECHERCHE ET DEVELOPPEMENT') 

Maintenant, pour vérifier les données, exécutez le code que vous pouvez voir ci-dessous :

SELECT [EMPLOYEENAME], [BIRTHDATE], [JOBTITLE], [EMAILID], [PHONENUMBER], [HIREDATE], [DEPARTMENTID] FROM [EMPLOYEES] GOSELECT [DEPARTMENTID], [DEPARTMENTNAME] FROM [DEPARTMENT] GO 

Voici le résultat souhaité :

Créer et tester une fonction évaluée par table

Comme je l'ai déjà mentionné, "CROSS APPLY ” et “APPLIQUER EXTÉRIEUR ” sont utilisés pour joindre des tables SQL avec des fonctions évaluées par table. Pour le démontrer, créons une fonction évaluée par table nommée "getEmployeeData .” Cette fonction utilisera une valeur de DepartmentID comme paramètre d'entrée et renvoie tous les employés du service correspondant.

Pour créer la fonction, exécutez le script suivant :

CREATE FUNCTION Getemployeesbydepartment (@DEPARTMENTID INT) RETOURNE @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 

Maintenant, pour tester la fonction, nous allons passer "1 " comme "ID de service ” à la rubrique “Getemployeesbydepartment " une fonction. Pour cela, exécutez le script fourni ci-dessous :

UTILISEZ DEMODATABASEGOSELECT EMPLOYEENAME, BIRTHDATE, JOBTITLE, EMAILID, PHONENUMBER, HIREDATE, DEPARTMENTIDFROM GETTEMPLOYEESBYDEPARTMENT (1)

Le résultat devrait être le suivant :

Rejoindre une table avec une fonction évaluée par table à l'aide de CROSS APPLY

Maintenant, essayons de joindre la table Employés avec le "Getemployeesbydepartment ” fonction évaluée par table en utilisant CROSS APPLY . Comme je l'ai mentionné, la CROSS APPLY est similaire à la clause Join. Il remplira tous les enregistrements de la section "Employé ” pour laquelle il existe des lignes correspondantes dans la sortie de “Getemployeesbydepartment ”.

Exécutez le script suivant :

SELECT A.[EMPLOYEENAME], A.[BIRTHDATE], A.[JOBTITLE], A.[EMAILID], A.[PHONENUMBER], A.[HIREDATE], B.[DEPARTMENTNAME] FROM DEPARTMENT B CROSS APPLY GETTEMPLOYEESBYDEPARTMENT(B.DEPARTMENTID) A

Le résultat devrait être le suivant :

Rejoindre une table avec une fonction évaluée par table à l'aide de OUTER APPLY

Maintenant, essayons de joindre la table Employés avec le "Getemployeesbydepartment ” fonction évaluée par table en utilisant OUTER APPLY . Comme je l'ai déjà mentionné, l'APPLICATION EXTÉRIEURE ressemble à "OUTER JOIN ” clause. Il remplit tous les enregistrements de la section "Employé » et la sortie de la table « Getemployeesbydepartment ” fonction.

Exécutez le script suivant :

SELECT A.[EMPLOYEENAME], A.[BIRTHDATE], A.[JOBTITLE], A.[EMAILID], A.[PHONENUMBER], A.[HIREDATE], B.[DEPARTMENTNAME] FROM DEPARTMENT B OUTER APPLY GETTEMPLOYEESBYDEPARTMENT(B.DEPARTMENTID) A

Voici le résultat que vous devriez voir :

Identifier les problèmes de performances à l'aide de fonctions et de vues de gestion dynamiques

Permettez-moi de vous montrer un exemple différent. Ici, nous allons voir comment obtenir un plan de requête et le texte de requête correspondant en utilisant les fonctions de gestion dynamique et les vues de gestion dynamique.

À des fins de démonstration, j'ai créé un tableau nommé "SmokeTestResults ” dans la “DemoDatabase”. Il contient les résultats d'un test de fumée d'application. Imaginons que, par erreur, un développeur exécute une requête SQL pour remplir les données de "SmokeTestResults ” sans ajouter de filtre, ce qui réduit considérablement les performances de la base de données.

En tant que DBA, nous devons identifier la requête gourmande en ressources. Pour ce faire, nous allons utiliser les "sys.dm_exec_requests ” et la vue “sys.dm_exec_sql_text ” fonction.

« Sys.dm_exec_requests ” est une vue de gestion dynamique qui fournit les détails importants suivants que nous pouvons utiliser pour identifier la requête consommatrice de ressources :

  1. Identifiant de session
  2. Temps CPU
  3. Type d'attente
  4. ID de base de données
  5. Lectures (physiques)
  6. Écritures (physiques)
  7. Lectures logiques
  8. Descripteur SQL
  9. Positionner le plan
  10. État de la requête
  11. Commande
  12. Identifiant de transaction

« sys.dm_exec_sql_text ” est une fonction de gestion dynamique qui accepte un handle SQL comme paramètre d'entrée et fournit les détails suivants :

  1. ID de base de données
  2. ID d'objet
  3. Est chiffré
  4. Texte de la requête SQL

Maintenant, exécutons la requête suivante pour générer un peu de stress sur la base de données ASAP. Exécutez la requête suivante :

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

SQL Server alloue un ID de session "66" et démarre l'exécution de la requête. Voir l'image suivante :

Maintenant, pour résoudre le problème, nous avons besoin de l' ID de base de données, des lectures logiques, SQL Requête, Commande, ID de session, Type d'attente et Descripteur SQL . Comme je l'ai mentionné, nous pouvons obtenir ID de base de données, lectures logiques, commande, ID de session, type d'attente et handle SQL à partir de "sys.dm_exec_requests". Pour obtenir la requête SQL , nous devons utiliser "sys.dm_exec_sql_text. " C'est une fonction de gestion dynamique, il faudrait donc rejoindre " sys.dm_exec_requests ” par “sys.dm_exec_sql_text ” en utilisant CROSS APPLY.

Dans la fenêtre Nouvel éditeur de requête, exécutez la requête suivante :

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 

Il devrait produire la sortie suivante :

Comme vous pouvez le voir dans la capture d'écran ci-dessus, la requête a renvoyé toutes les informations requises pour identifier le problème de performances.

Maintenant, en plus du texte de la requête, nous voulons obtenir le plan d'exécution qui a été utilisé pour exécuter la requête en question. Pour ce faire, nous utiliserons le "sys.dm_exec_query_plan" fonction.

« sys.dm_exec_query_plan ” est une fonction de gestion dynamique qui accepte un descripteur de plan comme paramètre d'entrée et fournit les détails suivants :

  1. ID de base de données
  2. ID d'objet
  3. Est chiffré
  4. Plan de requête SQL au format XML

Pour remplir le plan d'exécution de la requête, nous devons utiliser CROSS APPLY pour rejoindre "sys.dm_exec_requests ” et “sys.dm_exec_query_plan.

Ouvrez la fenêtre de l'éditeur Nouvelle requête et exécutez la requête suivante :

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

Le résultat devrait être le suivant :

Maintenant, comme vous pouvez le voir, le plan de requête est généré par défaut au format XML. Pour l'ouvrir sous forme de représentation graphique, cliquez sur la sortie XML dans le query_plan colonne comme indiqué dans l'image ci-dessus. Une fois que vous aurez cliqué sur la sortie XML, le plan d'exécution s'ouvrira dans une nouvelle fenêtre comme illustré dans l'image suivante :

Obtenir une liste de tables avec des index très fragmentés en utilisant des vues et des fonctions de gestion dynamique

Voyons un autre exemple. Je souhaite obtenir une liste de tables avec des indices qui ont 50% ou plus de fragmentation dans une base de données donnée. Pour récupérer ces tables, nous devrons utiliser le "sys.dm_db_index_physical_stats » et la vue « sys.tables ” fonction.

"Sys.tables ” est une vue de gestion dynamique qui remplit une liste de tables sur la base de données spécifique.

« sys.dm_db_index_physical_stats ” est une fonction de gestion dynamique qui accepte les paramètres d'entrée suivants :

  1. ID de base de données
  2. ID d'objet
  3. ID d'index
  4. Numéro de partition
  5. Mode

Il renvoie des informations détaillées sur l'état physique de l'index spécifié.

Maintenant, pour remplir la liste des index fragmentés, nous devons joindre "sys.dm_db_index_physical_stats ” et “sys.tables ” en utilisant CROSS APPLY. Exécutez la requête suivante :

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 requête doit produire le résultat suivant :

Résumé

Dans cet article, nous avons couvert l'opérateur APPLY, ses variations - CROSS APPLY et OUTER APPLY et comment ton travail. Nous avons également vu comment vous pouvez les utiliser pour identifier les problèmes de performances SQL à l'aide des vues de gestion dynamique et des fonctions de gestion dynamique.