Загружаем каталог…
Загружаем каталог…
이번 주차에서는 1주차에 설계했던 ERD를 MySQL 테이블과 더미 데이터로 구현하고, SELECT , WHERE , JOIN , ORDER BY , LIMIT 등을 사용해 화면에 필요한 데이터를 직접 조회해보았다. 그중 가장 궁금했던 부분은 JOIN 이었다. 미션 3에서 특정 책의 태그 목록을 조회하기 위해 book , book_tag , tag 테이블을 JOIN했는데, 책은 하나인데 같은 책 제목이 여러 줄 나타났다. 처음에는 JOIN을 잘못해서 데이터가 중복된 건가? 라고 생각했다. 하지만 ERD의 관계를 다시 확인해보니, 이 결과는 오류가 아니라 1:N 관계가 JOIN 결과에 그대로 펼쳐진 것 이었다. 그래서 이번 글에서는 단순히 JOIN 문법을 정리하기보다, JOIN하면 왜 행의 개수가 늘어날 수 있는지 1:N, N:M 관계가 결과 행 수에 어떤 영향을 주는지 정상적인 반복과 잘못된 중복은 어떻게 다른지 DISTINCT 로 중복을 제거하면 항상 해결되는지 단순한 존재 여부를 확인할 때는 왜 EXISTS 를 사용할 수 있는지 를 중심으로 정리해보려고 한다. 1. JOIN은 단순히 테이블을 옆으로 붙이는 걸까? JOIN은 서로 다른 테이블에 나누어 저장된 데이터를 관계를 기준으로 하나의 결과로 조회할 때 사용한다. 이번 실습의 도서 상세 화면에서는 다음과 같은 정보가 필요했다. 책 제목 태그 이름 현재 사용자의 좋아요 여부 하지만 이 값들은 하나의 테이블에 모두 들어 있지 않았다. book → 책 정보 tag → 태그 정보 book_tag → 책과 태그의 관계 book_like → 사용자와 책의 좋아요 관계 특히 책과 태그는 다음과 같이 연결되어 있다. book ↓ book_tag ↓ tag book_tag 는 책과 태그 사이의 관계를 저장하는 중간 테이블이다. 예를 들어 book_id = 1 인 책에 tag_id = 1 , tag_id = 2 가 연결되어 있다면 book_tag 에는 다음과 같은 데이터가 존재할 수 있다. book_id | tag_id --------|------- 1 | 1 1 | 2 즉 책 한 권에 여러 태그가 연결될 수 있다. 이 관계가 JOIN 결과의 행 수를 결정하는 중요한 요소가 된다. 2. 책은 하나인데 왜 JOIN 결과는 두 줄일까? 미션 3에서 태그 정보를 조회하기 위해 다음과 같은 SQL을 작성했다. SELECT b.title, t.name AS tag_name FROM book b JOIN book_tag bt ON b.book_id = bt.book_id JOIN tag t ON bt.tag_id = t.tag_id WHERE b.book_id = 1; 실행 결과는 다음과 같이 나타났다. 달빛 도서관 | 소설 달빛 도서관 | 추천 달빛 도서관 이라는 책은 한 권인데 결과에서는 두 번 등장한다. 하지만 실제 관계를 보면 이유는 단순하다. 달빛 도서관 ├─ 소설 └─ 추천 책 자체는 하나지만 책과 태그 사이의 관계가 두 개 존재한다. JOIN은 이 두 관계를 각각 하나의 행으로 표현한다. 따라서 책 1개 + 연결된 태그 2개 = JOIN 결과 2행 이 된다. 즉 JOIN 결과에서 기준 테이블의 데이터가 반복되는 것은 항상 중복 오류를 의미하지 않는다. 하나의 데이터가 여러 데이터와 관계를 맺고 있다면 그 관계의 수만큼 기준 데이터가 반복될 수 있다. 3. 카디널리티에 따라 JOIN 결과는 어떻게 달라질까? ERD에서 자주 보는 1:1 , 1:N , N:M 은 단순히 다이어그램에 표시하는 관계가 아니다. 이 관계는 실제 JOIN 결과의 형태에도 영향을 준다. 1:1 관계 한 행이 상대 테이블의 한 행과만 연결된다. A 1 : 1 B A의 한 행에 B의 한 행만 연결된다면 JOIN 이후에도 일반적으로 한 관계가 한 행으로 표현된다. 1:N 관계 한 행이 상대 테이블의 여러 행과 연결된다. book 1 : N book_tag 예를 들어 책 한 권에 태그 연결 정보가 세 개 있다면 JOIN 결과에서는 같은 책 정보가 세 번 나타날 수 있다. 책 A | 태그 1 책 A | 태그 2 책 A | 태그 3 N:M 관계 양쪽 데이터가 모두 여러 개의 상대 데이터와 연결될 수 있다. 관계형 데이터베이스에서는 보통 중간 테이블을 이용해 두 개의 1:N 관계로 풀어서 표현한다. 이번 실습의 책과 태그도 사실상 다음 구조다. book 1 : N book_tag tag 1 : N book_tag 이를 전체적으로 보면 book N : M tag 관계가 된다. 이처럼 N:M 관계에서는 하나의 데이터에 여러 관계가 연결되기 때문에 JOIN 결과의 행 수가 더 쉽게 늘어날 수 있다. 결국 JOIN 결과의 행 수를 이해하려면 단순히 테이블에 몇 행이 있는지만 볼 것이 아니라, 각 행이 상대 테이블에서 몇 개의 행과 매칭되는지 를 함께 봐야 한다. 4. 같은 값이 반복되면 무조건 중복일까? JOIN 결과에서 같은 값이 여러 번 보이면 가장 먼저 중복 이라는 생각이 들기 쉽다. 하지만 다음 두 결과를 비교해보면 차이를 알 수 있다. 달빛 도서관 | 소설 달빛 도서관 | 추천 책 제목은 반복되지만 태그가 다르다. 각 행은 서로 다른 관계를 표현하기 때문에 두 행 모두 의미가 있다. 이것은 정상적인 반복 이다. 반대로 JOIN의 ON 조건을 잘못 작성하면 실제 관계와 맞지 않는 데이터까지 연결될 수 있다. JOIN에서 중요한 것은 다음과 같이 ERD의 PK·FK 관계에 맞는 조건을 사용하는 것이다. JOIN book_tag bt ON b.book_id = bt.book_id 그리고 다시 tag 와 연결할 때도 실제 FK 관계를 따른다. JOIN tag t ON bt.tag_id = t.tag_id 만약 이러한 관계를 무시하고 잘못된 조건으로 JOIN하면 의도하지 않은 행 조합이 생성되고, 결과 행 수가 예상보다 크게 증가할 수 있다. 따라서 JOIN 결과가 예상보다 많다면 바로 데이터를 지우거나 중복 제거부터 하기보다 다음 순서로 확인하는 것이 좋다. ERD 관계 확인 ↓ PK / FK 확인 ↓ ON 조건 확인 ↓ 각 결과 행이 어떤 관계를 표현하는지 확인 5. 중복처럼 보인다고 DISTINCT부터 쓰면 될까? 여기부터는 이번 실습에서 더 궁금해져 추가로 정리한 내용이다. SQL에는 중복 행을 제거할 수 있는 DISTINCT 가 있다. 예를 들어 다음처럼 작성할 수 있다. SELECT DISTINCT b.title FROM book b JOIN book_tag bt ON b.book_id = bt.book_id JOIN tag t ON bt.tag_id = t.tag_id WHERE b.book_id = 1; 이렇게 하면 결과는 책 제목 하나만 남는다. 달빛 도서관 겉으로 보면 깔끔해 보인다. 하지만 원래 알고 싶었던 것이 책의 태그 목록이라면 문제가 생긴다. 기존 결과는 달빛 도서관 | 소설 달빛 도서관 | 추천 처럼 각각의 태그 관계를 표현하고 있었다. 그런데 단순히 중복처럼 보인다는 이유로 필요한 컬럼을 빼거나 DISTINCT 를 사용해 결과를 줄여버리면, 실제로 필요한 관계 정보까지 사라질 수 있다. 따라서 DISTINCT 는 중복이 보이니까 일단 제거하는 도구 로 사용하기보다, 최종 결과에서 정말 동일한 행을 하나만 남기는 것이 요구사항에 맞는가? 를 먼저 판단한 뒤 사용하는 것이 중요하다. 즉 JOIN 결과가 많다고 해서 항상 DISTINCT 가 해결책인 것은 아니다. 6. 좋아요 여부를 JOIN하지 않고 EXISTS로 확인한 이유 이번 미션 3에서는 태그뿐만 아니라 현재 사용자가 해당 책에 좋아요를 눌렀는지도 함께 조회했다. 실제 쿼리는 다음과 같이 작성했다. SELECT b.title, t.name AS tag_name, EXISTS ( SELECT 1 FROM book_like bl WHERE bl.book_id = b.book_id AND bl.user_id = 1 ) AS is_liked FROM book b JOIN book_tag bt ON b.book_id = bt.book_id JOIN tag t ON bt.tag_id = t.tag_id WHERE b.book_id = 1; 여기서 태그 정보는 JOIN을 사용했지만 좋아요 여부는 EXISTS 를 사용했다. EXISTS 는 조건을 만족하는 행이 존재하는지를 확인한다. 이번 요구사항에서 필요한 것은 좋아요 데이터 자체를 여러 건 조회하는 것 이 아니라 현재 사용자가 이 책을 좋아요 했는가? 라는 존재 여부다. 따라서 book_like 의 행 자체를 결과에 펼치기보다 해당 관계가 존재하는지만 확인하는 방식이 자연스럽다. 좋아요 기록 존재 → 1 좋아요 기록 없음 → 0 실제 결과는 다음과 같이 나타났다. 달빛 도서관 | 소설 | 1 달빛 도서관 | 추천 | 1 여기서 좋아요 여부가 두 번 보이는 이유 역시 태그 관계가 두 개이기 때문이다. EXISTS 가 두 개의 좋아요 데이터를 만든 것이 아니라, 이미 태그 JOIN으로 두 행이 만들어졌고 각 행에서 같은 좋아요 존재 여부를 확인한 것이다. 이 부분을 통해 JOIN을 사용할지 다른 방식으로 관계를 확인할지는 최종적으로 어떤 데이터를 결과 행으로 펼치고 싶은가 에 따라 달라질 수 있다는 점도 알게 되었다. 7. JOIN을 볼 때는 결과보다 관계를 먼저 보자 1주차에서는 ERD를 설계하며 테이블 사이의 관계를 정리했다. 2주차에는 그 관계를 실제 SQL로 조회하면서 ERD의 카디널리티가 실제 SQL 결과의 형태로 나타난다. 는 것을 확인할 수 있었다. 처음에는 다음 결과만 보고 달빛 도서관 | 소설 달빛 도서관 | 추천 책 제목이 중복되었다고 생각했다. 하지만 ERD를 함께 보면 book 1 : N book_tag 관계이기 때문에 같은 책이 여러 행에 나타나는 것이 자연스럽다는 것을 바로 이해할 수 있다. 그래서 앞으로 JOIN 쿼리를 작성하거나 결과를 확인할 때는 쿼리만 보지 않고 다음을 먼저 확인하려고 한다. 기준 테이블은 무엇인가? 어떤 PK와 FK가 연결되어 있는가? 관계가 1:1, 1:N, N:M 중 무엇인가? 기준 행 하나에 몇 개의 상대 행이 매칭될 수 있는가? 현재 결과의 한 행은 어떤 관계를 의미하는가? 이 과정을 먼저 생각하면 결과 행이 예상보다 많을 때도 단순히 중복 오류 라고 판단하지 않고 원인을 더 정확하게 찾을 수 있다. 마무리 이번 주차에서 JOIN을 직접 사용하기 전에는 JOIN을 단순히 여러 테이블을 하나로 연결하는 SQL 문법 정도로 생각했다. 하지만 실제로 조회 결과를 확인하면서 JOIN은 단순히 테이블을 옆으로 붙이는 것이 아니라, 테이블 사이에 존재하는 관계를 결과 행으로 펼쳐 보여주는 과정 에 더 가깝다고 느꼈다. 특히 1:N 관계에서는 하나의 기준 데이터가 여러 데이터와 연결되어 있기 때문에 같은 값이 여러 행에 반복될 수 있다. 이 반복은 항상 잘못된 중복이 아니다. 중요한 것은 각 행이 실제로 서로 다른 관계를 나타내고 있는지를 확인하는 것이다. 또한 결과가 많다고 바로 DISTINCT 로 제거하기보다, ERD의 관계와 ON 조건을 먼저 확인해야 한다는 점도 알게 되었다. 이번 실습을 통해 가장 기억에 남은 내용을 한 문장으로 정리하면 다음과 같다. JOIN 결과의 행 수는 단순히 원본 테이블의 행 수가 아니라, 데이터 사이에 존재하는 관계의 수와 카디널리티에 의해 결정될 수 있다. 앞으로 JOIN 결과가 예상과 다르게 나온다면 ERD 관계 → PK/FK → 카디널리티 → ON 조건 → 결과 행의 의미 순서로 확인해보려고 한다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
[UMC 블로그챌린지] 2주차 - JOIN 결과는 왜 늘어날까? 카디널리티와 중복 행의 원리. 이번 주차에서는 1주차에 설계했던 ERD를 MySQL 테이블과 더미 데이터로 구현하고, SELECT , WHERE , JOIN , ORDER BY , LIMIT 등을 사용해 화면에 필요한 데이터를 직접 조회해보았다. 그중 가장 궁금했던 부분은 JOIN 이었다. 미션 3에서 특정 책의 태그 목록을 조회하기 위해 book , book_tag , tag 테이블을 JOIN했는데, 책은 하나인데 같은 책 제목이 여러 줄 나타났다. 처음에는 JOIN을 잘못해서 데이터가 중복된 건가? 라고 생각했다. 하지만 ERD의 관계를 다시 확인해보니, 이 결과는 오류가 아니라 1:N 관계가 JOIN 결과에 그대로 펼쳐진 것 이었다.…
Открыть источник