Database
 sql >> Base de Dados >  >> RDS >> Database

SQL avançado:CROSS APPLY e OUTER APPLY


Neste artigo, veremos o operador “APPLY” e suas variações – CROSS APPLY e OUTER APPLY juntamente com exemplos de como eles podem ser usados.

Em particular, aprenderemos:
  • a diferença entre CROSS APPLY e a cláusula JOIN
  • como unir a saída de consultas SQL com funções avaliadas por tabela
  • como identificar problemas de desempenho consultando visualizações de gerenciamento dinâmico e funções de gerenciamento dinâmico.


O que é a cláusula APPLY


A Microsoft introduziu o operador APPLY no SQL Server 2005. O operador APPLY é semelhante à cláusula T-SQL JOIN, pois também permite unir duas tabelas – por exemplo, você pode unir uma tabela externa a uma tabela interna. O operador APPLY é uma boa opção quando, de um lado, temos uma expressão avaliada por tabela que queremos avaliar para cada linha da tabela que temos do outro lado. Assim, a tabela do lado direito é processada para cada linha da tabela do lado esquerdo. A tabela do lado esquerdo é avaliada primeiro e, em seguida, a tabela do lado direito é avaliada em relação a cada linha da tabela do lado esquerdo para gerar o conjunto de resultados final. O conjunto de resultados final inclui todas as colunas de ambas as tabelas.

O operador APPLY tem duas variações:
  • APLICAÇÃO CRUZADA
  • APLICAÇÃO EXTERNA

APLICAÇÃO CRUZADA


CROSS APPLY é semelhante a INNER JOIN, mas também pode ser usado para unir funções avaliadas por tabela com tabelas SQL. A saída final do CROSS APPLY consiste em registros correspondentes entre a saída de uma função avaliada por tabela e uma Tabela SQL.

APLICAÇÃO EXTERNA


OUTER APPLY se assemelha a LEFT JOIN, mas tem a capacidade de unir funções avaliadas por tabela com tabelas SQL. A saída final do OUTER APPLY contém todos os registros da tabela do lado esquerdo ou da função avaliada pela tabela, mesmo que eles não correspondam aos registros da tabela do lado direito ou da função com valor de tabela.

Agora, deixe-me explicar ambas as variações com exemplos.

Exemplos de uso

Preparando a configuração de demonstração


Para preparar uma configuração de demonstração, você precisará criar tabelas chamadas “Employees” e “Department” em um banco de dados que chamaremos de “DemoDatabase”. Para isso, execute o seguinte código:
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

Em seguida, insira alguns dados fictícios em ambas as tabelas. O script a seguir irá inserir dados no campo “Funcionário s " tabela:

[expandir título ="CONSULTA COMPLETA “]
INSERIR [DBO].[EMPLOYEES] ([EMPLOYEENAME], [BIRTHDATE], [JOBTITLE], [EMAILID], [PHONENUMBER], [HIREDATE], [DEPARTMENTID]) VALORES (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'exemplo @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'exemplo @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)

[/expandir]

Para adicionar dados ao nosso “Departamento ” , execute o seguinte script:
INSERIR [DBO].[DEPARTMENT] ([DEPARTMENTID], [DEPARTMENTNAME]) VALORES (1, N'IT'), (2, N'TECHNICAL'), (3, N'RESEARCH AND DEVELOPMENT') 
Agora, para verificar os dados, execute o código que você pode ver abaixo:
SELECT [EMPLOYEENAME], [BIRTHDATE], [JOBTITLE], [EMAILID], [PHONENUMBER], [HIREDATE], [DEPARTMENTID] FROM [EMPLOYEES] GOSELECT [DEPARTMENTID], [DEPARTMENTNAME] FROM [DEPARTMENT] GO 
Aqui está a saída desejada:


Criando e testando uma função avaliada por tabela


Como já mencionei, “CROSS APPLY ” e “APLICAÇÃO EXTERNA ” são usados ​​para unir tabelas SQL com funções avaliadas por tabela. Para demonstrar isso, vamos criar uma função avaliada por tabela chamada “getEmployeeData .” Esta função usará um valor do DepartmentID coluna como parâmetro de entrada e retornar todos os funcionários do departamento correspondente.

Para criar a função, execute o seguinte script:
CREATE FUNCTION Getemployeesbydepartment (@DEPARTMENTID INT) RETURNS @EMPLOYEES TABLE ( EMPLOYEENAME VARCHAR (MAX), DATA DE NASCIMENTO, JOBTITLE VARCHAR(150), EMAILID VARCHAR(100), PHONENUMBER VARCHAR(20), HIREDATE DATETIME, DEPARTMENTID VARCHAR(500) ). 

Agora, para testar a função, vamos passar “1 ” como “ID do departamento ” para o “Receberempregadospordepartamento ” função. Para fazer isso, execute o script fornecido abaixo:
USE DEMODATABASEGOSELECT EMPLOYEENAME, BIRTHATE, JOBTITLE, EMAILID, PHONENUMBER, HIREDATE, DEPARTMENTIDFROM GETEMPLOYEESBYDEPARTMENT (1)

A saída deve ser a seguinte:


Juntar uma tabela com uma função avaliada por tabela usando CROSS APPLY


Agora, vamos tentar juntar a tabela Employees com o “Getemployeesbydepartment ” função avaliada por tabela usando CROSS APPLY . Como mencionei, o CROSS APPLY operador é semelhante à cláusula Join. Ele preencherá todos os registros da seção “Funcionário ” para a qual há linhas correspondentes na saída de “Getemployeesbydepartment ”.

Execute o seguinte script:
SELECIONE A.[EMPLOYEENAME], A.[DATA DE NASCIMENTO], A.[JOBTITLE], A.[EMAILID], A.[PHONENUMBER], A.[HIREDATE], B.[NOME DO DEPARTAMENTO] DO DEPARTAMENTO B CROSS APPLY GETEMPLOYEESBYDEPARTMENT(B.DEPARTMENTID) A

A saída deve ser a seguinte:


Juntar uma tabela com uma função avaliada por tabela usando OUTER APPLY


Agora, vamos tentar juntar a tabela Employees com o “Getemployeesbydepartment ” função avaliada por tabela usando OUTER APPLY . Como mencionei antes, o OUTER APPLY operador se assemelha ao “OUTER JOIN ” cláusula. Ele preenche todos os registros da seção “Funcionário ” e a saída do “Getemployeesbydepartment ” função.

Execute o seguinte script:
SELECIONE A.[EMPLOYEENAME], A.[DATA DE NASCIMENTO], A.[JOBTITLE], A.[EMAILID], A.[PHONENUMBER], A.[HIREDATE], B.[DEPARTMENTNAME] DO DEPARTMENT B OUTER APPLY GETEMPLOYEESBYDEPARTMENT(B.DEPARTMENTID) A

Aqui está a saída que você deve ver como resultado:


Identificando problemas de desempenho usando funções e visualizações de gerenciamento dinâmico


Deixe-me mostrar-lhe um exemplo diferente. Aqui, veremos como obter um plano de consulta e o texto de consulta correspondente usando funções de gerenciamento dinâmico e visualizações de gerenciamento dinâmico.

Para fins de demonstração, criei uma tabela chamada “SmokeTestResults ” no “Banco de dados de demonstração”. Ele contém resultados de um teste de fumaça do aplicativo. Vamos imaginar que, por engano, um desenvolvedor execute uma consulta SQL para preencher os dados de “SmokeTestResults ” sem adicionar um filtro, o que reduz significativamente o desempenho do banco de dados.

Como um DBA, precisamos identificar a consulta com muitos recursos. Para fazer isso, usaremos o “sys.dm_exec_requests ” e a visualização “sys.dm_exec_sql_text ” função.

Sys.dm_exec_requests ” é uma exibição de gerenciamento dinâmico que fornece os seguintes detalhes importantes que podemos usar para identificar a consulta que consome recursos:
  1. ID da sessão
  2. Tempo de CPU
  3. Tipo de espera
  4. ID do banco de dados
  5. Lê (Físico)
  6. Gravações (Física)
  7. Leituras lógicas
  8. Manuseio SQL
  9. Identificação do plano
  10. Status da consulta
  11. Comando
  12. ID da transação

sys.dm_exec_sql_text ” é uma função de gerenciamento dinâmico que aceita um manipulador SQL como um parâmetro de entrada e fornece os seguintes detalhes:
  1. ID do banco de dados
  2. ID do objeto
  3. Está criptografado
  4. Texto de consulta SQL

Agora, vamos executar a seguinte consulta para gerar algum estresse no banco de dados ASAP. Execute a seguinte consulta:
USE ASAP GO SELECT TSID, USERID, EXECUTIONID, EX_RESULTFILE, EX_TESTDATAFILE, EX_ZIPFILE, EX_STARTTIME, EX_ENDTIME, EX_REMARKS FROM [ASAP].[DBO].[SMOKETESTRESULTS]

O SQL Server aloca um ID de sessão “66” e inicia a execução da consulta. Veja a seguinte imagem:



Agora, para solucionar o problema, exigimos o ID do banco de dados, leituras lógicas, SQL Consulta, Comando, ID da Sessão, Tipo de Espera e Identificador SQL . Como mencionei, podemos obter ID do banco de dados, leituras lógicas, comando, ID da sessão, tipo de espera emanuseio SQL de “sys.dm_exec_requests.” Para obter a Consulta SQL , devemos usar “sys.dm_exec_sql_text. ” É uma função de gerenciamento dinâmico, portanto, seria necessário ingressar em “sys.dm_exec_requests ” com “sys.dm_exec_sql_text ” usando CROSS APPLY.

Na janela Novo editor de consulta, execute a seguinte consulta:
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 
Deve produzir a seguinte saída:



Como você pode ver na captura de tela acima, a consulta retornou todas as informações necessárias para identificar o problema de desempenho.

Agora, além do texto da consulta, queremos obter o plano de execução que foi usado para executar a consulta em questão. Para fazer isso, usaremos o “sys.dm_exec_query_plan” função.

sys.dm_exec_query_plan ” é uma função de gerenciamento dinâmico que aceita um identificador de plano como um parâmetro de entrada e fornece os seguintes detalhes:
  1. ID do banco de dados
  2. ID do objeto
  3. Está criptografado
  4. Plano de consulta SQL em formato XML

Para preencher o plano de execução da consulta, devemos usar CROSS APPLY para unir “sys.dm_exec_requests ” e “sys.dm_exec_query_plan.

Abra a janela do editor de Nova Consulta e execute a seguinte consulta:
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

A saída deve ser a seguinte:



Agora, como você pode ver, o plano de consulta é gerado no formato XML por padrão. Para abri-lo como uma representação gráfica, clique na saída XML no query_plan coluna como mostrado na imagem acima. Depois de clicar na saída XML, o plano de execução será aberto em uma nova janela, conforme mostrado na imagem a seguir:


Obtendo uma lista de tabelas com índices altamente fragmentados usando visualizações e funções de gerenciamento dinâmico


Vejamos mais um exemplo. Quero obter uma lista de tabelas com índices que tenham 50% ou mais de fragmentação em um determinado banco de dados. Para recuperar essas tabelas, precisaremos usar o “sys.dm_db_index_physical_stats ” e a visualização “sys.tables ” função.

Sist.tabelas ” é uma visão de gerenciamento dinâmica que preenche uma lista de tabelas no banco de dados específico.

sys.dm_db_index_physical_stats ” é uma função de gerenciamento dinâmico que aceita os seguintes parâmetros de entrada:
  1. ID do banco de dados
  2. ID do objeto
  3. ID do índice
  4. Número da partição
  5. Modo

Ele retorna informações detalhadas sobre o status físico do índice especificado.

Agora, para preencher a lista de índices fragmentados, devemos juntar “sys.dm_db_index_physical_stats ” e “sys.tables ” usando CROSS APPLY. Execute a seguinte consulta:
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 SYSJ.DM_DB_INDEX_PHYSICAL_STATS (DB_ID(), TABLES. , NULL, NULL, NULL) AS INDEXSTATISTICS WHERE INDEXSTATISTICS.DATABASE_ID =DB_ID() E AVG_FRAGMENTATION_IN_PERCENT>=50 ORDER BY INDEXSTATISTICS.AVG_FRAGMENTATION_IN_PERCENT DESC

A consulta deve produzir a seguinte saída:


Resumo


Neste artigo, abordamos o operador APPLY, suas variações – CROSS APPLY e OUTER APPLY e como você funciona. Também vimos como você pode usá-los para identificar problemas de desempenho do SQL usando exibições de gerenciamento dinâmico e funções de gerenciamento dinâmico.