2013-02-25 1 views
1

예를 들어, 학생들이 최대 10 개의 다른 테스트를 수행 할 수있는 연구를 수행 중이며 데이터베이스의 각 테이블에 하나의 테스트에 대한 모든 학생의 응답이 저장되어 있다고 가정합니다. 테이블의 이름은 각 테스트 후에 T1, T2, ..., T10으로 지정됩니다. 각 테이블에 각 학생을 식별하는 기본 키 열 '사용자 이름'이 있다고 가정합니다. 학생들은 각 시험을 완료했거나 완료하지 않았을 수 있으므로 각 학생마다 각 테이블에 기록이 있거나 없을 수 있습니다.일치하는 행이 있거나 없을 수도있는 여러 테이블의 데이터를 병합하는 TSQL 쿼리?

학생 당 한 행 (사용자 이름 당 한 행)이있는 모든 테이블의 모든 테스트 데이터를 반환하는 올바른 SQL 쿼리는 무엇입니까? 올바른 결과를 반환하는 가장 간단한 쿼리를 원합니다. 또한 최종 쿼리에서 Username 필드를 단일 Username 필드로 통합하려고합니다.

내가 SQL이 같은 하나 이상의 필드를 제외한 모든 열 을 선택 구문을 지원하지 않는 주요 제한에 가지고 이해 명확히하기 위해 "선택 * [^ ExcludeColumn1] [^ ExcludeColumn2]". 마지막 쿼리에서 모든 열의 이름을 지정하지 않으려면 RowID와 같은 이름의 시작 부분에 통합 된 사용자 이름 필드가 포함되어 있으면 모든 사용자 이름 열을 그대로 두는 것이 좋습니다.

전체 쿼리의 경우, 하나의 옵션은 모든 열 테이블의 사용자 이름 열에 대해 모두 유니온을 수행 한 다음 모든 테이블에서 고유 한 사용자 이름을 선택한 다음 고유 한 사용자 이름 목록에 대해 일련의 왼쪽 조인을 수행하는 것입니다 10 개의 테이블 모두에. 이렇게하면 각 왼쪽 가입이 동일한 별개의 사용자 이름 집합에서 수행되는 매우 간단한 쿼리가 발생하지만 별개의 사용자 이름에 대해 별도의 초기 쿼리를 사용하지 않아도됩니다. (그것이 최선의 선택이라면 알려주세요). 그것은 다음과 같이 보일 것이다 : 그 짧고 쉽게 작성할 수 있지만 그것은 매우 비효율적이다,

select * from 
(select distinct coalesce(t1.Username,t2.Username,...,t10.Username) as RowID from t1,t2,t3,t4,t5,t6,t7,t8,t9,t10) distinct_usernames 
left join t1 on t1.Username = distinct_usernames.RowID 
left join t2 on t2.Username = distinct_usernames.RowID 
... 
left join t10 on t10.Username = distinct_usernames.RowID 

을하고, 5000 개 행을 각각 테스트 테이블에서 실행하는 데 시간이 걸릴 것입니다 조정, 동등한 버전 그래서

select * from (
select distinct Username as RowID from (
select Username from t1 
union all 
select Username from t2 
union all 
... 
select Username from t10 
) all_usernames) distinct_usernames 
left join t1 on t1.Username = distinct_usernames.RowID 
left join t2 on t2.Username = distinct_usernames.RowID 
... 
left join t10 on t10.Username = distinct_usernames.RowID 

나는 내가 무엇을 위해야하는 것이 가장 효율적이고 정확한 쿼리를 할 수 있다는 생각은 (단지 몇 실행 초 반환 정확한 결과 집합 소요), 그러나 나는 또한 아마 생각 : 그 몇 초에서 실행하는 것입니다 그것은 일종의 완전 결합으로 단순화 될 수 있습니다. 문제는 사용자 이름을 미리 결정하지 않고 각각의 후속 테이블이 에 해당하는 이전 테이블의에 대한 레코드와 일치해야하기 때문에 전체 조인이 2 개 이상의 테이블과 혼동을 일으킬 수 있다는 것입니다. 추가 테이블마다 "[[ 이전 테이블 수] + 1 "사용자 이름 일치 조건.

+0

SQL 데이터베이스 엔진 튜닝 어드바이저를 사용하여 얼마나 효율적으로 얻을 수 있는지 봅니다. 원하는 결과를 쿼리 할 수있는 다른 방법이 없기 때문에 최적화에 집중해야 할 수도 있습니다. – Narnian

+0

그래, 아마 최적이라고 생각했는데 Tim이 게시 한 것처럼 '노조'를 사용하여 '모든'및 '고유 한'절을 제거 할 수있었습니다. 내 생각에 몇 가지 알려진 ID 집합에 여러 (불균형) 테이블을 병합하는 것은 매우 일반적인 구문으로 매우 일반적인 상황이었습니다. 이런 종류의 연산은 SQL에서 "select * from merge (t1, t2, t3, t4, t5) on [PrimaryKeyColName]"과 같은 SQL 구문을 사용할 수 있습니다. 이는 전체 조인이 실용적이지 않고 일련의 왼쪽 조인 최적이지만 고유 ID의 전체 목록에 조인 할 때만 올바르게 작동합니다. – Triynko

답변

2

, 두 번째 쿼리가 아니라 union all보다 distinct을 제거하고 간단하게 (별개의 의미) union를 사용하는 약간의 수정과 함께, 내가 먼저 시도 할 방법이있을 것 Username 각 테이블에서 고유한지 가정 :

select * 
from (
     select Username from t1 
     union 
     select Username from t2 
     union 
     -- ... 
     select Username from t10 
    ) distinct_usernames 
    left join t1 on t1.Username = distinct_usernames.Username 
    left join t2 on t2.Username = distinct_usernames.Username 
    -- ... 
    left join t10 on t10.Username = distinct_usernames.Username 

거기에서 Username이 색인되어 있는지 확인하고 가능하면 clustered index으로 사용하는 것입니다. 나는 또한 당신의 distinct_usernames을 임시 테이블 (인덱싱 된 뷰 또는 인덱싱 된 뷰)로 구현함으로써 과거에는 최적화 된 행운을 얻었지만, 테스트를 통해서만 가치가 있는지 판단 할 수있었습니다.

전체 외부 조인은 조건 또는 coalesce 인수를 필요로하지만 성능이 있는지를보기 위해 약간의 테이블에서 시도해 볼 가치가 있습니다. 쿼리 엔진이 무엇을 가장 좋아하는지 추측 할 수는 없습니다.

또한, 당신이 원하는 점점 단지 열 이름은 sys.columns 또는 information_schema.columns에 쿼리와 그 실행을 다음 문자열로 쿼리를 구축하는 dynamic SQL를 사용하여 수행 할 수 있습니다.

+0

"union"자체는 기본적으로 별개이므로 "all"을 생략하고 'distinct'쿼리를 래핑 할 수 있습니다. 왼쪽 조인은 전체 조인으로 수행 할 수있는 것보다 더 간단하고 효율적이라고 동의합니다. 이미 사용자 이름을 기본 키로 설정 (클러스터 됨) 했으므로 상당히 속도가 빨라진다는 것을 알았 기 때문에. 무슨 일이 있었는지 상사가 나에게 3 개의 Excel 파일을 주었고 SQL Server에서 사용자 이름을 가져 와서 병합하도록 요청했기 때문에 실제 사용자 데이터베이스가 아닌 해당 파일에 있던 고유 사용자 목록을 기반으로해야했습니다. – Triynko

+0

Gotcha, 당신은 그곳에 꽤 많이있었습니다. 실제로 성능을 향상시킨 것이 있습니까? –

+0

예, 두 가지 경우 모두 쿼리를 실행하는 데 약 3 초 밖에 걸리지 않으므로 눈에 not 정도는 아닙니다. 쿼리 계획을 보면 "유니온"을 단독으로 사용하면 단일 "병합 병합 (결합)"이 "스트림 집계 (집계) 및 병합 조인 (병합)"대신 사용됩니다. 또한 병합 조인을 병합에서 연합으로 전환합니다. 전후에 대한 계획은 여기 http://i.imgur.com/UbrOqCw.png를 참조하십시오. – Triynko