Comment aplatir les résultats d'un tableau avec deux «plusieurs» tableaux associés?

9

J'ai réorganisé certaines tables dans ma base de données pour être plus flexible, mais je ne sais pas vraiment comment écrire le SQL pour en extraire des données significatives.

J'ai les tableaux suivants (quelque peu abrégés pour un exemple plus clair):

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

Avec les données suivantes:

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'objectif est d'obtenir une requête plate pour un prêt avec tous ses champs. (Dans le monde réel, il y aura probablement entre 20 et 30 champs pour le même schéma, mais nous n'en avons que 2 dans l'exemple):

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

Je ne peux pas utiliser un pivot qui fait référence au «Prénom» et au «Nom de famille» car je n'aurai aucune idée de ce qui sera réellement là.

J'ai un SQL Fiddle ici avec un schéma déjà en place.

Comment puis-je obtenir le résultat souhaité?

Judo à voile
la source

Réponses:

7

Cela peut être fait à l'aide de la fonction PIVOT , mais comme il semble que vous souhaitiez modifier la requête en fonction de schemaId, vous souhaiterez utiliser SQL dynamique.

Si vous aviez un nombre connu de valeurs ou connaissiez les colonnes pour un schemaID spécifique, vous pouvez coder en dur la requête. Une requête statique serait:

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;

Voir SQL Fiddle avec démo .

Si vous aviez un nombre inconnu ou si vous souhaitez que les colonnes changent en fonction de SchemaIdcelle que vous passez dans une procédure, vous utiliserez du SQL dynamique pour générer la chaîne 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);

Voir SQL Fiddle avec démo . Ces deux requêtes généreront le résultat:

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

Vous pouvez utiliser ce modèle. Pour plus de champs de schéma, développez simplement sur la 2ème à la dernière ligne avec tous les noms de champ de schéma.

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;

Si SchemaId = 1représente les champs du Loantype, vous pouvez également générer dynamiquement la liste complète de l' schema field namesutilisation de quelque chose comme ci-dessous.

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