SQL Server 2008 - Buscando Stored Procedures que no están en el caché

Con el query que se publica a continuación se puede obtener una lista de Stored Procedures que no están en el cache, lo cual podría posibilitar investigar luego si esos S.P. que no están en el cache son realmente utilizados o no.

Atención, nunca borre o elimine un stored procedure basandose en este query, ya que este query indica solamente si el sp está en el cache, lo cual no significa necesariamente que no se utiliza, por caso si un S.P. tiene las sentencias RECOMPILE, nunca jamás aparecerá en el cache a pesar de ser plenamente utilizado.

Y recuerde que en vez de eliminar un S.P. debería renombrarlo con algún prefijo que identifique claramente a los SPs a investigar en el entorno de testing y luego hacer las pruebas correspondientes en entornos de testing y de pruebas para ver si ese S.P. es usado o no. Y obviamente, luego de todas esas pruebas y como medida de seguridad adicional, antes de borrar un S.P. en producción se debe guardar un script con toda la lógica.

Dicho lo anterior, veamos los scripts:
-- Obtengo una lista de SPs en la base de datos (SQL 2005 and 2008)
  SELECT p.name AS 'SP Name', p.create_date, p.modify_date      
  FROM sys.procedures AS p
  WHERE p.is_ms_shipped = 0
  ORDER BY p.name;


  -- Obtener una lista de SPs posiblemente no usados (SQL 2008 solamente)
  SELECT p.name AS 'SP Name'        
  FROM sys.procedures AS p
  WHERE p.is_ms_shipped = 0

  EXCEPT

  SELECT p.name AS 'SP Name'        -- Lista de SPs en la base actual
  FROM sys.procedures AS p          -- que están en el procedure cache
  INNER JOIN sys.dm_exec_procedure_stats AS qs
  ON p.object_id = qs.object_id
  WHERE p.is_ms_shipped = 0;
Adicionalmente usted puede usar el siguiente query (solamente SQL Server 2008)
para determinar las dependencias de un objeto.
SELECT referencing_schema_name, referencing_entity_name
FROM sys.dm_sql_referencing_entities (‘Person.Address’, 'OBJECT');


Estos consejos son dados "AS IS" "TAL COMO ESTÁN", no doy ni concedo explicita o implicitamente
garantía alguna acerca de estos scripts, queries y consejos, ni de su funcionalidad ni utilidad
y su uso queda bajo la exclusiva responsabilidad de un Administrador de Bases de Datos competente
y experimentado, toda responsabilidad por daños alguno o el mal uso de los mismos queda bajo la
responsabilidad exclusiva de dicho administrador y se da por entendido que las bases de Datos
SQL Server deben ser administradas por profesionales expertos en dicha tecnología.
No me hago cargo de modo alguno por daños en datos, sistemas y servidores o 
similes por el uso de este o cualquier otro artículo de este blog.

Hugo Román Bernachea
Mail de contacto: SQLServer777@gmail.com

Microsoft Certified DBA
Microsoft Certified Trainer
Twitter: @bernachea

Read More...

Intersect, Except, Union, All and Any - David Poole

David Poole en SQLServerCentral hace una revisión de algunos nuevos comandos en SQL Server 2008, por caso:

INTERSECT
EXCEPT
ALL
ANY
ALL y ANY no son nuevos, pero INTERSECT y EXCEPT son nuevos.

INTERSECT, EXCEPT and UNION

Para experimentar con esos comandos David decidió ver dos conjuntos de valores para CustomerID.

Customers (Clientes) en sales territory 10 (United Kingdom)
(pedidos) Sales orders de July 2004, el cual es el último mes de pedidos en Adventureworks
La mejor manera de ver que hacen estos dos comandos es comparar los datos en los diagramas.
redicate Illustration Description
EXCEPT Customers (clientes) de UK que no compraron en Julio 2004
INTERSECT Customers (clientes) de UK
AND (y) que compraron algo en Julio 2004
UNION Customers (clientes) de UK
OR (o) que hicieron una compra en Julio 2004

Diferentes formas de escribir un query con EXCEPT.

Si bien nunca usé EXCEPT antes yo he obtenido los mismos resultados por métodos tradicionales, hay muchas formas de hacer lo mismo, por ejemplo:
LEFT JOIN
Este query trae los resultados requeridos.
SELECT C.CustomerID
   FROM Sales.Customer AS C
    LEFT JOIN Sales.SalesOrderHeader AS OH
    ON C.CustomerID = OH.CustomerID
    AND OrderDate>='2004-07-01'
   WHERE OH.CustomerID IS NULL
   AND C.TerritoryID=10
WHERE CustomerID NOT IN(…)
Buscando la eficiencia, otra forma de hacer lo mismo.

SELECT CustomerID
   FROM Sales.Customer
   WHERE TerritoryID=10
   AND CustomerID NOT IN(
    SELECT customerid
    FROM  Sales.SalesOrderHeader
    WHERE OrderDate>='2004-07-01'
   )

EXCEPT

Finalmente usando el nuevo comando Except.
SELECT CustomerID
   FROM Sales.Customer
   WHERE TerritoryID=10
    EXCEPT
   SELECT customerid
   FROM Sales.SalesOrderHeader
   WHERE OrderDate>='2004-07-01'

Diferentes maneras de escribir un query tipo INTERSECT

Tres ejemplos:
INNER JOIN
Como un cliente puede tener mas de un pedido tengo que hacer una lista con distinct para los valores del customerid, veamos dos enfoques para lo mismo.

As any customer can have more than one order I am going to have to make a distinct list of CustomerID values. I decided to try a couple of approaches.
SELECT DISTINCT C.CustomerID
   FROM Sales.Customer AS C
    INNER JOIN Sales.SalesOrderHeader AS OH
    ON C.CustomerID = OH.CustomerID
   WHERE
    C.TerritoryID=10
    AND OH.OrderDate>='2004-07-01'

SELECT C.CustomerID
   FROM Sales.Customer AS C
    INNER JOIN (SELECT DISTINCT CustomerID FROM Sales.SalesOrderHeader WHERE OrderDate>='2004-07-01'
   )AS OH
    ON C.CustomerID = OH.CustomerID
   WHERE C.TerritoryID=10
WHERE CustomerID IN(…)
SELECT CustomerID
   FROM Sales.Customer
   WHERE TerritoryID=10
   AND CustomerID IN(
    SELECT customerid
    FROM  Sales.SalesOrderHeader
    WHERE OrderDate>='2004-07-01'
   )
INTERSECT
Finalmente un intersect.
SELECT CustomerID
   FROM Sales.Customer
   WHERE TerritoryID=10
    INTERSECT
   SELECT customerid
   FROM Sales.SalesOrderHeader
   WHERE OrderDate>='2004-07-01'
The ANY and ALL Predicate
ANY and ALL are predicates I have never needed to use.

ANY

Los dos queries nos ofrecen los mismos resultados y el mismo plan de ejecución:
SELECT *
   FROM Sales.SalesPerson
   WHERE TerritoryID = ANY(
    SELECT TerritoryID FROM Sales.SalesTerritory WHERE CountryRegionCode='US'
   )

SELECT *
   FROM Sales.SalesPerson
   WHERE TerritoryID IN(
    SELECT TerritoryID FROM Sales.SalesTerritory WHERE CountryRegionCode='US'
   )
   
ALL
All permite una comparación contra todos los valores en una lista de un select, por caso estos dos queries son idénticos:

SELECT *
   FROM Sales.SalesOrderHeader
   WHERE TotalDue > ALL(SELECT TotalDue FROM Sales.TopSales)
   ORDER BY Sales.TotalDue DESC 

SELECT *
   FROM Sales.SalesOrderHeader
   WHERE TotalDue > (SELECT MAX(TotalDue) FROM Sales.TopSales)
   ORDER BY Sales.TotalDue DESC 
La página original se encuentra en:

http://www.sqlservercentral.com/articles/T-SQL/67545/


Hugo Román Bernachea
Mail de contacto: SQLServer777@gmail.com

Microsoft Certified DBA
Microsoft Certified Trainer
Twitter: @bernachea

Read More...

Algunos Scripts para monitorear SQL Server

Para monitorear el estado de los jobs que fallaron en su última ejecución:

SELECT name FROM msdb.dbo.sysjobs A, msdb.dbo.sysjobservers B
                           WHERE A.job_id = B.job_id AND B.last_run_outcome = 0
Espacio en cada disco para la instancia SQL:

EXEC master..xp_fixeddrives
Para ver un listado de Jobs Deshabilitados:
SELECT name FROM msdb.dbo.sysjobs
           WHERE enabled = 0 ORDER BY name
Para ver un listado de los jobs que están actualmente en ejecución:

msdb.dbo.sp_get_composite_job_info
          NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, NULL
Para ver logines que son miembros de los roles de servidor:
SELECT 'ServerRole' = A.name, 'MemberName' =  B.name
      FROM master.dbo.spt_values A, master.dbo.sysxlogins B
              WHERE A.low = 0 AND A.type = 'SRV' AND B.srvid IS NULL
Para ver la última vez que las bases de datos fueron backupeadas:

SELECT  B.name as Database_Name, ISNULL(STR(ABS(DATEDIFF(day, GetDate(),
MAX(Backup_finish_date)))),
'NEVER') as DaysSinceLastBackup,
ISNULL(Convert(char(10), MAX(backup_finish_date), 101), 'NEVER')
as LastBackupDate
FROM master.dbo.sysdatabases B
LEFT OUTER JOIN msdb.dbo.backupset A
ON A.database_name = B.name AND A.type = 'D'
GROUP BY B.Name
ORDER BY B.name

Para leer las ultimas entradas del archivo de log (NO el transaction log):

CREATE TABLE #Errors (vchMessage varchar(255), ID int)
CREATE INDEX idx_msg ON #Errors(ID, vchMessage)
INSERT #Errors EXEC xp_readerrorlog
SELECT vchMessage
FROM #Errors
WHERE vchMessage
NOT LIKE '%Log backed up%' AND vchMessage
NOT LIKE '%.TRN%' AND vchMessage
NOT LIKE '%Database backed up%' AND vchMessage
NOT LIKE '%.BAK%' AND vchMessage
NOT LIKE '%Run the RECONFIGURE%' AND
vchMessage NOT LIKE '%Copyright (c)%'
ORDER BY ID

DROP TABLE #Errors


Espero sus comentarios, sugerencias, correcciones, etc y espero además
que estos scripts les sean de utilidad.

Hugo Román Bernachea
Mail de contacto: SQLServer777@gmail.com

Microsoft Certified DBA
Microsoft Certified Trainer
Twitter: @bernachea









Read More...

Como obtener todos los campos de una tabla en SQL 2000 y en 2005

--como obtener todos los campos de una tabla
Una vez determinado el object id, en este caso el 146230095 que corresponde a la tabla store de adventureworks, podremos ejecutar una de las siguientes consultas dependiendo de si estamos en 2000 o en 2005 (y 2008)

--sql server 2000
select syscolumns.name [name], systypes.name [type],
syscolumns.length as 'length',
syscolumns.isnullable as 'isnullable'
from syscolumns inner join systypes
on syscolumns.xtype = systypes.xtype and
syscolumns.xusertype = systypes.xusertype
where syscolumns.id = 14623095"
and systypes.name <> 'sysname'
order by syscolumns.colid

--sql server 2005 & 2008

with ctabla
as
(select s.name + '.' + t.name tabla, t.object_id oid from sys.tables t
inner join sys.schemas s on t.schema_id = s.schema_id)

select c.name [name],
(select top 1name from sys.systypes where xtype = c.system_type_id) [type],
c.max_length [length], c.is_nullable [isnullable] from sys.columns c
inner join ctabla t on c.object_id = t.oid
where t.oid = 14623095
order by c.name

Hugo Román Bernachea
Mail de contacto: SQLServer777@gmail.com

Microsoft Certified DBA
Microsoft Certified Trainer
Twitter: @bernachea

Read More...

Como obtener todas las tablas de una base de datos en SQL Server 2000 y en 2005

--Todas las tablas de una base de datos
-- en sql 2000

select name, Id from sysobjects
where type='U' and name <> 'dtproperties'
order by name

-- en sql 2005
with ctabla
as
(select s.name + '.' + t.name tabla, t.object_id oid from sys.tables t
inner join sys.schemas s on t.schema_id = s.schema_id)
select t.tabla, name from sys.columns c
inner join ctabla t on c.object_id = t.oid

Hugo Román Bernachea
Mail de contacto: SQLServer777@gmail.com

Microsoft Certified DBA
Microsoft Certified Trainer
Twitter: @bernachea

Read More...

sys.dm_os_performance_counters

La performance de un servidor SQL Server 2005 puede ser monitoreada utilizando los contadores de performance. Para lo cual podemos usar el System Monitor (perfmon) o, a partir de SQL Server 2005, utilizando la Dynamic Management View (DMV a partir de ahora) con sys.os_exec_performance_counters.

Algunos contadores útiles (en otro posteo indicaré como analizar estos contadores):
SQLServer:Buffer Partition
SQLServer:User Settable
SQLServer:Databases
SQLServer:CLR
SQLServer:Cursor Manager by Type
SQLServer:Exec Statistics
SQLServer:Transactions
SQLServer:Memory Manager
SQLServer:SQL Errors
SQLServer:Buffer Node
SQLServer:Plan Cache
SQLServer:Access Methods
SQLServer:Cursor Manager Total
SQLServer:Broker Activation
SQLServer:Latches
SQLServer:Wait Statistics
SQLServer:Broker/DBM Transport
SQLServer:General Statistics
SQLServer:SQL Statistics
SQLServer:Catalog Metadata
SQLServer:Broker Statistics
SQLServer:Locks
SQLServer:Buffer Manage
En SQL Server 2000 podíamos obtener la información desde la tabla master.dbo.sysperfinfo. En 2005 se nos provee con una vista que representa esta tabla, solo a los efectos de mantener compatibilidad con codificaciones previas, pero en 2005 usted debiera usar el DMV “sys.os_exec_performance_counters”.

Para Finalizar un ejemplo:
Vamos a crear un script para calcular el "buffer cache hit ratio", mientras mas cercano a 100% mejor el valor, ya que indicaría que el buffer cache está siendo utilizado de manera óptima y que las páginas permanecen en el cache de buffer. Y por ende la performance de su servidor será mejor.
Create Proc dbo.P_ContadorBufferCacheHit
as
SELECT (a.cntr_value * 1.0 / b.cntr_value) * 100.0 [BufferCacheHitRatio]
FROM (SELECT *, 1 x FROM sys.dm_os_performance_counters
WHERE counter_name = 'Buffer cache hit ratio'
AND object_name = 'SQLServer:Buffer Manager') a
JOIN
(SELECT *, 1 x FROM sys.dm_os_performance_counters
WHERE counter_name = 'Buffer cache hit ratio base'
AND object_name = 'SQLServer:Buffer Manager') b
GO

Hugo Román Bernachea
Mail de contacto: SQLServer777@gmail.com

Microsoft Certified DBA
Microsoft Certified Trainer
Twitter: @bernachea


Read More...

Un Stored para ejecutar en modo DAC (Conexión Administrativa Dedicada)

Como todos bien sabemos, en SQL Server 2005 disponemos de las DAC, Conexiones administrativas dedicadas, para ejecutar distintos tipos de tareas en caso de encontrarnos con fallos o problemas en nuestros servidores SQL Server 2005.

La pregunta es, que es lo que podríamos ejecutar para tener un vistazo general de los problemas de los servidores?

Googleando por ahí encontré el siguiente Stored que me parece muy util de tener creado como para poder ejecutar en modo DAC y obtener la información que estamos buscando.

Script---->

USE master
GO

-- Este Stored nos dará la info de los servidores en cuestión.
-- Conectar en modo DAC y ejecutar el Stored que YA existirá de ANTEMANO en la base

CREATE PROC dbo.p_Info_Servidor
AS

SELECT '*** comienzo de informe DAC ***'

SELECT '-- Mostrar SQL Server Info'
EXEC ('USE MASTER')

SELECT
CONVERT(char(20), SERVERPROPERTY('MachineName')) AS 'Nombre Maquina',
CONVERT(char(20), SERVERPROPERTY('ServerName')) AS 'Nombre SQL Server',

(CASE WHEN CONVERT(char(20), SERVERPROPERTY('InstanceName')) IS NULL
THEN 'Instancia Predeterminada'
ELSE CONVERT(char(20), SERVERPROPERTY('InstanceName'))
END) AS 'Nombre de Instancia',

CONVERT(char(20), SERVERPROPERTY('EDITION')) AS Edicion,
CONVERT(char(20), SERVERPROPERTY('ProductVersion')) AS 'Version',
CONVERT(char(20), SERVERPROPERTY('ProductLevel')) AS 'Level',

(CASE WHEN CONVERT(char(20), SERVERPROPERTY('ISClustered')) = 1
THEN 'Clustered'
WHEN CONVERT(char(20), SERVERPROPERTY('ISClustered')) = 0
THEN 'NOT Clustered'
ELSE 'INVALID INPUT/ERROR'
END) AS 'FAILOVER CLUSTERED',

(CASE WHEN CONVERT(char(20), SERVERPROPERTY('ISIntegratedSecurityOnly')) = 1
THEN 'Seguridad Integrada '
WHEN CONVERT(char(20), SERVERPROPERTY('ISIntegratedSecurityOnly')) = 0
THEN 'Seguridad SQL Server '
ELSE 'INVALID INPUT/ERROR'
END) AS 'SECURITY',

(CASE WHEN CONVERT(char(20), SERVERPROPERTY('ISSingleUser')) = 1
THEN 'Single User'
WHEN CONVERT(char(20), SERVERPROPERTY('ISSingleUser')) = 0
THEN 'Multi User'
ELSE 'INVALID INPUT/ERROR'
END) AS 'USER MODE',

CONVERT(char(30), SERVERPROPERTY('COLLATION')) AS COLLATION

SELECT '-- Mostrar las 5 sentencias mas consumidoras'
SELECT TOP 5 total_worker_time/execution_count AS [Avg CPU Time],
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 statement_text
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
ORDER BY total_worker_time/execution_count DESC;

SELECT '-- Mostrar quienes están logeados'
SELECT login_name ,COUNT(session_id) AS session_count
FROM sys.dm_exec_sessions
GROUP BY login_name;

SELECT '-- Mostrar cursores con tiempos extensos de ejecución'
EXEC ('USE master')

SELECT creation_time ,cursor_id
,name ,c.session_id ,login_name
FROM sys.dm_exec_cursors(0) AS c
JOIN sys.dm_exec_sessions AS s
ON c.session_id = s.session_id
WHERE DATEDIFF(mi, c.creation_time, GETDATE()) > 5;

SELECT '-- Mostrar sesiones con transacciones abiertas'
SELECT s.*
FROM sys.dm_exec_sessions AS s
WHERE EXISTS
(
SELECT *
FROM sys.dm_tran_session_transactions AS t
WHERE t.session_id = s.session_id
)
AND NOT EXISTS
(
SELECT *
FROM sys.dm_exec_requests AS r
WHERE r.session_id = s.session_id
);

SELECT '-- Mostrar espacio libre en tempdb '
SELECT SUM(unallocated_extent_page_count) AS [free pages],
(SUM(unallocated_extent_page_count)*1.0/128) AS [free space in MB]
FROM sys.dm_db_file_space_usage;

SELECT '-- Mostrar espacio ocupado por tempdb'
SELECT SUM(size)*1.0/128 AS [size in MB]
FROM tempdb.sys.database_files

SELECT '-- Mostrar jobs activos'
SELECT DB_NAME(database_id) AS [Database], COUNT(*) AS [Active Async Jobs]
FROM sys.dm_exec_background_job_queue
WHERE in_progress = 1
GROUP BY database_id;

SELECT '--Mostrar clientes conectados'
SELECT session_id, client_net_address, client_tcp_port
FROM sys.dm_exec_connections;

SELECT '--Mostrar batchs en ejecución'
SELECT * FROM sys.dm_exec_requests;

SELECT '--Mostrar request actualmente bloqueados'
SELECT session_id ,status ,blocking_session_id
,wait_type ,wait_time ,wait_resource
,transaction_id
FROM sys.dm_exec_requests
WHERE status = N'suspended'

SELECT '--Mostrar fechas de ultimos backups ' as ' '
SELECT B.name as Database_Name,
ISNULL(STR(ABS(DATEDIFF(day, GetDate(),
MAX(Backup_finish_date)))), 'NEVER')
as DaysSinceLastBackup,
ISNULL(Convert(char(10),
MAX(backup_finish_date), 101), 'NEVER')
as LastBackupDate

FROM master.dbo.sysdatabases B LEFT OUTER JOIN msdb.dbo.backupset A
ON A.database_name = B.name AND A.type = 'D' GROUP BY B.Name ORDER BY B.name

SELECT '--Mostrar jobs que están todavía en ejecución' as ' '
exec msdb.dbo.sp_get_composite_job_info NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, NULL

SELECT '--Mostrar informe de Jobs fallidos ' as ' '
SELECT name FROM msdb.dbo.sysjobs A, msdb.dbo.sysjobservers B WHERE A.job_id = B.job_id AND B.last_run_outcome = 0

SELECT '--Mostrar jobs deshabilitados ' as ' '
SELECT name FROM msdb.dbo.sysjobs WHERE enabled = 0 ORDER BY name

SELECT '--Mostrar espacio disponible de BD ' as ' '
exec sp_MSForEachDB 'Use ? SELECT name AS ''Name of File'', size/128.0 -CAST(FILEPROPERTY(name, ''SpaceUsed'' )
AS int)/128.0 AS ''Espacio disponible en MB'' FROM .SYSFILES'

SELECT '--Mostrar total DB size (.MDF+.LDF)' as ' '
set nocount on
declare @name sysname
declare @SQL nvarchar(600)
-- Use temporary table to sum up database size w/o using group by
create table #databases (
DATABASE_NAME sysname NOT NULL,
size int NOT NULL)
declare c1 cursor for
select name from master.dbo.sysdatabases
-- where has_dbaccess(name) = 1 -- Only look at databases to which we have access
open c1
fetch c1 into @name

while @@fetch_status >= 0
begin
select @SQL = 'insert into #databases
select N'''+ @name + ''', sum(size) from '
+ QuoteName(@name) + '.dbo.sysfiles'
-- Insert row for each database
execute (@SQL)
fetch c1 into @name
end
deallocate c1

select DATABASE_NAME, DATABASE_SIZE_MB = size*8/1000 -- Convert from 8192 byte pages to K and then convert to MB
from #databases order by 1

select SUM(size*8/1000)as '--Shows disk space used - ALL DBs - MB ' from #databases

drop table #databases

SELECT '--Mostrar espacio disponible en disco ' as ' '
EXEC master..xp_fixeddrives

SELECT '*** Fin de informes **** '

GO

A este stored le pueden agregar las sentencias que crean convenientes, pero pueden tomar este script como base.

Hugo Román Bernachea
Mail de contacto: SQLServer777@gmail.com

Microsoft Certified DBA
Microsoft Certified Trainer
Twitter: @bernachea

Read More...

Como determinar índices faltantes en SQL Server 2005??

En SQL Server 2005 existen unas nuevas vistas dinámicas que nos facilitan el proceso de determinar que índices optimizarían el rendimiento de nuestras consultas:

sys.dm_db_missing_index_group_stats Regresa información acerca de grupos de índices no existentes, por ejemplo, la performance que se podría obtener implementando un grupo específico de índices.
sys.dm_db_missing_index_groups Regresar información acerca de un grupo específico de indices no declarados, como el identificador de grupo y el identificador de todos los índices que están contenidos en dicho grupo.
sys.dm_db_missing_index_details Devuelve información detallada acerca de un posible índice a ser creado, por ejemplo nombre e identificador de la tabla donde el índice podría ser creado y las columnas y tipos que conformarían dicho índice.
sys.dm_db_missing_index_columns Devuelve info acerca de los campos que podrían conformar un índice.that are missing an index.

Cada vez que SQL ejecuta una consulta, internamente determina si esa consulta podía haber sido optimizada con el uso de algún índice inexistente al momento del query (por eso es missing index) y cuando ejecutemos algunas de estas vistas dinámicas nos dará dicha información.

Nada mas y nada menos.

Hugo Román Bernachea
Mail de contacto: SQLServer777@gmail.com

Microsoft Certified DBA
Microsoft Certified Trainer
Twitter: @bernachea

Read More...