如何优化规范化数据库结构上的查询?

时间:2018-12-29 19:56:28

标签: mysql query-performance entity-attribute-value

我正在尝试优化当前在MySQL 5.x DB上花费0.00x s的查询,以便在没有负载的情况下检索系统上的数据。

查询如下:

SELECT 
   a.article_id,
   GROUP_CONCAT(attr_f.attr_de) AS functions, 
   GROUP_CONCAT(attr_n.attr_de) AS miscellaneous
FROM `articles_test` a
LEFT JOIN articles_attr AS f ON a.article_id = f.article_id AND f.attr_group_id = 26
LEFT JOIN articles_attr AS attr ON a.article_id = attr.article_id AND attr.attr_group_id = 27
LEFT JOIN cat_attr AS attr_f ON attr_f.attr_id = f.attr_id
LEFT JOIN cat_attr AS attr_n ON attr_n.attr_id = attr.attr_id
WHERE a.article_id = 11

EXPLAIN返回

1   SIMPLE  a   
    NULL
    const   article_id  article_id  3   const   1   100.00  
    NULL

1   SIMPLE  f   
    NULL
    ref article_id_2,article_id article_id_2    6   const,const 2   100.00  Using index 
1   SIMPLE  attr    
    NULL
    ref article_id_2,article_id article_id_2    6   const,const 4   100.00  Using index 
1   SIMPLE  attr_f  
    NULL
    ref attr_id attr_id 3   test.f.attr_id  1   100.00  
    NULL

1   SIMPLE  attr_n  
    NULL
    ref attr_id attr_id 3   test.attr.attr_id   1   100.00  
    NULL

在所有查询的字段上都有索引。是否有另一种方法可以通过更简单,更快速的查询来检索数据?

CREATE TABLE `articles_attr` (
 `date_created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
 `article_id` mediumint(8) unsigned NOT NULL,
 `attr_group_id` mediumint(8) NOT NULL,
 `attr_id` mediumint(8) unsigned DEFAULT NULL,
 `value` varchar(255) DEFAULT NULL,
 UNIQUE KEY `article_id_2` (`article_id`,`attr_group_id`,`attr_id`),
 KEY `article_id` (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

CREATE TABLE `cat_attr` (
 `attr_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
 `attr_group_id` mediumint(8) unsigned NOT NULL,
 `sort` tinyint(4) NOT NULL,
 `attr_de` varchar(255) NOT NULL,
 UNIQUE KEY `attr_id` (`attr_id`,`attr_group_id`),
 UNIQUE KEY `attr_group_id` (`attr_group_id`,`attr_de`)
) ENGINE=InnoDB AUTO_INCREMENT=380 DEFAULT CHARSET=utf8

CREATE TABLE `articles_test` (
 `article_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
 UNIQUE KEY `article_id` (`article_id`),
) ENGINE=InnoDB AUTO_INCREMENT=221614 DEFAULT CHARSET=latin1

表articles_attr包含约50万行。

3 个答案:

答案 0 :(得分:1)

由于您的WHERE子句指定了article_id的值,因此实际上不需要让select子句返回它。最好将其删除,因为它不符合SQL标准,即如果您有聚合(group_concat,则select子句中所有非聚合表达式都必须位于{{ 1}}子句。但是这样做(如您的问题的第一个版本)会带来一些开销。因此最好将其删除。

由于group by条件位于主键上,并且您不需要WHERE表中的任何数据,因此可以省略articles_test表,并将{{1 }}以外键为条件。

最后,当您将articles_test中的每个匹配项与WHERE中的每个匹配项组合在一起时,就存在一种笛卡尔连接。这可能会导致attr_f输出中出现一些重复,并代表性能下降。

如果可以删除此类重复项,则可以通过将查询分为几组来获得更好的性能:一个用于 function 输出,一个用于 miscellaneous < / em>输出。然后由attr_n组成该组。

这还将允许将外部联接转换为内部联接。

所以输出将是您所追求的原始版本:

group_concat

所以现在输出将有两行。第一列中包含26的功能将在第二列中列出功能,第一列中包含27的功能将列出其他功能。

是的,输出格式是不同的,但是我认为您可以重做使用该查询的代码,同时受益于性能的提高(我期望如此)。

如果需要透视版本,请使用attr_group_id表达式:

SELECT     attr.attr_group_id, GROUP_CONCAT(cat.attr_de) AS functions
FROM       articles_attr AS attr 
INNER JOIN cat_attr AS cat ON cat.attr_id = attr.attr_id
WHERE      attr.article_id = 11
       AND attr.attr_group_id IN (26, 27) 
GROUP BY   attr.attr_group_id

答案 1 :(得分:0)

首先,对于这样的查询9毫秒还不错。没有根本的改进。您也许可以从查询中挤出另外一毫秒或两毫秒,但您可能没有。

您在articles_attr上的三列索引看起来不错。您可以尝试切换索引中前两列的顺序,以查看性能是否更好。

实际上,该表上的单列索引是不必要的:提供索引功能是因为同一列在三列索引中排在第一位。删除该索引可能不会帮助您提高查询性能,但将有助于提高性能。

GROUP_CONCAT()在这里很有意义。汇总整个结果集是完全有效的。为了清楚起见,您可以添加GROUP BY a.article_id;这不会对性能造成任何影响,因为您已经只选择了该列的单个值。

cat_attr上,(attr_id, attr_de)上的复合索引可能会有所帮助。但这显然是一张小桌子,所以不会有太大帮助。

您是否需要进行LEFT JOIN操作才能将articles_attr加入cat_attr?或者,根据数据的结构,是否保证articles_attr.attr_id的每个值都能在cat_attr.attr_id中找到匹配项。如果您可以将这些LEFT JOIN操作更改为JOIN,则可能会稍微加快速度。

答案 2 :(得分:0)

为什么if( !m_bs ) { CD3D11_BLEND_DESC blendDesc{ D3D11_DEFAULT }; D3D11_RENDER_TARGET_BLEND_DESC& rt = blendDesc.RenderTarget[ 0 ]; rt.BlendEnable = TRUE; rt.SrcBlend = D3D11_BLEND_BLEND_FACTOR; rt.BlendOp = D3D11_BLEND_OP_ADD; rt.DestBlend = D3D11_BLEND_ZERO; rt.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE; CHECK( device->CreateBlendState( &blendDesc, &m_bs ) ); } float f; switch( mode ) { case 0: // 2x brighter f = 2; break; case 1: // 50% darker f = 0.5f; break; default: return S_FALSE; } const float BlendFactor[ 4 ] = { f, f, f, f }; context->OMSetBlendState( m_bs, BlendFactor, 0xffffffff ); ?您不是总需要一个attr吗?我提出这个问题的原因是您在DXGI_FORMAT_R10G10B10A2_UNORM上没有明确的void main (int argc, char *argv[]) { while (--argc) { printf ("%s\n", *++argv); } } `attr_id` mediumint(8) unsigned DEFAULT NULL, 防止将NULL键提升为PK。更改为PRIMARY KEY,然后将articles_attr升级为PK。

NULL

冗余,将其删除。

many:many表的结构次优。几个技巧:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table

如果不需要“ many:many”,请切换到“ 1:many”;效率更高。

您可能可以使用UNIQUE而不是NOT NULL,因为您需要一直到UNIQUEKEY `article_id` (`article_id`)

将Group_concat的联接移到JOIN 可能有帮助:

LEFT JOIN

但也许最重要的是通过规范化属性来避免使本来很糟糕的EAV模式设计变得更糟!也就是说,摆脱表attr_f,并将attr_n移到SELECT中。这将减少SELECT a.article_id, ( SELECT GROUP_CONCAT(ca.attr_de) FROM articles_attr AS aa JOIN cat_attr AS ca USING(attr_id) WHERE aa.attr_group_id = 26 AND aa.article_id = a.article_id ) AS functions, ( SELECT GROUP_CONCAT(attr_f.attr_de) FROM .. JOIN .. WHERE .. ) AS miscellaneous FROM `articles_test` a WHERE a.article_id = 11 数量的一半。