Avertissement: veuillez me supporter en tant que personne qui n'utilise les bases de données qu'une infime fraction de son temps de travail. (La plupart du temps, je fais de la programmation C ++ dans mon travail, mais tous les mois impairs, je dois rechercher / corriger / ajouter quelque chose dans une base de données Oracle.)
J'ai eu à plusieurs reprises besoin d'écrire des requêtes SQL complexes, à la fois pour les requêtes ad hoc et pour les requêtes intégrées dans les applications, où de grandes parties des requêtes venaient de répéter du "code".
L'écriture de telles abominations dans un langage de programmation traditionnel vous causerait de graves problèmes, mais je ( I ) n'ai pas encore pu trouver de technique décente pour empêcher la répétition du code de requête SQL.
Edit: 1er, je tiens à remercier les répondeurs qui ont apporté d'excellentes améliorations à mon exemple d' origine . Cependant, cette question ne concerne pas mon exemple. Il s'agit de répétitivité dans les requêtes SQL. En tant que telles, les réponses ( JackP , Leigh ) font jusqu'à présent un excellent travail pour montrer que vous pouvez réduire la répétitivité en écrivant de meilleures requêtes . Cependant, même alors, vous faites face à une répétitivité qui ne peut apparemment pas être supprimée: cela m'a toujours harcelé avec SQL. Dans les langages de programmation "traditionnels", je peux refactoriser beaucoup pour minimiser la répétitivité dans le code, mais avec SQL, il semble qu'il n'y ait pas d'outils (?) Qui permettent cela, sauf pour écrire une déclaration moins répétitive pour commencer.
Notez que j'ai à nouveau supprimé la balise Oracle, car je serais vraiment intéressé s'il n'y a pas de base de données ou de langage de script qui permet quelque chose de plus.
Voici un tel joyau que j'ai bricolé ensemble aujourd'hui. Il signale essentiellement la différence dans un ensemble de colonnes d'une seule table. Veuillez parcourir le code suivant, en particulier. la grande requête à la fin. Je vais continuer ci-dessous.
--
-- Create Table to test queries
--
CREATE TABLE TEST_ATTRIBS (
id NUMBER PRIMARY KEY,
name VARCHAR2(300) UNIQUE,
attr1 VARCHAR2(2000),
attr2 VARCHAR2(2000),
attr3 INTEGER,
attr4 NUMBER,
attr5 VARCHAR2(2000)
);
--
-- insert some test data
--
insert into TEST_ATTRIBS values ( 1, 'Alfred', 'a', 'Foobar', 33, 44, 'e');
insert into TEST_ATTRIBS values ( 2, 'Batman', 'b', 'Foobar', 66, 44, 'e');
insert into TEST_ATTRIBS values ( 3, 'Chris', 'c', 'Foobar', 99, 44, 'e');
insert into TEST_ATTRIBS values ( 4, 'Dorothee', 'd', 'Foobar', 33, 44, 'e');
insert into TEST_ATTRIBS values ( 5, 'Emilia', 'e', 'Barfoo', 66, 44, 'e');
insert into TEST_ATTRIBS values ( 6, 'Francis', 'f', 'Barfoo', 99, 44, 'e');
insert into TEST_ATTRIBS values ( 7, 'Gustav', 'g', 'Foobar', 33, 44, 'e');
insert into TEST_ATTRIBS values ( 8, 'Homer', 'h', 'Foobar', 66, 44, 'e');
insert into TEST_ATTRIBS values ( 9, 'Ingrid', 'i', 'Foobar', 99, 44, 'e');
insert into TEST_ATTRIBS values (10, 'Jason', 'j', 'Bob', 33, 44, 'e');
insert into TEST_ATTRIBS values (12, 'Konrad', 'k', 'Bob', 66, 44, 'e');
insert into TEST_ATTRIBS values (13, 'Lucas', 'l', 'Foobar', 99, 44, 'e');
insert into TEST_ATTRIBS values (14, 'DUP_Alfred', 'a', 'FOOBAR', 33, 44, 'e');
insert into TEST_ATTRIBS values (15, 'DUP_Chris', 'c', 'Foobar', 66, 44, 'e');
insert into TEST_ATTRIBS values (16, 'DUP_Dorothee', 'd', 'Foobar', 99, 44, 'e');
insert into TEST_ATTRIBS values (17, 'DUP_Gustav', 'X', 'Foobar', 33, 44, 'e');
insert into TEST_ATTRIBS values (18, 'DUP_Homer', 'h', 'Foobar', 66, 44, 'e');
insert into TEST_ATTRIBS values (19, 'DUP_Ingrid', 'Y', 'foo', 99, 44, 'e');
insert into TEST_ATTRIBS values (20, 'Martha', 'm', 'Bob', 33, 88, 'f');
-- Create comparison view
CREATE OR REPLACE VIEW TA_SELFCMP as
select
t1.id as id_1, t2.id as id_2, t1.name as name, t2.name as name_dup,
t1.attr1 as attr1_1, t1.attr2 as attr2_1, t1.attr3 as attr3_1, t1.attr4 as attr4_1, t1.attr5 as attr5_1,
t2.attr1 as attr1_2, t2.attr2 as attr2_2, t2.attr3 as attr3_2, t2.attr4 as attr4_2, t2.attr5 as attr5_2
from TEST_ATTRIBS t1, TEST_ATTRIBS t2
where t1.id <> t2.id
and t1.name <> t2.name
and t1.name = REPLACE(t2.name, 'DUP_', '')
;
-- NOTE THIS PIECE OF HORRIBLE CODE REPETITION --
-- Create comparison report
-- compare 1st attribute
select 'attr1' as Different,
id_1, id_2, name, name_dup,
CAST(attr1_1 AS VARCHAR2(2000)) as Val1, CAST(attr1_2 AS VARCHAR2(2000)) as Val2
from TA_SELFCMP
where attr1_1 <> attr1_2
or (attr1_1 is null and attr1_2 is not null)
or (attr1_1 is not null and attr1_2 is null)
union
-- compare 2nd attribute
select 'attr2' as Different,
id_1, id_2, name, name_dup,
CAST(attr2_1 AS VARCHAR2(2000)) as Val1, CAST(attr2_2 AS VARCHAR2(2000)) as Val2
from TA_SELFCMP
where attr2_1 <> attr2_2
or (attr2_1 is null and attr2_2 is not null)
or (attr2_1 is not null and attr2_2 is null)
union
-- compare 3rd attribute
select 'attr3' as Different,
id_1, id_2, name, name_dup,
CAST(attr3_1 AS VARCHAR2(2000)) as Val1, CAST(attr3_2 AS VARCHAR2(2000)) as Val2
from TA_SELFCMP
where attr3_1 <> attr3_2
or (attr3_1 is null and attr3_2 is not null)
or (attr3_1 is not null and attr3_2 is null)
union
-- compare 4th attribute
select 'attr4' as Different,
id_1, id_2, name, name_dup,
CAST(attr4_1 AS VARCHAR2(2000)) as Val1, CAST(attr4_2 AS VARCHAR2(2000)) as Val2
from TA_SELFCMP
where attr4_1 <> attr4_2
or (attr4_1 is null and attr4_2 is not null)
or (attr4_1 is not null and attr4_2 is null)
union
-- compare 5th attribute
select 'attr5' as Different,
id_1, id_2, name, name_dup,
CAST(attr5_1 AS VARCHAR2(2000)) as Val1, CAST(attr5_2 AS VARCHAR2(2000)) as Val2
from TA_SELFCMP
where attr5_1 <> attr5_2
or (attr5_1 is null and attr5_2 is not null)
or (attr5_1 is not null and attr5_2 is null)
;
Comme vous pouvez le voir, la requête pour générer un "rapport de différence" utilise le même bloc SQL SELECT 5 fois (pourrait facilement être 42 fois!). Cela me semble absolument mort cérébral (j'ai le droit de le dire, après tout, j'ai écrit le code), mais je n'ai pas pu trouver de bonne solution à cela.
Si cela devait être une requête dans un code d'application réel, je pourrais écrire une fonction qui concocte cette requête sous forme de chaîne , puis j'exécuterais la requête sous forme de chaîne.
- -> La construction de chaînes est horrible et horrible à tester et à entretenir. Si le "code d'application" est écrit dans un langage tel que PL / SQL, il se sent tellement mal qu'il fait mal.
Alternativement, s'il est utilisé à partir de PL / SQL ou similaire, je suppose qu'il existe des moyens procéduraux pour rendre cette requête plus maintenable.
- -> Dérouler quelque chose qui peut être exprimé en une seule requête en étapes procédurales juste pour éviter la répétition du code semble également mal.
Si cette requête était nécessaire en tant que vue dans la base de données, alors - pour autant que je comprends - il n'y aurait pas d'autre moyen que de maintenir réellement la définition de la vue comme je l'ai posté ci-dessus. (!!?)
- -> J'ai en fait dû faire un peu de maintenance sur une définition de vue de 2 pages une fois que ce n'était pas loin de la déclaration ci-dessus. De toute évidence, changer quoi que ce soit dans cette vue nécessitait une recherche textuelle regexp sur la définition de la vue pour savoir si la même sous-instruction avait été utilisée dans une autre ligne et si elle devait être modifiée à cet endroit.
Donc, comme le titre l'indique - quelles techniques existe-t-il pour éviter d'avoir à écrire de telles abominations?
UNION ALL
. Souvent,UNION
sansALL
entraîne généralement une bobine de stockage temporaire pour l'opération de tri requise (comme «UNION» est effectivementUNION ALL
suivi parDISTINCT
ce qui implique un tri), dans certains cas, la différence de performances peut être énorme.