수색…


소개

최근에 새 버전의 MySQL 서버는 작동했던 쿼리에 1055 개의 오류를 생성하기 시작했습니다. 이 주제에서는 이러한 오류에 대해 설명합니다. MySQL 팀은 GROUP BY 대한 비표준 확장 기능을 GROUP BY 하거나 적어도 쿼리 작성 개발자가이 기능을 사용하기가 어려워 지도록 노력하고 있습니다.

비고

오랜 시간 동안 MySQL은 GROUP BY 대한 비표준 확장 기능을 포함하고 있습니다.이 확장 기능은 효율성이라는 이름으로 괴상한 동작을 허용합니다. 이 확장을 통해 전세계 수많은 개발자가 자신이하는 일을 완전히 이해하지 않고도 프로덕션 코드에서 GROUP BY 를 사용할 수있었습니다.

특히 표준 GROUP BY 절에 열을 열거해야하기 때문에 SELECT *GROUP BY 쿼리에 사용하는 것은 좋지 않습니다. 많은 개발자들이 불행히도이를 수행했습니다.

이것을 읽으십시오. https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

MySQL 팀은 프로덕션 코드를 엉망으로 만들지 않고이 잘못된 기능을 수정하려고 노력하고 있습니다. 표준 동작을 강요하기 위해 ONLY_FULL_GROUP_BY 라는 5.7.5에 sql_mode 플래그를 추가했습니다. 최근 버전에서는 기본적으로 해당 플래그를 켰습니다. 로컬 MySQL을 5.7.14로 업그레이드 할 때 플래그가 켜지고 이전 확장명에 따라 운영 코드가 작동하지 않습니다.

최근에 1055 개의 오류가 발생하기 시작했다면 무엇을 선택해야합니까?

  1. 문제가되는 SQL 쿼리를 수정하거나 작성자에게 그렇게하십시오.
  2. 사용중인 응용 프로그램 소프트웨어와 호환되는 MySQL 호환 버전으로 롤백하십시오.
  3. 서버의 sql_mode 를 변경하여 새로 설정된 ONLY_FULL_GROUP_BY 모드를 제거하십시오.

SET 명령을 사용하여 모드를 변경할 수 있습니다.

SET  sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'

응용 프로그램이 MySQL에 연결되면 바로 수행하십시오.

또는 MySQL 설치에서 init 파일을 찾고 sql_mode= 행을 찾아 ONLY_FULL_GROUP_BY 를 생략하도록 변경 한 다음 서버를 다시 시작하십시오.

GROUP BY 사용 및 오용

 SELECT item.item_id, item.name,     /* not SQL-92 */ 
        COUNT(*) number_of_uses
  FROM item 
  JOIN uses ON item.item_id, uses.item_id
 GROUP BY item.item_id

item 이라는 테이블에 행을 표시하고 uses 라는 테이블에 관련된 행의 수를 표시 uses . 이것은 잘 작동하지만 불행히도 표준 SQL-92는 아닙니다.

왜 안돼? GROUP BY 쿼리의 SELECT 절 (및 ORDER BY 절)에는 다음과 같은 열이 포함되어야합니다.

  1. GROUP BY 절에서 언급 한
  2. COUNT() , MIN() 등과 같은 집계 함수.

이 예제의 SELECT 절은 item.name 언급 item.name , 그 중 하나를 충족하지 않는 열입니다. MySQL 5.6 및 이전 버전에서는 SQL 모드에 ONLY_FULL_GROUP_BY 가 포함되어 있으면이 쿼리를 거부합니다.

이 예제 쿼리는 이와 같이 GROUP BY 절을 변경하여 SQL-92 표준을 준수하도록 만들 수 있습니다.

SELECT item.item_id, item.name, 
       COUNT(*) number_of_uses
  FROM item 
  JOIN uses ON item.item_id, uses.item_id
 GROUP BY item.item_id, item.name

이후의 SQL-99 표준은 SELECT 문이 DBMS가 그룹 키 열과 그룹 키 열 간의 기능적 종속성을 증명할 수있는 경우 그룹화되지 않은 열을 그룹 키에서 생략 할 수 있도록합니다. item.nameitem.name 에 기능적으로 종속되어 item.item_id 초기 예제는 유효한 SQL-99입니다. MySQL은 버전 5.7에서 기능 의존성 검사기 를 얻었다. 원래 예제는 ONLY_FULL_GROUP_BY 작동합니다.

예측할 수없는 결과를 반환하기 위해 GROUP BY를 잘못 사용 : Murphy 's Law

SELECT item.item_id, uses.category,   /* nonstandard */ 
       COUNT(*) number_of_uses 
  FROM item 
  JOIN uses ON item.item_id, uses.item_id
 GROUP BY item.item_id

item이라는 테이블에 행을 표시하고 uses라는 테이블에 관련된 행의 수를 표시합니다. 또한 uses.category 라는 열의 값을 보여줍니다.

이 쿼리는 ONLY_FULL_GROUP_BY 플래그가 나타나기 전에 MySQL에서 작동합니다. 그것은 MySQL의 비표준 확장을 GROUP BY 합니다.

그러나 쿼리에는 문제가 있습니다. uses 테이블의 여러 행이 JOIN 절의 ON 조건과 일치하면 MySQL은 해당 행 중 하나에서 category 열을 반환합니다. 어떤 행? 쿼리 작성자와 응용 프로그램 사용자는 미리 알지 못합니다. 공식적으로 말하자면, 예측할 수없는 일입니다 . MySQL은 원하는 모든 가치를 반환 할 수 있습니다.

예측할 수없는 것은 무작위 처럼 하나의 중요한 차이가 있습니다. 때때로 무작위적인 선택이 바뀔 것으로 예상됩니다. 따라서 선택 항목이 임의적 인 경우 디버깅 또는 테스트 중에 선택 항목을 감지 할 수 있습니다. 예측할 수없는 결과는 더욱 심각합니다. MySQL은 쿼리를 사용할 때까지 쿼리를 사용할 때마다 동일한 결과를 반환합니다 . 때로는 다른 결과를 초래하는 MySQL 서버의 새로운 버전입니다. 때때로 그것은 문제를 일으키는 성장하는 테이블입니다. 무엇이 잘못 될 수 있으며 잘못 될 것이며 예상하지 못할 때 일어납니다. 그것은 머피의 법칙 입니다.

MySQL 팀은 개발자가이 실수를 저 지르도록하기 위해 노력해 왔습니다. 5.7 시퀀스의 최신 버전의 MySQL에는 ONLY_FULL_GROUP_BY 라는 sql_mode 플래그가 있습니다. 해당 플래그가 설정되면 MySQL 서버는 1055 오류를 반환하고 이러한 종류의 쿼리 실행을 거부합니다.

GROUP BY를 SELECT *로 오용하고이를 수정하는 방법.

때로 쿼리는 SELECT 절의 * 와 함께 다음과 같이 보입니다.

 SELECT item.*,     /* nonstandard */ 
        COUNT(*) number_of_uses
  FROM item 
  JOIN uses ON item.item_id, uses.item_id
 GROUP BY item.item_id

이러한 쿼리는 ONLY_FULL_GROUP_BY 표준을 준수하도록 리팩토링해야합니다.

이렇게하려면 각 item_idnumber_of_uses 값을 올바르게 반환하는 GROUP BY 를 사용하는 하위 쿼리가 필요합니다. 이 부속 조회는 쉽고 간단합니다. 왜냐하면 uses 테이블 만 봐야하기 때문입니다.

                              SELECT item_id, COUNT(*) number_of_uses
                                FROM  uses 
                               GROUP BY item_id

그런 다음 해당 하위 쿼리를 item 테이블과 조인 할 수 있습니다.

 SELECT item.*, usecount.number_of_uses
   FROM item
   JOIN (
                              SELECT item_id, COUNT(*) number_of_uses
                                FROM  uses 
                               GROUP BY item_id
        ) usecount ON item.item_id = usecount.item_id

이렇게하면 GROUP BY 절이 간단하고 정확하며 * 지정자를 사용할 수 있습니다.

참고 : 그럼에도 불구하고 현명한 개발자는 어쨌든 * 지정자를 사용하지 않습니다. 일반적으로 쿼리에서 원하는 열을 나열하는 것이 좋습니다.

ANY_VALUE ()

 SELECT item.item_id, ANY_VALUE(uses.tag) tag,   
        COUNT(*) number_of_uses
  FROM item 
  JOIN uses ON item.item_id, uses.item_id
 GROUP BY item.item_id

item 이라는 테이블의 행, 관련 행의 수 및 uses 라는 관련 테이블의 값 중 하나를 표시 uses .

ANY_VALUE() 함수 를 이상한 일종의 집계 함수라고 생각할 수 있습니다. count, sum 또는 maximum을 반환하는 대신 MySQL 서버에 문제의 그룹에서 하나의 값을 임의로 선택하도록 지시합니다. 오류 1055를 해결하는 방법입니다.

프로덕션 응용 프로그램의 쿼리에서 ANY_VALUE() 를 사용할 때는주의하십시오.

정말 SURPRISE_ME() 라고해야합니다. GROUP BY 그룹의 일부 행의 값을 반환합니다. 반환하는 행은 불확정합니다. 이는 MySQL 서버에 달려 있다는 것을 의미합니다. 형식적으로 예기치 않은 값을 반환합니다.

서버는 임의의 값을 선택하지 않습니다. 쿼리가 실행될 때까지 쿼리를 실행할 때마다 동일한 값을 반환합니다. 그것은 테이블이 커지거나 줄어들거나, 서버에 RAM이 많거나 적을 때, 서버 버전이 변경 될 때, 또는 화성이 역행하는 경우 (의미가 무엇이든) 또는 전혀 이유없이 변경 될 수 있습니다.

당신은 경고를 받았습니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow