MySQL ON проти використання?


252

У MySQL JOIN, в чому різниця між ONі USING()? Наскільки я можу сказати, USING()це просто зручніший синтаксис, тоді як ONдозволяє трохи більше гнучкості, коли назви стовпців не однакові. Однак ця різниця настільки незначна, ви можете подумати, що вони просто усунуть USING().

Чи є в цьому більше, ніж зустрічає око? Якщо так, то що я повинен використовувати в даній ситуації?


1
Там також NATURAL JOIN: 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.
Jason McCreary

9
Власне, обидва трактують звичайний старий тета-стиль. Ви можете переконатися, що, звернувшись до ПОЯСНЕННЯ, розширено у вашому запиті, а потім - ПОКАЗУВАТИ ПОПЕРЕДЖЕННЯ.
Шломі Ноач

2
Ви також можете зробити USING(категорію ,field_id, )яка корисна при приєднанні до складених первинних ключів, також я чув, що оптимізатор USINGв деяких випадках використовує для підвищення продуктивності
Timo Huovinen

Є чи USINGвизначення MySQL або це стандарт?
PhoneixS

5
@PhoneixS це в стандарті ANSI SQL 92
Shlomi Noach

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")
Roemer

Я колись навіть зробив ПРИЄДНАЙТЕ з підзапитом у ВКЛ !!! Це все можливо, а іноді і високоефективно.
Roemer

11

У Вікіпедії є така інформація про USING:

Однак конструкція USING - це не просто синтаксичний цукор, оскільки набір результатів відрізняється від набору результатів версії явним предикатом. Зокрема, будь-які стовпці, згадані у списку USING, з’являться лише один раз з некваліфікованим іменем, а не один раз для кожної таблиці в об’єднанні. У наведеному вище випадку буде один стовпчик DepartmentID і немає співробітника.DepartmentID або Department.DepartmentID.

Таблиці, про які мова йшла:

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

Документація Postgres також їх досить добре визначає:

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

Стаття USING - це скорочення, яке дозволяє скористатися конкретною ситуацією, коли обидві сторони з'єднання використовують однакове ім'я для стовпців (ів) приєднання. Він бере список, розділений комами, іменами спільних стовпців і формує умову приєднання, що включає порівняння рівності для кожного. Наприклад, з'єднання T1 і T2 з USING (a, b) створює умову приєднання ON T1.a = T2.a І 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, і в Terminal (командний рядок), і ті, які знаходяться в phpMyAdmin, створюють деякі невдалі відповіді:

1) LIMITстаття в кінці, здається, ігнорується.
2) передбачувана кількість рядків, як повідомляється вгорі сторінки з результатами, іноді помиляється: наприклад, 4 повертаються, але вгорі написано "Показано рядки 0 - 24 (всього 2503, запит займав 0,0018 секунд.) "

Нормальний вхід у mysql та виконання одних і тих же запитів не призводить до цих помилок. Ці помилки також не трапляються під час запуску одного запиту в phpMyAdmin за допомогою JOIN ... ON .... Імовірно, помилка phpMyAdmin.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.