SQL魔术 – 查询不应该需要15个小时,但它是

好的,所以我有一个真正可怕的 MySQL表(900k记录,总共180 MB),我想从每个组中更高的date_updated和计算加权平均数的子组记录中提取.计算运行了〜15个小时,我有一个强烈的感觉,我做错了.

首先,怪异桌布局:

>类别
> element_id
> date_updated
>值
>体重
> source_prefix
> source_name

只有这里的关键是element_id(BTREE,〜8k独特元素).

并计算过程:

为每个组和子组创建散列.

CREATE TEMPORARY TABLE `temp1` (INDEX ( `ds_hash` ))
                SELECT `category`,`element_id`,`source_prefix`,`source_name`,`date_updated`,`value`,`weight`,MD5(CONCAT(`category`,`source_name`)) AS `subcat_hash`,`date_updated`)) AS `cat_hash` 
                FROM `bigbigtable` WHERE `date_updated` <= '2009-04-28'

我真的不明白这个哈希的大惊小怪,但是这样做的速度更快.黑魔法,我假定.

查找每个子组的最大日期

CREATE TEMPORARY TABLE `temp2` (INDEX ( `subcat_hash` ))

                SELECT MAX(`date_updated`) AS `maxdate`,`subcat_hash`
                FROM `temp1`
                GROUP BY `subcat_hash`;

使用temp2加入temp1以查找类别的加权平均值

CREATE TEMPORARY TABLE `valuebycats` (INDEX ( `category` ))
            SELECT `temp1`.`element_id`,`temp1`.`category`,`temp1`.`source_prefix`,`temp1`.`source_name`,`temp1`.`date_updated`,AVG(`temp1`.`value`) AS `avg_value`,SUM(`temp1`.`value` * `temp1`.`weight`) / SUM(`weight`) AS `rating`

            FROM `temp1` LEFT JOIN `temp2` ON `temp1`.`subcat_hash` = `temp2`.`subcat_hash`
            WHERE `temp2`.`subcat_hash` = `temp1`.`subcat_hash`
            AND `temp1`.`date_updated` = `temp2`.`maxdate`

            GROUP BY `temp1`.`cat_hash`;

(现在我看过它,写下来,在我看来,我应该在最后一个查询中使用INNER JOIN(以避免900k * 900k的临时表)).

不过,有没有正常的方法呢?

UPD:一些图片供参考:

删除死亡的ImageShack链接

UPD:提出解决方案的解释:

+----+-------------+-------+------+---------------+------------+---------+--------------------------------------------------------------------------------------+--------+----------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key        | key_len | ref                                                                                  | rows   | filtered | Extra                                        |
+----+-------------+-------+------+---------------+------------+---------+--------------------------------------------------------------------------------------+--------+----------+----------------------------------------------+
|  1 | SIMPLE      | cur   | ALL  | NULL          | NULL       | NULL    | NULL                                                                                 | 893085 |   100.00 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | next  | ref  | prefix        | prefix     | 1074    | bigbigtable.cur.source_prefix,bigbigtable.cur.source_name,bigbigtable.cur.element_id |      1 |   100.00 | Using where                                  |
+----+-------------+-------+------+---------------+------------+---------+--------------------------------------------------------------------------------------+--------+----------+----------------------------------------------+

解决方法

使用hashses是数据库引擎可以执行连接的方式之一.应该是非常罕见的,你必须编写自己的基于哈希的联接;这肯定不像其中一个,有一个900k行的表与一些聚合.

根据您的评论,此查询可能会执行您要查找的内容

SELECT cur.source_prefix,cur.source_name,cur.category,cur.element_id,MAX(cur.date_updated) AS DateUpdated,AVG(cur.value) AS AvgValue,SUM(cur.value * cur.weight) / SUM(cur.weight) AS rating
FROM eev0 cur
LEFT JOIN eev0 next
    ON next.date_updated < '2009-05-01'
    AND next.source_prefix = cur.source_prefix 
    AND next.source_name = cur.source_name
    AND next.element_id = cur.element_id
    AND next.date_updated > cur.date_updated
WHERE cur.date_updated < '2009-05-01'
AND next.category IS NULL
GROUP BY cur.source_prefix,cur.element_id

GROUP BY对每个源类别元素执行计算.

JOIN在那里过滤掉旧条目.它会查找稍后的条目,然后WHERE语句过滤出后面条目存在的行.这样的连接可以从(source_prefix,source_name,element_id,date_updated)上的索引中获益.

过滤掉旧条目有许多方法,但是这个方法往往表现得很好.

相关文章

SELECT a.*,b.dp_name,c.pa_name,fm_name=(CASE WHEN a.fm_n...
if not exists(select name from syscolumns where name=&am...
select a.*,pano=a.pa_no,b.pa_name,f.dp_name,e.fw_state_n...
要在 SQL Server 2019 中设置定时自动重启,可以使用 Window...
您收到的错误消息表明数据库 &#39;EastRiver&#39; 的...
首先我需要查询出需要使用SQL Server Profiler跟踪的数据库标...