如何优化包含子查询的MySQL查询?

时间:2010-04-22 16:54:06

标签: sql mysql optimization subquery left-join

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

e.g。

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并且所有相关列都已编入索引。 EXPLAIN possible_keys中没有列出任何内容。

感谢。

4 个答案:

答案 0 :(得分:4)

怎么样:

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

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:1)

使用:

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

答案 3 :(得分:0)

这是在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'