SQL: получить список несвязанных элементов, используя MySQL 4 - PullRequest
2 голосов
/ 25 января 2010

Я не очень хорош в SQL и столкнулся с проблемой, которую не знаю, как решить.Я прочитал и перечитал части книги об SQL (Учебный SQL О'Рейли), которая, как я надеялся, содержала необходимую мне информацию, но я ее не нашел.

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

У меня есть три стола, машина, обод и их комбинация: carRim.

car
    carId
    description

rim
    rimId
    description

carRim
    carRimId
    carId
    rimId
    price

Вtable carRim У меня есть дополнительный атрибут цены, потому что цена обода потенциально различна для каждого типа автомобиля.У меня есть ограничение: каждый тип обода должен быть соединен только один раз с каждым типом автомобиля.Поэтому все комбинации автомобильных дисков должны быть уникальными.

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

Я написал(простой) запрос, чтобы составить список дисков, которые связаны с конкретным автомобилем, в следующем примере автомобиль с carId 9.

SELECT
    * 
FROM
    rims 
INNER JOIN
    carRims 
ON
    carRims.rimId = rim.rimId 
WHERE
    carRims.carId = 9 

Но теперь мне нужен списокдисков, которые еще не связаны с конкретным автомобилем.Проблема в том, что, если я сделаю ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ, список, который я получу, «испорчен» соединениями дисков с другими автомобилями, поэтому условие фильтра «WHERE carRims.carId IS NULL» не работает.

SELECT
    * 
FROM
    rims 
LEFT OUTER JOIN
    carRims 
ON
    carRims.rimId = rim.rimId 
WHERE
    carRims.carId IS NULL

Другая проблема заключается в том, что я не могу использовать любой синтаксис, новый для MySQL 5, например, подзапросы, потому что мой клиент использует MySQL 4 и в настоящее время не может выполнить обновление.

Можно запросить эту проблемубыть написано в MySQL 4, я подозреваю, что это может.

Спасибо!

Ответы [ 3 ]

2 голосов
/ 25 января 2010
SELECT  *
FROM    rims r
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    carRims cr
        WHERE   cr.rimId = r.RimId
                AND cr.carID = 9
        )

Обновление:

Чтобы переписать NOT EXISTS в LEFT JOIN / IS NULL, вам нужно поместить все условия в предложение ON объединения:

SELECT  r.* 
FROM    rims r
LEFT OUTER JOIN
        carRims cr 
ON      cr.rimId = r.rimId 
        AND cr.crID = 9
WHERE   cr.carId IS NULL
1 голос
/ 25 января 2010

Вы можете поместить дополнительные условия в выражение левого внешнего соединения ON. То, что вы хотите, это строка из carRims, которая соответствует вашей строке rims, и принадлежит автомобилю номер 9, верно?

SELECT
    * 
FROM
    rims 
LEFT OUTER JOIN
    carRims 
ON
    carRims.rimId = rim.rimId 
    AND carRims.carId = 9
WHERE
    carRims.carId IS NULL

Если есть другие carRims для других автомобилей, они будут отфильтрованы дополнительным условием в предложении ON.


Ваши вопросы о том, куда поставить условие, в JOIN или в предложении WHERE:

Для соединений external важно, куда вы поместите сравнение. Условие в предложении JOIN предназначено для проверки, соответствуют ли строки в одной таблице строкам в другой таблице. То, что мы делаем со строками из соответствующих таблиц после проверки соответствия, зависит от типа объединения.

Для внешнего объединения нам нужны строки из rims, даже если в carRims нет подходящей строки. Что если мы добавим условие carID = 9 в предложение WHERE?

FROM rims r LEFT OUTER JOIN carRims c ON r.rimId = c.rimID
WHERE c.carID = 9

Вот что происходит: внешнее объединение возвращает все строки из rims, причем строки из carRims соответствуют rimID, а включает строки с ложными значениями carID. Только если нет автомобилей, соответствующих данному ободу, он использует NULL для c.* столбцов.

Но тогда предложение WHERE удаляет все строки, полученные в результате объединения, если только carID не является значением 9. Это означает, что оно также устраняет, где carID равно NULL, то есть оно удаляет строки для любого обода, не соответствующего ни одному из автомобилей. Следовательно, результат становится эквивалентным результату INNER JOIN.

Таким образом, мы должны исключить строки из carRims с неверным идентификатором carID, прежде чем строки будут объединены в rims.

FROM rims r LEFT OUTER JOIN carRims c ON r.rimId = c.rimID AND c.carID = 9

Это условие того, какие строки из этой отдельной таблицы могут совпадать со строками в другой объединенной таблице.

Во многих книгах говорится, что вы можете свободно смешивать условия между предложением ON и предложением WHERE. Но это не так во всех случаях. Это работает для INNER JOIN, потому что конечный результат в любом случае одинаков. Это также работает для условия, которое применяется только к левой таблице в левом соединении, например:

FROM rims r LEFT OUTER JOIN carRims c ON r.rimId = c.rimID AND r.make = 'ABC Rims'

FROM rims r LEFT OUTER JOIN carRims c ON r.rimId = c.rimID 
WHERE r.make = 'ABC Rims'

Для условия на правой таблице в левом внешнем соединении, имеет значение , где вы ставите условие.


Еще один комментарий к тому, что вы сказали:

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

Итак, вы объявили UNIQUE ограничение на carRims(carId,rimId)?

0 голосов
/ 25 января 2010

(мне нужно было больше места для разработки, в комментарии не хватило символов)

@ Билл Карвин Заданный вами запрос является правильным ответом на мою проблему. Спасибо!

Мне нужен был список строк (ободков), которые не связаны с конкретным автомобилем.

В книге «Изучение SQL» я прочитал, что расположение фильтра и условия соединения в запросе не имеют значения. Они привели пример внутреннего объединения, где было 1 условие соединения (после предложения ON) и 1 условие фильтра (после предложения WHERE). Они продемонстрировали, что независимо от того, поместите ли вы эти два условия после предложения ON или после предложения WHERE, не имеет значения.

Имея это в виду, я не понимаю часть вашего решения. Если условие

carRims.carId = 9

и состояние

carRims.carId IS NULL

оба присутствуют, разве это не приводит к пустому набору результатов? Потому что ни одно поле не может быть 9 и NULL одновременно, верно?

Тогда я подозреваю: условия в предложении JOIN привязаны к местоположению, и вы не можете просто поместить их в предложение WHERE.

Это правильно?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...