두 테이블에서 첫 글자 일치를 할 수 있습니까?


9
select value 
from persons p join persons2 p2 
    on left(p.lastname,1) = left(p2.lastname,1)

SQL Server. 이 SARGable / 실행 속도를 높이는 방법이 있습니까? persons 테이블에는 열을 만들 수 없지만 persons2에는 열을 만들 수 있습니다.


3
당신은 그 쿼리의 결과가 일종의 CROSS JOIN이 될 것이라는 것을 알고 있습니까?
ypercubeᵀᴹ

1
테이블이 얼마나 큽니까? 이들이 각각 10K 행에 불과하면 결과는 최소 4 백만 행이됩니다. 그런 쿼리를 어떻게 사용할지 궁금합니다.
ypercubeᵀᴹ

1
@ ypercubeᵀᴹ 퍼지 매칭을 사용하여 일부 중복 제거 프로세스에 초기 입력입니까?
Martin Smith

나쁜 생각 인 것 같습니다. 여기서 무엇을 달성하려고합니까?
David דודו Markovitz

이것은 단지 예입니다. 술어가 더 있습니다. Martin Smith는 중복 제거를위한 올바른 아이디어를 가지고 있습니다.
lastchancexi

답변:


9

LEFT(lastname, 1)각 테이블에 대해 정의 된 지속 계산 열이있는 테이블에 대한보기 를 작성한 후 계산 지속 열 값을 비교하십시오.

다음은이를 수행하는 방법을 보여주는 테스트 베드입니다.

CREATE TABLE dbo.Persons
(
    PersonID int NOT NULL
        CONSTRAINT PK_Persons
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , FirstName nvarchar(500) NOT NULL
    , LastName nvarchar(500) NOT NULL
);

CREATE TABLE dbo.Persons2
(
    PersonID int NOT NULL
        CONSTRAINT PK_Persons2
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , FirstName nvarchar(500) NOT NULL
    , LastName nvarchar(500) NOT NULL
);

GO
CREATE VIEW dbo.PersonsView
WITH SCHEMABINDING
AS
SELECT p1.PersonID
    , p1.FirstName
    , p1.LastName 
    , LastNameInitial = LEFT(p1.LastName, 1)
FROM dbo.Persons p1;
GO
CREATE VIEW dbo.PersonsView2
WITH SCHEMABINDING
AS
SELECT p2.PersonID
    , p2.FirstName
    , p2.LastName 
    , LastNameInitial = LEFT(p2.LastName, 1)
FROM dbo.Persons p2;
GO
CREATE UNIQUE CLUSTERED INDEX CX_PersonsView
ON dbo.PersonsView(PersonID);
CREATE NONCLUSTERED INDEX IX_PersonsView_LastNameInitial
ON dbo.PersonsView(LastNameInitial)
INCLUDE (FirstName, LastName);

CREATE UNIQUE CLUSTERED INDEX CX_PersonsView2
ON dbo.PersonsView2(PersonID);
CREATE NONCLUSTERED INDEX IX_PersonsView2_LastNameInitial
ON dbo.PersonsView2(LastNameInitial)
INCLUDE (FirstName, LastName);

CREATE STATISTICS ST_PersonsView_001
ON dbo.PersonsView(LastName);

CREATE STATISTICS ST_PersonsView2_001
ON dbo.PersonsView2(LastName);

다음은 몇 가지 샘플 데이터를 삽입합니다.

INSERT INTO dbo.Persons(FirstName, LastName)
VALUES ('Max', 'Vernon')
    , ('Joe', 'Black');

INSERT INTO dbo.Persons2(FirstName, LastName)
VALUES ('Max', 'Vernon')
    , ('Joe', 'Black');

SELECT쿼리 는 다음과 같습니다 .

SELECT *
FROM dbo.PersonsView pv1
    INNER JOIN dbo.PersonsView2 pv2 ON pv1.LastNameInitial = pv2.LastNameInitial;

그리고 결과 :

+ ---------- + ----------- + ------------ + ----------------- -+ ---------- + ----------- + ------------ + --------------- ---- +
| PersonID | 이름 | 성 | 성 이니셜 | PersonID | 이름 | 성 | 성 이니셜 |
+ ---------- + ----------- + ------------ + ----------------- -+ ---------- + ----------- + ------------ + --------------- ---- +
| 2 | 조 | 블랙 | B | 2 | 조 | 블랙 | B |
| 1 | 최대 | 버논 | V | 1 | 최대 | 버논 | V |
+ ---------- + ----------- + ------------ + ----------------- -+ ---------- + ----------- + ------------ + --------------- ---- +

테이블 당 2 개의 행만있는 실행 계획 (행이 많지 않음)

여기에 이미지 설명을 입력하십시오


11

lastname열이 하나 이상의 테이블에서 색인화 되면 다음을 사용할 수도 있습니다.LIKE

SELECT *
FROM   persons p
       INNER JOIN persons2 p2
               ON p2.lastname LIKE LEFT(p.lastname, 1) + '%' 

여기에 이미지 설명을 입력하십시오

이에 대한 계획은 왼쪽에 지정된 테이블을 탐색 할 수 있습니다.

즉 위에 사용 된 ON p.lastname LIKE LEFT(p2.lastname, 1) + '%'인덱스를 사용할 수는 없지만 인덱스를 persons2사용할 있습니다 persons.

그러나 양쪽에서 계산 열을 인덱싱한다는 다른 대답은 더 유연합니다. 중첩 루프 계획의 경우 두 테이블 중 하나가 내부에있을 수 있으며 정렬없이 많은 수의 병합 조인을 허용 할 수도 있습니다.


무엇에 대한 이러한 접근 방식 ? 이점이 있으면 답변에 자유롭게 추가하십시오. 두 테이블 모두에서 인덱스를 사용하고 있습니까? 그렇다면 더 효율적입니까?
ypercubeᵀᴹ

@ ypercubeᵀᴹ 인덱스가 i.stack.imgur.com/RSzcT.png를 다루는 경우 이와 같은 계획을 세울 수 있습니다. 그래도 내 대답에서 계획보다 이점을 보지 못했습니다. 여전히 외부 테이블의 모든 행을 읽을 필요가 있기 때문에 지금은 한 번의 스캔이 아닌 26 개의 탐색을 통해 가능합니다.
Martin Smith

2

에 3,423 개의 행과 195 개의 고유 값이있는 테이블이 Name있습니다. 이 테이블 P(person)을 호출 하고 생성하여 P2(person2) 복제합니다 . 정수 ID 열에는 고유 한 클러스터 된 기본 키가 있습니다. 32GB RAM이있는 Windows 10 Pro 6.3에서 Microsoft SQL Server 2016 (KB3194716) Developer Edition (64 비트)을 사용하고 있습니다.

기본 검색어

select
    p.pid
from dbo.p
inner join dbo.p2 
    on LEFT(p.name, 1) = LEFT(p2.name, 1);

3200-3300ms에 1.5M 개의 행이 반환됩니다 (통계 io에서).

여기에 이미지 설명을 입력하십시오

이렇게 다시 쓰면-

select
    p.pid
from dbo.p
where exists
(
    select 1
    from dbo.p2 
    where LEFT(p.name, 1) = LEFT(p2.name, 1)
);

경과 시간은 50-60ms로 줄어들고 계획은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

일치 알고리즘으로 인해 더 적은 행이 반환됩니다 (3,423). 기본 쿼리를로 변경하여 동일한 계획 및 행 수를 얻을 수 select distinct있습니다.

인덱스 된 계산 열을 생성하여

alter table dbo.p2
add Name1 as Left(Name, 1);

create index ix1 on dbo.p2(Name1);

경과 시간은 45-50ms로 떨어집니다.

여기에 이미지 설명을 입력하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.