> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chainstream.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 메트릭 및 집계

> count, sum, avg, min, max, uniq로 데이터 집계 — of 파라미터, HAVING을 위한 selectWhere, metrics와 사전 집계 Cubes의 사용 시점

## 메트릭이란?

메트릭은 Cube Record 타입의 필드로 제공되는 **집계 함수**입니다. GraphQL 쿼리 안에서 직접 통계를 계산해 후처리 없이 쓸 수 있습니다. 차원 필드와 함께 메트릭 필드를 선택하면, 선택한 차원으로 그룹화한 뒤 각 그룹에 대해 메트릭을 계산합니다.

**지원 메트릭:**

| 메트릭     | SQL에 대응                       | 설명            |
| :------ | :---------------------------- | :------------ |
| `count` | `COUNT(*)` 또는 `COUNT(column)` | 행 수 또는 고유 값 수 |
| `sum`   | `SUM(column)`                 | 숫자 합계         |
| `avg`   | `AVG(column)`                 | 숫자 평균         |
| `min`   | `MIN(column)`                 | 최솟값           |
| `max`   | `MAX(column)`                 | 최댓값           |
| `uniq`  | `COUNT(DISTINCT column)`      | 고유 값 개수       |

***

## Record 타입의 메트릭 필드

메트릭은 각 Cube의 Record 타입 **최상위 필드**로 나타납니다. 모든 Cube가 모든 메트릭을 지원하는 것은 아니며, Cube 정의에 따릅니다.

```graphql theme={null}
type DEXTradesRecord {
  # Dimension fields...
  Block { ... }
  Trade { ... }

  # Metric fields
  count: Int
  sum(of: DEXTradesSumOf!): Float
}
```

메트릭을 쓰려면 필드 선택에 포함하면 됩니다:

```graphql theme={null}
query {
  Solana {
    DEXTrades(
      tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      where: { Block: { Time: { after: "2025-03-27T00:00:00Z" } } }
    ) {
      count
      Trade { Dex { ProtocolName } }
    }
  }
}
```

이 쿼리는 DEX 거래를 프로토콜 이름별로 그룹화하고 각 그룹의 건수를 반환합니다.

***

## `of` 파라미터

`sum`, `avg`, `min`, `max`, `uniq` 같은 메트릭은 **어떤 차원을 집계할지** 지정하는 `of` 파라미터가 필요합니다. `of` 값은 차원 경로 명명 규칙에 따라 Cube마다 생성되는 enum입니다.

```graphql theme={null}
sum(of: Trade_Buy_Amount)
avg(of: Trade_Buy_PriceInUSD)
min(of: Block_Time)
max(of: Trade_Sell_Amount)
uniq(of: Trade_Buy_Account_Owner)
```

### 예: DEX별 총 매수 볼륨

```graphql theme={null}
query {
  Solana {
    DEXTrades(
      tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      where: { Block: { Time: { after: "2025-03-27T00:00:00Z" } } }
    ) {
      count
      sum(of: Trade_Buy_Amount)
      Trade { Dex { ProtocolName } }
    }
  }
}
```

**응답:**

```json theme={null}
{
  "data": {
    "Solana": {
      "DEXTrades": [
        {
          "count": 1842,
          "sum": 2847291.45,
          "Trade": { "Dex": { "ProtocolName": "Raydium" } }
        },
        {
          "count": 923,
          "sum": 1293847.12,
          "Trade": { "Dex": { "ProtocolName": "Orca" } }
        }
      ]
    }
  }
}
```

***

## count — 행 세기

`of` 없이 쓰는 `count` 는 각 그룹의 총 행 수를 셉니다(`COUNT(*)` 와 동일):

```graphql theme={null}
query {
  Solana {
    DEXTrades(
      where: { Block: { Time: { after: "2025-03-27T00:00:00Z" } } }
    ) {
      count
    }
  }
}
```

차원 필드와 함께 쓰면 그룹별 건수를 반환합니다:

```graphql theme={null}
query {
  Solana {
    DEXTrades(
      where: { Block: { Time: { after: "2025-03-27T00:00:00Z" } } }
    ) {
      count
      Trade { Dex { ProtocolName } }
    }
  }
}
```

***

## uniq — 고유 개수

`uniq` 는 SQL의 `COUNT(DISTINCT column)` 에 대응합니다. 차원의 고유 값 개수를 셀 때 사용합니다:

```graphql theme={null}
query {
  Solana {
    DEXTrades(
      tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      where: { Block: { Time: { after: "2025-03-27T00:00:00Z" } } }
    ) {
      uniq(of: Trade_Buy_Account_Owner)
    }
  }
}
```

오늘 Solana에서 USDC를 거래한 **고유 매수 지갑** 수를 반환합니다.

***

## selectWhere — HAVING 스타일 필터

`selectWhere` 는 SQL의 `HAVING` 과 비슷하게 **집계 결과**에 대해 필터합니다. 그룹화·집계 이후에 적용되며, 메트릭 값으로 그룹을 걸러 냅니다.

```graphql theme={null}
query {
  Solana {
    DEXTrades(
      where: { Block: { Time: { after: "2026-04-01T00:00:00Z" } } }
    ) {
      count(selectWhere: { gt: "100" })
      Trade { Dex { ProtocolName } }
    }
  }
}
```

**거래가 100건을 넘은** DEX 프로토콜만 반환하고, 그보다 적은 프로토콜은 결과에서 제외됩니다.

<Note>
  `selectWhere` 값은 반드시 **문자열**로 넘깁니다(예: `"100"` 이며 `100` 이 아님). 내부적으로 숫자로 파싱됩니다.
</Note>

`selectWhere` 가 지원하는 비교 연산자:

| 연산자  | 설명    |
| :--- | :---- |
| `gt` | 보다 큼  |
| `ge` | 이상    |
| `lt` | 보다 작음 |
| `le` | 이하    |
| `eq` | 같음    |

<Warning>
  **알려진 제한:** `selectWhere` 를 쓸 때 `orderBy` 는 암시적 GROUP BY 에 포함된 차원(선택 중인 필드)이나 집계 결과를 참조해야 합니다. GROUP BY 에 없는 필드(예: `Block_Time`)로 정렬하면 DB 오류가 납니다.
</Warning>

***

## 실전 예: 상위 트레이더

오늘 특정 토큰에 대해 거래 건수 기준 상위 10지갑을 찾고, 총 매수 볼륨과 고유 거래 수를 표시:

```graphql theme={null}
query TopTraders {
  Solana {
    DEXTrades(
      tokenAddress: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"
      where: {
        Block: { Time: { after: "2025-03-27T00:00:00Z" } }
      }
      orderBy: {descending: Block_Time}
      limit: { count: 10 }
    ) {
      count
      sum(of: Trade_Buy_Amount)
      Trade {
        Buy {
          Account { Owner }
        }
      }
    }
  }
}
```

**응답:**

```json theme={null}
{
  "data": {
    "Solana": {
      "DEXTrades": [
        {
          "count": 47,
          "sum": 892341023.5,
          "Trade": {
            "Buy": {
              "Account": { "Owner": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU" }
            }
          }
        },
        {
          "count": 31,
          "sum": 451203847.2,
          "Trade": {
            "Buy": {
              "Account": { "Owner": "3kMq5RezM9XBbBGRNxP9vXkJHAfG8S7gn5WfBsHFQr7T" }
            }
          }
        }
      ]
    }
  }
}
```

***

## 여러 메트릭 조합

한 쿼리에서 여러 메트릭 필드를 함께 선택할 수 있습니다:

```graphql theme={null}
query {
  Solana {
    DEXTrades(
      tokenAddress: "So11111111111111111111111111111111111111112"
      where: {
        Block: { Time: { after: "2025-03-27T00:00:00Z" } }
      }
    ) {
      count
      sum(of: Trade_Buy_Amount)
      min(of: Trade_Buy_PriceInUSD)
      max(of: Trade_Buy_PriceInUSD)
      uniq(of: Trade_Buy_Account_Owner)
      Trade { Dex { ProtocolName } }
    }
  }
}
```

DEX별로 거래 건수, 총 볼륨, 가격 범위, 고유 트레이더 수를 한 번에 반환합니다.

***

## 메트릭 vs 사전 집계 Cube

<Info>
  자주 묻는 질문: DWD Cube에 메트릭을 쓸까, DWM/DWS Cube를 직접 조회할까?
</Info>

| 접근                                                | 언제 쓰나                   | 성능                    |
| :------------------------------------------------ | :---------------------- | :-------------------- |
| **DWD Cube의 메트릭**(예: `DEXTrades.count`)           | 맞춤 집계, 임시 그룹화, 유연한 시간 창 | 느림 — 쿼리 시점에 원시 이벤트 집계 |
| **DWM Cube**(예: `Pairs`, `Tokens`)                | 표준 시계열, OHLC 차트, 시간별 볼륨 | 빠름 — 분 단위 사전 롤업 읽기    |
| **DWS Cube**(예: `TokenHolders`, `WalletTokenPnL`) | 현재 스냅샷, 누적 합계, 리더보드     | 가장 빠름 — 사전 집계 요약 데이터  |

<Tip>
  **경험칙**: 유스케이스가 DWM/DWS로 커버되면 그쪽을 쓰세요 — 사전 집계라 훨씬 빠릅니다. 사전 빌드 Cube가 지원하지 않는 맞춤 그룹화·집계가 필요할 때만 DWD + 메트릭으로 돌아가세요.
</Tip>

### 선택 가이드

<AccordionGroup>
  <Accordion title="캔들스틱 차트가 필요할 때">
    **Pairs**(DWM)를 쓰세요. 분당 시가/고가/저가/종가/볼륨이 이미 계산되어 있습니다. DEXTrades를 직접 집계할 필요가 없습니다.
  </Accordion>

  <Accordion title="토큰별 매수/매도 건수가 필요할 때">
    **Tokens**(DWM)를 쓰세요. 분당 거래 건수, 볼륨, 고유 트레이더를 미리 집계합니다.
  </Accordion>

  <Accordion title="토큰 홀더 순위가 필요할 때">
    **TokenHolders**(DWS)를 쓰세요. 홀더별 최신 잔액이 미리 계산되어 BalanceUpdates를 집계하는 것보다 훨씬 빠릅니다.
  </Accordion>

  <Accordion title="DEX별 볼륨을 맞춤으로 나눌 때">
    `Trade.Dex.ProtocolName` 으로 그룹화한 **DEXTrades** 에 `count` + `sum(of: Trade_Buy_Amount)` 를 쓰세요. 사전 빌드 Cube에 없는 형태이므로 DWD 메트릭이 맞습니다.
  </Accordion>

  <Accordion title="지갑별 토큰 PnL이 필요할 때">
    **WalletTokenPnL**(DWS)를 쓰세요. 지갑–토큰 페어당 매수/매도 볼륨과 거래 건수가 미리 계산되어 있습니다.
  </Accordion>
</AccordionGroup>

***

## 다음 단계

<CardGroup cols={2}>
  <Card title="데이터 Cube" icon="cubes" href="/ko/graphql/schema/cubes">
    25개 Cube와 필드 구조를 살펴봅니다.
  </Card>

  <Card title="쿼리 예시" icon="flask" href="/ko/graphql/examples/dex-trades">
    메트릭과 집계를 쓰는 실전 쿼리 예시를 확인합니다.
  </Card>
</CardGroup>
