sábado, 26 de junio de 2010

Particionar Indice SQL

Hola nuevamente, y con la esperanza de que este post sea de utilidad para al menos una persona me dispongo a escribir.

Si alguien ya ha leído alguna otra entrada empezara a notar que tengo el tatuaje de Microsoft en la palma de las manos, y la realidad es que me gusta .NET y SQL y mucho, el que algunas soluciones tecnológicas aveces no corran como se espera tiene mas que ver con el echo de desconocer la tecnología e implementar de una manera equivocada algo.

Esta vez hablare de particionar Indices en SQL, y si bien es algo que en el escenario ideal debe contemplarse desde un inicio del proyecto el ejemplo que pondré se concentra en una base de datos ya poblada, esta es una técnica sumamente importante para mejorar el desempeño de nuestras bases de datos.
Para nuestro ejemplo hare referencia a la Base de datos ADVENTUREWORKS, la cual podemos confirmar tiene solo un FileGroup, para lo cual usaremos

USE ADVENTUREWORKS

Select * from sys.filegroups
Lo primero que haremos sera crear FileGroups adicionales para nuestra Base de datos


SQL Server nos permite un mejor manejo de tablas sumamente grandes mediante el uso de particiones.


I. Agregar FileGroups a la Base de datos

ALTER DATABASE ADVENTUREWORKS
ADD FILEGROUP Productos_200;
GO

ALTER DATABASE ADVENTUREWORKS
ADD FILEGROUP Productos_400;
GO

ALTER DATABASE ADVENTUREWORKS
ADD FILEGROUP Productos_600;
GO

ALTER DATABASE ADVENTUREWORKS
ADD FILEGROUP Productos_800;
GO

Alter database AdventureWorks
add Filegroup Productos_2000
GO
ALTER DATABASE ADVENTUREWORKS
ADD FILEGROUP Productos_X;
GO





II. Para consultar las particiones creadas lo pueden hacer mediante el Managment Studio o con el script :

Select * from sys.filegroups

III. El siguiente paso es agregar archivos de datos para cada FileGroup
a. En este punto aplicaremos las mejores prácticas respecto a
i. Inicializar los archivos de datos con espacio suficiente(tanto como tengamos disponible sin afectar otras aplicaciones y sin dejar sin espacio el o los discos duros disponibles).
ii. Respecto a la forma en que crecerán los archivos (Menos de un 1 GB al mismo tiempo)
iii. Todos los archivos de datos con el mismo tamaño.

Alter DataBASE ADVENTUREWORKS Add FILE
(
NAME=Productos_200,
FILENAME='C:\AdventureWorks\Productos_200.ndf',
SIZE=1000MB,
FILEGROWTH =150MB
) TO FILEGROUP Productos_200;
GO

Alter DataBASE ADVENTUREWORKS
Add FILE
(
NAME=DatosPromotorias_400,
FILENAME='C:\AdventureWorks\Productos_400.ndf',
SIZE=1000MB,
FILEGROWTH =150MB
) TO FILEGROUP Productos_400;
GO

Alter DataBASE ADVENTUREWORKS
Add FILE
(
NAME=Productos_600,
FILENAME='C:\AdventureWorks\Productos_600.ndf',
SIZE=1000MB,
FILEGROWTH =150MB
) TO FILEGROUP Productos_600;
GO

Alter DataBASE ADVENTUREWORKS
Add FILE
(
NAME=Productos_800,
FILENAME='C:\AdventureWorks\Productos_800.ndf',
SIZE=1000MB,
FILEGROWTH =150MB
) TO FILEGROUP Productos_800;
GO
Alter DataBASE ADVENTUREWORKS
Add FILE
(
NAME=Productos_2000,
FILENAME='C:\AdventureWorks\Productos_2000.ndf',
SIZE=1000MB,
FILEGROWTH =150MB
) TO FILEGROUP Productos_2000;
GO

Alter DataBASE ADVENTUREWORKS
Add FILE
(
NAME=Productos_X,
FILENAME='C:\AdventureWorks\Productos_X00.ndf',
SIZE=1000MB,
FILEGROWTH =150MB
) TO FILEGROUP Productos_X;
GO

IV. Posteriormente será necesario crear una función de partición

Create Partition Function FnParticionesProductos(Int)
as Range Left
For Values (200,400,600,800,2000);

a. Es posible consultar su creación mediante el query :

SELECT * FROM sys.partition_functions WHERE name = 'FnParticionesProductos'
SELECT * FROM sys.partition_range_values


V. Una vez creada la función de partición se procede a crear un schema de partición, el cual en conjunto con la función antes creada se encargaran de encapsular el que SQL sepa a que FileGroup y FileData se grabara o se leerá nuestra información


Create Partition Scheme SchemaProductos
as Partition FnParticionesProductos
TO (
Productos_200,
Productos_400,
Productos_600,
Productos_800,
Productos_2000,
Productos_X
)

a. Es posible consultar el schema de partición que se ha creado mediante el query :
Select * from sys.partition_schemes

b. La información en nuestra implementación caería de la siguiente manera

Filegroup Particion Min ProductoID Max ProductoID
Productos_200 1 -Max(Int) 200
Productos_400 2 201 400
Productos_600 3 401 600
Productos_800 4 601 800
Productos_2000 5 801 2000
Productos_X 6 2001 Max(Int)


VI. Utilizaremos la tabla Sales.SalesOrderDetail para mostrar cómo es posible particionar una tabla poblada
a. Consultamos sus índices,


Normalmente usaria el store procedure Sp_helpIndex para verificarlos, sin embargo al tener un schema diferente al que tengo como predeterminado en mi usuario , me quede pensando y arme un script que nos da los indices, finalmente un problema nos da una oportunidad de hacer las cosas de una forma diferente, asi que :

Select Distinct Indexes.Name,Indexes.Index_Id,Type_desc,Columns.Name from sys.indexes Indexes
Inner join sys.columns Columns on Indexes.Object_ID=Columns.Object_ID
Inner join sys.index_columns IndexColumns
ON Columns.Object_ID=IndexColumns.Object_ID and Columns.Column_ID=IndexColumns.Column_ID
where Columns.object_ID=(Select object_id
from sys.tables where name='SalesOrderDetail')
Order by Indexes.Index_Id





b. Se identifican los índices que están involucrados con la columna mediante la cual deseamos crear un índice particionado.

Drop index AK_SalesOrderDetail_rowguid on Sales.SalesOrderDetail
Drop index IX_SalesOrderDetail_ProductID on Sales.SalesOrderDetail



c. Recreamos el índice conforme a nuestros requerimientos


Create NonClustered Index NC_ProducID_SalesOrderDetail
On Sales.SalesOrderDetail(ProductID)
on SchemaProductos(ProductID)


d. Posteriormente podemos consultar la información de cómo esta ahora particionada nuestra tabla

Select * from sys.partitions where object_ID=(Select object_id
from sys.tables where name='SalesOrderDetail')



Ejemplo :Para identificar que tenemos grabado en una partición especifica

Select $Partition.FnParticionesProductos(777)

Ejemplo: Para identificar a que partición le corresponde un valor.

Nota: Particionar una tabla sin datos es aun más sencillo pues únicamente tenemos que hacer referencia al esquema de partición al crear la tabla.
Beneficios

Tener una estrategia de tablas particionadas le permite a SQL Server evitar leer del mismo FileGroup y FileData la información solicitada, si la condición SARG se hace sobre un índice particionado SQL sabrá en que segmento debe buscar por lo que es mas rápido el acceso a la información.

Si los archivos de datos se encuentran en unidades de disco duro independiente SQL puede efectuar lecturas paralelas.

Nota Final: Como se menciono, el detalle de la estrategia a implementar usando particiones depende de la infraestructura disponible, no es tarea sencilla y debe hacerse con el respeto que se merece esta tarea respaldando siempre la BD y los scripts de los constraints e índices a borrar y recrear en caso de tablas ya pobladas.

Referencias
http://msdn.microsoft.com/en-us/library/ms186307(SQL.90).aspx
http://msdn.microsoft.com/en-us/library/ms179854.aspx
http://msdn.microsoft.com/en-us/library/ms187802(v=SQL.90).aspx
http://msdn.microsoft.com/en-us/library/ms175012(v=SQL.90).aspx
http://msdn.microsoft.com/en-us/library/aa275464(SQL.80).aspx

Diccionario de datos

Si alguien se pregunta sobre la posibilidad de obtener la definiciòn de tablas (Nombre, tipo de dato, longitud, nulo) en SQL, la respuesta es si.





Es realmente muy sencillo, existen otras formas de obtenerlo , les comparto una :


Declare @Tabla varchar(100)
Set @Tabla= 'CAT_TIPOS_ARCHIVO'
Select Schemas.Table_Name [Tabla],schemas.Column_name [Columna],
EP.Value [Descripcion]
,Schemas.data_type [Tipo de dato],
col.max_length [Longitud],col.is_nullable [Permite Nullo]from
INFORMATION_SCHEMA.COLUMNS Schemas
Inner join (Select * from Sys.columns
where object_id=(Select Object_ID from sys.objects where name=@Tabla)
) Col
On Schemas.Column_name=Col.Name
Left Join sys.extended_properties EP
on EP.Major_id=Col.object_id and EP.minor_id=Col.Column_id
where Schemas.Table_Name=@Tabla
EXEC sys.sp_addextendedproperty @name=N'MS_Description',
@value=N'Clave del usuario que modifico el registro' ,
@level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',
@level1name=N'CAT_TIPOS_ARCHIVO', @level2type=N'COLUMN',
@level2name=N'TAR_CVE_USUARIO';





Espero sea de su utilidad



Saludos

lunes, 14 de junio de 2010

Habilitar AWE

Por Default el máximo de memoria que puede utilizar SQL Server en un sistema operativo de 32 Bits es de 2 GB , sin deseamos poder acceder a mas memoria es necesario habilitar AWE lo cual no per-mitirá usar hasta 3 GB en un sistema operativo de 32 Bits. Describiremos los pasos para habilitarlo.

A. Procedemos a habilitar el mostrar opciones avanzadas en la instancia instalada y habili-tamos AWE

sp_configure 'show advanced options', 1
RECONFIGURE
GO
sp_configure 'awe enabled', 1
RECONFIGURE
GO
B. Redimensionamos los limites de memoria a usar

sp_configure 'min server memory', 1024
RECONFIGURE
GO
sp_configure 'max server memory', 3144
RECONFIGURE
GO


C. Modificamos el archivo Boot.Ini ,


Agregue lo siguiente al final de la línea de inicio que incluye el modificador /fastdetect: /3GB



Referencias
http://msdn.microsoft.com/es-es/library/ms190731.aspx
http://technet.microsoft.com/es-es/library/bb124810(EXCHG.65).aspx

Indices perdidos

Cuando creamos nuestros objetos de base de datos, concretamente Tablas y vistas indexa-das suele ocurrir que no contemplemos todos los índices que serán necesarios para satisfacer nuestras llamadas a la Base de Datos.

Una forma de identificar índices perdidos (no existen) es hacer uso de la vista del sistema sys.dm_db_missing_index_details la cual nos regresa información detallada de los índices que hacen falta en nuestra Base de datos. Si nuestra labor fuera supervisar el funcionamiento de un servidor o un conjunto de servidores con una o más instancias de SQL Server lo correcto sería empezar a dar prioridad a las Bases de Datos con un número de índices no creados más alto, para lo cual podemos hacer uso del siguiente script:

SELECT
DB_NAME(database_id) [Nombre de base de datos]
, count(*) [Numero de indices perdidos]
FROM sys.dm_db_missing_index_details
GROUP BY DB_NAME(database_id)
ORDER BY 2 DESC;


Una vez que sabemos que Base de datos es la que nos puede involucrar en mayor medida índices que no fueron creados el siguiente paso es identificar las tablas y columnas que corresponden a dichos índices.



Hasta aquí hemos visto en que nos podemos apoyar, nuestro siguiente paso es generar un script que nos retorne el detalle de los índices perdidos:

SELECT TOP 100
DB_NAME(database_id) [Nombre de base de datos]
,[Costo total] = ROUND(avg_total_user_cost * avg_user_impact * (user_seeks + user_scans),0)
, avg_user_impact
, TableName = statement
, [EqualityUsage] = equality_columns
, [InequalityUsage] = inequality_columns
, [Include Cloumns] = included_columns
FROM sys.dm_db_missing_index_groups g
INNER JOIN sys.dm_db_missing_index_group_stats s
ON s.group_handle = g.index_group_handle
INNER JOIN sys.dm_db_missing_index_details d
ON d.index_handle = g.index_handle
ORDER BY [Nombre de base de datos],[Costo total] DESC;


Una vez analizado este punto, podemos darnos cuenta que el crear los índices perdidos no es un proceso que se pueda automatizar de una manera por decir sencilla, ya que la creación de un índice que involucra más de una columna y que incluso tiene columnas de tipo INCLUDE es una tarea que debe ser revisada detalladamente, pues el orden de las columnas en que establezcamos la creación de nuestros índices puede marcar la diferencia entre un índice bien construido y uno mal o regularmente construido.

Indices mas costosos no utilizados

Identificar indices mas costosos no utilizados

Las operaciones de Inserción, actualización y eliminación provocan un mantenimiento a los índices relacionados a nuestra tabla. Recordemos que SQL determina el mejor camino (plan de ejecución) con el que resolverá una consulta, se apoya en las estadísticas de las columnas de las tablas involucradas y en el porcentaje de fragmentación de los índices, si bien es cierto que podemos forzar el uso de un índice cuando se resuelve una consulta esto debe hacerse solo en casos extremos y con un amplio análisis.

El punto al cual pretende dirigir esta tema es que si un índice no esta siendo usado por SQL , su presencia puede afectar el desempeño de nuestras consultas ,por lo cual es importante identificarlos y borrarlos una vez que hemos comprobado que las columnas involucradas tie-nen sus estadísticas actualizadas y que a pesar de eso SQL no los usa.

Una vez mas nos apoyaremos en las vistas dinámicas del sistema , sys.dm_db_index_usage_stats es el nombre de la vista que usaremos y las columnas de nuestro interés son:

database_id : Id. de la base de datos en la que se define la tabla o vista.
object_id : Id. de la tabla o vista en la que se define el índice.
index_id : Id. del índice.
user_seeks : Número de consultas de búsqueda realizadas por el usuario.
user_scans : Número de consultas de recorrido realizadas por el usuario.
user_lookups : Número de búsquedas de marcadores realizadas por consultas de usuario.
user_updates : Número de consultas de actualización realizadas por el usuario.
last_user_seek : Hora en que el usuario realizó la última búsqueda.
last_user_scan : Hora en que el usuario realizó el último recorrido.
last_user_lookup : Hora de la última búsqueda del usuario.
last_user_update : Hora en que el usuario realizó la última actualiza-ción.
system_seeks : Número de consultas de búsqueda realizadas por el sistema.
system_scans : Número de consultas de recorrido realizadas por el sistema.
system_lookups : Número de búsquedas realizadas por consultas del sistema.
system_updates : Número de consultas de actualización realizadas por el sistema.

Para recolectar la información correspondiente a los índices más costosos no utilizados de-berán ejecutar el siguiente script :

SELECT OBJECT_NAME(ind.object_id) [Tabla], ind.name [Indice]
FROM Sys.Indexes ind
INNER JOIN Sys.Objects obj ON obj.object_id = in-d.object_id
WHERE OBJECTPROPERTY(obj.object_id,'IsUserTable') = 1
AND NOT EXISTS
(SELECT 1 FROM Sys.dm_db_index_usage_stats usg
WHERE usg.object_id = ind.object_id
AND usg.index_id = ind.index_id
AND ind.index_id = usg.index_id
) and ind.name is not null
ORDER BY
[Tabla],
[Indice]




Referencias
http://technet.microsoft.com/es-mx/library/ms188755.aspx

Optimizar TEMPDB

TEMPDB es una base de datos de SQL (sistema), optimizarla nos ayudara a que nuestras operaciones sean mas rapidas, en busca de dar una receta de cocina se dira lo siguiente :

I. Modo de recuperación : Establecer el modo de recuperación de la BD en SIMPLE , se puede consultar el modo actual de recuperación mediante el siguiente script

Select [name] ,state_desc ,recovery_model_desc From sys.databases where Name='TEMPDB'

Posteriormente en caso de encontrar en un modo diferente a [SIMPLE], se puede modificar con el script :

ALTER DATABASE tempdb SET RECOVERY SIMPLE;

II. Ubicación : Colocar TEMPDP en un Disco duro diferente al disco duro del sistema operativo y diferente al disco duro donde radica la BD del SIP.
a. SQL Server 2005 efectúa operaciones en TEMPDB al crear tablas tempora-les, agrupar, ordenar , utilizar cursores, entre algunas otras. Cuando movemos de ubicación TEMPDB logramos reducir el nivel de contención al disco duro.
b. Debe ser un disco duro independiente ,no una partición.

III. Data File : Crear un archivo de datos por cada procesador existente en el servidor, es decir si el servidor tuviera 8 procesadores deberíamos tener 8 archivos de datos. Asigne el mismo tamaño a cada archivo creado

IV. Establecer el tipo de crecimiento de los archivos en tamaño ,la configuración deberá corresponder a que la suma de los archivos de datos de TEMPDB no excedan 500 Megas. Si se desea un valor más exacto deberá hacerse un análisis de la capacidad de escritura en el disco duro del procesador,

a. Para evitar que se produzcan tiempos de espera de bloqueo temporal, se recomienda limitar la operación de crecimiento automático a unos dos minutos

Por ejemplo, si el subsistema de E/S puede inicializar un archivo a 50 MB por segundo, se deberá establecer el incremento de FILEGROWTH en 6 GB como máximo.

Como siempre podemos recurrir a las recomendaciones de Microsoft, aqui una excelente referencia


jueves, 27 de mayo de 2010

Casos de Exito SQL Server

Muchas veces he escuchado comentarios sobre que SQL Server no es tan bueno, no soporta mucha informacion y cosas por el estilo. Yo no coincido con ellos, SQL Server es un excelente producto que solo debemos saber explotar.

Les dejo la siguiente liga, yo pues entre mas conosco el producto mas me enamoro de el.

http://www.microsoft.com/sqlserver/2008/en/us/case-studies.aspx

domingo, 23 de mayo de 2010

Identificando planes de ejecuciòn ineficientes


El tema de optimizar nuestra Base de datos es bastante amplio, esta entrada será solo una de una serie que pretendo subir tocando diversos puntos para identificar y solucionar problemas en nuestra Base de Datos.
Recuerdo haber tenido contacto con un administrador de Base de datos al que se le cuestionaba el rendimiento tan pobre de la Base de datos, su respuesta era mostrarnos una grafica del Monitor de performance en donde se veían picos en el servidor y cuellos de botella en el procesador. ¿Pero eso es suficiente? La respuesta es no, los cuellos de botella en nuestro procesador se pueden deber a diversas causas, es decir eso solo es lo que se ve por fuera, pero tenemos que investigar que es lo que exactamente hizo que el procesador estuviera tan demandado.
Para hacerlo una opción es ejecutar una traza y analizarla pero también debemos siempre buscar apoyarnos en las DMVs que SQL desde la versión 2005 nos proporciona, estas son vistas y funciones que nos proporcionan detalle del comportamiento de nuestra Base de datos.
En esta ocasión las usaremos para obtener un script que nos permita identificar las consultas más costosas en términos de procesador, el resultado se debe analizar a fondo, pues un escenario posible es que tengamos una consulta sumamente costosa pero que se ejecuta solo esporádicamente y quizá otra menos costosa pero que se ejecuta cientos o miles de veces, entonces debemos nosotros saber a dónde apuntar nuestra atención.
Select Top 10
total_worker_time/execution_count as avg_cpu_cost,plan_handle,
execution_count,(Select substring(text,statement_start_offset/2+1,
(Case when Statement_end_offset=-1
Then len(convert(nvarchar(max),text))*2
Else Statement_end_offset
End - statement_start_offset)/2)
From sys.dm_exec_sql_text(sql_handle)) as query_text
From sys.dm_exec_query_stats
Order by [avg_cpu_cost] desc

Lo que nos regresa esta consulta es el porcentaje promedio de CPU que es utilizado cada vez que se ejecuta una consulta , el número de veces que es invocada esa consulta , el Id del plan de ejecución y por último la consulta en sí, nuestro trabajo es una vez identificadas poder aislar el porqué de ese costo.
Nota: La ejecuciòn de esta consulta unicamente nos traera los planes que SQL mantiene en el cache, es decir pudieran existir otros que no nos arroje la consulta . Para resolver este problema la recomendaciòn de Microsoft es que ejecutemos periodicamente esta consulta para obtener informaciòn mas completa.

Por ahora con esto termino este post, esto solo es una pequeña parte de las cosas que tenemos que considerar cuando buscamos optimizar nuestra Base de datos, saludos y ojala les sea de utilidad.

Schemas

SQL Server desde la version 2005 nos ofrece como una de las nuevas características el uso de schemas, ¿para qué sirven, que son, realmente es importante calificar los nombres de los objetos con el esquema al que pertenecen?

Los esquemas son objetos que sirven como contenedores de otros objetos, para la gente que es de tecnología de .NET la analogía serian los NameSpaces. Los esquemas nos permiten agrupar objetos en ellos.

La sintaxis para crear un esquema es

Create Schema [NombreEsquema] AUTHORIZATION [NombreUsuario]

Tambien lo podemos crear mediante el asistente para lo cual abrimos el Managment Studio/Object Explorer, nos colocamos en el nodo de nuestra Base de Datos, despues en el nodo [Security] y dentro de el en el nodo [Schemas] podemos crearlo haciendo click con el botòn derecho en la opcion Nuevo esquema.


Vamos a crear nuestro esquema y continuaremos profundizando sobre su uso

Create Schema Audit;

Como se menciono antes los esquemas nos permiten contener objetos de Base de datos.
Crearemos un par de tablas que estaran dentro del esquema Audit.

Create Table Audit.Tbl_Eventos
(
ID Int Identity(1,1) Primary key
,Evento varchar(100)
);
GO
Create Table Audit.Tbl_Usuarios
(
ID Int Identity(1,1) Primary key
,Usuario varchar(100)
);

Veamos como funciona esto mendiante un select a nuestras tablas antes creadas
Lo que muchas veces hacemos al invocar el nombre de un objeto es apuntar directamente a el con algo como:

Select * from Tbl_Eventos;

Lo cual si lo ejecutan veran que les ha generado un error ¿Por qué? , la razon es porque desde SQL 2005 los nombres de objetos se califican mediante cuatro partes [DBServerName].[DBName].[Schema].[Table], en la version de SQL 2000 los nombres se calificaban de una manera un poco distinta y era mediante [DBServer].[DBName].[ObjectOwner].[Table]

Analizando lo anterior,lo que debemos entender es que SQL antes calificaba los nombres de objetos directamente asociandolos al propietario.Desde SQL 2005 los objetos no le pertenecen directamente a un usuario, a quien estan vinculados es a un esquema y el esquema tiene un dueño, esto nos habre posibilidades de las cosas que podemos hacer con los esquemas.
Nuestra instrucción Select debe quedar de la forma

Select * from Audit.Tbl_Eventos;

¿Por qué es bueno indicar el nombre del esquema? SQL al resolver una petición y no tener especificado el esquema al que pertenece el objeto invocado tiene que determinar si existe en el esquema que tiene por default el usuario firmado, parece poco, sin embargo todo es importante y al calificar nuestra tabla con el esquema nos aseguramos de indicarle a SQL en que esquema esta nuestra tabla.
Los esquemas además nos sirven muy bien para asegurar los accesos a nuestros objetos, imaginemos que tenemos cien tablas dentro del esquema Audit y que tenemos que dar acceso de solo lectura a todas ellas a un usuario nuevo, siempre que pensemos en resolver algo lo debemos hacer en términos de “con el menor esfuerzo”.
La instrucción que nos permite asignarle algún permiso
GRANT [Permiso] ON SCHEMA::[SchemaName] TO [NombreUsuario]

Ejemplo:
CREATE LOGIN MyLogin WITH PASSWORD='pa$w00rd'
GO
CREATE USER MyLogin FROM LOGIN MyLogin


GRANT SELECT ON SCHEMA::[Audit] TO [MyLogin]
¿Pero que sucede si un dia necesitamos cambiar de un esquema la ubicación de una tabla? Eso es relativamente sencillo.
Con la instrucciòn Alter Schema lo podemos hacer
Ejemplo
Create schema Audit2
--transferir objetos
Alter schema Audit2 Transfer Audit.Tbl_Eventos

Como podemos ver es realmente simple cambiar de esquema una table.
Por último me gustaría mencionar algo que se llaman Sinónimos en SQL y que nos pueden ahorrar un poco de trabajo al nombrar objetos.
Los sinónimos no son más que objetos que nos sirven para asignarle un alias a otro objeto, si este está en un esquema podemos ahorrarnos entonces tener que escribir el esquema y el nombre completo del objeto.
Ejemplo:
Create Synonym AD For Audit2.Tbl_Eventos
Select * from Ad

Con esto terminamos el post sobre esquemas, solo quiero volver a mencionar la importancia de pensar en términos del menor esfuerzo, ¿Qué significa, que implica? Lo que implica es que sepamos diversas soluciones a un problema, que sepamos o que busquemos cual es la mejor y eso con el pasar del tiempo nos dara oportunidad de tener más tiempo que debemos ocupar en aprender, aprender y seguir aprendiendo. Es redondo, cuando nos preocupamos y ocupamos por lo importante antes que lo urgente nos hace mejores profesionistas. Recuerdo con afecto a un jefe hace años , bastante severo en algunas cosas, pero con mucha experiencia y la verdad con mucha sabiduría.

Un día llamandome la antencion me dijo :Charly imaginate que vas en carretera, conduces tu auto, te urge llegar a tu destino es urgentísimo llegar, pero te das cuenta que la gasolina no te alcanzara, ¿Qué debes hacer? La reflexion es que lo urgente es llegar , pero lo importante es que tengas como hacerlo, se tendria uno que dar un tiempo para recargar gasolina.

Así mismo en nuestro trabajo tenemos lo urgente y lo importante, lo importante es que nuestro conocimiento sea más amplio en buscar la mejor forma de resolver un problema lo cual nos llevara a ser mejores profesionistas y a encajar mejor en nuestro empleo, a poder ayudar de una mejor forma a la empresa para la que trabajamos y eso se los aseguro se traducirá en bienestar para nosotros.
Esto no quiere decir que debemos ser obsesivos con la perfección en más de una ocasión yo he tenido que decidir que así como esta mi código debe quedarse aunque no sea la mejor forma, porque si no nunca terminaríamos un proyecto.

Les dejo mis mejores saludos, y espero les sea de utilidad este post.

sábado, 22 de mayo de 2010

Guia de Instalaciòn de SQL Server

¿Como instalar SQL Server?, en esta ocasiòn la intención es compartirle un documento de Word que contiene los pasos y una breve explicación sobre cada uno de ellos. Me resulta a veces un poco complicado el tema de pegar imágenes en el post y como este tiene muchas decidí mejor subir el archivo y dejarles la liga.

http://docs.google.com/Doc?docid=0AdMayil2lBnHZGRzNHN3c3ZfMTk4YjY0OXpnaw&hl=en

o

Download ...
http://docs.google.com/leaf?id=0B9Mayil2lBnHYTRmODZiMjAtZGVlZi00ZmRmLWE1MzktMDFjYzhjNGM0NTYz&hl=en

Saludos y espero les sea de utilidad

jueves, 20 de mayo de 2010

Save Transaction

Hablemos un poco de transacciones, lo ideal es que nuestras peticiones a la Base de Datos sean ACID, que quiere decir Atómicas, Consistente, Aisladas y durables, siempre que una serie de instrucciones a la Base de datos cumple con estas características se denomina transacción.
ACID : (Atómicas)Lo que quiere decir es que nuestras peticiones sean ejecutadas como un solo bloque en el que se ejecuta todo o nada ,(Consistente) en el que se garantice que la integridad de nuestra base de datos no es alterada ,(Aisladas) que nuestras peticiones estén aisladas u ofrezcan un tipo de aislamiento que nos garantice que otras peticiones sobre los mismos datos no afecten las nuestras y que una vez que se ejecutaron los datos persistan y no se pierdan (durable).
El tema de las transacciones realmente es muy amplio, en este post únicamente pretendo abordar un escenario específico que son los puntos de retorno en una transacción.
En nuestra vida profesional nos puede tocar enfrentarnos a escenarios en los cuales tengamos algún proceso de SQL que requiera que podamos hacer un Rollback parcial. En otras palabras es posible con esto establecer puntos de retorno para nosotros en donde podamos deshacer solo parte de nuestra transacciòn.
Para poder crear puntos de retorno lo hacemos mediante la instrucciòn Save Transation [NOMBRE TRANSACTION] , en nuestro codigo podemos tener tantos como querramos y cuando necesitemos regresar a un punto en especifico lo que se ejecuta es RollBack Transaction [NOMBRE TRANSACTION].

Para dejar atrás la teoría necesitaremos crear una tabla que utilizaremos en nuestro ejemplo.
--Creamos la tabla que usaremos
If Not exists(Select * from sys.Objects where name='Tbl_Clientes'
and Type='U')
Create Table dbo.Tbl_Clientes
(
ClienteId Int Identity(1,1) Primary Key
,Nombre varchar(30) Not null
,[Fecha Registro] DateTime Not null Default Getdate()
)

GO

--
Begin Transaction
Insert into dbo.Tbl_Clientes(Nombre)
Values('Julio Rodriguez');
Insert into dbo.Tbl_Clientes(Nombre)
Values('Oscar Rodriguez');
Insert into dbo.Tbl_Clientes(Nombre)
Values('Lourdes Rodriguez');


Save Transaction PrimerBloque
Select ClienteId,Nombre,[Fecha REgistro] from dbo.Tbl_Clientes

Insert into dbo.Tbl_Clientes(Nombre)
Values('Omar Ramirez');
Insert into dbo.Tbl_Clientes(Nombre)
Values('Mario Ramirez');
Insert into dbo.Tbl_Clientes(Nombre)
Values('Fernando Ramirez');

Save Transaction SegundoBloque
Select ClienteId,Nombre,[Fecha REgistro] from dbo.Tbl_Clientes


Insert into dbo.Tbl_Clientes(Nombre)
Values('Dante Romero');
Insert into dbo.Tbl_Clientes(Nombre)
Values('Ignacio Romero');

Save Transaction TercerBloque
Select ClienteId,Nombre,[Fecha REgistro] from dbo.Tbl_Clientes


Rollback Transaction SegundoBloque
Select ClienteId,Nombre,[Fecha REgistro] from dbo.Tbl_Clientes


Commit Transaction;
GO
El punto a ejemplificar es que las posibilidades de lo que podemos hacer con esto ,pues estan en nuestras manos, nosotros podemos condicionar que bajo algun tipo de error o logica efectuemos un commit de nuestra transaccion y podamos hacer a un lado algo que se ejecuto pero que al final no nos interesa.
Espero les sea de utilidad

domingo, 16 de mayo de 2010

Intersect

Alguna vez platicaba con algunos compañeros expresándoles que SQL Server no solo son sentencias Insert y Select, es necesario que nosotros como desarrolladores conozcamos las diferentes ofertas que nos da SQL para resolver diferentes problemas.
En esta ocasión quiero platicarles de una nueva forma de unir tablas que esta disponible desde SQL Server 2005 y esto es con la instrucción Intersect.
Para poder ejemplificar y buscar dejar claro este tema crearemos un par de tablas y ejecutaremos una unión con INNER y la otra con Intersect para mostrar las diferencias.

Create Table dbo.Tbl_Clientes
(
IdCliente Int Identity(1,1) Primary key
,Nombre varchar(200)
,observaciones varchar(400)
)
Go

Insert into dbo.Tbl_Clientes
Values ('Cliente 1','NA');
Insert into dbo.Tbl_Clientes
Values ('Cliente 2','NA');
Insert into dbo.Tbl_Clientes
Values ('Cliente 3','NA');
Insert into dbo.Tbl_Clientes
Values ('Cliente 4','NA');

Go

Create Table dbo.Tbl_Facturas
(
IdFactura Int Identity(1,1) primary key
,idCliente Int Not null
,Fecha Datetime Not null Default Getdate()
,Iva float Not null
,Importe float Not null
,Total float Not null
,Observaciones varchar(max)
)
GO
Alter Table dbo.Tbl_Facturas add Constraint Fk_Cliente
Foreign key (idCliente) References dbo.Tbl_Clientes(IdCliente)
Select IdCliente,Nombre from dbo.Tbl_Clientes;
Insert dbo.Tbl_Facturas
Values (1,getdate(),16,100,116,'Factura A');

Insert dbo.Tbl_Facturas
Values (1,getdate(),16,100,116,'Factura A.1');

Insert dbo.Tbl_Facturas
Values (1,getdate(),16,100,116,'Factura A.2');

Insert dbo.Tbl_Facturas
Values (2,getdate(),16,100,116,'Factura B');

Insert dbo.Tbl_Facturas
Values (3,getdate(),16,100,116,'Factura A');

Insert dbo.Tbl_Facturas
Values (4,getdate(),16,100,116,'Factura A');

Select IdFactura ,idCliente ,Fecha ,Iva ,Importe ,Total ,Observaciones From dbo.Tbl_Facturas
Ahora veremos justamente las diferencias entre usar INNER e Intersect

Al crear un Select con una union interna veremos que nos regresa seis filas
Select Tbl_Clientes.IdCLiente from dbo.Tbl_Clientes
Inner join dbo.Tbl_Facturas on Tbl_Clientes.IDCliente=Tbl_Facturas.IDCliente

Si agregamos la condiciòn Distinct recuperamos cuatro filas
Select Distinct Tbl_Clientes.IdCLiente from dbo.Tbl_Clientes
Inner join dbo.Tbl_Facturas on Tbl_Clientes.IDCliente=Tbl_Facturas.IDCliente

Si hacemos uso de Intersect veremos que hace lo mismo que el anterior pero de una forma mas facil
Select IdCliente from dbo.Tbl_Clientes
Intersect
Select IdCliente from dbo.Tbl_Facturas


Es decir, Intersect devuelve los valores distintos retornados por las consultas del lado izquierdo y derecho de la consulta
La condición que tenemos para poder utilizar Intersect es que las consultas que estamos ejecutando tengan las mismas columnas y tipo de datos
Saludos y hasta la proxima

Download AdventureWorks

Practicar, practicar y volver a practicar, es la única forma de mejorar nuestro perfil, Microsoft nos proporciona ejemplos en su documentación y mayormente esta referida a las bases de datos de AdventureWorks. SQL Server 2005 y 2008 no instalan por default las bases de datos de ejemplo, si te interesa descargarlas puedes hacerlo desde el sitio:

http://sqlserversamples.codeplex.com/

En donde además encontraras ejemplos.

sábado, 15 de mayo de 2010

El Rol de un arquitecto

Hola a todos nuevamente, es un placer para mi poder contribuir en algo a las nuevas generaciones y porque no también a los que ya estamos encaminados, ciertamente todos los días se puede aprender algo.

Para comenzar este post quisiera poner una pregunta en la mesa ¿nos gusta desarrollar, te apasiona? ¿Quisieras cambiar de actividad y el desarrollo dejarlo en un par de años? . Creo que es válido que tengamos aspiraciones distintas, que nuestros gustos varíen. Si eres uno a quienes les apasiona la tecnología y el desarrollo seguramente esperas crecer tu perfil técnico, obtener alguna certificación y en algún momento erguirte como arquitecto de software.
¿Pero es suficiente saber mucho? En mi opinión la respuesta es no, un arquitecto no solo debe ser un experto en tecnología, no es suficiente eso. Un arquitecto debe ser un excelente comunicador, de hecho si leemos sobre la certificación de Microsoft veremos el peso que ellos mismos le dan a las habilidades blandas de los candidatos.
Un arquitecto efectivamente debe tener un excelente nivel no solo en aplicaciones, creo yo que también en Bases de datos pues uno es el complemento del otro, pero además debe ser capaz de poder compartir su experiencia y guiar a su equipo de trabajo y no hablo de dar un ejemplo sino de ser el ejemplo, de respeto a sus compañeros ,de buscar crear un ambiente de camaradas, en el que la gente se reconoce imperfecta y que sabe pedir ayuda y recibirla; pues siempre es mejor en un proyecto poder ahorrarnos tres días de búsqueda e investigación si en el equipo tenemos a un experto.
Un arquitecto también debe poder hablar en el lenguaje de los usuarios entenderlos y no marearlos porque para ellos sus requerimientos, su funcionalidad debe hablarse en el lenguaje del negocio.
Un arquitecto debe buscar ser un mentor para su equipo, debe apostar por el bienestar del grupo, de su gente. Porque sabe que su papel depende del papel del equipo y que es el pieza clave en la tarea de guiarlos al éxito
Un arquitecto no tiene miedo de compartir su conocimiento porque todos los días hará algo por saber más que el día anterior y sabe también que cuando enseña aprende dos veces, que cuando se vuelve en mentor de un equipo de una persona perdurara en alguien mas y eso es una satisfacción en sí misma.
Un arquitecto debe lograr ser asertivo, tolerante y que su acción sea el detonante para que la gente que trabaje con él o a su lado se esfuerce por ser un mejor profesional.
Un arquitecto no sustituye la labor de un Project Manager, la función de administrar el proyecto sigue siendo del PM

No quisiera redundar en esto, mejor los refiero a las publicaciones de Microsoft en donde la revista Architecture Journal en su número 15 nos habla del The Role of an Architect .
Los invito a subscribirse a http://msdn.microsoft.com/en-us/architecture/bb410935.aspx

En resumen yo diría que el expertice técnico no basta la actitud es importante, es lo más importante, con una buena actitud nos abrimos puertas, nos ganamos un amigo, vencemos la frustración porque les aseguro que más de una vez es la actitud la que nos sacara de un problema aun por encima de los conocimientos técnicos y es redondo porque es una buena actitud la que nos hace humildes, nos permite aprender de los demás sin miedo y es lo que nos mueve a buscar ser mejores.

No pretendo que este post tenga la definiciòn de el Rol de un arquitecto, mi intenciòn solo es enumerar algunas cualidades, espero les sea de utilidad.

lunes, 10 de mayo de 2010

Capacitaciòn gratuita Microsoft

Hola nuevamente ,siguiendo mis convicciones sobre "el conocimiento que no se comparte no se esta utilizando de la manera correcta" les quiero compartir un recurso que recien encontre en esta busqueda de sitios y foros que puedan nutrir.
En el podremos encontrar cursos y carreras, pero para que nos les cuente mejor visitenlo.
Creo que nuestra mentalidad en general debe ser de compartir, de ver crecer a los demas y alegrarnos por ellos, de no conformarnos y de aspirar siempre a un futuro mejor, cuando logramos en nosotros cambiar nuestro comportamiento podemos darle la vuelta al problema de cambiar a la gente, recuerden : No puedes cambiar a la gente pero puedes cambiar tu, si cambias tu ,quiza puedas cambiar a tu hermano a tus hijos , a tus compañeros y entonces podremos cambiar a la gente.

Auditar Base de datos usando Trigger DDL

Recientemente un compañero me pregunto si existía forma de identificar en que fecha se agrego una columna a una tabla en la base de datos, en ese momento respondí que no aunque mi respuesta no fue completa.

Quiero en este blog compartir como debió terminar esa conversación .
SQL Server nos permite auditar lo que sucede en nuestra Base de datos con diferentes mecanismos como C2 Audit ,SQL Server Audit o incluso una traza y desde la versión de SQL 2005 se agrego el uso de trigers DDL(Data Definition Language)
Como siempre hago hincapié en que el objetivo de tener diferentes formas de atacar un problema es para usar lo más adecuado a nuestras necesidades y circustancias, por ejemplo si tenemos problemas de desempeño activar un mecanismo como C2 Audit nos podría generar aun más problemas, aun mas quizá solo nos interesa auditar cambios a la estructura de nuestra BD y no un nivel granular.


Para nuestro caso utilizaremos desencadenadores DDL para auditar cambios en la estructura de nuestra Base de datos, específicamente capturaremos todos los eventos del tipo DDL_DATABASE_LEVEL_EVENTS,a continucación se muestra un arbol de eventos para tener presentes que tipo de acciones estaremos auditando.








Los eventos se recuperan con la función EVENTDATA la cual retorna un XML con el ID del proceso, la fecha, el evento y la sentencia ejecutada


El script para crear y probar nuestro disparador DDL es :


CREATE DATABASE AuditDDL –Creamos l

use AuditDDL

Create schema Audit


CREATE TABLE Audit.DDL_DATABASE_LEVEL_EVENTS_LOG
(
ID uniqueIdentifier primary key Default NewId()
, Fecha datetime Not null
, DB_Session_User nvarchar(100) Not null default Session_User
, DB_System_User nvarchar(100) Not null default System_User
, DB_User_Name nvarchar(100) Not null default User_Name()
, DB_Nombre varchar(100) Not null default Db_Name()
, Host nvarchar(50) Not null default Host_Name()
, Evento nvarchar(1000) Not null
, Sentencia nvarchar(Max) Not null
)


GO


CREATE TRIGGER LogEventos
ON DATABASE
FOR DDL_DATABASE_LEVEL_EVENTS
AS
BEGIN
Set NoCount ON
DECLARE @data XML
,@Definicion varchar(1000)
SET @data = EVENTDATA();

INSERT Audit.DDL_DATABASE_LEVEL_EVENTS_LOG
( ID
, Fecha
, DB_Session_User
, DB_System_User
, DB_User_Name
, DB_Nombre
, Host
, Evento
, Sentencia )
VALUES
(NewId(),
GETDATE(),
CONVERT(nvarchar(100), Session_User),
CONVERT(nvarchar(100), System_User),
CONVERT(nvarchar(100), User_Name()),
CONVERT(nvarchar(100), Db_Name()),
CONVERT(nvarchar(100), Host_Name()),
@data.value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(100)'),
@data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(2000)')
) ;
END;
GO


Se que en la Web existen ejemplos similares e indudablemente en las referencias de Microsoft siempre encontraremos cada dato que necesitamos, mi idea es solo compartir algún ejemplo y en su caso mi interpretación ,creo que un ejemplo adicional nunca está de mas.

sábado, 8 de mayo de 2010

Reconocimiento Microsoft

Hola, les quiero compartir que hace unos días me llego un paquete por parte de Microsoft con un libro de certificación, una playera, una pluma, una carpeta, un tarjetero muy bonito de madera y un termo.

Antes de eso me habían escrito por mail para hacerme saber que me iban a enviar un reconocimiento por mi asistencia/participación en los eventos del 2009 este gesto considero es sumamente gratificante porque la realidad es que es un regalo solo por aprender. No dejen de buscar y asistir a eventos de Microsoft, en especial a los webcast que son sumamente enriquecedores.


sábado, 10 de abril de 2010

Crecimiento de archivos log y mdf, SQL Server

Establecer el crecimiento de nuestra Base de datos

Las bases de datos se conforman de dos tipos de archivos: Datos y Log.
Es preciso que cuando se cree una Base de Datos se establezca un tamaño adecuado para el archivo de datos y para el Log, por default el crecimiento del archivo Log esta en automático y a un 10%, sin embargo mantener estos valores la mayor parte de las veces no es una buena práctica. Imaginemos el escenario en que tendremos cuando nuestro Log sea muy pequeño y posteriormente cuando sea muy grande.

Si el archivo tuviera un tamaño digamos de 1 MB y el crecimiento está establecido en un 10% de forma automática estaría incrementando de tamaño muy frecuentemente afectando el performance de nuestra Base de Datos
Si nuestro archivo Log ha crecido bastante digamos 20 Gigabytes y el crecimiento está establecido de forma automático por porcentaje ¿Qué sucederá la siguiente vez que necesite crecer? El crecimiento del archivo tardara cada vez mas hasta que llegue a provocar un problema de TimeOut si coincide con operaciones de alta demanda

SQL internamente divide el Log de transacciones en pequeños fragmentos llamados VLFs (Virtual Log Files). Cuando no establecemos un tamaño adecuado al log de transacciones tendremos una gran cantidad de VLFs.

Una buena práctica es

A. Limitar el tamaño de nuestro Log de transacciones
B. Establecer su crecimiento en MB y no en porcentaje






Este razonamiento aplica igualmente para los archivos de datos y para el de Log, y si consideramos que podemos tener más de un archivo de datos y de Log de transacciones cuando necesite incrementar su tamaño, SQL incrementa el tamaño de todos los archivos.

Referencias
http://www.dbasupport.com.mx/index.php?option=com_content&view=article&id=11:Fragmentación%20en%20el%20log%20de%20Transacciones&catid=11:administracion&Itemid=2

Mantenimiento al LOG de transacciones ,SQL Server

Si nunca diéramos un mantenimiento al archivo de Log de transacciones este crecería indefinidamente, esto tiene dos implicaciones.

A. El espacio ocupado en disco duro
a. Cuando nuestro archivo Log ha ocupado todo el espacio disponible en el disco duro o ha llegado a su límite de crecimiento establecido el Log ya no puede crecer y si consideramos que cualquier transacción Insert, Delete , update se registra en el Log de transacciones tendremos un problema pues ninguna de estas transacciones podrá ejecutarse en la BD
B. Un problema de timeOut cuando el Log de transacciones necesite crecer y sea ya demasiado grande.

Es conveniente crear un plan de mantenimiento que respalde el Log de transacciones y posteriormente lo trunque, lo cual nos permitirá reducir el tamaño del Log de transacciones.

El siguiente script satisface nuestros requerimientos:


/* 1) Nombre de la BD a utilizar
Nota : El nombre de la BD debera ser re-emplazado por la BD en cuestion */
USE [MyDataBase]

/* 2) Permitirmos que todas las transacciones
en el Buffer pendientes hagan commit */
CHECKPOINT
GO

/* 3)Agregamos un medio de respaldo para almacenar el backup del LOG
Nota: La ruta o dispositivo de respaldo debera ser re-emplazada
por una ruta valida */
Declare @Mes varchar(2) --variable para guardar el mes
,@dia varchar(2) --variable para guardar el dia
,@Anio varchar(4) --variable para guardar el año
,@Fecha varchar(10) --variable para guardar la fecha
,@Archivo varchar(100) --variable para guardar el nombre del archivo
Select @Mes=Day(getdate()),@dia=Month(getdate()),@Anio=Year(getdate())

Select @Fecha= @Mes+ @dia+ @Anio , @Archivo='C:\Respaldos\LogMiBase_db' + @Fecha + '.bak'


/* Eliminamos el dispositivo de respaldo para evitar un error
en caso de que ya existiera */
if Exists (Select * from sys.backup_devices where name='CopiaMiBase_db')
exec sys.sp_dropdevice 'CopiaMiBase_db'

/* Linea para agregar el dispositivo de respaldo
Nota : El nombre del dispositov y/o las caracteristicas del mismo
deberan ser re-emplazadas por las que satisfagan nuestros
requerimientos*/
EXEC sp_addumpdevice 'disk'
,'CopiaMiBase_db'
,@Archivo

/* 4)Respaldamos el Log de transacciones y la BD
Nota: El nombre de la Base de datos y el LOG
debera ser re-emplazado por el nombre real de la BD en cuestion
y por el nombre logico del archivo LOG de transacciones
*/
BACKUP DATABASE [MyDataBase] TO CopiaMiBase_db
BACKUP LOG [MyDataBase] WITH TRUNCATE_ONLY

--5)Establecemos un tamaño adecuado al Log de transacciones
DBCC SHRINKFILE ('MyDataBase_log', 100)
/*[100] debera ser re-emplazado por el tamaño
adecuado para el LOG de la BD en cuestion */

Lo que restaría es crear un JOB que se encarge de ejecutar nuestras sentencias antes vistas. Espero que sea de utilidad este ejemplo, se que en la red existen muchos, pero uno mas no cae mal.

Mantenimiento a Indices ,SQL Server

MANTENIMIENTO A INDICES

Hola de nuevo, en esta ocasión trataremos un tema no solo interesante, me parece que sumamente importante y al cual casi nunca le damos el lugar que se merece.

Al crear nuestros objetos de Base de datos y específicamente tablas y vistas (indexadas) hacemos uso de índices. Pero un índice que hoy funciona y que el motor de SQL utiliza al resolver una consulta no necesariamente funcionara al pasar el tiempo.

¿Qué ocurre cuando nuestras Bases de datos OLTP son usadas? Su naturaleza nos indica que miles o quizá cientos de miles de registros se agregaran a nuestras tablas, los registros también serán actualizados y eliminados, esos eventos provocaran que nuestros índices sufran de fragmentación interna , externa o ambas e irremediablemente con esto afectaran el performance de nuestras consultas , pues SQL al resolver una consulta determina cual es el mejor camino a seguir (plan de ejecución) y si nuestros índices están fragmentados no los usara y con ellos nuestras consultas sufrirán un decremento en el tiempo de respuesta.

Fragmentación interna:
La unidad mínima de almacenamiento de nuestras bases de datos son las paginas, en ellas se almacenan entre otras cosas los índices cuando la información de nuestras tablas es borrada se libera el espacio que usaba en la pagina asignada, es entonces cuando se presenta la fragmentación interna, esto quiere decir que las paginas no están siendo usadas completamente y que quizá se necesiten mas de las necesarias.

Fragmentación externa:
La fragmentación externa es más delicada, y sucede cuando SQL necesita agregar más filas y no existe suficiente espacio es las paginas para alojarlas, entonces ocurre una división de las paginas (page Split), esa división mantiene el orden lógico de las filas en nuestro índice, pero no mantiene el orden físico de la pagina, usualmente cuando ocurre una división de paginas físicamente no quedaran adjunta a la pagina original. Esto nos lleva a que cuando las paginas no están en un orden físico podemos decir que tenemos fragmentación externa.

¿En que nos podemos apoyar para determinar que índices son necesarios reconstruir?
Existe una función de SQL Server que tiene por nombre sys.dm_db_index_physical_stats la cual nos permitirá identificar la fragmentación de índices. Los argumentos que recibe son:

sys.dm_db_index_physical_stats (@IDBD, null, null,null,'DETAILED')

database_id: ID de la Base de datos, el cual recuperaremos utilizando la función del sistema db_ID
object_id: Es el identificador de la tabla o vista a analizar, si establecemos null recuperaremos información de todas las tablas
index_id: Es el identificador del índice a analizar, si establecemos null recuperaremos información de todos los índices de una tabla.
partition_number: Es el número de partición del objeto (vistas/tablas particionadas), si establecemos null recuperamos información de todas las particiones
Mode: Este parámetro nos permite especificar el nivel del examen a realizar para obtener datos estadísticos

Las columnas de nuestro interés que devuelve la función son las siguientes:
object_id int Identificador de objeto de la tabla o vista en la que se encuentra el índice.
index_id int Identificador de índice.
0 = Montón.
avg_fragmentation_in_percent float Fragmentación lógica para índices o fragmentación de extensión para montones en la unidad de asignación IN_ROW_DATA.
El valor se mide como un porcentaje y tiene en cuenta varios archivos. Para obtener definiciones de fragmentación lógica y de extensión, vea la sección Notas.
0 para unidades de asignación LOB_DATA y ROW_OVERFLOW_DATA.
NULL para montones cuando mode = SAMPLED.
avg_page_space_used_in_percent float Porcentaje medio del espacio de almacenamiento de datos disponible utilizado en todas las páginas.
En el caso de un índice, el promedio se aplica al nivel actual del árbol b en la unidad de asignación IN_ROW_DATA.
En el caso de un montón, se trata del promedio de todas las páginas de datos en la unidad de asignación IN_ROW_DATA.
En el caso de unidades de asignación LOB_DATA o ROW_OVERFLOW_DATA, se trata del promedio de todas las páginas en la unidad de asignación.
NULL cuando mode = LIMITED.


Después de revisar brevemente lo que ocurre cuando se fragmenta un índice y de analizar la función sys.dm_db_index_physical_stats establezcamos los pasos para crear un plan de mantenimiento.

Se dice que un índice que está fragmentado en un porcentaje mayor al 10 % está fragmentado externamente

1. Creamos un procedimiento almacenado que determine que índices sufrieron fragmentación externa
El script correspondiente al procedimiento almacenado se puede abrir dando clic al icono, se coloca su contenido en el documento solo para referencias.
IF EXISTS (SELECT * FROM sysobjects WHERE type = 'P' AND name = 's_ReconstruirIndices')
BEGIN
PRINT 'Dropping Procedure s_ReconstruirIndices'
DROP Procedure s_ReconstruirIndices
END
GO

PRINT 'Creating Procedure dbo.s_ReconstruirIndices'
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

Create Procedure dbo.s_ReconstruirIndices
(
@NameBD varchar(100) /*Nombre de la Base de datos analizar*/
)
/******************************************************************************
** All rights reserved.
** File: dbo.s_ReconstruirIndices.sql
** Name: dbo.s_ReconstruirIndices
** Desc: NEVER EDIT THIS FILE.
**
** This template can be customized:
** Return values:N/A
** Called by: Puede ser invocado manualmente o por un plan de mantenimiento(JOB)
**
** Parameters: Nombre de la Base de Datos a analizar
** Input
** ----------
**
**
** Output Retorna Tres conjuntos de Filas
** .-El primer Result Set nos indica Los indices y el porcentaje de fragmentación
** que se encontro.
** .-El segundo Result Set nos indica las sentencias que se generaron para
** reconstruir indices
** .-El tercer Result set nos indica cuales sentencias no pudieron ejecutarse
**
**
** -----------
** Auth: Carlos Nabor Espinoza G
** Date: 1 Abril 2010
*******************************************************************************
** Change History
*******************************************************************************
** Date: Author: Description:
** -------- -------- -------------------------------------------
** 07/04/2010 Carlos Espinoza Creación
**
*******************************************************************************/
as
BEGIN

Declare @IDBD int --ID de la base de datos

set @IDBD=db_ID(@NameBD); --Recuperamos el ID de la BAse de datos

/*Cursor que analiza la Base de datos que deseamos para buscar
fragmentación en los indices
Declarar nuestro cursor como Fas_Forward lo optimiza,
lo hace de solo lectura y unicamente para adelante */
Declare IndicesFragmentados Cursor Fast_forward For
Select
OBJECT_SCHEMA_NAME(dt.object_id, @IDBD) SchemaName
,Object_name(DT.Object_id) [Tabla], si.name [Indice],
dt.avg_fragmentation_in_percent [Fragmentacion Externa]
, dt.avg_page_space_used_in_percent [fragmentacion Interna]
From
(Select Object_id,index_id,avg_fragmentation_in_percent, avg_page_space_used_in_percent
from sys.dm_db_index_physical_stats (@IDBD, null, null,null,'DETAILED')
Where Index_id <>0) as DT
Inner join sys.indexes si
on si.object_id =dt.object_id and si.index_id=dt.index_id

/*Tabla que almacenara los indices que estan fragmentados (TODOS) */
Create table #IndicesFragmentadosTodos
(
SchemaName varchar(20)
,Tabla varchar(100)
,Indice varchar(100)
,fragmentacionExterna float
,FragmentacionInterna float
)

/*Tabla que almacenara unicamente las sentencias
que corresponden a indices fragmentados externamente */
Create table #IndicesFragmentadosExternamente
(
Sentencia varchar(1000)
)

/*Tabla temporal que almacena unicamente las sentencias que no
pudieron ser ejecutadas */
Create table #IndicesFragmentadosExternamenteNoEjecutados
(
[Sentencia] varchar(1000)
,[Mensaje Error] varchar(1000)
,[Numero Error] Int
,[Severidad] Int
)

Declare @Schema varchar(20) /*Nombre del esquema al que pertenece la tabla*/
,@Tabla varchar(100) /*Nombre de la tabla al que pertenece el indice*/
,@Indice varchar(100) /*Nombre del indice*/
,@fragmentacionExterna float /*Porcentaje de fragmentación externa*/
,@FragmentacionInterna float /*Porcentaje de fragmentación Interna*/
,@Sentencia Nvarchar(1000)/*Sentencia SQL que se arma para reconstruir el indice*/

--Abrimos el cursor previamente declarado
Open IndicesFragmentados
Fetch IndicesFragmentados into @Schema,@Tabla,@Indice,@fragmentacionExterna,@FragmentacionInterna
While @@Fetch_status=0
/* Este ciclo nos permitira armar las sentencias sql
para reconstruir los indices.
Asi como alimentar las tablas que nos indicaran los
indices que se encontraron fragmenteados y
las sentencias que no pudieron ser ejecutadas */
Begin
Insert into #IndicesFragmentadosTodos([SchemaName],[Tabla],[Indice],[fragmentacionExterna],[FragmentacionInterna])
values (@Schema ,@Tabla ,@Indice ,@fragmentacionExterna,@FragmentacionInterna);
If @fragmentacionExterna>=10
/*Se clasifica como indice fragmentado externamente cuando
la fragmentación en porcentaje(avg_fragmentation_in_percent) supera un 10 % */
Begin
Set @Sentencia='Alter index All'
+ ' ON ' + @Schema + '.'+ @Tabla + ' REBUILD WITH (FILLFACTOR=90, ONLINE=ON);';
Insert into #IndicesFragmentadosExternamente(Sentencia)
values (@Sentencia);
Begin try
Exec sp_executeSQL @Sentencia
End try
Begin catch
Insert into #IndicesFragmentadosExternamenteNoEjecutados
([Sentencia],[Mensaje Error],[Numero Error],[Severidad])
values (@Sentencia,ERROR_MESSAGE(),ERROR_NUMBER(),ERROR_SEVERITY())
End catch
End
Fetch IndicesFragmentados into @Schema,@Tabla,@Indice,@fragmentacionExterna,@FragmentacionInterna
End
Close IndicesFragmentados
Deallocate IndicesFragmentados

-- Evidencia de indices fragmentados
Select [SchemaName],[Tabla],[Indice],[fragmentacionExterna],[FragmentacionInterna] From #IndicesFragmentadosTodos
--Sentencias generadas
Select [Sentencia] from #IndicesFragmentadosExternamente
--Sentencias no Ejecutadas
Select [Sentencia]
,[Mensaje Error]
,[Numero Error]
,[Severidad]
from #IndicesFragmentadosExternamenteNoEjecutados

END
GO


Ejemplo de salidas del procedimiento almacenado
El primer conjunto de datos
Schema Tabla Índice FragmentacionExterna FragmentacionInterna
dbo CLY_TIPOS_EXTENSION PK_CLY_TIPOS_EXTENSION 50 1.704967
dbo AM_DATOS_CENTRALES PK_AM_DATOS_CETRALES 70 98.48603

El segundo conjunto de datos
Sentencia
Alter index PK_GCPROESTRUCTURA_IDESTRUCTURA ON dbo.GC_PRO_ESTRUCTURA Rebuild;
Alter index PK_OPSERVICIOSOPERADOS_SEOCONSERV ON dbo.OP_SERVICIOS_OPERADOS Rebuild;


El tercer conjunto de datos
Sentencia Mensaje Error Numero Error Severidad
Alter index Pk_indice ON dbo.OP_DETALLES_VALIDACION Rebuild; Cannot find index 'Pk_indice'. 2727 11



El procedimiento almacenado lo podemos ejecutar manualmente o dejar programado mediante el JOB, esto nos permitira dar matenimiento a los indices de nuestras tablas, pues indice al que no se le da mantenimiento en un Base de datos altamente transaccional no servira de nada al pasar el tiempo.

Referencias

http://technet.microsoft.com/es-mx/library/ms188917.aspx
http://www.dbasupport.com.mx/index.php?option=com_content&view=article&id=133:fragmentacion-en-sql-server-2005&catid=11:administracion&Itemid=2
http://www.dbasupport.com.mx/index.php?option=com_content&view=article&id=134&catid=11:administracion&Itemid=2

domingo, 21 de febrero de 2010

Acceso a datos

¿Cómo acceder a nuestra Base de Datos? ¿Cómo organizar las clases?

Empezare por comentar que la creación de nuestras clases y componentes debe buscar seguir un principio “alta cohesión y bajo acoplamiento”. Esto quiere decir que no debemos tener clases que sean todologas, por el contrario deben aislar la responsabilidad de una funcionalidad especifica. ¿Han manejado código con sentencias DML en el front End, les suena? Bueno eso es exactamente una de las cosas que no debemos hacer .
¿Tres capas, como es? Es separar la responsabilidad de nuestra aplicación en tres grandes bloques:
1)Acceso a datos
2)Capa de negocio
3)Front-End , que es nuestra interfaz.

Han manejado reglas de negocio directamente en un formulario Windows o Web? Eso es lo que debemos evitar. ¿Y si un día nos piden que hagamos un proceso Batch que involucran a nuestras clases o una aplicación Windows cuando era Web y tenemos reglas del negocio en todas partes? A eso me refiero con aislar responsabilidades.

Capa de Acceso a datos
Crearemos un proyecto de librería de clases , para el ejemplo usare el proveedor de datos de SQL, existe EnterpriseLibrary 4 que nos da otras bondades pero eso lo veremos en otra entrega
Agregar el espacio de nombres a la clase
Agregar la referencia a System.Configuration
Por ahora a nuestro componente de acceso a Datos solo le agregare un método para la ejecución de store procedure (executeNonQuery).
Las clases que deben agregar son :

NOTA , TODAS LA IMAGENES SE AGRANDAN AL DAR CLICK SOBRE ELLAS


Clase Parameter ,esta clase nos permitira "envolver" lo que normalmente hariamos con objectos de tipo SqlParameter, esto lo hice asi, pensando en un futuro modificar el componente de acceso a datos para cualquier proveedor, es decir al consumidor le damos un objecto de tipo parametro pero generico.

Clase DataAcces : Tendra metodos para realizar peticiones a la Base de datos (executeNonQuery, ExecuteScalar,etc..), habiamos platicado de los parametros que requerimos pasar a una petición a la BD,¿como pasarlos?Finalmente es una colección, recurriremos a Generics usando un diccionario personalizado del tipo la clase parametro.

La clase la prepararemos de tal forma que nos permita manejar transacciones. exhibiendo un metodo para iniciar una transacción, un metodo para confirmarla, y un metodo para rechazarla, nuestra clase ademas nos permitira establecer el nivel de aislamiento con el que queremos que se efectuen nuestras peticiones a la Base de Datos, en otra entrega hablaremos de los diferentes niveles de aislamiento.

Tambien implementaremos la interfaz IDisposable para prevenir que nuestro componente deje conexiones abiertas en la Base de Datos




Capa de Negocio

Agregaremos otro proyecto de clases a nuestra solución que nos servirá como capa de negocio. El cual deberá tener una referencia a nivel proyecto del componente de acceso a datos.

Nuestra capa de negocio debe tener implementadas todas las reglas de negocio, si con el tiempo nos pidieran hiciéramos un proceso Batch por ejemplo de facturas , imaginen el costo de tener en el front-end las reglas de cómo hacer una factura (por poner un ejemplo) .


En nuestro componente de acceso a datos implementaremos una práctica denominada “Inversor de control”, que nos permite “acoplar” fuertemente dos clases ,se que suena a contradicción con lo que escribir arriba pero les explicare: Hemos mencionado que no queremos crear clases todologas, esta práctica nos permitirá mover la responsabilidad de crear una instancia de una clase concreta a su manejador .


.

Es decir tendremos una clase que exhibe las propiedades de un cliente, y otra clase que sabe grabar, actualizar, abrir un cliente.
1)Agregar una clase que contendra las propiedades del cliente

2)Agregar una clase que contendrá solo los métodos que reciben o devuelven un cliente para operarlo.





El escenario comun cuando tenemos un catalogo(o al menos el que mas he visto) es que se crea una clase con las propiedades, en ella misma los metodos para manipular la información del catalogo, la mayor parte de las veces usando parametros para cada campo en un metodo grabar, eso NO es una buena practica.

¿Como trabaja por citar un ejemplo el proveedor de datos de sql? Con objectos, de esa manera empezamos a garantizar que lo que grabo es un cliente , un objecto cliente.

Espero no hacer confusa esta parte, para que este ejemplo sirva un poco mas , crearemos un método que grabe registros (uno por uno) y otro método que pase un XML con el contenido de lo que deseamos grabar.

3)Crear una tabla (con la que trabajaremos el ejemplo :


4)Crear un store procedure para administrar las excepciones (SQL 2005/2008)


5)Crear un store procedure para grabar registro por registro
5)Crear un store procedure para grabar registro por registro , creamos otro store solo para ejemplificar las transacciones,aunque pudiera ser el mismo.


6)Crear un store procedure para grabar en una sola petición el contenido de un XML



Capa de presentación

La capa de presentación pudiera ser un proyecto Web, windows o cualquier otro que nos ofresca una UI

Agregamos una referencia al componente de negocio
El siguiente paso que tenemos es consumir nuestras clases en el Front-End

private void grabar()
{

try
{
MCliente objMcliente = new MCliente();
ICliente objCliente;
objCliente = objMcliente.Create();
objCliente.Apellido = "MiApellido";
objCliente.Nombre = "MiNombre";
objCliente.Observaciones = "MisObservaciones";
objMcliente.Save(objCliente);
}
catch (Exception)
{

throw;
}
}



Esto es solo el principio, cuando hablamos de capas, lo que queremos es sobre todo aislar resposabilidades. El tema es amplio, este ejemplo solo pretende resumir en breves pasos como debemos ordenar nuestro proyecto en capas.
Si desean descargar el codigo lo pueden bajar de la dirección :

Crisis

Este es un texto que en palabras menos,mas o exactas esta en la red, y me gusta mucho...

"No pretendamos que las cosas cambien, si siempre hacemos lo mismo. La crisis es la mejor bendición que puede sucederle a personas y países, porque la crisis trae progresos. La creatividad nace de la angustia como el día nace de la noche oscura. Es en la crisis que nace la inventiva, los descubrimientos y las grandes estrategias. Quien supera la crisis se supera a sí mismo sin quedar 'superado'.
Quien atribuye a la crisis sus fracasos y penurias, violenta su propio talento y respeta más a los problemas que a las soluciones. La verdadera crisis, es la crisis de la incompetencia. El inconveniente de las personas y los países es la pereza para encontrar las salidas y soluciones.
Sin crisis no hay desafíos, sin desafíos la vida es una rutina, una lenta agonía. Sin crisis no hay méritos. Es en la crisis donde aflora lo mejor de cada uno, porque sin crisis todo viento es caricia. Hablar de crisis es promoverla, y callar en la crisis es exaltar el conformismo. La creatividad nace de la angustia como el día nace de la noche oscura. Es en la crisis que nace la inventiva, los descubrimientos y las grandes estrategias
En vez de esto, trabajemos duro. Acabemos de una vez con la única crisis amenazadora, que es la tragedia de no querer luchar por superarla"

domingo, 7 de febrero de 2010

Clases parciales C#

Las clases parciales vienen a resolver una problemática que a nosotros los desarrolladores se nos presenta cuando trabajamos en un equipo y necesitamos “compartir una clase”. Muchos de ustedes seguramente conocen el concepto de controlador de versiones como el VSS y muchos más gratuitos, básicamente nos permiten atrapar/bloquear un archivo para que solo pueda ser modificado por una persona a la vez, ¿pero qué sucede si más de un programador requiere cambiar alguna funcionalidad de un método en una clase? Anteriormente solo nos quedaba esperar o si la urgencia lo ameritaba nos arriesgábamos a modificar o agregar y al último unir los cambios. Microsoft ha resuelto esta problemática con la aparición de Clases y métodos parciales,los cuales nos permiten cambiar al mismo tiempo en una clase métodos y propiedades y en su caso agregarlos, ¿pero cómo trabaja?

1) Es necesario agregar la palabra reservada partial a la clase : partial class MiClase {}
2) Es necesario crear diferentes archivos físicos de tipo clase (pueden vivir en el mismo, pero entonces no se me ocurre para que la tendríamos parcial)
3) Tener una definición de la clase parcial en cada uno de los archivos
4) Crear el metodo o metodos nuevos en uno de los archivos de la misma clase parcial que no esta atrapada(si tuvieramos este problema en un ambiente con contralador de versiones).

Veamos el ejemplo :
1)La clase inicial

2)Creando otro archivo y agregando otra definición de nuestra clase parcial y un nuevo metodo


Consumiendo la clase :


Finalmente cuando nuestra clase esta siendo consumida nosotros podemos verla como una sola, y los metodos y propiedades que pudieran estar creados en diferentes archivos seran visibles en nuestra clase .

Espero que les sea de utilidad esta información.

El placer de compartir

Hola, es un enorme gusto para mi poder contribuir en algo a los demás, procurare hacer estas entregas lo menos espaciadas, y siempre que quieran contactarme por correo háganlo y encontraran en mi una mano extendida ,aunque quizá no siempre con todas las respuestas.
MCP, MCTS, carlos.nabor@gmail.com

Serialización Parte I C#

¿Qué es serializar? Bueno intentare explicarlo de la forma en que a mí me hubiera gustado aprenderlo.
Me parece que los conceptos deberían ser enseñados a partir de una necesidad primeramente, de un problema, de un escenario. Empecemos…
En muchas de nuestras aplicaciones cuando la funcionalidad que damos se vuelve cada día mayor es necesario intercambiar información digamos más compleja que un simple dato de tipo carácter o numérico, podemos pensar quizá en los datos completos de un empleado (nombre, edad, domicilio, RFC, foto, etc...) o incluso una colección de ellos. Este intercambio puede ser entre aplicaciones, a nivel de Base de datos, usando la misma plataforma que la nuestra o incluso una distinta y con plataforma me refiero no solo al sistema operativo, sino que también a la tecnología con la que estamos desarrollando. ¿Qué sucederá cuando necesitemos comunicarnos con una aplicación echa en Java por poner un ejemplo?





Pues bien, la serialización viene al rescate, ya tenemos el escenario, nuestro problema está en la mesa. Tenemos nuestras clases, instancias concretas de esas clases y necesitamos almacenar y transmitir esa información. Aquí empezare a hacer hincapié que la serialización no es la panacea y que ahora debamos grabar así siempre en la Base de Datos o mandar de esa forma la información a otros sistemas, NO AHÍ COSAS BUENAS O MALAS, existen necesidades especificas y soluciones y está en nosotros saber identificar cuando debemos usar una pistola o un matamoscas.
Empezaremos con la serialización en formato binary, serializar es el proceso de crear un stream de bytes a partir de un objeto (una instancia concreta). Supongamos que tenemos una clase que se llama Empleado.





Si nosotros queremos serializar un objeto de esa clase necesitamos agregar el atributo [Serializable] a nuestra clase, si intentáramos serializar una clase que no tiene este atributo se generara una excepción.







Retomando lo que veníamos platicando si serializar es colocar nuestra instancia concreta en un stream de bytes ¿qué mas necesitamos?


1)Una instancia de nuestra clase Empleado


2)Un objeto de tipo stream que es donde almacenaremos nuestra instancia en un formato diferente, en este caso usaremos un objeto de tipo FileStream, esta clase nos proporciona nada menos que 15 constructores, usaremos lo necesario y eso es solo indicar el nombre del archivo que se generara y el modo en que estamos creando el objecto que es Create.


3)Requerimos por ultimo especificar la forma en que vamos a serializar,para serailizar en formato binary requerimos un objecto de tipo BinaryFormatter, este objecto nos permitira con el metodo “Serialize” serializar nuestra instancia concreta en el archivo stream que creamos, es decir lo vaciamos.





Hasta aquí hemos visto como convertir nuestra información de un objeto , ahora ¿qué sucede cuando necesitamos recuperar el objeto que serializamos?


1) Requerimos una variable del tipo de nuestra clase (NO una instancia).


2) Requerimos nuevamente un objeto de tipo stream para esta vez abrir el archivo que se genero y que contiene nuestra información serializada.


3) Requerimos nuevamente el objeto de tipo BinaryFormatter para esta vez con su metodo “Deserialize” deserealizar nuestra información y por ultimo hacer un cast de la información que hemos deserializado y setearlo a la variable que creamos en el paso 1








Comentarios :

I)BinaryFormatter es la mejor opción para serializar y deserializar objectos que solo seran interpretados por aplicaciones desarrolladas en .NETFrameWork
II)Existen otros "formateadores" XML , SOAP y Custom Serialization que son mas adecuados para ambientes que involucran sistemas operativos y aplicaciones ajenas al NetFrameWork e incluso a Windows y que nos permiten mayor flexibilidad.
III)Es una buena practica hacer nuestras clases serializables, aun cuando no tengamos claro si se usara, el dicho es "si ahi duda haslas serializables".

Los espacios de nombre utilizados fueron :
System.Runtime.Serialization.Formatters
System.Runtime.Serialization.Formatters.Binary
System.IO

Y regresando a la mecanica de plantear problemas dejo la pauta para la parte II con lo siguiente :
¿y si quiero que algunos miembros no sean serializables?
¿Como evitar problemas de compatibilidad entre versiones de mis clases?¿que pasa si agrego una nueva propiedad despues de que las clases ya estan siendo consumidas?
¿cual es la mejor practica para propiedades calculadas?

Como ven esto es solo el comienzo, en entregas posteriores continuaremos hablando de este tema, pues aun tenemos tela de donde cortar. Saludos y ojala les haya sido util esta información