web-dev-qa-db-ja.com

mysqlでの "不正な照合順序の組み合わせ"エラーのトラブルシューティング

MySQLでストアドプロシージャを介して選択を行おうとすると、以下のエラーが発生します。

操作 '='の照合順序(latin1_general_cs、IMPLICIT)と(latin1_general_ci、IMPLICIT)の組み合わせが不正です

ここで何がうまくいかない可能性がありますかについての任意のアイデア?

テーブルの照合はlatin1_general_ciで、where句の列の照合はlatin1_general_csです。

180
user355562

これは一般に、2つの互換性のない照合順序の文字列を比較したり、異なる照合順序のデータを結合列に選択しようとしたりすることによって発生します。

COLLATE句を使用すると、クエリで使用される照合順序を指定できます。

たとえば、次のWHERE句では、投稿したエラーが必ず発生します。

WHERE 'A' COLLATE latin1_general_ci = 'A' COLLATE latin1_general_cs

解決策は、クエリ内の2つの列に共有照合順序を指定することです。これはCOLLATE句を使用した例です。

SELECT * FROM table ORDER BY key COLLATE latin1_general_ci;

別のオプションはBINARY演算子を使うことです。

BINARY strはCAST(str AS BINARY)の短縮形です。

あなたの解決策はこのように見えるかもしれません:

SELECT * FROM table WHERE BINARY a = BINARY b;

または、

SELECT * FROM table ORDER BY BINARY a;
187
defines

TL; DR

文字列の一方(または両方)の照合順序を変更して一致するようにするか、またはCOLLATE句を式に追加します。


  1. とにかくこの「照合」とは何ですか?

    一般的な文字セットと照合順序 で説明されているとおり:

    文字セットは、シンボルとエンコーディングのセットです。 collat​​ionは、文字セット内の文字を比較するための一連の規則です。想像上の文字セットの例で区別を明確にしましょう。

    A」、「B」、「a」、「b」の4文字のアルファベットがあるとします。各文字に数字を付けます:「A」= 0、「B」= 1、「a」= 2、「b」=3。文字「A」は記号であり、数字の0はencodingは「A」であり、4文字すべてとそのエンコーディングの組み合わせはcharacter set

    2つの文字列値「A」と「B」を比較するとします。これを行う最も簡単な方法は、エンコードを調べることです:「A」の場合は0、「B」の場合は1。 0は1より小さいので、「A」は「B」より小さいと言います。行ったのは、照合を文字セットに適用することです。照合は一連のルールです(この場合は1つのルールのみです)。「エンコードを比較します」。すべての可能な照合のうち、この最も単純な照合をbinary照合と呼びます。

    しかし、小文字と大文字が同等であると言いたい場合はどうでしょうか?次に、少なくとも2つのルールがあります。(1)小文字の「a」と「b」を「A」と「B」と同等として扱います。 (2)次に、エンコードを比較します。これをcase-insensitive照合と呼びます。バイナリ照合よりも少し複雑です。

    実際には、ほとんどの文字セットには多くの文字があります。「A」と「B」だけでなく、アルファベット全体、時には複数のアルファベットまたは数千文字の東洋の書記体系に加えて、多くの特殊記号と句読点があります。また、実際には、ほとんどの照合順序には、大文字と小文字を区別するかどうかだけでなく、アクセント(「アクセント」はドイツ語の「Ö」のように文字に付けられるマーク)と、文字マッピング(2つのドイツ語照合のいずれかで「Ö」=「OE」というルールなど)。

    照合の効果の例 でさらに例を示します。

  2. はい、しかし、MySQLは指定された式に使用する照合をどのように決定しますか?

    式の照合 で文書化されているとおり:

    ほとんどのステートメントでは、MySQLが比較演算を解決するために使用する照合順序は明らかです。たとえば、次の場合、照合が列charset_nameの照合であることは明らかです。

    SELECT x FROM T ORDER BY x;
    SELECT x FROM T WHERE x = x;
    SELECT DISTINCT x FROM T;
    

    ただし、複数のオペランドを使用すると、あいまいさが生じる可能性があります。例えば:

    SELECT x FROM T WHERE x = 'Y';
    

    比較では、列xの照合、または文字列リテラル'Y'の照合を使用する必要がありますか? x'Y'の両方に照合があるので、どの照合が優先されますか?

    標準SQLは、「強制」ルールと呼ばれていたものを使用して、このような問題を解決します。

    [deletia]

    MySQLは、曖昧さを解決するために、次のルールで保磁力値を使用します。

    • 最小の保磁力値を持つ照合を使用します。

    • 両側の保磁力が同じである場合:

      • 両側がユニコードであるか、両側がユニコードでない場合、エラーです。

      • 一方の側にUnicode文字セットがあり、もう一方の側に非Unicode文字セットがある場合、Unicode文字セットを持つ側が勝ち、自動文字セット変換が非Unicode側に適用されます。たとえば、次のステートメントはエラーを返しません。

        SELECT CONCAT(utf8_column, latin1_column) FROM t1;
        

        utf8の文字セットとutf8_columnと同じ照合を持つ結果を返します。 latin1_columnの値は、連結する前に自動的にutf8に変換されます。

      • 同じ文字セットのオペランドで、_bin照合と_ciまたは_cs照合が混在する操作の場合、_bin照合が使用されます。これは、非バイナリ文字列とバイナリ文字列を混在させる操作がオペランドをバイナリ文字列として評価する方法と似ていますが、データ型ではなく照合用です。

  3. つまり、「照合の不法な組み合わせ」とは何ですか?

    「照合の違法な組み合わせ」は、式が異なる照合の2つの文字列を比較するときに発生しますが、同等の強制力と強制力規則は競合の解決に役立ちません。これは、上記の引用の3番目の箇条書きで説明されている状況です。

    質問で与えられた特定のエラーIllegal mix of collations (latin1_general_cs,IMPLICIT) and (latin1_general_ci,IMPLICIT) for operation '='は、同等の強制力を持つ2つの非Unicode文字列間で同等の比較があったことを示しています。さらに、照合はステートメントで明示的に指定されたのではなく、文字列のソース(列メタデータなど)から暗示されたことを示しています。

  4. これで十分ですが、このようなエラーをどのように解決しますか?

    上記のマニュアルの抜粋が示唆しているように、この問題はいくつかの方法で解決できますが、そのうちの2つが賢明で推奨されます。

    • 文字列の一方(または両方)の照合順序を変更して、一致するようにし、あいまいさを解消します。

      これを行う方法は、文字列がどこから来たかによって異なります。リテラル式は、 collation_connection システム変数で指定された照合を取ります。テーブルの値は、列のメタデータで指定された照合を取ります。

    • 強制的に1つの文字列を強制しない。

      上記から次の引用を省略しました。

      MySQLは、次のように保磁力値を割り当てます。

      • 明示的なCOLLATE句の強制力は0です(強制力はまったくありません)。

      • 2つの文字列を異なる照合順序で連結すると、強制力は1になります。

      • 列またはストアドルーチンのパラメーターまたはローカル変数の照合の強制力は2です。

      • 「システム定数」( USER()VERSION() などの関数によって返される文字列)の保磁力は3です。

      • リテラルの照合順序の強制力は4です。

      • NULLまたはNULLから派生した式の保磁力は5です。

      したがって、比較で使用される文字列のいずれかにCOLLATE句を追加するだけで、その照合の使用が強制されます。

    このエラーを解決するためだけに展開された場合、他の人はひどく悪い習慣になるでしょう:

    • 1つ(または両方)の文字列に強制的に他の強制力値を持たせ、1つが優先されるようにします。

      CONCAT() または CONCAT_WS() を使用すると、1の強制力を持つ文字列になります。および(ストアドルーチン内の場合)パラメーター/ローカル変数を使用すると、強制力2の文字列が生成されます。

    • 一方(または両方)の文字列のエンコードを変更して、一方がUnicodeで他方がUnicodeでないようにします。

      これは、 CONVERT(expr USING transcoding_name) ;を使用したトランスコーディングによって実行できます。または、データの基礎となる文字セットの変更(例:列の変更、リテラル値の character_set_connection の変更、または異なるエンコーディングでのクライアントからの送信と character_set_client /の変更)キャラクターセットイントロデューサーの追加)。希望する文字を新しい文字セットでエンコードできない場合、エンコードを変更すると他の問題が発生することに注意してください。

    • 文字列の一方(または両方)のエンコーディングを変更して両方が同じになるようにし、関連する_bin照合順序を使用するように1つの文字列を変更します。

      エンコードと照合を変更する方法については上記で詳しく説明しました。 _bin照合で提供されるよりも高度な照合ルールを実際に適用する必要がある場合、このアプローチはほとんど役に立ちません。

136
eggyal

将来のグーグルの議論に私の2cを追加します。

Varcharパラメーターを受け取るカスタムfunctionsを使用すると、次のエラーが発生する同様の問題を調査していました。

Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and 
(utf8_general_ci,IMPLICIT) for operation '='

次のクエリを使用します。

mysql> show variables like "collation_database";
    +--------------------+-----------------+
    | Variable_name      | Value           |
    +--------------------+-----------------+
    | collation_database | utf8_general_ci |
    +--------------------+-----------------+

DBはtf8_general_ciを使用しているのに対し、テーブルはtf8_unicode_ciを使用して定義されていることがわかりました。

mysql> show table status;
    +--------------+-----------------+
    | Name         | Collation       |
    +--------------+-----------------+
    | my_view      | NULL            |
    | my_table     | utf8_unicode_ci |
    ...

ビューにはNULL照合があることに注意してください。このクエリは1つのビューに対してnullを示しますが、ビューと関数には照合定義があるようです。使用される照合は、ビュー/関数が作成されたときに定義されたDB照合です。

悲しい解決策は、データベース照合を変更し、ビュー/関数を再作成して、現在の照合を強制的に使用することでした。

  • データベースの照合の変更:

    ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;
    

これが誰かの助けになることを願っています。

56
Ariel T

特に大量のデータを含むデータベースで、文字セットを変換することが危険な場合があります。最良のオプションは「バイナリ」演算子を使用することだと思います。

e.g : WHERE binary table1.column1 = binary table2.column1
13
Justin Vincent

同様の問題があり、文字列variableを指定したFIND_IN_SETプロシージャを使用しようとしていました。

SET @my_var = 'string1,string2';
SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);

エラーを受け取っていました

エラーコード:1267。操作 'find_in_set'の照合(utf8_unicode_ci、IMPLICIT)と(utf8_general_ci、IMPLICIT)の不正な組み合わせ

短い答え:

Collat​​ion_YYYY変数を変更する必要はありません。正しい照合を追加するだけです変数宣言の隣、つまり.

SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);

長答:

最初に照合変数をチェックしました:

mysql> SHOW VARIABLES LIKE 'collation%';
    +----------------------+-----------------+
    | Variable_name        | Value           |
    +----------------------+-----------------+
    | collation_connection | utf8_general_ci |
    +----------------------+-----------------+
    | collation_database   | utf8_general_ci |
    +----------------------+-----------------+
    | collation_server     | utf8_general_ci |
    +----------------------+-----------------+

次に、テーブルの照合をチェックしました。

mysql> SHOW CREATE TABLE my_table;

CREATE TABLE `my_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `column_name` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=125 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

これは、テーブルがtf8_unicode_ciとして設定されている間に、変数がtf8_general_ciのデフォルト照合で設定されたことを意味します。

変数宣言の隣にCOLLATEコマンドを追加することにより、変数照合はテーブルに設定された照合と一致しました。

7
nkatsar

このスクリプト を試すと、すべてのデータベースとテーブルがutf8に変換されます。

5

リテラルが関係する場合の解決策。

Pentaho Data Integrationを使用していますが、SQL構文を指定できません。非常に単純なDBルックアップを使用すると、エラー「操作 '='の照合(cp850_general_ci、COERCIBLE)と(latin1_swedish_ci、COERCIBLE)の混合が不正です」が発生しました。

生成されたコードは「SELECT DATA_DATE AS latest_DATA_DATE FROM hr_cc_normalised_data_date_v WHERE PSEUDO_KEY =?」

ストーリーを短くすると、ルックアップはビューになり、発行したときに

mysql> show full columns from hr_cc_normalised_data_date_v;
+------------+------------+-------------------+------+-----+
| Field      | Type       | Collation         | Null | Key |
+------------+------------+-------------------+------+-----+
| PSEUDO_KEY | varchar(1) | cp850_general_ci  | NO   |     |
| DATA_DATE  | varchar(8) | latin1_general_cs | YES  |     |
+------------+------------+-------------------+------+-----+

「cp850_general_ci」の由来を説明しています。

ビューは単に「SELECT 'X'、......」で作成されました。このような手動リテラルによると、このように「latin1」と「latin1_general_cs」として正しく定義されたサーバー設定から文字セットと照合を継承する必要があります明らかに起こりませんでした私はビューの作成でそれを強制しました

CREATE OR REPLACE VIEW hr_cc_normalised_data_date_v AS
SELECT convert('X' using latin1) COLLATE latin1_general_cs        AS PSEUDO_KEY
    ,  DATA_DATE
FROM HR_COSTCENTRE_NORMALISED_mV
LIMIT 1;

これで、両方の列にlatin1_general_csが表示され、エラーはなくなりました。 :)

2
jc508

問題が発生している列が「ハッシュ」である場合、次のことを考慮してください...

「ハッシュ」がバイナリ文字列の場合は、BINARY(...)データ型を実際に使用する必要があります。

「ハッシュ」が16進文字列の場合、utf8は必要ありません。文字チェックなどのためにutf8を避ける必要があります。たとえば、MySQLのMD5(...)は32バイトの固定長16進文字列を生成します。 SHA1(...)は、40バイトの16進文字列を提供します。これはCHAR(32) CHARACTER SET ascii(またはsha1の場合は40)に保存できます。

または、UNHEX(MD5(...))BINARY(16)に保存してください。これにより、列のサイズが半分になります。 (ただし、印刷できないようにします。)SELECT HEX(hash) ...読みたい場合。

2つのBINARY列を比較しても、照合の問題はありません。

1
Rick James

MySQLは、同じ照合順序に強制できない限り、照合順序の混在を本当に嫌います(明らかに、この場合は実行不可能です)。 COLLATE句 を使用して同じ照合を強制的に使用することはできませんか? (または、該当する場合は、より単純なBINARYショートカット...)。

1
Alex Martelli

照合に関する問題のもう1つの原因は、mysql.procテーブルです。ストレージの手順と機能の照合を確認します。

SELECT
  p.db, p.db_collation, p.type, COUNT(*) cnt
FROM mysql.proc p
GROUP BY p.db, p.db_collation, p.type;

mysql.proc.collation_connectionおよびmysql.proc.character_set_client列にも注意してください。

0
ruvim

可能な解決策は、 データベース全体をUTF8に変換する です(これも question を参照)。

0
utapyngo

PhpMyAdminがインストールされている場合は、次のリンクに記載されている指示に従うことができます。 https://mediatemple.net/community/products/dv/204403914/default-mysql-character-set-and-collat​​ion =データベースの照合とすべてのテーブルの照合、およびテーブルのフィールドを照合してから、すべてのストアドプロシージャと関数を再コンパイルする必要があります。それですべてが再び動作するはずです。