0% encontró este documento útil (0 votos)
218 vistas26 páginas

Buenas Practicas Desarrollo SQL Server

Este documento presenta varias buenas prácticas para el desarrollo sobre SQL Server 2008, incluyendo el uso adecuado de tipos de datos, diseño de tablas primarias y relaciones, nomenclatura y uso de índices. El objetivo es mejorar el rendimiento, funcionalidad y mantenimiento de las bases de datos. Se recomienda actualizar esta guía con cada nueva versión de SQL Server.

Cargado por

Facundo Acuña
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
218 vistas26 páginas

Buenas Practicas Desarrollo SQL Server

Este documento presenta varias buenas prácticas para el desarrollo sobre SQL Server 2008, incluyendo el uso adecuado de tipos de datos, diseño de tablas primarias y relaciones, nomenclatura y uso de índices. El objetivo es mejorar el rendimiento, funcionalidad y mantenimiento de las bases de datos. Se recomienda actualizar esta guía con cada nueva versión de SQL Server.

Cargado por

Facundo Acuña
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 26

Guía de buenas Practicas en desarrollo

sobre SQL 2008

Objetivo de la Guía

El objetivo de esta guía es tener un documento en el cual se enumeren las malas prácticas de
trabajo sobre SQL Server las cuales afectarían algunas características como la performance,
funcionales y mantenimiento.

Esta guía deberá ser actualizada con cada nueva versión de SQL Server que Sancor migre,
actualmente como SQL server de plataforma está la versión 2008.
Diseño de tablas

Tipos de datos a utilizar. (Rojo)

Utilizar el tipo de dato correcto ayuda a que luego en las consultas y operaciones no se deban
aplicar conversiones o bien funciones que puedan afectar la performance y lo funcional.

Como dominio si utilizamos otro tipo de datos a los adecuados para cada situación se debería
agregar constraint de Check para hacer las validaciones, por ejemplo si para una fecha se usara
char(8) sería necesario usar los check para evitar datos no validos como una fecha (por ejemplo
‘ABCD’)

Dato a Guardar Tipo de dato SQL Server 2008 Observaciones


Fecha Date
Hora Time
Fecha y Hora DateTime
Fecha y hora con DateTimeOffset
Zona horaria
Boolean Bit Decidimos utilizar este tipo de
datos ya que se integra mejor
con las aplicaciones y si en su
lugar se decide usar otro tipo
de datos habría que definir
cuál es el True / False y
además aplicar Check a las
tablas
String Menores que 10 tratar de utilizar char. La utilización de un varchar
Mayores que 10 tratar de usar Varchar para valores pequeños
termina siendo más costoso
para el motor en términos de
almacenamiento
String Varchar(max)
(Comentarios /
Memo)
Binarios (Fotos, Varbinary(max)
objetos,
documentos,
video, etc)
Decimales Decimal Utilizar la precisión necesaria
Enteros De 0 a 255 TinyInt Utilizar el tipo de dato entero
De -32768 a 32767 SmallInt necesario hará que ocupe el
De -2,147,483,648 a 2,147,483,647 INT espacio adecuado en tablas e
Resto: BigInt índices
Geográficos Geography
Geométrico Geometry
CUIT / Email Armar un tipo de dato utilizando CLR Este tipo de datos si se arman
con CLR se podrán obtener
ventajas como el check de
integridad de los mismos.

Tipos de datos a deprecar (no se usaran más en versiones futuras de SQL Server)

Los tipos de datos a deprecar no se deberían utilizar en nuevos desarrollos ya que los mismos
serán removidos en futuras versión de SQL Server.

Este es un listado de los tipos de datos que no se deberían usar en nuevos desarrollos.

Tipo a Deprecado Reemplazo


Text / Ntext / Image Varchar(max) / Nvarchar(max) /
Varbinary(max)

Diseño de Primary Key (Rojo)

Toda tabla del sistema debe contener una PK.

Esta buena práctica permite no tener tablas donde no se pueda identificar registros como así
también que se generara de forma automática el índice CLuster (ver luego notas adjuntas)

El siguiente listado enumera las buenas prácticas al desarrollar una PK.

● Usar claves naturales a menos que la PK sea mayor a 4 campos , en este caso utilizar claves
artificiales como pueden ser los Identity y GUID.
● Analizar en cada caso si la PK debe ser el índice Cluster de la tabla
o Por ejemplo en una tabla de movimientos la PK es muy probable que sea el
numero o ID pero las consultas se buscan por Fecha, si dejamos el Default el
Cluster será el ID y estará desaprovechado
● Evitar que dentro de la PK existan campos actualizables
Clustered Index (Amarillo)

● Toda tabla de usuario debe contener un índice Clustered, el mismo ayudara no solo en el
ordenamiento sino que además en varios puntos de performance.

http://msdn.microsoft.com/en-us/library/cc917672.aspx

● Seleccionar campos con menor actualización posible


● Evitar la utilización sobre campos Identity / GUID
● Si la tabla es de muchos insert / update y Delete vs Select, entonces si es una buena idea
poner los Clustered sobre columnas como identity
● Primero se debería crear el índice clustered antes de los NonClusteres para evitar re
apuntes.
● No usar este tipo de índices sobre campos largos como así también no usar más de 4.
o En este caso se debería armar una clave artificial (identity o GUID) como PK en
modo NonClustered Index y luego armar un índice Único para los campos
originales
● Lo ideal sería que el clustered sea por una sola columna

http://www.sql-server-performance.com/tips/clustered_indexes_p1.aspx

Relaciones entre tablas (integridad referencial)

Las relaciones entre tablas deben estar definidas dentro del motor relacional y no fuera de este
mismo.

La buena práctica de armar las reglas de integridad referencial en el motor es que desde cualquier
sistema donde se quiera manipular datos el esquema es consistente.

Por lo cual no se van a poder armar relaciones de integridad referencial en los sistemas.

● Dentro de la misma base de datos utilizar las Foreing Key


● Para armar relaciones entre tablas de distintas bases se deberá armar triggers

El siguiente código es un ejemplo de integridad referencial con triggers

create trigger title_del


on titles for delete
as
delete titleauthor
from titleauthor
inner join deleted
where titleauthor.title_id = deleted.title_id
return

create trigger title_ins


on titleauthor for insert
as

if exists (select id
from inserted
where ID
not in
(
select ID from titles
)
begin
raiserror 20001 "Invalid title: title_id
does not exist on titles table"
rollback transaction
return
end
return

Nomenclatura de campos (Verde)

● Evitar que dentro del nombre del campo se haga mención al tipo de dato, esta técnica no
permite luego tener un modelo ordenado cuando cambian los tipos de datos.
● Utilizar los comentarios de los campos para poner especificaciones y descripción del
mismo así queda en todo el SQL Server el catalogo lo más detallado posible.
● No usar caracteres no alfanuméricos (&%/(,etc) , espacios y acentos, esto luego dificulta
las querys. (si es valido usar _ y - )
Nomenclatura de Tablas (Verde)

● No usar caracteres no estándar (&%/(,etc) , espacios y acentos, esto luego dificulta las
querys, (si es valido usar _ y - )

● No usar en el nombre nada que tenga que ver con el modulo del sistema, para ello utilizar
Schemas

CREATE TABLE DBO.COMPRAS_PO (ID INT);


CREATE TABLE DBO.COMPRAS_PO_DET (PO_ID INT, LINE INT) MAL

CREATE SCHEMA COMPRAS;

CREATE TABLE COMPRAS.PO (ID INT);


CREATE TABLE COMPRAS.PO_DET (PO_ID INT, LINE INT) OK

Indices NonClustered (Rojo)

● Se deberían armar índices para las condiciones de WHERE y JOIN como mínimo.
● Evitar la superposición de índices (esto genera mayor costo de mantenimiento y tiene un
fuerte impacto en la performance)

CREATE INDEX IX1 ON CLIENTES (CIUDAD,ZONA,MANZANA)


CREATE INDEX IX2 ON CLIENTES (CIUDAD,ZONA)
CREATE INDEX IX1 ON CLIENTES (CIUDAD)

El primer índice aplica los otros dos también

● No armar índices sobre campos con mucha selectividad (por ejemplo Sexo), esto hará que
en muchas de las querys se haga un Scan ya que el índice termina no siendo eficiente por
la selectividad de datos
● Utilizar índices con filtro para los Flag (Leído / No Leído, Procesado / No Procesado /
ENVIOSPDF)
o La utilización de índices con filtro en estos casos hace ahorrar recursos de espacio
en disco.

CREATE NONCLUSTERED INDEX


FIBillOfMaterialsWithEndDate
ON Production.BillOfMaterials (ComponentID, StartDate)
WHERE EndDate IS NOT NULL;
GO
● Si se arman índices Cover, intentar usar la cláusula INCLUDE

SELECT A.PostalCode
FROM #Address2 AS a
WHERE A.StateProvinceID = 42

CREATE INDEX IX2 ON #ADDRESS2 (stateprovinceid)


include (postalcode)

Manejo de imágenes / documentos / BLOB (Amarillo)

De optar por almacenar este tipo dentro de la base de datos, será necesario armar una tabla
adicional donde solo se contendrá el ID del registro identifícate y su información BLOB.

Esto ayuda a los efectos de poder tener mayor control sobre la tabla como así también poder
aplicar distintas políticas administrativas , como así también forzar un JOIN para traer los objetos y
así proteger del uso inadecuado de SELECT * sobre la tabla principal.

● Separar en FileGroups distintos


● Aplicar backups distintos al resto de la base
Diseño en general (rojo)

● Utilizar la tercera forma normal


● En caso de aplicar alguna des normalización por performance se debería utilizar triggers
para mantenerla (demis_maximas_modificaciones)

CREATE TABLE
DBO.ARTICULOS (ID INT PRIMARY KEY,
NOMBRE VARCHAR(100),
STOCK DECIMAL (18,2))

GO

CREATE TABLE DBO.MOVIMIENTOS (TRANSACCION INT,


FECHA DATE DEFAULT(GETDATE()),
ARTICULO_ID INT FOREIGN KEY
REFERENCES DBO.ARTICULOS(ID),
CANTIDAD DECIMAL(18,2),
TIPO CHAR(1)
CHECK (TIPO ='I' OR TIPO = 'O')
)
GO

-- INSERTAMOS REGISTROS EN LA TABLA ARTICULOS

INSERT INTO DBO.ARTICULOS (ID,NOMBRE,STOCK)


VALUES (1,'TECLADO',0),(2,'MOUSE',0),(3,'LCD',0)

-- CREAMOS LOS TRIGGER PARA TENER ACTUALIZADO EL STOCK

CREATE TRIGGER DBO.TR1 ON DBO.MOVIMIENTOS


FOR INSERT
AS
BEGIN

SET NOCOUNT ON -- NO RETORNA MENSAJES DE CANTIDAD DE REG


AFECTADOS

UPDATE DBO.ARTICULOS
SET STOCK = STOCK + T.PARCIAL
FROM DBO.ARTICULOS A
INNER JOIN
( SELECT ARTICULO_ID,
SUM(CASE WHEN TIPO='I' THEN CANTIDAD ELSE -CANTIDAD END)
AS PARCIAL FROM INSERTED
GROUP BY ARTICULO_ID
) T
ON

A.ID = T.ARTICULO_ID
END
GO

CREATE TRIGGER DBO.TR2 ON DBO.MOVIMIENTOS


FOR DELETE
AS
BEGIN

SET NOCOUNT ON -- NO RETORNA MENSAJES DE CANTIDAD DE REG


AFECTADOS

UPDATE DBO.ARTICULOS
SET STOCK = STOCK - T.PARCIAL
FROM DBO.ARTICULOS A
INNER JOIN
( SELECT ARTICULO_ID,
SUM(CASE WHEN TIPO='I' THEN CANTIDAD ELSE -CANTIDAD END)
AS PARCIAL FROM DELETED
GROUP BY ARTICULO_ID
) T
ON
A.ID = T.ARTICULO_ID
END
GO

● Tratar de utilizar Schemas , estos permiten agrupar los objetos de distintas formas
Programabilidad

Traer solo lo necesario (Rojo)

En los distintos querys solamente llevar a la aplicación los campos y registros necesarios para
evitar una sobrecarga al motor relacional.

Por ejemplo si se quiere hacer un filtro por un campo que se aplique sobre la base y no sobre el
resulset de dicha aplicación.

Esto mejorara la performance no solo en el servidor de base de datos sino que entre la aplicación y
el SQL Server

No usar Select * (Rojo)

La utilización del SELECT * no es una buena práctica de trabajo ya que traerá no solo que todos los
campos (por ejemplo si hay un campo image ) sino que además de existir algún índice Cover no
será aprovechado y esto tendrá efectos negativos en la performance.

Cursores y While (Rojo)

El motor de base de datos esta optimizado para código orientado a teoría de conjuntos y no
iterativa.

Cualquier utilización de código iterativo (Cursores / While) hará que la performance se degrade de
forma importante, aumentando también así los loqueos

Se define entonces como practica que toda necesidad de aplicar un cursor / while deberá ser
analizado por distintos responsables en el área de desarrollo.

Estos son algunos casos donde se podrían aplicar cursores / whiles ya que no hay alternativas, en
el resto de los casos deberá ser analizado.

● Renovación de Pólizas
● Impresiones masivas
● Emisión de pólizas automáticas

De utilizar cursores se deberán declarar como FAST_FORWARD


Utilizar Alias en los campos de una query o proceso (Verde)

La utilización de los alias asegura que se seleccionen los campos adecuados cuando hay más de un
conjunto.

SELECT ID, LINEA


FROM DBO.CAB
INNER JOIN
DBO.DET
ON
CAB.ID = DET.ID -MAL

SELECT CAB.ID, DET.LINEA


FROM DBO.CAB
INNER JOIN
DBO.DET
ON
CAB.ID = DET.ID -OK

Utilizar [ ] para objetos (tablas / vistas / funciones / Stores Procedures) y campos. (Verde)

Dentro del motor de base de datos van apareciendo distintas palabras reservadas, si nuestra
codificación esta en ingles seremos más propensos a poder caer en alguna palabra reservada.

Utilizando [] evitamos cualquier problema con este punto como así también si existen caracteres
no alfanuméricos en la nomenclatura.

Dentro de la sentencia INSERT declarar los campos (Verde)

En la utilización de la instrucción INSERT será necesario que se declaren los campos a ser
insertados.

Si no se realiza esta declaración se tomaran todos los campos de la tabla, lo que implica que ante
un nuevo campo hay que modificar si o si la sentencia ya que de lo contrario emitirá un error.

INSERT INTO DBO.CLIENTES


VALUES (1,'SANCOR SEGUROS') MAL

INSERT INTO DBO.CLIENTES (ID, NAME)


VALUES (1,'SANCOR SEGUROS') -- OK
Utilizar new JOIN en lugar de OLD JOIN (Rojo)

La utilización de OLD Style JOIN esta deprecada en SQL Server 2005 y superior.

No se recomienda utilizar el OLD Style y si reemplazarlo por el new Style

● JOIN
● LEFT JOIN
● RIGHT JOIN
● FULL JOIN
● CROSS JOIN
● APPLY

-- ANSI SQL-92

SELECT C.custid, E.empid


FROM Sales.Customers AS C
CROSS JOIN HR.Employees AS E; -- OK

-- ANSI SQL-89

SELECT C.custid, E.empid


FROM Sales.Customers AS C, HR.Employees AS E; -- MAL

SELECT E.empid, E.firstname, E.lastname, O.orderid


FROM HR.Employees AS E
JOIN Sales.Orders AS O
ON E.empid = O.empid; -- OK

-- ANSI SQL-89
SELECT E.empid, E.firstname, E.lastname, O.orderid
FROM HR.Employees AS E, Sales.Orders AS O
WHERE E.empid = O.empid; -- MAL
GO
Utilizar Exist en lugar de COUNT para cuando es necesario encontrar registro y aplicar algún
condicional (Amarillo)

En varios de nuestros procesos nos podemos encontrar ante la necesidad de buscar si existen
registros con determinada condición y luego seguir en el proceso con el resto de los pasos.

Para hacer esta operación se debe utilizar Exist en lugar de Count ya que es mucho más eficiente

DECLARE @n INT
SELECT @n = COUNT(*)
FROM Sales.SalesOrderDetail AS sod
WHERE sod.OrderQty = 1

IF @n > 0
PRINT 'Record Exists' NO USAR

IF EXISTS ( SELECT sod.*


FROM Sales.SalesOrderDetail AS sod
WHERE sod.OrderQty = 1 )
PRINT 'Record Exists'

Utilizar los índices de forma eficiente (Rojo)

Los índices por más que estén creados en nuestras tablas, dependiendo de cómo escribamos las
querys podemos lograr que no se utilicen de forma eficiente, esto quiere decir que se hará una
operación de SCAN en lugar de un SEEK.

Las querys deben ser SARG’s compatibles para que los índices sean usados de forma eficiente.

La manipulación del campo indexado en una query dentro del WHERE, JOIN, Order By, Group BY o
función de agregación hará que la misma no sea eficiente.

Estos son algunos tips donde la query no es SARG.

● Aplicar funciones a los campos en la búsqueda


● Aplicar operadores matemáticos con valores

SELECT * FROM
Purchasing.PurchaseOrderHeader
WHERE PurchaseOrderID * 2 = 3400 MAL

SELECT * FROM
Purchasing.PurchaseOrderHeader
WHERE PurchaseOrderID = 3400 / 2 ok
SELECT ORDERDATE FROM
Sales.SalesOrderHeader
WHERE YEAR(OrderDate) = 2001 MAL

SELECT ORDERDATE FROM


Sales.SalesOrderHeader
WHERE OrderDate >='20010101' AND
OrderDate < '20020101' ok

Select theDate
from aTable
where datediff(day, theDate, getdate()) < 3 MAL

Declare @afterDate datetime


Select @afterDate = dateadd(day, -3, getdate())

Select theDate
from aTable
where theDate < @afterDate

HINT (Rojo)

Los HINT permiten cambiar el comportamiento de una query e indicarle a SQL Server que en lugar
que el analice cual es el mejor camino (mas rápido) lo hagamos nosotros por él.

Esto puede implicar grandes perjuicios de performance a futuro ya que los sistemas no son
estáticos.

Como regla general no está permitido la utilización de ningún tipo de HINT y solo se podrán aplicar
en casos donde se haya estudiado con los DBA que para esa query era la mejor alternativa.

Estos son algunos ejemplos de lo que no se debe hacer

SELECT * FROM
Purchasing.PurchaseOrderHeader
WITH (INDEX (PK_PURCHASEORDERHEADER_PURCHASEORDERID))
WHERE PurchaseOrderID * 2 = 3400

SELECT ProductID, OrderQty, SUM(LineTotal) AS Total


FROM Sales.SalesOrderDetail
WHERE UnitPrice < $5.00
GROUP BY ProductID, OrderQty
ORDER BY ProductID, OrderQty
OPTION (MAXDOP 2);
SELECT *
FROM HumanResources.Employee AS e1
UNION
SELECT *
FROM HumanResources.Employee AS e2
OPTION (MERGE UNION);

SELECT ProductID, OrderQty, SUM(LineTotal) AS Total


FROM Sales.SalesOrderDetail
WHERE UnitPrice < $5.00
GROUP BY ProductID, OrderQty
ORDER BY ProductID, OrderQty
OPTION (HASH GROUP, FAST 10);

Indicar el OWNER/SChema en los FROM / JOIN

Se deberá indicar el OWNER del objeto tanto en los FROM como en los JOIN.

Esto permite ser más eficiente la búsqueda del objeto dentro SQL Server ya que puede existir un
mismo nombre de objeto para distinto schema

SELECT CLIENTE FROM VENTAS MAL

SELECT CLIENTE FROM DBO.VENTAS OK

Order BY (Amarillo)

La instrucción ORDER BY es la que permite garantizar el ordenamiento en una consulta.

En esta sección se indicaran los tips a tener en cuenta para que el uso de esta sintaxis no
perjudique la performance.

La necesidad de aplicar ordenamiento en la mayoría de los casos está relacionado con una capa de
presentación más que con la de datos.

● Si se utiliza la cláusula TOP (no 100 Percent) es necesario usar Order BY para determinar
los registros
● Si no hay índices sobre los campos del ORDER BY entonces analizar el query plan , si la
operación SORT supera el 20% entonces evitar la utilización de ORDER BY en donde no se
use TOP, y en donde se utilice analizar los índices necesarios a crear o bien ordenar desde
la aplicación con el resulset ya procesado.
Stores Procedures (Amarillo)

La utilización de Stores procedures es una buena práctica de desarrollo contra el motor ya que
beneficia a los siguientes ítems

● Seguridad (solo se da permiso al SP y no a los objetos que este llame)


● Centralización de código
o Mejor análisis a la hora de performance
o Reducción de los tiempos de análisis en una migración a una nueva versión ya que
el mismo SQL Server hará el diagnóstico del código invalido o no compatible.
o Mantenimiento del sistema controlado, al hacer alguna modificación en alguna
tabla se podrá saber el impacto de forma inmediata dentro del código
● Performance
o En la mayoría de los SP de querys al estar compilado el plan se genera una mejora
en el tiempo que SQL Server debe compilar cada uno.
http://blogs.msdn.com/b/queryoptteam/archive/2006/03/31/565991.aspx

Para el acceso a datos se deberán utilizar Stores Procedures.

Las reglas de negocio podrán ser o no implementadas en ellos dependiendo de la arquitectura.

No se podrá utilizar como Prefijo SP_ ya que es utilizado por SQL Server como de sistema lo que
implica doble comprobación a la hora de ejecución (primero en la master y luego en la base que
estamos posicionados) degradando así los tiempos de ejecución.

No se deberán encriptar el código.

Como primer sintaxis dentro del código utilizar SET NOCOUNT ON para no enviar información
de registros afectados hacia la aplicación y así generar una sobrecarga adicional.

Evitar la utilización de IF y la ejecución de varias querys dentro, es recomendable armar en este


caso más de un SP y luego si desde uno padre hacer el condicional. Esta técnica mejorara las
recopilaciones.
WHERE Dinámicos (Amarillo)

En muchos casos es necesario la utilización de WHERE en forma dinámica, por ejemplo dentro de
un SP con varios parámetros que el usuario puede completar, algunos ejemplos donde es
necesario esta utilidad seria: (Impresión de caratula, impresión masiva, lectura de tramites).

Para poder hacer dicha operación se deberá realizar con SQL Dinámico SP_EXECUTESQL para
armar la concatenación del WHERE solamente.

Al Store Procedure habrá que cambiarle el contexto de ejecución distinto de CALLER (por ejemplo
un usuario definido) ya que de lo contrario habrá que darle permisos al que ejecute el Store no
solo de EXECUTE sino que además a los objetos que se utilicen dentro del SQL Dinámico.

http://www.sommarskog.se/dyn-search-2008.html

Utilizar parámetros con nombres para los campos y luego armar el SQL dentro del SP y no tener un
solo parámetro @sql y pasarlo directamente, esto es a los efectos de disminuir la injection de
código.

CREATE PROC USP_1 @SQL VARCHAR(400)


AS

DECLARE @CMD VARCHAR(1000)


SET @CMD = N'Select Id,nombre FROM Clientes Where ' + @SQL
exec(@cmd)
go -- mal

CREATE PROC USP_1 @pais VARCHAR(10),@ciudad varchar(30)


AS

DECLARE @CMD VARCHAR(1000)


SET @CMD = N'Select Id,nombre FROM Clientes Where Pais=' + @pais
+ ' and ciudad=' + @ciudad
exec(@cmd)
go

Orden en los campos para la cláusula WHERE y JOIN (Amarillo)

Dentro de la cláusula WHERE y JOIN se deberán respetar el orden de los campos del índice a
utilizar.

El orden es importante respetarlo por cuestiones de performance

Por ejemplo si tenemos un índice conformado por CIUDAD y luego ZONA nuestra consulta debería
estar armada de la siguiente manera

WHERE CIUDAD='BS AS' AND ZONA = 1


Vistas (Rojo)

La utilización de vistas solamente está permitido en casos donde se necesite hacer una interfaz
entre base de datos (por ejemplo Extrared)

No está permitido la utilización de vistas que llaman a su vez a otras vistas, esta técnica además de
ser poco legible a la hora de buscar el funcionamiento, atenta contra la performance ya que
muchas veces no es necesario para la query que deseamos obtener tener acceso a todas las tablas
que hacen referencias cada una de estas vistas.

Las vistas deberán tener una nomenclatura un prefijo en su nomenclatura que permita
identificarlas dentro del código.

CREATE VIEW DBO.VENTASPORDIA


AS
...... MAL

CREATE VIEW DBO.V_VENTASPORDIA


AS
.... OK

Consultar el ultimo valor Identity: (Amarillo)

Al utilizar los campos identity es necesario en alguno de los procesos determinar qué valor se
insertó para luego poderlo utilizar.

Por ejemplo si se dispone de un maestro / Detalle y es necesario para insertar los detalles saber el
valor identity que se generó en el maestro.

Para hacer esta operación se debe utilizar la función SCOPE_IDENTITY ().Esta función
determina el ultimo valor identity del Scope (por ejemplo el Store Procedure que estoy
ejecutando)

Si se utiliza Para @@identity Si se utiliza esta función se retornara el ultimo valor de la sesión
pero no del Scope.

SELECT SCOPE_IDENTITY () OK

SELECT @@identity MAL

http://blog.sqlauthority.com/2007/03/25/sql-server-identity-vs-scope_identity-vs-ident_current-
retrieve-last-inserted-identity-of-record/
Consultar fechas: (Rojo)

Las fechas en los tipos de datos dedicados a ella no se guardan en ningún formato (dd-mm-yyyy o
mm-dd-yyyy) ya que se almacena en Bytes.

El formato de las fechas depende del lenguaje definido en el login de SQL Server.

Para evitar problemas con los distintos formatos es necesario utilizar el estándar ANSI
(YYYYMMDD HH:mm:ss)

Los siguientes ejemplos ilustran la forma de manejar las fechas en este formato

SELECT * FROM
Production.TransactionHistory
WHERE TRANSACTIONDATE >='09-01-2003'
AND TRANSACTIONDATE < '09-03-2003' mal

SELECT * FROM
Production.TransactionHistory
WHERE TRANSACTIONDATE >='20030901'
AND TRANSACTIONDATE < '20030903'

Para consultar un rango de fechas no se debe utilizar el BETWEEN cuando el tipo de dato no sea
Date ya que el mismo representa >= and <= y los otros tipos de datos guardan fecha y hora.

Los siguientes ejemplos ilustran como se debe escribir la query para traer los registro del
1/12/2010

WHERE fecha >= '20101201'


and
Fecha < '20101202'

El patrón de trabajo es el siguiente

WHERE campo >= @fechadesde


and
campo < dateadd(dd,1,@fechaHasta)
Tablas temporales (Amarillo)

La utilización de tablas temporales tiene un efecto negativo en la performance ya que tienen un


mayor consumo de I/O

En su lugar trate de utilizar funciones más avanzadas de TSQL (CTE, Tablas derivadas, etc).

La utilización de tablas temporales deberá debidamente ser justificada ya que en SQL 2008 con las
diferentes funcionalidades nuevas al lenguaje son menos necesarias.

Algunas situaciones donde se podrían utilizar cuando un resulset es necesario trabajarlo en varias
partes dentro del Scope (Store Procedure / Funcion / Trigger)

Si el resultado final es muy inferior a la cantidad de registros insertados en una tabla temporal
existirá una pérdida de performance en esos casos y deberán ser analizados

Sub query a nivel campo (Amarillo)

Los subquerys a nivel campo son aquellas consultas que se generan para mostrar una query
dentro de un campo.

Si dicha query requiere el registro externo para determinar el valor entonces se estará evaluando
la misma por cada uno y esto en lo general afectara la performance, por lo cual su utilización debe
ser reducida y tratar de utilizar otras funciones como puede ser APPLY

Aquí se describen algunos ejemplos


select t1.nrofac,
(select top 1 t2.precio
from #auxi t2
where t2.nrofac = t1.nrofac
order by t2.precio desc) as PrecioMayor,
(select top 1 t2.articulo
from #auxi t2
where t2.nrofac = t1.nrofac
order by t2.precio desc) as Articulo
from
#auxi t1
group by t1.nrofac
order by t1.nrofac mal

SELECT O1.articulo,O1.nrofac,O1.precio
FROM #auxi AS O1
WHERE O1.precio =
(SELECT MAX(O2.precio)
FROM #auxi AS O2
WHERE O2.nrofac = O1.nrofac )
order by O1.nrofac ok

select venta,
(select SUM(venta) from ventas) as total_ventas
FROM VENTAS OK- SOLO SE EJECUTA UNA VEZ YA QUE EL VALOR
VENTA_TOTAL NO DEPENDE DE CADA REGISTRO

SELECT orderid, custid, val,


CAST(val / (SELECT SUM(val) FROM dbo.MyOrderValues) * 100.
AS NUMERIC(5, 2)) AS pct,
CAST(val - (SELECT AVG(val) FROM dbo.MyOrderValues)
AS NUMERIC(12, 2)) AS diff
FROM dbo.MyOrderValues; mal
WITH Aggs AS

(
SELECT SUM(val) AS sumval, AVG(val) AS avgval
FROM dbo.MyOrderValues
)
SELECT orderid, custid, val,
CAST(val / sumval * 100. AS NUMERIC(5, 2)) AS pct,
CAST(val - avgval AS NUMERIC(12, 2)) AS diff
FROM dbo.MyOrderValues
CROSS JOIN Aggs; ok

SELECT orderid, custid, val,


CAST(val / sumval * 100. AS NUMERIC(5, 2)) AS pct,
CAST(val - avgval AS NUMERIC(12, 2)) AS diff
FROM dbo.MyOrderValues
cross apply (
SELECT SUM(val) AS sumval, AVG(val) AS avgval
FROM dbo.MyOrderValues )Aggs; ok
Haciendo el query de la segunda o tercer forma podemos obtener todos los
valores porcentaje/suma/cantidad etc, accediendo una sola vez a la tabla
MyOrderValues.

Control de Transacciones (Rojo)

Las transacciones deberán ser controladas pos las sintaxis BEGIN TRAN / COMMIT TRAN /
ROLLBACK TRAN

Se deberán tener los siguientes tips a la hora de manejar transacciones

● Deberán ser lo más corta posibles ya que el tiempo afectara a los loqueos que se verán en
problemas de performance.
o Esto implica que dentro del código de la transacción solo deba ir lo necesario.
● Si hay anidamiento de transacciones no hacer evaluaciones del estado de la transacción ya
que estas cosas estirarían el tiempo de la misma, definir bien al ACID adecuado.
● No usar funciones Select dentro de una transacción

En el siguiente ejemplo se muestra como el ACID no está bien definido, ya que si la auditoria sale
mal igual se hace la transacción, en este caso sería conveniente que primero se haga el insert y
luego la auditoria en otra transacción separada y no anidada ya que por más que de error se ha
definido como regla que se continúe.

Al estar el código escrito así lo que se lograra es un mayor tiempo de duración en la transacción

BEGIN TRAN

INSERT INTO CLIENTES (ID)


VALUES (1)

EXEC AUDITAR
IF @@ERROR <> OR @@ERROR = 0 -- si hay o no errores en auditar
BEGIN
COMMIT TRAN -- hacemos igual el COMMIT
END

Triggers (Rojo)

Los triggers forman parte de cada una de las transacciones (sean explicitas o implícitas) lo cual el
código que deben contener debe estar con las mejores prácticas para así evitar un mayor tiempo
de duración de las transacciones y que esto impacte en mayores loqueos y a su vez en
performance.

La siguiente lista son las consideraciones que se deben hacer en los Triggers

● Generar un trigger por operación en lugar de uno combinado.


o Si necesitamos disparar un trigger para INSERT y DELETE por ejemplo es
aconsejable armar dos triggers en lugar de uno solo ya que de hacerlo así se
tendrá que evaluar en cada ejecución que tipo de operación lo ha disparado
utilizando las tablas Inserted y Deleted, esta operación de consulta constante hará
incrementar el tiempo de proceso y estirar la transacción que lo disparo.
● Utilizar al inicio del código SET NOCOUNT ON
o Esto permite no generar una sobrecarga de informacion entre el trigger y quien lo
disparo para determinar los registros afectados
● No se podran utilizar en el codigo del trigger
o Cursores
o Tablas Temporales
o Whiles
o Querys No SARGS
● Los Trigger no se disparan registro a registro sino que pos instrucción con lo cual deberan
ser programados de tal forma que se considere que hay mas de un registro en las tablas
internas Inserted y Deleted

CREATE TRIGGER DBO.TRE1 ON PRUEBA


FOR INSERT
AS

DECLARE @ID INT


SELECT @ID = ID FROM INSERTED

INSERT INTO DBO.PRUEBA2 (ID)


VALUES(@ID)

GO MAL, ESTA PENSADO COMO SI SE DISPARARIA REGISTRO A REGISTRO

CREATE TRIGGER DBO.TRE1 ON PRUEBA


FOR INSERT
AS

INSERT INTO DBO.PRUEBA2 (ID)


SELECT ID FROM inserted
GO FORMA CORRECTA

Control de errores (Amarillo)

El control de errores dentro del TSQL es fundamental para que nuestros procesos sean
consistentes.

Para el mismo se deberán seguir los siguientes lineamientos

● Utilizar bloques BEGIN TRY / BEGIN CATCH


o A partir de SQL 2005 esta es la forma correcta de controlar los errores en lugar de
@@error ya que no es necesario evaluarlos por cada sentencia como así también
permite customizar los resultados del error
● La información enviada al usuario final no debe contener información sensible (nombre de
tabla, servidor, etc)
o Si el error no es manipulado por la aplicación y simplemente enviado el mensaje
de SQL Server estaremos enviando al usuario información sensible que puede
afectar la seguridad del sistema
o Tratar de utilizar una tabla para auditar los errores con toda su descripción y al
usuario enviarle algo mas genérico.
● Utilizar RAISERROR para emitir el error a un nivel superior
● Usar el RETURN en los SP para retornar el número de error además del RaiseError
SELECT 1/0 AS DIVISION
select @@error --NO USAR

begin try
SELECT 1/0 AS DIVISION
end try

begin catch
raiserror('Se genero un error en la operacion',16,1)
end catch

-- USAR DE ESTA FORMA

Acceso a objetos fuera de la base de datos. (Verde)

Si dentro del código TSQL es necesario acceder a objetos que no están dentro de la misma base de
datos se deberán utilizar o vistas o Sinónimos (este no solo es útil para tablas sino que también
para el resto de los objetos)

El uso de estas técnicas evitara el hardcoded y acoplamiento en el código.

SELECT ID
FROM RRHH.HUMANRESOURCES.EMPLOYEE -MAL

CREATE SYNONYM EMPLEADOS


FOR RRHH.HUMANRESOURCES.EMPLOYEE
GO

SELECT * FROM EMPLEADOS

Funciones a deprecar (Amarillo)

Las funciones a deprecar son aquellas que no estarán más soportadas en nuevas versión de SQL
Server y que deben ser reemplazadas.

No se deberán usar ninguna de estas funciones para nuevos desarrollos (incluyendo reingeniería)

En el siguiente link se encuentran dichas funciones

http://msdn.microsoft.com/en-us/library/ms143729.aspx

También podría gustarte