2016-09-08 2 views
1

필터 매개 변수를 사용하거나 사용하지 않을 수있는 많은 작업이 포함 된 저장 프로 시저를 작성하고 있습니다. 필터링을 수행하는 것은 자체적으로 비용이 많이 들고 필터링되는 테이블이 큽니다. 방금 내부 필터 함수를 변경하려고 시도 했으므로 잘못된 매개 변수를 사용하여 호출하면 오류가 발생합니다. 개발자가 이러한 방식으로 사용하지 않도록 경고합니다.Sql Server UDF는 null이있는 변수가 전달 될 때와 달리 null이 전달 될 때와 다르게 동작합니다.

하지만 내 외부 테스트 함수를 NULL로 호출하면 내부 함수를 호출하지 않고 오류를 던지지 않고 예상대로 작동합니다. VALUE 값이 NULL 인 변수로 외부 테스트 함수를 호출하면 null 매개 변수를 사용하여 필터 함수를 호출하고 값이 null이 아닐 때 코드가 함수를 호출한다고 만 생각하더라도 오류가 발생합니다.

여기 무슨 일 이니?

훨씬 간단한 예 :

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[MyTable]') AND type in (N'U')) DROP TABLE MyTable 
GO 

CREATE TABLE MyTable (Pk int, Field int) 
GO 

INSERT INTO MyTable VALUES (1, 1) 
INSERT INTO MyTable VALUES (2, 4) 
INSERT INTO MyTable VALUES (3, 9) 
INSERT INTO MyTable VALUES (4, 16) 
GO 

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[FilterRows]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION FilterRows 
GO 
CREATE FUNCTION FilterRows(@searchParameter int) 
RETURNS @Pks TABLE 
    (   
     Pk int 
    ) 
AS 
BEGIN 
    IF (@searchParameter IS null) 
    BEGIN 
     -- This is bad news. We don't want to be here with a null search, as the only thing we can do is return every row in the whole table 
     -- RAISERROR ('Avoid calling FilterRows with no search parameter', 16, 1)  
     -- we can't raise errors in functions! 
     -- Make it divide by zero instead then 
     INSERT INTO @Pks SELECT Pk FROM MyTable WHERE 1/0 = 1 
    END 
    ELSE 
    BEGIN 
     INSERT INTO @Pks SELECT Pk FROM MyTable WHERE Field > @searchParameter 
    END 
    RETURN 
END 
GO 


IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[OuterFunction]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION OuterFunction 
GO 
CREATE FUNCTION OuterFunction(@searchParameter int) 
RETURNS TABLE AS 
RETURN 
SELECT * 
FROM 
MyTable 
WHERE 
(@SearchParameter IS NULL) OR (@searchParameter IS NOT NULL AND Pk IN (SELECT Pk FROM dbo.FilterRows(@searchParameter))) 
GO 

SELECT * FROM dbo.OuterFunction(2) -- Returns filtered values 
SELECT * FROM dbo.OuterFunction(null) -- returns everything, doesn't call FilterRows 
DECLARE @x int = null 
SELECT * FROM dbo.OuterFunction(@x) -- WTF! Throws error! 
+0

"선택적 필터"는 옵티마이 저가 * 첫 번째 호출에 대해 생성 된 실행 계획을 캐시하므로 설계상의 실수입니다. 단락의 대신에 당신은 더 나쁜 성과로 끝낼 것이다. 그런 것들에 대한 필요성은 ORM과 LINQ를 사용하여 필요한 필터만으로 쿼리를 생성합니다. ad-hoc 쿼리 또는 이와 동등한 저장 프로 시저를 실행하는지 여부에 관계없이 성능은 동일합니다. –

+0

PS 필터링은 비용이 많이 드는 것과 반대입니다. 결과가 줄어 듭니다. 그렇지 않은 경우 쿼리에 문제가 있습니다. 적절한 색인이있는 경우 조건이 많을수록 * 더 빨리 실행됩니다."단락"시도는 * 느린 * 실행으로 이어집니다. 왜냐하면 그들은 무엇을 발견하기 위해 모든 것을 처리해야하기 때문입니다 ... –

+0

간결한 질문을하기 위해 상황을 의도적으로 단순화했습니다. 내가하고있는 일을하기에 좋은 이유가 있으며 쿼리 분석기의 작동 방식을 알고 있습니다. 내가 묻는 질문은 UDF의 예기치 않은 동작에 관한 것입니다. 나는 달성하려고 애 쓰고있는 것이 잘못을 저지르는 것이라고 단골 손님에게 묻고 싶지 않다. –

답변

0

내가 무슨 일인지 것은 생각

SELECT * FROM MyTable WHERE (@SearchParameter IS NULL) OR 
(@searchParameter IS NOT NULL AND Pk IN (SELECT Pk FROM dbo.FilterRows(@searchParameter))) 

목에 쿼리 분석기에서 하위 쿼리

(SELECT Pk FROM dbo.FilterRows(@searchParameter)) 

이 MyTable의 값에 종속되지 않음을 알 수 있습니다. 모든 행에 대해 상수이므로 MyTable을 결과에 조인하기 위해 먼저 해당 하위 쿼리를 실행합니다. 따라서 @searchParameter가 NULL인지 여부를 테스트하는 WHERE 절을 평가하기 전에이를 실행합니다.

@searchParameter가 NULL이고 값이 NULL 인 변수가 아닌 경우 분석기는 실행 계획의 전체 where 절을 단락시킬 수 있으므로 하위 쿼리를 미리 계산하지 않도록합니다.

또는 이와 비슷한 것입니다.

0

값이 널이 경과되면 일정 NULL이 아닌 통과 차이를 이용하여 (NULL이다) 및 (= NULL)

@var = null -- considered as false 

@var is null -- considered as unknown 
과 같은 차이가있다 자세한 내용은

: 그래서 당신은 모두의 행동을하려는 경우 SQL is null and = null

(상수 널 & 패스 Null 값이 호출) 나는이 것을 선호하지는 않지만 다음과 같이 까다롭게 사용하십시오.

바꿔 FilterRows이

IF (@searchParameter = null) 
--IF (@searchParameter is null) 

참고로 기능 : 여기이 답변을 입력 죄송합니다,이 대신 대답의 코멘트 있어야하는데, 규칙은 "당신은 코멘트 (50)의 명성이 있어야합니다"입니다 내가에만 22 :(

+0

나는 그것이 옳다고 생각하지 않는다. DECLARE @x int = NULL; IF (@x IS NULL) PRINT 'X is null' 인쇄 X는 null입니다. @x IS NULL을 테스트하면 true를 반환하기 때문입니다. @x = NULL을 테스트하면 null이 반환됩니다. IF (@searchParameter = null)는 AFAIK를 true로 반환하지 않습니다. 그렇지 않으면 내가 끼어들 것입니다 코드 스 니펫을했다! –

+0

위의 예 (질문에있는)를 사용하여 게시하기 전에 내 대답을 테스트했으며 주석의 코드도 맞았습니다. 무엇인가가 유선입니다! –

관련 문제