2010-06-26 27 views
5

Je suis en train de créer une application de gestion pour aider à gérer ma société de détaillants d'automobiles mobiles (et, espérons-le, d'autres). J'ai du mal à comprendre comment modéliser certaines données.Rendez-vous et postes individuels

Cette question est liée à une question précédente que j'ai posté, mais je l'ai reproduit les informations pertinentes ci-dessous: Database design - google app engine

Dans cette application, il y a des notions de « rendez-vous » et « les éléments de ligne. "

Les rendez-vous sont un lieu et un moment où les employés doivent être en mesure de fournir un service.

Les éléments de campagne constituent un service, des frais ou des remises et leurs informations associées. Un exemple des postes qui pourraient aller dans un rendez-vous:

 
Name:       Price: Commission: Time estimate 
Full Detail, Regular Size:  160  75  3.5 hours 
$10 Off Full Detail Coupon:  -10  0   0 hours 
Premium Detail:     220  110  4.5 hours 
Derived totals(not a line item): $370  $185  8.0 hours 

Dans ma précédente mise en œuvre de cette application, les éléments de ligne ont été endigués par un seul rendez-vous. Cela a fonctionné très bien la plupart du temps, mais a causé des problèmes parfois. Un exemple serait si un rendez-vous a été interrompu à mi-chemin à cause de la pluie et le technicien a dû revenir le lendemain et finir. Cette situation nécessitait deux rendez-vous pour le même élément de campagne. Dans des cas comme celui-ci, je voudrais juste faufiler les données un peu en définissant le "élément de ligne" sur le deuxième rendez-vous pour lire quelque chose comme "Terminer Up", puis le coût serait de 0 $.

Dans cette nouvelle version, j'envisage permettant les éléments de ligne d'être jumelés à plus d'un rendez-vous avec une structure de table qui ressemble à ceci:

Appointment 
start_time 
etc... 

Line_Item 
appointment_Key_List 
name 
price 
etc... 

Un problème général avec cette structure est qu'elle est compliquée et je ne suis même pas sûr s'il est approprié de faire correspondre un élément de campagne avec plusieurs rendez-vous. Si les éléments de campagne ne peuvent faire partie que d'un seul rendez-vous, alors je peux simplement mettre une liste d'éléments de ligne dans chaque rendez-vous, quand j'obtiens des rendez-vous, je recevrais déjà des éléments de campagne. Un problème plus spécifique est que j'utilise google app engine et si je veux interroger un ensemble de rendez-vous et leurs éléments de campagne associés, je dois d'abord interroger l'ensemble des rendez-vous, puis faire une seconde interroger les éléments de campagne à l'aide de l'opérateur IN pour tester si l'une des clés de rendez-vous Line_Item correspond à l'ensemble des clés de rendez-vous renvoyées par la requête précédente. La deuxième requête échouera si j'ai plus de 30 clés qui me demandent de partitionner la requête. Je pourrais dénormaliser les données pour éviter cette requête de lecture compliquée et étendue, et je devrai probablement dénormaliser dans une certaine mesure de toute façon, mais je préfère éviter la complexité le cas échéant.

Ma question est de savoir comment ce type de situation est-il habituellement modélisé? Est-il même approprié d'associer un élément de campagne à plusieurs rendez-vous, ou est-il normal de simplement diviser les éléments de ligne en plusieurs éléments distincts pour chaque rendez-vous, par exemple «1ère moitié d'un emploi de 2 jours»? " Comment les applications similaires réussies le font-elles? Quelles sont les règles de base dans ce type de situation? Quelles mises en œuvre se sont révélées moins problématiques?

Merci!

Répondre

2

L'approche que vous suggérez fonctionnera bien; Vous pouvez modéliser la liste de rendez-vous de l'élément de campagne en tant que propriété de liste et cela fonctionnera comme prévu. Vous n'avez pas besoin d'utiliser l'opérateur IN - pour faire correspondre une seule valeur dans la banque de données avec une liste de clés (par exemple, "WHERE datastore_column IN ('a', 'b', 'c')), vous faites l'inverse - faire correspondre une seule valeur avec une liste dans le magasin de données

Je suggérerais, cependant, que l'inverse pourrait être mieux adapté à votre tâche: Avoir chaque rendez-vous ont une liste de clés d'article. Cela fonctionne de la même manière, mais pour récupérer toutes les données d'un rendez-vous, vous devez d'abord aller chercher le rendez-vous, puis faire un bulk sur les éléments de ligne, en utilisant les touches de l'entité Rendez-vous. , vous évitez ainsi le besoin de faire des requêtes du tout.

J'ai essayé d'expliquer à Pindatjuh pourquoi l'interrogation d'une propriété de liste n'est pas moins efficace qu'une propriété de valeur unique, mais une description plus détaillée est nécessaire, donc sans plus attendre, voici ...

une brève introduction sur l'indexation App Engine datastore

Bien que Python et Java fournissent différentes interfaces de haut niveau au datastore, le datastore lui-même parle d'une abstraction bas niveau, appelées entités. Une entité se compose des éléments suivants:

  1. Une clé primaire unique
  2. Une liste de (nom, valeur) paires

La clé primaire est la clé Datastore vous êtes déjà familier avec. La liste des paires (nom, valeur) est la représentation d'App Engine pour les données de votre entité. Jusqu'à présent, si simple. Une entité avec les valeurs suivantes:

a_string = "Hello, world" 
an_int = 123 

serait sérialisé à quelque chose qui ressemble à ceci:

[('a_string', 'Hello, world'), ('an_int', 123)] 

Mais comment cette interaction avec les listes? Eh bien, les listes sont traitées comme des propriétés «à valeurs multiples». En d'autres termes, une liste contenant n éléments est stockée sous forme de n propriétés distinctes. Un exemple est probablement cela plus clair:

a_string = "Hello, world" 
an_int = 123 
a_list_of_ints = [42, 314, 9] 

sera publié en feuilleton comme:

[('a_string', 'Hello, world'), ('an_int', 123), ('a_list_of_ints', 42), ('a_list_of_ints', 314), ('a_list_of_ints', 9)] 

Comme vous pouvez le voir, la liste est représentée une série de valeurs, tous avec le même nom. Lorsque vous chargez des données à partir du magasin de données, le SDK voit la valeur répétée et la transforme en liste.

Lorsque cela devient important, c'est quand il interagit avec l'indexation. Supposons que vous ayez un index sur 'a_string' et 'an_int'. Lorsque vous insérez ou modifiez une valeur, App Engine génère un ensemble d'entrées d'index; pour l'indice ci-dessus et l'entité ci-dessus, il génère une seule ligne dans l'index qui ressemble à ceci:

('Hello, world', 123, a_key) 

(« Clé A » ici est un espace réservé pour la clé de l'entité d'origine.) Lorsque vous faites une requête qui utilise cet index, il suffit de faire une recherche sur l'index pour trouver les lignes avec le préfixe approprié (par exemple, 'SELECT * FROM Type WHERE a_string = "Bonjour, monde" ORDER BY an_int').

Lorsque vous indexez une liste, App Engine insère toutefois plusieurs lignes d'index. Un index sur « un_entier » et « a_list_of_ints » générerait ces lignes pour l'entité ci-dessus:

(123, 42, a_key) 
(123, 314, a_key) 
(123, 9, a_key) 

Encore une fois, l'interrogation fonctionne comme il l'a fait auparavant - App Engine a juste pour regarder la ligne avec le préfixe correct dans l'index. Le nombre d'entrées dans la liste n'a aucun impact sur la rapidité de la requête, mais uniquement sur le temps nécessaire pour générer et écrire les entrées d'index. En fait, le planificateur de requêtes n'a aucune idée que 'a_list_of_ints' est une propriété à valeurs multiples - il la traite comme n'importe quelle autre entrée d'index.

Donc, en résumé:

  1. Il n'y a pas de différence pratique entre une liste avec un élément et une propriété individuelle, l'indexation et les conditions d'interrogation
  2. La taille d'une liste indexée affecte le temps et l'espace requis pour l'indexation, mais pas pour l'interrogation.
  3. Vous pouvez faire une requête qui correspond à n'importe quelle entité avec une valeur donnée dans une liste en utilisant un filtre d'égalité simple.
+0

Une réponse très informative! Merci de partager cette information avec SO. @DutrowLLC marquez s'il vous plaît cette réponse comme la bonne, car elle est, à mon avis, une meilleure réponse à votre question. @Nick Johnson Mes excuses pour croire les mauvaises choses. Merci d'expliquer et de fournir cette très belle réponse avec de super informations pour tout le monde! – Pindatjuh

+0

@Pindatjuh - C'est beaucoup à prendre en compte. Cette vidéo va également dans quelques détails sur la façon dont les listes sont indexées et la recherche. J'ai trouvé la seconde moitié sur fusion-join extrêmement utile. C'était un pdf avec des diapositives que vous pouvez regarder en regardant la vidéo: http://code.google.com/events/io/2009/sessions/BuildingScalableComplexApps.html –

+0

Merci d'avoir pris le temps de répondre à cette question si complètement J'espère que d'autres personnes pourront également trouver votre réponse et en tirer profit. –

1

La solution habituelle pour ce genre de problèmes est de normaliser le modèle, c'est-à-dire le First Normal Form.

Votre modèle, sous forme normalisée, aurait une troisième table, avec des références aux Appointment et Line_Item lignes:

Appointment 
start_time 
... 

Line_Item 
name 
price 
... 

Appointment_Line_Item 
appointment_key 
line_item_key 

Il y a un problème cependant! Étant donné que vous utilisez Google App Engine et que leur magasin de données est assez limité, il nécessite généralement une dénormalisation.

Vous avez suggéré d'utiliser un champ semblable à une liste. C'est une possiblité de l'utiliser, mais il est très difficile de l'indexer. La recherche d'une clé (appointment_key) dans une liste par ligne dans la base de données n'est pas vraiment performante. Je propose deux possibilités:

  1. Dupliquer Line_Item.

    Line_Item 
    appointment_key 
    name 
    price 
    finished 
    ... 
    

    Un Line_Item devrait avoir un état finished, lorsque l'élément est fini ou non par l'employé. Si un employé n'a pas terminé tous les éléments de campagne, indiquez-les comme non terminés, créez un nouveau rendez-vous et copiez tous les éléments inachevés. Vous pouvez indexer sur le champ appointment_key sur tous les Line_Items, ce qui est une bonne chose. Cependant, les données dupliquées peuvent poser problème.

  2. champs dynamiques pour Line_Item:

    Line_Item 
    duplicate_key 
    appointment_key 
    name 
    price 
    finished 
    ... 
    

    Créer un nouveau champ, duplicate_key, pour Line_Item qui pointe vers une autre Line_Item ou null (réserve cette clé!). Null signifie que le Line_Item est d'origine, toute autre valeur signifie que ce Line_Item est un doublon du Line_Item pointé par le champ. Tous les champs de Line_Item marqués en tant que doublons héritent des champs de l'original Line_Item, à l'exception de appointment_key: l'espace de stockage est donc réduit. Cette solution doit également être indexée appointment_key, pour accélérer les temps de recherche. Cela nécessite une requête supplémentaire par Line_Item dupliqué, ce qui peut poser un problème.

Maintenant, c'est un choix évident: soit une meilleure vitesse soit un meilleur stockage. Je voudrais aller pour le premier, car il réduit la complexité de votre modèle, et le stockage n'est jamais un problème avec les systèmes modernes. Moins de complexité signifie généralement moins de bogues et moins de coûts de développement/test, ce qui justifie le coût de l'exigence de stockage.

+0

Merci pour votre réponse. Je n'ai jamais pensé à l'approche clé en double, c'est une solution vraiment intéressante. Une chose à garder à l'esprit avec le moteur d'application, c'est qu'ils font des listes d'index et vous permettent de les rechercher. Ils l'appellent "fusionner-joindre" et il semble étendre leurs capacités au-delà d'un simple magasin de valeurs-clés: http://code.google.com/events/io/2009/sessions/BuildingScalableComplexApps.html –

+1

"Recherche de une clé (la clé de rendez-vous) dans une liste par ligne dans la base de données n'est pas vraiment performante. " - pas vrai. Vous pouvez filtrer les propriétés de liste dans App Engine aussi efficacement que sur les non-listes. –

+0

@Nick Johnson - Merci de chuter avec cela. Je pense que c'est un changeur de jeu clé avec le moteur de l'application qui est inattendu et pas bien connu. –