Dicas SQL Server

SQL Server Logo thumb Dicas SQL ServerHoje vou falar sobre alguns problemas que detectei além de algumas facetas que uso no meu dia a dia.

 

“Parameter Sniffing”

Algumas situações fazem com que o SQL Server Engine não consiga mapear o melhor plano de execução nas consultas feitas dentro de Stored Procedures, passando os parâmetros na cláusula where.

 

Exemplo:

CREATE PROC [dbo].[spExemplo] (@pcModelo VARCHAR (3) = 'MOD')
AS SELECT *
FROM [dbo].[Tabela] TAB (NOLOCK)
WHERE (TAB.Modelo = @pcModelo OR @pcModelo = 'MOD');

Este é um típico caso onde o engine do SQL Server não consegue encontrar o melhor plano de execução. Para entender mais sobre o problema, procure sobre “Parameter Sniffing”.

Como resolvemos?

Existem duas formas. Ou você usa a cláusula Option com a opção Optimize for Unknown. Ou declara variáveis locais e coloca a variável local na cláusula Where ao invés do parâmetro da Stored Procedure.

Solução 1:

CREATE PROC [dbo].[spExemplo] (@pcModelo VARCHAR (3) = 'MOD')
AS SELECT * FROM [dbo].[Tabela] TAB (NOLOCK)
WHERE (TAB.Modelo = @pcModelo OR @pcModelo = 'MOD')
OPTION (OPTIMIZE FOR UNKNOWN);

Solução 2:

CREATE PROC [dbo].[spExemplo] (@pcModelo VARCHAR (3) = 'MOD')
AS
DECLARE @lModelo AS VARCHAR (3);
SET @lModelo = @pcModelo;

SELECT *
FROM [dbo].[Tabela] TAB (NOLOCK)
WHERE (TAB.Modelo = @lModelo OR @lModelo = 'MOD');
Pesquisa de strings dentro de Stored Procedures
CREATE FUNCTION [dbo].[fnCountChar] (
@String AS TEXT,
@charToFind AS VARCHAR(100) )
RETURNS INT
AS BEGIN
DECLARE @Count AS INT = 0;
DECLARE @Lastpos As INT = CHARINDEX(@charToFind, @String);
IF (@Lastpos>0) BEGIN
  SET @Count = 1;
  WHILE @Lastpos >0 BEGIN
    SET @Count = @Count + 1;
    SET @Lastpos = CHARINDEX(@charToFind, @String, @Lastpos+1);
  END;
END;
RETURN @Count;
END
GO

DECLARE @String AS VARCHAR(8000) = 'CARACTER_A_SER_PROCURADO';
SELECT o.[name], line=[dbo].[fnCountChar](SUBSTRING(c.[text],0,CHARINDEX(@String, c.[text])),CHAR(13)),[text]=SUBSTRING(REPLACE(REPLACE(REPLACE(REPLACE(c.[text], CHAR(13), ' '), CHAR(10), ''), CHAR(9), ''), ' ', ' '),CHARINDEX(@String, REPLACE(REPLACE(REPLACE(REPLACE(c.[text], CHAR(13), '|'), CHAR(10), ''), CHAR(9), ' '), ' ', ' '))-50, 100) FROM MSCIS_Temp.sys.syscomments c JOIN MSCIS_Temp.sys.sysobjects o on o.id = c.id WHERE o.[xtype] = 'P' AND REPLACE(REPLACE(REPLACE(c.[text],CHAR(13),' '),CHAR(10),' '),CHAR(9),' ') like '%' + @String + '%';
GO

DROP FUNCTION [dbo].[fnCountChar];
Executando uma Stored Procedure em um Select From
SELECT *
FROM OPENQUERY(sszsdev06,'exec [Database].[dbo].[StoredProcedure] 111,''TEXTO''');

Note que:

‘exec [Database].[dbo].[StoredProcedure] 111,”TEXTO”’

É o mesmo que:

exec [Database].[dbo].[StoredProcedure] 111,’TEXTO’

 

Fazendo split de string delimitada
DECLARE @sNomes varchar(max)= 'Alex,Pimenta,Desenvolvedor,SQL,DOT NET';
DECLARE @sDelimitador CHAR(1) = ',';
DECLARE @xml XML = N'<root><strings>' + replace(@sNomes,@sDelimitador,'</strings><strings>') + '</strings></root>';
SELECT Tab.S.value('.','varchar(17)') AS [Coluna] FROM @xml.nodes('//root/strings') as Tab(S);

 

Pivot Table – Consultas Cruzadas.

Imaginem que temos uma tabela de alunos e cada aluno tem suas notas mensalmente.

Uma consulta neste cenário nos retornaria o resultado linearmente, repetindo o nome de cada aluno com suas notas mensalmente. Existe uma forma de mostrarmos uma linha com o nome do aluno e suas notas mensalmente em colunas. Para isso usamos o Pivot Table.

Vamos demonstrar. Primeiro, preparamos as tabelas:

CREATE TABLE tb_temp_alunos (
id INT NOT NULL PRIMARY KEY,
nome VARCHAR (50));
GO

INSERT INTO tb_temp_alunos VALUES (1, 'Alex Pimenta');
INSERT INTO tb_temp_alunos VALUES (2, 'Thatiane Pimenta');
INSERT INTO tb_temp_alunos VALUES (3, 'Andressa Pimenta');
INSERT INTO tb_temp_alunos VALUES (4, 'Estela Pimenta');
INSERT INTO tb_temp_alunos VALUES (5, 'Maria Pimenta');
GO

CREATE TABLE tb_temp_notas (
id INT NOT NULL IDENTITY (1,1) PRIMARY KEY,
id_nome INT NOT NULL,
mes VARCHAR(20),
nota INT,
FOREIGN KEY (id_nome) REFERENCES tb_temp_alunos);
GO

INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (1, 'Janeiro', 10);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (1, 'Fevereiro', 9);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (1, 'Março', 8);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (1, 'Abril', 5);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (1, 'Maio', 9);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (1, 'Junho', 10);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (2, 'Janeiro', 5);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (2, 'Fevereiro', 6);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (2, 'Março', 7);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (2, 'Abril', 5);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (2, 'Maio', 9);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (2, 'Junho', 8);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (3, 'Janeiro', 4);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (3, 'Fevereiro', 2);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (3, 'Março', 8);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (3, 'Abril', 10);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (3, 'Maio', 10);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (3, 'Junho', 10);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (4, 'Janeiro', 1);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (4, 'Fevereiro', 2);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (4, 'Março', 5);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (4, 'Abril', 8);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (4, 'Maio', 9);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (4, 'Junho', 10);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (5, 'Janeiro', 1);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (5, 'Fevereiro', 1);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (5, 'Março', 10);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (5, 'Abril', 10);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (5, 'Maio', 9);
INSERT INTO tb_temp_notas (id_nome, mes, nota) VALUES (5, 'Junho', 9);
GO

Agora se fizermos um consulta teremos o resultado linear:

SELECT nm.id, nome, mes, nota
FROM tb_temp_alunos nm
JOIN tb_temp_notas nt ON (nt.id_nome=nm.id)
Id nome mes nota
1 Alex Pimenta Janeiro 10
1 Alex Pimenta Fevereiro 9
1 Alex Pimenta Março 8
1 Alex Pimenta Abril 5
1 Alex Pimenta Maio 9
1 Alex Pimenta Junho 10
2 Thatiane Pimenta Janeiro 5
2 Thatiane Pimenta Fevereiro 6
2 Thatiane Pimenta Março 7
2 Thatiane Pimenta Abril 5
2 Thatiane Pimenta Maio 9
2 Thatiane Pimenta Junho 8
3 Andressa Pimenta Janeiro 4
3 Andressa Pimenta Fevereiro 2
3 Andressa Pimenta Março 8
3 Andressa Pimenta Abril 10
3 Andressa Pimenta Maio 10
3 Andressa Pimenta Junho 10
4 Estela Pimenta Janeiro 1
4 Estela Pimenta Fevereiro 2
4 Estela Pimenta Março 5
4 Estela Pimenta Abril 8
4 Estela Pimenta Maio 9
4 Estela Pimenta Junho 10
5 Maria Pimenta Janeiro 1
5 Maria Pimenta Fevereiro 1
5 Maria Pimenta Março 10
5 Maria Pimenta Abril 10
5 Maria Pimenta Maio 9
5 Maria Pimenta Junho 9

Nosso objetivo é tornar a visão agrupado por usuário, mostrando suas notas mensalmente na horizontal.

DECLARE @COLUMNS VARCHAR (6000) = STUFF(( SELECT DISTINCT TOP 100 PERCENT
'],[' + nm.mes FROM tb_temp_notas nm ORDER BY '],[' + nm.mes FOR XML PATH('')), 1, 2, '') + ']';
DECLARE @SQL VARCHAR(max) = 'select * from (select nm.id, nome, mes, nota from tb_temp_alunos nm join tb_temp_notas nt on (nt.id_nome=nm.id)) tbl pivot (sum(nota) for tbl.mes in (' + @COLUMNS + ')) pvt';

EXEC(@SQL);
Id Nome Abril Fevereiro Janeiro Junho Maio Março
1 Alex Pimenta 5 9 10 10 9 8
2 Thatiane Pimenta 5 6 5 8 9 7
3 Andressa Pimenta 10 2 4 10 10 8
4 Estela Pimenta 8 2 1 10 9 8
5 Maria Pimenta 10 1 1 9 9 10

Deixe uma resposta

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

SEO Powered by Platinum SEO from Techblissonline