Как оптимизировать MySQL-запрос, содержащий подзапрос? - PullRequest
2 голосов
/ 22 апреля 2010

У меня есть две таблицы, House и Person.Для любой строки в House может быть 0, 1 или много соответствующих строк в Person.Но из этих людей максимум один будет иметь статус «АКТИВНЫЙ», остальные будут иметь статус «ОТМЕНЕН».

например,

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 ]

4 голосов
/ 22 апреля 2010

Как насчет:

SELECT *
FROM House
LEFT JOIN Person
ON House.ID = Person.HouseID 
AND Person.Status != "CANCELLED"
2 голосов
/ 22 апреля 2010

У вас есть контроль над структурой базы данных? Если это так, я думаю, что вы могли бы лучше представить свои данные, удалив столбец Status из таблицы Person и вместо этого добавив столбец ActivePersonID в таблицу House. Таким образом вы удаляете все избыточные значения CANCELED из Person и удаляете код приложения или хранимой процедуры, чтобы гарантировать, что активен только один человек на домохозяйство.

Кроме того, вы можете представить свой запрос как

 SELECT * FROM House LEFT JOIN Person ON House.ActivePersonID = Person.ID
1 голос
/ 22 апреля 2010

Использование:

   SELECT * 
     FROM HOUSE h 
LEFT JOIN PERSON p ON p.houseid = h.id
                  AND p.status = 'ACTIVE'
0 голосов
/ 22 апреля 2010

Это в SQL Server, но логика, кажется, работает, повторяя Криса выше:

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'
...