MySQL ON vs USANDO?

252

Em um MySQL JOIN, qual é a diferença entre ONe USING()? Tanto quanto posso dizer, USING()é apenas uma sintaxe mais conveniente, enquanto ONpermite um pouco mais de flexibilidade quando os nomes das colunas não são idênticos. No entanto, essa diferença é tão pequena que você pensaria que eles acabariam com isso USING().

Existe mais do que aparenta? Se sim, qual devo usar em uma determinada situação?

Natanael
fonte
1
Também há NATURAL JOIN: stackoverflow.com/questions/8696383/…
allyourcode
Observe que usingtem outro uso além de junções. Veja stackoverflow.com/a/13750399/632951
Pacerier

Respostas:

400

É principalmente açúcar sintático, mas algumas diferenças são dignas de nota:

ON é o mais geral dos dois. Pode-se juntar tabelas em uma coluna, um conjunto de colunas e até uma condição. Por exemplo:

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

USING é útil quando ambas as tabelas compartilham uma coluna com o mesmo nome exato em que se juntam. Nesse caso, pode-se dizer:

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

Uma boa sugestão adicional é que não é necessário qualificar totalmente as colunas de junção:

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

Para ilustrar, para fazer o acima com ON , teríamos que escrever:

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 ...

Observe a film.film_idqualificação na SELECTcláusula. Seria inválido dizer apenas film_iduma vez que isso criaria uma ambiguidade:

ERRO 1052 (23000): a coluna 'film_id' na lista de campos é ambígua

Quanto a select *, a coluna de junção aparece no conjunto de resultados duas vezes com ONenquanto aparece apenas uma vez com 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>
Shlomi Noach
fonte
2
+1 Boa resposta sobre a diferença sintática. Estou curioso sobre as diferenças de desempenho, se houver. Eu imagino que USINGinterpreta para ON.
Jason McCreary
9
Na verdade, ambos interpretam para o velho estilo Theta. Você pode ver isso invocando EXPLAIN EXTENDED na sua consulta, seguido de SHOW WARNINGS.
Shlomi Noach
2
você também pode fazer USING(categoria ,FIELD_ID )que é útil quando se juntar por chaves primárias compostas, também eu ouvi que o otimizador faz uso USINGpara melhorar o desempenho em alguns casos
Timo Huovinen
É USINGuma definição do MySQL ou é padrão?
PhoneixS
5
@PhoneixS está no padrão ANSI SQL 92 #
Shlomi Noach
18

Achei que iria me interessar aqui quando achar ONmais útil do que USING. É quando as OUTERjunções são introduzidas nas consultas.

ONse beneficia de permitir que o conjunto de resultados da tabela ao qual uma consulta está OUTERingressando seja restrito, mantendo a OUTERassociação. Tentar restringir os resultados definidos através da especificação de uma WHEREcláusula, efetivamente, transformará a OUTERassociação em uma INNERassociação.

Concedido que este pode ser um caso de canto relativo. Vale a pena colocar lá fora ...

Por exemplo:

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)
; 
Tom Mac
fonte
4
Ponto extremamente bom. De todas as vantagens usingoferecidas, ele não pode ser combinado com outros predicados: select*from t join t2 using(i) and on 1não funcionaria.
Pacerier
where hasAirport ;- O que isto significa ? nenhum valor existe para comparar.
Istiaque Ahmed
Observe também que você pode fazer muito mais comparações com ON do que apenas =. O Like SELECT * FROM country LEFT JOIN city ON country.countryId=city.countryId AND city.city BETWEEN 'C' AND 'E' listará todos os países, mas apenas as cidades que começam com C ou D (se houver). (Mais cidades chamadas 'E')
Roemer
Uma vez eu até fiz um JOIN com uma subconsulta no ON !!! Tudo é possível e às vezes altamente eficaz.
Roemer
11

A Wikipedia possui as seguintes informações sobre USING:

A construção USING é mais do que mero açúcar sintático, no entanto, uma vez que o conjunto de resultados difere do conjunto de resultados da versão com o predicado explícito. Especificamente, quaisquer colunas mencionadas na lista USING aparecerão apenas uma vez, com um nome não qualificado, em vez de uma vez para cada tabela na associação. No caso acima, haverá uma única coluna DepartmentID e nenhum funcionário.DepartmentID ou department.DepartmentID.

Tabelas sobre as quais estava falando:

insira a descrição da imagem aqui

A documentação do Postgres também os define muito bem:

A cláusula ON é o tipo mais geral de condição de junção: recebe uma expressão de valor booleano do mesmo tipo que é usada em uma cláusula WHERE. Um par de linhas de T1 e T2 corresponde se a expressão ON for avaliada como verdadeira.

A cláusula USING é uma abreviação que permite tirar proveito da situação específica em que ambos os lados da junção usam o mesmo nome para a (s) coluna (s) de junção. Ele pega uma lista separada por vírgula dos nomes das colunas compartilhadas e forma uma condição de junção que inclui uma comparação de igualdade para cada uma. Por exemplo, unir T1 e T2 com USING (a, b) produz a condição de junção ON T1.a = T2.a AND T1.b = T2.b.

Além disso, a saída de JOIN USING suprime colunas redundantes: não há necessidade de imprimir as duas colunas correspondentes, pois elas devem ter valores iguais. Enquanto JOIN ON produz todas as colunas de T1 seguidas por todas as colunas de T2, JOIN USING produz uma coluna de saída para cada um dos pares de colunas listados (na ordem listada), seguida por quaisquer colunas restantes de T1, seguidas por colunas restantes de T2 .

Robert Rocha
fonte
1

Para aqueles que experimentam isso no phpMyAdmin, apenas uma palavra:

O phpMyAdmin parece ter alguns problemas USING. Para registro, este é o phpMyAdmin, executado no Linux Mint, versão: "4.5.4.1deb2ubuntu2", servidor de banco de dados: "10.2.14-MariaDB-10.2.14 + maria ~ xenial - distribuição binária mariadb.org".

Eu executei SELECTcomandos usando JOINe USINGno phpMyAdmin e no Terminal (linha de comando), e os do phpMyAdmin produzem algumas respostas desconcertantes:

1) uma LIMITcláusula no final parece ser ignorada.
2) o número suposto de linhas, conforme relatado na parte superior da página com os resultados, às vezes está errado: por exemplo, 4 são retornados, mas na parte superior diz "Mostrando linhas 0 - 24 (total de 2503, a consulta demorou 0,0018 segundos). "

Efetuar logon no mysql normalmente e executar as mesmas consultas não produz esses erros. Esses erros também não ocorrem ao executar a mesma consulta no phpMyAdmin usando JOIN ... ON .... Presumivelmente um bug do phpMyAdmin.

microfone roedor
fonte