Bagaimana cara meratakan hasil tabel dengan dua tabel "banyak" terkait?

9

Saya telah mengatur ulang beberapa tabel dalam database saya menjadi lebih fleksibel tetapi saya tidak begitu yakin bagaimana menulis SQL untuk mengekstrak data yang bermakna dari mereka.

Saya memiliki tabel berikut (agak disingkat untuk contoh yang lebih jelas):

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)
);

Dengan data berikut:

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');

Tujuannya adalah untuk mendapatkan permintaan yang flat untuk pinjaman dengan semua bidangnya. (Di dunia nyata kemungkinan akan ada antara 20-30 bidang untuk skema yang sama, tetapi kita hanya memiliki 2 pada contoh):

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

Saya tidak dapat menggunakan pivot yang merujuk pada 'Nama Depan' dan 'Nama Belakang' karena saya tidak tahu apa yang sebenarnya ada di sana.

Saya punya SQL Fiddle di sini dengan skema yang sudah ada.

Bagaimana saya bisa mendapatkan hasil yang diinginkan?

Judo berlayar
sumber

Jawaban:

7

Ini dapat dilakukan dengan menggunakan fungsi PIVOT , tetapi karena sepertinya Anda ingin mengubah kueri berdasarkan schemaId, maka Anda ingin menggunakan SQL dinamis.

Jika Anda memiliki jumlah nilai yang diketahui atau tahu kolom untuk skema tertentu, maka Anda bisa meng-kodekan kueri. Kueri statis adalah:

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;

Lihat SQL Fiddle dengan Demo .

Jika Anda memiliki nomor yang tidak dikenal atau Anda ingin kolom berubah berdasarkan apa SchemaIdyang Anda lewati ke prosedur, maka Anda akan menggunakan SQL dinamis untuk menghasilkan string 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);

Lihat SQL Fiddle dengan Demo . Kedua pertanyaan ini akan menghasilkan hasilnya:

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

Anda dapat menggunakan pola ini. Untuk bidang skema lainnya, cukup rentangkan pada baris ke-2 hingga terakhir dengan semua nama bidang skema.

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;

Jika SchemaId = 1mewakili bidang untuk Loanjenis, maka Anda juga dapat secara dinamis menghasilkan daftar lengkap schema field namesmenggunakan sesuatu seperti di bawah ini.

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);
孔夫子
sumber