2009-05-09 5 views
0

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

Основная таблица имеет два дочерних таблиц:

CREATE TABLE main (id INT PRIMARY KEY, name VARCHAR(8)); 

CREATE TABLE child1(id INT PRIMARY KEY, main_id int, name VARCHAR(8)); 
ALTER TABLE child1 add constraint fk_child1_main foreign key (main_id) references main (id); 

CREATE TABLE child2(id INT PRIMARY KEY, main_id int, name VARCHAR(8)); 
ALTER TABLE child2 add constraint fk_child2_main foreign key (main_id) references main (id); 

INSERT INTO main (id, name) VALUES (1, 'main'); 
INSERT INTO child1 (id, main_id, name) VALUES (2, 1, 'child1'); 

Там нет строк в child2 и следующий запрос не возвращает ни одной строки, когда она пуста:

SELECT 
    main.* 
FROM 
    main 
INNER JOIN 
    child1 
ON 
    main.id = child1.main_id 
INNER JOIN 
    child2 
ON 
    main.id = child2.main_id 
WHERE 
    child1.name = 'child1' OR 
    child2.name = 'DOES NOT EXIST'; 

Если строка добавляется child2, даже если он не соответствует предложению WHERE, тогда SELECT возвращает строку в основной таблице.

INSERT INTO child2 (id, main_id, name) VALUES (4, 1, 'child2'); 

Я тестировал это на Derby и SQLite, поэтому это выглядит как нечто общее с базами данных.

Почему это ведет себя так?

Что я могу сделать, чтобы исправить это?

Я мог бы переключиться на отдельные SELECT-соединения UNION, но это гораздо более многословно, и, кроме того, мы динамически генерируем SQL, и мне бы не пришлось менять код.

Еще одно решение - просто добавить в базу данных тупик, но это грязно.

PS Основная таблица - таблица сеансов в системе управления активами, которая регистрирует активы, которые клиенты ищут. Существуют различные типы поиска, и каждый вид получает отдельную дочернюю таблицу, плюс есть таблица дочерних элементов атрибутов для пар ключ/значение для сеанса, на который можно выполнить поиск.

ответ

4

Когда child2 не имеет строк, запрос не возвращает строк из-за внутреннего соединения с таблицей child2. Если вы присоединитесь к таблице, у которой нет строк, вы никогда не получите никаких результатов - вместо этого вам придется использовать внешнее соединение с child2, если вы хотите получить результаты, когда child2 пуст.

Когда child2 действительно есть ряд, причину вашего возвращения результатов запроса из-за пункта, где:

 
WHERE 
    child1.name = 'child1' OR 
    child2.name = 'DOES NOT EXIST'; 

Внутреннее соединение говорит там должно быть что-то в child2 с согласующим ID, но Where в нем есть OR, поэтому вы получите результаты только потому, что child1.name = 'child1'. После этого база данных не должна беспокоиться о просмотре таблиц child2.

Чтобы это исправить:

У меня есть подозрение, что вы только хотите, чтобы вернуть ребенка строки, когда некоторые условия. Вы должны внешнее_соединение к обоим из них, и, возможно, также переместить дополнительные условия от ИНЕКЕ джойна пункта, например:

 
SELECT 
    main.* 
FROM 
    main 
LEFT OUTER JOIN 
    child1 
ON 
    main.id = child1.main_id 
    AND child1.name = 'child1' 
LEFT OUTER JOIN 
    child2 
ON 
    main.id = child2.main_id 
    AND child2.name = 'whatever' 
  • Наружный присоединяется означает, что вы имеете возможность получения результатов даже если одна таблица пуста.

  • Перемещение дополнительных условий (child1.name = ...) из предложения WHERE во внешнее соединение означает, что вы получите только информацию о таблицах, если условие истинно.(Я думаю, что это может быть то, что вы пытаетесь сделать, но, может быть, нет, в этом случае оставьте условия в предложении WHERE, где первоначально имел их.)

2

Он никогда возвращения ничего, потому что вы используете внутренние соединения ,

Измените ваш внутренний присоединяется к левой присоединяется

+0

Тогда почему добавление несогласованной строки в child2 изменяет результат запроса? Согласно этому утверждению, даже после добавления строки несоответствия к child2, запрос все равно не должен возвращать строки. –

+1

Nevermind, я вижу, что это main.id = child2.main_id, который мешал запросу возвращать какие-либо результаты, даже если child2.name не совпало. Я просто игнорировал эту часть запроса. –

2

Когда вы говорите INNER JOIN вы задаете запрос для возврата строк, которые имеют результаты на обеих сторонах соединения. Это означает, что любые строки, которые не имеют соответствующих дочерних строк, будут удалены.

Похоже, что вы ищете LEFT JOIN, который будет включать все строки в левой части соединения (main), даже если у них нет соответствующей записи с правой стороны (child1, child2) ,

Это стандартное поведение и очень распространенная проблема для людей, не знакомых с SQL. Wikipedia содержит всю информацию, в противном случае быстрый Google search доставит много результатов.

+0

Тогда почему добавление несогласованной строки в child2 изменяет результат запроса? Согласно этому утверждению, даже после добавления строки несоответствия к child2, запрос все равно не должен возвращать строки. –

+0

Nevermind, я вижу, что это main.id = child2.main_id, который мешал запросу возвращать какие-либо результаты, даже если child2.name не совпало. Я просто игнорировал эту часть запроса. –

Смежные вопросы