dominios y alojamiento web en hostalia

Optimizar consultas SQL

28 de octubre de 2005
Valoración del artículo:
Distintas formas de optimizar las consultas realizadas en SQL.
Atención: Contenido exclusivo de DesarrolloWeb.com. No reproducir. Copyright.
El lenguaje SQL es no procedimental, es decir, en las sentencias se indica que queremos conseguir y no como lo tiene que hacer el interprete para conseguirlo. Esto es pura teoría, pues en la práctica a todos los gestores de SQL hay que especificar sus propios truquitos para optimizar el rendimiento.

Por tanto, muchas veces no basta con especificar una sentencia SQL correcta, sino que además, hay que indicarle como tiene que hacerlo si queremos que el tiempo de respuesta sea el mínimo. En este apartado veremos como mejorar el tiempo de respuesta de nuestro interprete ante unas determinadas situaciones:

Diseño de las tablas

  • Normaliza las tablas, al menos hasta la tercera forma normal, para asegurar que no hay duplicidad de datos y se aprovecha al máximo el almacenamiento en las tablas. Si hay que desnormalizar alguna tabla piensa en la ocupación y en el rendimiento antes de proceder.
  • Los primeros campos de cada tabla deben ser aquellos campos requeridos y dentro de los requeridos primero se definen los de longitud fija y después los de longitud variable.
  • Ajusta al máximo el tamaño de los campos para no desperdiciar espacio.
  • Es muy habitual dejar un campo de texto para observaciones en las tablas. Si este campo se va a utilizar con poca frecuencia o si se ha definido con gran tamaño, por si acaso, es mejor crear una nueva tabla que contenga la clave primaria de la primera y el campo para observaciones.
Gestión y elección de los índices

Los índices son campos elegidos arbitrariamente por el constructor de la base de datos que permiten la búsqueda a partir de dicho campo a una velocidad notablemente superior. Sin embargo, esta ventaja se ve contrarrestada por el hecho de ocupar mucha más memoria (el doble más o menos) y de requerir para su inserción y actualización un tiempo de proceso superior.

Evidentemente, no podemos indexar todos los campos de una tabla extensa ya que doblamos el tamaño de la base de datos. Igualmente, tampoco sirve de mucho el indexar todos los campos en una tabla pequeña ya que las selecciones pueden efectuarse rápidamente de todos modos.

Un caso en el que los índices pueden resultar muy útiles es cuando realizamos peticiones simultáneas sobre varias tablas. En este caso, el proceso de selección puede acelerarse sensiblemente si indexamos los campos que sirven de nexo entre las dos tablas.

Los índices pueden resultar contraproducentes si los introducimos sobre campos triviales a partir de los cuales no se realiza ningún tipo de petición ya que, además del problema de memoria ya mencionado, estamos ralentizando otras tareas de la base de datos como son la edición, inserción y borrado. Es por ello que vale la pena pensárselo dos veces antes de indexar un campo que no sirve de criterio para búsquedas o que es usado con muy poca frecuencia por razones de mantenimiento.

Campos a Seleccionar

  • En la medida de lo posible hay que evitar que las sentencias SQL estén embebidas dentro del código de la aplicación. Es mucho más eficaz usar vistas o procedimientos almacenados por que el gestor los guarda compilados. Si se trata de una sentencia embebida el gestor debe compilarla antes de ejecutarla.
  • Seleccionar exclusivamente aquellos que se necesiten
  • No utilizar nunca SELECT * por que el gestor debe leer primero la estructura de la tabla antes de ejecutar la sentencia
  • Si utilizas varias tablas en la consulta especifica siempre a que tabla pertenece cada campo, le ahorras al gestor el tiempo de localizar a que tabla pertenece el campo. En lugar de SELECT Nombre, Factura FROM Clientes, Facturacion WHERE IdCliente = IdClienteFacturado, usa: SELECT Clientes.Nombre, Facturacion.Factura WHERE Clientes.IdCliente = Facturacion.IdClienteFacturado.
Campos de Filtro

  • Se procurará elegir en la cláusula WHERE aquellos campos que formen parte de la clave del fichero por el cual interrogamos. Además se especificarán en el mismo orden en el que estén definidos en la clave.
  • Interrogar siempre por campos que sean clave.
  • Si deseamos interrogar por campos pertenecientes a índices compuestos es mejor utilizar todos los campos de todos los índices. Supongamos que tenemos un índice formado por el campo NOMBRE y el campo APELLIDO y otro índice formado por el campo EDAD. La sentencia WHERE NOMBRE='Juan' AND APELLIDO Like '%' AND EDAD = 20 sería más optima que WHERE NOMBRE = 'Juan' AND EDAD = 20 por que el gestor, en este segundo caso, no puede usar el primer índice y ambas sentencias son equivalentes por que la condición APELLIDO Like '%' devolvería todos los registros.
Orden de las Tablas

Cuando se utilizan varias tablas dentro de la consulta hay que tener cuidado con el orden empleado en la cláusula FROM. Si deseamos saber cuantos alumnos se matricularon en el año 1996 y escribimos: FROM Alumnos, Matriculas WHERE Alumno.IdAlumno = Matriculas.IdAlumno AND Matriculas.Año = 1996 el gestor recorrerá todos los alumnos para buscar sus matriculas y devolver las correspondientes. Si escribimos FROM Matriculas, Alumnos WHERE Matriculas.Año = 1996 AND Matriculas.IdAlumno = Alumnos.IdAlumnos, el gestor filtra las matrículas y después selecciona los alumnos, de esta forma tiene que recorrer menos registros.

Compartir en redes sociales

Comentarios
Fueron enviados 23 comentarios al artículo
10 comentarios no revisados
13 comentarios revisados:
Por: Daniel
04/11/2005
Me parece interante tu artículo, pero hay que recordar lo siguiente para cuando manejas gran volúmen de datos:

1. Es preferible realizar las relaciones entra tablas usando INNER JOIN, LEFT JOIN, RIGHT JOIN ... y no realizar la relación con la cláusula WHERE.
2. No se recomienda usar cláusula muy frecuentes como LIKE
3. Utilizar lo menos posible ANY, SOME, EXISTS, IN (SELECT Campo1 From Tabla2)
4. Utilizar FILEGROUPS, lo q permite asignar una base de datos, tabla, indices o determinados campos TEXT o IMAGES hacia diferentes archivos, lo cual permite una excelente aceleración.
5. Colocar las bases de datos en diferentes discos duros
6. Configurar los campos con la misma intercalación.
5. No utilizar cursores!!!!!
6. No utilizar tablas temporales con # o ##
7. Utilizar para poco volumen de datos en lugar de cursores las llamadas variables tipo tablas: DECLARE T1 TABLE (CAMPO1 VARCHAR(1))
8. Utilizar vistas en lugar de consultas gigantes en tus StoreProc. o mezclar el uso de estas, porque recordemos que las vistas son ya pre-compiladas.

Saludos
Daniel Yafac
Lima - Perú

Por: Antonio
04/11/2005
A veces, sobre todo para tablas cuya especifación acerca de los campos puede variar, como clientes, etc. aporta ventajas crear tablas con datos genericos. Por ejemplo, podemos crear una tabla de valores ENTEROS (integer), con un valor que puede ser un char(2) para identificar grupos de valores y dos campos mas que nos indiquen un valor ajeno de clave primaria y otro de valor de dato. Viola casi todas las normas de diseño, pero personalmente he de deciros que en algunos casos es muy práctico.
Por ejemplo.
Create table params(param char(3), descrip char(30);
insert into params values('ES','Idioma español');
Create table valores(param char(2), valor integer, valor2 integer)


Por: Antonio
04/11/2005
Tener en cuenta también que la creación de indices puede ser ASC (ascendente) o DESC (descendente). Que el orden de los campos a la hora de crear el índice es importante a la hora de hacer la consulta (mismo orden + rápido).
Evitar las creación de tablas temporales con LOG para los sistemas Informix p.e.
Por: Juan Pablo Touron
03/12/2007
todo bien, pero ojo, esto solo funciona en ADO 2.8 para arriba o en SQL server 2000.

Para usar el LIKE en ADO 2.7 para abajo deben cambiarse los comodines '%' por los comodines '*' (asteriscos). De no hacerlo el LIKE funcionara buscando un STRING que contenga el %

Ejemplos:

BUSCA TODO LO Q TENGA H EN Mi_Campo en ADO <= 2.7
SELECT * FROM Mi_Tabla WHERE Mi_Campo LIKE '*H*'

BUSCA TODO LO Q TENGA H EN Mi_Campo en ADO >= 2.8 y SQL SERVER
SELECT * FROM Mi_Tabla WHERE Mi_Campo LIKE '%H%'

Espero les sea util, estos cambios estan muy mal documentados en las referencias de MSDN y demas, y realemente rompen mucho la paciencia... NUNCA SUPE POR QUE HACEN ESTAS COSAS...
Por: Aptiliux
03/3/2008
lo primero, si la consulta que se está haciendo es realmente lenta, es desglosarla para ver en que parte de la consulta se encuentra la mayor lentitud, luego de identificarlo, probar si con un índice optimizas el tiempo de respuesta, si a mejorado pero la diferencia es muy poca, puedes utilizar consultas anidadas para definir el orden en el que el manejador realizaría la consulta, los niveles mas profundos se ejecutan primero, entonces debemos comenzar a limitar el volumen de data en estos niveles, e intentar dejar la consulta mas lenta para el nivel mas alto, pues en teoría debería haber menos data en esos niveles. Un modelo anidado de selects me funcionó en una consulta larga que la base de comparación eran fecha. No me gusta mucho utilizar joins pues la consulta es menos legible, pero es bueno saber que es mas rápido.
Por: Rodrigo
03/4/2009
Ojo, muchos se pueden tentar con crear índices para acelerar sus consultas, pero estas no tienen ninguna validez si el campo indizado se consulta a través de alguna función SQL... En este caso se debe crear el indice incorporando la función.
Tunning
Por: jrd
27/10/2009
La mejor forma de hacer tunning es revisando el manual de oracle donde puedes encontrar variedad de ejmplos y no comentarios confusos y muy pero muy basicos.
Otros errores comunes
Por: sendyourquery
12/1/2010
Hola.

Muy bueno el artículo.

Comentar algunos errores comunes observados en numerosas instalaciones:
1) En uniones de consultas es mejor el uso de UNION ALL en vez de UNION si ambas subconsultas no pueden devolver datos repetidos.
2) Las LEFT JOIN y RIGHT JOIN son en realidad OUTER JOINs a pesar de que no se explicite la palabra OUTER.
3) En el uso de índices no es necesario utilizar todos los campos del mismo pero no es óptimo utilizar un campo si no se han informado los campos previos por igualdad .

Hemos creado una página web para la optimización de querys con problemas de rendimiento:

www.sendyourquery.com

Un saludo.
error de redacción
Por: julian
11/2/2010
Estimados amigos, econtre un error de redacción en el articulo optimizar consultas SQL
TEXTO ORIGINAL
En lugar de SELECT Nombre, Factura FROM Clientes, Facturacion WHERE IdCliente = IdClienteFacturado, usa: SELECT Clientes.Nombre, Facturacion.Factura WHERE Clientes.IdCliente = Facturacion.IdClienteFacturado.

TEXTO CORRECTO
En lugar de SELECT Nombre, Factura FROM Clientes, Facturacion WHERE IdCliente = IdClienteFacturado, usa: SELECT Clientes.Nombre, Facturacion.Factura FROM Clientes, Facturacion WHERE Clientes.IdCliente = Facturacion.IdClienteFacturado.
Mi SP Tiene Rendimiento?
Por: Jesus
15/4/2010
esta es la estructura de mis SP en SQL:


set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
/*----------------------------------------------------------------
PROPOSITO : Permite Listar Ordenes de Compra Trenes Peru Rail
AUTOR : Jesus Echevarria
FECHA : 15/MAR/10
INPUTS :
OUTPUTS : Lista de Ordenes de Compra Trenes Peru Rail
------------------------------------------------------------------
EXEC P_OrdenCompraTrenListar Null,Null,Null,Null,Null,Null,Null,Null,Null,Null,'A'
SELECT * FROM OrdenCompraTren
----------------------------------------------------------------*/
ALTER Procedure [dbo].[P_OrdenCompraTrenListar]
@IDOrdenCompraTren int=null,
@IDOrdenCompra Int=Null,
@IDServicioTren int=null,
@IDFile Int=null,
@FechaEmisionIni Datetime = NULL,
@FechaEmisionFin Datetime = NULL,
@CodigoReservaTren varchar(100)=Null,
@NroOpeDep varChar(100)=Null,
@EstadoTipSer Char(1)=Null,
@IDTipoMoneda int=null,
@EstadoDetTren char(1)=Null
As
Select IDOrdenCompraTren,
OCT.IDOrdenCompra,
OCT.IDServicioTren,
ST.DescripcionSerTren,
OCT.IDFile,
Isnull(FC.NomenclaturaFile,'')as NomenclaturaFile,
GrupoPaxTren,
Isnull(PaxIda,0)as PaxIda,
Isnull(NinoIda,0)as NinoIda,
Isnull(GuiaIda,0)as GuiaIda,
Isnull(MasajistaIda,0)as MasajistaIda,
Isnull(OtrosIda,0)as OtrosIda,
Isnull(PaxVuelta,0)as PaxVuelta,
Isnull(NinoVuelta,0)as NinoVuelta,
Isnull(GuiaVuelta,0)as GuiaVuelta,
Isnull(MasajistaVuelta,0)as MasajistaVuelta,
Isnull(OtrosVuelta,0)as OtrosVuelta,
Isnull(PrePaxIda,0)as PrePaxIda,
Isnull(PreNinIda,0)as PreNinIda,
Isnull(PreGuiIda,0)as PreGuiIda,
Isnull(PreMasIda,0)as PreMasIda,
Isnull(PreOtroIda,0)as PreOtroIda,
Isnull(PrePaxVue,0)as PrePaxVue,
Isnull(PreNinVue,0)as PreNinVue,
Isnull(PreGuiVue,0)as PreGuiVue,
Isnull(PreMasVue,0)as PreMasVue,
Isnull(PreOtroVue,0)as PreOtroVue,
TotalTren,
ISNULL(CodigoReservaTren,'')as CodigoReservaTren,
ISNULL(NroOpeDep,'')as NroOpeDep,
SaldoTotal,
Case ISNULL(EstadoTipSer,'')
When 'G' then 'Generado'
When 'A' then 'Aprobado'
End as EstadoTipSer,
isNull(DBO.FU_Tren_ID(IDTrenIda),'')as TrenIda,
isNull(DBO.FU_Tren_ID(IDTrenvuelta),'')as TrenVuelta,
OC.IDTipoMoneda,
IsNull(TM.TipoMonedaDescripcion,'')as TipoMonedaDescripcion,
Isnull(Convert(char(10),OC.FechaEmision,103),'')as FechaEmision,
Isnull(Convert(char(10),OC.FechaEntrega,103),'')as FechaEntrega,
isNull(OC.ObsOrdenCompra,'')as ObsOrdenCompra,
U.IDUsuario,
Isnull(U.UsuarioNombres+' '+UsuarioApellidoPaterno,'')as [Usuario],
Case Isnull(OC.EstadoOrdenCompra,'')
When 'A' then 'Activo'
When 'I' then 'Inactivo'
End as EstadoOrdenCompra
From OrdenCompraTren OCT Inner Join OrdenCompra OC on(OCT.IDOrdenCompra=OC.IDOrdenCompra)
Inner Join ServicioTren ST on(OCT.IDServicioTren=ST.IDServicioTren)
Inner Join FileCabecera FC on(OCT.IDFile=FC.IDFile)
Left Outer Join TipoMoneda TM on(OC.IDTipoMoneda=TM.IDTipoMoneda)
Left Outer Join Usuario U on(OC.IDUsuario=U.IDUsuario)
Where
(OCT.IDOrdenCompraTren=@IDOrdenCompraTren or @IDOrdenCompraTren IS NULL)
And (OCT.IDOrdenCompra=@IDOrdenCompra or @IDOrdenCompra IS NULL)
And (OCT.IDServicioTren=4)
And (OCT.IDFile=@IDFile or @IDFile IS NULL)
And (CONVERT(Smalldatetime,CONVERT(Varchar,OC.FechaEmision,103),103)>=CONVERT(Smalldatetime,CONVERT(Varchar,@FechaEmisionIni,103),103) or CONVERT(Smalldatetime,CONVERT(Varchar,@FechaEmisionIni,103),103) IS NULL)
And (CONVERT(Smalldatetime,CONVERT(Varchar,OC.FechaEmision,103),103)<=CONVERT(Smalldatetime,CONVERT(Varchar,@FechaEmisionFin,103),103) or CONVERT(Smalldatetime,CONVERT(Varchar,@FechaEmisionFin,103),103) IS NULL)
And (OCT.CodigoReservaTren=@CodigoReservaTren or @CodigoReservaTren IS NULL)
And (OCT.NroOpeDep=@NroOpeDep or @NroOpeDep IS NULL)
And (OC.IdTipoMoneda=@IDTipoMoneda or @IDTipoMoneda IS NULL)
And (OCT.EstadoTipSer=@EstadoTipSer or @EstadoTipSer is null)
And (OCT.EstadoDetTren=@EstadoDetTren or @EstadoDetTren is null)
Manejo del sql
Por: Ricardo Castro
19/4/2010
Deseo saber Cual es la forma correcta del uso de la sintaxis, cuando se desea buscar dentro de un grupo de registros indexados que estan ordenados por numeros o matriculas y deseamos ver los que comiencen con el numero 5 y que solo se muestren estos.. Sabemos que para buscar un nombre podemos usar %pepe% o *pepe*
Buscando un registro en la base de datos Sql
Por: Ricardo Castro
19/4/2010
Yo se que para buscar un nombre especifico dentro de un grupo de registro usando el Sql se puede lograr usando la sentencia Like acompañada del signo de % o el signo de * dependiendo de la version de la Sql. Mi pregunta es que sentencia debo usar cuando se trata de buscar dentro de un grupo de numero a un numero especifico. en el modo de buscar nombre se utiliza Like %p%, cual es la forma correcta para buscar un numero especifico dentro de una base de datos grande
Excelente artículo... excelentes comentarios
Por: Golveron
20/4/2010
Señores, agradecido... estaba con un problema de una consulta que involucraba varias tablas. No hice el diseño de la BD yo lamentablemente, pero leyendo éste artículo y gracias a algunos conocimientos anteriores, revisé la bd y me di cuenta que los algunos campos relacionados a la consulta no estaban indexados. Luego de indexarlos reduje el tiempo de espera de una forma suficientemente considerable.
Gracias y sigan subiendo éstos artículos tan útiles.

Les saluda cordialmente,

Golveron.
josbolivar@gmail.com

Manuales relacionados
Categorias relacionadas
El autor
Últimas noticias
Donaciones
Si piensas que te hemos ayudado y merecemos tu apoyo económico...