Задавайте вопросы, мы ответим
Вы не зашли.
Здравствуйте. Есть две таблицы:
a: id int, Name: varchar(50)
b: id int, Title : varchar(50), id_a : int
b ссылается на a через ключ id_a. Мне нужно получить все записи из таблицы a, на которые нет ссылок из таблицы b. Делаю запрос
SELECT a.Name FROM a JOIN b ON a.id != b.id_a
Если в таблице b есть хоть одна запись, то запрос работает "на ура". Но вот если таблица b пустая, то вместо всех записей из a возвращается пустота. Делать через
SELECT a.Name FROM a WHERE a.id NOT IN (SELECT DISTINCT b.id_a FROM b)
ибо быстродействие этого запроса оставляет желать лучшего.
Посоветуйте, как быть, пожалуйста!
Неактивен
Вам нужен LEFT JOIN
примерно так:
SELECT a.Name FROM a LEFT JOIN b ON a.id = b.id_a WHERE b.id_a IS NULL
вроде так, гуру поправят, если ошибся
Отредактированно Shopen (07.07.2008 17:37:52)
Неактивен
Спасибо, Shopen, пожоже, что это то, что нужно, сейчас попробую.
Неактивен
Подскажите пожалуйста, а как из а выбрать все записи кроме тех, которые имеют ссылки на них в b с определенным флагом.
Неактивен
В WHERE добавляешь нужное условие
Неактивен
а можно пример ? потому и спрашиваю что не получается это условие сделать. те же таблицы только в b есть еще поле флаг
Неактивен
Задача у тебя поставлена не четко.
SELECT a.Name FROM a LEFT JOIN b ON a.id = b.id_a WHERE b.id_a IS NULL AND b.Flag = true
или
SELECT a.Name FROM a LEFT JOIN b ON a.id = b.id_a WHERE b.id_a IS NULL OR b.Flag = true
Неактивен
Это не совсем то. Так в результате будут все записи на которых нет ссылки в b и записи на которые есть ссылка но с определенным флагом.
А в перовом вашем примере с AND по моему будет всегда результатом 0 строк.
А мне нужно получить все записи из таблицы А кроме тех на которые есть ссылки из B с определенным флагом.
У меня таблица А с вопросами для игры. В таблице B вопрос который задается. но так как два игрока а вопросы они задают друг другу одни и те же (10 вопросов по очереди друг другу) то в таблице В есть флаг , кому сейчас отвечать. Я хочу выбрать из таблицы А все вопросы кроме тех которые задавались игроку.
Неактивен
А мне нужно получить все записи из таблицы А кроме тех на которые есть ссылки из B с определенным флагом.
Ага, тогда добавь условие в ON:
SELECT a.Name FROM a LEFT JOIN b ON (a.id = b.id_a AND b.Flag = true) WHERE b.id_a IS NULL
В этом случае у тебя будут все записи из А, а для тех записей из А, которым нашлось соответствие в Б (по id и флагу), поле b.id не будет пустым. Его мы в WHERE и уберем.
Неактивен
Потрясающе, не представляете как я вам благодарен. Я очень долго мучался с этими JOIN а оказалось так просто.
Неактивен
Знал бы ты, как я досадовал на себя, что не додумался до того простого решения, которое подсказал мне Shopen
Неактивен
Интересный момент нашел в мануале.
Manual написал:
6.4.1.1 Синтаксис оператора JOIN
Никогда не следует указывать в части ON какие бы то ни было условия, накладывающие ограничения на строки в наборе результатов. Если необходимо указать, какие строки должны присутствовать в результате, следует сделать это в выражении WHERE.
5.2.6 Как MySQL оптимизирует LEFT JOIN и RIGHT JOIN
Все условия LEFT JOIN перемещаются в предложение WHERE.
AND b.Flag = true - это же получается условие ограничения на строки ? но ведь если его вынести в where получается совсем не то что нужно =\
Неактивен
SELECT a.Name FROM a LEFT JOIN b ON (a.id = b.id_a AND b.Flag = true) WHERE b.id_a IS NULL
AND b.Flag = true это я и к тому что как вместо true подставить теперь поле из другой таблицы. Чтобы было чтото вроде :
SELECT a.Name FROM c, a LEFT JOIN b ON (a.id = b.id_a AND b.Flag = c.flag) WHERE b.id_a IS NULL
Неактивен
Если я правильно Вас понял, то Вам нужно объединить несколько табличек таким образом.
Можете писать просто так, как думается:
SELECT ... FROM a LEFT JOIN b ON (связь a <-> b) LEFT JOIN c ON (связь того, что слева, с c) ... WHERE (условия ограничения)
Т.е. в Вашем случае как-то так:
SELECT a.Name FROM a LEFT JOIN b ON (a.id = b.id_a) LEFT JOIN c ON (b.Flag = c.flag) WHERE b.id_a IS NULL
Неактивен
То что вы написали понял, спасибо. )
А нельзя вынести b.Flag = true как то из JOIN в WHERE ? У меня просто таблиц больше чем три, их все надо связать чтобы получить флаг для b.Flag. В where это было бы проще. У меня такая связь между таблицами:
games.game_id=1 AND players.game_id=games.game_id AND players.player_id=5 AND players.id_gp=player_tasks.id_gp
b.Flag=player_tasks.id_gp чтобы так получить нужно все таблицы объеденить через JOIN ?
P.S. я бы с удовольствием вас не мучал, но кроме мануала по JOIN ничего доступного найти не смог.
Отредактированно Student20 (09.07.2008 18:51:45)
Неактивен
Запись
SELECT ... FROM a, b WHERE (a<->b)
эквивалентна полностью записи
SELECT ... FROM a JOIN b on (a<->b) (но join внутренний, а не левый).
Так что в принципе никто не мешает Вам выбирать данные по такой схеме:
SELECT ... FROM a, b, c, d, e LEFT JOIN f ON (e<->f) WHERE cond(a) AND cond(b) ... AND f.field IS NULL
Единственное сложное тут - сообразить, с чем реально связана таблица f по
внешней связи так, чтобы правильно отработала выборка значений NULL.
P.S. Внутренняя связь отличается от внешней тем, что никогда не выбирает строки, если хотя бы
в одной таблице отношений нет значений, т.е. не выбирает NULLы.
Неактивен
А что такое cond (a)?
SELECT a.Name FROM a LEFT JOIN b ON (a.id = b.id_a),c WHERE b.id_a IS NULL AND b.flag=c.flag;
и
SELECT a.Name FROM a LEFT JOIN b ON (a.id = b.id_a) LEFT JOIN c ON (b.Flag = c.flag) WHERE b.id_a IS NULL;
Это эквивалентные запросы ? )
Неактивен
Это просто некоторое условие на a
Запросы не эквивалентные, т.к. второй допускает строчки с c.flag = NULL.
Если c.flag не бывает NULL, то эквивалентные.
Неактивен
Спасибо paulus, Magz. разобрался и благодарен за помощь. )
Неактивен