2009-05-15 4 views
6

SQL 문에서 DATEDIFF를 사용하고 있습니다. 나는 그것을 선택하고 WHERE 절에서도 사용해야한다. ... 잘못된 열 이름 "InitialSave"T-SQL에서 DATEDIFF 사용

그러나이 문은 잘 작동

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave 
FROM MyTable 
WHERE DATEDIFF(ss, BegTime, EndTime) <= 10 

프로그래머의 :이 문은

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave 
FROM MyTable 
WHERE InitialSave <= 10 

그것은

메시지를 제공합니다 ... 작동하지 않습니다 나)이 비효율적 인 (내가 두 번 함수를 호출하는 것처럼 보입니다)라고 말합니다.

두 가지 질문이 있습니다. 왜 첫 번째 문장은 효과가 없습니까? 그것은 두 번째 문장을 사용하여 비효율적입니까?

답변

5

where 문이 실행될 때까지 생성되지 않기 때문에 where 문에서 select 문에 정의 된 열에 액세스 할 수 없습니다.

당신은 (!) 참고로이 그러나

select InitialSave from 
(SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave 
FROM MyTable) aTable 
WHERE InitialSave <= 10 

을 수행 할 수 있습니다 -이 본질적으로는 처음 정의 된 어디 측면에서 어디에 문에 DATEDIFF 이동합니다. where 문에서 컬럼에 함수를 사용하면 인덱스를 효율적으로 사용하지 못하며 가능한 경우 피해야합니다. 그러나 datediff를 사용해야하는 경우에는해야합니다.

+0

그래서이 외에도 20 개의 컬럼을 선택한다면, 두 문장 (내부와 외부)에서 모두 SELECT해야 할 것입니다. 맞습니까? 더 좋은 방법이 있습니까? –

+2

원하는 경우 InitialSave, Col2, Col3 등을 선택하는 대신 첫 번째 줄에서 select *를 사용할 수 있습니다. –

+0

오, 이런! 감사! * 머리에 자기를 때린다 * –

2

열 별칭 대신 함수를 사용해야합니다. 이는 count (*) 등에서 동일합니다. PITA.

+0

그것은 똑똑를에 *하지 * 같은 광범위한 계산을 여러 번 통과? 나는 의심 스럽다. –

7

참고 : 원래이 답변을 작성했을 때 나는 열 중 하나의 색인이 다른 답변보다 성능이 좋은 쿼리를 만들 수 있다고 말했고 Dan Fuller가 언급했습니다. 그러나 나는 100 % 올바르게 생각하지 않았습니다. 사실 계산 된 열이나 인덱싱 된 (materialized) 뷰가없는 경우 이 필요하고 전체 테이블 스캔은 일 것입니다. 왜냐하면 비교되는 두 날짜 열이 과 동일하고 테이블이기 때문입니다!

나는 아래 정보에 여전히 가치가 있다고 믿습니다. 즉, 1) 다른 테이블의 열 사이에서 비교할 때와 같이 올바른 상황에서 성능이 향상 될 가능성, 2) SQL 개발자의 습관을 다음과 같이 촉진하는 것 모범 사례와 옳은 방향으로 사고를 재구성하십시오.

만들기 조건 스 SARGable

내가 말하는 겁니다 가장 좋은 방법은, 비교 연산자의 한쪽에 혼자과 같이 하나의 열을 이동 중 하나입니다 : I로

SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime) 
FROM dbo.MyTable T 
WHERE T.EndTime <= T.BegTime + '00:00:10' 

단일 테이블에 대한 검색을 피할 수는 없지만 이와 같은 상황에서는 큰 차이를 만들 수 있습니다 :

SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime) 
FROM 
    dbo.BeginTime B 
    INNER JOIN dbo.EndTime E 
     ON B.BeginTime <= E.EndTime 
     AND B.BeginTime + '00:00:10' > E.EndTime 

EndTime은 현재 비교의 한 쪽에서 두 조건 모두에 있습니다.BeginTime 테이블의 행 수가 많고 EndTime 테이블의 인덱스가 EndTime 인 경우이 값은 DateDiff(second, B.BeginTime, E.EndTime)을 사용하는 것보다 훨씬 더 우수합니다. 엔진 스캔BeginTime 테이블, 그것은 EndTime 테이블에을 추구 할 수 있도록 - 이제 유효한 "검색 인수가"가 의미 스 SARGable이다. 그것은

I 참조 DateDiff의 AND B.BeginTime > E.EndTime - '00:00:10'

정밀로 전환 할 일부 대수를 수행하여 자체 BeginTime를 넣어 실험을 가치가있을 수 있습니다 - 컬럼의주의 깊은 선택은 연산자의 한쪽이 필요 자체입니다 또한 DateDiff이 경과 된 시간 인을 반환하지 않으며 대신 경계의 수를 계산합니다 (). 초를 사용하는 DateDiff에 대한 호출이 1을 반환하면 이것은 3 ms 경과 시간을 의미 할 수 있으며 1997 ms을 의미 할 수 있습니다! 이것은 본질적으로 ± 1 시간 단위의 정밀도입니다. +의 더 나은 정밀도 - 2 시간 단위, 당신은 비교 다음 쿼리를 원할 것 0EndTime - BegTime에 :

SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave 
FROM MyTable 
WHERE EndTime <= BegTime + '00:00:10' 

이 이제 하나의 초 총의 최대 반올림 오류가 아니라 두 (효과, floor() 작업). date 또는 time 값을 뺀 값을 datetime으로 변경하거나 다른 방법을 사용하여 더 좋은 정밀도를 얻을 수 있습니다 (전체적으로 많이 DateAdd, DateDiff 및 기타 정크가 될 수 있음). 즉, datetime 데이터 유형을 빼기 만하면됩니다. 아마도 더 높은 정밀도 시간 단위를 사용하고 나누기).

이 원칙은 시간, 일 또는 월과 같이 더 큰 단위를 계산할 때 특히 중요합니다. DateDiff1 month 일 경우 62 일 간격이 될 수 있습니다 (2013 년 7 월 1 일 - 2013 년 8 월 31 일). "작업"을 만드는 넘어

+1

그건 바보 야. 나는 그 주석을 만들 때 냉담한 공기에 영향을 미치지 않고 실제로 설명하는 것들의 실제적인 효과를 알지 못하는 사람에게 실제로 도움이되도록 노력했습니다. 내가 쓴 것을 다시 읽으십시오. 아마 너무 작 았지만 사실 모욕하지 않았습니까? – ErikE

+0

오 ...그리고 당신이 오해 한 단어가 있기 때문에 내 성능이 좋은 * 질의에 -1이 표시됩니다 ... 여기에 공백을 채우지 만 무료는 아닙니다. 이봐 요, 내가 할 수있는 것을 나눠주고 싶었어. – ErikE

+0

+1 부당하게 당신을 표시 jvanderh를 만회하는 –

3

, 당신은 인덱스를

사용 인덱스 또는 인덱스 볼 수있는 계산 된 열을 사용할 필요가 그렇지 않으면 당신은 테이블 스캔 것입니다. 충분한 행이 생기면 PAIN 느린 검색을 느낄 것입니다!

계산 열 & 인덱스 :

ALTER TABLE MyTable ADD 
    ComputedDate AS DATEDIFF(ss,BegTime, EndTime) 
GO 
CREATE NONCLUSTERED INDEX IX_MyTable_ComputedDate ON MyTable 
    (
    ComputedDate 
    ) WITH(STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 

이보기 & 인덱스 생성 :

CREATE VIEW YourNewView 
AS 
SELECT 
    KeyValues 
     ,DATEDIFF(ss, BegTime, EndTime) AS InitialSave 
    FROM MyTable 
GO 
CREATE CLUSTERED INDEX IX_YourNewView 
    ON YourNewView(InitialSave) 
GO 
+1

뷰에 인덱스를 만드는 것이 항상 마법의 총알은 아닙니다. EndTime 열의 인덱스가 충분히 성공적으로 수행되고 업데이트 오버 헤드가 발생하지 않을 것입니다. 그것은 update/select 패턴으로 조금 내려갑니다. – ErikE

+0

이것은 정말 좋은 지적이며 계산 된 열을 사용하는 것은 실제로 데이터와 사용되는 열의 빈도에 따라 달라집니다. 열이 사용되는 테이블의 크기가 크고 (수백만 행 +와 같이) 문제가되는 열의 크기가 다음 중 하나 일 경우 1. 일반적으로 응용 프로그램을 실행하는 데 매우 자주 사용됩니다. 2. 빠른 액세스가 필요한 임원 수준의 직원이 드물게 액세스 할 수 있습니다. 그러면 계산 된 열을 사용해야합니다. 그렇지 않으면 삽입물/업데이트 속도를 늦추지 않고 사용하지 않는 것이 좋습니다. –