2010-10-11 146 views
3

我遇到了一个查询“太长”。该查询在10个左右表格之间有50多个左连接。为了简要概述数据库模型,连接的表格是存储特定数据类型(例如:date_fields,integer_fields,text_fields等)的数据的表格,每个表格都有一个值列,一个“数据字段”和一个票证号码。该查询是基于“票证”与其“数据字段”之间的关联表以编程方式构建的。优化在同一个表上使用多个左连接的查询

的加入语句看起来类似以下内容:

...FROM tickets t 
LEFT JOIN ticket_text_fields t001 ON(t.id=t001.ticket_id AND t001.textfield_id=7) 
... 
LEFT JOIN ticket_date_fields t056 ON(t.id=t056.ticket_id AND t056.datafield_id=434) 

在使用上查询说明显示如下:

1 SIMPLE t  ref idx_dataset_id     idx_dataset_id 5 const 2871 Using where; Using temporary; Using filesort 
1 SIMPLE t001 ref idx_ticket_id,idx_datafield_id idx_ticket_id 5 t.id 5 
... 
1 SIMPLE t056 ref idx_ticket_id,idx_datafield_id idx_ticket_id 5 t.id 8 

我可以采取什么方向来调整这个查询?所有指标似乎都已到位。也许应该减少t表(门票)行号(2871)。剩下多少个连接太多?数据字段表是否应该只加入一次,然后为每个数据查询所需的数据?

回答

7

您正在使用名为Entity-Attribute-Value的可怕antipattern变体。您将属性存储在单独的行中,因此如果您想重新构建看起来像传统数据行的东西,则需要为每个属性创建一个连接。

这并不奇怪,这会创建一个包含50个连接的查询。这对于大多数数据库高效运行来说太多了(您尚未识别出您正在使用哪个数据库)。最终,您还需要其他几个属性,并且您可能会超出数据库的一些体系结构限制。

解决方法是:不重建SQL中的行。

而是将属性查询为多行,而不是试图将它们组合到一行中。

SELECT ... FROM tickets t 
INNER JOIN ticket_text_fields f ON t.id=f.ticket_id 
WHERE f.textfield_id IN (7, 8, 9, ...) 
UNION ALL 
SELECT ... FROM tickets t 
INNER JOIN ticket_date_fields d ON t.id=d.ticket_id 
WHERE d.datafield_id IN (434, 435, 436, ...) 

然后你必须写在你的应用程序遍历所产生的行集功能,并逐一收集属性到应用空间中的对象,这样的话,如果它是一个单一的实体,您可以使用它。

+1

伟大的回应比尔!我不能在不改变模式的情况下想到一个好的解决方案,所以我不断重新加载这个问题,看看别人会说些什么。我喜欢你的解决方案。 – 2010-10-11 17:32:09

+0

同意。感谢您的信息和解决方案! – 2010-10-12 17:29:56

0

了更清晰的查询我会用这样的:

SELECT ... FROM tickets as t 
JOIN ticket_text_fields as txt ON t.id = txt.ticket_id 
JOIN ticket_date_fields as dt ON t.id = dt.ticket_id 
WHERE txt.textfield_id IN (...) 
AND dt.datefield_id IN (...) 

加盟将可能离开了,但是这取决于你的数据的结构。
查询中没有联合,只有两个联接