如何在关系数据库中建模和查询对象?

时间:2013-04-14 22:54:59

标签: sql performance object-relational-model

我有一个复杂的字典数据库方案。每个对象(本质上是一个翻译)与此类似:

Entry {
   keyword;
   examples;
   tags;
   Translations;
}

Translation {
    text;
    tags;
    examples;
}

 Example {
    text;
    translation;
    phonetic_script;
 }

即。标签(即语法)可以属于关键字本身或翻译(外语的语法),并且类似的示例可以属于翻译本身(即,解释外来词)或条目中的文本。我最终得到了这种关系设计:

entries(id,keyword,)
tags(tag)
examples(id,text,...)
entrytags(entry_id,tag)
entryexamples(entry_id,example_id)
translations(id,belongs_to_entry,...)
translationtags(transl_id, tag)
translationexamples(transl_id,example_id)

我的主要任务是查询此数据库。假设我搜索“foo”,我目前的处理方式是:

query all entries with foo, get ids A
foreach id in A
   query all examples belonging to id
   query all tags belonging to id
   query all translations belonging to A, store their ids in B
   foreach tr_id in B
       query all tags belonging to tr_id
       query all examples belonging to tr_id

重建我的对象。这看起来很麻烦,而且很慢。我不知道如何通过使用连接或其他方式来显着改善这一点。我很难将这些对象建模到数据库中的关系。这是一个合适的设计吗?

如何才能提高查询时间?

1 个答案:

答案 0 :(得分:1)

在循环中调用的每个查询都至少需要执行一定的基本持续时间,即使对于简单的查询也是如此。许多环境因素影响了这个持续时间,但现在我们假设它是10毫秒。如果第一个查询匹配100个条目,则至少有301个查询被调用,每个查询占用10毫秒,总共3秒。循环迭代次数的变化会导致性能的显着变化。

使用连接重构查询将创建更复杂的查询,但调用的查询总数可以减少到固定数量,在下面的查询中为4。现在假设每个查询执行时需要50毫秒,因为它更复杂,总持续时间变为200毫秒,从3000毫秒大幅减少。

下面显示的4个查询应该接近达到预期的结果。还有其他方法可以编写查询,例如使用子查询或在FROM子句中包含表,但这些方法显示了如何使用JOIN进行查询。条件entries.keyword = 'foo'用于表示原始查询中的条件以选择条目。

值得注意的是,如果foo上的entries条件计算成本非常高,则可能需要进行其他优化以进一步提高性能。在这些示例中,条件是一个简单的比较,可以在索引中快速查找,但使用可能需要全表扫描的LIKE可能无法很好地处理这些查询。

以下查询选择与原始查询匹配的所有示例。原始查询中的条件在WHERE列上表示为entries.keyword子句。

SELECT entries.id, examples.text
  FROM entries
 INNER JOIN entryexamples
    ON (entries.id = entryexamples.entry_id)
 INNER JOIN examples
    ON (entryexamples.example_id = examples.id)
 WHERE entries.keyword = 'foo';

此查询选择与原始查询匹配的标记。在这种情况下,只使用了两个连接,因为entrytags.tag列是所需的,并且与tags的连接只会提供相同的值。

SELECT entries.id, entrytags.tag
  FROM entries
 INNER JOIN entrytags
    ON (entries.id = entrytags.entry_id)
 WHERE entries.keyword = 'foo'';

此查询选择原始查询的翻译标签。这类似于上一个选择entrytags的查询,但此处使用了另一层连接进行翻译。

SELECT entries.id, translationtags.tag
  FROM entries
 INNER JOIN translations
    ON (entries.id = translations.belongs_to_entry)
 INNER JOIN translationtags
    ON (translations.id = translationtags.transl_id)
 WHERE entries.keyword = 'foo';

最终查询与examples的第一个查询相同,但也包含其他连接。它已成为很多连接,但总的来说应该比循环执行单个查询要好得多。

SELECT entries.id, examples.text
  FROM entries
 INNER JOIN translations
    ON (entries.id = translations.belongs_to_entry)
 INNER JOIN translationexamples
    ON (translations.id = translationexamples.transl_id)
 INNER JOIN examples
    ON (translationexamples.example_id = examples.id)
 WHERE entries.keyword = 'foo';