Algorithm / / 2025. 3. 29. 21:38

[LeetCode SQL] 585: Investments in 2016 - 코드 리뷰 및 분석

LeetCode SQL 585: Investments in 2016 - 코드 리뷰 및 분석

 

제출하신 MySQL 쿼리 코드는 문제의 요구사항을 정확히 충족하며 정상적으로 작동합니다. 로직은 명확하며, JOINGROUP BY를 효과적으로 사용하여 두 가지 조건을 만족하는 정책 보유자를 필터링하고 있습니다.

# Write your MySQL query statement below


select round(sum(tiv_2016), 2) as tiv_2016
from insurance
join (
    select tiv_2015
    from insurance
    group by tiv_2015 having count(tiv_2015) > 1
) t1 using(tiv_2015)
join (
    select lat, lon
    from insurance
    group by lat, lon having count(lon) = 1
) t2 using(lat, lon)

코드 분석:

 

  1. FROM insurance: 기본 테이블을 insurance로 지정합니다.
  2. JOIN (...) t1 using(tiv_2015): 첫 번째 JOINtiv_2015 값을 기준으로 합니다.
    • t1 서브쿼리: select tiv_2015 from insurance group by tiv_2015 having count(tiv_2015) > 1
    • 목적: insurance 테이블에서 tiv_2015 값이 2번 이상 나타나는 (즉, 다른 사람과 동일한 tiv_2015 값을 가지는) tiv_2015 값들을 찾습니다.
    • 결과: 이 JOIN을 통해 insurance 테이블에서 tiv_2015 값이 다른 사람과 공유되는 행들만 남게 됩니다. (문제의 첫 번째 조건 충족)
  3. JOIN (...) t2 using(lat, lon): 두 번째 JOINlatlon 값을 기준으로 합니다.
    • t2 서브쿼리: select lat, lon from insurance group by lat, lon having count(lon) = 1 (여기서 count(lon) 대신 count(*) 또는 count(lat)를 사용해도 동일하며, 가독성 측면에서 count(*)가 더 일반적일 수 있습니다.)
    • 목적: insurance 테이블에서 (lat, lon) 조합이 유니크한 (즉, 다른 사람과 같은 위치에 있지 않은) 위치 정보들을 찾습니다.
    • 결과: 이 JOIN을 통해 이전 단계에서 필터링된 결과 중 위치 (lat, lon)가 유니크한 행들만 남게 됩니다. (문제의 두 번째 조건 충족)
  4. select round(sum(tiv_2016), 2) as tiv_2016: 최종적으로 필터링된 행들의 tiv_2016 값을 합산하고, 그 결과를 소수점 둘째 자리까지 반올림하여 tiv_2016이라는 별칭으로 출력합니다.

 

결론: 코드는 논리적으로 정확하며 문제의 요구사항을 잘 반영하고 있습니다.

 

 


다른 방식의 솔루션

 

문제 해결에는 여러 가지 접근 방식이 가능합니다. 다음은 몇 가지 대안적인 SQL 솔루션입니다.

 

1. 윈도우 함수 (Window Functions) 사용:

 

윈도우 함수 COUNT() OVER()를 사용하면 서브쿼리와 조인 없이 한 번의 스캔으로 각 행에 대해 조건을 확인할 수 있어 더 효율적일 수 있습니다.

SELECT
    ROUND(SUM(tiv_2016), 2) AS tiv_2016
FROM (
    SELECT
        tiv_2016,
        -- 각 tiv_2015 값의 전체 개수를 계산
        COUNT(*) OVER (PARTITION BY tiv_2015) as tiv_count,
        -- 각 (lat, lon) 조합의 전체 개수를 계산
        COUNT(*) OVER (PARTITION BY lat, lon) as loc_count
    FROM
        Insurance
) AS SubQuery
WHERE
    -- tiv_2015 값이 1개 초과 (다른 사람과 공유)하고
    tiv_count > 1
    -- (lat, lon) 조합이 1개 (유니크한 위치)인 경우
    AND loc_count = 1;

 

  • 장점: 코드가 간결하고, 데이터베이스가 테이블을 여러 번 스캔할 필요가 줄어들어 성능상 이점이 있을 수 있습니다.
  • 단점: 윈도우 함수를 지원하는 SQL 버전에서만 사용 가능합니다. (대부분의 최신 RDBMS는 지원)

 

2. WHERE 절과 IN / NOT IN 사용:

 

서브쿼리를 JOIN 대신 WHERE 절의 IN 조건과 결합하여 사용할 수 있습니다.

SELECT
    ROUND(SUM(tiv_2016), 2) AS tiv_2016
FROM
    Insurance
WHERE
    -- 조건 1: tiv_2015 값이 다른 사람들과 공유되는 값 목록에 포함되어야 함
    tiv_2015 IN (
        SELECT tiv_2015
        FROM Insurance
        GROUP BY tiv_2015
        HAVING COUNT(*) > 1
    )
    AND
    -- 조건 2: (lat, lon) 조합이 유니크한 위치 목록에 포함되어야 함
    -- (MySQL, PostgreSQL 등 튜플 비교 지원)
    (lat, lon) IN (
        SELECT lat, lon
        FROM Insurance
        GROUP BY lat, lon
        HAVING COUNT(*) = 1
    );

 

  • 장점: 각 조건을 명확하게 WHERE 절로 표현할 수 있습니다.
  • 단점: 서브쿼리의 결과 집합이 클 경우 IN 연산자의 성능이 JOIN 이나 EXISTS 보다 떨어질 수 있습니다. (lat, lon) 튜플 비교는 모든 RDBMS에서 지원되지 않을 수 있습니다.

 

3. WHERE 절과 EXISTS / NOT EXISTS 사용:

 

EXISTSNOT EXISTS를 사용하여 각 행에 대해 조건 충족 여부를 확인할 수 있습니다. 이는 상관 서브쿼리(Correlated Subquery)를 활용하는 방식입니다.

SELECT
    ROUND(SUM(i1.tiv_2016), 2) AS tiv_2016
FROM
    Insurance i1
WHERE
    -- 조건 1: i1과 다른 pid를 가지면서 tiv_2015 값이 같은 행이 존재하는가?
    EXISTS (
        SELECT 1
        FROM Insurance i2
        WHERE i2.tiv_2015 = i1.tiv_2015 AND i2.pid <> i1.pid
    )
    AND
    -- 조건 2: i1과 다른 pid를 가지면서 lat, lon 값이 같은 행이 존재하지 않는가?
    NOT EXISTS (
        SELECT 1
        FROM Insurance i3
        WHERE i3.lat = i1.lat AND i3.lon = i1.lon AND i3.pid <> i1.pid
    );

 

  • 장점: IN 보다 성능상 이점이 있는 경우가 많으며, SQL 표준에 잘 부합합니다. 조건의 의미를 직접적으로 표현합니다.
  • 단점: 코드가 조금 더 길어질 수 있습니다.

 

요약:

제출하신 코드는 좋은 솔루션입니다. 다른 대안들 (특히 윈도우 함수나 EXISTS)은 특정 상황에서 가독성이나 성능 면에서 장점을 가질 수 있으므로, 다양한 SQL 작성 방식을 알아두는 것이 좋습니다. LeetCode와 같은 환경에서는 여러 솔루션이 모두 정답으로 처리될 가능성이 높습니다.

 

 


 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유