문제

저는 현재 특히 복잡한 사용 사례를 연구하고 있습니다. 아래 단순화 :)

첫째, 클라이언트 레코드는 서비스 모음과 다중 관계를 가지고 있습니다.

트리거 내에서 특정 기준에 따라 클라이언트의 ID를 반환하는 쿼리를 작성하고 있습니다. 기준은 다음과 같습니다.

  1. 하나 이상의 서비스가 유형 B이고 유형 A의 서비스가 존재하지 않으면 ID를 반환합니다.
  2. 적어도 하나의 서비스가 C 형이 있고 유형 B 또는 A의 서비스가없는 경우 ID를 반환합니다.
  3. 하나 이상의 서비스가 유형 D 인 경우 C 또는 B 형 또는 A가 존재하지 않으면 ID를 반환합니다.

그리고 현재의 접근 방식은 아래의 쿼리와 유사한 쿼리를 형성하는 것입니다.

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

어디 [dbo].[Get_ServicesByClientIdAndType] 지정된 클라이언트 ID 및 서비스 유형에 대한 관련 서비스를 반환하는 기능입니다. 비슷하다

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

이것이이 유스 케이스를 표현하는 최적의 수단이라고 가정하면 기능합니다. [dbo].[Get_ServicesByClientIdAndType] 하위 쿼리가 캐시되거나 변화하는 서비스 매개 변수가 각 호출을 새로운 평가를 필요로합니까? [나는 9 번처럼 이걸 불러 내고있다 !!! SQL Server 2005 실행

SQL Server 2005는 캐싱 결과와 같은 일부 하위 쿼리 최적화를 지원하지만 SQL Server의 기능을 최대한 활용할 수 있도록 내 하위 쿼리 [또는 기능]을 형성하는 방법에 대해서는 알 수 없습니다.


편집하다: 위의 기준을 검토했으며 무언가가 꺼진 느낌을 버릴 수 없었습니다. 나는 머리에 약간의 논리를 가지고 놀았고이 [훨씬 간단한] 공식을 생각해 냈습니다.

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

기본적으로 C 및 D와 유사하게 거부로 이어질 B와 관련된 시나리오가 없으므로 모든 구성이 허용됩니다. 우리는 선택에 A가 존재하지 않는다는 것을 관리합니다. 아르! 찰리 브라운!


검토를 위해 두 표현을 모두 남겨두고 있으며 SQL Server의 성능 WRT 사용자 정의 기능에 대한 응답에 여전히 감사합니다.

도움이 되었습니까?

해결책

나는 당신의 질문에 대한 답을 쓰고 있었고 그 동안 당신은 당신의 요구 사항을 변경했지만 내 솔루션을 특정 요구 사항으로 변환하는 데 아무런 문제가 없어야합니다.

그러나 처음부터 시작하겠습니다. 나는 그것을 확신합니다 SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A') 어쨌든 서버에 의해 캐시되지 않습니다. 그다지 똑똑하지 않습니다.) 주 쿼리에서 여러 번 계산됩니다.

따라서 첫 번째 최적화는 그 방향으로 이동해야합니다. 언제 횟수를 줄여야합니다 Get_ServicesByClientIdAndType 호출됩니다. 여러 가지 방법으로 할 수 있습니다. 그러나 일반적인 규칙은 모든 고객 에게이 기능의 가능한 모든 결과를 계산해야한다는 것입니다. 이러한 결과는 일부 시간 테이블에 넣어야합니다. 그렇지 않으면 SQL Server 자체에 의해 가상 테이블 속삭임에 넣어야합니다.

가능한 모든 결과를 얻었을 때 단순히 고객 테이블과 함께 참여합니다. 그러나 당신은 그것들 만 합류합니다 한 번.

물론 많은 것들과 최적화 트릭은 실제 예에 따라 다릅니다. 당신이 주신 예에서는 사용법이 필요하지 않습니다. Get_ServicesByClientIdAndType. 이 두 테이블에 가입하고 계산을 수행하지 않겠습니까?

이 쿼리를 살펴보십시오.

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

내부 쿼리에서 우리는 테이블에 가입합니다. 우리는 그것을 필요로하지 않기 때문에 기능을 버립니다. 대신, 우리는 각 클라이언트의 다른 서비스 수를 계산합니다. 다음으로 내부 쿼리 결과를 통해 조건을 구현합니다. 우리는 단순히 입자 세트에서 주어진 서비스의 발생을 확인합니다.

결과는 다음과 같습니다.

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

물론 서비스 열에서 최종 결과를 제거 할 수 있습니다. 나는 그런 식으로 좋아하기 때문에 그것들을 포함 시켰고 ;-) 그리고 쿼리가 제대로 작동하는지 확인할 수 있습니다. 주어진 클라이언트의 주어진 서비스 유형 수를 계산하지 않는 쿼리를 작성할 수도 있습니다. 더 빠르게 작동하고 적절한 결과를 제공합니다.

또한 기능이 실제로 필요하다면, 처음 SuccesFull 조인 후 기능이 반환되고 ID가있는 방식으로 구현을 변경하지 않겠습니까? 그것은 당신에게 많은 시간을 절약 할 것입니다.

그러나 당신만이 더 큰 그림을 알고 있으므로 여기에 쓴 모든 것이 쓰레기 일 수 있습니다 ;-)

어쨌든, 나는 어떤 식 으로든 당신을 도왔기를 바랍니다.

다른 팁

SQL Server는 각 매개 변수 값의 각 조합에 대해 get_servicesbyclientIdandType를 한 번만 호출하지만 클라이언트 테이블의 모든 행에 대해 호출한다고 생각합니다. 값의 3 가지 조합이 있으므로 클라이언트 테이블의 100 행의 경우 300 개의 호출 기능이 표시 될 수 있습니다.

그러나 자신감있게 SQL Server Management Studio에서 쿼리를 실행하고 "실행 계획 표시"옵션을 켜십시오. 이렇게하면 쿼리의 어느 부분이 가장 많은 리소스를 소비하는지 쉽게 감지하고 해당 부분을 최적화하는 데 도움이됩니다.

명심해야 할 한 가지는 가능하다면 "아님"을 피하는 것입니다. "NOT"은 부당하지 않으며 인덱싱의 모든 장점을 취할 수 없습니다. 언뜻보기에, 나는 표현을 피하기 위해 그것을 다시 쓰는 방법을 보지 못했습니다. fwiw, ymmv. :-)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top