2012-06-01 3 views
3

비트 값 열이있는 테이블이 있습니다. 연결된 항목의 모든 레코드가 true이면 true를 반환하는 함수를 작성하고 싶습니다.sql-server에서 모든 레코드를 쿼리하면 값 비싼 성능이 현저하게 향상됩니다.

Select @Ret = CAST(MIN(CAST(IsCapped as tinyInt)) As Bit) 
    from ContractCover cc 
    Inner join ContractRiskVersion crv on cc.ContractRiskId = crv.ContractRiskId 
    WHERE crv.ContractVersionId = @ContractVersionId 
    AND cc.IsActive = 1 
    return @ret 

그러나 최소 비용을 얻기 위해 int로 캐스팅은 다음과 같습니다

한 가지 방법은 내가이 일을 찾았나요? 대신에 다음과 같은 말을 기반으로 쿼리해야합니까?

(count (Id) IsCapped = 0> 0) 여러 캐스트를 수행하는 대신 false를 반환합니까?

실행 계획에서이 함수를 실행하는 것이 무거워 보이지 않습니다 (하지만 쿼리 계획을 분석하는 데 너무 익숙하지 않습니다 - 저장 된 다른 섹션과 동일한 % 비용을 갖는 것 같습니다 2 %의 proc).

편집 - 함수를 호출하고 실행 계획을 조사하는 저장된 proc를 실행할 때 - 함수를 호출하는 부분은 쿼리 비용 (배치와 관련이 있음)을가집니다. 1 %는 다른 섹션과 비교됩니다. 저장된 proc. 내가 틀린 것을 보지 않는 한 :)

고마워!

답변

4

쿼리가 항상 모든 데이터를 읽을 때 IsCapped = 0 위치에서 1 레코드를 찾은 순간부터 쿼리에서 빠져 나와서 exist 문을 사용하면이 작업을 수행 할 수 있습니다.

CREATE FUNCTION dbo.fn_are_contracts_capped(@ContractVersionId int) 
RETURNS bit 
WITH SCHEMABINDING 
AS 
BEGIN 
    DECLARE @return_value bit 

    IF EXISTS(
     SELECT 1 
     FROM dbo.ContractCover cc 
     JOIN dbo.ContractRiskVersion crv 
      ON cc.ContractRiskId = crv.ContractRiskId 
     WHERE crv.ContractVersionId = @ContractVersionId 
     AND cc.IsActive = 1 
     AND IsCapped = 0) 
     BEGIN 
     SET @return_value = 0 
     END 
    ELSE 
     BEGIN 
     SET @return_value = 1 
     END 

    RETURN @return_value 
    END 

데이터를 읽는 데 필요한 IO와 비교할 때 캐스트는 많은 오버 헤드를 추가하지 않습니다.

편집 : 스칼라 함수로 묶인 코드.

+0

답변 해 주셔서 감사합니다. – Jen

+0

RETURN이 함수의 마지막 줄 일 필요가 있기 때문에 약간 조정했다. 다시 @ret 값을 반환했다. – Jen

+1

함수에서 래핑 될 코드를 업데이트했습니다. –

2

SELECT의 캐스팅은 CPU와 메모리 바운드입니다. 이 경우 얼마나 많은지는 알 수 없습니다. 일반적으로 정상적인 환경에서는 IO를 먼저 최적화하고 CPU와 메모리를 걱정하지 않습니다. 그래서 나는 거기에 대한 확실한 답을 갖고 있지 않습니다.

그렇다면이 문제에 대한 해결책은 단락이 발생하지 않는다는 것입니다. SQL Server는 ContractVersionId = @ContractVersionId 및 IsActive = 1 인 모든 행을 읽은 다음 IsCapped를 INT로 변환하고 min을 취합니다. 실제로 IsCapped = 0 인 단일 행을 찾으면 즉시 종료 할 수 있습니다. ContactVersionId가 고도로 선택적이고 테이블의 아주 작은 부분 만 반환하거나 대부분의 행에 제한이있는 경우에만 많은 부분을 해결할 수 있습니다. 그러나 ContactVersionId의 선택 빈도가 높지 않거나 열의 비율이 높지 않은 경우 SQL Server에 너무 많은 작업을 요청하고 있습니다.

두 번째 고려 사항은 스칼라 반환 함수가 SQL Server의 성능 악영향이라는 것입니다.

create function AreAllCapped(@ContractVersionId int) 
returns table as return (
select 
    ContractVersionId = @ContractVersionId 
, AreAllCapped = case when exists (
     select * 
     from ContractCover cc 
     join ContractRiskVersion crv on cc.ContractRiskId = crv.ContractRiskId 
     where crv.ContractVersionId = @ContractVersionId 
     and cc.IsActive = 1 
     and IsCapped = 0 
    ) 
    then 0 else 1 end 
) 

그런 다음 FROM 절 (가정 SQL 2005 이상)에 CROSS APPLY를 사용하여 호출 할 수 있습니다 : 인라인 테이블 함수 가능한 경우, 예를 들면로 만드는 것이 좋습니다.

최종주의 사항 : IsCapped = 0 인 곳에서 카운트하는 것은 비슷한 문제가 있습니다. LINQ의 Any()와 Count()의 차이점과 비슷합니다. 모든() 단락, Count() 실제로 모든 요소를 ​​계산해야합니다. SELECT COUNT(*) ... WHERE IsCapped = 0은 한 행만 있으면 이동해야하지만 여전히 개가되어야합니다.

+0

답장을 보내 주셔서 감사합니다. LINQ와 비교해 주시면 고맙겠습니다 : – Jen

1

물론, bit 열을 집계 함수에 인수로 전달할 수 없다는 사실은 물론 알려져 있습니다. 따라서 전달해야하는 경우 먼저 정수로 캐스팅해야합니다. 그러나 bit 열은 일 수 있습니다. by. 귀하의 요청에 따라서, 다음과 같이 다시 작성할 수 있습니다 :이 특정 쿼리에이 IsCapped는 NULL이 될 수 있다고 가정하는 것이

SELECT TOP 1 @Ret = IsCapped 
FROM ContractCover cc 
INNER JOIN ContractRiskVersion crv on cc.ContractRiskId = crv.ContractRiskId 
WHERE crv.ContractVersionId = @ContractVersionId 
    AND cc.IsActive = 1 
ORDER BY IsCapped; 

참고. 이 수 있다면, 당신은 WHERE 절에 추가 필터를 추가해야합니다 :

AND IsCapped IS NOT NULL 

,하지 않는 한 물론, 당신이 실제로있는 경우, 0 대신 NULL을 반환하는 것을 선호합니다.

캐스팅 비용에 관해서는 필립과 피터가 이미 말한 것에 덧붙일 내용이 없습니다. bit 데이터를 집계하기 전에 캐스팅해야한다는 것은 귀찮은 일이지만, 그다지 중요한 문제는 아닙니다.

+0

답장을 보내 주셔서 감사합니다 :) – Jen

관련 문제