Pregunta

Actualmente estoy trabajando en una especial complejidad de casos de uso. Simplificar a continuación:)

En primer lugar, un registro cliente tiene una relación muchos-a-uno con un conjunto de servicios, es decir, un solo cliente puede tener múltiples servicios asociados a ella.

Dentro de mi gatillo, estoy escribiendo una consulta que devuelve el ID de un cliente en función de ciertos criterios. Los criterios son los siguientes,

  1. Si al menos un servicio es de tipo B, y no existen servicios de tipo A, el retorno id
  2. Si al menos un servicio es de tipo C, y no existen servicios de tipo B o A, retorno id
  3. Si al menos un servicio es de tipo D, y no hay servicios de tipo C o B o A existe, volver id

y mi enfoque actual es la de formar una consulta similar a la de abajo

SELECT c.ClientId
FROM
  Clients AS c
    -- actually INNER JOIN is superfluous in this sample, but required for
    -- other auxilliary criteria i have left out. illustrates relationship
    -- between Clients and Services table
    INNER JOIN Services AS s ON c.ClientId = s.ClientId
WHERE
-- has at least one service of type B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A'))) OR 

-- has at least one service of type C, no B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A'))) OR

-- has at least one service of type D, no C, no B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'D')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A')))

donde [dbo].[Get_ServicesByClientIdAndType] es una función que devuelve los servicios asociados para la Identificación del cliente especificado y el tipo de servicio. Similar a

-- this query is actually significantly more complex than shown
-- below, but this illustrates use of parameters client id and
-- service type
SELECT s.ServiceType
FROM
  Services AS s
WHERE
  s.ClientId = @clientId AND
  s.ServiceType = @serviceType

Suponiendo esto se medios óptimos de expresar este caso de uso, funcionaría [dbo].[Get_ServicesByClientIdAndType] sub-consulta se almacena en caché o no el cambio de parámetro de servicio requieren una nueva evaluación cada invocación? [I estoy invocando esta cosa como 9 veces !!! ejecutar SQL Server 2005]

Sé que SQL Server 2005 es compatible con algunas optimizaciones sub-consulta, como los resultados de almacenamiento en caché, pero no sé a ciencia cierta en qué circunstancias o cómo formar mis sub-consultas o [cargo] de tal manera que hago la mayor parte de SQL las capacidades del servidor.


EDIT: revisado mis criterios anteriores, y no podía dejar ir de una molesta sensación de que algo estaba mal. He jugado un poco con algo de lógica en mi cabeza, y se acercó con esta [mucho más simple] formulación

SELECT c.ClientId
FROM
  Clients AS c
    INNER JOIN Services AS s ON c.ClientId = s.ClientId
WHERE
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A')) AND
    (EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) OR 
    EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) OR 
    EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'D')))

esencialmente, no existe escenario en el que B que daría lugar a rechazo, de manera similar para C y D, por lo que cualquier configuración es aceptable. sólo nos importa que A no está presente en cualquier selección. Arg! Charlie Brown!


dejando ambas expresiones para su revisión, y todavía mucho apreciar las respuestas relativas a las funciones definidas por el usuario WRT el rendimiento de SQL Server.

¿Fue útil?

Solución

Yo estaba escribiendo una respuesta para su pregunta y, mientras tanto, ha cambiado sus requisitos, pero que no debería tener ningún problema para convertir mi solución a sus necesidades específicas ..

Pero déjame empezar desde el principio. Estoy bastante seguro de que SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A') no se almacena en caché en modo alguno por el servidor. No es tan inteligente;.) Por lo tanto, se calcula varias veces en su consulta principal

Así que su primera optimización debe ir en esa dirección. Debe reducir el número de veces en que Get_ServicesByClientIdAndType se llama. Puede hacerlo de muchas maneras. Pero la regla general es que los que se debe calcular todos los posibles resultados de esta función para todos sus clientes. Esos resultados se deben poner en alguna mesa temporarty o serán computadas en una tabla virtual whis es hecho por sí SQL Server.

Cuando usted consiguió sus todos los resultados posibles, simplemente unirse a ellos con su tabla de clientes. Pero les sólo para unirse a una vez .

Por supuesto, muchas cosas y optimización truco depende de su ejemplo real. En el ejemplo que ha dado ni siquiera hay necesidad de uso de Get_ServicesByClientIdAndType. ¿Por qué no simplemente unirse a esas dos tablas y realizar algunos cálculos sobre ellos?

Tome un vistazo a esta consulta:

SELECT A.* FROM
(
 SELECT C.ClientID,
  SUM(CASE(S.ServiceType) WHEN 'A' THEN 1 ELSE 0 END) AS ServiceA,
  SUM(CASE(S.ServiceType) WHEN 'B' THEN 1 ELSE 0 END) AS ServiceB,
  SUM(CASE(S.ServiceType) WHEN 'C' THEN 1 ELSE 0 END) AS ServiceC,
  SUM(CASE(S.ServiceType) WHEN 'D' THEN 1 ELSE 0 END) AS ServiceD
 FROM Clients AS C
 INNER JOIN Services AS s ON c.ClientId = s.ClientId
 GROUP BY C.ClientID
) A
WHERE ((A.ServiceB > 0) AND (A.ServiceA = 0)) 
 OR ((A.ServiceC > 0) AND (A.ServiceA = 0) AND (A.ServiceB = 0))
 OR ((A.ServiceD > 0) AND (A.ServiceA = 0) AND (A.ServiceB = 0) AND (A.ServiceC = 0))

En la consulta interna que se unen a las tablas. Nos tiramos la función ya que no lo necesita. En su lugar, se calcula el número de diferentes servicios para cada cliente. Siguiente en los resultados de la consulta interna ponemos en práctica sus condiciones. Simplemente cheque por la aparición de servicios prestados en un conjunto particual.

El resultado es la siguiente:

ClientID ServiceA ServiceB ServiceC ServiceD
-------- -------- -------- -------- --------
26915       0        4        2        2
26917       0        0        1        1
26921       0        3        2        3
26927       0        4        2        4

Por supuesto se puede pelar el resultado final de las columnas de Servicio. Yo los he incluido porque me gusta que sea así ;-) y permite comprobar si la consulta funciona correctamente. Incluso puede escribir una consulta que no se calcule el número de tipo de servicio dado para un cliente determinado. Se trabajará aún más rápido y le dará los resultados adecuados.

Además, si usted realmente necesita su función, ¿por qué no cambiar su aplicación de manera que la función devolverá Identificación y después de la primera exitosa unirse? Esto le ahorrará mucho tiempo.

Sin embargo, sólo se conoce el cuadro más grande así que todo lo escrito aquí puede haber basura; -)

De todos modos, espero que ayudado de alguna manera.

Otros consejos

Me imagino que SQL Server llama a su función Get_ServicesByClientIdAndType una vez para cada combinación de valores de los parámetros, sino que para cada fila de la tabla Clientes. Tiene tres combinaciones de valores, por lo que para 100 filas en la tabla de clientes se pueden ver 300 llamadas de la función.

Sin embargo, para estar seguro, ejecutar la consulta en SQL Server Management Studio y conectar la opción "Mostrar plan de ejecución". De esta manera se puede detectar fácilmente qué parte de la consulta consume más recursos y Conentrate en la optimización de esa parte.

Una cosa a tener en cuenta es evitar el "NO" si es posible. "NO" no es sargable, no será capaz de aprovechar todas las ventajas de la indexación. A primera vista, no veo una manera de volver a escribir para evitar las expresiones NO embargo. Fwiw, tu caso es distinto. : -)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top