GROUP BY 범위 - GROUP BY beom-wi

주요 콘텐츠로 건너뛰기

이 브라우저는 더 이상 지원되지 않습니다.

최신 기능, 보안 업데이트, 기술 지원을 이용하려면 Microsoft Edge로 업그레이드하세요.

GROUP BY(Entity SQL)

  • 아티클
  • 09/22/2022
  • 읽는 데 5분 걸림

이 문서의 내용

쿼리 식(SELECT)을 통해 반환되는 개체가 배치될 그룹을 지정합니다.

구문

[ GROUP BY aliasedExpression [ ,...n ] ]

인수

aliasedExpression 그룹화가 수행되는 유효한 쿼리 식입니다. expression 은 속성일 수도 있고 FROM 절을 통해 반환되는 속성을 참조하는 비집계 식일 수도 있습니다. GROUP BY 절의 모든 식은 같지 않음을 비교할 수 있는 형식으로 계산되어야 합니다. 이런 형식은 일반적으로 숫자, 문자열, 날짜와 같은 스칼라 기본 형식입니다. 컬렉션을 기준으로 그룹화할 수 없습니다.

설명

집계 함수가 SELECT 절 <선택 목록에> 포함된 경우 GROUP BY는 각 그룹에 대한 요약 값을 계산합니다. GROUP BY가 지정된 경우, 선택 목록 내의 집계가 아닌 식에 있는 모든 속성 이름이 GROUP BY 목록에 포함되어야 하거나 아니면 GROUP BY 식이 선택 목록 식과 정확히 일치해야 합니다.

참고

ORDER BY 절이 지정되지 않은 경우에는 GROUP BY 절에 의해 반환되는 그룹에 특정 순서가 없습니다. 데이터에 특정 정렬 순서를 지정하려면 항상 ORDER BY 절을 사용하는 것이 좋습니다.

명시적으로든 암시적으로든(예: 쿼리에 HAVING 절 사용) GROUP BY 절이 지정된 경우, 현재 범위는 숨겨지고 새로운 범위가 나타납니다.

그러면 SELECT 절, HAVING 절 및 ORDER BY 절이 FROM 절에 지정된 요소 이름을 참조할 수 없게 됩니다. 그룹화 식 자체만 참조할 수 있습니다. 이렇게 하려면 각 그룹화에 새 이름(별칭)을 할당하면 됩니다. 그룹화 식에 별칭이 지정되지 않은 경우 Entity SQL 다음 예제와 같이 별칭 생성 규칙을 사용하여 별칭 생성 규칙을 생성하려고 합니다.

SELECT g1, g2, ...gn FROM c as c1

GROUP BY e1 as g1, e2 as g2, ...en as gn

GROUP BY 절의 식은 동일한 GROUP BY 절에 이전에 정의된 이름을 참조할 수 없습니다.

그룹화 이름뿐만 아니라 SELECT 절, HAVING 절 및 ORDER BY 절의 집계를 지정할 수도 있습니다. 집계에는 그룹의 각 요소에 대해 계산되는 식이 포함됩니다. 집계 연산자를 이러한 모든 식의 값을 일반적으로(항상 그렇지는 않음) 단일 값으로 줄입니다. 집계 식은 부모 범위에 나타난 원래 요소 이름을 참조하거나 GROUP BY 절에 의해 생성된 새 이름을 참조할 수 있습니다. 집계가 SELECT 절, HAVING 절, ORDER BY 절에 나타나기는 하지만 다음 예제에서 보여 주는 것처럼 그룹화 식과 동일한 범위에서 실제로 계산됩니다.

SELECT name, sum(o.Price * o.Quantity) as total

FROM orderLines as o

GROUP BY o.Product as name

이 쿼리는 GROUP BY 절을 사용하여 모든 주문 제품에 대해 제품별로 구분된 비용 보고서를 생성합니다. 여기서는 그룹화 식의 일부로서 제품에 이름 name 을 지정하며 그런 다음 이 이름을 SELECT 목록에서 참조합니다. 또한, 주문 라인의 가격과 수량을 내부적으로 참조하는 집계 sum 을 SELECT 목록에서 지정합니다.

각 GROUP BY 키 식에는 입력 범위에 대한 참조가 하나 이상 있어야 합니다.

SELECT FROM Persons as P
GROUP BY Q + P   -- GOOD
GROUP BY Q   -- BAD
GROUP BY 1   -- BAD, a constant is not allowed

GROUP BY 사용 예제는 HAVING항목을 참조하세요.

예제

다음 Entity SQL 쿼리에서는 GROUP BY 연산자를 사용하여 쿼리를 통해 반환되는 개체가 배치될 그룹을 지정합니다. 쿼리는 AdventureWorks Sales 모델을 기반으로 합니다. 이 쿼리를 컴파일하고 실행하려면 다음 단계를 수행하세요.

  1. 방법: PrimitiveType 결과를 반환하는 쿼리 실행의 절차를 따릅니다.

  2. 다음 쿼리를 ExecutePrimitiveTypeQuery 메서드에 인수로 전달합니다.

SELECT VALUE name FROM AdventureWorksEntities.Products 
    AS P GROUP BY P.Name HAVING MAX(P.ListPrice) > @price

참조

  • 엔터티 SQL 참조
  • 쿼리 식

전지적 송윤섭시점 TIL

GROUP BY 절을 보통 테이블을 스캔하고 결과를 임시 테이블에 넣어서 집계하는 과정을 거친다. 그래서 데이터 양에 따라서 매우매우 느린 쿼리가 될 수도 있다. 그래도 인덱스를 잘 설정한다면 임시 테이블을 생성하지 않고 빠르게 데이터를 가져올 수 있다.

관건은 GROUP BY 에 있는 모든 컬럼들이 바라보는 Index가 동일하고 순서도 맞아야 한다.

두가지 방법이 있는데

  1. The grouping operation is applied together with all range predicates (if any)
  2. 범위 스캔 후 결과를 집계하는 방법(First performs a range scan, and then groups the resulting tuples)

MySQL에서 GROUP BY는 Sorting을 위해 사용되는데 ORDER BY 를 최적화 할 수도 있다.

Loose Index Scan¶

  • Query는 하나의 테이블에서 동작한다.
  • GROUP BY 는 인덱스의 컬럼의 가장 왼쪽부터 지정한다. 에를 들면 t1 테이블에 인덱스가 (c1, c2, c3) 이렇게 3 개의 컬럼으로 지정되어 있다면 GROUP BY c1, c2는 Loose Index Scan이 되지만, GROUP BY c2, c3 이나 GROUP BY c1, c2, c4는 사용할 수 없다.
  • SELECT 절에서 MIN(), MAX() 를 제외하고 다른 집계함수는 사용할 수 없음. 그리고 그 컬럼은 인덱스 안에 들어가 있어야함.
  • SELECT 절에서 MIN(), MAX() 를 제외하고 다른 컬럼들은 상수여야만함.
  • For columns in the index, full column values must be indexed, not just a prefix. For example, with c1 VARCHAR(20), INDEX (c1(10)), the index cannot be used for loose index scan.

만약 Loose Index Scan이 사용된다면 EXPLAIN의 결과로 Extra 컬럼에 Using index for group-by 를 볼 수 있다.

테이블 t1(c1, c2, c3, c4) 에 (c1, c2, c3)으로 인덱스가 걸려있다면 다음 쿼리에서 Loose Index Scan이 된다.

SELECT c1, c2 FROM t1 GROUP BY c1, c2;
SELECT DISTINCT c1, c2 FROM t1;
SELECT c1, MIN(c2) FROM t1 GROUP BY c1;
SELECT c1, c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
SELECT MAX(c3), MIN(c3), c1, c2 FROM t1 WHERE c2 > const GROUP BY c1, c2;
SELECT c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
SELECT c1, c2 FROM t1 WHERE c3 = const GROUP BY c1, c2;

하지만 다음 쿼리는 만족하지 못하는 경우다.

  • MIN(), MAX() 가 아닌 다른 집계 함수가 들어가 있는 경우:

SELECT c1, SUM(c2) FROM t1 GROUP BY c1;

  • Group by 요소 중 맨 왼쪽의 요소가 들어가있지 않은 경우:

SELECT c1, c2 FROM t1 GROUP BY c2, c3;

  • Group By 부분에 컬럼들이 충분하지 않을 때:

SELECT c1, c3 FROM t1 GROUP BY c1, c2;

WHERE c3 = 'c3' 구문을 추가하면 Loose Index Scan을 사용할 수 있다.

Loose Index Scan은 MIN(), MAX()가 아닌 다른 형태의 집계 함수에도 사용가능하게 지원한다.

  • AVG(DISTINCT), SUM(DISTINCT), COUNT(DISTINCT)가 지원된다. AVG(DISTINCT), SUM(DISTINCT)는 하나의 인자를 받고, COUNT(DISTINCT)는 두개 이상의 인자를 받는다.
  • GROUP BY, DISTINCT 절이 없어야 한다.
  • 앞에서 설명한 Loose Index Scan의 제한사항이 여전히 적용된다.

다음 쿼리는 Loose Index Scan을 사용할 수 있다.

SELECT COUNT(DISTINCT c1), SUM(DISTINCT c1) FROM t1;
SELECT COUNT(DISTINCT c1, c2), COUNT(DISTINCT c2, c1) FROM t1;

Tight Index Scan¶

Tight Index Scan은 쿼리의 상태에 따라 Full Index Scan이 이루어질지 Range Index Scan이 이루어질지 결정된다.

다음 쿼리는 Loose Index Scan으로는 되지 않지만 Tight Index Scan으로 되는 쿼리다.

SELECT c1, c2, c3 FROM t1 WHERE c2 = 'a' GROUP BY c1, c3;
SELECT c1, c2, c3 FROM t1 WHERE c1 = 'a' GROUP BY c2, c3;

Refer¶

  • https://dev.mysql.com/doc/refman/5.7/en/group-by-optimization.html