2010-06-26 17 views
5

Estoy creando una aplicación de administración para ayudar a administrar mi compañía de auto detallando móviles (y con suerte otros). Estoy luchando para descubrir cómo modelar algunos de los datos.Nombramientos y líneas de pedido

Esta pregunta está relacionada con una pregunta anterior que he publicado, pero he reproduce a continuación la información relevante: Database design - google app engine

En esta aplicación, hay conceptos de "Citas" y "Elementos de línea. "

Las citas son un lugar y una época donde se espera que los empleados estén para prestar un servicio.

Las líneas de pedido son un servicio, tarifa o descuento y su información asociada. Un ejemplo de artículos de línea que podrían entrar en una cita:

 
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 

En mi aplicación previa de esta aplicación, Elementos de línea estuviera contenida por una sola cita. Esto funcionó bien la mayor parte del tiempo, pero a veces causaba problemas. Un ejemplo sería si una cita se interrumpió a mitad de camino debido a la lluvia y el técnico tuvo que volver al día siguiente y terminar. Esta situación requirió dos citas para la misma línea de pedido. En casos como este, me gustaría cambiar los datos un poco configurando la "línea de pedido" en la segunda cita para leer algo así como "Terminar" y luego el costo sería de $ 0.

En esta nueva versión, estoy considerando permitiendo Elementos de línea para ser emparejado con más de una cita con una estructura de tabla que tiene este aspecto:

Appointment 
start_time 
etc... 

Line_Item 
appointment_Key_List 
name 
price 
etc... 

Un problema general con esta estructura es que es complicado y ni siquiera estoy seguro de si es apropiado hacer coincidir una línea de pedido con varias citas. Si los artículos de línea solo pueden ser parte de una cita, entonces puedo poner una lista de artículos de línea en cada cita, cuando reciba las citas, ya recibiré artículos de línea.

Un problema más específico es que estoy usando el motor de la aplicación de Google y si quiero consultar un conjunto de citas y sus líneas de pedido asociadas, primero tendría que consultar el conjunto de citas y luego hacer un segundo consulta de las líneas de pedido utilizando el operador IN para comprobar si alguna de las claves de cita de Line_Item pertenece al conjunto de claves de cita que se devolvieron de la consulta anterior. La segunda consulta fallará si tengo más de 30 claves que me obliguen a fragmentar la consulta. Podría desnormalizar los datos para evitar esta complicada y extensa consulta de lectura, y probablemente tendré que desnormalizarme hasta cierto punto de todos modos, pero preferiría evitar la complejidad cuando sea apropiado.

Mi pregunta es ¿cómo se modela este tipo de situación? ¿Es apropiado incluso que un artículo de línea se combine con más de una cita, o es normal simplemente dividir los artículos de línea en apartados para cada cita, como "1ª mitad de 2 días de trabajo" y "2ª mitad de trabajo de dos días"? " ¿Cómo hacen esto aplicaciones similares exitosas? ¿Cuáles son las reglas generales en este tipo de situaciones? ¿Qué implementaciones han resultado menos problemáticas?

Gracias!

Respuesta

2

El enfoque que está sugiriendo funcionará bien; puede modelar la 'cita_clave_clase' de la línea de pedido como una propiedad de lista y funcionará como espera. No tiene que usar el operador IN, es decir, para hacer coincidir un único valor en el almacén de datos con una lista de claves que tenga (por ejemplo, "WHERE columna de almacén de datos IN ('a', 'b', 'c')), mientras está haciendo lo contrario: hacer coincidir un único valor con una lista en el almacén de datos.

Sugeriría, sin embargo, que lo contrario podría ser más adecuado para su tarea: Haga que cada cita tenga una lista de teclas de línea de pedido. Esto funciona de la misma manera, pero para recuperar todos los datos de una cita, en lugar de eso, primero busca la cita, luego realiza una obtención masiva de los artículos de línea, usando las claves de la entidad Cita. Si conoce la clave de la cita. , por lo tanto, evita la necesidad de realizar consultas.

He estado tratando de explicar a Pindatjuh por qué consultar una propiedad de la lista no es menos eficiente que una de un solo valor, pero aparentemente se requiere una descripción más detallada, así que sin más preámbulos, aquí está ...

un breve panorama sobre el almacén de datos de App Engine indexación

Aunque Python y Java proporcionan varias interfaces de alto nivel para el almacén de datos, el almacén de datos en sí habla de una abstracción de nivel inferior, denominado entidades. Una entidad consisten en lo siguiente:

  1. Una única clave primaria
  2. una lista de (nombre, valor) pares

La clave primaria es la clave de almacén de datos ya está familiarizado con. La lista de pares (nombre, valor) es la representación de App Engine para los datos en su entidad. Hasta ahora, tan sencillo. Una entidad con los siguientes valores:

a_string = "Hello, world" 
an_int = 123 

sería serializado a algo parecido a esto:

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

Pero ¿cómo afecta esto interactuar con las listas? Bueno, las listas se tratan como propiedades de 'valor multiplicado'. Es decir, una lista con n elementos se almacena como n propiedades separadas. Un ejemplo probablemente hace esto más claro:

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

será serializado como:

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

Como se puede ver, la lista se representa una serie de valores, todos con el mismo nombre. Cuando carga datos del almacén de datos, el SDK ve el valor repetido y lo convierte en una lista.

Donde esto se vuelve importante es cuando interactúa con la indexación. Supongamos que tiene un índice sobre 'a_string' y 'an_int'. Cuando inserta o modifica un valor, App Engine genera un conjunto de entradas de índice para él; para el índice de arriba y la entidad anterior, se genera una sola fila en el índice que se ve algo como esto:

('Hello, world', 123, a_key) 

('a_key' aquí es un marcador de posición para la clave de la entidad original.) Al hacer una consulta que usa este índice, solo necesita hacer una búsqueda en el índice para encontrar filas con el prefijo apropiado (por ejemplo, 'SELECCIONAR * FROM Kind DONDE a_string = "Hola, mundo" ORDER BY an_int').

Al indexar una lista, sin embargo, App Engine inserta varias filas de índice. Un índice en 'an_int' y 'a_list_of_ints' generaría estas filas para la entidad arriba:

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

Una vez más, consulta funciona de la misma como lo hizo anteriormente - App Engine sólo tiene que buscar la fila con el prefijo correcto en el índice. La cantidad de entradas en la lista no afecta la rapidez de la consulta, solo el tiempo que tomó generar y escribir las entradas del índice. De hecho, el planificador de consultas no tiene idea de que 'a_list_of_ints' es una propiedad con múltiples valores, simplemente lo trata como cualquier otra entrada de índice.

Así, en pocas palabras:

  1. No hay diferencia práctica entre una lista con un elemento en ella y una propiedad individual, en la indexación y los términos consulta de
  2. El tamaño de una lista indexada afecta el tiempo y espacio requerido para la indexación, pero no para consultar.
  3. Puede hacer una consulta que coincida con cualquier entidad con un valor dado en una lista utilizando un filtro de igualdad simple.
+0

¡Respuesta muy informativa! Gracias por compartir esta información con SO. @DutrowLLC marque esta respuesta como la correcta, ya que, en mi opinión, es una respuesta mucho mejor a su pregunta. @Nick Johnson Mis disculpas por creer las cosas equivocadas. ¡Gracias por explicar y proporcionar esta muy buena respuesta con excelente información para todos! – Pindatjuh

+0

@Pindatjuh - Es mucho para asimilar. Este video también entra en detalles sobre cómo se indexan las listas y la búsqueda. Encontré la segunda mitad en fusión-unión extremadamente útil. Era un pdf con diapositivas que puede ver mientras mira el video: http://code.google.com/events/io/2009/sessions/BuildingScalableComplexApps.html –

+0

Gracias por tomarse el tiempo para responder a esta pregunta tan a fondo , Espero que otras personas también puedan encontrar su respuesta y beneficiarse de ella. –

1

La solución habitual para este tipo de problemas es la normalización del modelo, es decir, al First Normal Form.

Su modelo, en forma normalizada, tendría una tercera tabla, con referencias a los Appointment y Line_Item filas:

Appointment 
start_time 
... 

Line_Item 
name 
price 
... 

Appointment_Line_Item 
appointment_key 
line_item_key 

Hay un problema, sin embargo! Dado que está utilizando Google App Engine, y su almacén de datos es bastante limitado ("GQL cannot perform an SQL-like JOIN") y en su mayoría requiere desnormalización.

Ha sugerido utilizar un campo similar a una lista. Es posible usar esto, pero es muy difícil indexarlo. La búsqueda de una clave (appointment_key) en una lista por fila en la base de datos no funciona realmente. Propongo dos posibilidades:

  1. Duplicado Line_Item.

    Line_Item 
    appointment_key 
    name 
    price 
    finished 
    ... 
    

    Un Line_Item debe tener un estado finished, cuando el artículo se terminó o no por el empleado. Si un empleado no terminó todas las líneas de pedido, márquelas como sin terminar, cree una nueva cita y copie todos los elementos que no se terminaron. Puede indexar en el campo appointment_key en todos Line_Items, que es una buena cosa. Sin embargo, el datos duplicados puede ser un problema.

  2. campos dinámicos para Line_Item:

    Line_Item 
    duplicate_key 
    appointment_key 
    name 
    price 
    finished 
    ... 
    

    crear un nuevo campo, duplicate_key, por Line_Item que apunta a otro Line_Item o nulo (se reservan esta clave!). Nulo significa que el Line_Item es original, cualquier otro valor significa que este Line_Item es un duplicado del Line_Item al que apunta el campo. Todos los campos de Line_Item marcados como duplicados heredan los campos del original Line_Item, excepto el appointment_key: por lo tanto, se necesitará menos almacenamiento. También esta solución debe tener appointment_key indexado, para acelerar los tiempos de búsqueda. Esto requiere una consulta adicional por duplicado Line_Item, lo que puede ser un problema.

Ahora, es una opción clara: o mejor velocidad o mejor almacenamiento. Me gustaría ir por la primera, ya que reduce la complejidad de su modelo, y el almacenamiento nunca es un problema con los sistemas modernos. Menos complejidad generalmente significa menos errores y menos costos de desarrollo/prueba, lo que justifica el costo del requisito de almacenamiento.

+0

Gracias por su respuesta. Nunca pensé en el enfoque duplicado de la llave, esa es una solución realmente interesante. Una cosa a tener en cuenta con el motor de aplicaciones es que hacen listas de índices y te permiten buscar en ellas. Lo llaman una "unión combinada" y parece ampliar sus capacidades más allá de un simple almacén de clave-valor: http://code.google.com/events/io/2009/sessions/BuildingScalableComplexApps.html –

+1

"Buscando una clave (la cita_clave) en una lista por fila en la base de datos no funciona realmente ". - no es verdad. Puede filtrar las propiedades de la lista en App Engine con la misma eficacia que en las no listas. –

+0

@Nick Johnson - Gracias por coincidir con eso. Creo que es un cambio de juego clave con el motor de la aplicación que es inesperado y poco conocido. –

Cuestiones relacionadas