본문으로 건너뛰기

그룹화

프리즈마 클라이언트의 groupBy를 사용하면 하나 이상의 필드 값(예: country, country와 city)을 기준으로 레코드를 그룹화하고 특정 도시에 거주하는 사람들의 평균 연령을 찾는 것과 같이 각 그룹에 대한 집계를 수행할 수 있습니다. groupBy는 2.20.0 이상에서 GA(Generally Available)입니다.

다음 예시에서는 모든 사용자를 country 필드별로 그룹화하고 각 국가의 총 프로필 조회 수를 반환합니다.

ts
const groupUsers = await prisma.user.groupBy({
by: ['country'],
_sum: {
profileViews: true,
},
})
ts
const groupUsers = await prisma.user.groupBy({
by: ['country'],
_sum: {
profileViews: true,
},
})
결과
ts
[
{ country: 'Germany', _sum: { profileViews: 126 } },
{ country: 'Sweden', _sum: { profileViews: 0 } },
]
결과
ts
[
{ country: 'Germany', _sum: { profileViews: 126 } },
{ country: 'Sweden', _sum: { profileViews: 0 } },
]

groupBy를 사용하여 대륙별 코로나19 확진자 수를 요약하는 예시를 유튜브에서 확인할 수 있습니다.

groupBy와 필터링​

groupBy는 두 가지 수준의 필터링, where과 having을 지원합니다.

where로 레코드 필터링하기​

where을 사용하여 그룹화하기 전에 모든 레코드를 필터링할 수 있습니다.

다음 예시는 국가별로 사용자를 그룹화하고 프로필 보기를 합산하지만, 이메일 주소에 prisma.io가 들어있는 사용자만 포함합니다.

ts
const groupUsers = await prisma.user.groupBy({
by: ['country'],
where: {
email: {
contains: 'prisma.io',
},
},
_sum: {
profileViews: true,
},
})
ts
const groupUsers = await prisma.user.groupBy({
by: ['country'],
where: {
email: {
contains: 'prisma.io',
},
},
_sum: {
profileViews: true,
},
})

having으로 그룹 필터링하기​

having을 사용하여 개별 레코드가 아닌 필드의 합계 또는 평균과 같은 집계 값으로 전체 그룹을 필터링합니다.

예를 들어 다음 질의는 평균 profileViews가 100보다 큰 그룹만 반환합니다.

ts
const groupUsers = await prisma.user.groupBy({
by: ['country'],
where: {
email: {
contains: 'prisma.io',
},
},
_sum: {
profileViews: true,
},
having: {
profileViews: {
_avg: {
gt: 100,
},
},
},
})
ts
const groupUsers = await prisma.user.groupBy({
by: ['country'],
where: {
email: {
contains: 'prisma.io',
},
},
_sum: {
profileViews: true,
},
having: {
profileViews: {
_avg: {
gt: 100,
},
},
},
})

having 사용 사례​

having의 주요 사용 사례는 집계를 필터링하는 것입니다. 그룹화 하기 전에 where을 사용하여 데이터 세트의 크기를 최대한 줄이는 것이 좋습니다. 이렇게 하면 ✔ 데이터베이스가 반환해야 하는 레코드 수가 줄어들고 ✔ 색인이 사용되기 때문입니다.

예를 들어 다음 질의는 스웨덴이나 가나 출신이 아닌 모든 사용자를 그룹화합니다.

ts
const fd = await prisma.user.groupBy({
by: ['country'],
where: {
country: {
notIn: ['Sweden', 'Ghana'],
},
},
_sum: {
profileViews: true,
},
having: {
profileViews: {
_min: {
gte: 10,
},
},
},
})
ts
const fd = await prisma.user.groupBy({
by: ['country'],
where: {
country: {
notIn: ['Sweden', 'Ghana'],
},
},
_sum: {
profileViews: true,
},
having: {
profileViews: {
_min: {
gte: 10,
},
},
},
})

다음 질의는 동일한 결과를 달성하지만 그룹화 후에 가나의 사용자를 제외합니다. 이는 어떤 이점도 제공하지 않으며 권장되지 않습니다.

ts
const groupUsers = await prisma.user.groupBy({
by: ['country'],
where: {
country: {
not: 'Sweden',
},
},
_sum: {
profileViews: true,
},
having: {
country: {
not: 'Ghana',
},
profileViews: {
_min: {
gte: 10,
},
},
},
})
ts
const groupUsers = await prisma.user.groupBy({
by: ['country'],
where: {
country: {
not: 'Sweden',
},
},
_sum: {
profileViews: true,
},
having: {
country: {
not: 'Ghana',
},
profileViews: {
_min: {
gte: 10,
},
},
},
})
참고

having 내에서는 by에서 사용 가능한 집계 값 또는 필드만 필터링할 수 있습니다.

groupBy와 orderBy​

groupBy와 orderBy를 같이 사용할 때 다음과 같은 제약 조건이 있습니다.

  • by에 있는 필드에 orderBy할 수 있습니다.
  • 집계를 orderBy할 수 있습니다. (2.21.0 이상에서 미리 보기 기능)
  • skip, take와 함께 groupBy를 사용하는 경우, 질의에 반드시 orderBy가 포함되야 합니다.

집계 그룹별 정렬하기​

집계 그룹별로 정렬할 수 있습니다. 버전 2.21.0에서는 관계형 데이터베이스의 집계된 그룹과 함께 orderBy를 사용하는 것이 지원되고, 3.4.0에서는 몽고DB에서도 지원됩니다.

다음 예시에서는 해당 그룹의 사용자 수를 기준으로 각 city 그룹을 정렬합니다. (가장 큰 그룹이 앞에 옴)

ts
const groupBy = await prisma.user.groupBy({
by: ['city'],
_count: {
city: true,
},
orderBy: {
_count: {
city: 'desc',
},
},
})
ts
const groupBy = await prisma.user.groupBy({
by: ['city'],
_count: {
city: true,
},
orderBy: {
_count: {
city: 'desc',
},
},
})
결과
ts
[
{ city: 'Berlin', count: { city: 3 } },
{ city: 'Paris', count: { city: 2 } },
{ city: 'Amsterdam', count: { city: 1 } },
]
결과
ts
[
{ city: 'Berlin', count: { city: 3 } },
{ city: 'Paris', count: { city: 2 } },
{ city: 'Amsterdam', count: { city: 1 } },
]

필드별 정렬하기​

다음 질의는 국가별로 그룹을 정렬하는데, 처음 두 그룹은 건너뛰고 세 번째와 네 번째 그룹을 반환합니다.

ts
const groupBy = await prisma.user.groupBy({
by: ['country'],
_sum: {
profileViews: true,
},
orderBy: {
country: 'desc',
},
skip: 2,
take: 2,
})
ts
const groupBy = await prisma.user.groupBy({
by: ['country'],
_sum: {
profileViews: true,
},
orderBy: {
country: 'desc',
},
skip: 2,
take: 2,
})

자주 묻는 질문​

groupBy와 select를 함께 사용할 수 있나요?​

불가능합니다. 그러나 by에 포함된 모든 필드는 자동으로 반환됩니다.

where을 사용하는 것과 groupBy와 having을 사용하는 것의 차이점은 무엇인가요?​

where는 그룹화하기 전에 모든 레코드를 필터링합니다. having은 전체 그룹을 필터링하며 해당 그룹에 있는 특정 필드의 평균 또는 합계와 같은 집계 필드 값에 대한 필터링을 지원합니다.

groupby와 distinct의 차이점은 무엇인가요?​

distinct, groupBy 둘 다 하나 이상의 고유한 필드 값으로 레코드를 그룹화합니다. groupBy는 각 그룹 내에서 데이터를 집계할 수 있습니다. 덴마크 게시물의 평균 조회수를 반환하는 것이 그 예입니다. 반면 distinct는 그렇지 않습니다.