Come appiattire i risultati di una tabella con due "molte" tabelle correlate?


9

Ho riorganizzato alcune tabelle nel mio database per renderle più flessibili, ma non sono sicuro di come scrivere l'SQL per estrarre dati significativi da esse.

Ho le seguenti tabelle (in qualche modo abbreviate per un esempio più chiaro):

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

Con i seguenti dati:

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

L'obiettivo è quello di ottenere una query piatta per un prestito con tutti i suoi campi. (Nel mondo reale ci saranno probabilmente tra 20-30 campi per lo stesso schema, ma ne abbiamo solo 2 nell'esempio):

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

Non riesco a usare un perno che fa riferimento a "Nome" e "Cognome" perché non ho idea di cosa ci sarà effettivamente.

Ho un violino SQL qui con lo schema già in atto.

Come posso ottenere il risultato desiderato?

Risposte:


7

Questo può essere fatto usando la funzione PIVOT , ma poiché sembra che tu voglia cambiare la query in base allo schemaId, allora vorrai usare l'SQL dinamico.

Se avessi un numero noto di valori o conoscessi le colonne per uno schemaID specifico, allora potresti codificare la query. Una query statica sarebbe:

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;

Vedi SQL Fiddle with Demo .

Se avevi un numero sconosciuto o vuoi che le colonne cambino in base a una SchemaIdche stai passando a una procedura, allora userai SQL dinamico per generare la stringa 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);

Vedi SQL Fiddle with Demo . Entrambe queste query genereranno il risultato:

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

3

Puoi usare questo modello. Per altri campi dello schema, basta espandere sulla seconda all'ultima riga con tutti i nomi dei campi dello schema.

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;

Se SchemaId = 1rappresenta i campi per il Loantipo, puoi anche generare dinamicamente l'elenco completo schema field namesdell'utilizzo di qualcosa come il seguente.

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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.