[SQL] Requête moche

Bonjour la zone,
j’ai un petit soucis pour faire une requête.
J’ai trouvé une solution qui fonctionne, mais c’est moche et je me dit qu’il doit y avoir plus propre et élégant.

  1. La problématique :
    J’ai une table, contenant une configuration pour un type de bidule.
    Cette table contient la configuration pour chaque type de bidule, ainsi qu’une configuration générique pour tous les bidules (dans ce cas l’id du type est NULL). Mais pas obligatoirement.

Je souhaiterai retourne SUR UNE LIGNE, les deux configs possible : la config spécifique pour ce type, et la config générique.

  1. la solution trouvée est la suivante :

SELECT config_type.*, config_defaut.* FROM
(SELECT * FROM maTable WHERE type=‹ XXX › AND id_data=‹ BIDULE01 ›) config_type
FULL OUTER JOIN (SELECT * FROM maTable WHERE type IS NULL AND id_data=‹ BIDULE01 ›) config_defaut
ON config_type.id_data=config_defaut.id_data;

Avec ça, j’ai bien mes deux lignes en une seule, que l’une et l’autre existe ou non (si je n’ai aucune ligne, j’ai un NOTFOUND, ça c’est normal).
Pour tous les autres essais que j’ai fait, le problème est que si le type n’est pas configuré, alors je n’ai aucun résultat (même pas la config par défaut qui existe).

Donc si vous avez une meilleure solution (je ne doute pas que ça existe), proposez !

Note : compatible Oracle ET Informix si possible (SQL std), les perfs ne sont pas prioritaires.

merci :kiss:

Coucou

Passer par une vue serai plus esthétique mais le résultat sera le même:smile:)
Et vu que tu fais une jointure, c’est normale de rien a voir si le type n’est pas configuré. Si tu es sur de n’avoir que 2 résultats, fait un produit cartésien ca devrai résoudre ton problème .

Huum?
SELECT * FROM maTable WHERE (type=‘XXX’ or type is null) AND id_data='BIDULE01’
non?

D’une manière générale, garder des champs NULL n’est jamais une bonne idée mais bon…

LoneWolf
To be null or not to be, c’est pas vraiment la question…

Oui j’ai aussi pensé aux vues, mais j’aime pas trop non plus ^^

Non c’est le problème, je ne suis pas sur. Le CROSS JOIN fonctionne si j’ai bien mes deux résultats, mais je n’ai plus rien si un des deux résultats n’existe pas.

Non, ça me retourne 2 lignes, je voudrais le tout sur une seule et même ligne.

Effectivement mais c’est ce qui est en place pour le moment

Tu as essayé avec un RIGHT JOIN à la place ?

Oui, mais même problème s’il manque l’élément “gauche” : aucun résultat

Bizarre, c’est pourtant la bonne approche.
Et si tu modifies ton SQL comme ci-dessous ?

select t1.type, t1.id_data, t2.type, t2.id_data
from maTable t1 outer join maTable t2 on t1.id_data = t2.id_data
where t1.type = 'xxx' and t1.id_data = 'bidule01'
and t2.type is null and t2.id_data = 'bidule01'

(je n’ai pas de DB SQL sous la main pour tester malheureusement)

J’ai pas essayé, mais quelque chose comme ça ne foncitonnerait pas?

SELECT  config_type.*,  config_defaut.* 
FROM maTable config_type FULL OUTER JOIN maTable config_defaut 
ON config_type.id_data=config_defaut.id_data;
WHERE config_type.type='XXX' AND config_type.id_data='BIDULE01'
AND config_defaut.type is NULL AND config_defaut.id_data='BIDULE01'

Le outer join pour ce que tu veux c’est clairement ce qu’il faut utiliser, c’est fait pour ça.
Le truc moche c’est les SELECT imbriqués, du coup en faisant le select sur chaque table et en mettant les conditions dans le select principal mais avec le prefixe ça devrait foncitonner.

Ah bah du coup on partait sur le même principe

Au moins tu as fait l’effort de garder les noms d’Astro ! :smile:
(par contre il y a un point-virgule qui traine dans ton SQL)

@feuildur et @Rabban : Effectivement j’étais partis là dessus au départ, mais ça ne fonctionne pas si le type n’existe pas :confused:

En gros si je fait la requête avec type =‹ ABC ›, et que le type ‹ ABC › n’existe pas, ça ne retourne rien

Je vais éditer avec des exemples de résultat, ça sera plus parlant

edit : voilà (mais il pète la mise en page en ajoutant du gras, je sais pas pourquoi, et je vois pas de mode ‹ manuel › à la IPB pour changer ça)

Mettons que j’ai 2 lignes dans maTable :
type | id | valeur

NULL | ‹ BIDULE01 › | ‹ toto ›
‹ ABC › | ‹ BIDULE01 › | ‹ tata ›

SELECT … FROM maTable config_type FULL OUTER JOIN maTable config_defaut

si je fait ma requête avec un type existant ‹ ABC ›, pas de pb, j’ai bien :

type | id | valeur | type_1 | id_1 | valeur_1

NULL | ‹ BIDULE01 › | ‹ toto › | ‹ ABC › | ‹ BIDULE01 › | ‹ tata ›

Par contre, si j’essaye avec ‹ DEF ›

type | id | valeur | type_1 | id_1 | valeur_1

          <AUCUN RESULTAT>

Alors que moi je voudrais :
type | id | valeur | type_1 | id_1 | valeur_1

NULL | ‹ BIDULE01 › | ‹ toto › | NULL | NULL | NULL

SELECT t1.id, t1.valeur, t2.id, t2.valeur FROM test t1 
left join test t2 on t2.id = t2.id and t2.type = 'DEF'
where t1.id = 'BIDULE01'
and t1.type is null

C’est pas ça que tu voudrais ?

Un truc comme ça peut être? Si tu étais en pur oracle j’aurais tenté une fonction analytique ;p

WITH config AS
(SELECT *
FROM maTable
WHERE nvl(type,‘XXX’)='XXX’
AND id_data=‘BIDULE01’
)
SELECT config_type., config_defaut.
FROM config config_type
FULL OUTER JOIN config config_defaut
ON config_type.id_data=config_defaut.id_data
WHERE config_type.type IS NOT NULL
AND config_defaut.type IS NULL;

Ca n’a pas l’air de fonctionner non plus

Je ne connaissais pas cette syntaxe, mais ça n’a pas l’air de fonctionner

Bon, je pensais pas que ça serait aussi complexe :D. Je pense donc rester sur ma solution à base de sous requêtes pour le moment, merci à tout le monde pour votre aide !

Il te suffit de faire une jointure sur une première table « fictive » qui contient juste des paramètres.

Voici le code qui marche pour SQL Server, ça devrait être adaptable facilement.

Sur un bidule qui n’existe pas tu auras une ligne avec les 2 configs à NULL. Sinon tu auras les configs si présentes.

Par contre attention, cela suppose que tu respectes bien l’uniticté des couples id_bidule/type_bidule sinon tu te retrouves avec toutes les combinaisons.

drop table biduleconfig
go

create table biduleconfig (
	id int constraint pk_biduleconfig primary key,
	id_bidule varchar(80),
	type_bidule varchar(80),
	config varchar(80)
)
go

insert into biduleconfig (id, id_bidule, type_bidule, config) values (1, 'bidule1', 'typeA', 'config01')
insert into biduleconfig (id, id_bidule, type_bidule, config) values (2, 'bidule1', null,    'config02')
insert into biduleconfig (id, id_bidule, type_bidule, config) values (3, 'bidule1', 'typeB', 'config03')
insert into biduleconfig (id, id_bidule, type_bidule, config) values (4, 'bidule2', null, 'config03')

declare @type varchar(80)
declare @bidule varchar(80)

set @type = 'typeA'
set @bidule = 'bidule3'

select
	t.type_bidule,
	t.id_bidule,
	t2.config as [type_config],
	t3.config as [default_config]
from
	(select @type as type_bidule, @bidule as id_bidule) as t
	left outer join (select * from biduleconfig) as t2 on t2.type_bidule = t.type_bidule and t2.id_bidule = t.id_bidule
	left outer join (select * from biduleconfig) as t3 on t3.id_bidule = t.id_bidule

PS : il suffit de faire un TAB de 4 espaces pour avoir un bloc de code :wink:

Allez, pour le plaisir, la version avec fonction analytique, ça ne marche qu’avec oracle, mais attention : aucune jointure !

SELECT champ1, champ2, champ1defaut, champ2defaut
  FROM (SELECT champ1,
               champ2,
               type,
               first_value(champ1) over(partition by id_data order by type nulls first) champ1defaut,
               first_value(champ2) over(partition by id_data order by type nulls first) champ2defaut
          FROM (SELECT 'valdefaut1' champ1, 'valdefaut1' champ2, null type, 'BIDULE01' id_data FROM DUAL
                UNION
                SELECT 'val1' champ1, 'val1' champ2, 'XXX' type, 'BIDULE01' id_data FROM DUAL
                UNION
                SELECT 'valdefaut2' champ1, 'valdefaut2' champ2, null type, 'BIDULE02' id_data FROM DUAL
                UNION
                SELECT 'val2' champ1, 'val2' champ2, 'YYY' type, 'BIDULE02' id_data FROM DUAL)matable
         WHERE id_data = 'BIDULE01'
           AND nvl(type, 'XXX') = 'XXX'
 )
 WHERE type IS NOT NULL;

Oui, mais tu triches avec from dual

1 « J'aime »

Les from dual c’est parce que j’avais la flemme de créer une nouvelle table, c’est comme si j’avais une table avec quatre lignes, mais si la table existait pour de vrai j’aurais pas eu besoin de ça. ^^

Là où je “triche” c’est plus avec les first_value over, c’est un outil dans oracle qui permet de chercher des valeurs dans les autres lignes de la requête, c’est ça qui permet de ne pas faire de jointures.

@Mistermick

Même si les fonctions analytiques sont à éviter quand on peut faire autrement, pour des questions de performance, en revanche quand on y est contraint ça fait un moment que les fonctions analytiques sont bien supportés par les principaux SGBDR maintenant.

Ta requête doit donc fonctionner pas seulement sur Oracle. Ou avec peu de modifs en tout cas sur Sql Server et Postgres par exemple.

Je savais pas qu’il fallait les éviter si on peut faire autrement, j’aurais dit qu’il faut les éviter justement parce que ce n’est pas supporté sur tous les SGBDR, mais du coup ça doit être autre chose.

C’est parce que c’est difficile à relire et à maintenir ?