2014-11-04 5 views
0

У меня есть выбор (MS SQL), где я присоединяюсь к родителям с тремя детьми. Я хотел бы выбрать всех детей, если ЛЮБОЙ из детей соответствует критериям (Тип <> «Готово»). Производительность очень важна.Выделить всех детей, если они соответствуют критериям

Я попытался

SELECT p.*,c.* FROM Parent p 
INNER JOIN Child c ON p.Id=c.ParentId 
WHERE p.Id IN (SELECT DISTINCT c.ParentId FROM Child c2 WHERE c2.ParentId=p.Id AND c2.Type<>'Done') 

Но внутренний выбор часто возвращается 2000+ детей, поэтому IN дает плохую производительность. Я также попытался EXISTS и посчитайте:

SELECT p.*,c.* FROM Parent p 
INNER JOIN Child c ON p.Id=c.ParentId 
WHERE EXISTS(SELECT 1 FROM Child c2 WHERE c2.ParentId=p.Id AND c2.Type<>'Done') 

SELECT p.*,c.* FROM Parent p 
INNER JOIN Child c ON p.Id=c.ParentId 
WHERE 0 < (SELECT COUNT(c2.Id) FROM Child c2 WHERE c2.ParentId=p.Id AND c2.Type<>'Done') 

Все это возвращает правильный результат, но с плохой производительностью. Я думаю, что у меня есть все необходимые индексы.

У кого-нибудь есть альтернативы?

+0

Какие индексы у вас есть? Индекс для Child.Type * может * увеличить производительность. – Karsten

+0

У меня есть указатели на Child.Type и все ID. –

ответ

1

Новый Ответ:

Ну тогда я хотел бы создать временную таблицу с запросами ниже и использовать их вместо ваших вложенных подзапросов и попробовать, какая комбинация является самым быстрым.

Update:

DECLARE @tmp TABLE(pid INT); 

INSERT INTO @tmp(pid) 
SELECT p.Id FROM Parent p 
WHERE EXISTS (SELECT 1 FROM Child c WHERE c.ParentId=p.Id AND c2.Type<>'Done'); 

SELECT c.*, p.* 
FROM @tmp t INNER JOIN Child c ON t.pid = c.ParentID INNER JOIN Parent p ON t.pid = p.Id 

Вы также можете объявить @tmp так же, как вы объявленную Родитель и заполнить всю таблицу. Таким образом, вы можете избежать JOIN с родителем. Но без профилирования всех возможных решений невозможно сказать, какой из них будет самым быстрым.

Старый Ответ:

Я думаю, что вам не нужно, чтобы присоединиться к столу ребенка. Это оставляет вас с:

SELECT p.Id FROM Parent p 
WHERE EXISTS (SELECT 1 FROM Child c2 WHERE c2.Type<>'Done' AND c2.Id = p.Id) 

Вы также можете попробовать

SELECT p.Id FROM Parent p 
INNER JOIN Child c ON p.Id=c.ParentId 
WHERE c.Type <> 'Done' 
GROUP BY p.Id 

Или

SELECT DISTINCT p.Id FROM Parent p 
INNER JOIN Child c ON p.Id=c.ParentId 
WHERE c.Type <> 'Done' 

Они все должны быть эквивалентны.

+0

Мне нужно присоединиться к таблице «Дети», если я хочу извлечь данные из него. Я поставлю вопрос, чтобы показать это. –

+0

Использование ваших 2-го и 3-го фрагментов кода не вернет дочерние строки, где Type = 'Done' –

+0

Если вы сообщите мне, какую СУБД вы используете, я мог бы привести пример. – Karsten

0

Вариант EXISTS обычно должен быть самым быстрым.

Предполагая, что у вас есть все соответствующие индексы, нет никакого способа оптимизировать этот запрос, кроме подготовки данных в временных таблицах. Вы можете создать денормализованную таблицу, которая объединяет родительскую и дочернюю таблицу. Это должно привести к увеличению производительности.

Это особенно приемлемое решение, если вы никогда не удаляете или не обновляете существующие родительские записи, а добавляете только новые. Потому что в этом случае вы не будете страдать от аномалий обновления.

Если вы удаляете или обновляете существующие родительские записи, это приведет к тому, что процесс денормализованной таблицы станет более сложным.

0

Вы также можете использовать CTE:

;WITH Parents 
AS 
(
    SELECT 
     p.* 
    FROM 
     [Parent] AS p 
    WHERE 
     EXISTS 
     (
      SELECT 
       1 
      FROM 
       [Child] AS c 
      WHERE 
       p.ID = c.ParentID 
       AND c.[Type] <> 'Done' 
     ) 
) 

-- Select all children and siblings... 
SELECT 
    p.ID AS [ParentID] 
    ,p.[Name] 
    ,c.ID AS [ChildID] 
    ,c.[Type] 
FROM 
    Parents AS p 
    INNER JOIN [Child] AS c 
     ON p.ID = c.ParentID 

план Запрос довольно мал, и это может хорошо работать для вас.