여러 테이블에서 SQL 쿼리 반환 데이터


434

다음을 알고 싶습니다.

  • 데이터베이스의 여러 테이블에서 데이터를 얻는 방법은 무엇입니까?
  • 이를 위해 어떤 유형의 방법이 있습니까?
  • 조인과 노조 란 무엇이며 서로 어떻게 다른가요?
  • 다른 제품과 비교하여 각 제품을 언제 사용해야합니까?

내 (예를 들어-PHP) 응용 프로그램에서 이것을 사용할 계획이지만 데이터베이스에 대해 여러 쿼리를 실행하고 싶지 않습니다. 단일 쿼리에서 여러 테이블의 데이터를 가져 오기 위해 어떤 옵션이 필요합니까?

참고 : PHP 큐에서 지속적으로 발생하는 수많은 질문에 대한 잘 작성된 안내서에 링크하고 싶기 때문에이 글을 작성하고 있으므로 답변을 게시 할 때 더 자세히 링크 할 수 있습니다.

답변은 다음과 같습니다.

  1. 1 부-조인 및 조합
  2. 2 부-서브 쿼리
  3. 3 부-트릭과 효율적인 코드
  4. 4 부-From 절의 서브 쿼리
  5. 5 부-요한 트릭의 혼합 가방

답변:


469

1 부-조인 및 조합

이 답변은 다음과 같습니다.

  1. 1 부
    • 내부 조인을 사용하여 둘 이상의 테이블 조인 ( 추가 정보 는 Wikipedia 항목 참조 )
    • 통합 쿼리를 사용하는 방법
    • 왼쪽 및 오른쪽 외부 조인 (이 stackOverflow 답변 은 조인 유형을 설명하기에 우수합니다)
    • 쿼리를 교차 (및 데이터베이스가 지원하지 않는 경우 쿼리를 재생산하는 방법)-이것은 SQL-Server의 기능 ( info 참조 )이며 처음 에이 모든 것을 작성한 이유의 일부입니다 .
  2. 2 부
    • 하위 쿼리-현재 상태, 사용 가능한 위치 및주의 할 사항
    • 데카르트가 AKA에 합류-아, 불행!

데이터베이스의 여러 테이블에서 데이터를 검색하는 방법에는 여러 가지가 있습니다. 이 답변에서는 ANSI-92 조인 구문을 사용합니다. 이것은 이전 ANSI-89 구문을 사용하는 다른 많은 자습서와 다를 수 있습니다 (89에 익숙하다면 훨씬 덜 직관적 인 것처럼 보일 수 있지만 시도해 보는 것만 큼) 훨씬 쉽습니다. 쿼리가 더 복잡 해지는 시점을 이해합니다. 왜 사용합니까? 성능 향상이 있습니까? 짧은 대답은 '아니요, 그러나 입니다 일단 익숙해지면 쉽게 읽을. 이 구문을 사용하여 다른 사람들이 작성한 쿼리를보다 쉽게 ​​읽을 수 있습니다.

또한 사용할 수있는 자동차를 추적 할 수있는 데이터베이스가있는 소형 마당의 개념을 사용하려고합니다. 소유자는 IT 컴퓨터 담당자로 고용했으며 모자를 떨어 뜨릴 때 요청한 데이터를 삭제할 수 있기를 기대합니다.

최종 테이블에서 사용할 다수의 조회 테이블을 만들었습니다. 이것은 우리에게 합리적인 모델을 제공 할 것입니다. 먼저 다음 구조를 가진 예제 데이터베이스에 대해 쿼리를 실행합니다. 나는 시작할 때 발생하는 일반적인 실수를 생각하고 무엇이 잘못되었는지 설명하고 실수를 바로 잡는 방법을 보여줄 것이다.

첫 번째 테이블은 단순히 색상 목록이므로 자동차 야드에 어떤 색상이 있는지 알 수 있습니다.

mysql> create table colors(id int(3) not null auto_increment primary key, 
    -> color varchar(15), paint varchar(10));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from colors;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| color | varchar(15) | YES  |     | NULL    |                |
| paint | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

mysql> insert into colors (color, paint) values ('Red', 'Metallic'), 
    -> ('Green', 'Gloss'), ('Blue', 'Metallic'), 
    -> ('White' 'Gloss'), ('Black' 'Gloss');
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from colors;
+----+-------+----------+
| id | color | paint    |
+----+-------+----------+
|  1 | Red   | Metallic |
|  2 | Green | Gloss    |
|  3 | Blue  | Metallic |
|  4 | White | Gloss    |
|  5 | Black | Gloss    |
+----+-------+----------+
5 rows in set (0.00 sec)

브랜드 표는 야드에서 판매 할 수있는 자동차의 여러 브랜드를 식별합니다.

mysql> create table brands (id int(3) not null auto_increment primary key, 
    -> brand varchar(15));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from brands;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| brand | varchar(15) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

mysql> insert into brands (brand) values ('Ford'), ('Toyota'), 
    -> ('Nissan'), ('Smart'), ('BMW');
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from brands;
+----+--------+
| id | brand  |
+----+--------+
|  1 | Ford   |
|  2 | Toyota |
|  3 | Nissan |
|  4 | Smart  |
|  5 | BMW    |
+----+--------+
5 rows in set (0.00 sec)

모델 테이블은 다른 유형의 자동차를 다루며, 실제 자동차 모델이 아닌 다른 자동차 유형을 사용하는 것이 더 간단합니다.

mysql> create table models (id int(3) not null auto_increment primary key, 
    -> model varchar(15));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from models;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| model | varchar(15) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> insert into models (model) values ('Sports'), ('Sedan'), ('4WD'), ('Luxury');
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select * from models;
+----+--------+
| id | model  |
+----+--------+
|  1 | Sports |
|  2 | Sedan  |
|  3 | 4WD    |
|  4 | Luxury |
+----+--------+
4 rows in set (0.00 sec)

마지막으로 모든 다른 테이블을 묶으십시오. ID 필드는 실제로 자동차를 식별하는 데 사용되는 고유 로트 번호입니다.

mysql> create table cars (id int(3) not null auto_increment primary key, 
    -> color int(3), brand int(3), model int(3));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from cars;
+-------+--------+------+-----+---------+----------------+
| Field | Type   | Null | Key | Default | Extra          |
+-------+--------+------+-----+---------+----------------+
| id    | int(3) | NO   | PRI | NULL    | auto_increment |
| color | int(3) | YES  |     | NULL    |                |
| brand | int(3) | YES  |     | NULL    |                |
| model | int(3) | YES  |     | NULL    |                |
+-------+--------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> insert into cars (color, brand, model) values (1,2,1), (3,1,2), (5,3,1), 
    -> (4,4,2), (2,2,3), (3,5,4), (4,1,3), (2,2,1), (5,2,3), (4,5,1);
Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql> select * from cars;
+----+-------+-------+-------+
| id | color | brand | model |
+----+-------+-------+-------+
|  1 |     1 |     2 |     1 |
|  2 |     3 |     1 |     2 |
|  3 |     5 |     3 |     1 |
|  4 |     4 |     4 |     2 |
|  5 |     2 |     2 |     3 |
|  6 |     3 |     5 |     4 |
|  7 |     4 |     1 |     3 |
|  8 |     2 |     2 |     1 |
|  9 |     5 |     2 |     3 |
| 10 |     4 |     5 |     1 |
+----+-------+-------+-------+
10 rows in set (0.00 sec)

이것은 우리에게 다른 유형의 조인에 대한 아래의 예를 설명하기에 충분한 데이터를 제공하고 가치가 있도록 충분한 데이터를 제공합니다.

그래서, 그 사장님의 입장에 들어간 사장님은 자신이 가진 모든 스포츠카의 ID 를 알고 싶어합니다 .

이것은 간단한 두 테이블 조인입니다. 모델을 식별하는 테이블과 사용 가능한 재고가있는 테이블이 있습니다. 보시다시피 model, cars테이블 의 열에 있는 데이터는 우리가 가진 테이블 의 models열과 관련이 cars있습니다. 이제, 우리는 모델 테이블의 ID가 알고 1에 대해 Sports너무 조인을 작성할 수 있습니다.

select
    ID,
    model
from
    cars
        join models
            on model=ID

이 질문이 잘 생겼나요? 두 테이블을 식별했으며 필요한 정보를 포함하고 결합 할 열을 올바르게 식별하는 결합을 사용합니다.

ERROR 1052 (23000): Column 'ID' in field list is ambiguous

아뇨! 첫 번째 쿼리에서 오류가 발생했습니다! 네, 매실입니다. 알다시피, 쿼리에는 실제로 올바른 열이 있지만 그중 일부는 두 테이블에 모두 존재하므로 데이터베이스는 실제 열의 의미와 위치에 대해 혼란스러워합니다. 이 문제를 해결하기위한 두 가지 솔루션이 있습니다. 첫 번째는 훌륭하고 간단 tableName.columnName합니다. 다음과 같이 데이터베이스의 의미를 정확하게 알려주 는 데 사용할 수 있습니다 .

select
    cars.ID,
    models.model
from
    cars
        join models
            on cars.model=models.ID

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
|  2 | Sedan  |
|  4 | Sedan  |
|  5 | 4WD    |
|  7 | 4WD    |
|  9 | 4WD    |
|  6 | Luxury |
+----+--------+
10 rows in set (0.00 sec)

다른 하나가 더 자주 사용되며 테이블 별칭이라고합니다. 이 예제의 테이블에는 단순하고 간단한 이름이 있지만 다음과 같이 입력하면 KPI_DAILY_SALES_BY_DEPARTMENT빨리 오래 될 수 있으므로 간단한 방법으로 테이블의 별명을 지정합니다.

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID

이제 요청으로 돌아갑니다. 보시다시피 필요한 정보는 있지만 요청하지 않은 정보도 있으므로 요청에 따라 스포츠카 만 가져 오려면 명세서에 where 절을 포함시켜야합니다. 테이블 이름을 계속 사용하는 대신 테이블 별칭 방법을 선호하므로이 시점부터 계속 사용하겠습니다.

분명히 쿼리에 where 절을 추가해야합니다. ID=1또는로 스포츠카를 식별 할 수 있습니다 model='Sports'. ID가 색인화되고 기본 키 (및 입력 횟수가 줄어듦)를 쿼리에서 사용할 수 있습니다.

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID
where
    b.ID=1

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)

빙고! 사장님은 행복합니다. 물론, 보스가되고 그가 요청한 것에 만족하지 않으면 정보를 보고 색상도 원한다고 말합니다. .

자, 우리는 이미 작성한 쿼리의 좋은 부분을 가지고 있지만 색상 인 세 번째 테이블을 사용해야합니다. 이제 주요 정보 테이블 cars에 자동차 색상 ID가 저장되고 색상 ID 열로 다시 연결됩니다. 따라서 원본과 비슷한 방식으로 세 번째 테이블을 조인 할 수 있습니다.

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
where
    b.ID=1

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)

젠장, 테이블이 올바르게 조인되고 관련 열이 연결되었지만 방금 연결 한 새 테이블에서 실제 정보 를 가져 오는 것을 잊었습니다 .

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
where
    b.ID=1

+----+--------+-------+
| ID | model  | color |
+----+--------+-------+
|  1 | Sports | Red   |
|  8 | Sports | Green |
| 10 | Sports | White |
|  3 | Sports | Black |
+----+--------+-------+
4 rows in set (0.00 sec)

맞습니다. 잠시 동안 우리의 상사입니다. 이제이 중 일부에 대해 좀 더 자세히 설명하겠습니다. 보시다시피from 명령문 절은 기본 테이블과 연결됩니다 (저는 종종 조회 또는 차원 테이블이 아닌 정보를 포함하는 테이블을 사용합니다. 쿼리는 모든 테이블에서 잘 작동하지만, 이해가되지 않을 때 우리는이 쿼리로 돌아와서 몇 개월 만에 읽었으므로 이해하기 쉽고 좋은 쿼리를 작성하는 것이 가장 좋습니다. 직관적으로 배치하고 멋진 들여 쓰기를 사용하여 모든 것이 명확하게되도록하십시오. 계속해서 다른 사람들을 가르치려면 이러한 특성을 쿼리에 주입하십시오 (특히 문제를 해결하려는 경우).

이러한 방식으로 점점 더 많은 테이블을 연결하는 것이 전적으로 가능합니다.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1

join명령문 에 둘 이상의 열을 조인하려는 테이블을 포함하는 것을 잊었지만 여기에 예가 있습니다. 경우 models테이블 따라서 브랜드 별 모델을 가지고도라는 칼럼했다 brand에 다시 연결 brands상의 테이블 ID필드, 그것은이 같이 할 수있다 :

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
            and b.brand=d.ID
where
    b.ID=1

위의 쿼리는 조인 된 테이블을 기본 테이블에 연결할 cars뿐만 아니라 이미 조인 된 테이블 간의 조인도 지정합니다. 이 작업을 수행하지 않은 경우 결과는 데카르트 조인 (dcarsian join)이라고합니다. 데카르트 조인은 정보가 데이터베이스에 결과를 제한하는 방법을 알려주지 않기 때문에 행이 반환되는 것이므로 쿼리는 조건에 맞는 모든 행을 반환 합니다.

따라서 데카르트 조인의 예를 제공하려면 다음 쿼리를 실행하십시오.

select
    a.ID,
    b.model
from
    cars a
        join models b

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  1 | Sedan  |
|  1 | 4WD    |
|  1 | Luxury |
|  2 | Sports |
|  2 | Sedan  |
|  2 | 4WD    |
|  2 | Luxury |
|  3 | Sports |
|  3 | Sedan  |
|  3 | 4WD    |
|  3 | Luxury |
|  4 | Sports |
|  4 | Sedan  |
|  4 | 4WD    |
|  4 | Luxury |
|  5 | Sports |
|  5 | Sedan  |
|  5 | 4WD    |
|  5 | Luxury |
|  6 | Sports |
|  6 | Sedan  |
|  6 | 4WD    |
|  6 | Luxury |
|  7 | Sports |
|  7 | Sedan  |
|  7 | 4WD    |
|  7 | Luxury |
|  8 | Sports |
|  8 | Sedan  |
|  8 | 4WD    |
|  8 | Luxury |
|  9 | Sports |
|  9 | Sedan  |
|  9 | 4WD    |
|  9 | Luxury |
| 10 | Sports |
| 10 | Sedan  |
| 10 | 4WD    |
| 10 | Luxury |
+----+--------+
40 rows in set (0.00 sec)

좋은 신, 그건 못 생겼어 그러나 데이터베이스에 관한 한 정확히 요청 된 것입니다. 쿼리에서, 우리는에 대한 요구 ID에서 carsmodel에서 models. 그러나 테이블을 조인하는 방법 을 지정하지 않았으므로 데이터베이스는 첫 번째 테이블의 모든 행과 두 번째 테이블의 모든 행을 일치 시켰 습니다.

좋아, 그래서 보스는 돌아 왔고 더 많은 정보를 원한다. 나는 같은 목록을 원하지만 4WD도 포함합니다 .

그러나 이것은 우리에게 이것을 달성하기 위해 두 가지 다른 방법을 살펴볼 큰 변명을줍니다. 다음과 같이 where 절에 다른 조건을 추가 할 수 있습니다.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1
    or b.ID=3

위의 내용은 완벽하게 작동하지만 다르게 살펴 보겠습니다. 이것은 union쿼리의 작동 방식을 보여주는 훌륭한 변명 입니다.

다음은 모든 스포츠카를 반환한다는 것을 알고 있습니다.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1

그리고 다음은 모든 4WD를 반환합니다.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=3

따라서 union all이들 사이 에 절 을 추가 하면 두 번째 쿼리 결과가 첫 번째 쿼리 결과에 추가됩니다.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1
union all
select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=3

+----+--------+-------+
| ID | model  | color |
+----+--------+-------+
|  1 | Sports | Red   |
|  8 | Sports | Green |
| 10 | Sports | White |
|  3 | Sports | Black |
|  5 | 4WD    | Green |
|  7 | 4WD    | White |
|  9 | 4WD    | Black |
+----+--------+-------+
7 rows in set (0.00 sec)

보다시피, 첫 번째 쿼리 결과가 먼저 반환되고 두 번째 쿼리 결과가 반환됩니다.

이 예에서는 물론 첫 번째 쿼리를 사용하는 것이 훨씬 쉬웠지만 union특정 경우에는 쿼리가 훌륭 할 수 있습니다. 서로 쉽게 조인되지 않는 테이블 또는 완전히 관련이없는 테이블의 테이블에서 특정 결과를 반환하는 좋은 방법 입니다. 그러나 따라야 할 몇 가지 규칙이 있습니다.

  • 첫 번째 쿼리의 열 유형은 아래의 다른 모든 쿼리의 열 유형과 일치해야합니다.
  • 첫 번째 쿼리의 열 이름은 전체 결과 집합을 식별하는 데 사용됩니다.
  • 각 쿼리의 열 수는 같아야합니다.

이제 와 사용 차이점이 무엇인지 궁금 할 것 입니다. A는 동안 쿼리는 중복을 제거 하지 않습니다. 이것은 오버를 사용할 때 약간의 성능 저하가 있지만 결과가 가치 가 있다는 것을 의미합니다 .하지만 이것에 대해서는 추측하지 않습니다.unionunion allunionunion allunionunion all

이 메모에서는 여기에 몇 가지 추가 메모가 있습니다.

  • 결과를 주문하려면을 사용할 수 order by있지만 더 이상 별칭을 사용할 수 없습니다. 위의 쿼리 order by a.ID에서 결과를 추가하면 오류가 발생합니다. 결과와 관련 하여 동일한 별칭이 두 쿼리에 모두 사용 되었더라도 열 ID대신 호출 a.ID됩니다.
  • 우리는 하나의 order by진술 만 가질 수 있으며 마지막 진술과 같아야합니다.

다음 예제에서는 테이블에 행을 몇 개 더 추가합니다.

Holden브랜드 테이블에 추가 했습니다. 또한 연속으로 추가 cars즉 갖는다 color12색상 표에 대한 언급이 없다 -.

좋아, 보스가 다시 돌아와서 요청을 짖는다-* 우리가 가지고있는 각 브랜드의 수와 그 안에있는 자동차의 수를 원한다! '-전형적인, 우리는 토론의 흥미로운 부분에 도달하고 보스는 더 많은 작업을 원한다 .

바로 우리가해야 할 첫 번째 일은 가능한 브랜드의 전체 목록을 얻는 것입니다.

select
    a.brand
from
    brands a

+--------+
| brand  |
+--------+
| Ford   |
| Toyota |
| Nissan |
| Smart  |
| BMW    |
| Holden |
+--------+
6 rows in set (0.00 sec)

이제이 테이블을 자동차 테이블에 결합하면 다음과 같은 결과가 나타납니다.

select
    a.brand
from
    brands a
        join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+
| brand  |
+--------+
| BMW    |
| Ford   |
| Nissan |
| Smart  |
| Toyota |
+--------+
5 rows in set (0.00 sec)

물론 어떤 문제입니다-우리는 Holden내가 추가 한 멋진 브랜드에 대한 언급을 보지 못했습니다 .

조인은 테이블 에서 일치하는 행을 찾기 때문 입니다. 유형의 자동차에는 데이터 Holden가 없으므로 반환되지 않습니다. 여기서 outer조인을 사용할 수 있습니다 . 다른 테이블과 일치하는지 여부에 관계없이 한 테이블의 모든 결과를 반환 합니다 .

select
    a.brand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+
| brand  |
+--------+
| BMW    |
| Ford   |
| Holden |
| Nissan |
| Smart  |
| Toyota |
+--------+
6 rows in set (0.00 sec)

이제 우리는 멋진 집계 함수를 추가하여 카운트를 얻고 보스를 잠시 등 뒤로 내릴 수 있습니다.

select
    a.brand,
    count(b.id) as countOfBrand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+--------------+
| brand  | countOfBrand |
+--------+--------------+
| BMW    |            2 |
| Ford   |            2 |
| Holden |            0 |
| Nissan |            1 |
| Smart  |            1 |
| Toyota |            5 |
+--------+--------------+
6 rows in set (0.00 sec)

그리고 그와 함께, 보스는 멀리 떨어집니다.

이제 이것을 좀 더 자세히 설명하기 위해 외부 조인은 left또는 right유형일 수 있습니다 . 왼쪽 또는 오른쪽은 어떤 테이블이 완전히 포함 되는지 정의합니다 . A left outer join는 왼쪽에있는 테이블의 모든 행을 포함하지만 right outer join오른쪽에있는 테이블의 모든 결과를 결과 로 가져옵니다.

일부 데이터베이스는 테이블 모두full outer join 에서 결과가 일치하는지 여부를 확인할 수 있지만 일부 데이터베이스에서는 지원되지 않습니다.

아마 지금 시점에서 아마도 쿼리에서 조인 유형을 병합 할 수 있는지 궁금해 할 것입니다. 그리고 대답은 그렇습니다.

select
    b.brand,
    c.color,
    count(a.id) as countOfBrand
from
    cars a
        right outer join brands b
            on b.ID=a.brand
        join colors c
            on a.color=c.ID
group by
    a.brand,
    c.color

+--------+-------+--------------+
| brand  | color | countOfBrand |
+--------+-------+--------------+
| Ford   | Blue  |            1 |
| Ford   | White |            1 |
| Toyota | Black |            1 |
| Toyota | Green |            2 |
| Toyota | Red   |            1 |
| Nissan | Black |            1 |
| Smart  | White |            1 |
| BMW    | Blue  |            1 |
| BMW    | White |            1 |
+--------+-------+--------------+
9 rows in set (0.00 sec)

그렇다면 왜 예상 한 결과가 아닌가? 비록 자동차에서 브랜드로의 외부 조인을 선택했지만 색상에 조인으로 지정되지 않았기 때문에 특정 조인은 두 테이블에서 일치하는 결과 만 가져옵니다.

예상 한 결과를 얻는 데 유용한 쿼리는 다음과 같습니다.

select
    a.brand,
    c.color,
    count(b.id) as countOfBrand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
        left outer join colors c
            on b.color=c.ID
group by
    a.brand,
    c.color

+--------+-------+--------------+
| brand  | color | countOfBrand |
+--------+-------+--------------+
| BMW    | Blue  |            1 |
| BMW    | White |            1 |
| Ford   | Blue  |            1 |
| Ford   | White |            1 |
| Holden | NULL  |            0 |
| Nissan | Black |            1 |
| Smart  | White |            1 |
| Toyota | NULL  |            1 |
| Toyota | Black |            1 |
| Toyota | Green |            2 |
| Toyota | Red   |            1 |
+--------+-------+--------------+
11 rows in set (0.00 sec)

보시다시피 쿼리에 두 개의 외부 조인이 있으며 결과가 예상대로 전달됩니다.

이제 다른 유형의 조인은 어떻습니까? 교차로는 어떻습니까?

모든 데이터베이스를 지원하는 intersection것은 아니지만 거의 모든 데이터베이스를 사용하면 조인 (또는 최소한 잘 구조화 된 where 문)을 통해 교차를 만들 수 있습니다.

교차은 다소 유사 결합의 유형입니다 union상술 한 바와 같이 -하지만 차이가 있다는 것입니다 단지 동일한 데이터 행을 반환 (나는 동일한 뜻) 노동 조합에 합류 다양한 개별 쿼리 사이. 모든 점에서 동일한 행만 반환됩니다.

간단한 예는 다음과 같습니다.

select
    *
from
    colors
where
    ID>2
intersect
select
    *
from
    colors
where
    id<4

일반 union쿼리는 테이블의 모든 행 (첫 번째 쿼리는 무엇이든 반환 ID>2하고 두 번째 쿼리는)을 반환 ID<4하여 전체 집합을 생성하지만 교차 쿼리는 id=3두 기준을 모두 충족하므로 일치하는 행만 반환 합니다.

이제 데이터베이스가 intersect쿼리를 지원하지 않는 경우 다음 쿼리를 사용하여 위의 내용을 쉽게 수행 할 수 있습니다.

select
    a.ID,
    a.color,
    a.paint
from
    colors a
        join colors b
            on a.ID=b.ID
where
    a.ID>2
    and b.ID<4

+----+-------+----------+
| ID | color | paint    |
+----+-------+----------+
|  3 | Blue  | Metallic |
+----+-------+----------+
1 row in set (0.00 sec)

기본적으로 교차 쿼리를 지원하지 않는 데이터베이스를 사용하여 두 개의 다른 테이블에서 교차를 수행하려면 테이블의 모든 열에 조인을 만들어야합니다 .


2
@Fluffeh 니스 답변. 나는 제안을한다 : 만약 당신이 그것을 킬러 SQL 튜토리얼로 만들고 싶다면 벤 다이어그램을 추가하는 것만 놓칠 것이다. 나는 그들 덕분에 바로 왼쪽과 오른쪽 조인을 이해했습니다. 개인 요청 : 일반적인 실수 / 성능 튜닝에 대한 자습서가 있습니까?
StrayChild01

25
어머. 스크롤 휠이 고장났습니다. 좋은 질문과 답변. 나는 이것을 10 번 공표 할 수 있기를 바란다.
아말 무랄리

3
긍정적 인 피드백에 감사드립니다. 그래도 계속 스크롤하면 이것이 첫 번째 대답 일뿐입니다. 그래서 내 대답은 너무 길어서 하나의 "답변"에 맞지 않기 때문에 몇 가지를 사용해야했습니다.)
Fluffeh

7
솔직히이 답변은 다소 단축되어야한다고 생각합니다.
einpoklum

훌륭한 기사. 데이터베이스 가입 101.
maqs

101

좋아,이 게시물이 매우 흥미 롭다는 것을 알았으며 쿼리 작성에 대한 지식을 공유하고 싶습니다. 이 Fluffeh에 감사드립니다 . 이 글을 읽고 내가 틀렸다고 생각하는 다른 사람들은 내 대답을 편집하고 비판하는 데 101 % 무료입니다. ( 솔직히, 나는 내 실수를 고쳐 주셔서 대단히 감사합니다. )

자주 묻는 질문 중 일부를 MySQL태그에 게시하겠습니다 .


트릭 No. 1 ( 여러 조건과 일치하는 행 )

이 스키마가 주어지면

CREATE TABLE MovieList
(
    ID INT,
    MovieName VARCHAR(25),
    CONSTRAINT ml_pk PRIMARY KEY (ID),
    CONSTRAINT ml_uq UNIQUE (MovieName)
);

INSERT INTO MovieList VALUES (1, 'American Pie');
INSERT INTO MovieList VALUES (2, 'The Notebook');
INSERT INTO MovieList VALUES (3, 'Discovery Channel: Africa');
INSERT INTO MovieList VALUES (4, 'Mr. Bean');
INSERT INTO MovieList VALUES (5, 'Expendables 2');

CREATE TABLE CategoryList
(
    MovieID INT,
    CategoryName VARCHAR(25),
    CONSTRAINT cl_uq UNIQUE(MovieID, CategoryName),
    CONSTRAINT cl_fk FOREIGN KEY (MovieID) REFERENCES MovieList(ID)
);

INSERT INTO CategoryList VALUES (1, 'Comedy');
INSERT INTO CategoryList VALUES (1, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Drama');
INSERT INTO CategoryList VALUES (3, 'Documentary');
INSERT INTO CategoryList VALUES (4, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Action');

질문

찾기 모든 동영상 적어도에 속하는 모두 ComedyRomance범주를.

해결책

이 질문은 때때로 매우 까다로울 수 있습니다. 다음과 같은 쿼리가 답이 될 것 같습니다.

SELECT  DISTINCT a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName = 'Comedy' AND
        b.CategoryName = 'Romance'

SQLFiddle 데모

결과나오지 않기 때문에 매우 잘못되었습니다 . 이에 대한 설명은 각 행CategoryName 에 유효한 값이 하나만 있다는 것입니다 . 예를 들어 첫 번째 조건은 true를 반환 하고 두 번째 조건은 항상 false입니다. 따라서 연산자 를 사용 하면 두 조건이 모두 맞아야합니다. 그렇지 않으면 거짓입니다. 또 다른 쿼리는 다음과 같습니다.AND

SELECT  DISTINCT a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName IN ('Comedy','Romance')

SQLFiddle 데모

결과는에 일치하는 레코드가 하나 이상있는 레코드와 일치하기 때문에 여전히 올바르지 않습니다 categoryName. 진정한 해결책은 영화 당 기록 인스턴스의 수를 계산하여이 될 것입니다 . 인스턴스 수는 조건에 제공된 총 값 수와 일치해야합니다.

SELECT  a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName IN ('Comedy','Romance')
GROUP BY a.MovieName
HAVING COUNT(*) = 2

SQLFiddle 데모 (답변)


트릭 No. 2 ( 각 항목의 최대 기록 )

주어진 스키마,

CREATE TABLE Software
(
    ID INT,
    SoftwareName VARCHAR(25),
    Descriptions VARCHAR(150),
    CONSTRAINT sw_pk PRIMARY KEY (ID),
    CONSTRAINT sw_uq UNIQUE (SoftwareName)  
);

INSERT INTO Software VALUES (1,'PaintMe','used for photo editing');
INSERT INTO Software VALUES (2,'World Map','contains map of different places of the world');
INSERT INTO Software VALUES (3,'Dictionary','contains description, synonym, antonym of the words');

CREATE TABLE VersionList
(
    SoftwareID INT,
    VersionNo INT,
    DateReleased DATE,
    CONSTRAINT sw_uq UNIQUE (SoftwareID, VersionNo),
    CONSTRAINT sw_fk FOREIGN KEY (SOftwareID) REFERENCES Software(ID)
);

INSERT INTO VersionList VALUES (3, 2, '2009-12-01');
INSERT INTO VersionList VALUES (3, 1, '2009-11-01');
INSERT INTO VersionList VALUES (3, 3, '2010-01-01');
INSERT INTO VersionList VALUES (2, 2, '2010-12-01');
INSERT INTO VersionList VALUES (2, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 3, '2011-12-01');
INSERT INTO VersionList VALUES (1, 2, '2010-12-01');
INSERT INTO VersionList VALUES (1, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 4, '2012-12-01');

질문

각 소프트웨어에서 최신 버전을 찾으십시오. 다음 열을 표시합니다 SoftwareName, Descriptions, LatestVersion( VersionNo 열에서 )DateReleased

해결책

일부 SQL 개발자는 실수로 MAX()집계 함수를 사용 합니다. 그들은 이런 식으로 만드는 경향이 있습니다.

SELECT  a.SoftwareName, a.Descriptions,
        MAX(b.VersionNo) AS LatestVersion, b.DateReleased
FROM    Software a
        INNER JOIN VersionList b
            ON a.ID = b.SoftwareID
GROUP BY a.ID
ORDER BY a.ID

SQLFiddle 데모

( 대부분의 RDBMS는이 group by절 에서 집계되지 않은 일부 열을 지정하지 않기 때문에 구문 오류를 생성합니다. ) 결과는 LatestVersion각 소프트웨어 에서 올바른 결과를 생성 하지만 분명히 DateReleased부정확합니다. MySQL지원하지 않는 Window FunctionsCommon Table Expression일부 RDBMS가 이미처럼 아직. 이 문제의 해결 방법은 각 소프트웨어 subquery에서 개별 최대 값을 가져오고 versionNo나중에 다른 테이블에서 조인하는 소프트웨어 를 작성하는 것 입니다 .

SELECT  a.SoftwareName, a.Descriptions,
        b.LatestVersion, c.DateReleased
FROM    Software a
        INNER JOIN
        (
            SELECT  SoftwareID, MAX(VersionNO) LatestVersion
            FROM    VersionList
            GROUP BY SoftwareID
        ) b ON a.ID = b.SoftwareID
        INNER JOIN VersionList c
            ON  c.SoftwareID = b.SoftwareID AND
                c.VersionNO = b.LatestVersion
GROUP BY a.ID
ORDER BY a.ID

SQLFiddle 데모 (답변)


그렇습니다. 태그 에 대한 다른 FAQ 를 기억하자마자 다시 게시하겠습니다 MySQL. 이 작은 기사를 읽어 주셔서 감사합니다. 나는 당신이 이것으로부터 약간의 지식을 얻었기를 바랍니다.

업데이트 1


트릭 No. 3 ( 두 ID 사이에서 최신 레코드 찾기 )

주어진 스키마

CREATE TABLE userList
(
    ID INT,
    NAME VARCHAR(20),
    CONSTRAINT us_pk PRIMARY KEY (ID),
    CONSTRAINT us_uq UNIQUE (NAME)  
);

INSERT INTO userList VALUES (1, 'Fluffeh');
INSERT INTO userList VALUES (2, 'John Woo');
INSERT INTO userList VALUES (3, 'hims056');

CREATE TABLE CONVERSATION
(
    ID INT,
    FROM_ID INT,
    TO_ID INT,
    MESSAGE VARCHAR(250),
    DeliveryDate DATE
);

INSERT INTO CONVERSATION VALUES (1, 1, 2, 'hi john', '2012-01-01');
INSERT INTO CONVERSATION VALUES (2, 2, 1, 'hello fluff', '2012-01-02');
INSERT INTO CONVERSATION VALUES (3, 1, 3, 'hey hims', '2012-01-03');
INSERT INTO CONVERSATION VALUES (4, 1, 3, 'please reply', '2012-01-04');
INSERT INTO CONVERSATION VALUES (5, 3, 1, 'how are you?', '2012-01-05');
INSERT INTO CONVERSATION VALUES (6, 3, 2, 'sample message!', '2012-01-05');

질문

두 사용자 간의 최신 대화를 찾으십시오.

해결책

SELECT    b.Name SenderName,
          c.Name RecipientName,
          a.Message,
          a.DeliveryDate
FROM      Conversation a
          INNER JOIN userList b
            ON a.From_ID = b.ID
          INNER JOIN userList c
            ON a.To_ID = c.ID
WHERE     (LEAST(a.FROM_ID, a.TO_ID), GREATEST(a.FROM_ID, a.TO_ID), DeliveryDate)
IN
(
    SELECT  LEAST(FROM_ID, TO_ID) minFROM,
            GREATEST(FROM_ID, TO_ID) maxTo,
            MAX(DeliveryDate) maxDate
    FROM    Conversation
    GROUP BY minFROM, maxTo
)

SQLFiddle 데모


대박! 주의해야 할 첫 번째 솔루션은 두 필드에 고유 한 제약이 있기 때문에 작동합니다. 일반적인 문제를 해결하기 위해보다 일반적인 솔루션을 사용할 수 있습니다. 내 의견으로는 유일한 해결책은 comedy및에 대한 개별 선택을 수행하는 것 romance입니다. Having그때 적합하지 않습니다 ..
nawfal

고유 제한 조건이 추가되지 않은 경우 @nawfal 정말, 당신은 추가 할 필요가 없습니다 distinctHAVING 절에 SQLFiddle 데모 : D
오우삼

63

2 부-서브 쿼리

자, 이제 보스가 다시 터졌습니다. 나는 브랜드가있는 모든 자동차 목록과 우리가 가지고있는 브랜드의 총 개수를 원합니다!

이는 SQL 쿼리 백에서 하위 쿼리 인 다음 트릭을 사용할 수있는 좋은 기회입니다. 용어에 익숙하지 않은 경우 하위 쿼리는 다른 쿼리 내에서 실행되는 쿼리입니다. 그것들을 사용하는 방법은 여러 가지가 있습니다.

우리의 요청을 위해 먼저 각 자동차와 브랜드를 나열하는 간단한 쿼리를 작성해 보겠습니다.

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID

이제 브랜드별로 정렬 된 자동차 수를 얻으려면 물론 다음과 같이 작성할 수 있습니다.

select
    b.brand,
    count(a.ID) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID
group by
    b.brand

+--------+-----------+
| brand  | countCars |
+--------+-----------+
| BMW    |         2 |
| Ford   |         2 |
| Nissan |         1 |
| Smart  |         1 |
| Toyota |         5 |
+--------+-----------+

따라서 count 함수를 단순히 원래 쿼리에 추가 할 수 있어야합니까?

select
    a.ID,
    b.brand,
    count(a.ID) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID
group by
    a.ID,
    b.brand

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  1 | Toyota |         1 |
|  2 | Ford   |         1 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  5 | Toyota |         1 |
|  6 | BMW    |         1 |
|  7 | Ford   |         1 |
|  8 | Toyota |         1 |
|  9 | Toyota |         1 |
| 10 | BMW    |         1 |
| 11 | Toyota |         1 |
+----+--------+-----------+
11 rows in set (0.00 sec)

안타깝게도 우리는 그렇게 할 수 없습니다. 그 이유는 자동차 ID (열 a.ID)를 추가 할 때 그룹별로 추가해야하므로 카운트 기능이 작동하면 ID 당 하나의 ID 만 일치하기 때문입니다.

그러나 여기서는 하위 쿼리를 사용할 수 있습니다. 실제로 필요한 결과와 동일한 결과를 반환하는 완전히 다른 두 가지 유형의 하위 쿼리를 수행 할 수 있습니다. 첫 번째는 하위 쿼리를 select절 에 넣는 것 입니다. 즉, 데이터 행을 얻을 때마다 하위 쿼리가 실행되고 데이터 열을 가져온 다음 데이터 행에 데이터가 표시됩니다.

select
    a.ID,
    b.brand,
    (
    select
        count(c.ID)
    from
        cars c
    where
        a.brand=c.brand
    ) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  2 | Ford   |         2 |
|  7 | Ford   |         2 |
|  1 | Toyota |         5 |
|  5 | Toyota |         5 |
|  8 | Toyota |         5 |
|  9 | Toyota |         5 |
| 11 | Toyota |         5 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  6 | BMW    |         2 |
| 10 | BMW    |         2 |
+----+--------+-----------+
11 rows in set (0.00 sec)

그리고 Bam !, 이것은 우리를 할 것입니다. 그래도이 하위 쿼리는 반환하는 각 데이터 행마다 실행해야합니다. 이 작은 예에서도 5 가지 브랜드의 자동차 만 있지만 하위 쿼리는 11 개의 데이터 행을 반환하므로 11 번 실행되었습니다. 따라서이 경우 코드를 작성하는 가장 효율적인 방법은 아닙니다.

다른 접근 방식의 경우 하위 쿼리를 실행하고 테이블 인 것처럼 가장하십시오.

select
    a.ID,
    b.brand,
    d.countCars
from
    cars a
        join brands b
            on a.brand=b.ID
        join
            (
            select
                c.brand,
                count(c.ID) as countCars
            from
                cars c
            group by
                c.brand
            ) d
            on a.brand=d.brand

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  1 | Toyota |         5 |
|  2 | Ford   |         2 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  5 | Toyota |         5 |
|  6 | BMW    |         2 |
|  7 | Ford   |         2 |
|  8 | Toyota |         5 |
|  9 | Toyota |         5 |
| 10 | BMW    |         2 |
| 11 | Toyota |         5 |
+----+--------+-----------+
11 rows in set (0.00 sec)

자, 우리는 동일한 결과를 얻습니다 (약간 다르게 정렬되었습니다-데이터베이스는 이번에 선택한 첫 번째 열로 정렬 된 결과를 반환하려고하는 것 같습니다)-동일한 숫자입니다.

그렇다면 두 가지의 차이점은 무엇이며 언제 각 유형의 하위 쿼리를 사용해야합니까? 먼저 두 번째 쿼리의 작동 방식을 이해해야합니다. from쿼리 절 에서 두 개의 테이블을 선택한 다음 쿼리를 작성하고 데이터베이스가 실제로는 테이블이라는 것을 데이터베이스에 알 렸습니다. 이 방법을 사용하면 몇 가지 이점 이있을 있습니다 (일부 제한 사항도 있음). 가장 먼저이 하위 쿼리가 한 번 실행 되었습니다 . 데이터베이스에 많은 양의 데이터가 포함 된 경우 첫 번째 방법에 비해 크게 개선 될 수 있습니다. 그러나 이것을 테이블로 사용함에 따라 추가 데이터 행을 가져와야 실제로 데이터 행에 다시 결합 될 수 있습니다. 우리는 또한 충분한위의 쿼리와 같이 간단한 조인을 사용하려는 경우 데이터 행. 당신이 기억하는 경우, 만에 일치하는 데이터가 행을 다시 끌어 가입 모두 조인의 측면을. 주의하지 않으면이 하위 쿼리에 일치하는 행이 없으면 cars 테이블에서 유효한 데이터가 반환되지 않을 수 있습니다.

이제 첫 번째 하위 쿼리를 살펴보면 몇 가지 제한 사항이 있습니다. 데이터를 단일 행으로 다시 가져 오기 때문에 한 행의 데이터 다시 가져올 수 있습니다 . 에 사용되는 하위 쿼리 select쿼리의 절은 매우 자주 같은 경우에만 집계 함수를 사용 sum, count, max또는 다른 유사한 집계 함수. 꼭 그럴 필요 없지만 종종 글을 쓰는 방식입니다.

계속 진행하기 전에 하위 쿼리를 사용할 수있는 곳을 간단히 살펴 보겠습니다. 우리는이 where절 에서 사용할 수 있습니다 -이제이 예제는 데이터베이스에서 약간 고안된 것입니다. 다음 데이터를 얻는 더 좋은 방법이 있지만 예제 용으로 만 볼 수 있습니다.

select
    ID,
    brand
from
    brands
where
    brand like '%o%'

+----+--------+
| ID | brand  |
+----+--------+
|  1 | Ford   |
|  2 | Toyota |
|  6 | Holden |
+----+--------+
3 rows in set (0.00 sec)

이름에 문자가 포함 된 브랜드 ID 및 브랜드 이름 (두 번째 열은 브랜드를 보여주기 위해 추가 된 것) 목록을 반환합니다 o.

이제이 쿼리의 결과를 where 절에서 다음과 같이 사용할 수 있습니다.

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID
where
    a.brand in
        (
        select
            ID
        from
            brands
        where
            brand like '%o%'
        )

+----+--------+
| ID | brand  |
+----+--------+
|  2 | Ford   |
|  7 | Ford   |
|  1 | Toyota |
|  5 | Toyota |
|  8 | Toyota |
|  9 | Toyota |
| 11 | Toyota |
+----+--------+
7 rows in set (0.00 sec)

보시다시피, 하위 쿼리가 세 개의 브랜드 ID를 반환했지만 자동차 테이블에는 두 개의 브랜드 ID 만있었습니다.

이 경우 세부 정보를 위해 하위 쿼리는 다음 코드를 작성한 것처럼 작동합니다.

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID
where
    a.brand in (1,2,6)

+----+--------+
| ID | brand  |
+----+--------+
|  1 | Toyota |
|  2 | Ford   |
|  5 | Toyota |
|  7 | Ford   |
|  8 | Toyota |
|  9 | Toyota |
| 11 | Toyota |
+----+--------+
7 rows in set (0.00 sec)

다시 한 번, 하위 쿼리와 수동 입력이 데이터베이스에서 돌아올 때 행 순서를 어떻게 변경했는지 확인할 수 있습니다.

하위 쿼리에 대해 논의하는 동안 하위 쿼리로 수행 할 수있는 다른 작업을 살펴 ​​보겠습니다.

  • 다른 하위 쿼리 내에 하위 쿼리 등을 배치 할 수 있습니다. 데이터베이스에 따라 한계가 있지만, 일부 미친 사람과 열광적 인 프로그래머의 재귀 기능이 부족하면 대부분의 사람들이 그 한계에 도달하지 않습니다.
  • 하나의 쿼리에 여러 개의 하위 쿼리를 배치 할 수 있으며 select, 일부 는 절에, 일부 는 절에, from몇 개는 where더 많이 쿼리에 넣을 수 있습니다. 실행하십시오.

효율적인 코드를 작성해야하는 경우 쿼리를 여러 가지 방법으로 작성하고 결과를 얻는 데 가장 적합한 쿼리 인 타이밍 또는 설명 계획을 사용하여 참조하는 것이 좋습니다. 작동하는 첫 번째 방법이 항상 최선의 방법은 아닙니다.


새 개발자에게는 매우 중요합니다. 하위 쿼리를 조인으로 사용할 수 없는 경우 하위 쿼리는 모든 결과에 대해 한 번만 실행될 수 있습니다 (위 그림 참조).
Xeoncross

59

3 부-트릭과 효율적인 코드

MySQL의 효율성

나는 팁과 요령을 위해 약간의 비트를 추가 할 것이라고 생각했습니다.

내가 볼 수있는 한 가지 질문은 공정하게 나타납니다. 두 테이블에서 일치하지 않는 행을 얻는 방법은 다음과 같이 가장 일반적으로 받아 들여지는 답변을 봅니다 (자동차 및 브랜드 테이블을 기반으로 -Holden 이 브랜드이지만 자동차 표에는 표시되지 않음) :

select
    a.ID,
    a.brand
from
    brands a
where
    a.ID not in(select brand from cars)

그리고 그렇습니다 .

+----+--------+
| ID | brand  |
+----+--------+
|  6 | Holden |
+----+--------+
1 row in set (0.00 sec)

그러나 일부 데이터베이스 에서는 효율적 이지 않습니다 . 여기 에 그것에 대해 묻는 스택 오버플로 질문에 대한 링크 가 있습니다. 여기서 딱딱한 부분에 들어가고 싶다면 깊이있는 기사 가 있습니다.

짧은 대답은 최적화 프로그램이 효율적으로 처리하지 않으면 일치하지 않는 행을 가져 오기 위해 다음과 같은 쿼리를 사용하는 것이 훨씬 좋습니다.

select
    a.brand
from
    brands a
        left join cars b
            on a.id=b.brand
where
    b.brand is null

+--------+
| brand  |
+--------+
| Holden |
+--------+
1 row in set (0.00 sec)

하위 쿼리에서 동일한 테이블로 테이블 업데이트

Ahhh, 또 다른 oldie이지만 goodie-old FROM 절에서 업데이트 할 대상 테이블 'brands'를 지정할 수 없습니다 .

MySQL update...에서는 동일한 테이블에서 하위 선택을 사용하여 쿼리 를 실행할 수 없습니다 . 자, 당신은 생각하고있을 것입니다. 왜 where 절에 넣을까요? 그러나 max()날짜가 다른 행만 다른 행 으로 업데이트하려면 어떻게해야 합니까? where 절에서는 정확히 그렇게 할 수 없습니다.

update 
    brands 
set 
    brand='Holden' 
where 
    id=
        (select 
            id 
        from 
            brands 
        where 
            id=6);
ERROR 1093 (HY000): You can't specify target table 'brands' 
for update in FROM clause

그래서 우리는 그것을 할 수 없습니다? 정확히는 아닙니다. 놀랍게도 많은 사용자가 알지 못하는 몰래 해결 방법이 있습니다.주의를 기울여야 할 해커도 포함됩니다.

하위 쿼리를 다른 하위 쿼리에 고정하면 두 쿼리 사이에 충분한 간격을 두어 작동 할 수 있습니다. 그러나 트랜잭션 내에서 쿼리를 유지하는 것이 가장 안전 할 수 있습니다. 이렇게하면 쿼리가 실행되는 동안 테이블에서 다른 변경이 이루어지지 않습니다.

update 
    brands 
set 
    brand='Holden' 
where id=
    (select 
        id 
    from 
        (select 
            id 
        from 
            brands 
        where 
            id=6
        ) 
    as updateTable);

Query OK, 0 rows affected (0.02 sec)
Rows matched: 1  Changed: 0  Warnings: 0

3
WHERE NOT EXISTS () 구성은 '효율성 관점'과 거의 동일하지만 내 의견으로는 읽기 / 이해하기가 훨씬 쉽다는 점에 유의하고 싶습니다. 다시 말하지만, 내 지식은 MSSQL로 제한되어 있으며 다른 플랫폼에서도 마찬가지입니다.
deroby

나는 다른 날 에이 유형의 비교를 시도했지만 NOT IN ()에는 수백 개의 ID 목록이 있었고 쿼리와 조인 버전 사이에는 차이가 없었습니다. 아마도 수천 또는 수십억에 달할 때 차이가 생길 것입니다.
Buttle Butkus

18

FROM 키워드에 여러 쿼리 개념을 사용할 수 있습니다. 하나의 예를 보여 드리겠습니다.

SELECT DISTINCT e.id,e.name,d.name,lap.lappy LAPTOP_MAKE,c_loc.cnty COUNTY    
FROM  (
          SELECT c.id cnty,l.name
          FROM   county c, location l
          WHERE  c.id=l.county_id AND l.end_Date IS NOT NULL
      ) c_loc, emp e 
      INNER JOIN dept d ON e.deptno =d.id
      LEFT JOIN 
      ( 
         SELECT l.id lappy, c.name cmpy
         FROM   laptop l, company c
         WHERE l.make = c.name
      ) lap ON e.cmpy_id=lap.cmpy

원하는만큼 테이블을 사용할 수 있습니다. 테이블 하위 쿼리 내부에서도 필요한 경우 외부 조인과 공용체를 사용하십시오.

그것은 테이블과 필드만큼 많은 것을 포함하는 매우 쉬운 방법입니다.


8

희망 사항을 통해 테이블을 찾을 수 있기를 바랍니다.

jsfiddle

mysql> show columns from colors;                                                         
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+           
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| color | varchar(15) | YES  |     | NULL    |                |
| paint | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.