Checkup

Como consultor, muitas vezes me deparo com ambientes SQL que não fui eu que fiz as configurações, cada ambiente tem sua peculiaridade.
Este script serve para te ajudar a fazer uma checagem estrutural do SQL, entender o que existe neste ambiente e quais os riscos existentes.
Ele não irá modificar nada existente no seu ambiente, no máximo criará tabelas temporárias para coleta de resultado.
Ele não é um script do tipo cole e execute, coloquei alguns raisererror para impedir que você execute tudo de uma vez (estou montando outro que poderemos fazer isso).
Execute o script por partes, vá coletando os resultados para uma planilha excel para analisá-los depois…
***Lembrete: este é um script que você está copiando da internet, execute-o por sua conta e risco. Lembre-se: na dúvida, não confie em ninguém.
Novas versões serão modificadas nesta página e atualizado o link do skydrive e no meu NAS.
05/03/15 10:32 – Atualizada a parte do código que lista os jobs e os agendamentos
08/04/2020 17:45 – Atualizei apenas algumas coisas que estavam apresentando erro no script devido ao conversor do WordPress, coloquei uma formatação no script para forçar você a ter que copiar o texto para seu editor e ler.
22/04/2020 13:35 – com a atualização do WordPress para trabalhar com blocagem ele está apagando metade do código e removendo alguns operadores de SARG, vou adicionar o código sem a formatação de código. Eu seu que vai ficar muito feio, mas pelo menos não deve quebrar o código.
O arquivo com o código também está em http://www.doomtech.com.br/scriptSQL/checkup.sql
/* Versao: 1.07 Data: 20200408 Por: Ricardo Leka Site: https://leka.com.br email: ricardo@leka.com.br twitter:@bigleka Este script é para SQL 2005/2008/R2/2012/2014+ */ /* Esse script deve ser executado em pedaços, você executar apenas a parte que interessa e vai copiando o resultado para o excel ou bloco de notas */ raiserror ('Esse script é para ser executado por partes, você está executando tudo de uma vez,,,',20,-1) with log GO /* Vamos ver se você é importante o suficiente para rodar essas querys o nível necessário é sysadmin */ IF (IS_SRVROLEMEMBER('sysadmin') = 0) RAISERROR('Seu usuário não é importante o suficiente,,,', 15, -1); ELSE IF (IS_SRVROLEMEMBER('sysadmin') IS NULL) RAISERROR('A server role que seu usuário está não é valida,,,', 15, -1); ELSE IF (IS_SRVROLEMEMBER('sysadmin') = 1) PRINT 'Legal, podemos continuar,,,'; GO /* Versao do SQL Traz informacoes interessante sobre o nome da maquina, se tem instancia, versao de produto, SP, collation da instancia, se esta ou nao em cluster */ Print 'Versao de SQL' SELECT CAST(SERVERPROPERTY('MachineName') AS VARCHAR(30)) AS MachineName, CAST(SERVERPROPERTY('InstanceName') AS VARCHAR(30)) AS Instance, CAST(SERVERPROPERTY('ProductVersion') AS VARCHAR(30)) AS ProductVersion, CAST(SERVERPROPERTY('ProductLevel') AS VARCHAR(30)) AS ProductLevel, CAST(SERVERPROPERTY('Edition') AS VARCHAR(30)) AS Edition, (CASE SERVERPROPERTY('EngineEdition') WHEN 1 THEN 'Personal or Desktop' WHEN 2 THEN 'Standard' WHEN 3 THEN 'Enterprise' END ) AS EngineType, CAST(SERVERPROPERTY('LicenseType') AS VARCHAR(30)) AS LicenseType, SERVERPROPERTY('NumLicenses') AS #Licenses, (CASE SERVERPROPERTY('IsIntegratedSecurityOnly') WHEN 0 THEN 'Mista' WHEN 1 THEN 'Integrada' END ) AS [IsIntegratedSecurityOnly], SERVERPROPERTY('Collation') AS Collation, (CASE SERVERPROPERTY('IsClustered') WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS Cluster; SELECT @@VERSION AS [SQL Server Details]; exec xp_msver /* Backup FULL Uma das informacoes mais importantes, tem backup? */ Print 'Backup FULL' SELECT d.name, MAX(b.backup_finish_date) AS [ultima data do backup] FROM master.sys.databases d LEFT OUTER JOIN msdb.dbo.backupset b ON d.name = b.database_name AND b.type = 'D' WHERE d.database_id NOT IN ( 2, 3 ) GROUP BY d.name ORDER BY 2 DESC; /* Backup LOG Outra informacao muito importante,,,, */ Print 'Backup Log' SELECT d.name, MAX(b.backup_finish_date) AS [ultima data do backup] FROM master.sys.databases d LEFT OUTER JOIN msdb.dbo.backupset b ON d.name = b.database_name AND b.type = 'L' WHERE d.database_id NOT IN ( 2, 3 ) AND d.recovery_model <> 3 GROUP BY d.name ORDER BY 2 DESC; /* Localizacao dos backups Se houve backup, pra onde ele foi? */ Print 'Localizacao dos Backups' SELECT TOP 100 a.database_name, b.physical_device_name FROM msdb.dbo.backupmediafamily b INNER JOIN msdb.dbo.backupset a ON b.media_set_id = a.media_set_id WHERE a.type = 'D' ORDER BY a.backup_start_date DESC; SELECT TOP 100 a.database_name, b.physical_device_name FROM msdb.dbo.backupmediafamily b INNER JOIN msdb.dbo.backupset a ON b.media_set_id = a.media_set_id WHERE a.type = 'L' ORDER BY a.backup_start_date DESC; /* A quanto tempo existe historico de backup? No SQL 2k, havia um grande problema de performance quando o historico ficava muito grande. */ Print 'Backup History' SELECT TOP 1 backup_start_date FROM msdb.dbo.backupset WITH (NOLOCK) ORDER BY backup_set_id ASC; /* Informacoes sobre o SO (SQL Server 2008 R2 SP1 ou superior) para saber mais sobre Windows release (http://msdn.microsoft.com/en-us/library/ms724832(VS.85).aspx) para saber mais sobre SKU (http://msdn.microsoft.com/en-us/library/ms724358.aspx) */ SELECT windows_release, windows_service_pack_level, windows_sku, os_language_version FROM sys.dm_os_windows_info WITH (NOLOCK) OPTION (RECOMPILE); /* Informacoes sobre o Servico do SQL Server o principal ponto ver o service_account (SQL Server 2008 R2 SP1 ou superior) */ SELECT servicename, startup_type_desc, status_desc, last_startup_time, service_account, is_clustered, cluster_nodename FROM sys.dm_server_services WITH (NOLOCK) OPTION (RECOMPILE); /* Informacao sobre o fabricante do hardware se o errorlog nao foi reciclado */ EXEC xp_readerrorlog 0, 1, "Manufacturer"; /* Informacao sobre o processador */ EXEC xp_instance_regread 'HKEY_LOCAL_MACHINE', 'HARDWARE\DESCRIPTION\System\CentralProcessor\0', 'ProcessorNameString'; /* quantos processadores existem, quanto de memoria tem no servidor */ SELECT cpu_count, affinity_type_desc, (physical_memory_kb) / 1024 AS [memory in MB] FROM sys.dm_os_sys_info; /* quais processadores estao disponiveis para o SQL? */ SELECT cpu_id, status, is_online FROM sys.dm_os_schedulers WHERE status = 'VISIBLE ONLINE'; /* Informacoes no registro sobre o SQL */ SELECT registry_key, value_name, value_data FROM sys.dm_server_registry WITH (NOLOCK) OPTION (RECOMPILE); /* permicoes do usuario que inicia o SQL */ sp_configure 'show advanced options', 1 reconfigure sp_configure 'xp_cmdshell', 1 reconfigure xp_cmdshell "whoami /priv" sp_configure 'xp_cmdshell', 0 reconfigure sp_configure 'show advanced options', 0 reconfigure /* Se o SQL estiver em cluster qual o nome do host atual? */ SELECT NodeName FROM sys.dm_os_cluster_nodes WITH (NOLOCK) OPTION (RECOMPILE); /* Pega algumas informacoes interessantes como nome, owner, data de criação, dbid, Modo de Compatiblidade das bases, recovery model, versao e status ATENCAO: Lembre-se que a ideia do script eh de apenas ver o que tem, caso encontre alguma base com status diferente de online ele nao faz nada e na teoria ainda nao eh a hora de voce fazer. Apenas reporte. */ PRINT 'Nome, owner, dbcreate, dbid, cmptlvl, recovery, version, status'; SELECT name AS [NAME], SUSER_SNAME(owner_sid) AS [Owner], CONVERT(NVARCHAR(11), create_date) AS [Data Criacao], database_id AS [DBID], compatibility_level, DATABASEPROPERTYEX(name, 'recovery') AS [Recovery Model], page_verify_option_desc AS [Page Verify], (CASE is_auto_create_stats_on WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [is_auto_create_stats_on], (CASE is_auto_update_stats_on WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [is_auto_update_stats_on], (CASE is_auto_update_stats_async_on WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [is_auto_update_stats_async_on], (CASE is_parameterization_forced WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [is_parameterization_forced], snapshot_isolation_state_desc, (CASE is_read_committed_snapshot_on WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [is_read_committed_snapshot_on], (CASE is_auto_close_on WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [is_auto_close_on], (CASE is_auto_shrink_on WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [is_auto_shrink_on], DATABASEPROPERTYEX(name, 'status') AS [Status] FROM master.sys.databases; /* Quais traces estão habilitados no SQL? */ Print 'Quais Traces estão habilitados' DBCC TRACESTATUS /* Quando o SQL Server foi instalado? */ Print 'Quando o SQL Server foi instalado' SELECT @@SERVERNAME AS [Server Name], createdate AS [Instalacao do SQL Server] FROM sys.syslogins WHERE [sid] = 0x010100000000000512000000; /* Quando foi que o DBCC CHECKDB rodou pela última vez? Isso eh tao bom quando retorna alguma coisa,,, */ Print 'DBCC CHECKDB' CREATE TABLE #temp ( ParentObject VARCHAR(255), [Object] VARCHAR(255), Field VARCHAR(255), [Value] VARCHAR(255) ); CREATE TABLE #DBCCResults ( ServerName VARCHAR(255), DBName VARCHAR(255), LastCleanDBCCDate DATETIME ); EXEC master.dbo.sp_MSforeachdb @command1 = 'USE ? INSERT INTO #temp EXECUTE (''DBCC DBINFO WITH TABLERESULTS'')', @command2 = 'INSERT INTO #DBCCResults SELECT @@SERVERNAME, ''?'', Value FROM #temp WHERE Field = ''dbi_dbccLastKnownGood''', @command3 = 'TRUNCATE TABLE #temp'; SELECT DISTINCT ServerName, DBName, CASE LastCleanDBCCDate WHEN '1900-01-01 00:00:00.000' THEN 'Nunca rodou DBCC CHECKDB' ELSE CAST(LastCleanDBCCDate AS VARCHAR) END AS LastCleanDBCCDate FROM #DBCCResults ORDER BY 3; DROP TABLE #temp, #DBCCResults; /* Existem páginas corrompidas? */ Print 'Páginas corrompidas' SELECT * FROM msdb..suspect_pages; /* Essa query é apenas para SQL Server 2008 e SQL Server 2008 R2 quando você tem Mirror configurado ele vai analisar se houve necessidade de recuperação de página corrompida. */ Print 'Recuperaçãp de página usando o mirror' SELECT * FROM sys.dm_db_mirroring_auto_page_repair /* Quem faz parte do sysadmin ou securityadmin Quantas outras pessoas podem acessar o SQL e fazer algum tipo de estrago? */ SELECT l.name AS [Nome], (CASE l.denylogin WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [Negado], (CASE l.isntname WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [nt nome], (CASE l.isntgroup WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [nt grupo], (CASE l.isntuser WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [nt user] FROM master.dbo.syslogins l WHERE l.sysadmin = 1 OR l.securityadmin = 1 ORDER BY l.isntgroup, l.isntname, l.isntuser; /* Users Quais sao os usuarios de Windows e os de SQL Preste atencao quais sao os usuarios, pode ter mais do que precisa,,,, */ Print 'Usuarios windows e SQL' SELECT name AS [Name], (CASE isntname WHEN 0 THEN 'SQL Server Standard' WHEN 1 THEN 'Windows Authentication' END ) AS [Type] FROM master.sys.syslogins; /* Usuarios com senha em branco ou senha com o mesmo nome */ SELECT SERVERPROPERTY('machinename') AS 'Server Name', ISNULL(SERVERPROPERTY('instancename'), SERVERPROPERTY('machinename')) AS 'Instance Name', name AS 'Login com senha em branco' FROM master.sys.sql_logins WHERE PWDCOMPARE('', password_hash) = 1 ORDER BY name OPTION (MAXDOP 1); SELECT SERVERPROPERTY('machinename') AS 'Server Name', ISNULL(SERVERPROPERTY('instancename'), SERVERPROPERTY('machinename')) AS 'Instance Name', name AS 'Login com senha igual ao nome' FROM master.sys.sql_logins WHERE PWDCOMPARE(name, password_hash) = 1 ORDER BY name OPTION (MAXDOP 1); /* usuários sem base associada */ CREATE TABLE #UsrSemBase ( LoginName NVARCHAR(MAX), DBname NVARCHAR(MAX), Username NVARCHAR(MAX), AliasName NVARCHAR(MAX) ); INSERT INTO #UsrSemBase EXEC master..sp_MSloginmappings; SELECT * FROM #UsrSemBase WHERE DBname IS NULL ORDER BY DBname, Username; DROP TABLE #UsrSemBase; /* Usuarios x Bases Esse aqui nao deve demorar muito,,, apenas gera um relacionamento dos usuarios que estao nas bases compare com a quantidade de usuarios que voce pegou na lista acima */ /* Versão para SQL Server 2005/2008/2008R2/2012 */ Print 'Usuarios por Bases' CREATE TABLE #UsrDataMapping ( [database_name] [sysname] NULL, [name] [sysname] NULL, [schema] [sysname] NULL ); EXEC sp_MSforeachdb 'insert into #UsrDataMapping SELECT ''?'' as DBNAME, --u.name AS [Name], SUSER_SNAME(sid) AS [Name], ISNULL(u.default_schema_name,N'''') AS [DefaultSchema] FROM [?].sys.database_principals AS u LEFT OUTER JOIN [?].sys.database_permissions AS dp ON dp.grantee_principal_id = u.principal_id WHERE (u.type in (''U'', ''S'', ''G'', ''C'', ''K'')) and dp.state = ''G'' '; SELECT * FROM #UsrDataMapping ORDER BY database_name; DROP TABLE #UsrDataMapping; /* Versão para 2000 */ create table #UsrDataMapping( [database_name] [sysname] NULL, [name] [sysname] NULL ) EXEC sp_MSforeachdb 'insert into #UsrDataMapping SELECT ''?'' as DBNAME, --u.name AS [Name], SUSER_SNAME(u.sid) AS [Name] FROM [?].dbo.sysusers AS u LEFT OUTER JOIN [?].dbo.syspermissions AS dp ON dp.grantee = u.u_id ' select distinct database_name, name from #UsrDataMapping where name is not null order by database_name drop table #UsrDataMapping /* Membros de roles */ create table ##RolesMembers ( [Database] sysname, RoleName sysname, MemberName sysname ) exec dbo.sp_MSforeachdb 'insert into ##RolesMembers select ''[?]'', ''['' + r.name + '']'', ''['' + m.name + '']'' from [?].sys.database_role_members rm inner join [?].sys.database_principals r on rm.role_principal_id = r.principal_id inner join [?].sys.database_principals m on rm.member_principal_id = m.principal_id -- where r.name = ''db_owner'' and m.name != ''dbo'' -- you may want to uncomment this line'; select * from ##RolesMembers order by [Database], [RoleName] drop table ##RolesMembers /* usuários órfãos */ create table ##OrphanedUsers ( [Database] sysname, Username sysname ) exec dbo.sp_MSforeachdb 'insert into ##OrphanedUsers select ''[?]'', UserName = name from [?].sys.sysusers where issqluser = 1 and (sid is not null and sid <> 0x0) and (len(sid) <= 16) and suser_sname(sid) is null;' select * from ##OrphanedUsers with (nolock) drop table ##OrphanedUsers /* Objetos em bases de sistema Coisas que nao deveriam estar neste lugar */ Print 'Coisas no lugar errado' SELECT * FROM master.sys.tables WHERE name NOT IN ( 'spt_fallback_db', 'spt_fallback_dev', 'spt_fallback_usg', 'spt_monitor', 'spt_values', 'MSreplication_options' ) AND is_ms_shipped = 0; SELECT * FROM master.sys.procedures WHERE name NOT IN ( 'sp_MSrepl_startup', 'sp_MScleanupmergepublisher' ) AND is_ms_shipped = 0; SELECT * FROM model.sys.tables; SELECT * FROM model.sys.procedures; /* Start up Stored procedure Achou alguma procedure aqui? estranho,,, veja o que ela faz,,, pode ser o mau esperando um boot,,, */ SELECT * FROM master.INFORMATION_SCHEMA.ROUTINES WHERE OBJECTPROPERTY(OBJECT_ID(ROUTINE_NAME), 'ExecIsStartup') = 1 /* Quem mais existe em server-level */ SELECT * FROM sysservers /* Usuarios com acesso ao linkedserver */ SELECT S.srvname , U.rmtloginame , SUSER_SNAME(U.loginsid) AS [local_login] FROM sysservers AS S INNER JOIN sys.sysoledbusers AS U ON S.srvid = U.rmtsrvid /* Agora mais informacoes sobre as bases classico,,, nada fora do comum,,, */ SELECT * FROM sysdatabases /* Onde estao os arquivos onde eles estao? quantos sao? */ SELECT DB_NAME(database_id), name, type_desc, physical_name FROM sys.master_files; /* --versão antiga CREATE TABLE #ArquivosDB ( [Banco] [sysname] NOT NULL, [file_guid] [SMALLINT] NULL, [Local] [NVARCHAR](260) NOT NULL, ); EXEC dbo.sp_MSforeachdb 'INSERT INTO #ArquivosDB SELECT ''[?]'' AS database_name, groupid, filename FROM [?].dbo.sysfiles'; SELECT Banco, (CASE file_guid WHEN 0 THEN 'Log' ELSE 'Data' END ), Local FROM #ArquivosDB ORDER BY Banco, file_guid DESC; DROP TABLE #ArquivosDB; */ /* TempDB voce deve ter visto a quantidade de datafiles para o TempDB na query acima mas sera que eh um problema? query para contencao de TempDB (http://sqlcat.com/sqlcat/b/technicalnotes/archive/2011/01/25/table-valued-functions-and-tempdb-contention.aspx) */ SELECT r.session_id , r.status , r.command , r.database_id , r.blocking_session_id , r.wait_type , AVG(r.wait_time) AS [WaitTime] , r.wait_resource FROM sys.dm_exec_requests AS r INNER JOIN sys.dm_exec_sessions AS s ON ( r.session_id = s.session_id ) WHERE r.wait_type IS NOT NULL AND s.is_user_process = 1 GROUP BY GROUPING SETS(( r.session_id , r.status , r.command , r.database_id , r.blocking_session_id , r.wait_type , r.wait_time , r.wait_resource ), ( )) /* Triggers isso pode demorar um pouco,,, e mais ainda pra ler,,,, */ PRINT 'Triggers'; EXEC dbo.sp_MSforeachdb 'SELECT ''[?]'' AS Banco, o.name AS Tabela, t.* FROM [?].sys.triggers t INNER JOIN [?].sys.objects o ON t.parent_id = o.object_id'; /* Alguns Waits Nao zere o contador, apenas veja o que tem,,, */ Print 'Alguns Waits' WITH [Waits] AS (SELECT [wait_type], [wait_time_ms] / 1000.0 AS [WaitS], ([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [ResourceS], [signal_wait_time_ms] / 1000.0 AS [SignalS], [waiting_tasks_count] AS [WaitCount], 100.0 * [wait_time_ms] / SUM([wait_time_ms]) OVER () AS [Percentage], ROW_NUMBER() OVER (ORDER BY [wait_time_ms] DESC) AS [RowNum] FROM sys.dm_os_wait_stats WHERE [wait_type] NOT IN ( N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP', N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE', N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE', N'CXCONSUMER', -- DBmirror N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE', N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE', N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX', -- AG N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE', N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', N'MEMORY_ALLOCATION_EXT', N'ONDEMAND_TASK_QUEUE', N'PARALLEL_REDO_DRAIN_WORKER', N'PARALLEL_REDO_LOG_CACHE', N'PARALLEL_REDO_TRAN_LIST', N'PARALLEL_REDO_WORKER_SYNC', N'PARALLEL_REDO_WORKER_WAIT_WORK', N'PREEMPTIVE_OS_FLUSHFILEBUFFERS', N'PREEMPTIVE_XE_GETTARGETSTATE', N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'PWAIT_DIRECTLOGCONSUMER_GETNEXT', N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', N'QDS_ASYNC_QUEUE', N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'QDS_SHUTDOWN_QUEUE', N'REDO_THREAD_PENDING_WORK', N'REQUEST_FOR_DEADLOCK_SEARCH', N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP', N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY', N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK', N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SOS_WORK_DISPATCHER', N'SP_SERVER_DIAGNOSTICS_SLEEP', N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES', N'VDI_CLIENT_OTHER', N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_RECOVERY', N'WAIT_XTP_HOST_WAIT', N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN', N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT' ) AND [waiting_tasks_count] > 0) SELECT MAX([W1].[wait_type]) AS [WaitType], CAST(MAX([W1].[WaitS]) AS DECIMAL(16, 2)) AS [Wait_S], CAST(MAX([W1].[ResourceS]) AS DECIMAL(16, 2)) AS [Resource_S], CAST(MAX([W1].[SignalS]) AS DECIMAL(16, 2)) AS [Signal_S], MAX([W1].[WaitCount]) AS [WaitCount], CAST(MAX([W1].[Percentage]) AS DECIMAL(5, 2)) AS [Percentage], CAST((MAX([W1].[WaitS]) / MAX([W1].[WaitCount])) AS DECIMAL(16, 4)) AS [AvgWait_S], CAST((MAX([W1].[ResourceS]) / MAX([W1].[WaitCount])) AS DECIMAL(16, 4)) AS [AvgRes_S], CAST((MAX([W1].[SignalS]) / MAX([W1].[WaitCount])) AS DECIMAL(16, 4)) AS [AvgSig_S] FROM [Waits] AS [W1] INNER JOIN [Waits] AS [W2] ON [W2].[RowNum] <= [W1].[RowNum] GROUP BY [W1].[RowNum] HAVING SUM([W2].[Percentage]) - MAX([W1].[Percentage]) < 95; -- percentage threshold GO /* Querys de alto custo */ SELECT TOP (1) MAX(query) AS sample_query, SUM(execution_count) AS cnt, SUM(total_worker_time) AS cpu, SUM(total_physical_reads) AS reads, SUM(total_logical_reads) AS logical_reads, SUM(total_elapsed_time) AS duration FROM ( SELECT QS.*, --sq.query_plan, SUBSTRING( ST.text, (QS.statement_start_offset / 2) + 1, ((CASE statement_end_offset WHEN -1 THEN DATALENGTH(ST.text) ELSE QS.statement_end_offset END - QS.statement_start_offset ) / 2 ) + 1 ) AS query FROM sys.dm_exec_query_stats AS QS CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) AS ST CROSS APPLY sys.dm_exec_plan_attributes(QS.plan_handle) AS PA --cross apply sys.dm_exec_query_plan (QS.plan_handle) sq WHERE PA.attribute = 'dbid' AND PA.value = DB_ID() ) AS D --Alterar aqui para verificar alguma base específica GROUP BY query ORDER BY duration DESC; /* Frangmentacao de indices se voce achou que o de Triggers demorou,,, imagina esse,,,, esse tem que rodar banco a banco */ Print 'Fragmentacao de indices' SELECT db.name AS databaseName, ps.object_id AS objectID, ps.index_id AS indexID, ps.partition_number AS partitionNumber, ps.avg_fragmentation_in_percent AS fragmentation, ps.page_count FROM sys.databases db INNER JOIN sys.dm_db_index_physical_stats(NULL, NULL, NULL, NULL, N'Limited') ps ON db.database_id = ps.database_id WHERE ps.index_id > 0 AND ps.page_count > 100 AND ps.avg_fragmentation_in_percent > 30 OPTION (MAXDOP 1); /* Índices hipotéticos */ CREATE TABLE #hipotetico ( banco sysname NULL, table_name sysname NULL, index_name sysname NULL ); INSERT INTO #hipotetico EXEC dbo.sp_MSforeachdb ' select ''[?]'' AS banco, object_name(object_id) AS tabela, name from [?].sys.indexes where is_hypothetical = 1 '; SELECT * FROM #hipotetico; DROP TABLE #hipotetico; /* Índices faltantes */ SELECT migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) AS improvement_measure, DB_NAME(mid.database_id) AS DatabaseName, OBJECT_NAME(mid.[object_id], mid.database_id) AS ObjectName, 'CREATE INDEX [missing_index_' + CONVERT(VARCHAR, mig.index_group_handle) + '_' + CONVERT(VARCHAR, mid.index_handle) + '_' + LEFT(PARSENAME(mid.statement, 1), 32) + ']' + ' ON ' + mid.statement + ' (' + ISNULL(mid.equality_columns, '') + CASE WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN ',' ELSE '' END + ISNULL(mid.inequality_columns, '') + ')' + ISNULL(' INCLUDE (' + mid.included_columns + ')', '') AS create_index_statement, migs.*, mid.database_id, mid.[object_id] FROM sys.dm_db_missing_index_groups mig INNER JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mig.index_group_handle INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle WHERE migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) > 10 --AND mid.database_id = DB_ID() -- descomente aqui para executar em uma base específica, ou colocando o Database ID ou coloque no contexto do banco ORDER BY migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans) DESC; /* Índices sem uso */ SELECT o.name, indexname = i.name, i.index_id, reads = user_seeks + user_scans + user_lookups, writes = user_updates, rows = ( SELECT SUM(p.rows) FROM sys.partitions p WHERE p.index_id = s.index_id AND s.object_id = p.object_id ), CASE WHEN s.user_updates < 1 THEN 100 ELSE 1.00 * (s.user_seeks + s.user_scans + s.user_lookups) / s.user_updates END AS reads_per_write, 'DROP INDEX ' + QUOTENAME(i.name) + ' ON ' + QUOTENAME(c.name) + '.' + QUOTENAME(OBJECT_NAME(s.object_id)) AS 'drop statement' FROM sys.dm_db_index_usage_stats s INNER JOIN sys.indexes i ON i.index_id = s.index_id AND s.object_id = i.object_id INNER JOIN sys.objects o ON s.object_id = o.object_id INNER JOIN sys.schemas c ON o.schema_id = c.schema_id WHERE OBJECTPROPERTY(s.object_id, 'IsUserTable') = 1 AND s.database_id = DB_ID() AND i.type_desc = 'nonclustered' AND i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND ( SELECT SUM(p.rows) FROM sys.partitions p WHERE p.index_id = s.index_id AND s.object_id = p.object_id ) > 10000 ORDER BY reads; /* Essa query mostra algumas informações sobre a fragmentação dos índices e estatísticas mas o mais interessante é a coluna lastStatsUpdate */ CREATE TABLE #estatisticas ( Banco sysname, table_schema sysname, table_name sysname, index_name sysname, table_id BIGINT, index_id TINYINT, groupid TINYINT, modifiedRows BIGINT, rowcnt BIGINT, ModifiedPct DECIMAL(18, 8), lastStatsUpdate DATETIME, Processed VARCHAR(5) ); EXEC dbo.sp_MSforeachdb 'INSERT INTO #estatisticas SELECT ''[?]'', schemas.name AS table_schema , tbls.name AS table_name , i.name AS index_name , i.id AS table_id , i.indid AS index_id , i.groupid , i.rowmodctr AS modifiedRows , ( SELECT MAX(rowcnt) FROM sysindexes i2 WHERE i.id = i2.id AND i2.indid < 2 ) AS rowcnt , CONVERT(DECIMAL(18, 8), CONVERT(DECIMAL(18, 8), i.rowmodctr) / CONVERT(DECIMAL(18, 8), ( SELECT MAX(rowcnt) FROM sysindexes i2 WHERE i.id = i2.id AND i2.indid < 2 ))) AS ModifiedPct , STATS_DATE(i.id, i.indid) AS lastStatsUpdate , ''False'' AS Processed FROM [?].sys.sysindexes i INNER JOIN [?].sys.sysobjects tbls ON i.id = tbls.id INNER JOIN [?].sys.sysusers schemas ON tbls.uid = schemas.uid INNER JOIN [?].information_schema.tables tl ON tbls.name = tl.table_name AND schemas.name = tl.table_schema AND tl.table_type = ''BASE TABLE'' WHERE 0 < i.indid AND i.indid < 255 AND table_schema <> ''sys'' AND i.rowmodctr <> 0 AND ( SELECT MAX(rowcnt) FROM [?].sys.sysindexes i2 WHERE i.id = i2.id AND i2.indid < 2 ) > 0 '; SELECT * FROM #estatisticas ORDER BY lastStatsUpdate DESC; DROP TABLE #estatisticas; /* Configurações da instância Isso mostra como esta configurado hoje, e nao como estava configurado... */ EXEC dbo.sp_configure 'show advanced options', 1 GO RECONFIGURE GO EXEC dbo.sp_configure /* Extended Stored Procedures procure por qualquer coisa fora do padrao... */ EXEC sp_helpextendedproc; GO /* Email O SQL envia email? */ /* Pule para a próxima linha,,, esse é apenas um teste para saber se vc está executando todo o script ou está indo por partes */ raiserror ('Eu já escrevi que esse script é para ser executado por partes,,,',20,-1) with log GO PRINT 'Email'; EXEC msdb.dbo.sp_send_dbmail @recipients = 'seuemail@aqui.com.br', @body = @@SERVERNAME, @subject = 'Testando SQL Server Database Mail - veja no corpo o nome do servidor'; GO /* Jobs Simples,,, quais sao, a quem pertence e se estao ativos,,, NAO tente corrigir-los agora,,, entenda o que eles fazem ou deveriam fazer,,, */ PRINT 'Jobs'; SELECT name AS [Name], SUSER_SNAME(owner_sid) AS [Owner], (CASE enabled WHEN 0 THEN 'Nao' WHEN 1 THEN 'Sim' END ) AS [Enable], description AS [Description] FROM msdb.dbo.sysjobs_view ORDER BY Name; /* Informação detalhada sobre os Jobs */ USE msdb GO SELECT /*S.job_id,*/ S.job_name AS [Nome do Job], S.is_job_enabled AS [Job], S.is_schedule_enabled AS [Agenda], SUSER_SNAME(S.job_owner) AS [Owner], S.schedule_name, S.Description AS [Descriçao], AVG(((H.run_duration / 1000000) * 86400) + (((H.run_duration - ((H.run_duration / 1000000) * 1000000)) / 10000) * 3600) + (((H.run_duration - ((H.run_duration / 10000) * 10000)) / 100) * 60) + (H.run_duration - (H.run_duration / 100) * 100) ) AS [MédiaDeDuraçao(s)] -- ,number_of_runs = count(1) FROM ( SELECT SJ.job_id, SJ.owner_sid AS job_owner, SJ.name AS job_name, SJ.enabled AS is_job_enabled, SS.enabled AS is_schedule_enabled, SS.name AS schedule_name, CASE freq_type WHEN 1 THEN 'Ocorre em ' + STUFF(RIGHT(active_start_date, 4), 3, 0, '/') + '/' + LEFT(active_start_date, 4) + ' as ' + REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME)/* hh:mm:ss 24H */, 9 ), 14), ':000', ' ' ) /* HH:mm:ss:000AM/PM then replace the :000 with space.*/ WHEN 4 THEN 'Ocorre a cada ' + CAST(freq_interval AS VARCHAR(10)) + ' dia(s) ' + CASE freq_subday_type WHEN 1 THEN 'a(s) ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) WHEN 2 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' segundo(s)' WHEN 4 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' minuto(s)' WHEN 8 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' hora(s)' ELSE '' END + CASE WHEN freq_subday_type IN ( 2, 4, 8 ) /* repeat seconds/mins/hours */ THEN ' entre ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) + ' e ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_end_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) ELSE '' END WHEN 8 THEN 'Ocorre a cada ' + CAST(freq_recurrence_factor AS VARCHAR(10)) + ' semana(s) em ' + REPLACE( CASE WHEN freq_interval & 1 = 1 THEN 'Domingo, ' ELSE '' END + CASE WHEN freq_interval & 2 = 2 THEN 'Segunda, ' ELSE '' END + CASE WHEN freq_interval & 4 = 4 THEN 'Terça, ' ELSE '' END + CASE WHEN freq_interval & 8 = 8 THEN 'Quarta, ' ELSE '' END + CASE WHEN freq_interval & 16 = 16 THEN 'Quinta, ' ELSE '' END + CASE WHEN freq_interval & 32 = 32 THEN 'Sexta, ' ELSE '' END + CASE WHEN freq_interval & 64 = 64 THEN 'Sabado, ' ELSE '' END + '|', ', |', ' ' ) /* get rid of trailing comma */ + CASE freq_subday_type WHEN 1 THEN 'a(s) ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) WHEN 2 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' segundo(s)' WHEN 4 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' minuto(s)' WHEN 8 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' hora(s)' ELSE '' END + CASE WHEN freq_subday_type IN ( 2, 4, 8 ) /* repeat seconds/mins/hours */ THEN ' entre ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) + ' e ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_end_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) ELSE '' END WHEN 16 THEN 'Ocorre a cada ' + CAST(freq_recurrence_factor AS VARCHAR(10)) + ' mes(s) on ' + 'dia ' + CAST(freq_interval AS VARCHAR(10)) + ' deste mes ' + CASE freq_subday_type WHEN 1 THEN 'a(s) ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) WHEN 2 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' segundo(s)' WHEN 4 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' minutos(s)' WHEN 8 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' horas(s)' ELSE '' END + CASE WHEN freq_subday_type IN ( 2, 4, 8 ) /* repeat seconds/mins/hours */ THEN ' entre ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) + ' e ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_end_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) ELSE '' END WHEN 32 THEN 'Ocorre ' + CASE freq_relative_interval WHEN 1 THEN 'toda primeira ' WHEN 2 THEN 'toda segunda ' WHEN 4 THEN 'toda terceira ' WHEN 8 THEN 'toda quarta ' WHEN 16 THEN 'na última ' END + CASE freq_interval WHEN 1 THEN 'Domingo' WHEN 2 THEN 'Segunda' WHEN 3 THEN 'Terça' WHEN 4 THEN 'Quarta' WHEN 5 THEN 'Quinta' WHEN 6 THEN 'Sexta' WHEN 7 THEN 'Sabado' WHEN 8 THEN 'dia' WHEN 9 THEN 'dia da semana' WHEN 10 THEN 'final de semana' END + ' de cada ' + CAST(freq_recurrence_factor AS VARCHAR(10)) + ' mes(s) ' + CASE freq_subday_type WHEN 1 THEN 'a(s) ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) WHEN 2 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' segundo(s)' WHEN 4 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' minuto(s)' WHEN 8 THEN 'a cada ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' hora(s)' ELSE '' END + CASE WHEN freq_subday_type IN ( 2, 4, 8 ) /* repeat seconds/mins/hours */ THEN ' entre ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) + ' e ' + LTRIM(REPLACE( RIGHT(CONVERT( VARCHAR(30), CAST(CONVERT( VARCHAR(8), STUFF( STUFF( RIGHT('000000' + CAST(active_end_time AS VARCHAR(10)), 6), 3, 0, ':' ), 6, 0, ':' ), 8 ) AS DATETIME), 9 ), 14), ':000', ' ' ) ) ELSE '' END WHEN 64 THEN 'Roda quando o serviço do SQL Server Agent iniciar' WHEN 128 THEN 'Roda quando o computador estiver idle' END AS [Description] FROM msdb.dbo.sysjobs SJ INNER JOIN msdb.dbo.sysjobschedules SJS ON SJ.job_id = SJS.job_id INNER JOIN msdb.dbo.sysschedules SS ON SJS.schedule_id = SS.schedule_id INNER JOIN msdb.dbo.syscategories SC ON SJ.category_id = SC.category_id --WHERE SC.name = 'Name from query below' ) S INNER JOIN msdb.dbo.sysjobhistory H ON S.job_id = H.job_id AND H.step_id = 0 WHERE H.run_date >= /* 7 days ago */ CAST(DATEPART(yyyy, DATEADD(d, -7, GETDATE())) AS VARCHAR(10)) + CAST(DATEPART(mm, DATEADD(d, -7, GETDATE())) AS VARCHAR(10)) + CAST(DATEPART(dd, DATEADD(d, -7, GETDATE())) AS VARCHAR(10)) --format getDate once to compare against multiple run_dates GROUP BY /*S.job_id,*/ S.job_name, S.is_job_enabled, S.is_schedule_enabled, S.job_owner, S.schedule_name, S.Description ORDER BY S.job_name;
Buen post!