1. 程式人生 > 實用技巧 >MySQL only_full_group_by導致的group by錯誤解決

MySQL only_full_group_by導致的group by錯誤解決

問題

MySQL5.7以上版本,預設是開啟了 only_full_group_by模式的:

> select @@sql_mode
> select @@global.sql_mode;
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

開啟這個模式後,原先的 group by 語句就報錯:

SELECT list is not in GROUP BY clause and contains nonaggregated column 'test' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

原因

MySQL在低版本(5.7.x 以下)中允許 select 後面的非聚合列不出現在 group by 中。以下sql在低版本中是可以執行的,但是在5.7及以上版本會報錯:

select name, age, count(name) from student group by age

因為在SQL標準中,出現在select target list中,並且沒有出現在聚集函式中的表示式必須出現在group by子句中:

  1. order by後面的列必須是在select中存在

  2. select、having或order by後面存在的非聚合列必須全部在group by中存在

而沒有遵循原則的sql

會被認為是不合法的sql,如SQLServerOraclePostgreSql都不支援select target list中出現語義不明確的列,MySQL 5.7開始修正,即ONLY_FULL_GROUP_BY語義。

解決

開啟 only_full_group_bygroup by 將變成和 distinct 一樣,只能獲取受到其影響的欄位資訊,無法和其他未受其影響的欄位共存,這樣,group by 的功能將變得十分狹窄。

  1. 保持only_full_group_by 模式開啟

    MySQL提供了any_value(field) 函式允許非分組欄位的出現(和關閉 only_full_group_by 模式有相同效果)。

    select any_value(name), age, count(name) from student group by age
    
  2. 關閉only_full_group_by

    • 當前會話關閉:

      只在當前會話生效,關閉當前會話就失效。

      > select @@sql_mode
      

      複製查詢出來的值,去除only_full_group_by,再設定回去:

      > set sql_mod='去除only_full_group_by後的值'
      

      比如:在navicat中當前視窗執行,在當前視窗生效,新開視窗失效。

    • 當前服務關閉:

      當前服務生效,重啟服務失效(該方法,我設定好像不生效?)

      > select @@global.sql_mode
      

      複製查詢出來的值,去除only_full_group_by,再設定回去:

      > set global sql_mod='去除only_full_group_by後的值'
      
    • 修改MySQL程式配置:

      需重啟MySQL生效。永久生效,但生產環境上是禁止重啟MySQL服務。

      MySQL配置檔案my.conf目錄:

      # mysql --help | grep 'my.cnf'
                            order of preference, my.cnf, $MYSQL_TCP_PORT,
      /etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
      

      從左到右優先順序,後者覆蓋前者重複引數。

      新增:

      [mysqld]    
      sql_mode=去除only_full_group_by後的值
      
    • JDBC 引數關閉only_full_group_by

      jdbc:mysql://localhost:3306/test?sessionVariables=sql_mode='去除only_full_group_by後的值'