优化联合 SQL 查询

Optimizing an union SQL query

我有一个很大的查询,所有查询都包含我在我的应用程序中列出的供稿所需的相同类型。现在的问题是这个查询不是很快。我在想,如果我限制每个单独的工会,它可能会加快一点,但我不确定。

所以基本上我的问题是如何优化此查询以更快地执行?

SELECT DISTINCT 
      alert_id, 
      uniquekey, 
      type, 
      user_id, 
      date_added 
   FROM 
   (
     ( SELECT 
             r.alert_id, 
             r.alert_id AS uniquekey, 
             'reply' AS `type`,
             r.user_id, 
             r.date_added
          FROM 
             `reply` r 
                LEFT JOIN `alerts` a
                   ON r.alert_id = a.alert_content_id  
          WHERE 
                 r.user_id = :id 
             AND a.hide = '0'
          ORDER BY 
             date_added DESC )
     UNION
     ( SELECT 
             r.alert_id, 
             r.alert_id AS uniquekey, 
             'replyfromfollowing' AS `type`,
             r.user_id, 
             r.date_added
          FROM 
             `reply` r 
                LEFT JOIN `alerts` a
                   ON r.alert_id = a.alert_content_id 
          WHERE 
                 r.user_id IN( '$followingstring' ) 
             AND a.hide = '0'
            ORDER BY date_added DESC )
     UNION 
     ( SELECT 
             i.alert_id, 
             i.alert_id AS uniquekey, 
             'liked' AS `type`,
             i.user_id, 
             i.date_added 
          FROM 
             `interactions` i 
                LEFT JOIN `alerts` a
                   ON i.alert_id = a.alert_content_id 
          WHERE 
                 i.user_id = :id 
             AND a.hide = '0'
          GROUP BY 
             alert_id
          ORDER BY 
             date_added DESC )
     UNION 
     ( SELECT 
             i.alert_id, 
             i.alert_id AS uniquekey, 
             'likedfromfollowing' AS `type`,
             i.user_id, 
             i.date_added 
          FROM 
             `interactions` i 
                LEFT JOIN `alerts` a
                   ON i.alert_id = a.alert_content_id
          WHERE 
                 i.user_id IN ( '$followingstring' ) 
             AND a.hide = '0'
          GROUP BY 
             alert_id
          ORDER BY 
             date_added DESC ) 
     UNION 
     ( SELECT 
             alerts as alert_id,
             alert_content_id AS uniquekey,
             'following' AS `type`,
             user_id, 
             date_added 
          FROM 
             alerts a 
                LEFT JOIN `alerts_content` ac 
                   ON ac.id = a.alert_content_id 
          WHERE 
                 a.user_id IN ( '$followingstring' ) 
             AND ac.anoniem = '0' 
             AND a.hide = '0' 
          GROUP BY 
             alert_id 
          ORDER BY 
             date_added DESC )
      ) joined 
   GROUP BY 
      uniquekey 
   ORDER BY 
      date_added DESC 
   LIMIT 
      ".(int)$start.",20"

table 结构

Reply table Structure:
id
user_id
alert_id
description
reply_on_alert
reply_on_reply
date_added

Interaction table Structure:
id
alert_id
action_id
reply_id
user_id
date_added

Alerts table structure(Yes i know BIG mistake naming `id` : `alerts`):
alerts
title
alert_content_id
user_id
cat
lat
lon
state
hide
date_added

alerts_content table structure:
id
alert_id
description
img

查询结果:

Array
(
    [0] => Array
        (
            [alert_id] => 173404
            [uniquekey] => 173404
            [type] => reply
            [user_id] => 2
            [date_added] => 2015-06-01 16:34:16
        )

    [1] => Array
        (
            [alert_id] => 172174
            [uniquekey] => 172174
            [type] => replyfromfollowing
            [user_id] => 1380
            [date_added] => 2015-06-01 16:01:04
        )

    [2] => Array
        (
            [alert_id] => 171772
            [uniquekey] => 171772
            [type] => liked
            [user_id] => 2
            [date_added] => 2015-06-01 15:58:44
        )

    [3] => Array
        (
            [alert_id] => 149423
            [uniquekey] => 149423
            [type] => reply
            [user_id] => 2
            [date_added] => 2015-06-01 15:25:56
        )

    [4] => Array
        (
            [alert_id] => 164742
            [uniquekey] => 164742
            [type] => reply
            [user_id] => 2
            [date_added] => 2015-05-12 09:46:39
        )

    [5] => Array
        (
            [alert_id] => 163344
            [uniquekey] => 163344
            [type] => replyfromfollowing
            [user_id] => 3
            [date_added] => 2015-05-12 09:44:46
        )

    [6] => Array
        (
            [alert_id] => 164205
            [uniquekey] => 164205
            [type] => liked
            [user_id] => 2
            [date_added] => 2015-05-11 11:06:39
        )

    [7] => Array
        (
            [alert_id] => 160890
            [uniquekey] => 160890
            [type] => replyfromfollowing
            [user_id] => 1380
            [date_added] => 2015-05-08 14:29:34
        )

    [8] => Array
        (
            [alert_id] => 163002
            [uniquekey] => 163002
            [type] => replyfromfollowing
            [user_id] => 1380
            [date_added] => 2015-05-08 13:31:12
        )

    [9] => Array
        (
            [alert_id] => 159123
            [uniquekey] => 159123
            [type] => replyfromfollowing
            [user_id] => 48
            [date_added] => 2015-04-30 15:10:32
        )

    [10] => Array
        (
            [alert_id] => 150546
            [uniquekey] => 150546
            [type] => replyfromfollowing
            [user_id] => 16
            [date_added] => 2015-04-21 21:52:49
        )

    [11] => Array
        (
            [alert_id] => 149497
            [uniquekey] => 149497
            [type] => reply
            [user_id] => 2
            [date_added] => 2015-04-10 15:19:06
        )

    [12] => Array
        (
            [alert_id] => 141078
            [uniquekey] => 141078
            [type] => liked
            [user_id] => 2
            [date_added] => 2015-04-10 15:15:32
        )

    [13] => Array
        (
            [alert_id] => 125466
            [uniquekey] => 125466
            [type] => replyfromfollowing
            [user_id] => 3
            [date_added] => 2015-04-09 00:15:22
        )

    [14] => Array
        (
            [alert_id] => 134592
            [uniquekey] => 134592
            [type] => replyfromfollowing
            [user_id] => 3
            [date_added] => 2015-04-09 00:11:04
        )

    [15] => Array
        (
            [alert_id] => 124194
            [uniquekey] => 124194
            [type] => likedfromfollowing
            [user_id] => 3
            [date_added] => 2015-04-09 00:08:35
        )

    [16] => Array
        (
            [alert_id] => 128645
            [uniquekey] => 128645
            [type] => likedfromfollowing
            [user_id] => 3
            [date_added] => 2015-04-09 00:07:29
        )

    [17] => Array
        (
            [alert_id] => 144867
            [uniquekey] => 144867
            [type] => replyfromfollowing
            [user_id] => 3
            [date_added] => 2015-04-06 13:59:19
        )

    [18] => Array
        (
            [alert_id] => 133355
            [uniquekey] => 133355
            [type] => liked
            [user_id] => 2
            [date_added] => 2015-03-31 16:16:15
        )

    [19] => Array
        (
            [alert_id] => 141075
            [uniquekey] => 141075
            [type] => liked
            [user_id] => 2
            [date_added] => 2015-03-30 15:17:01
        )

)

一些可能性,排名不分先后:

优化#1:

也在子查询中使用 LIMIT。但是,由于您使用的是 OFFSET,因此可能不太清楚如何操作。

在查询之前,计算$start+20 并将其放入,比如说$limit。然后使用 LIMIT $limit 进行内部查询。不,不要对它们使用 OFFSET。这保证您从每个查询中获得足够的行以满足外部 OFFSET $start LIMIT 20.

优化#2:

重组tables,这样你就不需要JOIN到另一个table(alerts)来确定是否显示记录。也就是说,hide 会阻止许多潜在的优化。在进一步建议之前,我们需要了解 LEFT 的必要性。 reply 等中是否有不在 alerts 中的行?如果没有,去掉 LEFT 并研究搜索 alerts with theOFFSETandLIMIT`,然后加入其他 4 个 table。

优化#3:

重组数据,使一个核心table,alerts和另外4个table挂在上面。确保在这个新的 table.

中包含 this 查询所需的大部分(全部?)字段

优化#4:

当前结构需要在考虑 OFFSETLIMIT 之前对 4 个 table 中的每一个进行完整扫描。这闻起来像 "pagination";是吗?为了优化 "pagination",目标是避免 table 扫描和 OFFSET;相反,请记住您 "left off" 的位置,以便查询可以

WHERE ... AND x < $left_off
ORDER by x DESC
LIMIT 20

这应该可以只读取 20 行,而不是整个 table(s)。这将使查询 much 更快,尤其是对于后面的页面。 (更大的 OFFSET 需要更多时间`)

我在 my blog 中讨论分页优化。