Sugerencias para mejorar la SELECT de esta consulta.

07/09/2006 - 21:08 por Salvador Ramos | Informe spam
Hola a todos,

Estoy revisando con profiler una serie de ejecuciones y he visto esta
consulta que se ejecuta con bastante frecuencia, quería consultaros si se os
ocurre alguna forma de optimizarla (a parte de poner índices y demás que eso
ya lo tengo), es decir, obtener otra instrucción select que ofrezca los
mismos resultados y tenga mejor rendimiento.

Primero os explico la estructura de datos, intervienen tres tablas:
Clientes: Cliente, nombre, ... (Cliente es PK de Clientes)
Tarjetas: Cliente, Tarjeta, ... (Cliente es FK - relación uno a muchos con
Clientes, Tarjeta es PK de Tarjetas)
Servicios: Id, Tarjeta, Importe, ... (Id es PK de Servicios, Tarjeta es
FK -relación uno a muchos con Tarjetas)

select sum(importe) as sumaP
from servicios where fecha >= '20060709'
and tarjeta in (select tarjeta
from tarjetas
where cliente = (select cliente
from tarjetas
where tarjeta = 'ABCD' ))

Necesito que la consulta me devuelva la suma de los importes de todas las
tarjetas del cliente, por eso la select de nivel inferior obtiene el cliente
al que pertenece esa tarjeta, la del siguiente nivel obtiene la relación de
tarjetas de ese cliente, y ya la de nivel superior suma los importes de
todas esas tarjetas.

He intentado mejorarla, pero no lo he conseguido, y es una consulta que se
ejecuta con bastante frecuencia. Igual aún no he conectado 100% tras la
vuelta de vacaciones :-)

Muchas gracias

Un saludo
Salvador Ramos
Murcia - España

[Microsoft MVP SQL Server]
www.helpdna.net (información sobre SQL Server y .NET)

Preguntas similare

Leer las respuestas

#1 jcac
07/09/2006 - 22:04 | Informe spam
Hola,

Creo que podrías utilizar exists en lugar de in, te quedaría de esta manera
mas o menos

select sum(s.importe) as sumaP
from servicios s where s.fecha >= '20060709'
and exists (select tarjeta
from tarjetas
where tarjeta = s.tarjeta and
cliente = (select cliente
from tarjetas
where tarjeta = 'ABCD' ))

Espero sea más rápido

Saludos


"Salvador Ramos" escribió en el
mensaje news:uQ%
Hola a todos,

Estoy revisando con profiler una serie de ejecuciones y he visto esta
consulta que se ejecuta con bastante frecuencia, quería consultaros si se
os ocurre alguna forma de optimizarla (a parte de poner índices y demás
que eso ya lo tengo), es decir, obtener otra instrucción select que
ofrezca los mismos resultados y tenga mejor rendimiento.

Primero os explico la estructura de datos, intervienen tres tablas:
Clientes: Cliente, nombre, ... (Cliente es PK de Clientes)
Tarjetas: Cliente, Tarjeta, ... (Cliente es FK - relación uno a muchos con
Clientes, Tarjeta es PK de Tarjetas)
Servicios: Id, Tarjeta, Importe, ... (Id es PK de Servicios, Tarjeta es
FK -relación uno a muchos con Tarjetas)

select sum(importe) as sumaP
from servicios where fecha >= '20060709'
and tarjeta in (select tarjeta
from tarjetas
where cliente = (select cliente
from tarjetas
where tarjeta = 'ABCD' ))

Necesito que la consulta me devuelva la suma de los importes de todas las
tarjetas del cliente, por eso la select de nivel inferior obtiene el
cliente al que pertenece esa tarjeta, la del siguiente nivel obtiene la
relación de tarjetas de ese cliente, y ya la de nivel superior suma los
importes de todas esas tarjetas.

He intentado mejorarla, pero no lo he conseguido, y es una consulta que se
ejecuta con bastante frecuencia. Igual aún no he conectado 100% tras la
vuelta de vacaciones :-)

Muchas gracias

Un saludo
Salvador Ramos
Murcia - España

[Microsoft MVP SQL Server]
www.helpdna.net (información sobre SQL Server y .NET)



Respuesta Responder a este mensaje
#2 Microsoft
07/09/2006 - 22:11 | Informe spam
pues yo cambiaria los IN por JOIN...

algo como

select sum(importe) as sumaP
from servicios S INNER JOIN (select tarjeta
from tarjetas
INNER JOIN (select cliente CL
from tarjetas
where tarjeta = 'ABCD' ) D
on D.cliente = CL.Cliente) C
on S.Tarjeta = C.Tarjeta
where fecha >= '20060709'

Saludos desde Colombia...

Fredy (Co)


"Salvador Ramos" escribió en el
mensaje news:uQ%
Hola a todos,

Estoy revisando con profiler una serie de ejecuciones y he visto esta
consulta que se ejecuta con bastante frecuencia, quería consultaros si se
os ocurre alguna forma de optimizarla (a parte de poner índices y demás
que eso ya lo tengo), es decir, obtener otra instrucción select que
ofrezca los mismos resultados y tenga mejor rendimiento.

Primero os explico la estructura de datos, intervienen tres tablas:
Clientes: Cliente, nombre, ... (Cliente es PK de Clientes)
Tarjetas: Cliente, Tarjeta, ... (Cliente es FK - relación uno a muchos con
Clientes, Tarjeta es PK de Tarjetas)
Servicios: Id, Tarjeta, Importe, ... (Id es PK de Servicios, Tarjeta es
FK -relación uno a muchos con Tarjetas)

select sum(importe) as sumaP
from servicios where fecha >= '20060709'
and tarjeta in (select tarjeta
from tarjetas
where cliente = (select cliente
from tarjetas
where tarjeta = 'ABCD' ))

Necesito que la consulta me devuelva la suma de los importes de todas las
tarjetas del cliente, por eso la select de nivel inferior obtiene el
cliente al que pertenece esa tarjeta, la del siguiente nivel obtiene la
relación de tarjetas de ese cliente, y ya la de nivel superior suma los
importes de todas esas tarjetas.

He intentado mejorarla, pero no lo he conseguido, y es una consulta que se
ejecuta con bastante frecuencia. Igual aún no he conectado 100% tras la
vuelta de vacaciones :-)

Muchas gracias

Un saludo
Salvador Ramos
Murcia - España

[Microsoft MVP SQL Server]
www.helpdna.net (información sobre SQL Server y .NET)



Respuesta Responder a este mensaje
#3 Alejandro Mesa
07/09/2006 - 22:53 | Informe spam
Salvador,

Bienvenido de vuelta al grupo. Veo que aun no te recuperas de esas vacaciones.

Mi pregunta es como se ejecuta esa consulta, osea, desde un sp, o envias la
consulta desde la aplicacion cliente, etc. Te lo pregunto por que quiero
saber si usas un parametro para pasar el valor de la tarjeta. Me parece, como
sugirio Fredy, que el uso de joins vendria bien, aunque como bien sabes,
debes compara los planes de ejecucion y estadisticas de tiempo de ambas
sugerencias. Que tal si usamos:

select
sum(importe) as sumaP
from
dbo.servicios as s
inner join
dbo.tarjetas as t
on s.tarjeta = t.tarjeta
where
s.fecha >= '20060709'
and t.cliente = (
select t1.cliente
from dbo.tarjetas as t1
where t1.tarjeta = @tarjeta
)
go

Espero estes usando el dueño de la tabla cuando las referencias en la
consulta, recuerda que eso es muy importante para que el plan de ejecucion
compilado pueda ser compartido por multiples usuarios. Tambien recalco que el
valor de la tarjeta se pase como parametro, de esa forma el plan de ejecucion
puede ser reusado, ya que el valor hash con el que este se identifica, es
calculado en base al texto de la consulta, por lo que la misma consulta con
diferentes valores de tarjeta resultarian en diferentes planes de ejecucion.
Como ultimo, no uses una variable intermedia para traer el valor de la
columna [cliente] y luego usar esta variable en la consulta, pues entonces
SQL Server, al no saber el valor de la variable en tiempo de compilacion,
usara la densidad del indice, en vez de las estadisticas, para estimar las
filas que machan la condicion de busqueda.


Saludos,

Alejandro Mesa

"Salvador Ramos" wrote:

Hola a todos,

Estoy revisando con profiler una serie de ejecuciones y he visto esta
consulta que se ejecuta con bastante frecuencia, quería consultaros si se os
ocurre alguna forma de optimizarla (a parte de poner índices y demás que eso
ya lo tengo), es decir, obtener otra instrucción select que ofrezca los
mismos resultados y tenga mejor rendimiento.

Primero os explico la estructura de datos, intervienen tres tablas:
Clientes: Cliente, nombre, ... (Cliente es PK de Clientes)
Tarjetas: Cliente, Tarjeta, ... (Cliente es FK - relación uno a muchos con
Clientes, Tarjeta es PK de Tarjetas)
Servicios: Id, Tarjeta, Importe, ... (Id es PK de Servicios, Tarjeta es
FK -relación uno a muchos con Tarjetas)

select sum(importe) as sumaP
from servicios where fecha >= '20060709'
and tarjeta in (select tarjeta
from tarjetas
where cliente = (select cliente
from tarjetas
where tarjeta = 'ABCD' ))

Necesito que la consulta me devuelva la suma de los importes de todas las
tarjetas del cliente, por eso la select de nivel inferior obtiene el cliente
al que pertenece esa tarjeta, la del siguiente nivel obtiene la relación de
tarjetas de ese cliente, y ya la de nivel superior suma los importes de
todas esas tarjetas.

He intentado mejorarla, pero no lo he conseguido, y es una consulta que se
ejecuta con bastante frecuencia. Igual aún no he conectado 100% tras la
vuelta de vacaciones :-)

Muchas gracias

Un saludo
Salvador Ramos
Murcia - España

[Microsoft MVP SQL Server]
www.helpdna.net (información sobre SQL Server y .NET)




Respuesta Responder a este mensaje
#4 Alejandro Mesa
07/09/2006 - 23:03 | Informe spam
Salvador,

Me pregunto cual columna estas usando en la tabla [servicios] para el indice
clustered?. Si esta consulta, con digistes ante, es usada con frecuencia,
entnnces usar la columna [fecha] para el indice clustered ayudaria muchisimo,
ya que estas usando una comparacion abierta contra esta columna.

...
where s.fecha >= '20060709'
...

En la descripcion de las tablas, aparece la columna [Id] como clave
primaria, pero no especificas si estas usando [clustered] para la clave
primaria por defecto.


AMB

"Alejandro Mesa" wrote:

Salvador,

Bienvenido de vuelta al grupo. Veo que aun no te recuperas de esas vacaciones.

Mi pregunta es como se ejecuta esa consulta, osea, desde un sp, o envias la
consulta desde la aplicacion cliente, etc. Te lo pregunto por que quiero
saber si usas un parametro para pasar el valor de la tarjeta. Me parece, como
sugirio Fredy, que el uso de joins vendria bien, aunque como bien sabes,
debes compara los planes de ejecucion y estadisticas de tiempo de ambas
sugerencias. Que tal si usamos:

select
sum(importe) as sumaP
from
dbo.servicios as s
inner join
dbo.tarjetas as t
on s.tarjeta = t.tarjeta
where
s.fecha >= '20060709'
and t.cliente = (
select t1.cliente
from dbo.tarjetas as t1
where t1.tarjeta = @tarjeta
)
go

Espero estes usando el dueño de la tabla cuando las referencias en la
consulta, recuerda que eso es muy importante para que el plan de ejecucion
compilado pueda ser compartido por multiples usuarios. Tambien recalco que el
valor de la tarjeta se pase como parametro, de esa forma el plan de ejecucion
puede ser reusado, ya que el valor hash con el que este se identifica, es
calculado en base al texto de la consulta, por lo que la misma consulta con
diferentes valores de tarjeta resultarian en diferentes planes de ejecucion.
Como ultimo, no uses una variable intermedia para traer el valor de la
columna [cliente] y luego usar esta variable en la consulta, pues entonces
SQL Server, al no saber el valor de la variable en tiempo de compilacion,
usara la densidad del indice, en vez de las estadisticas, para estimar las
filas que machan la condicion de busqueda.


Saludos,

Alejandro Mesa

"Salvador Ramos" wrote:

> Hola a todos,
>
> Estoy revisando con profiler una serie de ejecuciones y he visto esta
> consulta que se ejecuta con bastante frecuencia, quería consultaros si se os
> ocurre alguna forma de optimizarla (a parte de poner índices y demás que eso
> ya lo tengo), es decir, obtener otra instrucción select que ofrezca los
> mismos resultados y tenga mejor rendimiento.
>
> Primero os explico la estructura de datos, intervienen tres tablas:
> Clientes: Cliente, nombre, ... (Cliente es PK de Clientes)
> Tarjetas: Cliente, Tarjeta, ... (Cliente es FK - relación uno a muchos con
> Clientes, Tarjeta es PK de Tarjetas)
> Servicios: Id, Tarjeta, Importe, ... (Id es PK de Servicios, Tarjeta es
> FK -relación uno a muchos con Tarjetas)
>
> select sum(importe) as sumaP
> from servicios where fecha >= '20060709'
> and tarjeta in (select tarjeta
> from tarjetas
> where cliente = (select cliente
> from tarjetas
> where tarjeta = 'ABCD' ))
>
> Necesito que la consulta me devuelva la suma de los importes de todas las
> tarjetas del cliente, por eso la select de nivel inferior obtiene el cliente
> al que pertenece esa tarjeta, la del siguiente nivel obtiene la relación de
> tarjetas de ese cliente, y ya la de nivel superior suma los importes de
> todas esas tarjetas.
>
> He intentado mejorarla, pero no lo he conseguido, y es una consulta que se
> ejecuta con bastante frecuencia. Igual aún no he conectado 100% tras la
> vuelta de vacaciones :-)
>
> Muchas gracias
>
> Un saludo
> Salvador Ramos
> Murcia - España
>
> [Microsoft MVP SQL Server]
> www.helpdna.net (información sobre SQL Server y .NET)
>
>
>
>
Respuesta Responder a este mensaje
#5 Salvador Ramos
08/09/2006 - 12:29 | Informe spam
Muchas gracias,

Me había obcecado en sacarlo con join entre las tres tablas, ha sido de esas
veces que no ves algo claro y que por más que pruebas te acabas liando. La
solución era un término medio una join y una subselect :-)

Comparto con vosotros que con las joins se obtiene mejor rendimiento.

La consulta se ejecuta desde la aplicación cliente, y ante eso no puedo
hacer nada, ya que la aplicación no es mía, y no puedo pedir ese tipo de
modificaciones a mi proveedor, me tendré que conformar con que me cambie la
instrución SELECT (pero nada de que utilice procedmientos almacenados, es
una pena), y esto lo hará porque había un fallo de cálculos en ella que he
detectado, y luego seguí la ejecución con profiler (la condición where la
hacía por fecha > y tenía que ser >=).

En cuanto al resto de temas sobre índices apropiados, clustered, non
clustered etc. Tengo que hacer una revisión de la base de datos, ya que creo
que la han creado con el Enterprise Manager sin preocuparse de nada, hay un
índice clustered por cada PK, además lo poco que visto que han hecho
adicional, tiene muy mala pinta. En la tabla clientes, además de hacer un
índice clustered por la PK (columna cliente), tiene otro indice non
clustered por esa misma columna. En la tabla servicios tiene un índice
clustered por Id (que es la PK), y además un índice non-clustered por Id,
Tarjeta, Fecha, lo cual no tiene ningún sentido.
En fin, que me toca pegar un repaso a fondo a todo esto.

Un saludo
Salvador Ramos
Murcia - España

[Microsoft MVP SQL Server]
www.helpdna.net (información sobre SQL Server y .NET)


"Alejandro Mesa" escribió en el
mensaje news:
Salvador,

Me pregunto cual columna estas usando en la tabla [servicios] para el
indice
clustered?. Si esta consulta, con digistes ante, es usada con frecuencia,
entnnces usar la columna [fecha] para el indice clustered ayudaria
muchisimo,
ya que estas usando una comparacion abierta contra esta columna.

...
where s.fecha >= '20060709'
...

En la descripcion de las tablas, aparece la columna [Id] como clave
primaria, pero no especificas si estas usando [clustered] para la clave
primaria por defecto.


AMB

"Alejandro Mesa" wrote:

Salvador,

Bienvenido de vuelta al grupo. Veo que aun no te recuperas de esas
vacaciones.

Mi pregunta es como se ejecuta esa consulta, osea, desde un sp, o envias
la
consulta desde la aplicacion cliente, etc. Te lo pregunto por que quiero
saber si usas un parametro para pasar el valor de la tarjeta. Me parece,
como
sugirio Fredy, que el uso de joins vendria bien, aunque como bien sabes,
debes compara los planes de ejecucion y estadisticas de tiempo de ambas
sugerencias. Que tal si usamos:

select
sum(importe) as sumaP
from
dbo.servicios as s
inner join
dbo.tarjetas as t
on s.tarjeta = t.tarjeta
where
s.fecha >= '20060709'
and t.cliente = (
select t1.cliente
from dbo.tarjetas as t1
where t1.tarjeta = @tarjeta
)
go

Espero estes usando el dueño de la tabla cuando las referencias en la
consulta, recuerda que eso es muy importante para que el plan de
ejecucion
compilado pueda ser compartido por multiples usuarios. Tambien recalco
que el
valor de la tarjeta se pase como parametro, de esa forma el plan de
ejecucion
puede ser reusado, ya que el valor hash con el que este se identifica, es
calculado en base al texto de la consulta, por lo que la misma consulta
con
diferentes valores de tarjeta resultarian en diferentes planes de
ejecucion.
Como ultimo, no uses una variable intermedia para traer el valor de la
columna [cliente] y luego usar esta variable en la consulta, pues
entonces
SQL Server, al no saber el valor de la variable en tiempo de compilacion,
usara la densidad del indice, en vez de las estadisticas, para estimar
las
filas que machan la condicion de busqueda.


Saludos,

Alejandro Mesa

"Salvador Ramos" wrote:

> Hola a todos,
>
> Estoy revisando con profiler una serie de ejecuciones y he visto esta
> consulta que se ejecuta con bastante frecuencia, quería consultaros si
> se os
> ocurre alguna forma de optimizarla (a parte de poner índices y demás
> que eso
> ya lo tengo), es decir, obtener otra instrucción select que ofrezca los
> mismos resultados y tenga mejor rendimiento.
>
> Primero os explico la estructura de datos, intervienen tres tablas:
> Clientes: Cliente, nombre, ... (Cliente es PK de Clientes)
> Tarjetas: Cliente, Tarjeta, ... (Cliente es FK - relación uno a muchos
> con
> Clientes, Tarjeta es PK de Tarjetas)
> Servicios: Id, Tarjeta, Importe, ... (Id es PK de Servicios, Tarjeta es
> FK -relación uno a muchos con Tarjetas)
>
> select sum(importe) as sumaP
> from servicios where fecha >= '20060709'
> and tarjeta in (select tarjeta
> from tarjetas
> where cliente = (select cliente
> from tarjetas
> where tarjeta = 'ABCD' ))
>
> Necesito que la consulta me devuelva la suma de los importes de todas
> las
> tarjetas del cliente, por eso la select de nivel inferior obtiene el
> cliente
> al que pertenece esa tarjeta, la del siguiente nivel obtiene la
> relación de
> tarjetas de ese cliente, y ya la de nivel superior suma los importes de
> todas esas tarjetas.
>
> He intentado mejorarla, pero no lo he conseguido, y es una consulta que
> se
> ejecuta con bastante frecuencia. Igual aún no he conectado 100% tras la
> vuelta de vacaciones :-)
>
> Muchas gracias
>
> Un saludo
> Salvador Ramos
> Murcia - España
>
> [Microsoft MVP SQL Server]
> www.helpdna.net (información sobre SQL Server y .NET)
>
>
>
>
Respuesta Responder a este mensaje
Ads by Google
Help Hacer una preguntaSiguiente Respuesta Tengo una respuesta
Search Busqueda sugerida