Сегодня мы сосредоточимся на выполнении внутренних и внешних соединений в SQL. Во-первых, join
— это способ объединения данных из двух таблиц, которые связаны друг с другом. Чтобы настроить наши примеры, я буду использовать таблицу users
и таблицу posts
. Вот определения SQL.
create table users ( id SERIAL PRIMARY KEY, name VARCHAR(30)); create table posts ( id SERIAL PRIMARY KEY, title VARCHAR(30), body VARCHAR(100), userid int references users(id));
Каждый пользователь получает свой уникальный идентификатор и имя. Каждое сообщение получает уникальный идентификатор, заголовок, текст и идентификатор, который ссылается на таблицу пользователей. Связь заключается в том, что у одного пользователя может быть несколько сообщений, но у одного сообщения может быть не более одного пользователя в качестве его автора.
После вставки четырех пользователей имеем:
id | name ----+------- 1 | eric 2 | bob 3 | david 4 | chris (4 rows)
И мы можем написать несколько постов.
id | title | body | userid ----+--------+--------+-------- 1 | post 1 | body 1 | 1 2 | post 2 | body 2 | 2 3 | post 3 | body 3 | 2
В таблице сообщений вы можете увидеть, что Боб является автором двух сообщений, Эрик является автором одного сообщения, а Дэвид и Крис создали 0 сообщений.
Давайте воспользуемся отношением «один ко многим», которое мы создали между пользователями и сообщениями, для выполнения некоторых запросов.
Во-первых, давайте объединим данные из двух таблиц вместе. Мы хотели бы видеть все заголовки и тела сообщений вместе с их авторами.
select p.title, p.body, u.name from posts as p, users as u where p.userid = u.id; title | body | name --------+--------+------ post 1 | body 1 | eric post 2 | body 2 | bob post 3 | body 3 | bob
Это внутреннее соединение. Мы ограничиваем данные из обеих таблиц, чтобы показывать нам только отдельные сообщения, в которых совпадает поле идентификатора.
Так как же выглядит внешнее соединение? Внешнее соединение позволит нам включить в результат больше данных.
select p.title, p.body, u.name from users u left join posts p on p.userid = u.id; title | body | name --------+--------+------- post 1 | body 1 | eric post 2 | body 2 | bob post 3 | body 3 | bob [null] | [null] | chris [null] | [null] | david
Который показывает те же три поста, авторами которых являются Эрик и Боб, но также показывает нам, что есть некоторые пользователи (Крис и Дэвид), которые не написали ни одного поста. Это отражено [нулевыми] записями под заголовком и телом.
Мне кажется, здесь важнее всего порядок. Левое соединение вернет все записи из левой таблицы (пользователи) и соответствующие записи из правой таблицы (сообщения). В этом случае, поскольку таблица слева — это пользователи, мы начинаем со *всех* пользователей, а затем ищем совпадения в правой таблице.
Если мы изменим порядок, мы получим другой результат, который должен иметь смысл.
select p.title, p.body, u.name from posts p left join users u on p.userid = u.id; title | body | name --------+--------+------ post 1 | body 1 | eric post 2 | body 2 | bob post 3 | body 3 | bob
Здесь мы начинаем с левой таблицы (сообщений), а затем находим соответствующие записи в таблице пользователей. Поскольку есть только три сообщения с авторами bob и eric, мы никогда не видим дополнительных данных из таблицы пользователей, потому что в сообщениях с этими идентификаторами пользователей нет соответствующей записи.
Наконец, разработчики довольно часто (я думаю) просто всегда используют левое внешнее соединение, даже если левое и правое внешние соединения делают одно и то же, но в другом порядке.
Я нашел этот рисунок в Интернете, который помогает визуализировать то, что происходит с внутренними и внешними соединениями.
На словах вы можете думать об этом так:
Внутреннее соединение вернет записи, имеющие совпадающие значения в обеих таблицах.
Левое внешнее соединение вернет все записи из левой таблицы и соответствующие записи из правой таблицы.
Правое внешнее соединение вернет все записи из правой таблицы и соответствующие записи из левой таблицы.
Полное внешнее соединение вернет все записи, если есть совпадение из любой таблицы.