DITIC – Desarrollo de
Sistemas
Sentencias SQL optimizadas
José R. Ortiz G.
No usar select *
Seleccionar sólo aquellos campos que se necesiten. Cuando se utiliza un select *
el motor de base de datos debe usar tiempo extra indagando en las bases de
datos del sistema para identificar cuales son las columnas que pertenecen a
la(s) tabla(s) participantes en el querie y no puede hacer uso de los planes de
ejecución generados previamente para consultas similares
--Usar:
select p.Nombre, p.Codigo from proveedor p
where
p.Nombre like 'VAR%’
--En lugar de
select *
where
p.Nombre like 'VAR%'

Evitar usar count(*)
• Si intentamos obtener el conteo de registros de una tabla,
regularmente usamos el siguiente query:
SELECT COUNT(*) FROM dbo.orders
• Esto provocará que se escanee toda la tabla para obtener la
cantidad de registros.
• El siguiente query no requiere un escaneo completo de la tabla:
SELECT rows FROM sysindexes WHERE id = OBJECT_ID('dbo.Orders')
AND indid < 2
• También se puede usar:
SELECT COUNT(1) FROM dbo.orders

Usar Nombre de las columnas
Al igual que en los select debemos utilizar los nombres de las columnas de las
tablas al momento de hacer los queries de insert.

Usar
INSERT INTO [proveedor]
([IdProveedorAch], [Nombre])
VALUES
(1000, 'El Proveedor 100')
GO
En lugar de
INSERT INTO [proveedor]
VALUES
(1000, 'El Proveedor 100')
GO
Usar
INSERT INTO [proveedor]
([IdProveedorAch], [Nombre])
VALUES
(@IdProveedor, @NombreProveedor)
GO
En lugar de
INSERT INTO [proveedor]
VALUES
(@IdProveedor, @NombreProveedor)
GO
Asignación de variables
Para asignar un valor a una variable puede utilizar las siguientes opciones:
DECLARE @Count INT, @ NombreAlmacen varchar(100)
USAR:
SET @Count = 0
EN LUGAR DE:
SELECT @Count = 0
SELECT Name
FROM @NombreAlmacen = Sales.Store
WHERE CustomerID = 1000
• Para los casos de asignaciones que no provienen de una consulta de tablas se
recomienda usar el SET.
• Se recomienda el SELECT cuando se hacen consultas a una tabla.

Asignación de variables
Al utilizar SELECT para asignar un valor a una variable que es el resultado de una consulta
que no devuelve datos la variable podría tomar el valor de NULL o continuar con su valor
anterior
Forma Recomendada
DECLARE @var1 nvarchar(30)
SET @var1 = 'Generic Name’
SET @var1 = (SELECT Name
FROM Sales.Store
WHERE CustomerID = 1000
)
select @var1

Forma no Recomendada
DECLARE @var1 nvarchar(30)
SELECT @var1 = 'Generic Name'
SELECT @var1 = Name
FROM Sales.Store
WHERE CustomerID = 1000
select @var1
Tratar de no usar el !=, <>, NOT
Se leen todos los datos de las tablas involucradas creando
tablas temporales del sistema para luego hacer los filtros
Si se utiliza WHERE en las consultas se recomienda utilizar los
operadores de mejor rendimiento. Orden de rendimiento de
mayor a menor:
• =
• >,>=,<=,<
• LIKE
• IN
• <>,NOT IN, NOT LIKE

Uso de cursores
No se recomienda el uso de cursores en el desarrollo de aplicaciones.
De ser necesario el uso de cursores utilizar la cláusula FAST_FORWARD en la
declaración del Cursor.
Ejemplo:
declare ReciboMatricula cursor FAST_FORWARD
FOR
Select Cedestu, NO_IMPRESA, ano, peracad, CODCARR
FROM #TREC with (nolock)

Evitar la condiciones IN ( SELECT…)
sustituyéndolas por joins o DERIVED TABLES
Usar
SELECT MIN(Salary)
FROM (SELECT TOP 2 Salary
FROM Employees
ORDER BY Salary DESC)
En lugar de
SELECT MIN(Salary)
FROM Employees
WHERE EmpID IN (SELECT TOP 2 EmpID
FROM Employees
ORDER BY Salary Desc)

1. Evitar el uso de las
condiciones in ( select…)
2. Procurar el uso de Tablas
derivadas cuando sea
posible
En lo posible usar DERIVED TABLES en lugar de
TEMPORARY TABLES
--Usar:
select s.IdSolicitudBS from solicitud_BS_detalle s,
(select sum(ppd.Monto) monto, ppd.IdDocumentoTranDet
from PreProcesoDetalle ppd where
ppd.IdDocumentoTipoPres = 63 and ppd.IdDocumentoTipoTran in (4,5)
group by ppd.IdDocumentoTranDet
) p
where s.IdSolicitudBSDetalle = p.IdDocumentoTranDet
and p.monto <> s.CostoTotal
--En lugar de:
select sum(ppd.Monto) monto, ppd.IdDocumentoTranDet
into #p
from PreProcesoDetalle ppd where
ppd.IdDocumentoTipoPres = 63 and ppd.IdDocumentoTipoTran in (4,5)
group by ppd.IdDocumentoTranDet
select s.IdSolicitudBS from solicitud_BS_detalle s, #p p
where s.IdSolicitudBSDetalle = p.IdDocumentoTranDet
and p.monto <> s.CostoTotal

En lo posible usar DERIVED TABLES en lugar de
TEMPORARY TABLES
--Usar: CTE: Common Table Expressions: Expresiones Comunes de Tablas
with p ( monto, IdDocumentoTranDet)
as
(
select sum(ppd.Monto) monto, ppd.IdDocumentoTranDet
from PreProcesoDetalle ppd where
ppd.IdDocumentoTipoPres = 63 and ppd.IdDocumentoTipoTran in (4,5)
group by ppd.IdDocumentoTranDet
)
select s.IdSolicitudBS
from solicitud_BS_detalle s with (nolock), p
where
s.IdSolicitudBSDetalle = p.IdDocumentoTranDet and
p.monto <> s.CostoTotal

Usar DERIVED TABLES en lugar de
TEMPORARY TABLES
A menos que sea realmente necesario debemos evitar el uso de tablas
temporales, en su lugar usemos tablas de variables (cuando la cantidad de
registros sea pequeña o Tablas Derivadas).
Criterio de
Comparación
Tablas
Temporales
Tablas
de Variables
Declaración Create Table #tabla Declare @Tabla table
Almacenamiento Tempdb (disco) Proceso(ram)
Restricciones Indices:si, constraints:si,
recompilan:si, unique: si
eliminación:manual, primary
key: si, autoincremento: si
Indices:no, constraints:si,
recompilan:no, unique: no,
eliminación:auto, primary key: si,
autoincremento: si
Bloqueos ddl si No
Situaciones
comunes de uso
Muchos registros (aprox
5000), necesidad de indices
Pocos registros (menos de 100),
operaciones de dml básicas

En lo posible usar DERIVED TABLES en lugar de
TEMPORARY TABLES
En caso de utilizar tablas temporales recuerde
eliminarlas una vez finalice el proceso
CREATE TABLE #Temp (Id INT)
… Procesamiento e datos ...
… Procesamiento e datos ...
… Procesamiento e datos ...
DROP TABLE #Temp

Usar SET NOCOUNT On en Procedimientos,
Funciones y Triggers
Evita que el motor de base de datos haga calculo de registros
por cada sentencia SQL que se ejecute. La cantidad de datos
afectados por sentencia requiere de tiempo.
Si requiere saber la cantidad de registros afectados puede
utilizar la variable @@rowcount

Uso de literales y consultas parametrizadas
--Usar:
Declare @IdReciboFacrura integer
Set @IdReciboFacrura = 339320
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen, TipoFactura
FROM finReciboFactura f
WHERE
f.NumeroDocumentoOrigen in ( '14-00031557')
AND NOT EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a
WHERE a.IdFactura = f.IdReciboFactura )
AND f.IdReciboFactura = @IdReciboFacrura
--En lugar de:
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen, TipoFactura
FROM finReciboFactura f
WHERE
f.NumeroDocumentoOrigen in ( '14-00031557')
AND NOT EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a
WHERE a.IdFactura = f.IdReciboFactura )
AND f.IdReciboFactura = 339320

Procesamiento Lógico de una Consulta
Step 1: The FROM Phase
Step 2: The WHERE Phase
Step 3: The GROUP BY Phase
Step 4: The HAVING Phase
Step 5: The SELECT Phase
Step 6: The Presentation ORDER BY Phase

Procesamiento Lógico de una Consulta

Se genera la primera tabla virtual VT1
Procesamiento Lógico de una Consulta

Se genera la
segunda tabla
virtual VT2
Se genera la
tercera tabla
virtual VT3
Se genera la
cuarta tabla
virtual VT4
Procesamiento Lógico de una Consulta

Se genera la
segunda tabla
virtual VT5
Se genera el
resultado
Final
Orden en que se colocan las tablas en el from
debe ir de menor a mayor
--Usar:
select r.IdReciboFactura, r.Numero, r.FechaDocumento, rd.IdItem, rd.Cantidad,
rd.PrecioUnitario
from finReciboFactura r
inner join finReciboFacturaDetalle rd
on rd.IdReciboFactura = r.IdReciboFactura
--En lugar de
select r.IdReciboFactura, r.Numero, r.FechaDocumento, rd.IdItem, rd.Cantidad,
rd.PrecioUnitario
from finReciboFacturaDetalle rd
inner join finReciboFactura r
on r.IdReciboFactura = rd.IdReciboFactura

Las tablas con
menos cantidad
de registros a la
izquierda
Orden en que se colocan las tablas en el from
debe ir de menor a mayor

-- Hacer esto:
Select
t.title_id, t.title, a.au_id,
a.au_fname + ' ' + a.au_lname
From titles t inner join titleauthor ta
on t.title_id = ta.title_id
inner join authors a
on ta.au_id = a.au_id
-- En lugae de esto:
Select
t.title_id, t.title, a.au_id,
a.au_fname + ' ' + a.au_lname
From authors a inner join titleauthor ta
on a.au_id = ta.au_id
inner join titles t
on ta.title_id = t.title_id
Las tablas con menos cantidad de registros a la izquierda
Orden de ejecución del Where

Como nos ayuda:
1. Se identifica cual es la
parte de la consulta con
mayor cantidad de
elementos filtrados
2. Reduce la cantidad de
registros a procesar
3. Las tablas intermedias
creadas por el DBMS son
mas pequeñas
Orden de ejecución del Where

1. Se recomienda todos los valores literales o constantes como las instrucciones al final
de la consulta.
2. Si estamos trabajando con un procedimiento almacenado toda constante debe ser
trabajada a través de variables (consultas parametrizadas)
-- Hacer esto:
SELECT
P.NÚMEROP, P.NÚMD, E.APELLIDO,
E.DIRECCIÓN, E.FECHA_NCTO
FROM
PROYECTO AS P, DEPARTAMENTO AS D,
EMPLEADO AS E
WHERE
P.NÚMD=D.NÚMEROD AND
D.NSS_JEFE=E.NSS AND
P.LOCALIZACIÓN=‘Stafford’
-- En lugar de esto:
SELECT
P.NÚMEROP, P.NÚMD, E.APELLIDO,
E.DIRECCIÓN, E.FECHA_NCTO
FROM
PROYECTO AS P, DEPARTAMENTO AS D,
EMPLEADO AS E
WHERE
P.LOCALIZACIÓN=‘Stafford’ AND
P.NÚMD=D.NÚMEROD AND
D.NSS_JEFE=E.NSS
Orden de ejecución del Where

1. Se debe analizar el resultado de los sub consultas para determinar su tamaño y
ubicación dentro del Where o el uso de una de una CTE: Common Table Expession
2. Si el sub consulta retorna demasiados datos puede colocarse al inicio del WHERE
de forma tal que cuando llegue el momento de su ejecución la cantidad de datos
filtrados sea la máxima
SELECT
P.NÚMEROP, P.NÚMD, E.APELLIDO,
E.DIRECCIÓN, E.FECHA_NCTO
FROM
PROYECTO AS P, DEPARTAMENTO AS D,
EMPLEADO AS E
WHERE
P.NÚMD=D.NÚMEROD AND
D.NSS_JEFE=E.NSS AND
P.LOCALIZACIÓN=‘Stafford’
SELECT
P.NÚMEROP, P.NÚMD, E.APELLIDO,
E.DIRECCIÓN, E.FECHA_NCTO
FROM
PROYECTO AS P, DEPARTAMENTO AS D,
EMPLEADO AS E
WHERE
P.LOCALIZACIÓN=‘Stafford’ AND
P.NÚMD=D.NÚMEROD AND
D.NSS_JEFE=E.NSS
Uso de sintaxis UNION
Por defecto un UNION equivale a realizar un SELECT DISTINCT
sobre el resultado final de una consulta.

En lugar de:
select distinct
r.IdPersonaUnica,
r.NumeroDocumentoOrigen from
finReciboFactura r
where
r.TipoFactura = 0 and
r.Cedula = '02-0735-000483'
UNION
select distinct
r.IdPersonaUnica,
r.NumeroDocumentoOrigen
from finReciboFactura r
where
r.TipoFactura = 1 and
r.Cedula = '02 -0735-000483'
Usar :
select
r.IdPersonaUnica,
r.NumeroDocumentoOrigen from
finReciboFactura r
where
r.TipoFactura = 0 and
r.Cedula = '02-0735-000483'
UNION
select
r.IdPersonaUnica,
r.NumeroDocumentoOrigen
from finReciboFactura r
where
r.TipoFactura = 1 and
r.Cedula = '02 -0735-000483'
Usar EXISTS en lugar de IN
--Usar:
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f
WHERE
EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a
WHERE a.IdFactura = f.IdReciboFactura ) AND
f.NumeroDocumentoOrigen = '14-00031557'
--En lugar de
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f
WHERE
f.IdReciboFactura in ( SELECT a.IdFactura
FROM finReciboFacturaAsociado a) AND
f.NumeroDocumentoOrigen = '14-00031557'

Las subconsultas deben estar mas cerca del
Where
--Usar:
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f
WHERE
EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a
WHERE a.IdFactura = f.IdReciboFactura ) AND
f.NumeroDocumentoOrigen = '14-00031557'
--En lugar de
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f
WHERE
f.NumeroDocumentoOrigen = '14-00031557’ AND
f.IdReciboFactura in ( SELECT a.IdFactura
FROM finReciboFacturaAsociado a)

Usar BETWEEN en lugar de IN
--Usar:
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f
WHERE
f.IdReciboFactura between 511236 and 625487
--En lugar de
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f
WHERE
f.IdReciboFactura in ( select t.IdFactura from tempFacturas )

Usar BETWEEN en lugar de ( a >= x and b <=
y )
--Usar:
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f
WHERE
f.IdReciboFactura between 511236 and 625487
--En lugar de
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f
WHERE
f.IdReciboFactura >= 511236 and f.IdReciboFactura <= 625487

Usar like con leading character
-- La base de datos puede usar el índice
select p.Nombre, p.Codigo from proveedor p
where
p.Nombre like 'VAR%'
-- La base de datos no puede usar el índice, por el uso de la función
Se recorrerá toda la tabla
select p.Nombre, p.Codigo from proveedor p
where
p.Nombre like '%VAR'

Usar like en lugar substring()
-- La base de datos puede usar el índice
select p.Nombre, p.Codigo from proveedor p
where
p.Nombre like 'VAR%'
-- La base de datos no puede usar el índice, por el uso de la función
select p.Nombre, p.Codigo from proveedor p
where
substring( p.Nombre, 1, 3 ) = 'VAR'

No hacer Order by cuando se usa la clausula
distinct
Se hace que el motor de base de datos se esfuerce
innecesariamente
Select distinct f.IdReciboFactura, F.Numero
From finReciboFactura
Order by f.IdReciboFactura

Evitar en lo posible operaciones en las
consultas
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f, FinReciboFacturaDetalle fd
WHERE
f.IdReciboFactura = fd.IdReciboFactura and
fd.PrecioUnitario * fd.Cantidad >= 1500

Si se deben hacer cálculos en los filtros
colocar el cálculo del lado derecho
--Usar esto:
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f, FinReciboFacturaDetalle fd
WHERE
f.IdReciboFactura = fd.IdReciboFactura and
fd.PrecioUnitario = ( 1500 * 1.07)
--En lugar de:
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f, FinReciboFacturaDetalle fd
WHERE
f.IdReciboFactura = fd.IdReciboFactura and
( 1500 * 1.07) = fd.PrecioUnitario

Union all en lugar de Or
Reescribir consultas que tienen OR’s no inclusivos.
Evitar en la mayor medida posible el or en las consultas. Reemplazar su
uso por Union, preferiblemente union all.
El union ordena los registros que se están leyendo, el union all
simplemente los une y no altera el orden
En lugar de
Select * from solicitud_bs s
where (s.idestatus = 12 and
s.presupuestario = 0)
or (s.idestatus = 55 and
s.presupuestario = 1)
Usar
Select * from solicitud_bs s
where (s.idestatus = 12 and
s.presupuestario = 0)
Union all
Select * from solicitud_bs s
where (s.idestatus = 55 and
s.presupuestario = 1)

Práctica
Union y Distinct
Recordar que el UNION muestra los registros diferentes de la unión
de las consultas, por lo que no es necesario aplicar la operación de
distinct.
Se recomienda no utilizar operaciones innecesarias ya que
consumen tiempo de procesamiento y memoria del servidor.
En lugar de
Select distinct [IdSolicitudBS],
[Numero], [FechaDocumento]
from solicitud_bs s where (s.idestatus
= 12 and s.presupuestario = 0)
Union
Select distinct [IdSolicitudBS],
[Numero], [FechaDocumento]
from solicitud_bs s where (s.idestatus
= 55 and s.presupuestario = 1)
Usar
Select [IdSolicitudBS], [Numero],
[FechaDocumento]
from solicitud_bs s where (s.idestatus
= 12 and s.presupuestario = 0)
Union
Select [IdSolicitudBS], [Numero],
[FechaDocumento]
from solicitud_bs s where (s.idestatus
= 55 and s.presupuestario = 1)

Uso de los alias
Si se están usando alias debe colocarse a todas las columnas de la
tabla utilizadas en el querie. El no usarlo implica tiempo del motor de
base de datos para indagar a que tabla pertenece la columna
independientemente de que esta no exista en las otras tablas
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen,
TipoFactura
FROM finReciboFactura f
WHERE
f.NumeroDocumentoOrigen in ( '14-00031557')
AND NOT EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a
WHERE a.IdFactura = f.IdReciboFactura )
AND f.IdReciboFactura = 339320

Uso del with (nolock)
A este concepto se le conoce como lecturas sucias (dirty reads).
Sólo debe utilizarse cuando se esta seguro que los datos a consultar
no serán afectados por otras sentencias SQL ejecutadas por otros
usuarios o cuando no importe realmente que eso suceda.
select s.IdSolicitudBS from solicitud_BS_detalle s with (nolock),
(select sum(ppd.Monto) monto, ppd.IdDocumentoTranDet
from PreProcesoDetalle ppd where
ppd.IdDocumentoTipoPres = 63 and
ppd.IdDocumentoTipoTran in (4,5)
group by ppd.IdDocumentoTranDet
) p
where s.IdSolicitudBSDetalle = p.IdDocumentoTranDet
and p.monto <> s.CostoTotal

Evitar el SQL Dinámico
• A menos que sea realmente necesario, debemos evitar su uso
por las siguientes razones:
• Es difícil de verificar sus errores
• Si la entrada del usuario contiene una entrada de usuario existen
posibilidades de ataques con Inyecciones de SQL.
SELECT COUNT(*) FROM tabla WHERE campo1='loquesea' AND
campo2='loquesea' OR user LIKE 'a%';--

Evita llamados innecesarios
Si necesitas llamar repetidamente a una función de SQL o UDF, se
debe buscar la posibilidad de almacenar la salida de la misma en
variables para evitar ejecutar código innecesario.
DECLARE @NombreColaborador varchar(31)
select @NombreColaborador = dbo.fx_NombreColaborador(1)
Declare @fecha datetime
Select @fecha = getdate()

Uso de trigger’s
• Evitar en la medida de lo
posible el uso de trigger’s.
• Causan un impacto alto sobre
el rendimiento del servidor
• No usar trigger’s que se
puedan implementar usando
constraints
• Evitar usar el mismo trigger
para distintos eventos (Insert,
Update, Delete)
• No usar transacciones dentro
de los triggers. Estos usan la
transacción sobre la que se
está ejecutando.
--Usar
Create Trigger tgr_Algo_I
For insert
Create trigger tgr_Algo_U
For update
Create trigger tgr_Algo_U
For delete
--En lugar de
Create trigger tgr_ALGO
For insert, update, delete

NOT EXISTS para encontrar datos del padre
que no tiene hijos
SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen
FROM finReciboFactura f
WHERE f.NumeroDocumentoOrigen = '14-00031557’ AND
NOT EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a
WHERE a.IdFactura = f.IdReciboFactura )

Casos especiales
Conteo por Total de Registros
USE AdventureWorks;
GO
SELECT
ROW_NUMBER() OVER(ORDER BY s.SalesPersonID ASC) AS ‘NumeroLinea'
,s.SalesPersonID, c.FirstName, c.LastName
,s.SalesYTD, a.PostalCode, c.FirstName, c.LastName
FROM Sales.SalesPerson s
INNER JOIN Person.Contact c
ON s.SalesPersonID = c.ContactID
INNER JOIN Person.Address a
ON a.AddressID = c.ContactID
WHERE
TerritoryID IS NOT NULL
AND SalesYTD <> 0;
GO
Casos especiales
Conteo por grupo de registros de Registros
USE AdventureWorks;
GO
SELECT
ROW_NUMBER() OVER( ORDER BY s.TerritoryID ASC) AS 'NumeroLinea',
ROW_NUMBER() OVER( PARTITION BY s.TerritoryID ORDER BY s.TerritoryID ASC) AS
'NumeroLineaT'
,s.TerritoryID,s.SalesYTD, a.PostalCode, c.FirstName, c.LastName
FROM Sales.SalesPerson s
INNER JOIN Person.Contact c
ON s.SalesPersonID = c.ContactID
INNER JOIN Person.Address a
ON a.AddressID = c.ContactID
WHERE
TerritoryID IS NOT NULL
AND SalesYTD <> 0
;
GO
Conteo por Total y por grupo de registros de
Registros
USE AdventureWorks;
GO
SELECT
ROW_NUMBER() OVER( ORDER BY s.TerritoryID ASC) AS 'NumeroLinea',
ROW_NUMBER() OVER( PARTITION BY s.TerritoryID ORDER BY s.TerritoryID ASC) AS 'NumeroLineaT'
,s.TerritoryID,s.SalesYTD, a.PostalCode, c.FirstName, c.LastName
FROM Sales.SalesPerson s
INNER JOIN Person.Contact c
ON s.SalesPersonID = c.ContactID
INNER JOIN Person.Address a
ON a.AddressID = c.ContactID
WHERE
TerritoryID IS NOT NULL
AND SalesYTD <> 0
;
GO
Casos especiales
Simular un Limit de MySQL
USE AdventureWorks;
GO
declare @Rango1 int, @Rango2 int
set @Rango1 = 4; set @Rango2 = 12
SELECT c.row, c.ContactID, c.FirstName, c.LastName
FROM
( SELECT pc.ContactID, pc.FirstName, pc.LastName,
ROW_NUMBER() OVER (ORDER BY pc.FirstName) as row
FROM Person.Contact pc
) as c
WHERE row >= @Rango1 and row <= @Rango2
order by c.FirstName
Casos especiales
Casos especiales
Simular un Limit de MySQL y Contar los registros
USE AdventureWorks;
GO
declare @Rango1 int, @Rango2 int
set @Rango1 = 4; set @Rango2 = 12
SELECT ROW_NUMBER() OVER (ORDER BY c.ContactID) as NumeroLinea,
c.row, c.ContactID, c.FirstName, c.LastName
FROM
( SELECT pc.ContactID, pc.FirstName, pc.LastName,
ROW_NUMBER() OVER (ORDER BY pc.FirstName) as row
FROM Person.Contact pc
) as c
WHERE row >= @Rango1 and row <= @Rango2
order by c.FirstName
Casos especiales
Recursividad
Casos especiales
Recursividad
Estructura de una CTE Recursiva
WITH cte_name ( column_name [,...n] )
AS
(
CTE_query_definition -- Elemento inicial/Padre/Delimitador
UNION ALL
CTE_query_definition -- Elemento Hijo/Recursivo
)
SELECT ( column_name [,...n] -- Statement using the CTE
FROM cte_name
Casos especiales
Recursividad
Una condición importante en la consulta
recursiva es que debe haber un nodo final
select o.opcion_id, o.idOpcionPadre, o.descripcion
from eopciones o, eapli_opciones ao, eaplicaciones a
where
o.opcion_id = ao.opcion_id and
ao.aplicacion_id = a.aplicacion_id and
o.idOpcionPadre = 0 and
a.aplicacion_id = 3
Union
select o.opcion_id, o.idOpcionPadre, o.descripcion
from eopciones o
where
o.opcion_id = 0
Casos especiales
Recursividad
declare @aplicacion_id int, @IdPadre int ;
set @aplicacion_id = 3; set @IdPadre = 0;
with JerarquiaOpciones( opcion_id, idOpcionPadre, aplicacion_id, nivel, jerarquia, orden) -- CTE: Common Table Expression
as
(
select o.opcion_id, o.idOpcionPadre, a.aplicacion_id, 1 as nivel, cast( o.opcion_id as varchar(max)) as jerarquia, ao.orden
from eopciones o, eapli_opciones ao, eaplicaciones a
where
o.opcion_id = ao.opcion_id and
ao.aplicacion_id = a.aplicacion_id and
o.idOpcionPadre = @IdPadre and
a.aplicacion_id = @aplicacion_id
union all
select o.opcion_id, o.idOpcionPadre, j.aplicacion_id, (nivel + 1 ) as nivel,
jerarquia + '' + cast( o.opcion_id as varchar(20) ) as jerarquia, j.orden
from eopciones o
inner join JerarquiaOpciones j on o.idOpcionPadre = j.opcion_id
)
select a.aplicacion_id, a.nombre , o.opcion_id, o.descripcion, j.nivel, j.jerarquia, o.idOpcionPadre, j.orden
from eopciones o
inner join JerarquiaOpciones j on o.opcion_id = j.opcion_id
inner join eaplicaciones a on a.aplicacion_id = j.aplicacion_id
order by j.jerarquia, j.orden
Práctica

DITIC - Desarrollo de Sistemas_Recomendaciones_BD.pdf

  • 1.
    DITIC – Desarrollode Sistemas Sentencias SQL optimizadas José R. Ortiz G.
  • 2.
    No usar select* Seleccionar sólo aquellos campos que se necesiten. Cuando se utiliza un select * el motor de base de datos debe usar tiempo extra indagando en las bases de datos del sistema para identificar cuales son las columnas que pertenecen a la(s) tabla(s) participantes en el querie y no puede hacer uso de los planes de ejecución generados previamente para consultas similares --Usar: select p.Nombre, p.Codigo from proveedor p where p.Nombre like 'VAR%’ --En lugar de select * where p.Nombre like 'VAR%' 
  • 3.
    Evitar usar count(*) •Si intentamos obtener el conteo de registros de una tabla, regularmente usamos el siguiente query: SELECT COUNT(*) FROM dbo.orders • Esto provocará que se escanee toda la tabla para obtener la cantidad de registros. • El siguiente query no requiere un escaneo completo de la tabla: SELECT rows FROM sysindexes WHERE id = OBJECT_ID('dbo.Orders') AND indid < 2 • También se puede usar: SELECT COUNT(1) FROM dbo.orders 
  • 4.
    Usar Nombre delas columnas Al igual que en los select debemos utilizar los nombres de las columnas de las tablas al momento de hacer los queries de insert.  Usar INSERT INTO [proveedor] ([IdProveedorAch], [Nombre]) VALUES (1000, 'El Proveedor 100') GO En lugar de INSERT INTO [proveedor] VALUES (1000, 'El Proveedor 100') GO Usar INSERT INTO [proveedor] ([IdProveedorAch], [Nombre]) VALUES (@IdProveedor, @NombreProveedor) GO En lugar de INSERT INTO [proveedor] VALUES (@IdProveedor, @NombreProveedor) GO
  • 5.
    Asignación de variables Paraasignar un valor a una variable puede utilizar las siguientes opciones: DECLARE @Count INT, @ NombreAlmacen varchar(100) USAR: SET @Count = 0 EN LUGAR DE: SELECT @Count = 0 SELECT Name FROM @NombreAlmacen = Sales.Store WHERE CustomerID = 1000 • Para los casos de asignaciones que no provienen de una consulta de tablas se recomienda usar el SET. • Se recomienda el SELECT cuando se hacen consultas a una tabla. 
  • 6.
    Asignación de variables Alutilizar SELECT para asignar un valor a una variable que es el resultado de una consulta que no devuelve datos la variable podría tomar el valor de NULL o continuar con su valor anterior Forma Recomendada DECLARE @var1 nvarchar(30) SET @var1 = 'Generic Name’ SET @var1 = (SELECT Name FROM Sales.Store WHERE CustomerID = 1000 ) select @var1  Forma no Recomendada DECLARE @var1 nvarchar(30) SELECT @var1 = 'Generic Name' SELECT @var1 = Name FROM Sales.Store WHERE CustomerID = 1000 select @var1
  • 7.
    Tratar de nousar el !=, <>, NOT Se leen todos los datos de las tablas involucradas creando tablas temporales del sistema para luego hacer los filtros Si se utiliza WHERE en las consultas se recomienda utilizar los operadores de mejor rendimiento. Orden de rendimiento de mayor a menor: • = • >,>=,<=,< • LIKE • IN • <>,NOT IN, NOT LIKE 
  • 8.
    Uso de cursores Nose recomienda el uso de cursores en el desarrollo de aplicaciones. De ser necesario el uso de cursores utilizar la cláusula FAST_FORWARD en la declaración del Cursor. Ejemplo: declare ReciboMatricula cursor FAST_FORWARD FOR Select Cedestu, NO_IMPRESA, ano, peracad, CODCARR FROM #TREC with (nolock) 
  • 9.
    Evitar la condicionesIN ( SELECT…) sustituyéndolas por joins o DERIVED TABLES Usar SELECT MIN(Salary) FROM (SELECT TOP 2 Salary FROM Employees ORDER BY Salary DESC) En lugar de SELECT MIN(Salary) FROM Employees WHERE EmpID IN (SELECT TOP 2 EmpID FROM Employees ORDER BY Salary Desc)  1. Evitar el uso de las condiciones in ( select…) 2. Procurar el uso de Tablas derivadas cuando sea posible
  • 10.
    En lo posibleusar DERIVED TABLES en lugar de TEMPORARY TABLES --Usar: select s.IdSolicitudBS from solicitud_BS_detalle s, (select sum(ppd.Monto) monto, ppd.IdDocumentoTranDet from PreProcesoDetalle ppd where ppd.IdDocumentoTipoPres = 63 and ppd.IdDocumentoTipoTran in (4,5) group by ppd.IdDocumentoTranDet ) p where s.IdSolicitudBSDetalle = p.IdDocumentoTranDet and p.monto <> s.CostoTotal --En lugar de: select sum(ppd.Monto) monto, ppd.IdDocumentoTranDet into #p from PreProcesoDetalle ppd where ppd.IdDocumentoTipoPres = 63 and ppd.IdDocumentoTipoTran in (4,5) group by ppd.IdDocumentoTranDet select s.IdSolicitudBS from solicitud_BS_detalle s, #p p where s.IdSolicitudBSDetalle = p.IdDocumentoTranDet and p.monto <> s.CostoTotal 
  • 11.
    En lo posibleusar DERIVED TABLES en lugar de TEMPORARY TABLES --Usar: CTE: Common Table Expressions: Expresiones Comunes de Tablas with p ( monto, IdDocumentoTranDet) as ( select sum(ppd.Monto) monto, ppd.IdDocumentoTranDet from PreProcesoDetalle ppd where ppd.IdDocumentoTipoPres = 63 and ppd.IdDocumentoTipoTran in (4,5) group by ppd.IdDocumentoTranDet ) select s.IdSolicitudBS from solicitud_BS_detalle s with (nolock), p where s.IdSolicitudBSDetalle = p.IdDocumentoTranDet and p.monto <> s.CostoTotal 
  • 12.
    Usar DERIVED TABLESen lugar de TEMPORARY TABLES A menos que sea realmente necesario debemos evitar el uso de tablas temporales, en su lugar usemos tablas de variables (cuando la cantidad de registros sea pequeña o Tablas Derivadas). Criterio de Comparación Tablas Temporales Tablas de Variables Declaración Create Table #tabla Declare @Tabla table Almacenamiento Tempdb (disco) Proceso(ram) Restricciones Indices:si, constraints:si, recompilan:si, unique: si eliminación:manual, primary key: si, autoincremento: si Indices:no, constraints:si, recompilan:no, unique: no, eliminación:auto, primary key: si, autoincremento: si Bloqueos ddl si No Situaciones comunes de uso Muchos registros (aprox 5000), necesidad de indices Pocos registros (menos de 100), operaciones de dml básicas 
  • 13.
    En lo posibleusar DERIVED TABLES en lugar de TEMPORARY TABLES En caso de utilizar tablas temporales recuerde eliminarlas una vez finalice el proceso CREATE TABLE #Temp (Id INT) … Procesamiento e datos ... … Procesamiento e datos ... … Procesamiento e datos ... DROP TABLE #Temp 
  • 14.
    Usar SET NOCOUNTOn en Procedimientos, Funciones y Triggers Evita que el motor de base de datos haga calculo de registros por cada sentencia SQL que se ejecute. La cantidad de datos afectados por sentencia requiere de tiempo. Si requiere saber la cantidad de registros afectados puede utilizar la variable @@rowcount 
  • 15.
    Uso de literalesy consultas parametrizadas --Usar: Declare @IdReciboFacrura integer Set @IdReciboFacrura = 339320 SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen, TipoFactura FROM finReciboFactura f WHERE f.NumeroDocumentoOrigen in ( '14-00031557') AND NOT EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a WHERE a.IdFactura = f.IdReciboFactura ) AND f.IdReciboFactura = @IdReciboFacrura --En lugar de: SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen, TipoFactura FROM finReciboFactura f WHERE f.NumeroDocumentoOrigen in ( '14-00031557') AND NOT EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a WHERE a.IdFactura = f.IdReciboFactura ) AND f.IdReciboFactura = 339320 
  • 16.
    Procesamiento Lógico deuna Consulta Step 1: The FROM Phase Step 2: The WHERE Phase Step 3: The GROUP BY Phase Step 4: The HAVING Phase Step 5: The SELECT Phase Step 6: The Presentation ORDER BY Phase 
  • 17.
    Procesamiento Lógico deuna Consulta  Se genera la primera tabla virtual VT1
  • 18.
    Procesamiento Lógico deuna Consulta  Se genera la segunda tabla virtual VT2 Se genera la tercera tabla virtual VT3 Se genera la cuarta tabla virtual VT4
  • 19.
    Procesamiento Lógico deuna Consulta  Se genera la segunda tabla virtual VT5 Se genera el resultado Final
  • 20.
    Orden en quese colocan las tablas en el from debe ir de menor a mayor --Usar: select r.IdReciboFactura, r.Numero, r.FechaDocumento, rd.IdItem, rd.Cantidad, rd.PrecioUnitario from finReciboFactura r inner join finReciboFacturaDetalle rd on rd.IdReciboFactura = r.IdReciboFactura --En lugar de select r.IdReciboFactura, r.Numero, r.FechaDocumento, rd.IdItem, rd.Cantidad, rd.PrecioUnitario from finReciboFacturaDetalle rd inner join finReciboFactura r on r.IdReciboFactura = rd.IdReciboFactura  Las tablas con menos cantidad de registros a la izquierda
  • 21.
    Orden en quese colocan las tablas en el from debe ir de menor a mayor  -- Hacer esto: Select t.title_id, t.title, a.au_id, a.au_fname + ' ' + a.au_lname From titles t inner join titleauthor ta on t.title_id = ta.title_id inner join authors a on ta.au_id = a.au_id -- En lugae de esto: Select t.title_id, t.title, a.au_id, a.au_fname + ' ' + a.au_lname From authors a inner join titleauthor ta on a.au_id = ta.au_id inner join titles t on ta.title_id = t.title_id Las tablas con menos cantidad de registros a la izquierda
  • 22.
    Orden de ejecucióndel Where  Como nos ayuda: 1. Se identifica cual es la parte de la consulta con mayor cantidad de elementos filtrados 2. Reduce la cantidad de registros a procesar 3. Las tablas intermedias creadas por el DBMS son mas pequeñas
  • 23.
    Orden de ejecucióndel Where  1. Se recomienda todos los valores literales o constantes como las instrucciones al final de la consulta. 2. Si estamos trabajando con un procedimiento almacenado toda constante debe ser trabajada a través de variables (consultas parametrizadas) -- Hacer esto: SELECT P.NÚMEROP, P.NÚMD, E.APELLIDO, E.DIRECCIÓN, E.FECHA_NCTO FROM PROYECTO AS P, DEPARTAMENTO AS D, EMPLEADO AS E WHERE P.NÚMD=D.NÚMEROD AND D.NSS_JEFE=E.NSS AND P.LOCALIZACIÓN=‘Stafford’ -- En lugar de esto: SELECT P.NÚMEROP, P.NÚMD, E.APELLIDO, E.DIRECCIÓN, E.FECHA_NCTO FROM PROYECTO AS P, DEPARTAMENTO AS D, EMPLEADO AS E WHERE P.LOCALIZACIÓN=‘Stafford’ AND P.NÚMD=D.NÚMEROD AND D.NSS_JEFE=E.NSS
  • 24.
    Orden de ejecucióndel Where  1. Se debe analizar el resultado de los sub consultas para determinar su tamaño y ubicación dentro del Where o el uso de una de una CTE: Common Table Expession 2. Si el sub consulta retorna demasiados datos puede colocarse al inicio del WHERE de forma tal que cuando llegue el momento de su ejecución la cantidad de datos filtrados sea la máxima SELECT P.NÚMEROP, P.NÚMD, E.APELLIDO, E.DIRECCIÓN, E.FECHA_NCTO FROM PROYECTO AS P, DEPARTAMENTO AS D, EMPLEADO AS E WHERE P.NÚMD=D.NÚMEROD AND D.NSS_JEFE=E.NSS AND P.LOCALIZACIÓN=‘Stafford’ SELECT P.NÚMEROP, P.NÚMD, E.APELLIDO, E.DIRECCIÓN, E.FECHA_NCTO FROM PROYECTO AS P, DEPARTAMENTO AS D, EMPLEADO AS E WHERE P.LOCALIZACIÓN=‘Stafford’ AND P.NÚMD=D.NÚMEROD AND D.NSS_JEFE=E.NSS
  • 25.
    Uso de sintaxisUNION Por defecto un UNION equivale a realizar un SELECT DISTINCT sobre el resultado final de una consulta.  En lugar de: select distinct r.IdPersonaUnica, r.NumeroDocumentoOrigen from finReciboFactura r where r.TipoFactura = 0 and r.Cedula = '02-0735-000483' UNION select distinct r.IdPersonaUnica, r.NumeroDocumentoOrigen from finReciboFactura r where r.TipoFactura = 1 and r.Cedula = '02 -0735-000483' Usar : select r.IdPersonaUnica, r.NumeroDocumentoOrigen from finReciboFactura r where r.TipoFactura = 0 and r.Cedula = '02-0735-000483' UNION select r.IdPersonaUnica, r.NumeroDocumentoOrigen from finReciboFactura r where r.TipoFactura = 1 and r.Cedula = '02 -0735-000483'
  • 26.
    Usar EXISTS enlugar de IN --Usar: SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f WHERE EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a WHERE a.IdFactura = f.IdReciboFactura ) AND f.NumeroDocumentoOrigen = '14-00031557' --En lugar de SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f WHERE f.IdReciboFactura in ( SELECT a.IdFactura FROM finReciboFacturaAsociado a) AND f.NumeroDocumentoOrigen = '14-00031557' 
  • 27.
    Las subconsultas debenestar mas cerca del Where --Usar: SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f WHERE EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a WHERE a.IdFactura = f.IdReciboFactura ) AND f.NumeroDocumentoOrigen = '14-00031557' --En lugar de SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f WHERE f.NumeroDocumentoOrigen = '14-00031557’ AND f.IdReciboFactura in ( SELECT a.IdFactura FROM finReciboFacturaAsociado a) 
  • 28.
    Usar BETWEEN enlugar de IN --Usar: SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f WHERE f.IdReciboFactura between 511236 and 625487 --En lugar de SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f WHERE f.IdReciboFactura in ( select t.IdFactura from tempFacturas ) 
  • 29.
    Usar BETWEEN enlugar de ( a >= x and b <= y ) --Usar: SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f WHERE f.IdReciboFactura between 511236 and 625487 --En lugar de SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f WHERE f.IdReciboFactura >= 511236 and f.IdReciboFactura <= 625487 
  • 30.
    Usar like conleading character -- La base de datos puede usar el índice select p.Nombre, p.Codigo from proveedor p where p.Nombre like 'VAR%' -- La base de datos no puede usar el índice, por el uso de la función Se recorrerá toda la tabla select p.Nombre, p.Codigo from proveedor p where p.Nombre like '%VAR' 
  • 31.
    Usar like enlugar substring() -- La base de datos puede usar el índice select p.Nombre, p.Codigo from proveedor p where p.Nombre like 'VAR%' -- La base de datos no puede usar el índice, por el uso de la función select p.Nombre, p.Codigo from proveedor p where substring( p.Nombre, 1, 3 ) = 'VAR' 
  • 32.
    No hacer Orderby cuando se usa la clausula distinct Se hace que el motor de base de datos se esfuerce innecesariamente Select distinct f.IdReciboFactura, F.Numero From finReciboFactura Order by f.IdReciboFactura 
  • 33.
    Evitar en loposible operaciones en las consultas SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f, FinReciboFacturaDetalle fd WHERE f.IdReciboFactura = fd.IdReciboFactura and fd.PrecioUnitario * fd.Cantidad >= 1500 
  • 34.
    Si se debenhacer cálculos en los filtros colocar el cálculo del lado derecho --Usar esto: SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f, FinReciboFacturaDetalle fd WHERE f.IdReciboFactura = fd.IdReciboFactura and fd.PrecioUnitario = ( 1500 * 1.07) --En lugar de: SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f, FinReciboFacturaDetalle fd WHERE f.IdReciboFactura = fd.IdReciboFactura and ( 1500 * 1.07) = fd.PrecioUnitario 
  • 35.
    Union all enlugar de Or Reescribir consultas que tienen OR’s no inclusivos. Evitar en la mayor medida posible el or en las consultas. Reemplazar su uso por Union, preferiblemente union all. El union ordena los registros que se están leyendo, el union all simplemente los une y no altera el orden En lugar de Select * from solicitud_bs s where (s.idestatus = 12 and s.presupuestario = 0) or (s.idestatus = 55 and s.presupuestario = 1) Usar Select * from solicitud_bs s where (s.idestatus = 12 and s.presupuestario = 0) Union all Select * from solicitud_bs s where (s.idestatus = 55 and s.presupuestario = 1) 
  • 36.
  • 37.
    Union y Distinct Recordarque el UNION muestra los registros diferentes de la unión de las consultas, por lo que no es necesario aplicar la operación de distinct. Se recomienda no utilizar operaciones innecesarias ya que consumen tiempo de procesamiento y memoria del servidor. En lugar de Select distinct [IdSolicitudBS], [Numero], [FechaDocumento] from solicitud_bs s where (s.idestatus = 12 and s.presupuestario = 0) Union Select distinct [IdSolicitudBS], [Numero], [FechaDocumento] from solicitud_bs s where (s.idestatus = 55 and s.presupuestario = 1) Usar Select [IdSolicitudBS], [Numero], [FechaDocumento] from solicitud_bs s where (s.idestatus = 12 and s.presupuestario = 0) Union Select [IdSolicitudBS], [Numero], [FechaDocumento] from solicitud_bs s where (s.idestatus = 55 and s.presupuestario = 1) 
  • 38.
    Uso de losalias Si se están usando alias debe colocarse a todas las columnas de la tabla utilizadas en el querie. El no usarlo implica tiempo del motor de base de datos para indagar a que tabla pertenece la columna independientemente de que esta no exista en las otras tablas SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen, TipoFactura FROM finReciboFactura f WHERE f.NumeroDocumentoOrigen in ( '14-00031557') AND NOT EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a WHERE a.IdFactura = f.IdReciboFactura ) AND f.IdReciboFactura = 339320 
  • 39.
    Uso del with(nolock) A este concepto se le conoce como lecturas sucias (dirty reads). Sólo debe utilizarse cuando se esta seguro que los datos a consultar no serán afectados por otras sentencias SQL ejecutadas por otros usuarios o cuando no importe realmente que eso suceda. select s.IdSolicitudBS from solicitud_BS_detalle s with (nolock), (select sum(ppd.Monto) monto, ppd.IdDocumentoTranDet from PreProcesoDetalle ppd where ppd.IdDocumentoTipoPres = 63 and ppd.IdDocumentoTipoTran in (4,5) group by ppd.IdDocumentoTranDet ) p where s.IdSolicitudBSDetalle = p.IdDocumentoTranDet and p.monto <> s.CostoTotal 
  • 40.
    Evitar el SQLDinámico • A menos que sea realmente necesario, debemos evitar su uso por las siguientes razones: • Es difícil de verificar sus errores • Si la entrada del usuario contiene una entrada de usuario existen posibilidades de ataques con Inyecciones de SQL. SELECT COUNT(*) FROM tabla WHERE campo1='loquesea' AND campo2='loquesea' OR user LIKE 'a%';-- 
  • 41.
    Evita llamados innecesarios Sinecesitas llamar repetidamente a una función de SQL o UDF, se debe buscar la posibilidad de almacenar la salida de la misma en variables para evitar ejecutar código innecesario. DECLARE @NombreColaborador varchar(31) select @NombreColaborador = dbo.fx_NombreColaborador(1) Declare @fecha datetime Select @fecha = getdate() 
  • 42.
    Uso de trigger’s •Evitar en la medida de lo posible el uso de trigger’s. • Causan un impacto alto sobre el rendimiento del servidor • No usar trigger’s que se puedan implementar usando constraints • Evitar usar el mismo trigger para distintos eventos (Insert, Update, Delete) • No usar transacciones dentro de los triggers. Estos usan la transacción sobre la que se está ejecutando. --Usar Create Trigger tgr_Algo_I For insert Create trigger tgr_Algo_U For update Create trigger tgr_Algo_U For delete --En lugar de Create trigger tgr_ALGO For insert, update, delete 
  • 43.
    NOT EXISTS paraencontrar datos del padre que no tiene hijos SELECT f.Cedula, f.Anio, f.Peracad, f.NumeroDocumentoOrigen FROM finReciboFactura f WHERE f.NumeroDocumentoOrigen = '14-00031557’ AND NOT EXISTS ( SELECT 1 FROM finReciboFacturaAsociado a WHERE a.IdFactura = f.IdReciboFactura ) 
  • 44.
    Casos especiales Conteo porTotal de Registros USE AdventureWorks; GO SELECT ROW_NUMBER() OVER(ORDER BY s.SalesPersonID ASC) AS ‘NumeroLinea' ,s.SalesPersonID, c.FirstName, c.LastName ,s.SalesYTD, a.PostalCode, c.FirstName, c.LastName FROM Sales.SalesPerson s INNER JOIN Person.Contact c ON s.SalesPersonID = c.ContactID INNER JOIN Person.Address a ON a.AddressID = c.ContactID WHERE TerritoryID IS NOT NULL AND SalesYTD <> 0; GO
  • 45.
    Casos especiales Conteo porgrupo de registros de Registros USE AdventureWorks; GO SELECT ROW_NUMBER() OVER( ORDER BY s.TerritoryID ASC) AS 'NumeroLinea', ROW_NUMBER() OVER( PARTITION BY s.TerritoryID ORDER BY s.TerritoryID ASC) AS 'NumeroLineaT' ,s.TerritoryID,s.SalesYTD, a.PostalCode, c.FirstName, c.LastName FROM Sales.SalesPerson s INNER JOIN Person.Contact c ON s.SalesPersonID = c.ContactID INNER JOIN Person.Address a ON a.AddressID = c.ContactID WHERE TerritoryID IS NOT NULL AND SalesYTD <> 0 ; GO
  • 46.
    Conteo por Totaly por grupo de registros de Registros USE AdventureWorks; GO SELECT ROW_NUMBER() OVER( ORDER BY s.TerritoryID ASC) AS 'NumeroLinea', ROW_NUMBER() OVER( PARTITION BY s.TerritoryID ORDER BY s.TerritoryID ASC) AS 'NumeroLineaT' ,s.TerritoryID,s.SalesYTD, a.PostalCode, c.FirstName, c.LastName FROM Sales.SalesPerson s INNER JOIN Person.Contact c ON s.SalesPersonID = c.ContactID INNER JOIN Person.Address a ON a.AddressID = c.ContactID WHERE TerritoryID IS NOT NULL AND SalesYTD <> 0 ; GO Casos especiales
  • 47.
    Simular un Limitde MySQL USE AdventureWorks; GO declare @Rango1 int, @Rango2 int set @Rango1 = 4; set @Rango2 = 12 SELECT c.row, c.ContactID, c.FirstName, c.LastName FROM ( SELECT pc.ContactID, pc.FirstName, pc.LastName, ROW_NUMBER() OVER (ORDER BY pc.FirstName) as row FROM Person.Contact pc ) as c WHERE row >= @Rango1 and row <= @Rango2 order by c.FirstName Casos especiales
  • 48.
    Casos especiales Simular unLimit de MySQL y Contar los registros USE AdventureWorks; GO declare @Rango1 int, @Rango2 int set @Rango1 = 4; set @Rango2 = 12 SELECT ROW_NUMBER() OVER (ORDER BY c.ContactID) as NumeroLinea, c.row, c.ContactID, c.FirstName, c.LastName FROM ( SELECT pc.ContactID, pc.FirstName, pc.LastName, ROW_NUMBER() OVER (ORDER BY pc.FirstName) as row FROM Person.Contact pc ) as c WHERE row >= @Rango1 and row <= @Rango2 order by c.FirstName
  • 49.
  • 50.
    Casos especiales Recursividad Estructura deuna CTE Recursiva WITH cte_name ( column_name [,...n] ) AS ( CTE_query_definition -- Elemento inicial/Padre/Delimitador UNION ALL CTE_query_definition -- Elemento Hijo/Recursivo ) SELECT ( column_name [,...n] -- Statement using the CTE FROM cte_name
  • 51.
    Casos especiales Recursividad Una condiciónimportante en la consulta recursiva es que debe haber un nodo final select o.opcion_id, o.idOpcionPadre, o.descripcion from eopciones o, eapli_opciones ao, eaplicaciones a where o.opcion_id = ao.opcion_id and ao.aplicacion_id = a.aplicacion_id and o.idOpcionPadre = 0 and a.aplicacion_id = 3 Union select o.opcion_id, o.idOpcionPadre, o.descripcion from eopciones o where o.opcion_id = 0
  • 52.
    Casos especiales Recursividad declare @aplicacion_idint, @IdPadre int ; set @aplicacion_id = 3; set @IdPadre = 0; with JerarquiaOpciones( opcion_id, idOpcionPadre, aplicacion_id, nivel, jerarquia, orden) -- CTE: Common Table Expression as ( select o.opcion_id, o.idOpcionPadre, a.aplicacion_id, 1 as nivel, cast( o.opcion_id as varchar(max)) as jerarquia, ao.orden from eopciones o, eapli_opciones ao, eaplicaciones a where o.opcion_id = ao.opcion_id and ao.aplicacion_id = a.aplicacion_id and o.idOpcionPadre = @IdPadre and a.aplicacion_id = @aplicacion_id union all select o.opcion_id, o.idOpcionPadre, j.aplicacion_id, (nivel + 1 ) as nivel, jerarquia + '' + cast( o.opcion_id as varchar(20) ) as jerarquia, j.orden from eopciones o inner join JerarquiaOpciones j on o.idOpcionPadre = j.opcion_id ) select a.aplicacion_id, a.nombre , o.opcion_id, o.descripcion, j.nivel, j.jerarquia, o.idOpcionPadre, j.orden from eopciones o inner join JerarquiaOpciones j on o.opcion_id = j.opcion_id inner join eaplicaciones a on a.aplicacion_id = j.aplicacion_id order by j.jerarquia, j.orden
  • 53.