Programing

GROUP BY 절에 나타나거나 집계 함수에 사용되어야합니다.

lottogame 2020. 4. 16. 08:12
반응형

GROUP BY 절에 나타나거나 집계 함수에 사용되어야합니다.


이 발신자 '메이커'처럼 보이는 테이블이 있습니다.

 cname  | wmname |          avg           
--------+-------------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000

그리고 각 cname에 대한 최대 평균을 선택하고 싶습니다.

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;

하지만 오류가 발생합니다.

ERROR:  column "makerar.wmname" must appear in the GROUP BY clause or be used in an   aggregate function 
LINE 1: SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;

그래서 나는 이것을한다

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname, wmname;

그러나 이것은 의도 한 결과를 제공하지 않으며 아래의 잘못된 출력이 표시됩니다.

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000

실제 결과는

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

이 문제를 해결하려면 어떻게해야합니까?

참고 :이 테이블은 이전 작업에서 생성 된 VIEW입니다.


예, 이것은 일반적인 집계 문제입니다. SQL3 (1999) 이전 에는 선택된 필드가 GROUP BY절 [*]에 나타나야합니다 .

이 문제를 해결하려면 하위 쿼리에서 집계를 계산 한 다음 자체와 결합하여 표시해야 할 추가 열을 가져와야합니다.

SELECT m.cname, m.wmname, t.mx
FROM (
    SELECT cname, MAX(avg) AS mx
    FROM makerar
    GROUP BY cname
    ) t JOIN makerar m ON m.cname = t.cname AND t.mx = m.avg
;

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

그러나 더 단순 해 보이는 창 함수를 사용할 수도 있습니다.

SELECT cname, wmname, MAX(avg) OVER (PARTITION BY cname) AS mx
FROM makerar
;

이 방법의 유일한 점은 모든 레코드를 표시한다는 것입니다 (창 함수는 그룹화되지 않음). 그러나 각 행의 국가에 대해 올바른 (즉, 최대 cname수준) 표시 MAX되므로 귀하에게 달려 있습니다.

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  |     5.0000000000000000
 spain  | usopp  |     5.0000000000000000

(cname, wmname)최대 값과 일치하는 유일한 튜플 을 표시하는 덜 우아한 솔루션 은 다음과 같습니다.

SELECT DISTINCT /* distinct here matters, because maybe there are various tuples for the same max value */
    m.cname, m.wmname, t.avg AS mx
FROM (
    SELECT cname, wmname, avg, ROW_NUMBER() OVER (PARTITION BY avg DESC) AS rn 
    FROM makerar
) t JOIN makerar m ON m.cname = t.cname AND m.wmname = t.wmname AND t.rn = 1
;


 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

[*] : 흥미롭게도 사양 종류에 따라 그룹화되지 않은 필드를 선택할 수 있지만 주요 엔진은 실제로 마음에 들지 않는 것 같습니다. Oracle과 SQLServer는 이것을 전혀 허용하지 않습니다. MySQL은 기본적으로 허용했지만 5.7 이후 관리자는 ONLY_FULL_GROUP_BY이 기능을 지원하려면 서버 구성 에서이 옵션 ( )을 수동으로 활성화해야합니다 ...


Postgres에서는 특수 DISTINCT ON (expression)구문을 사용할 수도 있습니다 .

SELECT DISTINCT ON (cname) 
    cname, wmname, avg
FROM 
    makerar 
ORDER BY 
    cname, avg DESC ;

group by선택 에서 그룹화되지 않은 필드와 집계되지 않은 필드를 지정할 때의 문제점 은 엔진이이 경우 어떤 레코드의 필드를 리턴해야하는지 알 수 없다는 것입니다. 처음인가요? 마지막인가요? 자연스럽게 집계 결과에 해당하는 (아무 기록 일반적으로 없습니다 minmax예외가).

그러나 해결 방법이 있습니다. 필수 필드도 집계하십시오. posgres에서는 다음과 같이 작동합니다.

SELECT cname, (array_agg(wmname ORDER BY avg DESC))[1], MAX(avg)
FROM makerar GROUP BY cname;

이것은 avg 순서로 모든 wname의 배열을 생성하고 첫 번째 요소를 반환합니다 (postgres의 배열은 1을 기반으로 함).


SELECT t1.cname, t1.wmname, t2.max
FROM makerar t1 JOIN (
    SELECT cname, MAX(avg) max
    FROM makerar
    GROUP BY cname ) t2
ON t1.cname = t2.cname AND t1.avg = t2.max;

rank() 창 기능 사용 :

SELECT cname, wmname, avg
FROM (
    SELECT cname, wmname, avg, rank() 
    OVER (PARTITION BY cname ORDER BY avg DESC)
    FROM makerar) t
WHERE rank = 1;

노트

둘 중 하나는 그룹당 여러 개의 최대 값을 유지합니다. 평균이 max와 동일한 레코드가 두 개 이상인 경우에도 그룹당 단일 레코드 만 원하는 경우 @ypercube의 답변을 확인해야합니다.


저에게는 "일반적인 집계 문제"가 아니라 잘못된 SQL 쿼리에 관한 것입니다. "각 cname에 대한 최대 평균 선택 ..."에 대한 정답은

SELECT cname, MAX(avg) FROM makerar GROUP BY cname;

결과는 다음과 같습니다.

 cname  |      MAX(avg)
--------+---------------------
 canada | 2.0000000000000000
 spain  | 5.0000000000000000

이 결과는 일반적으로 "각 그룹에 가장 적합한 결과는 무엇입니까?"라는 질문에 대답합니다. . 스페인의 경우 최상의 결과는 5이고 캐나다의 경우 최상의 결과는 2입니다. 사실이며 오류가 없습니다. wmname표시해야하는 경우 , " 결과 세트에서 wmname을 선택 하는 규칙 은 무엇입니까 ?"라는 질문에 대답해야합니다. 실수를 명확히하기 위해 입력 데이터를 약간 변경해 봅시다 :

  cname | wmname |        avg           
--------+--------+-----------------------
 spain  | zoro   |  1.0000000000000000
 spain  | luffy  |  5.0000000000000000
 spain  | usopp  |  5.0000000000000000

이 쿼리를 실행하면 어떤 결과가 예상 SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;됩니까? 그것은이어야한다 spain+luffyspain+usopp? 왜? 되지 않은 결정 "더 나은"를 선택하는 방법을 쿼리에서 wmname을 여러 적합하면 결과도 결정되지 않도록. 이것이 SQL 인터프리터가 오류를 반환하는 이유입니다. 쿼리가 올바르지 않습니다.

다시 말해, " spain그룹 에서 누가 최고 입니까?"라는 질문에 대한 정답은 없습니다. . Lopp은 usopp보다 낫지 않습니다. 왜냐하면 usopp는 동일한 "점수"를 갖기 때문입니다.


최근에를 사용하여 계산하려고 할 때이 문제가 발생 case when하여 whichand count문의 순서를 변경 하면 문제가 해결 된다는 것을 알았습니다 .

SELECT date(dateday) as pick_day,
COUNT(CASE WHEN (apples = 'TRUE' OR oranges 'TRUE') THEN fruit END)  AS fruit_counter

FROM pickings

GROUP BY 1

후자를 사용하는 대신 사과와 오렌지가 집계 함수에 표시되어야하는 오류가 발생했습니다.

CASE WHEN ((apples = 'TRUE' OR oranges 'TRUE') THEN COUNT(*) END) END AS fruit_counter

이것은 잘 작동하는 것 같습니다

SELECT *
FROM makerar m1
WHERE m1.avg = (SELECT MAX(avg)
                FROM makerar m2
                WHERE m1.cname = m2.cname
               )

참고 URL : https://stackoverflow.com/questions/19601948/must-appear-in-the-group-by-clause-or-be-used-in-an-aggregate-function

반응형