Estou fazendo index scan sim, idaí?


Complementando o Post Procurando por conversão implícita sobre a apresentação do Marcos Freccia (Blog | Twitter)  “10 coisas que todo desenvolvedor deveria saber sobre SQL Server

Um dos grandes problemas dessa conversão é o SQL utilizar index scan ao invés de index seek.

Esse código mostra consultas que estão executado Index Scan por motivos de Conversões Implícitas.

with XMLNAMESPACES
('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as sql)
select
total_worker_time/execution_count AS AvgCPU
, total_elapsed_time/execution_count AS AvgDuration
, (total_logical_reads+total_physical_reads)/execution_count AS AvgReads
, execution_count
, SUBSTRING(st.TEXT, (qs.statement_start_offset/2)+1 , ((CASE
qs.statement_end_offset WHEN -1 THEN datalength(st.TEXT) ELSE
qs.statement_end_offset END - qs.statement_start_offset)/2) + 1) AS txt
, qs.max_elapsed_time
, db_name(qp.dbid) as database_name
, quotename(object_schema_name(qp.objectid, qp.dbid)) + N'.' +
quotename(object_name(qp.objectid, qp.dbid)) as obj_name
, qp.query_plan.value(
N'(/sql:ShowPlanXML/sql:BatchSequence/sql:Batch/sql:Statements/sql:StmtSimple[@StatementType = "SELECT"]/sql:QueryPlan/sql:RelOp/descendant::*/sql:ScalarOperator[contains(@ScalarString, "CONVERT_IMPLICIT")])[1]/@ScalarString', 'nvarchar(4000)' ) as scalar_string
, qp.query_plan
from sys.dm_exec_query_stats as qs
cross apply sys.dm_exec_query_plan(qs.plan_handle) as qp
cross apply sys.dm_exec_sql_text(qs.sql_handle) st
where qp.query_plan.exist(
N'/sql:ShowPlanXML/sql:BatchSequence/sql:Batch/sql:Statements/sql:StmtSimple[@StatementType = "SELECT"]/sql:QueryPlan/sql:RelOp/sql:IndexScan/descendant::*/sql:ScalarOperator[contains(@ScalarString, "CONVERT_IMPLICIT")]' ) = 1;
Anúncios

T-SQL Tuesday #21 – Depois arrumo esse código,,,


Dessa vez o anfitrião do T-SQL Tuesday é o próprio idealizador Adam Machanic (Blog | Twitter).

E de uma forma diferente, não é na Terça-feira,,, por quê?

Porque não importa, desde que funcione pode ser zuado mesmo,,, o tópico desse mês é sobre “mostrar seu código lixo para o mundo”,,,

Todo mundo já escreveu um código uma vez ou outra,,,, todo o DBA tem aquele conjunto de scripts que o ajudam a identificar alguns problemas, fazer um tunning, arrumar alguma coisa,,, E com certeza todo mundo tem aquele código que hoje, pega para olha e começa a dar risada de como é que teve coragem de fazer uma coisa como aquela…

Meu exemplo é bem simples, O código é horrível, demora demais mas, funciona,,,

Ele troca os dados de posição de uma coluna específica.

WITH cteTableTel AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY NEWID()) AS n,
        SOBRENOME
    FROM CLIENTES
    )
UPDATE CLIENTES
   SET ENDERECO = (
       SELECT ENDERECO
       FROM cteTableTel
       WHERE cteTableTel.n = CLIENTES.ID)
Esse código funciona, não é nada bonito, não é performático, mas para a necessidade de uma base de treino com dados reais ele ajuda bastante,,,
Se você percebeu ele tem um problema,,, sabe qual é?
Como você garante que todas as linhas da tabela de clientes realmente estão em uma sequencia? Você pode ter apagado algum registro uma hora ou outra,,, Logo, haverá cliente que o endereço não vai ser atualizado,,
Já sei, você nem reparou nesse problema,,, você deve estar pensando: “por que ele esta fazendo um update na tabela de clientes se ele está usando uma CTE?”, ou também, “Legal, ele está fazendo isso em uma tabela que tem algum tipo de ID e quando tem aquelas tabelas que não da pra usar nada de referencia?”
Como eu disse, ele não é performático, e eu nem tinha me atentado nesses detalhes na época,,,  Houveram duas situações que me refizeram rever esse código: o primeiro caso dos usuários que não tinham os dados alteradores e o último… ai com muita vontade resolvi reescrever,,,
E ele ficou mais ou menos assim:
;WITH cte AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY NEWID()) AS n1,
        ROW_NUMBER() OVER (order by SOBRENOME) AS n2,
        SOBRENOME
    FROM CLIENTES
    )
UPDATE c1
   SET SOBRENOME = c2.SOBRENOME
   from cte as c1
   inner join cte as c2
   on c1.n1 = c2.n2

Dessa vez não tem problema com o usuário sem o dado alterado, performance melhorada em quase 90% e pouco importa se a tabela em algum registro para referencia,,,

Eu comparei a execução dos 2 códigos em uma tabela com um pouco mais de  3 milhões de linhas e o segundo código demorou quase 4 horas para finalizar,,, já o primeiro código eu parei a execução dele depois de 2 SEMANAS executando.

Tenho códigos piores no meu repositório? claro que sim. Esse é apenas um exemplo de um dos piores que já fiz…

Qual é o seu? tem coragem mostrar?

Procurando por conversão implícita


Assistindo ontem a apresentação do Marcos Freccia (Blog | Twitter) sobre “10 coisas que todo desenvolvedor deveria saber sobre SQL Server” alguém perguntou como ver as conversões implícitas que estão sendo executadas no SQL,,, ou alguma coisa assim,,,

Então, segue um script rapidão que mostra as conversões,,, o chato desse script é que ele tem que ser executado por banco,,,

Vou tentar montar um outro que traga a informação de todos os bancos,,,

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 

DECLARE @dbname SYSNAME
SET @dbname = QUOTENAME(DB_NAME()); 

WITH XMLNAMESPACES
   (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT
   stmt.value('(@StatementText)[1]', 'varchar(max)'),
   t.value('(ScalarOperator/Identifier/ColumnReference/@Schema)[1]', 'varchar(128)'),
   t.value('(ScalarOperator/Identifier/ColumnReference/@Table)[1]', 'varchar(128)'),
   t.value('(ScalarOperator/Identifier/ColumnReference/@Column)[1]', 'varchar(128)'),
   ic.DATA_TYPE AS ConvertFrom,
   ic.CHARACTER_MAXIMUM_LENGTH AS ConvertFromLength,
   t.value('(@DataType)[1]', 'varchar(128)') AS ConvertTo,
   t.value('(@Length)[1]', 'int') AS ConvertToLength,
   query_plan
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp
CROSS APPLY query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS batch(stmt)
CROSS APPLY stmt.nodes('.//Convert[@Implicit="1"]') AS n(t)
JOIN INFORMATION_SCHEMA.COLUMNS AS ic
   ON QUOTENAME(ic.TABLE_SCHEMA) = t.value('(ScalarOperator/Identifier/ColumnReference/@Schema)[1]', 'varchar(128)')
   AND QUOTENAME(ic.TABLE_NAME) = t.value('(ScalarOperator/Identifier/ColumnReference/@Table)[1]', 'varchar(128)')
   AND ic.COLUMN_NAME = t.value('(ScalarOperator/Identifier/ColumnReference/@Column)[1]', 'varchar(128)')
WHERE t.exist('ScalarOperator/Identifier/ColumnReference[@Database=sql:variable("@dbname")][@Schema!="[sys]"]') = 1