【问题标题】:How to optimise MySQL query containing a subquery?如何优化包含子查询的 MySQL 查询?
【发布时间】:2011-02-11 04:16:20
【问题描述】:

我有两张桌子,HousePerson。对于 House 中的任何一行,Person 中可以有 0、1 或多个对应行。但是,在这些人中,最多有一个人的状态为“ACTIVE”,其他人的状态均为“CANCELLED”。

例如

SELECT * FROM House LEFT JOIN Person ON House.ID = Person.HouseID

House.ID | Person.ID | Person.Status
       1 |         1 |     CANCELLED
       1 |         2 |     CANCELLED
       1 |         3 |        ACTIVE
       2 |         1 |        ACTIVE
       3 |      NULL |          NULL
       4 |         4 |     CANCELLED

我想过滤掉取消的行,得到这样的结果:

House.ID | Person.ID | Person.Status
       1 |         3 |        ACTIVE
       2 |         1 |        ACTIVE
       3 |      NULL |          NULL
       4 |      NULL |          NULL

我通过以下子选择实现了这一点:

SELECT *
FROM House
LEFT JOIN 
(
    SELECT *
    FROM Person
    WHERE Person.Status != "CANCELLED"
) Person
ON House.ID = Person.HouseID

...有效,但会破坏所有索引。有没有更好的解决方案?

我正在使用 MySQL,所有相关列都已编入索引。 EXPLAINpossible_keys 中没有列出任何内容。

谢谢。

【问题讨论】:

    标签: sql mysql optimization subquery left-join


    【解决方案1】:

    怎么样:

    SELECT *
    FROM House
    LEFT JOIN Person
    ON House.ID = Person.HouseID 
    AND Person.Status != "CANCELLED"
    

    【讨论】:

    • 在我的示例中,这将删除 House.ID = 4 的行
    • 上面的查询应该返回所有四行并在我的测试数据库中执行。试一试。
    • 糟糕,抱歉,我误读了您的回答。我认为那将是完美的!
    • 是的,我是个傻瓜,因为我不知道这一点,并且第一次没有正确阅读您的答案。谢谢!
    【解决方案2】:

    您可以控制数据库结构吗?如果是这样,我认为您可以通过从 Person 表中删除列 Status 并将 ActivePersonID 列添加到 House 表来更好地表示您的数据。这样您就可以从 Person 中删除所有多余的 CANCELED 值,并消除应用程序或存储过程代码,以确保每个家庭只有一个人处于活动状态。

    此外,您可以将查询表示为

     SELECT * FROM House LEFT JOIN Person ON House.ActivePersonID = Person.ID
    

    【讨论】:

    • 这是一种新颖的方法。我经常忘记像这样“跳出框框思考”。感谢那。即便如此,我想我还是会坚持上面提到的解决方案(我需要保持被取消的人和房子之间的联系)。
    • 您不会丢失已取消的人员与房屋之间的链接,因为您维护了 Person.ID 和 Person.HouseID 列。您只需知道 House.ActivePersonID 中指定的单个 Person ID 是活动的,所有其他的都被取消。您的数据库写入量净减少,存储空间净减少,但存储的数据值没有净损失。
    • 哦,我明白了!那就更好了!谢谢拉里。
    【解决方案3】:

    用途:

       SELECT * 
         FROM HOUSE h 
    LEFT JOIN PERSON p ON p.houseid = h.id
                      AND p.status = 'ACTIVE'
    

    【讨论】:

    • @Frank Shearar:编辑以纠正不准确之处,而不是出于您的风格偏好,即表格名称只有首字母大写。
    【解决方案4】:

    这是在 SQL Server 中,但逻辑似乎有效,与上面的 Chris 相呼应:

    declare @house table
    (
        houseid int
    )
    
    declare @person table
    (
        personid int,
        houseid int,
        personstatus varchar(20)
    )
    
    insert into @house (houseid) VALUES (1)
    insert into @house (houseid) VALUES (2)
    insert into @house (houseid) VALUES (3)
    insert into @house (houseid) VALUES (4)
    
    insert into @person (personid, houseid, personstatus) VALUES (1, 1, 'CANCELLED')
    insert into @person (personid, houseid, personstatus) VALUES (2, 1, 'CANCELLED')
    insert into @person (personid, houseid, personstatus) VALUES (3, 1, 'ACTIVE')
    insert into @person (personid, houseid, personstatus) VALUES (1, 2, 'ACTIVE')
    insert into @person (personid, houseid, personstatus) VALUES (4, 4, 'CANCELLED')
    
    select * from @house
    select * from @person
    
    select *
    from @house h LEFT OUTER JOIN @person p ON h.houseid = p.houseid 
        AND p.personstatus <> 'CANCELLED'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-10
      • 2023-01-03
      • 1970-01-01
      • 2016-04-07
      • 2011-11-27
      • 2021-04-15
      相关资源
      最近更新 更多