代码之家  ›  专栏  ›  技术社区  ›  Chris Dutrow

约会和行项目

  •  5
  • Chris Dutrow  · 技术社区  · 14 年前

    我正在构建一个管理应用程序来帮助管理我的移动汽车公司(希望还有其他公司)。我正在努力弄清楚如何对一些数据进行建模。

    Database design - google app engine

    约会 是一个地点和时间,员工被期望在那里提供服务。

    行项目

    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
    

    在下一个版本中,我将考虑使用如下表结构使行项目与多个约会匹配:

    Appointment
     start_time
     etc...
    
    Line_Item
     appointment_Key_List
     name
     price
     etc...
    

    这种结构的一个普遍问题是它很复杂,我甚至不确定是否适合将一个行项目与多个约会相匹配。如果行项目只能是一个约会的一部分,那么我实际上可以在每个约会中放置一个行项目列表,当我得到约会时,我已经得到了行项目。

    我的问题是这种情况通常是如何模拟的?一个行项目与多个预约配对是否合适,或者简单地将每个预约的行项目拆分为单独的行项目(如“2天工作的前半部”和“2天工作的后半部”)是否正常。类似的成功申请是如何做到这一点的?这种情况下的经验法则是什么?哪些实现的问题较少?

    谢谢!

    2 回复  |  直到 7 年前
        1
  •  2
  •   Nick Johnson    14 年前

    你所建议的方法会很管用;您可以将行项目的'appointment\u Key\u list'建模为列表属性,它将按您所期望的那样工作。您不必使用IN运算符—这是为了将数据存储中的单个值与您拥有的键列表(例如,“WHERE datastore\u column IN('a'、'b'、'c'))进行匹配,而相反—将单个值与数据存储中的列表进行匹配。

    不过,我建议,反过来可能更适合您的任务:让每个约会都有一个行项目键列表。这与操作方式大致相同,但要检索约会上的所有数据,您需要先获取约会,然后使用约会实体中的键对行项目执行批量获取。如果您知道约会的关键,那么您就完全不需要进行任何查询。

    我一直在试图向Pindatjuh解释为什么查询列表属性的效率不比单值属性低,但显然需要更详细的描述,因此不需要进一步的讨论,下面是。。。

    appengine数据存储索引简介

    尽管Python和Java为数据存储提供了各种高级接口,但数据存储本身表示一种较低级别的抽象,称为实体。实体由以下部分组成:

    1. 唯一主键
    2. (名称、值)对的列表

    a_string = "Hello, world"
    an_int = 123
    

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

    但这和列表是如何相互作用的呢?嗯,列表被视为“多值”属性。也就是说,包含n个项目的列表存储为n个单独的属性。举个例子可能会更清楚地说明这一点:

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

    将序列化为:

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

    如您所见,列表由一系列值表示,所有值都具有相同的名称。从数据存储加载数据时,SDK会看到重复的值并将其转换为列表。

    ('Hello, world', 123, a_key)
    

    ('a_key'这里是原始实体键的占位符。)当您执行使用此索引的查询时,它只需要在索引上进行查找,以找到具有适当前缀的行(例如,'SELECT*FROM Kind WHERE a_string=“Hello,world”ORDER BY an_int')。

    不过,当您为列表编制索引时,appengine会插入多个索引行。“一个整数”和“一个整数列表”上的索引将为上述实体生成以下行:

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

    同样,查询的工作原理与以前相同—应用程序引擎只需在索引中查找具有正确前缀的行。列表中的条目数对查询的速度没有影响,只影响生成和写入索引条目所用的时间。事实上,查询规划器并不知道“一个\个\个\个\个\个\个\个\个整数的列表\个”是一个多值属性—它只是将其视为任何其他索引项。

    简而言之:

    1. 在索引和查询术语方面,包含一个元素的列表和单个属性之间没有实际的区别
    2. 索引列表的大小会影响索引所需的时间和空间,但不会影响查询所需的时间和空间。
    3. 您可以使用简单的相等筛选器来执行查询,以匹配列表中具有给定值的任何实体。
        2
  •  1
  •   Pindatjuh    14 年前

    First Normal Form .

    标准化形式的模型将有第三个表,其中引用 Appointment Line_Item 排:

    Appointment
     start_time
     ...
    
    Line_Item
     name
     price
     ...
    
    Appointment_Line_Item
     appointment_key
     line_item_key
    

    但是有一个问题!因为你使用的是googleappengine,而且他们的数据存储非常有限 ("GQL cannot perform an SQL-like JOIN") 而且大多需要反规范化。

    您建议使用类似列表的字段。这是一个可能的使用,但它是非常困难的索引。寻找钥匙 appointment_key )在一个列表中,数据库中的每一行实际上都没有执行。我提出两种可能性:

    1. 复制 行项目

      Line_Item
       appointment_key
       name
       price
       finished
       ...
      

      A 行项目 finished 说明员工何时完成项目。如果员工尚未完成所有行项目,请将其标记为未完成,创建新约会并复制所有未完成的项目。你可以在 约会密钥 所有字段 Line_Items 重复数据

    2. 行项目 :

      Line_Item
       duplicate_key
       appointment_key
       name
       price
       finished
       ...
      

      创建新字段, duplicate_key 行项目 指向另一个 行项目 或设置为null(保留此密钥!)。Null表示 行项目 行项目 行项目 字段指向。所有领域 标记为重复项继承原始项的字段 行项目 ,除了 :因此需要较少的存储空间。这个解决方案也应该有 索引,以加快查找时间。这需要 每重复一个额外的查询 行项目

    现在,这是一个明确的选择:要么更快,要么更好的存储。首先,它降低了模型的复杂性,而且存储从来都不是现代系统的问题。较低的复杂性通常意味着更少的bug和更少的开发/测试成本,这就证明了存储需求的成本是合理的。