2009-01-28 13 views
1

Je cherche à refactoriser la requête ci-dessous pour quelque chose de plus lisible et modifiable. La première moitié est identique à la deuxième, à l'exception de la base de données interrogée à partir de (noms de table sont les mêmes que.)Comment écrivez-vous cette requête?

SELECT 
    Column 1 AS c1, 
    ... 
    Column N AS cN 
    FROM 
    database1.dbo.Table1 

UNION 

    SELECT 
    'Some String' as c1, 
    ... 
    NULL as cN 
    FROM 
    database1.dbo.Table2 

UNION 

    SELECT 
    Column 1 AS c1, 
    ... 
    Column N AS cN 
    FROM 
    database2.dbo.Table1 

UNION 

    SELECT 
    'Some String' as c1, 
    ... 
    NULL as cN 
    FROM 
    database2.dbo.Table2 

Cette requête est la définition de DRY et me demande à être réécrite mais je ne sais pas comment!

EDIT: Nous ne pouvons pas utiliser linq et nous désirons des résultats distincts; Je cherche à rendre la requête plus petite dans la taille de fichier physique, pas dans les résultats retournés.

EDIT: La base de données que j'interroge est une base de données ERP propriétaire. Restructurer ce n'est pas une option.

+0

Est-ce que vous essayez essentiellement d'exécuter la même requête sur les tables de noms différents où un ou plusieurs champs peuvent exister dans certains les tables mais en grande partie tous les autres champs sont communs à toutes les tables et ensuite se syndiquer [est-ce un mot?] ensemble? – BenAlabaster

+0

@balabaster - Oui, c'est exactement ce que je fais. –

Répondre

2

Ceci est un modèle SQL assez standard. Parfois, il est facile de transférer les principes du code OOP/Procedural comme DRY vers SQL, mais ce ne sont pas nécessairement des concepts transférables.

Notez à quel point vous pouvez facilement faire glisser l'intégralité de la conception logique de la requête, par rapport à la recherche dans les sous-modules. Si l'une des sous-expressions avait une colonne supplémentaire, ou des colonnes inversées, cela ressortirait. Ceci est fondamentalement une instruction SQL assez simple à grok comme une unité d'exécution, où la désagréger la brouillerait. Lorsque vous déboguez, il est utile de pouvoir utiliser l'option de mise en surbrillance du texte de l'éditeur pour exercer sélectivement des parties de l'instruction, une technique qui n'existe pas dans le code de procédure. OTOH, ça peut devenir désordonné d'essayer de retracer toutes les pièces si elles sont dispersées dans les vues, etc. Même les CTE peuvent rendre cela incommode.

2

Un conseil de performance que je vois d'emblée utilise UNION ALL au lieu de UNION sauf si vous voulez intentionnellement des enregistrements distincts. Un simple UNION permettra d'éliminer les doublons, ce qui prend du temps. UNION ALL ne fait pas cela.

Vous pouvez le réécrire avec SQL dynamique et une boucle mais je pense que le résultat serait pire. S'il y a suffisamment de code en double pour justifier l'approche SQL dynamique, alors je suppose que cela pourrait être justifié.

Vous avez également envisagé de déplacer la logique de la procédure stockée vers quelque chose comme LINQ? Pour beaucoup, ce n'est pas une option donc je demande juste.

Une note finale: résistez à l'envie de réparer ce qui n'est pas cassé juste pour le faire paraître plus propre. Si le nettoyage aide à la maintenance, la vérification, etc., alors allez-y.

3

Je vais sortir d'ici et dire, en fonction des informations que vous nous avez fournies;

C'est aussi bon que ça va se

+0

Y a-t-il autre chose que je peux fournir? –

+0

@ LFSR-I parlait des hypothèses générales que nous pouvons faire à partir du code. Mais non, je doute que plus d'infos serait d'une grande aide. Je parie que cela ne vaut pas la peine, mais il semble presque qu'ils ont des besoins d'affaires, ce que le modèle de données actuel ne correspond pas. Refactoriser la base de données 'peut' être en ordre. –

+0

Peut être en ordre, mais pas probable (et pas facilement imaginé dans un système d'entreprise typique.) – dkretz

1

Quel est le problème? Trop long? Trop répétitif?

Parfois, vous obtenez SQL laide - pas grand-chose que vous pouvez faire à ce sujet.

Je ne vois aucun moyen de le nettoyer, sauf si vous voulez utiliser des vues séparées, puis les unir ensemble.

+0

True - SQL est rarement joli. –

+0

Trop répétitif. C'est juste la même chose 4 fois. Et tous nos sprocs ressemblent à ça. –

+0

Je ne sais pas ... Je peux voir la beauté dedans. J'ai écrit du SQL génialement génial ... des pages longues ... et ça m'a l'air élégant. –

1

Je vote pour les vues, qui imposent un temps d'attente proche de zéro (OK, peut-être un petit coût de compilation mais cela devrait être tout). Ensuite, vos procs deviennent quelque chose de la forme

SELECT * FROM database1.view1 
UNION 
SELECT * FROM database1.view2 
UNION 
SELECT * FROM database2.view1 
UNION 
SELECT * FROM database2.view2 

Je ne sais pas si je veux condenser plus loin, bien que je pense la plupart des plates-formes ne tolérer.

+0

Ce qui augmente la complexité, malheureusement. Si vous changez l'énoncé, vous finissez par avoir besoin de le changer à 5 endroits, pas à 1. – dkretz

+0

D'accord - bien qu'il puisse y avoir une certaine portée, une fois que nous avons extrait des éléments plus petits et plus «cohésifs» (si nous pouvons utiliser ce terme à SQL) il peut être possible d'utiliser une sorte de génération de code pour gérer les vues et amener le lieu de changement à un endroit. Juste la spéculation. –

0

Sur le thème Dynamic SQL - voici un exemple - je ne sais pas si c'est mieux. L'avantage est que vous n'avez qu'à écrire la liste SELECT une fois.

DECLARE @Select1 varchar(1000) 
DECLARE @Select2 varchar(1000) 

DECLARE @SQL varchar(4000) 


SET @Select1 = 'SELECT 
    Column 1 AS c1, 
    ... 
    Column N AS cN' 


SET @Select2 = 'SELECT 
    ''Some String'' as c1, 
    ... 
    NULL as cN' 


SET @SQL = @Select1 + ' FROM database1.dbo.Table1 ' 

SET @SQL = @SQL + ' UNION ' + @Select2 + ' FROM database1.dbo.Table2 ' 

SET @SQL = @SQL + ' UNION ' + @Select1 + ' FROM database2.dbo.Table1 ' 

SET @SQL = @SQL + ' UNION ' + @Select2 + ' FROM database2.dbo.Table2 ' 


EXEC @SQL 
0

Si tous vos procs ressemblent à ceci - vous avez probablement un problème d'architecture.

Vos appels à table2 ont-ils tous un seul champ utile? (et à cause de UNION, finissent par avoir juste une rangée?Je souscris totalement à l'idée d'aller avec le SQL dynamique paramétré et/ou la génération de code pour ce travail, allant même jusqu'à générer la liste des colonnes dynamiquement en utilisant INFORMATION_SCHEMA. Ce n'est pas exactement ce dont vous avez besoin, mais il est un début (vous pouvez générer sur une table de bases de données et tables):

DECLARE @template AS varchar(MAX) 
SET @template = 'SELECT {@column_list} FROM {@database_name}.dbo.{@table_name}' 
DECLARE @column_list AS varchar(MAX) 

SELECT @column_list = COALESCE(@column_list + ',', '') + COLUMN_NAME 
FROM database1.dbo.INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_NAME = @table_name 
ORDER BY ORDINAL_POSITION 

DECLARE @sql AS varchar(MAX) 
SET @sql = @template 
SET @sql = REPLACE(@sql, '{@column_list}', @column_list) 
SET @sql = REPLACE(@sql, '{@database_name}', @database_name) 
SET @sql = REPLACE(@sql, '{@table_name}', @table_name) 
+0

Il s'agit probablement d'un problème d'architecture DB. Nous utilisons un ERP propriétaire. Leur structure de base de données est médiocre lors d'une bonne journée, suicidaire lors d'une mauvaise journée. –

+0

Si la requête est coûteuse en termes d'optimisation, cette approche élimine la possibilité d'un plan de requête stocké. Ça pourrait être une bonne chose, je ne sais pas. Peut-être que la génération de code pourrait se produire en dehors de la proc sous le contrôle d'un processus de construction automatisé? –

+0

À droite, vous pouvez toujours générer du code pour vos SP - traiter les modèles comme le code réel et tout ce qui est généré par le code comme une simple sortie de votre processus de développement. –

0

En fonction du nombre de lignes renvoyées, mieux vaut utiliser UNION ALL sur les sélections avec une requête de sélection distincte. Je l'ai vu un problème similaire avant et avait différents plans d'exécution pour les deux styles différents

 
SELECT DISTINCT subquery.c1, subquery.cN 
FROM 
(
SELECT Column 1 AS c1, Column N AS cN FROM database1.dbo.Table1 
UNION ALL 
SELECT 'Some String' as c1, NULL as cN FROM database1.dbo.Table2 
UNION ALL 
SELECT Column 1 AS c1, Column N AS cN FROM database2.dbo.Table1 
UNION ALL 
SELECT 'Some String' as c1, NULL as cN FROM database2.dbo.Table2 
) subquery 
Questions connexes