가장 이른 주문일, 가장 낮은 단가, 두 값 중 작은 쪽. 이런 “가장 작은 값”이 필요할 때 쓰는 함수가 MIN입니다. 이름만 보면 더 설명할 것이 없어 보이는데, 열 하나를 받는 형태와 값 두 개를 받는 형태가 따로 있고, 빈 값을 다루는 방식이 형태마다 다르게 설명되어 있어서 처음 쓸 때 헷갈리기 쉽습니다. 이 글에서는 두 형태의 차이, 실무에서 자주 쓰는 상황 세 가지, 그리고 빈 값에서 조심할 점까지 정리합니다.
문법: 두 가지 형태
MIN( <열> )
MIN( <식1>, <식2> )
Microsoft 문서의 설명대로 MIN은 “열에서 가장 작은 값, 또는 두 식 사이에서 더 작은 값”을 돌려줍니다. 열 형태는 숫자뿐 아니라 날짜에도 쓸 수 있고, 날짜에 쓰면 가장 이른 날짜가 반환됩니다. DAX Guide는 문자열 열도 알파벳(사전) 순서로 비교한다고 설명합니다. 반환값은 입력과 같은 데이터 형식입니다.
Power BI Desktop에서 측정값으로 만들기
메뉴 이름은 Power BI 버전과 언어 설정에 따라 조금 다를 수 있다는 점을 먼저 말씀드립니다. 일반적인 순서는 이렇습니다.
- 보고서 보기에서 오른쪽 필드 창의 테이블(예: Sales)을 선택합니다.
- 상단 리본에서 “새 측정값”(영문판은 New measure)을 누릅니다.
- 수식 입력줄에
최저단가 = MIN( Sales[단가] )처럼 이름과 식을 적고 Enter를 누릅니다. - 만든 측정값을 표 시각화에 올리고 상품 같은 열과 함께 놓아 줄마다 값이 어떻게 달라지는지 확인합니다.
예제 데이터
앞선 SUM, SUMX, AVERAGE 글과 이어지는 가상 데이터입니다. 이번에는 주문일 열을 추가했습니다. 결과는 Power BI에서 돌린 화면이 아니라 직접 구한 값이니, 같은 식을 본인 데이터에 적용해서 확인해 보세요.
| 주문 | 주문일 | 상품 | 수량 | 단가 |
|---|---|---|---|---|
| 1 | 3/2 | 노트북 | 1 | 120 |
| 2 | 3/3 | 마우스 | 3 | 4 |
| 3 | 3/5 | 모니터 | 2 | 20 |
| 4 | 3/7 | 마우스 | 5 | 4 |
| 5 | 3/9 | 노트북 | 2 | 100 |
실무 시나리오 1: 가장 이른 날짜와 가장 낮은 값
첫주문일 = MIN( Sales[주문일] )
최저단가 = MIN( Sales[단가] )
첫 주문일은 3/2(1번 주문)이고, 최저 단가는 마우스의 4입니다. 이 측정값을 상품별 표에 올리면 상품마다 따로 계산됩니다.
| 상품 | 첫주문일 | 최저단가 |
|---|---|---|
| 노트북 | 3/2 | 100 |
| 마우스 | 3/3 | 4 |
| 모니터 | 3/5 | 20 |
| 합계 | 3/2 | 4 |
노트북은 단가 120과 100 중 작은 100이고, 합계 줄은 필터가 없는 전체에서 구하므로 3/2와 4가 나옵니다. 앞선 AVERAGE 글에서 본 것처럼, 합계 줄은 위 줄들의 값을 다시 계산하는 것이 아니라 필터 없이 새로 계산한 값입니다. 최솟값은 줄들의 최솟값과 우연히 같아 보이지만 원리는 같습니다.
실무 시나리오 2: 두 값 중 작은 값으로 상한 두기
MIN의 두 번째 형태는 “더 작은 쪽을 고른다”는 뜻이라서, 값에 상한을 씌울 때 자주 씁니다. 예를 들어 영업팀 실적을 인정할 때 목표를 넘은 부분은 인정하지 않는 규칙이라고 해 보겠습니다.
인정실적 =
MIN( [실적], [목표] )
| 팀 | 실적 | 목표 | 인정실적 |
|---|---|---|---|
| A | 130 | 100 | 100 |
| B | 80 | 100 | 80 |
A팀은 실적이 목표를 넘었으니 목표인 100까지만, B팀은 실적 80이 더 작으니 80이 인정됩니다. 재고와 주문 수량 중 작은 쪽으로 출고 가능 수량을 정하는 식도 같은 모양입니다. 다만 아래 “빈 값” 항목에서 설명하듯, 이 형태는 두 값 중 하나가 빈 값일 때 결과가 문서마다 다르게 적혀 있으니 주의가 필요합니다.
실무 시나리오 3: 계산한 값들의 최솟값 (MINX)
MIN은 모델에 있는 열을 받습니다. 수량 × 단가처럼 행마다 계산한 값의 최솟값은 MINX를 씁니다.
최소주문금액 =
MINX(
Sales,
Sales[수량] * Sales[단가]
)
행마다 120, 12, 40, 20, 200이 계산되므로 최솟값은 12입니다. DAX Guide는 열 하나에 쓴 MIN이 내부적으로 MINX를 실행하는 것과 같고 성능 차이가 없다고 설명합니다. MIN이 모델의 열에만 쓸 수 있다는 점에서, 변수에 담긴 값이나 계산한 열을 돌아야 할 때는 MINX가 필요하다고 덧붙입니다.
MINX는 측정값을 그룹별로 계산한 뒤 그중 최솟값을 구할 때도 씁니다. “상품별 총매출 중 가장 적은 매출”은 이렇게 구합니다.
총매출 = SUMX( Sales, Sales[수량] * Sales[단가] )
최소상품매출 =
MINX(
VALUES( Sales[상품] ),
[총매출]
)
상품별 총매출은 노트북 320, 마우스 32, 모니터 40이라서 결과는 32입니다. 반복 안에서 측정값을 부르면 “지금 도는 상품”이 필터로 바뀌는 컨텍스트 전환이 일어납니다(CALCULATE 함수 글 참고).
빈 값: 설명이 서로 다른 부분
빈 값 처리는 MIN에서 가장 조심해야 할 부분입니다. 이 글을 쓰면서 문서를 비교해 보니 서로 다르게 적혀 있는 대목이 있어서, 확인된 내용과 갈리는 내용을 나눠 정리합니다.
- 열 형태: 빈 값은 건너뛰고, 값이 있는 행이 하나도 없으면 빈 값을 돌려준다고 DAX Guide가 설명합니다. 논리값(TRUE/FALSE)은 지원하지 않고, 필요하면 MINA를 쓴다고 Microsoft 문서가 안내합니다.
- 두 값 형태: 여기서 설명이 갈립니다. DAX Guide는 두 값 형태에서 MIN이 빈 값을 “의미 있는 값”으로 취급해서 건너뛰지 않으며, 그래서
MIN( 2, BLANK() )의 결과가 빈 값이라고 설명하고, MINX로 같은 비교를 하면 빈 값이 무시되어 2가 나온다고 대비합니다. 반면 Microsoft 문서는 비교할 때 빈 값이 0으로 취급된다고 적고, 예로MIN( 1, BLANK() )이 0을 돌려준다고 설명합니다. 둘 다 “빈 값이 열 형태처럼 그냥 무시되지는 않는다”는 점은 같지만 결과 값은 다릅니다.
두 설명 중 어느 쪽이 지금 사용하는 버전에 맞는지는 이 글에서 단정할 수 없습니다. 제가 직접 Power BI에서 돌려 본 것이 아니기 때문입니다. 그래서 실무에서는 빈 값 가능성이 있는 두 값을 MIN에 넣기 전에 미리 처리하는 것이 안전합니다.
인정실적_안전 =
VAR 실적값 = [실적]
VAR 목표값 = [목표]
RETURN
IF(
ISBLANK( 실적값 ) || ISBLANK( 목표값 ),
BLANK(),
MIN( 실적값, 목표값 )
)
이렇게 빈 값일 때의 결과를 식에서 직접 정해 두면 어느 쪽 설명이 맞더라도 결과가 흔들리지 않습니다. 빈 값을 0으로 바꾸면 편해 보이지만, SQLBI의 글(빈 값 대신 0을 반환하는 방법)이 지적하듯 데이터가 없는 구간까지 0이 표시되는 부작용이 있습니다. BLANK에 0을 더하면 0이 된다는 점은 확실하지만, 그 결과를 어디까지 보여줄지는 따로 정해야 합니다.
텍스트 열에도 쓸 수 있을까
SQLBI의 2015년 글은 MIN과 MAX를 텍스트 열에는 쓸 수 없다고 하면서 FIRSTNONBLANK, LASTNONBLANK를 대안으로 제시했습니다. 그런데 현재 Microsoft 문서와 DAX Guide는 텍스트도 처리하며 알파벳 순서로 비교한다고 설명합니다. 글이 쓰인 시기가 10년 가까이 차이 나는 만큼 버전에 따라 동작이 달라졌을 수 있습니다. 텍스트 열에 쓸 계획이라면 사용하는 환경에서 직접 확인해 보시길 권합니다.
쓰지 말아야 할 때
- TRUE/FALSE 열: MIN은 지원하지 않습니다. MINA를 쓰세요.
- 계산한 값이나 변수의 최솟값: 열이 아니라 식이므로 MINX를 써야 합니다.
- 빈 값이 섞일 수 있는 두 값 비교: 위처럼 미리 처리하세요.
- “가장 이른 날짜” 대신 “첫 번째 값”이 필요할 때: 목적이 다르면 다른 함수가 맞을 수 있습니다.
알아둘 점
- DAX Guide는 MIN이 DirectQuery의 측정값과 계산 열에서 지원된다고 안내합니다.
- 열 형태에 숫자, 날짜, 텍스트를 쓸 수 있지만 한 열의 데이터 형식은 하나입니다.
- 지역 설정에 따라 인수 구분자가 쉼표 대신 세미콜론일 수 있습니다. 식을 붙여 넣었는데 오류가 난다면 먼저 확인해 보세요.
자주 하는 실수
- 합계 줄이 줄별 값의 최솟값과 같은 이유를 모름: 필터 없이 새로 계산한 값이고, 우연히 일치할 수 있습니다.
- 두 값 형태에서 빈 값 때문에 의도와 다른 결과: 문서마다 다르게 설명하는 부분이라 미리 처리하는 것이 안전합니다.
- 계산식을 MIN에 바로 넣음: MIN( Sales[수량] * Sales[단가] )처럼 쓰면 안 되고, MINX가 필요합니다.
- 논리값 열에 MIN: 지원하지 않습니다.
비슷한 함수
- MAX: 가장 큰 값. 사용법이 같습니다.
- MINX, MAXX: 행마다 계산한 값의 최솟값, 최댓값
- MINA: 논리값까지 처리하는 형태
- FIRSTDATE, LASTDATE: 날짜 열에서 첫 날짜와 마지막 날짜. 시간 비교 계산에서 자주 쓰입니다.
정리
MIN은 열의 최솟값(또는 가장 이른 날짜)과 두 값 중 작은 값, 두 가지 용도로 나눠 기억하면 됩니다. 계산한 값의 최솟값은 MINX, 논리값은 MINA, 그리고 빈 값이 섞일 수 있는 두 값 비교는 미리 처리해 두는 것이 안전합니다. 예제는 가상 데이터이니 본인 모델의 열 이름으로 바꿔 직접 확인해 보시고, 특히 빈 값이 있는 데이터에서는 결과를 꼭 눈으로 확인해 보세요. 동작은 버전에 따라 다를 수 있어 공식 문서도 함께 보시길 권합니다.
참고한 자료
- Microsoft Learn, MIN function (DAX): https://learn.microsoft.com/en-us/dax/min-function-dax
- Microsoft Learn, MINX function (DAX): https://learn.microsoft.com/en-us/dax/minx-function-dax
- DAX Guide, MIN: https://dax.guide/min/
- DAX Guide, MINX: https://dax.guide/minx/
- SQLBI, How to return 0 instead of BLANK in DAX: https://www.sqlbi.com/articles/how-to-return-0-instead-of-blank-in-dax/
- SQLBI, Use FIRSTNONBLANK and LASTNONBLANK instead of MIN and MAX (2015): https://www.sqlbi.com/blog/marco/2015/02/24/use-firstnonblank-and-lastnonblank-instead-of-min-and-max-dax/
“DAX MIN 함수 정리: 열의 최솟값과 두 값 비교, 빈 값 처리의 함정”에 대한 1개의 생각