Vous n'écririez pas une application avec des fonctions de 200 lignes. Vous décomposeriez ces longues fonctions en fonctions plus petites, chacune avec une seule responsabilité clairement définie.
Pourquoi écrire votre SQL comme ça?
Décomposez vos requêtes, tout comme vous décomposez vos fonctions. Cela les rend plus courts, plus simples, plus faciles à comprendre, plus faciles à tester , plus faciles à refactoriser. Et cela vous permet d'ajouter des "shims" entre eux, et des "wrappers" autour d'eux, comme vous le faites dans le code procédural.
Comment est-ce que tu fais ça? En transformant chaque chose importante d'une requête en vue. Ensuite, vous composez des requêtes plus complexes à partir de ces vues plus simples, tout comme vous composez des fonctions plus complexes à partir de fonctions plus primitives.
Et ce qui est formidable, c'est que pour la plupart des compositions de vues, vous obtiendrez exactement les mêmes performances de votre SGBDR. (Pour certains, vous ne le ferez pas; et alors? L'optimisation prématurée est la racine de tous les maux. Commencez par coder correctement, puis optimisez si vous en avez besoin.)
Voici un exemple d'utilisation de plusieurs vues pour décomposer une requête complexe.
Dans l'exemple, étant donné que chaque vue n'ajoute qu'une seule transformation, chacune peut être testée indépendamment pour trouver des erreurs, et les tests sont simples.
Voici la table de base dans l'exemple:
create table month_value(
eid int not null, month int, year int, value int );
Ce tableau est imparfait, car il utilise deux colonnes, mois et année, pour représenter une donnée, un mois absolu. Voici nos spécifications pour la nouvelle colonne calculée:
Nous ferons cela comme une transformation linéaire, de sorte qu'elle trie de la même manière que (année, mois), et de telle sorte que pour tout tuple (année, mois), il y ait une et seule valeur, et toutes les valeurs sont consécutives:
create view cm_absolute_month as
select *, year * 12 + month as absolute_month from month_value;
Maintenant, ce que nous devons tester est inhérent à notre spécification, à savoir que pour tout tuple (année, mois), il y a un et un seul (mois_absolu), et que les (mois_absolus) sont consécutifs. Écrivons quelques tests.
Notre test sera une select
requête SQL , avec la structure suivante: un nom de test et une instruction case caténés ensemble. Le nom du test est simplement une chaîne arbitraire. L'instruction case n'est que case when
des instructions de test then 'passed' else 'failed' end
.
Les instructions de test ne seront que des sélections SQL (sous-requêtes) qui doivent être vraies pour que le test réussisse.
Voici notre premier test:
--a select statement that catenates the test name and the case statement
select concat(
-- the test name
'For every (year, month) there is one and only one (absolute_month): ',
-- the case statement
case when
-- one or more subqueries
-- in this case, an expected value and an actual value
-- that must be equal for the test to pass
( select count(distinct year, month) from month_value)
--expected value,
= ( select count(distinct absolute_month) from cm_absolute_month)
-- actual value
-- the then and else branches of the case statement
then 'passed' else 'failed' end
-- close the concat function and terminate the query
);
-- test result.
L'exécution de cette requête produit ce résultat: For every (year, month) there is one and only one (absolute_month): passed
Tant qu'il y a suffisamment de données de test dans month_value, ce test fonctionne.
Nous pouvons également ajouter un test pour des données de test suffisantes:
select concat( 'Sufficient and sufficiently varied month_value test data: ',
case when
( select count(distinct year, month) from month_value) > 10
and ( select count(distinct year) from month_value) > 3
and ... more tests
then 'passed' else 'failed' end );
Maintenant, testons c'est consécutif:
select concat( '(absolute_month)s are consecutive: ',
case when ( select count(*) from cm_absolute_month a join cm_absolute_month b
on ( (a.month + 1 = b.month and a.year = b.year)
or (a.month = 12 and b.month = 1 and a.year + 1 = b.year) )
where a.absolute_month + 1 <> b.absolute_month ) = 0
then 'passed' else 'failed' end );
Maintenant, mettons nos tests, qui ne sont que des requêtes, dans un fichier, et exécutons ce script sur la base de données. En effet, si nous stockons nos définitions de vue dans un script (ou des scripts, je recommande un fichier par vues associées) à exécuter sur la base de données, nous pouvons ajouter nos tests pour chaque vue au même script, de sorte que l'acte de (re -) la création de notre vue exécute également les tests de la vue. De cette façon, nous obtenons tous les deux des tests de régression lorsque nous recréons des vues et, lorsque la création de la vue s'exécute contre la production, la vue sera également testée en production.
Créez une base de données du système de test que vous pouvez recharger aussi souvent que vous le souhaitez. Chargez vos données ou créez vos données et enregistrez-les. Créez un moyen simple de le recharger. Attachez votre système de développement à cette base de données et validez votre code avant de passer à la production. Donnez-vous un coup de pied chaque fois que vous parvenez à laisser un problème entrer en production. Créez une suite de tests pour vérifier les problèmes connus et développer votre suite de tests au fil du temps.
la source
Vous voudrez peut-être vérifier DbUnit , vous pouvez donc essayer d'écrire des tests unitaires pour vos programmes avec un ensemble fixe de données. De cette façon, vous devriez être capable d'écrire des requêtes avec des résultats plus ou moins prévisibles.
L'autre chose que vous voudrez peut-être faire est de profiler votre pile d'exécution SQL Server et de savoir si toutes les requêtes sont effectivement les bonnes, par exemple, si vous utilisez une seule requête qui renvoie à la fois des résultats corrects et incorrects, alors clairement la requête est utilisé est en question, mais qu'en est-il si votre application envoie différentes requêtes à différents points du code?
Toute tentative de corriger votre requête serait alors futile ... les requêtes non fiables pourraient encore être celles qui déclenchent les mauvais résultats de toute façon.
la source
Re: tpdi
Notez que cela vérifie uniquement que les valeurs am pour des mois consécutifs seront consécutives, et non que des données consécutives existent (ce qui est probablement ce que vous vouliez initialement). Cela passera toujours si aucune de vos données sources n'est consécutive (par exemple, vous n'avez que des mois pairs), même si votre calcul AM est totalement désactivé.
Est-ce que je manque également quelque chose, ou est-ce que la seconde moitié de cette clause ON remplace la mauvaise valeur du mois? (c'est à dire vérifie que 12/2011 vient après 1/2010)
Ce qui est pire, si je me souviens bien, SQL Server vous permet au moins moins de 10 niveaux de vues avant que l'optimiseur ne jette ses mains virtuelles en l'air et ne commence à effectuer des analyses complètes de la table à chaque demande, alors ne sur-faites pas cette approche.
N'oubliez pas de tester à fond vos cas de test!
Sinon, créer un très large ensemble de données pour englober la plupart ou toutes les formes d'entrées possibles, en utilisant SqlUnit ou DbUnit ou toute autre unité * pour automatiser la vérification des résultats attendus par rapport à ces données, et réviser, les maintenir et les mettre à jour si nécessaire semble généralement être le chemin à parcourir.
la source