SQL Joignez deux tables avec les mêmes noms de colonnes et obtenez la sum de chaque colonne

Je sais qu'à long terme … les noms des colonnes étant les mêmes, cela n'a probablement pas d'importance. Mais voici ce que j'ai:

Table principale:

| PersonID | 1970 | 1971 | 1972 | 1973 | |----------|------|------|------|------| | 1 | 50 | 50 | 100 | 50 | | 2 | 30 | 30 | 40 | 40 | | 3 | 40 | 40 | 40 | 20 | 

Tableau supplémentaire

 | PersonID | 1972 | 1973 | |----------|--------|--------| | 1 | 100 | (null) | | 2 | (null) | (null) | | 3 | (null) | 200 | 

Je voudrais avoir une table (une vue, en fait) qui joindra ces deux tables, et appenda la sum des colonnes dans une colonne avec le même nom

Table combinée:

 | PersonID | 1970 | 1971 | 1972 | 1973 | |----------|------|------|------|------| | 1 | 50 | 50 | 200 | 50 | | 2 | 30 | 30 | 40 | 40 | | 3 | 40 | 40 | 40 | 220 | 

La table résultante doit avoir les deux tables combinées. Les années vont jusqu'en 2017, et sont le même nom pour chaque table.

SQLfiddle

S'il n'y a qu'une seule ligne par id, alors la left join fait:

 select t1.person_id, t1.[1970], t1.[1971], (t1.[1972] + coalesce(t2.[1972], 0)) as [1972], (t1.[1973] + coalesce(t2.[1973], 0)) as [1973] from t1 left join t2 on t1.person_id = t2.person_id 

À less que vous ne choisissiez d'utiliser le SQL dynamic (généralement préférable d'éviter l'injection SQL, les bogues et les impacts négatifs sur les performances), les noms de colonnes équivalents ne nous donnent aucun raccourci ici.

La manière canonique de faire ceci serait de sumr chaque colonne explicitement:

 SELECT mt.PersonID, COALESCE(mt.[1970], 0) + COALESCE(st.[1970], 0), COALESCE(mt.[1971], 0) + COALESCE(st.[1971], 0), ... COALESCE(mt.[2017], 0) + COALESCE(st.[2017], 0) FROM MasterTable mt LEFT JOIN SupplementalTable st ON mt.PersonID = st.PersonID; 

Cependant, ça craint avec 40 colonnes (et ça sonne peut-être plus dans le futur). Le SQL dynamic est généralement préférable d'éviter, mais parfois c'est toujours le meilleur outil pour le travail. Voici comment vous pourriez résoudre le même problème avec SQL dynamic:

 DECLARE @dynamicSql NVARCHAR(MAX); DECLARE @selectList NVARCHAR(MAX); SELECT @selectList = COALESCE(@selectList + ', ', '') + 'COALESCE(mt.[' + mtColumns.COLUMN_NAME + '], 0)' + COALESCE(' + COALESCE(st.[' + stColumns.COLUMN_NAME + '], 0)', '') FROM INFORMATION_SCHEMA.COLUMNS mtColumns LEFT JOIN INFORMATION_SCHEMA.COLUMNS stColumns ON mtColumns.COLUMN_NAME = stColumns.COLUMN_NAME WHERE mtColumns.TABLE_NAME = 'MasterTable' AND stColumns.TABLE_NAME = 'SupplementaryTable'; SET @dynamicSql = 'SELECT ' + @selectList + ' FROM MasterTable mt INNER JOIN SupplementaryTable st ON mt.PersonID = st.PersonID;'; EXECUTE sp_executesql @dynamicSql; 

Je n'ai pas généré les tables réelles pour tester cela, mais cela devrait être assez proche. La requête doit générer une list de sums de champs SELECT pour chaque champ, puis l'exécuter. Il doit également ignorer les champs inexistants dans SupplementaryTable et ne fonctionnera que pour les champs qui existent dans MasterTable .

L'utilisation d'un ORM (comme Hibernate) peut faire beaucoup pour réduire les bogues potentiels ou les vulnérabilités d'injection SQL en validant les noms de colonnes à l'avance.