2009-06-18 7 views
3

내가가 subselect를 사용하여 결과 집합에 열을 추가 SQL 쿼리 (MSSQLSERVER)를 보내고 있습니다 :SQL : 선택적 하위 쿼리

SELECT P.name, 
(select count(*) from cars C where C.type = 'sports') AS sportscars, 
(select count(*) from cars C where C.type = 'family') AS familycars, 
(select count(*) from cars C where C.type = 'business') AS businesscars 
FROM people P 
WHERE P.id = 1; 

쿼리 위의 조금의 넌센스 테스트 설정에서이지만, 내가 생각하는 것처럼 충분히 도움이된다. 내가 실제로 작업하고있는 쿼리는 복잡한 문제를 다루는 여러 테이블에 걸쳐 있습니다.

위의 예제에서 "people"테이블의 각 레코드에는 "wantsSportscar", "wantsFamilycar"및 "wantsBusinesscar"라는 세 개의 추가 열도 있습니다. 이제 people 테이블의 각각의 "wants ....."필드가 "true"로 설정된 경우 각 추가 열의 subselect를 수행하기 만하면됩니다. 즉, 특정 개인에 대해 P.wantsSportscar가 true로 설정된 경우에만 첫 번째 subselect를 수행하려고합니다. 두 번째 및 세 번째 subselect는 비슷한 방식으로 작동해야합니다.

그래서이 쿼리는 특정 사람의 이름과 소유하고자하는 자동차 종류에 사용할 수있는 모델 수를 보여줍니다. 최종 결과에는 항상 단일 레코드, 즉 특정 사용자의 레코드 만 포함된다는 점에 유의할 가치가 있습니다.

사람이 특정 유형의 자동차에 관심이 없으면 해당 유형의 열이 최종 결과 집합에 포함되지 않는 것이 중요합니다. 이 확실한 예 :

사람이 스포츠카와 가족차를 원하면 결과에 "이름", "스포츠카"및 "가족"열이 포함됩니다.

사람이 B를 원하는 경우, 결과에는 "name"과 "businesscar"열이 포함됩니다.

IF, CASE 및 EXISTS 문과 함께 다양한 조합을 사용하려고했지만 구문 적으로 올바른 솔루션을 얻을 수 없었습니다. 누구라도 이것이 가능한지 알고 있습니까? 쿼리는 저장 프로 시저에 저장됩니다.

답변

5

가능한 경우 8 열 레이아웃이 있으며이 작업을 수행하려면 8 개의 별도 쿼리가 필요하며 동적으로 쿼리를 작성해야합니다.

단일 쿼리 내에서 결과 집합 레이아웃을 변경할 수 없습니다. 다음과 같이

대신에, 당신은 당신의 쿼리를 디자인 할 수 있습니다

SELECT P.name, 
     CASE WHEN wantssport = 1 THEN (select count(*) from cars C where C.type = 'sports') ELSE NULL END AS sportscars, 
     CASE WHEN wantsfamily = 1 THEN (select count(*) from cars C where C.type = 'family') ELSE NULL END AS familycars, 
     CASE WHEN wantsbusiness = 1 THEN (select count(*) from cars C where C.type = 'business') ELSE NULL END AS businesscars 
FROM people P 
WHERE P.id = 1 

을 한 사람이 그것을 원하지 않을 경우 해당 열에서 NULL을 선택하고 클라이언트 측에서 이러한 NULL 's을 (를) 구문 분석한다.

관계형 모델은 relations으로 쿼리에 응답합니다.

당신의 경우에 관계는 다음과 같습니다 : "이 사람은이 많은 스포츠카,이 많은 비즈니스 차량과이 많은 가족 차량으로 만족합니다."

관계형 모델은 항상이 특정 질문에 4 진 관계로 응답합니다.

관계 구성원 중 일부를 생략하지 않고 대신 NULL으로 설정합니다. 이는 관계 구성원이 정의되지 않았거나 적용 가능하거나 의미가 없음을 나타내는 SQL의 방법입니다.

+0

나는 이것이 내 문제를 해결했다고 생각한다. (ChrisCM의 답변에 대한 나의 코멘트를 보라). 이 솔루션을 사용하면 프로그래밍 코드에서 다른 열의 값을 확인하고 적절한 조치를 취할 수 있습니다. 그것은 나를 위해 충분하다. 감사. – Jerry

0

저는 주로 오라클 사람이지만 동일한 가능성이 있습니다. 내가 오해 해 본 적이 없다면, 당신이 원하는 것은 그 수준에서 가능하지 않습니다. 당신은 항상 정적 인 수의 컬럼을 가질 것입니다. 쿼리를 통해 열이 비어 있는지 여부를 제어 할 수 있지만 쿼리의 가장 바깥 쪽 부분에 X 열을 지정 했으므로 결과 집합에서 X 열을 가져올 수 있습니다.

필자는 MS SQL Server에 익숙하지 않지만 동적 SQL을 실행하는 방법이있을 것이라고 생각합니다.이 경우 유연한 쿼리를 작성할 수 있어야하므로 연구해야합니다.

+0

좋아, 열 고정 번호를 합리적인 소리. 왜냐하면 컬럼이 존재할 수 있기 때문에 이것은 생각하게 만들지 만 관련 "want ...."컬럼이 false로 설정되면 결과 세트의 컬럼에 특정 값을 넣을 수 있습니다. "want ...."필드의 값을 기준으로 열을 거기에 두지 만 값을 -1 또는 이와 비슷한 값으로 설정할 수 있습니까? – Jerry

0

먼저 값을 임시 테이블로 별도의 행으로 선택한 다음 해당 테이블에서 PIVOT을 수행하여 행을 열로 바꿔서 원하는 작업을 수행 할 수 있습니다.

0

그것은 중요합니다 그 사람이 아닌 경우 해당 유형의 열이 최종 결과 집합에 포함되지 것 자동차의 특정 유형, 에 관심이 . 예 :

평이한 SQL에서는이 작업을 수행 할 수 없으므로 확실하지 않으려면 예제를 사용하십시오. 그냥이 열을 NULL 또는 ZERO로 설정하는 것이 좋습니다.

새 자동차를 추가 할 때 쿼리가 동적으로 확장되도록하려면 PIVOTing이 도움이 될 수 있습니다.

0

이 작업을 쉽게하기 위해 배우고 싶은 세 가지 기본 요소가 있습니다. 첫 번째는 데이터 정규화이고 두 번째는 GROUP BY이며 세 번째는 PIVOT입니다.

먼저 데이터 정규화. 사람 테이블의 디자인이 첫 번째 정규 양식이 아닙니다. 컬럼 "wantsports", "wantfamily", "wantbusiness"는 실제로는 반복 그룹이지만, 마치 하나처럼 보이지 않을 수도 있습니다. 테이블 디자인을 수정할 수 있다면 세 번째 테이블을 만드는 것이 유리하며, personid와 cartype이라는 두 개의 키 열이있는 "peoplewant"라고 부를 수 있습니다. 원한다면이 디자인이 왜 더 유연하고 강력 해지는 지 자세히 설명 할 수 있지만 지금은 건너 뛸 것입니다.

켜짐 GROUP BY. 결과의 한 행에 각 그룹을 요약하는 결과를 생성 할 수 있습니다.

SELECT 
    p.name, 
    c.type, 
    c.count(*) as carcount 
FROM people p, 
    INNER JOIN peoplewant pw ON p.id = pw.personid 
    INNER JOIN cars c on pw.cartype = c.type 
WHERE 
    p.id = 1 
GROUP BY 
    p.name, 
    c.type 

결과가 사용자가 원하는 각 자동차 유형에 대해 별도의 행이 있다는 것을 제외하면이 (테스트되지 않은) 쿼리는 원하는 결과를 제공합니다.

마지막으로 PIVOT. DBMS에있는 PIVOT 도구를 사용하면이 결과를 사람의 행이 하나있는 양식으로 바꿀 수 있으며 해당 사람이 원했던 각 카피 유형에 대해 별도의 열이 있습니다. 나는 PIVOT을 직접 사용하지 않았으므로 다른 사람이 PIVOT을 사용하여 예제를 제공하도록이 응답을 편집하게 할 것입니다.

같은 기술을 사용하여 여러 사람의 데이터를 한 번에 검색하는 경우 원하는 사람이 원하는 유형별로 열이 표시되고 그렇지 않은 사람의 경우 PIVOT 결과에 0이 표시된다는 점에 유의하십시오 결과 열에있는 자동차 유형을 원합니다.

0

그냥 구글 검색을 통해이 게시물을 가로 질러 왔어, 그래서 조금 늦게이 파티에 알지만 ... 이건 정말 할 수 있습니다 ...하지만, 나는 제안하지 않을거야 일반적으로 매우 나쁜 일 (tm)로 간주되기 때문에 실제로 이렇게하는 것입니다.

동적 SQL이 사용자의 대답입니다.

내가 어떻게 해야할지 말하기 전에 동적 SQL은 응용 프로그램에서 입력을 위생적으로 처리하지 않으면 매우 위험합니다.

그래서, 그러므로, 신중하게 진행 :

declare @sqlToExecute nvarchar(max); 
declare @includeSportsCars bit; 
declare @includeFamilyCars bit; 
declare @includeBusinessCars bit; 

set @includeBusinessCars = 1 
set @includeFamilyCars = 1 
set @includeSportsCars = 1 

set @sqlToExecute = 'SELECT P.name ' 

if @includeSportsCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''sports'') AS sportscars, '; 
if @includeFamilyCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''family'') AS familycars, '; 
if @includeBusinessCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''business'') AS businesscars ' 

set @sqlToExecute = @sqlToExecute + ' FROM people P WHERE P.id = 1;'; 

exec(@sqlToExecute)