Lier 3 tables [Résolu]

Signaler
Messages postés
36
Date d'inscription
lundi 17 mars 2008
Statut
Membre
Dernière intervention
16 mai 2008
-
Messages postés
6352
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
-
Bonjour à tous,

Je développe un projet MVS2005 en C#  sur une BBD Sql server 2005. La première chose qu'il faut que j'arrive a faire est d'afficher la liste des composants qui composent un produit. Malheureusement dans ma BDD pour l'intégrité des données j'ai une table de tansition (DETAIL) entre ma table COMPONENT et ma table PRODUCT...
Donc PODUCT (product_id, product_name....) DETAIL(detail_id, product_id,compo_id, quantity...) COMPONENT (compo_id, compo_name,value,...)
A l'aide du gestionnaire "Source de donné" (donc sans écrire de code)...J'arrive a créer 3 datagridview qui sont liés entre eux mon DgvDetail m'affiche bien toutes les entrés qui correspondent au produit sélectionné dans le DgvProduct (pour un unique produc_id). Par contre mon DgvCompo ne m'affiche que l'entré sélectionné dans le DgvDetail...(pour un unique compo_id)
Comment faire pour afficher dans mon DgvCompo tous les composents "utilisés"  dans le DgvDetail... C a d retrouver pour chaque entré du DataAdapter de mon DgvDetail le composant correspondant et l'afficher (pour chaque compo_id)

J'essai en vain de changer la requete qui rempli mon CompoAdapter... Mais rien n'y fait...Je ne réussi à afficher qu'une entré correspondant à la ligne sélectionné dans le DgvDetail...
Je pensais qu'une requete du genre ... SELECT * FROM dbo.COMPONENT WHERE EXISTS  ( SELECT * FROM DETAIL WHERE COMPENENT.compo_id= DETAIL.compo_id)  meme en métant dans la sous requete des conditions bidon du genre WHERE compo_id<50 ca me renvoi qu'une entré...

Si quelqu'un peu m'aider ca me sauverais...Je suis méga en retard sur mon projet et je stagne sur ce probléme depuis trop longtemps...
Merci d'avance

Pierre qui roule n'amasse pas mousse...

16 réponses

Messages postés
6352
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
82
Salut,

Heu, où est passé l'ID du produit dans cette histoire, c'est quand même lui qui est censé permettre de filtrer la liste des composants.

A vue de nez pour la requête, en restant sur l'idée de la sous-requête, ça donnerait quelquechose du genre :
SELECT * -- mettre plutot la liste des colonnes ici
FROM [dbo].[COMPONENT] AS Components
WHERE Components.[compo_id] IN ( SELECT [compo_id]
                                FROM [dbo].[DETAIL] AS Details
                                WHERE Details.[product_id] = @productID
                                )

Et avec une jointure plutot ceci :
SELECT Components.* -- mettre plutot la liste des colonnes ici
FROM [dbo].[COMPONENT] AS Components
    INNER JOIN [dbo].[DETAIL] AS Details ON Components.[compo_id]=Details.[compo_id]
                                                AND Details.[product_id]=@productID

En gardant quand même une préférence pour la seconde qui devrait avoir un cout moins important à l'execution (à tester).

/*
coq
MVP Visual C#
CoqBlog
*/
Messages postés
36
Date d'inscription
lundi 17 mars 2008
Statut
Membre
Dernière intervention
16 mai 2008

Bon a priori c'est fait...

Avec le currency manager on peut récupérer les évènements intéressants pour mettre a jour les table au bon moment avec les valeurs "sélectionné".
Et pour le remplissage j'utilise la requete que coq donne en début de post.....Merci Coq

Pour l'utilisation du currencyManager l'ami Arthenius nous a fait un joli exemple...

http://www.csharpfr.com/codes/LIAISON-TABLE-GRACES-CURRENCYMANAGER_46670.aspx

Voila mon code :)

public partial class Form1 : Form
    {
        CurrencyManager CM_Product;
        CurrencyManager CM_Detail;
        SqlDataAdapter Da_Product;
        SqlDataAdapter Da_Detail;
        SqlDataAdapter Da_Component;
        string MyConnectionString = "Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\MBSDB.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
    
        public Form1()
        {
            InitializeComponent();

        }

        private void pRODUCTBindingNavigatorSaveItem_Click(object sender, EventArgs e)
        {
            this.Validate();
            this.pRODUCTBindingSource.EndEdit();
            this.pRODUCTTableAdapter.Update(this.mBSDBDataSet.PRODUCT);

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //On creer les dataadapter dont on aura besoin Mais parcontre SELECT * A changer c'est maaaaaal :)
            Da_Product = new SqlDataAdapter("SELECT * FROM PRODUCT", new SqlConnection(MyConnectionString));            Da_Detail new SqlDataAdapter("SELECT * FROM PR_CP_DETAIL WHERE PRODUCT_ID @PRODUCT_ID ", new SqlConnection(MyConnectionString));
            //A changer en fonction du type de ton Product_id (varchar, numeric ou autre)            Da_Component new SqlDataAdapter("SELECT * FROM [dbo].[COMPONENT] AS Components WHERE Components.[compo_id] IN ( SELECT [compo_id] FROM [dbo].[PR_CP_DETAIL] AS Details WHERE Details.[product_id] @PRODUCT_ID)", new SqlConnection(MyConnectionString));
           
           
            Da_Detail.SelectCommand.Parameters.Add("@PRODUCT_ID", SqlDbType.Decimal);
            Da_Component.SelectCommand.Parameters.Add("@PRODUCT_ID", SqlDbType.Decimal); //A changer en fonction du type de ton COMPO_ID (varchar, numeric ou autre)

            //On affecte les datatable au datagridview
            pRODUCTDataGridView.DataSource = mBSDBDataSet.PRODUCT;
            pR_CP_DETAILDataGridView.DataSource = mBSDBDataSet.PR_CP_DETAIL;
            cOMPONENTDataGridView.DataSource = mBSDBDataSet.COMPONENT;

            //On instancie les Currencymanager
            CM_Product = (CurrencyManager)BindingContext[mBSDBDataSet.PRODUCT];
            CM_Detail = (CurrencyManager)BindingContext[mBSDBDataSet.PR_CP_DETAIL];

            //On s'abbonne au evenement de changement de position de ligne dans les 2 datagridview product et detail (via les currencymanager)
            CM_Product.PositionChanged += new EventHandler(CM_Product_PositionChanged);
            CM_Detail.PositionChanged += new EventHandler(CM_Detail_PositionChanged);

            //On charge la table product
            Da_Product.Fill(mBSDBDataSet.PRODUCT);

       
        }
        void CM_Detail_PositionChanged(object sender, EventArgs e)
        {
            //Ici on charge la table COMPONENT===>on recupere le compo_id sur ds.DETAIL =>on rempli le dataset ds.COMPONENT
            if (CM_Detail.Count > 1)//au moins un produit dans la liste
            {
                mBSDBDataSet.COMPONENT.Rows.Clear();
                //on affecte le product_id de la ligne courante de detail au dataadapter component
                Da_Component.SelectCommand.Parameters["@PRODUCT_ID"].Value = (decimal)((DataRowView)CM_Detail.Current)["PRODUCT_ID"];
                Da_Component.Fill(mBSDBDataSet.COMPONENT);
                //ca marche car on sait que le product_id dans detail est le meme que celui du product sélectionné sinon on peut aussi aller chercher le product_id dans product directemnt
            }
        }
        void CM_Product_PositionChanged(object sender, EventArgs e)
        {
            //Ici on charge la table detail===>on recupere le product_id de DS.PRODUCT =>on rempli le dataset ds.DETAIL
            if (CM_Product.Count > 1)//au moins un produit dans la liste
            {
                mBSDBDataSet.PR_CP_DETAIL.Rows.Clear();
                //on affecte le PRODUCT_ID de la ligne courante de PRODUCT au dataadapter Detail
                Da_Detail.SelectCommand.Parameters["@PRODUCT_ID"].Value = (decimal)((DataRowView)CM_Product.Current)["PRODUCT_ID"];
                Da_Detail.Fill(mBSDBDataSet.PR_CP_DETAIL);
            }
        }
    }

dsl je connais pas les balises pour le code...

Pierre qui roule n'amasse pas mousse...
Messages postés
36
Date d'inscription
lundi 17 mars 2008
Statut
Membre
Dernière intervention
16 mai 2008

Salut Coq,

Pour le product_id vu que DETAIL est directement (et correctement) lié à PRODUCT, je pense qu'il n'est plus vraiment nécessaire de remonter jusqu'a lui...

Tu utiliserais donc une requete paramaètré, est- il possible d'éviter cela? Car ca implique fournir une valeur du paramètre. C'est l'une des solutions que j'ai retenu aussi (sauf que ma requete n'utilisait pas de AS...) mais je n'ai pas eu bcp de succès pour passer le param au bon moment.
J'avais utiliser deux méthodes :

- soit je récupére le paramètre manuellement genre en prenant la valeur contenu dans la colonne product_id de mon DgvDetail.. Ca marche j'affiche les composants mais ceux qui sont affichés avant le load ...(J'ai du mal/je sais pas me lier au bon event?).
- Ma deuxième tentative a été d'ajouter un paramètre en output dans les propriété de la requete qui remplie mon DataadapterDetail mais j'arrive pas à récolter une valeur pour la réinjecter dans la requete qui remplie DataAdapter COMPONENT...Le parametre reste NULL...

Je test avec ta requete...
Messages postés
36
Date d'inscription
lundi 17 mars 2008
Statut
Membre
Dernière intervention
16 mai 2008

Bon ca fonctionne plutot bien avec ta requete en lui passant manuelement le parametre (1ere méthode que énoncé plus haut)...Mais comme prévu il me reste le probléme de l'évènement à utiliser pour la mise à jour...Je me dis qu'un OnCellValueChanger ca va bien le faire mais vue que je me base sur une case du DgvDetail pour récupérer la valeur du param (productid). Ca me pause un probléme a l'initialisation (Exeption ReferenceNull). Je pense que l'événement "arrive" avant la création de la cellule ou je récupère ma valeur...Il me faut donc trouver une condition qui lui dise d'attendre que la cell soit créée avant d'aller regarder ca valeur...j'ai pensé à un truc du genre :

 private void DETAILDataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
 {         
 if (DgvDetail.created = = true)
{
 Decimal Param = DgCdetail.CurrentRows.Cell[1].Value;
this.cOMPONENTTableAdapter.FillByCoqMeth(this.mBSDBDataSet.COMPONENT, prodparam);
}
}

Mais bon visiblement created n'est jamais true...Je pense avoir testé toutes les propriétés qui pourraient aller...mais rien ne va. Qu'est ce que je pourrai mettre a la place du .created? Dois-je prendre un autre event??
Messages postés
6352
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
82
Moi et le databinding, ça fait 2, il va falloir attendre quelqu'un d'autre.

/*
coq
MVP Visual C#
CoqBlog
*/
Messages postés
36
Date d'inscription
lundi 17 mars 2008
Statut
Membre
Dernière intervention
16 mai 2008

Merci Quand même tu m'as bien aidé....
Il me suffirait de trouver une manière de bien faire ma mise a jour et mes tables sont a peu près liées.
Messages postés
36
Date d'inscription
lundi 17 mars 2008
Statut
Membre
Dernière intervention
16 mai 2008

 Dis peut être que par contre tu sais faire une requête qui fourni un paramètre en output? Cela existe-il?
 Ça serait selon moi un moyen efficace de faire ma 2ème liaison.
J'ai remarqué que dans les propriétés de la requête qui remplie mon dataadapter on peut insérer un paramètre de type output. Du coup il faut lui fournir une variable du type dans mon cas decimal? paramproduct_id ca compile mais il semble que paramproduct_id soit toujours null...

P.S : J'ai du mal a croire que je sois le seul à essayer de lier 3 tables! La structure de ma bdd me parait classique...
Messages postés
6352
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
82
Pour l'output, ça se joue avec la propriété ParameterDirection de l'instance de SqlParameter.

/*
coq
MVP Visual C#
CoqBlog
*/
Messages postés
6352
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
82
Et il faut bien entendu que la requête lui affecte une valeur.

/*
coq
MVP Visual C#
CoqBlog
*/
Messages postés
36
Date d'inscription
lundi 17 mars 2008
Statut
Membre
Dernière intervention
16 mai 2008

Ma maniere de faire est la suivante...

Dans mon dataset je définis la requete et dans les propriétés je définis un paramètre avec en direction output. Je le fais donc avec l'assistant mais je pense que ca équivaut à ce que tu préconise...

Pour affecté la valeur retour je définis une variable para (j'ai mi le ? par rapport aux erreur qui m'étaient retournés)...et je la place en utilisant out dans mon appel...

decimal? para;
this.TableAdapter.Fill(this.mBSDBDataSet.PR_CP_DETAIL, out para);

Mais para semble être toujours null...
Messages postés
6352
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
82
Oui, mais est ce que la requête select de l'adapter donne une valeur au paramètre ?

/*
coq
MVP Visual C#
CoqBlog
*/
Messages postés
36
Date d'inscription
lundi 17 mars 2008
Statut
Membre
Dernière intervention
16 mai 2008

NON...J'ai mis du temps à comprendre ta question...Mais il est clair que même si j'ai défini un paramètre ce n'est pas pour autant que la requète retourne une valeur...Si j'ai bien compirs ta question,Merci d'avoir ouvert mes yeux.
Quel serait le forme d'une requete qui retourne une valeur pour para?
Messages postés
6352
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
82
C'est du T-SQL standard :
SET @MonParam = ...
SELECT @MonParam = ...

Maintenant j'ai un doute, moi et le databinding...

/*
coq
MVP Visual C#
CoqBlog
*/
Messages postés
36
Date d'inscription
lundi 17 mars 2008
Statut
Membre
Dernière intervention
16 mai 2008

LE "Last step" pour moi est d'afficher les dgv de component et détail dans un seul dgv...C quand mem plus jolie....Si vous avez une idée...
Messages postés
36
Date d'inscription
lundi 17 mars 2008
Statut
Membre
Dernière intervention
16 mai 2008

Bon voila....J'essaye d'imbriquer mon dgvComponent dans mon dgvDetail pour cela j'ai créé une datatable avec tous les champs qui m'intéresse et je cherche la requete qui va me donner la bonne liste...
En m'inspirant des deux requètes que Coq m'a conseillé j'obtient ca :

SELECT device, value, manufacturer, category, parts, von_mbs, quantity
 FROM COMPONENT, PR_CP_DETAIL
 AS compo
WHERE compo.[compo_id]
 IN ( SELECT [compo_id] FROM [dbo].[PR_CP_DETAIL] AS Details WHERE Details.[product_id] = @PRODUCT_ID)"

cette requete fonctionne mais elle me retourne des "associations" non souhaité. En clair elle me retourne toutes les combinaisons possibles...

Je pense qu'une jointure serait en effet plus appropriée afin d'éviter toutes les combinaisons non cohérentes...
Celon moi ca donnerais ca :

SELECT device, value, manufacturer, category, parts, von_mbs, quantity
 FROM [dbo].[COMPONENT], [dbo].[PR_CP_DETAIL]
 AS Compo
INNER JOIN [dbo].[PR_CP_DETAIL]
AS Details
 ON Compo.[compo_id]=Details.[compo_id] AND Details.[product_id]=@PRODUCT_ID

Malheureusement je n'arrive pas à la faire fonctionner, comme je vous la présente parts, von_mbs, quantity sont des "colonnes ambigues".
avec [PR_CP_DETAIL].parts, [PR_CP_DETAIL].von_mbs...etc il me retourne "could not bound" donc il ne les reconnais pas
avec [MBSDB].[PR_CP_DETAIL].parts...etc de même....
MBSDB c'est ma base de donné et ca ne marche pas même en présicant [MBSDB.mdf]...

Si vous pouviez m'aider à trouver l'erreur, ca me sauverais une fois de plus...

Merci d'avance

Pierre qui roule n'amasse pas mousse...
Messages postés
6352
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
82
Salut,

C'est normal ça ? :

SELECT device, value, manufacturer, category, parts, von_mbs, quantity
 FROM [dbo].[COMPONENT], [dbo].[PR_CP_DETAIL]
 AS Compo
INNER JOIN [dbo].[PR_CP_DETAIL]
AS Details
 ON Compo.[compo_id]=Details.[compo_id] AND Details.[product_id]=@PRODUCT_ID

/*
coq
MVP Visual C#
CoqBlog
*/