【问题标题】:Query with Multiple subqueries is making my system hang带有多个子查询的查询使我的系统挂起
【发布时间】:2016-09-09 01:00:35
【问题描述】:

我写了一个查询,其中有一个包含人名的文件。每个人都有一个家庭身份证。所以同一个家庭的多个成员都将拥有相同的 ID。

我编写了一个查询,首先列出家庭 ID,然后在同一输出行上列出所有家庭成员。


ID39874 John Smith Harry Smith Jane Smith


我有一些家庭成员超过 9 人的家庭。我的以下查询非常适合测试带有几条记录的小文件。但是当我使用包含超过 20,000 条记录的文件运行查询时,我的系统会严重挂起。我知道我的系统由于子查询太多而被杀死,但是为了测试,我想不出任何其他方法来解决这个问题。

谁能提出另一种在不杀死我的系统的情况下解决此问题的方法。谢谢

注意 - 我知道我有 8 个子查询来涵盖最多有 9 个家庭成员的家庭。任何少于 9 个成员的家庭只会将成员输出为空值,所以我对这个结果很满意。


工作查询:

select v.family_id, v.full_name, 
    (select b.full_name 
        from family_table b 
        where v.family_id = b.family_id 
        LIMIT 1,1) as voter_02,
    (select c.full_name 
        from family_table c 
        where v.family_id = c.family_id 
        LIMIT 2,1) as voter_03,
    (select d.full_name 
        from family_table d 
        where v.family_id = d.family_id 
        LIMIT 3,1) as voter_04,
    (select e.full_name 
        from family_table e 
        where v.family_id = e.family_id 
        LIMIT 4,1) as voter_05,
    (select f.full_name 
        from family_table f 
        where v.family_id = f.family_id 
        LIMIT 5,1) as voter_06,
    (select g.full_name 
        from family_table g 
        where v.family_id = g.family_id 
        LIMIT 6,1) as voter_07,
    (select h.full_name 
        from family_table h 
        where v.family_id = h.family_id 
        LIMIT 7,1) as voter_08,
    (select i.full_name 
        from family_table i 
        where v.family_id = i.family_id 
        LIMIT 8,1) as voter_09
from family_table v
group by family_id

【问题讨论】:

  • 我假设您已经尝试过索引?
  • @bassick 为什么所有成员必须在同一行不同列?
  • blackpen - 我索引了“family_id”列,我从上面的原始查询工作得非常快。谢谢!!
  • @Pachonk - 我有一个我的用户使用的表格编辑器。实际上,我将为每个家庭成员显示另一个相应的列,以便我的用户可以在同一个“家庭编辑页面”上编辑每个家庭和成员的信息。因此,将所有家庭成员和我选择的任何相应信息作为一行查询以适合我的表格编辑器布局,这一点很重要。谢谢

标签: mysql subquery limit


【解决方案1】:

如果一个家庭的人数超过 8 人怎么办?我认为您使用的子查询方法不是获取所有家庭成员的可扩展方法。

话虽如此,我认为您确实有两个选择:

选项 1 (GROUP_CONCAT):

SELECT v.family_id, GROUP_CONCAT(v.fullname) as voters
FROM family_table v
GROUP BY v.family_id

这里有一个 SQLFiddle 来显示结果集:http://sqlfiddle.com/#!9/6da73/1

选项 2(将所有选民作为单独的行返回,直接相邻):

SELECT v.family_id, v.fullname
FROM family_table v
ORDER BY v.family_id:

选项 3(不是我最喜欢的,可以做得更好,但我相信它应该比您使用的子查询更快):

SELECT v.family_id, v.fullname, v2.fullname, v3.fullname, v4.fullname, v5.fullname,
  v6.fullname, v7.fullname, v8.fullname
FROM family_table v
LEFT JOIN family_table v2 ON v2.family_id = v.family_id AND v2.fullname != v.fullname
LEFT JOIN family_table v3 ON v3.family_id = v.family_id AND v3.fullname NOT IN (v.fullname,  v2.fullname)
LEFT JOIN family_table v4 ON v4.family_id = v.family_id AND v4.fullname NOT IN (v.fullname,  v2.fullname, v3.fullname)
LEFT JOIN family_table v5 ON v5.family_id = v.family_id AND v5.fullname NOT IN (v.fullname,  v2.fullname, v3.fullname, v4.fullname)
LEFT JOIN family_table v6 ON v6.family_id = v.family_id AND v6.fullname NOT IN (v.fullname,  v2.fullname, v3.fullname, v4.fullname,
                                                                            v5.fullname)
LEFT JOIN family_table v7 ON v7.family_id = v.family_id AND v7.fullname NOT IN (v.fullname,  v2.fullname, v3.fullname, v4.fullname,
                                                                                v5.fullname, v6.fullname)
LEFT JOIN family_table v8 ON v8.family_id = v.family_id AND v8.fullname NOT IN (v.fullname,  v2.fullname, v3.fullname, v4.fullname,
                                                                                v5.fullname, v6.fullname, v7.fullname)
LEFT JOIN family_table v9 ON v9.family_id = v.family_id AND v9.fullname NOT IN (v.fullname,  v2.fullname, v3.fullname, v4.fullname,
                                                                                v5.fullname, v6.fullname, v7.fullname, v8.fullname)
GROUP BY v.family_id;

(SQL Fiddle)

注意:对于 NOT IN 部分,如果您在每一行都有一个主键,您应该使用该主键作为 not in,因为它会更好地被索引(并且有一个很小的机会 2 人在 1 个家庭中同名)

【讨论】:

  • 谢谢回复。我知道我的查询仅涵盖 9 个人。我只是想展示一个例子。 “Group_concat”和多行对我不起作用。我需要同一行中的所有成员在他们自己的列中。
  • 我添加了第三个选项。它仍然效率低下,但我认为它应该比许多子查询更好。
  • Pachonk - 我收到了这个错误。 (#1104 - SELECT 将检查超过 MAX_JOIN_SIZE 行;检查您的 WHERE 并使用 SET SQL_BIG_SELECTS=1 或 SET MAX_JOIN_SIZE=# 如果 SELECT 没问题)
  • 抱怨您使用此查询加入了太多行。您可以接受错误的建议,或添加位置/限制。这不是主要的解决方案,但它应该比您的原始查询更快。出于好奇,我们尝试选择多少行?
  • 当我运行原始查询、后索引时,我最终得到了 8000 多个唯一的家庭行(有些有多个家庭成员,有些没有。但输出具有所有唯一的“family_id”值。
【解决方案2】:

索引列“family_id”使我的原始查询运行得非常快。感谢@blackpen 的评论。

在某些时候,我仍然想找出一个可以涵盖任意数量的家庭成员的查询,而不仅仅是我硬编码的大量子查询。但这绝对适用于现在。感谢@Pachonk 的输入。

【讨论】:

  • 列永远不能是动态的,只有行可以。如果您想让它支持任意数量的家庭成员,则必须使用上面的两个查询之一。
  • 如果您使用其他语言,您可以使用组连接来允许家庭成员的动态范围。这将是一个更快的查询,您可以使用 explode 将逗号分隔的列表转换为数组。 (当然,大多数其他语言都有类似的功能。
  • 但是,我很高兴听到索引有所帮助。
  • 是的。我累了,没有好好思考。哈哈。我想我现在对我的原始查询很好。我将在本周末尝试编辑您的第三个查询。再次感谢您的意见。
猜你喜欢
  • 2013-03-19
  • 1970-01-01
  • 1970-01-01
  • 2018-11-24
  • 1970-01-01
  • 2017-05-09
  • 1970-01-01
  • 1970-01-01
  • 2013-06-12
相关资源
最近更新 更多