Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1372 connectés 

  FORUM HardWare.fr
  Programmation
  SQL/NoSQL

  Pour les pros du SQL (prb avec récursivité)

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

Pour les pros du SQL (prb avec récursivité)

n°2015760
dragonspir​it
Posté le 10-08-2010 à 15:01:32  profilanswer
 

Bonjour, je cherche à effectuer une requête SQL mais je ne trouve pas comment.
 
Voici l'idée.
 
J'ai une table `news` avec les news
news_id | news
 
J'ai une table `flux` avec les flux d'information
flux_id | flux | company_id
 
J'ai une table `company` avec les sociétés (mais chaque société peut appartenir à une autre société/groupe, donc la base est récursive)
company_id | company | company_group_id
 
J'ai une table `news_in_flux` de correspondance entre les news et le flux  
flux_id | news_id
sachant que chaque news peut appartenir à plusieurs flux
 
Je souhaiterais faire une requête qui me permette de récupérer toutes les news dans une liste de flux données, mais vu qu'il peut y avoir des doublons car une même news peut être dans plusieurs flux, je ne veux récupérer que la news du flux de la société la plus haute dans la hiérarchie. (vous comprenez?)
 
Du coup, pour le moment, je fais un "select distinct news_id from flux, where flux IN (ma_liste_de_flux)" ce qui me retourne les news sans doublon, mais je ne sais plus de quel flux il provient.
 
Toute idée est la bienvenue.

mood
Publicité
Posté le 10-08-2010 à 15:01:32  profilanswer
 

n°2015773
alien cons​piracy
hardtrance addict
Posté le 10-08-2010 à 15:34:32  profilanswer
 

SELECT news_id, flux_id FROM news_in_flux WHERE flux_id IN (ma_liste_de flux) AND EXISTS (SELECT * FROM flux JOIN company ON flux.company_id = company.company_id WHERE flux.flux_id = news_in_flux.flux_id AND  company.company_group_id IS NULL)

 

Mais je tappe un peu au pif


Message édité par alien conspiracy le 10-08-2010 à 15:35:13
n°2015775
dragonspir​it
Posté le 10-08-2010 à 15:41:13  profilanswer
 

merci alien, je vais essayer ça, je comprends pas toute la requête mais intuitivement, j'ai l'impression que ça ne peut pas marcher s'il y a plusieurs niveaux de récursivité dans les sociétés (une société appartient à une société qui appartient à une autre société...), je me trompe?

n°2015789
alien cons​piracy
hardtrance addict
Posté le 10-08-2010 à 16:06:08  profilanswer
 

Je fais la supposition erronée que la company à la racine possède le flux en question. Je pense que tout problème est insolvable avec une simple requête, tu as besoin de procédure stockée.

n°2015791
dragonspir​it
Posté le 10-08-2010 à 16:11:12  profilanswer
 

Ok, c'est ce que je me disais aussi.
 
J'avais pensé à l'éventualité d'enregistrer la hiérarchie complète dans une colonne et ensuite de comparer cette arbo pour savoir quel était la société le plus haut dans la hiérarchie mais sinon je voyais pas comment le faire.
 
Apparemment, postgresql gère les requêtes récursives, c'était ma 2e solution.

n°2015957
Oliiii
Posté le 11-08-2010 à 09:20:20  profilanswer
 

En ajoutant 2 colonnes ya moyen de faire ce que tu veux en une seule query.
 
Dans la table company il faut ajouter une colonne company_level et company_source (meme data type que company_id).
Tu mets company_level = 0 where company_group_id is NULL, sinon quand tu rajoutes un company tu mets company_level = parentcompany.company_level + 1.
Tu mets company_source = company_id where company_group_id is NULL, pour les autres tu mets company_source = parentcompany.company_source.
 
En gros, le company_level sert a trouver la company la plus "haute" dans la hierarchie, le company_source permet de faire un group by et separer les differents groupes de company.
 
En SQL Server pour remplire company_source et company_level la toute premiere fois tu peux faire ca:

Code :
  1. WITH cte (company_id, company, company_group_id, company_level, company_source)
  2. AS
  3. (
  4.     SELECT company_id, company, company_group_id, 0 company_level, company_id company_source
  5.     FROM company
  6.     WHERE company_group_id IS NULL
  7.     UNION ALL
  8.     SELECT b.company_id, b.company, b.company_group_id, a.company_level + 1, a.company_source
  9.     FROM cte a
  10.         JOIN company b ON a.company_id = b.company_group_id
  11. )
  12. UPDATE company
  13.     SET company_level = b.company_level,
  14.     company_source = b.company_source
  15. FROM company a
  16.     JOIN cte b ON a.company_id = b.company_id


 
Ensuite quand tu ajoutes une company il te suffit de connaite le company_level et company_source du parent (si il y en a un) et de l'inserer directement.
Si tu modifies la hierarchie d'une company tu remets le tout a jour en refesant tourner la query d'initialisation.
 
Pour trouver la liste de news unique et leur flux tu peux faire ca:

Code :
  1. SELECT a.company, b.flux, d.news
  2. FROM company a
  3.     JOIN flux b ON a.company_id = b.company_id
  4.     JOIN news_in_flux c ON b.flux_id = c.flux_id
  5.     JOIN news d ON c.news_id = d.news_id
  6.     JOIN (
  7.     SELECT c.news_id, d.company_source, MIN(d.company_level) company_level
  8.     FROM flux a
  9.         JOIN news_in_flux b ON a.flux_id = b.flux_id
  10.         JOIN news c ON b.news_id = c.news_id
  11.         JOIN company d ON d.company_id = a.company_id
  12.     WHERE a.flux_id IN (1,2,3,5,9,10,11,13,15)
  13.     GROUP BY c.news_id, c.news, d.company_source
  14.         ) e ON e.news_id = d.news_id AND e.company_level = a.company_level AND e.company_source = a.company_source


 
Tu peux toujours avoir des doublons si des company qui ont la meme _source et le meme _level ont les meme news, dans ce cas la tu peux faire ca (change MIN en autre chose si tu ne veux pas la premiere company par order alphabetique):

Code :
  1. SELECT MIN(a.company), b.flux, d.news
  2. FROM company a
  3.     JOIN flux b ON a.company_id = b.company_id
  4.     JOIN news_in_flux c ON b.flux_id = c.flux_id
  5.     JOIN news d ON c.news_id = d.news_id
  6.     JOIN (
  7.     SELECT c.news_id, d.company_source, MIN(d.company_level) company_level
  8.     FROM flux a
  9.         JOIN news_in_flux b ON a.flux_id = b.flux_id
  10.         JOIN news c ON b.news_id = c.news_id
  11.         JOIN company d ON d.company_id = a.company_id
  12.     WHERE a.flux_id IN (1,2,3,5,9,10,11,13,15)
  13.     GROUP BY c.news_id, c.news, d.company_source
  14.         ) e ON e.news_id = d.news_id AND e.company_level = a.company_level AND e.company_source = a.company_source
  15. GROUP BY b.flux, d.news


 
En general ce n'est pas super de sauver les niveau de hierarchie comme ca, ca peu devenir lourd a gerer en cas de mise a jour frequente, mais dans ton cas je suppose que tu vas passer plus de temps a ajouter des company plutot que de les deplacer dans la hierarchie.
 
Ce n'est jamais facil d'expliquer des queries hierarchique, donc si ca peu t'aider voici la totalité du code que j'ai utilisé pour ton probleme (les noms et hierarchie des company ne sont pas correcte, mais c'est le matin et j'ai pas plus d'idée que ca :) ):

Code :
  1. CREATE TABLE news (
  2.     news_id int identity(1,1) PRIMARY KEY,
  3.     news varchar(max) NOT NULL
  4. )
  5.  
  6. CREATE TABLE flux (
  7.     flux_id int identity(1,1) PRIMARY KEY,
  8.     flux varchar(max) NOT NULL,
  9.     company_id int NOT NULL
  10. )
  11.  
  12. --drop table company
  13. CREATE TABLE company (
  14.     company_id int identity(1,1) PRIMARY KEY,
  15.     company varchar(max) NOT NULL,
  16.     company_group_id int NULL,
  17.     company_level int NULL,
  18.     company_source int NULL
  19. )
  20.  
  21. CREATE TABLE news_in_flux (
  22.     flux_id int NOT NULL,
  23.     news_id int NOT NULL
  24. )
  25.  
  26. INSERT news (news) VALUES ('My first news...'), ('Second news'), ('Third news')
  27. INSERT news (news) VALUES ('News 4'), ('News 5'), ('News 6'), ('News 7'), ('News 8'), ('News 9')
  28. SELECT * FROM news
  29.  
  30. INSERT company (company,company_group_id) VALUES ('Apple', NULL),
  31.     ('Microsoft',NULL),
  32.     ('Google',NULL),
  33.     ('iPhone Corp.',1),
  34.     ('iPad Corp.',1),
  35.     ('SilverLight',2),
  36.     ('Youtube',3),
  37.     ('Ebay', 3),
  38.     ('PayPal',8)
  39. SELECT * FROM company
  40.  
  41. INSERT flux (flux,company_id) VALUES ('Apple1',1),
  42.     ('Apple3',1),
  43.     ('Msft2',2),
  44.     ('Msft4',2),
  45.     ('Google7',3),
  46.     ('iPhone6',4),
  47.     ('iPhone5',4),
  48.     ('Silv8',6),
  49.     ('Silv2',6),
  50.     ('You7',7),
  51.     ('You2',7),
  52.     ('Ebay3',8),
  53.     ('Ebay7',8),
  54.     ('Paypal1',9),
  55.     ('Paypal7',9)
  56. SELECT * FROM flux
  57.  
  58. INSERT news_in_flux (flux_id,news_id)
  59. VALUES (1,1),
  60.        (2,3),
  61.        (3,2),
  62.        (4,4),
  63.        (5,7),
  64.        (6,6),
  65.        (7,5),
  66.        (8,8),
  67.        (9,2),
  68.        (10,7),
  69.        (11,2),
  70.        (12,3),
  71.        (13,7),
  72.        (14,1),
  73.        (15,7)
  74. SELECT * FROM news_in_flux
  75. ORDER BY news_id
  76.  
  77. SELECT *
  78. FROM flux a
  79.     JOIN news_in_flux b ON a.flux_id = b.flux_id
  80.     JOIN news c ON b.news_id = c.news_id
  81. WHERE a.flux_id IN (1,2,3,5,9,10,11,13,15)
  82.  
  83. SELECT * FROM company
  84. UPDATE company SET company_level = NULL
  85.  
  86. WITH cte (company_id, company, company_group_id, company_level, company_source)
  87. AS
  88. (
  89.     SELECT company_id, company, company_group_id, 0 company_level, company_id company_source
  90.     FROM company
  91.     WHERE company_group_id IS NULL
  92.     UNION ALL
  93.     SELECT b.company_id, b.company, b.company_group_id, a.company_level + 1, a.company_source
  94.     FROM cte a
  95.         JOIN company b ON a.company_id = b.company_group_id
  96. )
  97. UPDATE company
  98.     SET company_level = b.company_level,
  99.     company_source = b.company_source
  100. FROM company a
  101.     JOIN cte b ON a.company_id = b.company_id
  102.     
  103. SELECT *
  104. FROM flux a
  105.     JOIN news_in_flux b ON a.flux_id = b.flux_id
  106.     JOIN news c ON b.news_id = c.news_id
  107.     JOIN company d ON d.company_id = a.company_id
  108. WHERE a.flux_id IN (1,2,3,5,9,10,11,13,15)
  109.  
  110. SELECT MIN(a.company), b.flux, d.news
  111. FROM company a
  112.     JOIN flux b ON a.company_id = b.company_id
  113.     JOIN news_in_flux c ON b.flux_id = c.flux_id
  114.     JOIN news d ON c.news_id = d.news_id
  115.     JOIN (
  116.     SELECT c.news_id, d.company_source, MIN(d.company_level) company_level
  117.     FROM flux a
  118.         JOIN news_in_flux b ON a.flux_id = b.flux_id
  119.         JOIN news c ON b.news_id = c.news_id
  120.         JOIN company d ON d.company_id = a.company_id
  121.     WHERE a.flux_id IN (1,2,3,5,9,10,11,13,15)
  122.     GROUP BY c.news_id, c.news, d.company_source
  123.         ) e ON e.news_id = d.news_id AND e.company_level = a.company_level AND e.company_source = a.company_source
  124. GROUP BY b.flux, d.news


Message cité 1 fois
Message édité par Oliiii le 11-08-2010 à 09:26:58
n°2015961
dragonspir​it
Posté le 11-08-2010 à 09:34:39  profilanswer
 

Merci Oliiii pour cette longue réponse, j'imagine que tu as dû y passer pas mal de temps. Je vais regarder ça plus en détail et essayer de comprendre les requêtes. Je suis sur la requête en ce moment même et je pense avoir trouvé une solution qui permette de le faire sans recourir à des colonnes supplémentaires justement (ce qui m'embêtait un peu en terme de maintenance). Mais avoir une colonne company_level ou company_source peut toujours être utile. Je vais privilégier la solution la plus optimisée sachant que la majorité des requêtes vont être effectivement des SELECT pour récupérer les news tandis que les mises à jour de sociétés ou les changements dans la hiérarchie seront très limités.
 
Je reviens vers toi

n°2016045
lasnoufle
La seule et unique!
Posté le 11-08-2010 à 12:48:17  profilanswer
 

'Tention je balance juste l'idée comme ca, mais y aurait pas moyen de faire quelque chose de similaire sans avoir à créer les deux colonnes mais en les générant sur le coup dans une sous-requete, en utilisant une clause CONNECT BY?
Un truc du genre:

SELECT level AS company_level, CONNECT_BY_ROOT company_group_id AS company_source
START WITH company_group_id IS NULL
CONNECT BY PRIOR company_id = company_group_id;


Edit: ha oui, Oracle only je suppose par contre...


Message édité par lasnoufle le 11-08-2010 à 12:48:38

---------------
C'était vraiment très intéressant.
n°2016060
Oliiii
Posté le 11-08-2010 à 13:35:44  profilanswer
 

Il ya moyen de faire l'equivalent en SQL Server, le probleme est que les performances vont se degrader tres tres vite.
Les colonnes supplementaire sont la pour eviter de recalculer la meme chose a chaque Select (en partant du principe qu'on va faire beaucoup plus de Select/Insert que d'update).

n°2016094
lasnoufle
La seule et unique!
Posté le 11-08-2010 à 14:47:10  profilanswer
 

Agreed.


---------------
C'était vraiment très intéressant.
mood
Publicité
Posté le 11-08-2010 à 14:47:10  profilanswer
 

n°2016167
alien cons​piracy
hardtrance addict
Posté le 11-08-2010 à 19:28:37  profilanswer
 

Oliiii a écrit :

Pleins de choses


J'y ai pensé mais j'avais trop la fleme de l'expliquer. Chapeau d'avoir eu le courage.  :D


Message édité par alien conspiracy le 11-08-2010 à 19:29:57
n°2017528
dragonspir​it
Posté le 17-08-2010 à 23:52:27  profilanswer
 

J'ai finalement utiliser ta solution Oli qui me parait la meilleure.
 
Merci beaucoup pour ton aide !


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  SQL/NoSQL

  Pour les pros du SQL (prb avec récursivité)

 

Sujets relatifs
[PL/SQL] ORA-01403: Aucune donnée trouvéeHeure SQL
SQL server management studio 2005 & plan de maintenanceProblème en PHP pour une requête SQL
SQL Modification conditionnelle d'un string en fonction de son contenuPetite aide SQL/Access SVP
SQL Server 2008 Express[T-SQL] Générer les procédures sotckées
[SQL]Factorisation de sous-requêtesTrigger SQL sur colonne Update
Plus de sujets relatifs à : Pour les pros du SQL (prb avec récursivité)


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR