두 개의 "다수"테이블이있는 테이블의 결과를 평평하게하는 방법은 무엇입니까?


9

데이터베이스의 일부 테이블을보다 유연하게 재구성했지만 의미있는 데이터를 추출하기 위해 SQL을 작성하는 방법을 잘 모르겠습니다.

나는 다음과 같은 테이블을 가지고있다 (더 명확한 예를 위해 약칭 됨).

CREATE TABLE Loans(
    Id int,
    SchemaId int,
    LoanNumber nvarchar(100)
);

CREATE TABLE SchemaFields(
    Id int,
    SchemaId int,
    FieldName nvarchar(255)
);

CREATE TABLE LoanFields(
    Id int,
    LoanId int,
    SchemaFieldId int,
    FieldValue nvarchar(4000)
);

다음 데이터로 :

INSERT INTO Loans (Id, SchemaId, LoanNumber) VALUES (1, 1, 'ABC123');

INSERT INTO SchemaFields (Id, SchemaId, FieldName) VALUES (1, 1, 'First Name');
INSERT INTO SchemaFields (Id, SchemaId, FieldName) VALUES (2, 1, 'Last Name');

INSERT INTO LoanFields (Id, LoanId, SchemaFieldId, FieldValue) VALUES (1, 1, 1, 'John');
INSERT INTO LoanFields (Id, LoanId, SchemaFieldId, FieldValue) VALUES (2, 1, 2, 'Doe');

목표는 모든 필드가 포함 된 대출에 대해 평평한 쿼리를 얻는 것입니다. (실제로 동일한 스키마에 대해 20-30 개의 필드가있을 수 있지만 예제에는 2 개만 있습니다.)

LoanNumber   First Name    Last Name
----------   -----------   ----------
ABC123       John          Doe

실제로 이름이 무엇인지 모를 것이기 때문에 '이름'과 '성'을 참조하는 피벗을 사용할 수 없습니다.

스키마가 이미 있는 SQL Fiddle이 있습니다 .

원하는 결과를 얻으려면 어떻게해야합니까?

답변:


7

PIVOT 함수를 사용하여 수행 할 수 있지만 schemaId를 기반으로 쿼리를 변경하려는 것처럼 들리므로 동적 SQL을 사용하려고합니다.

알려진 수의 값이 있거나 특정 schemaID의 열을 알고 있으면 쿼리를 하드 코딩 할 수 있습니다. 정적 쿼리는 다음과 같습니다.

select loannumber,
  [First Name], 
  [Middle Name], 
  [Last Name]
from
(
  select 
    l.loannumber,
    sf.fieldname,
    lf.fieldvalue
  from loans l
  left join loanfields lf
    on l.id = lf.loanid
  left join schemafields sf
    on lf.schemafieldid = sf.id
    and l.schemaid = sf.schemaid
) src
pivot
(
  max(fieldvalue)
  for fieldname in ([First Name], [Middle Name], [Last Name])
)piv;

Demo with SQL Fiddle을 참조하십시오 .

알 수없는 번호가 있거나 SchemaId프로 시저에 전달한 데이터 에 따라 열을 변경하려는 경우 동적 SQL을 사용하여 SQL 문자열을 생성합니다.

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @schemaId int = 1

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(FieldName) 
                    from SchemaFields 
                    where schemaid = @schemaid
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT loannumber,' + @cols + ' 
            from 
            (
              select 
                l.loannumber,
                sf.fieldname,
                lf.fieldvalue
              from loans l
              left join loanfields lf
                on l.id = lf.loanid
              left join schemafields sf
                on lf.schemafieldid = sf.id
                and l.schemaid = sf.schemaid
              where sf.schemaid = '+cast(@schemaid as varchar(10))+'
            ) x
            pivot 
            (
                max(fieldvalue)
                for fieldname in (' + @cols + ')
            ) p '

execute(@query);

Demo with SQL Fiddle을 참조하십시오 . 이 두 쿼리 모두 결과를 생성합니다.

| LOANNUMBER | FIRST NAME | LAST NAME | MIDDLE NAME |
-----------------------------------------------------
|     ABC123 |       John |       Doe |      (null) |
|     XYZ789 |    Charles |     Smith |         Lee |

3

이 패턴을 사용할 수 있습니다. 더 많은 스키마 필드를 보려면 모든 스키마 필드 이름을 사용하여 두 번째 줄에서 마지막 줄까지 확장하십시오.

select *
  from (
    select l.LoanNumber, s.FieldName, f.FieldValue
      from Loans l
      join Schemafields s on s.SchemaId = l.SchemaId
      join LoanFields f on f.LoanId = l.Id and f.SchemaFieldId = s.Id) p
pivot (
    max(FieldValue) for FieldName in ([First Name], [Last Name])
    ) v;

유형 SchemaId = 1의 필드를 나타내는 경우 아래와 같은 것을 사용하여 Loan전체 목록을 동적으로 생성 할 수도 schema field names있습니다.

declare @sql nvarchar(max) =
    stuff((select ',' + quotename(FieldName)
      from SchemaFields
     where SchemaId = 1
       for xml path(''), type).value('/','nvarchar(max)'),1,1,'');
select @sql = '
select *
  from (
    select l.LoanNumber, s.FieldName, f.FieldValue
      from Loans l
      join Schemafields s on s.SchemaId = l.SchemaId
      join LoanFields f on f.LoanId = l.Id and f.SchemaFieldId = s.Id) p
pivot (
    max(FieldValue) for FieldName in (' + @sql + ')
    ) v';
exec (@sql);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.