Wie kann man die Ergebnisse einer Tabelle mit zwei verwandten "vielen" Tabellen reduzieren?

9

Ich habe einige Tabellen in meiner Datenbank neu organisiert, um flexibler zu sein, bin mir aber nicht sicher, wie ich SQL schreiben soll, um aussagekräftige Daten daraus zu extrahieren.

Ich habe die folgenden Tabellen (für ein klareres Beispiel etwas abgekürzt):

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

Mit folgenden Daten:

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

Ziel ist es, eine flache Anfrage für ein Darlehen mit all seinen Feldern zu erhalten. (In der realen Welt wird es wahrscheinlich zwischen 20 und 30 Felder für dasselbe Schema geben, aber wir haben nur 2 im Beispiel):

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

Ich kann keinen Pivot verwenden, der auf 'Vorname' und 'Nachname' verweist, da ich keine Ahnung habe, was tatsächlich dort sein wird.

Ich habe hier eine SQL-Geige mit bereits vorhandenem Schema.

Wie kann ich das gewünschte Ergebnis erzielen?

Segeln Judo
quelle

Antworten:

7

Dies kann mit der PIVOT- Funktion erfolgen. Da Sie jedoch die Abfrage basierend auf der schemaId ändern möchten, sollten Sie dynamisches SQL verwenden.

Wenn Sie eine bekannte Anzahl von Werten hatten oder die Spalten für eine bestimmte Schema-ID kannten, konnten Sie die Abfrage fest codieren. Eine statische Abfrage wäre:

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;

Siehe SQL Fiddle mit Demo .

Wenn Sie eine unbekannte Nummer hatten oder möchten, dass sich die Spalten basierend auf einer von SchemaIdIhnen an eine Prozedur übergebenen ändern , verwenden Sie dynamisches SQL, um die SQL-Zeichenfolge zu generieren:

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

Siehe SQL Fiddle mit Demo . Diese beiden Abfragen erzeugen das Ergebnis:

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

Sie können dieses Muster verwenden. Für weitere Schemafelder erweitern Sie einfach die vorletzte Zeile mit allen Schemafeldnamen.

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;

Wenn dies SchemaId = 1die Felder für den LoanTyp darstellt, können Sie auch dynamisch die vollständige Liste der folgenden schema field namesVerwendungszwecke generieren .

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