본문 바로가기
SQL

[SQL] JOIN

by busybee-busylife 2025. 2. 23.
728x90

JOIN: 여러 테이블에서 동시에 데이터 가져오기 

 

Cross Join

실제 사용 많지 않음 

ex. dogs와 owners 사이의 모든 조합을 알아보고 싶을 때 

SELECT * FROM dogs CROSS JOIN owners;
-- dogs 테이블과 owners 테이블의 모든 행을 결합하여 결과를 반환
-- dogs 테이블의 각 행은 owners 테이블의 모든 행과 한 번씩 결합

 

 

 

Inner Join 

가장 많이 사용 

어떤 row 끼리 연결시킬지 선택할 수 있음 

ex. 각 dog가 실제 owner, breed와 연결 

      owner, breed가 없는 강아지는 제외하고 반환  

SELECT dogs.name AS dog_name, owners.name AS owner_name, breeds.name AS breed_name
	FROM dogs 
  INNER JOIN owners ON dogs.owner_id = owners.owner_id
  INNER JOIN breeds ON dogs.breed_id = breeds.breed_id;
-- dogs 테이블과 owners 테이블을 owner_id가 일치하는 행끼리 결합
-- 위 결과와 breeds 테이블을 breed_id가 일치하는 행끼리 다시 결합
-- 세 테이블 모두에서 일치하는 owner_id와 breed_id를 가진 행들만 결과에 포함
SELECT dogs.name AS dog_name, owners.name AS owner_name, breeds.name AS breed_name
	FROM dogs 
  INNER JOIN owners USING (owner_id)
  INNER JOIN breeds USING (breed_id);
-- 칼럼명 구조가 같으면 위 처럼 할 수 있다 
-- owner_id / breed_id

 

 

 

Outer Join

Left Outer Join: owner가 없는 강아지를 포함 모든 강아지를 반환 

SELECT dogs.name AS dog_name, owners.name AS owner_name 
  FROM dogs 
  LEFT JOIN owners ON dogs.owner_id = owners.owner_id;

 

Right Outer Join: dog이 없는 owner를 포함 모든 owner를 반환 

SELECT dogs.name AS dog_name, owners.name AS owner_name 
  FROM dogs 
  RIGHT JOIN owners ON dogs.owner_id = owners.owner_id;

참고) SQLite에서는 Right Outer Join이 불가 

 


Practice

-- all dogs with their breed names
SELECT dogs.name AS dog_name, breeds.name AS breed_name
  FROM dogs
  LEFT JOIN breeds ON dogs.breed_id = breeds.breed_id;


-- all owners and their dogs 
SELECT owners.name AS owner_name, dogs.name AS dog_name
  FROM owners
  LEFT JOIN dogs ON owners.owner_id = dogs.owner_id;


-- all breeds and the dogs of that breed 
SELECT breeds.name AS breed_name, dogs.name AS dog_name
  FROM breeds
  LEFT JOIN dogs ON breeds.breed_id = dogs.breed_id;


-- all dogs with their pet passport info and owner data 
SELECT dogs.name AS dog_name, 
       pet_passports.blood_type AS blood_type, 
       pet_passports.allergies AS allergies, 
       pet_passports.last_checkup_date AS checkup,
       owners.name AS owner_name
  FROM dogs
  LEFT JOIN pet_passports ON dogs.dog_id = pet_passports.dog_id
  LEFT JOIN owners ON dogs.owner_id = owners.owner_id;
  

-- all tricks and the dogs that know them 
SELECT tricks.name AS trick_name, dogs.name AS dog_name, dog_tricks.proficiency, dog_tricks.date_learned
  FROM tricks
  LEFT JOIN dog_tricks ON tricks.trick_id = dog_tricks.trick_id
  LEFT JOIN dogs ON dog_tricks.dog_id = dogs.dog_id; 


-- all dogs that don't know a single trick
SELECT dogs.name, dog_tricks.dog_id
  FROM dogs
  LEFT JOIN dog_tricks using (dog_id)
  WHERE dog_tricks.dog_id IS NULL;
-- all breeds and the count of dogs for each breed
SELECT breeds.name, count(*) AS num_of_dogs
  FROM breeds
  RIGHT JOIN dogs ON dogs.breed_id = breeds.breed_id  -- breeds: 왼쪽 / dogs: 오른쪽
  GROUP BY breeds.name
  ORDER BY num_of_dogs DESC;


-- all owners with the count of their dogs, the average dog weight and the average dog age 
SELECT owners.name AS owner_name, 
  COUNT(dogs.dog_id), AVG(dogs.weight) AS dog_weight, AVG(2025-YEAR(dogs.date_of_birth)) AS dog_age
  FROM owners
  LEFT JOIN dogs ON owners.owner_id = dogs.owner_id  -- JOIN 구문은 GROUP BY 앞에!
  GROUP BY owners.owner_id; 
-- 나이계산: AVG(TIMESTAMPDIFF(YEAR, dogs.date_of_birth, CURDATE()))이 더 정확하다



-- all tricks and the number of dogs that know each trick ordered by poplularity
SELECT tricks.name, COUNT(dogs.dog_id) AS num_of_dogs
  FROM tricks
  LEFT JOIN dog_tricks ON tricks.trick_id = dog_tricks.trick_id
  LEFT JOIN	dogs ON dog_tricks.dog_id = dogs.dog_id
  GROUP BY tricks.name
  ORDER BY num_of_dogs DESC;

SELECT tricks.name, COUNT(*) AS num_of_dogs
  FROM tricks
  LEFT JOIN dog_tricks USING (trick_id)
  GROUP BY tricks.trick_id
  ORDER BY num_of_dogs DESC; -- 좀 더 간단함 



-- all dogs along with the count of tricks they know
SELECT dogs.name, COUNT(dog_tricks.trick_id) AS tricks_known
  FROM dogs 
  LEFT JOIN dog_tricks USING (dog_id)
  GROUP BY dogs.name
  ORDER BY tricks_known DESC;
  
  

-- all owners with their dogs and the tricks their dogs know 
SELECT owners.name AS owner_name, GROUP_CONCAT(DISTINCT dogs.name SEPARATOR ', ') AS dog_name, GROUP_CONCAT(DISTINCT tricks.name SEPARATOR ', ') AS trick
  FROM owners
  LEFT JOIN dogs USING (owner_id)
  LEFT JOIN dog_tricks ON dogs.dog_id = dog_tricks.dog_id
  LEFT JOIN tricks ON dog_tricks.trick_id = tricks.trick_id
  GROUP BY owners.name;
  
SELECT o.name, d.name, dt.proficiency, t.name
  FROM owners o
  JOIN dogs d USING (owner_id)
  JOIN dog_tricks dt USING (dog_id)
  JOIN tricks t USING (trick_id);
-- all breeds with their average dog weight and typical lifespan
SELECT breeds.name, breeds.typical_lifespan, AVG(dogs.weight) AS dog_weight
  FROM breeds
  LEFT JOIN dogs USING (breed_id)
  GROUP BY breeds.breed_id;



-- all dogs with their latest checkup date and the time since their last checkup
SELECT dogs.name, pet_passports.last_checkup_date, TIMESTAMPDIFF(DAY, pet_passports.last_checkup_date, CURDATE()) AS days_since_last_checkup
  FROM dogs
  LEFT JOIN pet_passports USING (dog_id);
  
                                          

-- all breeds with the name of the heaviest dog of that breed(서브쿼리)
SELECT breeds.name, MAX(dogs.weight) AS max_weight, dogs.name 
  FROM breeds
  JOIN dogs USING (breed_id)
  GROUP BY breeds.breed_id;
-- 에러 

SELECT b.name AS breed_name,
       d.name AS dog_name,
       d.weight AS max_weight
  FROM breeds b
  JOIN dogs d USING (breed_id)
  JOIN (
      SELECT breed_id, MAX(weight) AS max_weight
      FROM dogs
      GROUP BY breed_id   -- breed_id별 max_weight 
  ) md ON d.breed_id = md.breed_id AND d.weight = md.max_weight;

SELECT breeds.breed_id, breeds.name, dogs.weight AS heaviest_weight
  FROM breeds
  JOIN dogs USING (breed_id)
  WHERE dogs.weight = (SELECT MAX(dogs.weight) FROM dogs WHERE dogs.breed_id=breeds.breed_id);
  

-- all tricks with the name of the dog who learned it most recently 
SELECT tricks.name, dogs.name AS latest_learned_dog, MIN(TIMESTAMPDIFF(DAY, dog_tricks.date_learned, CURDATE())) AS days_after_learning
  FROM tricks
  JOIN dog_tricks USING (trick_id)
  JOIN dogs ON dog_tricks.trick_id=dogs.dog_id 
  GROUP BY tricks.trick_id;
-- 잘못: 이렇게 하면 dogs.name이 임의로 선택됨

SELECT t.name, d.name, dt.date_learned
  FROM tricks t
  JOIN dog_tricks dt USING (trick_id)
  JOIN dogs d ON dt.dog_id = d.dog_id
  WHERE dt.date_learned = (SELECT MAX(dt2.date_learned) FROM dog_tricks dt2 WHERE dt2.trick_id=dt.trick_id);
728x90

'SQL' 카테고리의 다른 글

[SQL] 자동실행 기능: Events / Triggers  (1) 2025.03.03
[SQL] Normalization / Union  (0) 2025.02.24
[SQL] Foreign Keys: Data Normalization / ON DELETE / One-To-Many / Many-To-Many  (0) 2025.02.20
[SQL] MySQL  (0) 2025.02.10
[SQLite] INDEX  (1) 2025.02.10