web-dev-qa-db-fra.com

Query efficace pour un modèle EAV avancé

Je suis un peu coincé sur la manière de créer une requête efficace qui retourne les données de la structure EAV suivante.

Aujourd'hui, une table de produits existe déjà, contenant 4 champs fixes. Nous souhaitons mettre à niveau le système, permettant une quantité infinie de champs de produits supplémentaires, définis par le fabricant de chaque produit. Nous appelons ces champs supplémentaires 'Paramètres'.

Un paramètre peut être du type de données suivant:

  • Texte
  • Date (portée)
  • Booléen
  • DropdownValue (célibataire)
  • DropdownValues ​​(multiple)

Sur la base de cela, j'ai créé le modèle de base de données suivant:

Database Scheme :

  • Produit: la table de produits d'origine contenant les champs fixes.
  • paramètre: Ce tableau définit tous les types de paramètres. Actuellement 5: Texte, date, Boolean, valeur de liste déroulante (unique), valeur déroulante (multiple)
  • fabricantParameter: Cette table enregistre quels paramètres sont définis par le fabricant de produits (tableau du fabricant non inclus dans l'image) pour tous ses produits.
  • ProductParameter: C'est ici que les données du produit réelles de tous les paramètres définis sont stockées. Un paramètre du type "texte", stocke ses informations dans le champ "Texte", "Date" dans "Daumbegin" et "Datumend", Bit/Boolean dans "Boolean".
  • Parametervaluaulistitem: Ce tableau contient les valeurs prédéfinies des valeurs DropdownList pour un couplage unique et Mulipe. Ce sont les valeurs que vous obtenez dans la liste déroulante lors de la création ou de la modification d'un produit.
  • ProductParameValuelistitem: C'est ici que les données du produit réelles sont stockées pour les valeurs DropdownList.

J'ai besoin de créer une page Web dans ASP.NET C # qui affiche une liste de toutes les données de produit, y compris chaque paramètre défini avec sa valeur pour ce produit. Je dois être capable de filtrer la liste en recherchant n'importe quel champ/paramètre. Cette liste doit également être tritable sur n'importe quel champ/paramètre.

J'ai essayé de filtrer la liste par produit de jonction externe - ProductParameter - ProductParameTervalutuisitem - ParameTervaluLisitem Pour obtenir un résultat de la table contenant toutes les données, puis filtrant cette liste à l'aide d'une clause dynamique, construite et passée par l'application Web. Cela fonctionne lorsque vous recherchez sur 1 paramètre, mais lorsque vous commencez à rechercher plusieurs paramètres, vous obtenez des résultats incorrects car chaque paramètre est une ligne différente du résultat de la table et il n'y a tout simplement pas de paramètre A et B, car ils n'existe pas dans le même record (ligne).

Quelqu'un peut-il conseiller comment cela peut être atteint? La solution la plus parfaite serait 1 résultat de la table contenant toutes les données et chaque paramètre étant indiqué comme une colonne de la même ligne de produit. Est-ce trop complexe pour gérer au niveau de la base de données?

Merci de lire aussi loin. Tout conseil est le bienvenu.

7
Zeep

Tout d'abord, ce que vous êtes sur le point de concevoir est probablement une très mauvaise idée. Une solution bien meilleure serait d'avoir un schéma dynamique sur lequel vous ajoutez de nouvelles tables et que vous avez la possibilité de comprendre comment interroger ces table (vous pouvez les placer dans un schéma). Cela évite largement tous les problèmes de plan de verrouillage et de requête que vous souhaitez rencontrer avec ce modèle. Il n'y a rien de mal avec les applications exécutées CREATE TABLE maintenant et encore.

Deuxièmement, je ne suis pas sûr de comprendre pourquoi vous avez normalisé Parameter dans sa propre table? Pourquoi ne pas mettre cela directement dans la table ManufacturerParameter.

Troisièmement, si vous insistez sur la procédure avec votre modèle actuel, il existe des moyens d'atteindre ce que vous voulez (au moins si j'interprète votre exigence correctement). Ce que vous pouvez faire, c'est d'écrire votre requête de telle sorte qu'il s'agisse d'une résolution de l'argument de recherche lorsqu'il y a une correspondance, puis utilisez HAVING pour filtrer les valeurs correspondantes. Je suppose que seul l'un des champs Text, Boolean, Datum etc. est peuplé par un enregistrement ProductParameter (vous voulez probablement appliquer cela avec une contrainte )

Par exemple, pour rechercher tous les produits qui ont un bolien = true pour un paramètre et texte = 'abc' pour un autre paramètre que vous pouvez faire:

SELECT P.Name
FROM Product P
JOIN ProductParameter PP
WHERE P.ID = Foo
  AND PP.Boolean = 1 OR PP.Text = 'abc'  ... /* For each filter */
GROUP BY P.Name /* And any other things you want out of product */
HAVING COUNT(*) >= [Number of where clauses]

Si vous devez répertorier tous les paramètres de ce produit, vous pouvez utiliser le modèle de requête ci-dessus comme requête imbriquée et se joindre à ProductParameter.

La requête ci-dessus peut être optimisée en maintenant une colonne calculée dans ProductParameter qui présente une représentation de chaîne des différents types de données dans ce tableau. De cette façon, les états ci-dessus OR peuvent être réécrites comme une liste dans la liste (que vous voudrez passer comme paramètre de valorisation de la table).

Je voudrais répéter que ce que vous faites est probablement très mal. Si vous le faites, vous aurez probablement besoin d'accorder la plupart de vos plans de requête - l'optimiseur ne vous aidera plus. Et cela suppose que vous n'avez pas trop de variantes de requête, qui dirigeront votre cache de plan complète.

3
Thomas Kejser

Quelqu'un peut-il conseiller comment cela peut être atteint? La solution la plus parfaite serait 1 résultat de la table contenant toutes les données et chaque paramètre étant indiqué comme une colonne de la même ligne de produit. Est-ce trop complexe pour gérer au niveau de la base de données?

Vous pouvez utiliser Dynamic SQL pour pivoter le EAV dans une table avec de nombreuses colonnes (soyez conscient de la limite de colonne de 1024) et d'avoir chaque produit sur une ligne. Je ne pense pas que cela fonctionnerait ainsi que la table indexée plus traditionnelle et la plus traditionnelle, mais vous pourriez essayer de concrétiser le résultat dans une table et de mettre des index à ce sujet.

1
g2server