MySQL ON против использования?

252

В MySQL JOIN, в чем разница между ONи USING()? Насколько я могу сказать, USING()это просто более удобный синтаксис, тогда как ONпозволяет немного больше гибкости, когда имена столбцов не идентичны. Тем не менее, эта разница настолько незначительна, что вы думаете, что с ними просто покончено USING().

Есть ли что-то большее, чем кажется на первый взгляд? Если да, что я должен использовать в данной ситуации?

Нафанаил
источник
1
Также есть ЕСТЕСТВЕННОЕ СОЕДИНЕНИЕ: stackoverflow.com/questions/8696383/…
allyourcode
Обратите внимание, что usingесть и другое использование, кроме соединений. См stackoverflow.com/a/13750399/632951
Pacerier

Ответы:

400

Это в основном синтаксический сахар, но заслуживает внимания пара отличий:

ON является более общим из двух. Можно объединить таблицы по столбцу, множеству столбцов и даже условию. Например:

SELECT * FROM world.City JOIN world.Country ON (City.CountryCode = Country.Code) WHERE ...

ИСПОЛЬЗОВАНИЕ полезно, когда обе таблицы совместно используют столбец с одинаковым именем, к которому они присоединяются. В этом случае можно сказать:

SELECT ... FROM film JOIN film_actor USING (film_id) WHERE ...

Дополнительным приятным дополнением является то, что не нужно полностью определять объединяемые столбцы:

SELECT film.title, film_id -- film_id is not prefixed
FROM film
JOIN film_actor USING (film_id)
WHERE ...

Чтобы проиллюстрировать вышеописанное с помощью ON , нам нужно написать:

SELECT film.title, film.film_id -- film.film_id is required here
FROM film
JOIN film_actor ON (film.film_id = film_actor.film_id)
WHERE ...

Обратите внимание на film.film_idквалификацию в SELECTпункте. Было бы неверно просто сказать, film_idтак как это сделало бы для двусмысленности:

ОШИБКА 1052 (23000): столбец 'film_id' в списке полей неоднозначен

Что касается select *, столбец присоединения появляется в наборе результатов дважды с, в ONто время как он появляется только один раз с USING:

mysql> create table t(i int);insert t select 1;create table t2 select*from t;
Query OK, 0 rows affected (0.11 sec)

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

Query OK, 1 row affected (0.19 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select*from t join t2 on t.i=t2.i;
+------+------+
| i    | i    |
+------+------+
|    1 |    1 |
+------+------+
1 row in set (0.00 sec)

mysql> select*from t join t2 using(i);
+------+
| i    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql>
Шломи Ноах
источник
2
+1 Хороший ответ на синтаксическую разницу. Меня интересует разница в производительности, если таковая имеется. Я представляю, как USINGинтерпретирует ON.
Джейсон МакКрири
9
На самом деле, оба интерпретируют как обычный старый тэта-стиль. Вы можете увидеть это, вызвав EXPLAIN EXTENDED для вашего запроса, а затем SHOW WARNINGS.
Шломи Ноах
2
Вы также можете сделать USING(категорию ,field_id, )которая полезна при объединении с помощью составных первичных ключей, также я слышал, что оптимизатор USINGв некоторых случаях использует для повышения производительности
Тимо Хуовинен,
Это USINGопределение MySQL или это стандарт?
PhoneixS
5
@PhoneixS это в стандарте ANSI SQL 92
Шломи Ноах
18

Думал, что я бы скинулся, когда я нашел, ONчтобы быть более полезным, чем USING. Это когда OUTERобъединения вводятся в запросы.

ONВыгодно от OUTERограничения набора результатов таблицы, к которой присоединяется запрос, при поддержании OUTERобъединения. Попытка ограничить набор результатов путем указания WHEREпредложения, по сути, изменит OUTERобъединение на INNERобъединение.

Конечно, это может быть относительный случай. Стоит поставить там хотя .....

Например:

CREATE TABLE country (
   countryId int(10) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   country varchar(50) not null,
  UNIQUE KEY countryUIdx1 (country)
) ENGINE=InnoDB;

insert into country(country) values ("France");
insert into country(country) values ("China");
insert into country(country) values ("USA");
insert into country(country) values ("Italy");
insert into country(country) values ("UK");
insert into country(country) values ("Monaco");


CREATE TABLE city (
  cityId int(10) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
  countryId int(10) unsigned not null,
  city varchar(50) not null,
  hasAirport boolean not null default true,
  UNIQUE KEY cityUIdx1 (countryId,city),
  CONSTRAINT city_country_fk1 FOREIGN KEY (countryId) REFERENCES country (countryId)
) ENGINE=InnoDB;


insert into city (countryId,city,hasAirport) values (1,"Paris",true);
insert into city (countryId,city,hasAirport) values (2,"Bejing",true);
insert into city (countryId,city,hasAirport) values (3,"New York",true);
insert into city (countryId,city,hasAirport) values (4,"Napoli",true);
insert into city (countryId,city,hasAirport) values (5,"Manchester",true);
insert into city (countryId,city,hasAirport) values (5,"Birmingham",false);
insert into city (countryId,city,hasAirport) values (3,"Cincinatti",false);
insert into city (countryId,city,hasAirport) values (6,"Monaco",false);

-- Gah. Left outer join is now effectively an inner join 
-- because of the where predicate
select *
from country left join city using (countryId)
where hasAirport
; 

-- Hooray! I can see Monaco again thanks to 
-- moving my predicate into the ON
select *
from country co left join city ci on (co.countryId=ci.countryId and ci.hasAirport)
; 
Том Мак
источник
4
Очень хороший момент. Из всех преимуществ using, это не может быть объединено с другими предикатами: select*from t join t2 using(i) and on 1не будет работать.
Pacerier
where hasAirport ;- что это значит ? Нет смысла сравнивать.
Истак Ахмед
Также обратите внимание, что вы можете сделать гораздо больше сравнений с ON, чем просто =. Лайк SELECT * FROM country LEFT JOIN city ON country.countryId=city.countryId AND city.city BETWEEN 'C' AND 'E' перечислит все страны, но только города, начинающиеся с C или D (если есть). (Плюс города под названием «E»)
Ремер
Я когда-то даже сделал JOIN с подзапросом в ON !!! Это все возможно, а иногда и очень эффективно.
Ремер
11

Википедия имеет следующую информацию о USING:

Однако конструкция USING - это больше, чем просто синтаксический сахар, поскольку набор результатов отличается от набора результатов версии с явным предикатом. В частности, любые столбцы, упомянутые в списке USING, будут отображаться только один раз с неквалифицированным именем, а не один раз для каждой таблицы в объединении. В приведенном выше случае будет один столбец DepartmentID, в котором нет employee.DepartmentID или департамента.DepartmentID.

Таблицы, о которых говорилось:

введите описание изображения здесь

Документация Postgres также определяет их довольно хорошо:

Предложение ON является наиболее общим видом условия соединения: оно принимает выражение логического значения того же типа, что и в предложении WHERE. Пара строк из T1 и T2 совпадает, если выражение ON оценивается как true.

Предложение USING является сокращением, которое позволяет вам воспользоваться конкретной ситуацией, когда обе стороны объединения используют одно и то же имя для соединяющихся столбцов. Он принимает разделенный запятыми список имен общих столбцов и формирует условие соединения, которое включает сравнение на равенство для каждого из них. Например, соединение T1 и T2 с помощью USING (a, b) создает условие соединения ON T1.a = T2.a AND T1.b = T2.b.

Кроме того, выходные данные JOIN USING подавляют избыточные столбцы: нет необходимости печатать оба сопоставленных столбца, поскольку они должны иметь одинаковые значения. В то время как JOIN ON создает все столбцы из T1, за которыми следуют все столбцы из T2, JOIN USING создает один выходной столбец для каждой из перечисленных пар столбцов (в указанном порядке), за которыми следуют все оставшиеся столбцы из T1, а затем все оставшиеся столбцы из T2 ,

Роберт Роша
источник
1

Для тех, кто экспериментирует с этим в phpMyAdmin, просто слово:

У phpMyAdmin, похоже, есть несколько проблем с USING. Для справки: phpMyAdmin работает на Linux Mint, версия: "4.5.4.1deb2ubuntu2", сервер базы данных: "10.2.14-MariaDB-10.2.14 + maria ~ xenial - mariadb.org бинарный дистрибутив".

Я запускал SELECTкоманды с использованием JOINи USINGв phpMyAdmin, и в терминале (командная строка), а те, что в phpMyAdmin, вызывают некоторые удивительные ответы:

1) LIMITпункт в конце, кажется, игнорируется.
2) предполагаемое количество строк, указанное в верхней части страницы с результатами, иногда неверно: например, возвращаются 4, но в верхней части написано «Показаны строки 0–24 (всего 2503, запрос занял 0,0018 секунд). "

Обычный вход в MySQL и выполнение тех же запросов не приводит к этим ошибкам. Эти ошибки также не возникают при выполнении одного и того же запроса в phpMyAdmin с помощью JOIN ... ON .... Предположительно ошибка phpMyAdmin.

Майк Грызун
источник