Pergunta

Atualmente, estou trabalhando em um caso de uso particularmente complexo. Simplificar a seguir:)

Em primeiro lugar, um registro cliente tem uma relação para-muitos-um com um conjunto de serviços, ou seja, um único cliente pode ter vários serviços associados a ela.

Dentro do meu gatilho, estou escrevendo uma consulta que retorna ID de um cliente com base em determinados critérios. Os critérios são os seguintes,

  1. Se pelo menos um serviço é do tipo B, e nenhum serviço do tipo A existir, retorno id
  2. Se pelo menos um serviço é do tipo C, e nenhum serviço do tipo B ou A exist, id retorno
  3. Se pelo menos um serviço é do tipo D, e nenhum serviço do tipo C ou B ou A exist, id retorno

e minha abordagem atual é formar uma consulta semelhante ao abaixo

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')))

onde [dbo].[Get_ServicesByClientIdAndType] é uma função que retorna serviços para id cliente especificado e tipo de serviço associados. Similar ao

-- 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

Assumindo que este é o meio ideal de expressar este caso de uso, seria função [dbo].[Get_ServicesByClientIdAndType] sub-consulta seja armazenada em cache ou não mudança de parâmetro de serviço exigem uma nova avaliação cada invocação? [I estou chamando essa coisa como 9 vezes !!! executando o SQL Server 2005]

Eu sei SQL Server 2005 suporta algumas otimizações sub-consulta, como cache de resultados, mas eu não sei ao certo em que circunstâncias ou como formar minhas sub-consultas [ou função] de tal forma que eu tirar o máximo do Sql capacidades do servidor.


EDIT: revisada meus critérios acima, e não podia deixar de ir a sensação incômoda de que algo estava fora. Eu brinquei com alguma lógica na minha cabeça, e veio com esta [mais simples] formulação

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')))

essencialmente, não existe qualquer cenário que envolve B que levaria a rejeição, de forma semelhante para C e D, de modo que qualquer configuração é aceitável. que só se preocupam que A não está presente em qualquer seleção. Arg! Charlie Brown!


deixando as duas expressões para revisão, e eu ainda aprecio muito as respostas sobre as funções definidas pelo usuário wrt o desempenho do SQL Server.

Foi útil?

Solução

Eu estava escrevendo uma resposta para a sua pergunta e, entretanto, você mudou suas necessidades, mas você não deve ter quaisquer problemas para converter a minha solução para suas necessidades específicas ..

Mas deixe-me começar desde o início. Tenho a certeza de que SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A') não está em cache de qualquer maneira pelo servidor. Não é tão inteligente;). Então, ela é calculada várias vezes em sua consulta principal

Portanto, o seu primeiro otimização deve ir nessa direção. Você deve reduzir o número de vezes em que Get_ServicesByClientIdAndType é chamado. Você pode fazê-lo de muitas maneiras. Mas a regra geral é que que você deve calcular todos os possíveis resultados desta função para todos os seus clientes. Esses resultados devem ser colocados em alguma tabela temporarty ou serão puted em uma tabela virtual whis é feita pelo próprio SQL Server.

Quando você tem seus todos os resultados possíveis que você simplesmente juntá-los com sua tabela de clientes. Mas você juntar-los somente UMA VEZ .

É claro que muitas coisas e truque de otimização depende do seu exemplo real. No exemplo que você deu há ainda nenhuma necessidade para Get_ServicesByClientIdAndType uso. Porque não basta juntar essas duas tabelas e executar alguns cálculos sobre eles?

Dê uma olhada 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))

Na consulta interna que unir as tabelas. Nós jogar fora a função, uma vez que não precisa dele. Em vez disso, calculamos o número de diferentes serviços para cada cliente. Em seguida sobre os resultados da consulta internas vamos implementar suas condições. Nós simplesmente verificar a ocorrência de serviços prestados em um conjunto particual.

O resultado é assim:

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

Claro que você pode retirar o resultado final a partir de colunas de Serviços. Eu incluí-los porque eu gosto desse jeito ;-) E isso permite verificar se a consulta funciona corretamente. Você pode até escrever uma consulta que não irá calcular o número de determinado tipo de serviço para um determinado cliente. Ele vai trabalhar ainda mais rápido e dar-lhe os resultados adequados.

Além disso, se você realmente precisa de sua função, por que não mudar a sua implementação de uma forma que a função retornará e ID após a primeira succesfull participar? Ela vai lhe poupar muito tempo.

Mas só você sabe a foto maior então tudo que eu escrevi aqui pode ser lixo; -)

De qualquer forma, espero que eu te ajudou de alguma forma.

Outras dicas

Eu diria que sql server chama sua função Get_ServicesByClientIdAndType uma vez para cada combinação de valores dos parâmetros, mas que para cada linha na tabela de clientes. Você tem três combinações de valores, portanto, para 100 linhas na tabela do cliente que você pode ver 300 chamadas de função.

Mas para estar confiante, executar a consulta no estúdio de gerenciamento do SQL Server e ligar o "plano de show de execução" opção. Desta forma, você pode facilmente detectar que parte de sua consulta consome mais recursos e conentrate sobre como otimizar essa parte.

Uma coisa a ter em mente é para evitar "NÃO", se possível. "NÃO" é não-sargable, ele não será capaz de tirar vantagens da indexação. À primeira vista, não vejo uma maneira de reescrevê-lo para evitar as expressões NÃO embora. FWIW, YMMV. : -)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top