DAB(데이터 API 작성기)는 SQL 제품군 데이터베이스 및 Azure Synapse Analytics(전용 SQL 풀)에 대한 GraphQL 집계 및 그룹화를 지원합니다. 집계를 사용하면 사용자 지정 API 코드를 작성하지 않고도 숫자 필드 및 그룹 결과를 요약할 수 있습니다. 집계 및 groupBy NoSQL, PostgreSQL 또는 MySQL용 Azure Cosmos DB에 사용할 수 없습니다.
필수 조건
- 지원되는 데이터베이스:
- SQL Server 2016 이상
- Azure SQL 데이터베이스
- Azure SQL 관리형 인스턴스
- Microsoft Fabric SQL
- Azure Synapse Analytics(전용 SQL 풀에만 해당)
- 데이터 API 작성기 CLI. CLI 설치
- GraphQL을 통해 엔터티가 노출되는 DAB 구성 파일입니다.
- 쿼리를 실행하는 GraphQL 클라이언트(예: 바나나 케이크 팝 또는 GraphQL Playground)입니다.
지원되는 데이터베이스
| 데이터베이스 | 집계 지원 |
|---|---|
| SQL Server / Azure SQL / Microsoft Fabric SQL | ✅ 예 |
| Azure Synapse(전용 SQL 풀) | ✅ 예 |
| Azure Synapse(서버리스 SQL 풀) | ❌ 아니요 |
| PostgreSQL | ❌ 아니요 |
| MySQL | ❌ 아니요 |
| NoSQL용 Azure Cosmos DB | ❌ 아니요 |
집계 함수
DAB는 다음 집계 함수를 지원합니다.
| 기능 | 적용 대상 | Description |
|---|---|---|
sum |
숫자 필드만 | 모든 값의 합계 |
average |
숫자 필드만 | 모든 값의 평균 |
min |
숫자 필드만 | 최솟값 |
max |
숫자 필드만 | 최대값 |
count |
모든 필드 | null이 아닌 값의 수 |
Constraints
-
sum,average및minmax숫자 데이터 형식(int, decimal, float 등)에서만 작동합니다. -
count는 문자열 및 날짜를 비롯한 모든 데이터 형식에서 작동합니다. - 테이블에 숫자 열이 없는 경우 DAB는 해당 엔터티에 대한 집계 노드를 생성하지 않습니다. 숫자가 아닌 필드에는 계속 사용할
count수 있습니다.
선택적 한정자
| 수식어 | 목적 | 예시 |
|---|---|---|
distinct: true |
고유 값만 계산 | 고유 고객 수 |
having: { ... } |
집계 후 그룹 필터링 | 합계 > 가 1000인 그룹 표시 |
DAB 런타임 실행
GraphQL 엔드포인트를 사용할 수 있도록 구성 파일로 DAB를 시작합니다.
dab start
쿼리 집계된 결과
이 섹션에서는 테이블 스키마, GraphQL 쿼리, 생성된 SQL 및 JSON 응답을 보여 주는 전체 예제를 안내합니다.
테이블 스키마
CREATE TABLE books (
id INT PRIMARY KEY,
title NVARCHAR(200),
year INT,
pages INT
);
GraphQL 쿼리
GraphQL을 사용하여 행을 그룹화하고 숫자 필드에 대한 집계 값을 반환합니다.
{
books(
groupBy: { fields: ["year"] }
) {
items {
year
}
aggregates {
pages {
sum
average
min
max
}
}
}
}
-
groupBy.fields는 지정된 열별로 행을 그룹화합니다. -
aggregates는 숫자 필드(예pages: )에 대한 집계 함수를 노출합니다. - GraphQL 스키마는 해당 스키마를 지원하는 필드에 대한 집계만 노출합니다. 클라이언트에서 스키마 내성 검사를 사용하여 사용 가능한 집계 필드 및 함수를 확인합니다.
생성된 SQL
DAB는 GraphQL 쿼리를 T-SQL로 변환합니다.
SELECT
[year],
SUM([pages]) AS [sum],
AVG([pages]) AS [average],
MIN([pages]) AS [min],
MAX([pages]) AS [max]
FROM [dbo].[books]
GROUP BY [year]
FOR JSON PATH, INCLUDE_NULL_VALUES
JSON 응답
{
"data": {
"books": {
"items": [
{ "year": 2023 },
{ "year": 2024 }
],
"aggregates": {
"pages": [
{ "sum": 3200, "average": 320, "min": 120, "max": 450 },
{ "sum": 4500, "average": 300, "min": 140, "max": 510 }
]
}
}
}
}
items 및 aggregates 배열은 인덱스별로 정렬되어 있으며, aggregates.pages의 첫 번째 요소는 items의 첫 번째 그룹에 해당합니다.
그룹화하지 않고 집계
groupBy을(를) 생략하면 모든 행에서 집계를 계산합니다.
GraphQL 쿼리
{
books {
aggregates {
pages {
sum
average
min
max
count
}
id {
count
}
}
}
}
생성된 SQL
SELECT
SUM([pages]) AS [sum],
AVG([pages]) AS [average],
MIN([pages]) AS [min],
MAX([pages]) AS [max],
COUNT([pages]) AS [count],
COUNT([id]) AS [count]
FROM [dbo].[books]
FOR JSON PATH, INCLUDE_NULL_VALUES
JSON 응답
{
"data": {
"books": {
"aggregates": {
"pages": {
"sum": 15420,
"average": 308,
"min": 120,
"max": 850,
"count": 50
},
"id": {
"count": 50
}
}
}
}
}
groupBy 없이, 모든 행이 하나의 결과로 축소되므로 응답은 배열이 아닌 단일 객체를 반환합니다.
하나 이상의 필드로 그룹화
하나 이상의 열을 기준으로 행을 그룹화하고 그룹당 집계를 반환합니다.
테이블 스키마
CREATE TABLE sales (
id INT PRIMARY KEY,
year INT,
category NVARCHAR(50),
revenue DECIMAL(10,2),
quantity INT
);
GraphQL 쿼리
{
sales(
groupBy: { fields: ["year", "category"] }
) {
items {
year
category
}
aggregates {
revenue {
sum
average
}
quantity {
sum
}
}
}
}
생성된 SQL
SELECT
[year],
[category],
SUM([revenue]) AS [sum],
AVG([revenue]) AS [average],
SUM([quantity]) AS [sum]
FROM [dbo].[sales]
GROUP BY [year], [category]
FOR JSON PATH, INCLUDE_NULL_VALUES
JSON 응답
{
"data": {
"sales": {
"items": [
{ "year": 2023, "category": "Books" },
{ "year": 2023, "category": "Electronics" },
{ "year": 2024, "category": "Books" }
],
"aggregates": {
"revenue": [
{ "sum": 45000.00, "average": 150.00 },
{ "sum": 120000.00, "average": 600.00 },
{ "sum": 52000.00, "average": 173.33 }
],
"quantity": [
{ "sum": 300 },
{ "sum": 200 },
{ "sum": 300 }
]
}
}
}
}
응답은 items에 대한 배열 및 집계를 동일한 순서로 반환하여 그룹을 집계된 값과 정렬할 수 있도록 합니다.
집계된 결과를 필터링해야 하는 경우
집계 후 그룹을 필터링하는 데 사용합니다 having . 이는 SQL의 HAVING 절과 동일합니다.
테이블 스키마
CREATE TABLE products (
id INT PRIMARY KEY,
category NVARCHAR(50),
price DECIMAL(10,2)
);
GraphQL 쿼리
{
products(
groupBy: { fields: ["category"] }
) {
items { category }
aggregates {
price {
sum(having: { gt: 10000 })
average
}
}
}
}
생성된 SQL
SELECT
[category],
SUM([price]) AS [sum],
AVG([price]) AS [average]
FROM [dbo].[products]
GROUP BY [category]
HAVING SUM([price]) > 10000
FOR JSON PATH, INCLUDE_NULL_VALUES
JSON 응답
합계가 10000을 초과하는 범주만 반환됩니다.
{
"data": {
"products": {
"items": [
{ "category": "Electronics" },
{ "category": "Furniture" }
],
"aggregates": {
"price": [
{ "sum": 15000.00, "average": 300.00 },
{ "sum": 12000.00, "average": 400.00 }
]
}
}
}
}
HAVING 연산자
| Operator | SQL 등가 | 예시 |
|---|---|---|
eq |
= |
having: { eq: 100 } |
neq |
<> |
having: { neq: 0 } |
gt |
> |
having: { gt: 1000 } |
gte |
>= |
having: { gte: 500 } |
lt |
< |
having: { lt: 100 } |
lte |
<= |
having: { lte: 50 } |
비고
각 having 필터는 집계 함수에 독립적으로 적용됩니다. 단일 GraphQL 쿼리에서는 "합계 > 1000 또는 개수 < 10"와 같은 교차 집계 조건을 만들 수 없습니다.
집계의 DISTINCT
를 사용하여 distinct: true고유 값 개수 계산
테이블 스키마
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT,
product_id INT
);
GraphQL 쿼리
{
orders(
groupBy: { fields: ["customer_id"] }
) {
items { customer_id }
aggregates {
product_id {
count(distinct: true)
count
}
}
}
}
생성된 SQL
SELECT
[customer_id],
COUNT(DISTINCT [product_id]) AS [count],
COUNT([product_id]) AS [count]
FROM [dbo].[orders]
GROUP BY [customer_id]
FOR JSON PATH, INCLUDE_NULL_VALUES
JSON 응답
{
"data": {
"orders": {
"items": [
{ "customer_id": 101 },
{ "customer_id": 102 }
],
"aggregates": {
"product_id": [
{ "count": 5 },
{ "count": 3 }
]
}
}
}
}
첫 번째 count (with distinct: true)는 고객당 고유한 제품을 반환합니다. 두 번째는 count 총 주문을 반환합니다.
비고
동일한 필드에 여러 집계를 요청할 때 DAB는 요청된 순서대로 반환합니다. 별칭(예 uniqueProducts: count(distinct: true): )을 사용하여 응답을 자체 문서화합니다.
필터와 집계를 결합하기
그룹화하기 전에 filter를 행에 적용하고, 집계 후 having를 그룹에 적용합니다. 작업 순서를 이해하는 것이 중요합니다.
-
필터 (SQL
WHERE)는 그룹화하기 전에 행을 제거합니다 - 그룹화는 나머지 행들을 그룹으로 묶습니다.
- 집계는 그룹별로 합계, 평균, 최소값, 최대값, 그리고 개수를 계산합니다.
- 조건과 일치하지 않는 그룹을 제거합니다.
GraphQL 쿼리
{
sales(
filter: { year: { gte: 2023 } }
groupBy: { fields: ["region"] }
) {
items { region }
aggregates {
revenue { sum average }
}
}
}
생성된 SQL
SELECT
[region],
SUM([revenue]) AS [sum],
AVG([revenue]) AS [average]
FROM [dbo].[sales]
WHERE [year] >= 2023
GROUP BY [region]
FOR JSON PATH, INCLUDE_NULL_VALUES
팁 (조언)
집계 전에 행을 제외하는 데 사용합니다 filter . 집계 후 그룹을 필터링하는 데 사용합니다 having .
집계와 함께 별칭 사용
GraphQL 별칭을 사용하여 의미 있는 필드 이름을 만듭니다.
{
products(
groupBy: { fields: ["category"] }
) {
items { category }
aggregates {
price {
totalRevenue: sum
avgPrice: average
cheapest: min
mostExpensive: max
productCount: count
}
}
}
}
스키마 탐색
내부 탐색을 통해 엔터티에 사용할 수 있는 집계를 확인합니다.
{
__type(name: "BooksAggregates") {
fields {
name
type { name }
}
}
}
숫자 필드는 sum, average, min, max 및 count를 노출합니다. 숫자가 아닌 필드가 노출됩니다 count.
팁 및 제한 사항
- SQL Server, Azure SQL, Microsoft Fabric SQL 및 Azure Synapse 전용 SQL 풀에만 집계와
groupBy이(가) 적용됩니다. - 집계는 숫자 필드에서 실행됩니다.
count는 모든 필드에서 작동합니다. 숫자 열이 없는 테이블은count만 노출합니다. - 그룹화는 동일한 엔터티의 필드에 적용됩니다(엔터티 간 groupBy 없음).
- 대규모 집계는 비용이 많이 들 수 있습니다. 가능한 경우 그룹화하기 전에 groupBy 열을 인덱싱하고 행을 필터링합니다.
- 자주 사용하는
groupBy열에 인덱스를 만들어 쿼리 성능을 향상시킵니다.
Troubleshooting
오류: 필드는 집계를 지원하지 않습니다.
원인: 숫자가 아닌 필드에 sum, average, min, 또는 max를 사용한 경우.
해결 방법:
- 스키마 탐색을 사용하여 필드 유형을 확인합니다.
- 숫자가 아닌 필드에 사용합니다
count. - 사용자 지정 필드 이름을 사용하는 경우 필드 매핑을 확인합니다.
오류: 집계 노드를 찾을 수 없음
원인: 엔터티에 숫자 열이 없습니다.
해결 방법:
- 테이블 스키마에 숫자 열이 하나 이상 있는지 확인합니다.
- 필요한 경우 숫자가 아닌 필드에 집계를 사용합니다
count.
느린 집계 쿼리
원인: 적절한 인덱스가 없는 큰 테이블입니다.
해결 방법:
-
groupBy열에 인덱스를 만듭니다. - 집계 전에 행을 제한하는 데 사용합니다
filter. - 반환되는 그룹 수를 줄이는 데 사용합니다
having.