IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage SQL Discussion :

Requête SQL récursive: problème de performance


Sujet :

Langage SQL

  1. #1
    Membre Expert
    Homme Profil pro
    Ingénieur
    Inscrit en
    Août 2010
    Messages
    673
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2010
    Messages : 673
    Par défaut Requête SQL récursive: problème de performance
    Bonjour,

    Avant tout, permettez-moi de préciser que je suis novice en SQL.
    Je travaille, via Power Query (Excel), depuis la base de données MS SQL Server de notre ERP. J'essaie de récupérer, à partir d'une liste d'articles présents en stock SAV, tous les cas d'emploi de ces articles pour un établissement donné de mon entreprise (@Facility='580' par exemple) quel que soit le niveau d'arborescence par rapport à l'article en stock SAV. Je prévois d'ailleurs une colonne calculant le niveau d'arborescence de ce produit/cas d'emploi par rapport à l'article en stock initial.
    Du fait de la récursivité, j'ai décidé de préparer une requête SQL utilisée dans mon script Power Query plutôt que de tenter d'utiliser le "Query Folding" de Power Query..

    Je me suis basé sur le tutoriel du site (https://sqlpro.developpez.com/cours/...te-recursives/) qui est très intéressant et couvre parfaitement mon besoin.
    A cette table récursive, j'ajoute simplement quelques infos supplémentaires (issues d'autres tables de la BDD) tantôt relatives à l'article en stock, tantôt relatives au cas d'emploi.

    Ma requête semble donner les bons résultats mais elle souffre de performances très médiocres: 14h pour un résultat de 29000 lignes (profondeur max atteinte: 9 avec seulement 238 lignes au total pour les profondeurs 7 à 9).
    Ce délai ne me semble pas justifié mais je ne vois pas comment optimiser cette requête; pourriez-vous s'il vous plaît m'aider?

    Voici ma requête:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    DECLARE @Facility VARCHAR(3);
    SET @Facility='580';
     
    WITH originalBOMTable (Facility, PartNumber, StructureType, ComponentPartNumber)
    AS
    (
    	/* J'ai créé cette table comme un préfiltrage de la table des nomenclatures / BOMs en considérant que ça améliorerait l'efficacité du traitement récursif qui se base sur cette table au lieu de la vraie table t_MPDMAT:
    		La table t_MPDMAT gère les nomenclatures / BOMs (une ligne par lien composant - BOM/produit)
    			Filtrage selon:
    			- le statut obsolète ou non du produit cas d'emploi (status ne commençant pas par '9'),
    			- la validité de la ligne (To date = 99999999),
    			- l'établissement (Facility)
    			- le type de structure (ou nomenclature) 'MET'
     
    		Dans cette table:
    		- [PRNO - Product] = produit / cas d'emploi dont la BOM/nomenclature contient l'article [MTNO - Component number]
    		- [MTNO - Component number] = composant appartenant à la nomenclature de l'article [PRNO - Product]
    		NB: Pas de boucle possible: un produit ne peut pas appartenir à lui-même quelle que soit la profondeur d'arborescence) 
    	*/
     
    	select t_MPDMAT.[FACI - Facility],
    		t_MPDMAT.[PRNO - Product],
    		t_MPDMAT.[STRT - Product structure type],
    		t_MPDMAT.[MTNO - Component number]
    		from [M3JDTP500].[V_MPDMAT - MF: Connect materials (PM)] as t_MPDMAT
    		INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITMAS - MF: Item Master File (MM)] AS t_MITMAS ON t_MITMAS.[ITNO - Item number] = t_MPDMAT.[PRNO - Product] AND t_MITMAS.[STAT - Status] NOT LIKE '9%'
    		where (t_MPDMAT.[TDAT - To date] = 99999999 and t_MPDMAT.[FACI - Facility] = @Facility and t_MPDMAT.[STRT - Product structure type] = 'MET')
    ),
    filteredBOMTable (Facility, PartNumber, StructureType, ComponentPartNumber, BOM_Depth, BOM_Path)
    AS
    (
    	/* Table récursive cherchant tous les cas d'emploi successifs des articles appartenant à la table initiale précisée ci-dessous */
    	SELECT [Facility],
    		[PartNumber],
    		[StructureType],
    		[ComponentPartNumber],
    		1,
    		CAST([PartNumber] AS VARCHAR(MAX))
    		FROM originalBOMTable
    		WHERE [ComponentPartNumber] in (
    			/* Table initiale pour la récurrence, basée sur les articles en stock SAV (SAV = Dépôt/Warehouse en '58D' ou '58S') à partir desquels on va chercher récursivement tous les cas d'emploi (parents, grand-parents, etc) */
    			select distinct [_].[ITNO - Item number]
    			from [M3JDTP500].[V_MITBAL - MF: Item - warehouse (MB)] as [_]
    			where ([_].[WHLO - Warehouse] = LEFT(@Facility, 2) + 'D' or [_].[WHLO - Warehouse] = LEFT(@Facility, 2) + 'S') and ([_].[STQT - On-hand balance approved] + [_].[QUQT - On-hand balance for inspection] + [_].[RJQT - Rejected on-hand balance]) <> 0
    		)
    		UNION ALL
    		SELECT oBT.[Facility],
    			oBT.[PartNumber],
    			oBT.[StructureType],
    			fBT.[ComponentPartNumber],
    			fBT.[BOM_Depth] + 1, /* Calcul de la profondeur d'arborescence */
    			CAST(oBT.[PartNumber] AS VARCHAR(MAX)) + ' / ' + fBT.[BOM_Path] /* Chaîne contenant l'arborescence complète du produit */
    			FROM originalBOMTable AS oBT
    			INNER JOIN filteredBOMTable AS fBT ON (oBT.[ComponentPartNumber] = fBT.[PartNumber])
    )
    /* Une fois la table récursive filteredBOMTable construite, il reste à compléter la table finale attendue via les infos obtenues par quelques JOIN ci-dessous.
     Les tables *_Comp récupèrent des informations appartenant au composant originel du cas d'emploi (issu de la table initiale utilisée pour la récurrence).
    Les autres tables récupèrent des informations appartenant au cas d'emploi (parent, grand-parent ou ...). */
    SELECT fBT.[Facility],
    		t_MITBAL_Comp.[WHLO - Warehouse] AS [Warehouse],
    		fBT.[ComponentPartNumber] AS [Component Part Number],
    		t_MITMAS_Comp.[ITDS - Name] AS [Component Name],
    		t_MITMAS_Comp.[STAT - Status] AS [Component Status],
    		t_MITMAS_Comp.[ACRF - User-defined accounting control object] AS [Component Nature],
    		t_MITMAS_Comp.[ITGR - Item group] AS [Component Group],
    		t_CSYTAB_Comp.[TX40 - Description] AS [Component Group Name],
    		t_MITBAL_Comp.[IDDT - Last receipt date] AS [Last receipt date],
    		t_MITBAL_Comp.[ODDT - Last issue date] AS [Last issue date],
    		t_MITBAL_Comp.[SSQT - Safety stock] AS [Component Safety Stock],
    		t_MITBAL_Comp.[STQT - On-hand balance approved] AS [On-hand balance approved],
    		t_MITBAL_Comp.[QUQT - On-hand balance for inspection] AS [On-hand balance for inspection],
    		t_MITBAL_Comp.[RJQT - Rejected on-hand balance] AS [Rejected on-hand balance],
    		t_MITBAL_Comp.[REQT - Reserved quantity] AS [Reserved quantity],
    		fBT.[PartNumber] As [Item Part Number],
    		t_MITMAS.[ITDS - Name] AS [Item Name],
    		t_MITMAS.[FUDS - Description 2] AS [Item Description],
    		t_MITMAS.[STAT - Status] AS [Item Status],
    		t_MPDHED.[STAT - Status] As [Item Structure Status],
    		t_MITMAS.[ITGR - Item group] AS [Item Group],
    		t_CSYTAB.[TX40 - Description] AS [Item Group Name],
    		fBT.[StructureType] AS [Item Structure Type],
    		fBT.[BOM_Depth],
    		fBT.[BOM_Path]
    	FROM filteredBOMTable AS fBT
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITMAS - MF: Item Master File (MM)] AS t_MITMAS_Comp ON t_MITMAS_Comp.[ITNO - Item number] = fBT.[ComponentPartNumber]
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITMAS - MF: Item Master File (MM)] AS t_MITMAS ON t_MITMAS.[ITNO - Item number] = fBT.[PartNumber]
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITBAL - MF: Item - warehouse (MB)] AS t_MITBAL_Comp ON t_MITBAL_Comp.[ITNO - Item number] = fBT.[ComponentPartNumber] AND (t_MITBAL_Comp.[WHLO - Warehouse] = LEFT(fBT.[Facility], 2) + 'D' OR t_MITBAL_Comp.[WHLO - Warehouse] = LEFT(fBT.[Facility], 2) + 'S')
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MPDHED - MF: Product file header (PH)] AS t_MPDHED ON t_MPDHED.[PRNO - Product] = fBT.[PartNumber] AND t_MPDHED.[FACI - Facility] = fBT.[Facility] AND t_MPDHED.[STRT - Product structure type] = fBT.[StructureType]
    	LEFT OUTER JOIN [M3_ODS_PROD].[M3JDTP500].[V_CSYTAB - MF: System tables (CT)] AS t_CSYTAB_Comp ON t_CSYTAB_Comp.[STCO - Constant value] = 'ITGR' AND t_CSYTAB_Comp.[STKY - Key value] = t_MITMAS_Comp.[ITGR - Item group]
    	LEFT OUTER JOIN [M3_ODS_PROD].[M3JDTP500].[V_CSYTAB - MF: System tables (CT)] AS t_CSYTAB ON t_CSYTAB.[STCO - Constant value] = 'ITGR' AND t_CSYTAB.[STKY - Key value] = t_MITMAS.[ITGR - Item group]
     
        OPTION (MAXRECURSION 10)
    Ci-dessous, vous trouverez respectivement un exemple pour t_MPDMAT et la table résultat attendue sur la base de cet exemple (en ignorant certaines colonnes pas utiles dans l'algo et en supposant que S100697 tout comme S100500 font partie des articles en stock SAV (ce que j'ai appelé "table initiale" dans les commentaires de ma requête SQL)):
    FACI - Facility PRNO - Product STRT - Product structure type MTNO - Component number TDAT - To date <Commentaire>
    580 S100702 MET S100697 99999999 Critères OK
    580 S100703 MET S100697 20211201 Critères NOK (To Date <> 99999999)
    540 S100704 MET S100697 99999999 Critères NOK (Facility <> '580')
    580 S100800 MET S100702 99999999 Critères OK
    580 S100900 MET S100800 99999999 Critères OK
    580 S100950 ETU S100702 99999999 Critères NOK (Structure Type <> 'MET')
    580 S100960 MET S100500 99999999 Critères OK
    580 S100970 MET S100697 99999999 Critères OK

    Facility Warehouse Component Part Number Component Status Item Part Number Item Status Item Structure Type BOM_Depth BOM_Path
    580 58D S100697 20 S100702 20 MET 1 S100702
    580 58D S100697 20 S100800 20 MET 2 S100702 / S100800
    580 58D S100697 20 S100900 20 MET 3 S100702 / S100800 / S100900
    580 58D S100697 20 S100970 20 MET 1 S100970
    580 58S S100500 20 S100960 20 MET 1 S100960

    N'hésitez pas à me dire s'il faut plus d'infos et merci d'avance pour votre aide.

  2. #2
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 993
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 21 993
    Billets dans le blog
    6
    Par défaut
    Simplifiez au maximum votre requête récursive.... et faites un jointure sur la vraie table...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    WITH originalBOMTable (Facility, PartNumber, StructureType, ComponentPartNumber)
    AS
    (
    	/* J'ai créé cette table comme un préfiltrage de la table des nomenclatures / BOMs en considérant que ça améliorerait l'efficacité du traitement récursif qui se base sur cette table au lieu de la vraie table t_MPDMAT:
    		La table t_MPDMAT gère les nomenclatures / BOMs (une ligne par lien composant - BOM/produit)
    			Filtrage selon:
    			- le statut obsolète ou non du produit cas d'emploi (status ne commençant pas par '9'),
    			- la validité de la ligne (To date = 99999999),
    			- l'établissement (Facility)
    			- le type de structure (ou nomenclature) 'MET'
     
    		Dans cette table:
    		- [PRNO - Product] = produit / cas d'emploi dont la BOM/nomenclature contient l'article [MTNO - Component number]
    		- [MTNO - Component number] = composant appartenant à la nomenclature de l'article [PRNO - Product]
    		NB: Pas de boucle possible: un produit ne peut pas appartenir à lui-même quelle que soit la profondeur d'arborescence) 
    	*/
     
    	select t_MPDMAT.[FACI - Facility],
    		t_MPDMAT.[PRNO - Product],
    		t_MPDMAT.[STRT - Product structure type],
    		t_MPDMAT.[MTNO - Component number]
    		from [M3JDTP500].[V_MPDMAT - MF: Connect materials (PM)] as t_MPDMAT
    		INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITMAS - MF: Item Master File (MM)] AS t_MITMAS ON t_MITMAS.[ITNO - Item number] = t_MPDMAT.[PRNO - Product] AND t_MITMAS.[STAT - Status] NOT LIKE '9%'
    		where (t_MPDMAT.[TDAT - To date] = 99999999 and t_MPDMAT.[FACI - Facility] = @Facility and t_MPDMAT.[STRT - Product structure type] = 'MET')
    ),
    filteredBOMTable (PartNumber, ComponentPartNumber, BOM_Depth, BOM_Path)
    AS
    (
    	/* Table récursive cherchant tous les cas d'emploi successifs des articles appartenant à la table initiale précisée ci-dessous */
    	SELECT[PartNumber],
    		[ComponentPartNumber],
    		1,
    		CAST([PartNumber] AS VARCHAR(MAX))
    		FROM originalBOMTable
    		WHERE [ComponentPartNumber] in (
    			/* Table initiale pour la récurrence, basée sur les articles en stock SAV (SAV = Dépôt/Warehouse en '58D' ou '58S') à partir desquels on va chercher récursivement tous les cas d'emploi (parents, grand-parents, etc) */
    			select distinct [_].[ITNO - Item number]
    			from [M3JDTP500].[V_MITBAL - MF: Item - warehouse (MB)] as [_]
    			where ([_].[WHLO - Warehouse] = LEFT(@Facility, 2) + 'D' or [_].[WHLO - Warehouse] = LEFT(@Facility, 2) + 'S') and ([_].[STQT - On-hand balance approved] + [_].[QUQT - On-hand balance for inspection] + [_].[RJQT - Rejected on-hand balance]) <> 0
    		)
    		UNION ALL
    		SELECT
    			oBT.[PartNumber],
    			fBT.[ComponentPartNumber],
    			fBT.[BOM_Depth] + 1, /* Calcul de la profondeur d'arborescence */
    			CAST(oBT.[PartNumber] AS VARCHAR(MAX)) + ' / ' + fBT.[BOM_Path] /* Chaîne contenant l'arborescence complète du produit */
    			FROM originalBOMTable AS oBT
    			INNER JOIN filteredBOMTable AS fBT ON (oBT.[ComponentPartNumber] = fBT.[PartNumber])
    )
    /* Une fois la table récursive filteredBOMTable construite, il reste à compléter la table finale attendue via les infos obtenues par quelques JOIN ci-dessous.
     Les tables *_Comp récupèrent des informations appartenant au composant originel du cas d'emploi (issu de la table initiale utilisée pour la récurrence).
    Les autres tables récupèrent des informations appartenant au cas d'emploi (parent, grand-parent ou ...). */
    SELECT OB.[Facility],
    		t_MITBAL_Comp.[WHLO - Warehouse] AS [Warehouse],
    		fBT.[ComponentPartNumber] AS [Component Part Number],
    		t_MITMAS_Comp.[ITDS - Name] AS [Component Name],
    		t_MITMAS_Comp.[STAT - Status] AS [Component Status],
    		t_MITMAS_Comp.[ACRF - User-defined accounting control object] AS [Component Nature],
    		t_MITMAS_Comp.[ITGR - Item group] AS [Component Group],
    		t_CSYTAB_Comp.[TX40 - Description] AS [Component Group Name],
    		t_MITBAL_Comp.[IDDT - Last receipt date] AS [Last receipt date],
    		t_MITBAL_Comp.[ODDT - Last issue date] AS [Last issue date],
    		t_MITBAL_Comp.[SSQT - Safety stock] AS [Component Safety Stock],
    		t_MITBAL_Comp.[STQT - On-hand balance approved] AS [On-hand balance approved],
    		t_MITBAL_Comp.[QUQT - On-hand balance for inspection] AS [On-hand balance for inspection],
    		t_MITBAL_Comp.[RJQT - Rejected on-hand balance] AS [Rejected on-hand balance],
    		t_MITBAL_Comp.[REQT - Reserved quantity] AS [Reserved quantity],
    		fBT.[PartNumber] As [Item Part Number],
    		t_MITMAS.[ITDS - Name] AS [Item Name],
    		t_MITMAS.[FUDS - Description 2] AS [Item Description],
    		t_MITMAS.[STAT - Status] AS [Item Status],
    		t_MPDHED.[STAT - Status] As [Item Structure Status],
    		t_MITMAS.[ITGR - Item group] AS [Item Group],
    		t_CSYTAB.[TX40 - Description] AS [Item Group Name],
    		OB.[StructureType] AS [Item Structure Type],
    		fBT.[BOM_Depth],
    		fBT.[BOM_Path]
    	FROM filteredBOMTable AS fBT
       JOIN originalBOMTable AS OB ON fBT.PartNumber = OB.PartNumber
       INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITMAS - MF: Item Master File (MM)] AS t_MITMAS_Comp ON t_MITMAS_Comp.[ITNO - Item number] = OB.[ComponentPartNumber]
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITMAS - MF: Item Master File (MM)] AS t_MITMAS ON t_MITMAS.[ITNO - Item number] = OB.[PartNumber]
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITBAL - MF: Item - warehouse (MB)] AS t_MITBAL_Comp ON t_MITBAL_Comp.[ITNO - Item number] = OB.[ComponentPartNumber] AND (t_MITBAL_Comp.[WHLO - Warehouse] = LEFT(fBT.[Facility], 2) + 'D' OR t_MITBAL_Comp.[WHLO - Warehouse] = LEFT(fBT.[Facility], 2) + 'S')
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MPDHED - MF: Product file header (PH)] AS t_MPDHED ON t_MPDHED.[PRNO - Product] = fBT.[PartNumber] AND t_MPDHED.[FACI - Facility] = fBT.[Facility] AND t_MPDHED.[STRT - Product structure type] = fBT.[StructureType]
    	LEFT OUTER JOIN [M3_ODS_PROD].[M3JDTP500].[V_CSYTAB - MF: System tables (CT)] AS t_CSYTAB_Comp ON t_CSYTAB_Comp.[STCO - Constant value] = 'ITGR' AND t_CSYTAB_Comp.[STKY - Key value] = t_MITMAS_Comp.[ITGR - Item group]
    	LEFT OUTER JOIN [M3_ODS_PROD].[M3JDTP500].[V_CSYTAB - MF: System tables (CT)] AS t_CSYTAB ON t_CSYTAB.[STCO - Constant value] = 'ITGR' AND t_CSYTAB.[STKY - Key value] = t_MITMAS.[ITGR - Item group]
     
        OPTION (MAXRECURSION 10)
    Une requête récursive sera toujours lente car elle est traitée de manière itérative alors que les requêtes classiques sont traitées de manière ensembliste. Il faut donc en minimiser la "lourdeur".

    Néanmoins, pour optimiser votre code il faudrait avoir l'intégralité du DDL de vos tables et vues ainsi que les index posés.

    Une solution efficace aux requêtes récursives consiste à utiliser la modélisation intervallaire comme je l'indique ici :
    https://sqlpro.developpez.com/cours/arborescence/

    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

  3. #3
    Membre Expert
    Homme Profil pro
    Ingénieur
    Inscrit en
    Août 2010
    Messages
    673
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2010
    Messages : 673
    Par défaut
    Bonjour SQLPro,

    Merci pour ta réponse.

    1/
    J'ai fait des tests basés sur tes modifications ce matin en réduisant ma liste de composants à deux articles seulement pour que ça ne prenne pas trop de temps (ma requête initiale demande un peu plus de 4mn pour ces deux composants et ressort 269 lignes).
    Mais je n'obtiens pas la bonne table: la jointure
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    JOIN originalBOMTable AS OB ON fBT.PartNumber = OB.PartNumber
    conduit à une multiplication des lignes car un même PartNumber apparaît plusieurs fois dans OB / originalBOMTable (il apparaît autant de fois qu'il contient de références de composants).
    J'ai bien sûr tout de suite eu envie de modifier la jointure en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    JOIN originalBOMTable AS OB ON fBT.PartNumber = OB.PartNumber AND fBT.ComponentPartNumber = OB.ComponentPartNumber
    pour revenir à une unicité mais celle-ci est trop restrictive (ne fonctionne que pour un parent direct) alors que la table fBT contient en tant que ComponentPartNumber le composant racine et non l'élément directement enfant.
    Je n'arrive pas à trouver le bon correctif s'il y en a un.


    2/
    La modélisation intervallaire semble très efficace mais, si j'ai bien compris, elle requiert de pouvoir modifier la table contenue dans la base avec deux colonnes supplémentaires, ce sur quoi je n'ai pas la main (BDD de l'ERP; j'imagine que seul l'éditeur a la main ou à défaut le groupe mère de ma société). A moins qu'il soit pertinent et possible de rajouter ces colonnes dynamiquement le temps de la requête?
    Mais à la lecture du tutoriel, je ne suis pas sûr que ça soit applicable à mon cas puisque je n'ai pas une arborescence simple mais n arborescences imbriquées: un même composant peut appartenir à plusieurs nomenclatures, il peut même appartenir à une nomenclature enfant et à une nomenclature parent (typiquement, ça arrive avec des vis ou des résistances). Et dans l'autre sens, chaque nomenclature de plus haut niveau (i.e. qui n'appartient à aucune autre) contient généralement plusieurs éléments.
    Je comprends qu'il faudrait un jeu de colonnes par nomenclature de plus haut niveau ou par composant de plus bas niveau selon la façon dont on attaque le problème (mais dis-moi si ma compréhension est mauvaise).


    3/
    Je n'ai pas eu le temps de poster hier mais avant ton message, samedi soir (tard :p), j'ai poursuivi mes recherches sur internet et j'ai trouvé une suggestion via une boucle While qui semble donner de bonnes performances (tout en me sortant la même table):
    https://stackoverflow.com/questions/...-recursive-cte
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    /*DECLARE @Facility VARCHAR(3);
    SET @Facility='580';*/
    DECLARE @Cnt INT=1;
     
    WITH originalBOMTable (Facility, PartNumber, StructureType, ComponentPartNumber)
    AS
    (
    	SELECT t_MPDMAT.[FACI - Facility],
    		t_MPDMAT.[PRNO - Product],
    		t_MPDMAT.[STRT - Product structure type],
    		t_MPDMAT.[MTNO - Component number]
    		FROM [M3JDTP500].[V_MPDMAT - MF: Connect materials (PM)] AS t_MPDMAT
    		INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITMAS - MF: Item Master File (MM)] AS t_MITMAS ON t_MITMAS.[ITNO - Item number] = t_MPDMAT.[PRNO - Product] AND t_MITMAS.[STAT - Status] NOT LIKE '9%'
    		WHERE (t_MPDMAT.[TDAT - To date] = 99999999 AND t_MPDMAT.[FACI - Facility] = @Facility AND t_MPDMAT.[STRT - Product structure type] = 'MET')
    )
    SELECT [Facility],
    	[PartNumber],
    	[StructureType],
    	[ComponentPartNumber],
    	BOM_Depth=1,
    	BOM_Path=CAST([PartNumber] AS VARCHAR(MAX))
    	INTO #filteredBOMTable
    	FROM originalBOMTable
    	WHERE [ComponentPartNumber] in (/*'S100697', 'S113400')*//*échantillon de test*/
    		SELECT DISTINCT [_].[ITNO - Item number]
    		FROM [M3JDTP500].[V_MITBAL - MF: Item - warehouse (MB)] AS [_]
    		WHERE ([_].[WHLO - Warehouse] = LEFT(@Facility, 2) + 'D' OR [_].[WHLO - Warehouse] = LEFT(@Facility, 2) + 'S') AND ([_].[STQT - On-hand balance approved] + [_].[QUQT - On-hand balance for inspection] + [_].[RJQT - Rejected on-hand balance]) <> 0
    	)
     
    WHILE @Cnt<=30     --<< Set Your Max Level
    	BEGIN
    		INSERT INTO #filteredBOMTable 
    		SELECT oBT.[Facility],
    		oBT.[PartNumber],
    		oBT.[StructureType],
    		fBT.[ComponentPartNumber],
    		fBT.[BOM_Depth] + 1,
    		CAST(oBT.[PartNumber] AS VARCHAR(MAX)) + ' / ' + fBT.[BOM_Path]
    		FROM (
    			/* Ici, j'ai dû dupliquer la requête utilisée pour originalBOMTable car (sauf incompréhension de ma part sur la cause racine) je n'ai pas réussi à formuler la filteredBOMTable en tant que CTE sans erreur de syntaxe à cause du While */
    			SELECT t_MPDMAT.[FACI - Facility] AS [Facility],
    				t_MPDMAT.[PRNO - Product] AS [PartNumber],
    				t_MPDMAT.[STRT - Product structure type] AS [StructureType],
    				t_MPDMAT.[MTNO - Component number] AS [ComponentPartNumber]
    				FROM [M3JDTP500].[V_MPDMAT - MF: Connect materials (PM)] AS t_MPDMAT
    				INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITMAS - MF: Item Master File (MM)] AS t_MITMAS ON t_MITMAS.[ITNO - Item number] = t_MPDMAT.[PRNO - Product] AND t_MITMAS.[STAT - Status] NOT LIKE '9%'
    				WHERE (t_MPDMAT.[TDAT - To date] = 99999999 AND t_MPDMAT.[FACI - Facility] = @Facility AND t_MPDMAT.[STRT - Product structure type] = 'MET')
    		) AS oBT
    		INNER JOIN #filteredBOMTable AS fBT ON (fBT.[BOM_Depth] = @Cnt AND oBT.[ComponentPartNumber] = fBT.[PartNumber])
    		SET @Cnt=@Cnt+1
    	END
    SELECT fBT.[Facility],
    		t_MITBAL_Comp.[WHLO - Warehouse] AS [Warehouse],
    		fBT.[ComponentPartNumber] AS [Component Part Number],
    		t_MITMAS_Comp.[ITDS - Name] AS [Component Name],
    		t_MITMAS_Comp.[STAT - Status] AS [Component Status],
    		t_MITMAS_Comp.[ACRF - User-defined accounting control object] AS [Component Nature],
    		t_MITMAS_Comp.[ITGR - Item group] AS [Component Group],
    		t_CSYTAB_Comp.[TX40 - Description] AS [Component Group Name],
    		t_MITBAL_Comp.[IDDT - Last receipt date] AS [Last receipt date],
    		t_MITBAL_Comp.[ODDT - Last issue date] AS [Last issue date],
    		t_MITBAL_Comp.[SSQT - Safety stock] AS [Component Safety Stock],
    		t_MITBAL_Comp.[STQT - On-hand balance approved] AS [On-hand balance approved],
    		t_MITBAL_Comp.[QUQT - On-hand balance for inspection] AS [On-hand balance for inspection],
    		t_MITBAL_Comp.[RJQT - Rejected on-hand balance] AS [Rejected on-hand balance],
    		t_MITBAL_Comp.[REQT - Reserved quantity] AS [Reserved quantity],
    		fBT.[PartNumber] As [Item Part Number],
    		t_MITMAS.[ITDS - Name] AS [Item Name],
    		t_MITMAS.[FUDS - Description 2] AS [Item Description],
    		t_MITMAS.[STAT - Status] AS [Item Status],
    		t_MPDHED.[STAT - Status] As [Item Structure Status],
    		t_MITMAS.[ITGR - Item group] AS [Item Group],
    		t_CSYTAB.[TX40 - Description] AS [Item Group Name],
    		fBT.[StructureType] AS [Item Structure Type],
    		fBT.[BOM_Depth],
    		fBT.[BOM_Path]
    	FROM #filteredBOMTable AS fBT
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITMAS - MF: Item Master File (MM)] AS t_MITMAS_Comp ON t_MITMAS_Comp.[ITNO - Item number] = fBT.[ComponentPartNumber]
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITMAS - MF: Item Master File (MM)] AS t_MITMAS ON t_MITMAS.[ITNO - Item number] = fBT.[PartNumber]
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MITBAL - MF: Item - warehouse (MB)] AS t_MITBAL_Comp ON t_MITBAL_Comp.[ITNO - Item number] = fBT.[ComponentPartNumber] AND (t_MITBAL_Comp.[WHLO - Warehouse] = LEFT(fBT.[Facility], 2) + 'D' OR t_MITBAL_Comp.[WHLO - Warehouse] = LEFT(fBT.[Facility], 2) + 'S')
    	INNER JOIN [M3_ODS_PROD].[M3JDTP500].[V_MPDHED - MF: Product file header (PH)] AS t_MPDHED ON t_MPDHED.[PRNO - Product] = fBT.[PartNumber] AND t_MPDHED.[FACI - Facility] = fBT.[Facility] AND t_MPDHED.[STRT - Product structure type] = fBT.[StructureType]
    	LEFT OUTER JOIN [M3_ODS_PROD].[M3JDTP500].[V_CSYTAB - MF: System tables (CT)] AS t_CSYTAB_Comp ON t_CSYTAB_Comp.[STCO - Constant value] = 'ITGR' AND t_CSYTAB_Comp.[STKY - Key value] = t_MITMAS_Comp.[ITGR - Item group]
    	LEFT OUTER JOIN [M3_ODS_PROD].[M3JDTP500].[V_CSYTAB - MF: System tables (CT)] AS t_CSYTAB ON t_CSYTAB.[STCO - Constant value] = 'ITGR' AND t_CSYTAB.[STKY - Key value] = t_MITMAS.[ITGR - Item group]
    Sur mon échantillon de deux articles, je passe de 4mn12s à 12s.
    Sur le périmètre complet (Facility = '580'), je passe de plus de 14h à 1mn31s.

    Cette écriture commence à dépasser ma maîtrise du SQL; je n'ai ainsi pas réussi à conserver filteredBOMTable en tant que CTE et j'ai été obligé de dupliquer la requête correspondant à originalBOMTable donc je la trouve mal écrite mais je n'arrive pas à l'améliorer.

    NB: Ma requête n'est appelée à être exécutée qu'une fois par mois max pour chaque établissement donc ces perfs semblent suffisantes mais j'aimerais arriver au bout des deux méthodes (i.e. les écrire de la meilleure façon possible) pour améliorer ma compréhension du SQL.

  4. #4
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 993
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 21 993
    Billets dans le blog
    6
    Par défaut
    Effectivement si cela n'est pas un arborescence, la simplification intervallaire ne va pas marcher.

    Ce que tu peut faire c'est de rajouter dans un filtre WHERE de la requête d'itération un test pour éliminer une branche déjà parcourue....

    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

Discussions similaires

  1. [AC-2010] Aide Résolution Requête SQL récursive
    Par Nouse dans le forum Requêtes et SQL.
    Réponses: 5
    Dernier message: 23/11/2016, 18h09
  2. Requête SQL récursive dans la clause WHERE
    Par haykelFST dans le forum Développement
    Réponses: 8
    Dernier message: 12/04/2012, 10h37
  3. [AC-2007] Requête SQL à Access (problème avec conversion)
    Par kh424 dans le forum Requêtes et SQL.
    Réponses: 2
    Dernier message: 28/02/2011, 20h24
  4. [12.5.4]Problème de performances sur requête SQL
    Par tdeco dans le forum Adaptive Server Enterprise
    Réponses: 5
    Dernier message: 25/05/2010, 22h06
  5. [SQL Server]Problème avec une requête récursive
    Par evans dans le forum Langage SQL
    Réponses: 3
    Dernier message: 05/04/2006, 20h16

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo