그것은 테이블의 데이터, 인덱스, ...에 달려 있습니다. 실행 계획 / io + 시간 통계를 비교할 수 없어 말하기 어렵습니다.
내가 기대할 수있는 차이점은 두 테이블 사이에 참여하기 전에 추가 필터링이 발생한다는 것입니다. 이 예에서는 테이블을 재사용하도록 업데이트를 변경했습니다.
"최적화"를 포함한 실행 계획
실행 계획
필자는 테스트 데이터에서 필터링 작업이 수행되는 레코드가 없으며 결과적으로 개선되지 않은 필터 작업이 있음을 분명히 알 수 있습니다.
"최적화"가없는 실행 계획
실행 계획
필터가 사라 졌으므로 불필요한 레코드를 필터링하려면 조인에 의존해야합니다.
다른 이유
쿼리를 변경 한 또 다른 이유 / 결과는 쿼리를 변경할 때 새로운 실행 계획이 만들어 져서 더 빠르기 때문일 수 있습니다. 이에 대한 예는 다른 Join 연산자를 선택하는 엔진이지만이 시점에서는 추측입니다.
편집하다:
두 가지 쿼리 계획을 얻은 후 설명 :
쿼리가 큰 테이블에서 550M 행을 읽고 필터링합니다.
술어는 탐색 술어가 아니라 대부분의 필터링을 수행하는 것임을 의미합니다. 데이터를 읽지 만 결과는 덜 리턴됩니다.
SQL Server에서 다른 인덱스 (쿼리 계획)를 사용하도록하거나 인덱스를 추가하면이 문제를 해결할 수 있습니다.
그렇다면 최적화 쿼리에 왜 같은 문제가 없습니까?
다른 쿼리 계획이 사용되기 때문에 탐색 대신 스캔이 사용됩니다.
검색을 수행하지 않고 작업 할 4M 행만 리턴합니다.
다음 차이점
업데이트 차이를 무시하고 (최적화 된 쿼리에서 아무것도 업데이트되지 않음) 해시 일치는 최적화 된 쿼리에서 사용됩니다.
최적화되지 않은 중첩 루프 조인 대신 :
중첩 루프는 한 테이블이 작고 다른 테이블이 클 때 가장 좋습니다. 둘 다 같은 크기에 가깝기 때문에이 경우 해시 일치가 더 나은 선택이라고 주장합니다.
개요
최적화 된 쿼리
최적화 된 쿼리 계획에는 병렬이 있으며 해시 일치 조인을 사용하며 잔여 IO 필터링을 줄여야합니다. 또한 비트 맵을 사용하여 조인 행을 생성 할 수없는 키 값을 제거합니다. (또한 아무것도 업데이트되지 않습니다)
최적화되지 않은 쿼리 최적화
되지 않은 쿼리의 계획에는 병렬이 없으며 중첩 루프 조인을 사용하며 550M 레코드에 대한 잔여 IO 필터링을 수행해야합니다. (또한 업데이트가 진행 중입니다)
최적화되지 않은 쿼리를 개선하기 위해 무엇을 할 수 있습니까?
키 열 목록에서 first_name & last_name을 갖도록 색인 변경 :
INDEX IX_largeTableOfPeople_birth_date_first_name_last_name on dbo.largeTableOfPeople (birth_date, first_name, last_name) include (id) 작성
그러나 함수를 사용하고이 테이블이 크면 최적의 솔루션이 아닐 수 있습니다.
- 더 나은 계획을 세우고 다시 컴파일하기 위해 재 컴파일을 사용하여 통계 업데이트
(HASH JOIN, MERGE JOIN)
쿼리에 OPTION 추가
- ...
테스트 데이터 + 사용 된 쿼리
CREATE TABLE #smallTableOfPeople(importantValue int, birthDate datetime2, first_name varchar(50),last_name varchar(50));
CREATE TABLE #largeTableOfPeople(importantValue int, birth_date datetime2, first_name varchar(50),last_name varchar(50));
set nocount on;
DECLARE @i int = 1
WHILE @i <= 1000
BEGIN
insert into #smallTableOfPeople (importantValue,birthDate,first_name,last_name)
VALUES(NULL, dateadd(mi,@i,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @i += 1;
END
set nocount on;
DECLARE @j int = 1
WHILE @j <= 20000
BEGIN
insert into #largeTableOfPeople (importantValue,birth_Date,first_name,last_name)
VALUES(@j, dateadd(mi,@j,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @j += 1;
END
SET STATISTICS IO, TIME ON;
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å');
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
--AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å')
drop table #largeTableOfPeople;
drop table #smallTableOfPeople;
AND LEFT(TRIM(largeTbl.last_name), 1) BETWEEN 'a' AND 'z' COLLATE LATIN1_GENERAL_CI_AI
모든 문자를 나열하지 않고 읽기 어려운 코드를 작성하지 않고도 원하는 작업을 수행해야합니다.