この記事は、Huawei Cloud Community「GaussDB (DWS) Function Result Difference Case: Greatest」から共有されたもので、著者: あなたは猿に誘われた救助者ですか。
GaussDB (DWS) は複数の互換モードをサポートしていますが、ターゲット データベースとの互換性を維持するために、モード間には多かれ少なかれ動作の違いがあります。mysql互換モードでの式関数の書き方の違いによる結果の違いの例です。
問題の背景
問題のバージョン GaussDB 8.1.1
問題の説明
ユーザーは、mysql 互換モードでは、次の 2 つの SQL の実行結果に違いがあると報告しました: selectgreater(1,2,100,-1,0,nvl(null,0)) 結果は 2 selectgreater(1,2,100) ,-1 ,0) 結果は 100 です
シーン再現
mysql=# selectgreater(1,2,100,-1,nvl(null,0)); 最大 ---------- 2 (1 行) mysql=# selectgreat(1,2,100,-1,0,0); 最大 ---------- 100 (1 行)
根本原因分析
1. 2 つの結果セットのうちの 1 つが左側に表示され、もう 1 つが右側に表示されていることを友人が気づいたかどうかはわかりませんが、まずこれら 2 つの結果のデータ型を確認しましょう。
mysql=# select pg_typeof(greatest(1,2,100,-1,nvl(null,0))); pg_typeof ----------- text (1 row) mysql=# select pg_typeof(greatest(1,2,100,-1,0)); pg_typeof ----------- 整数 (1行)
2. pg_typeof に依存して、返された結果のデータ型を取得します。これは、最初のステートメントがテキスト型でソートされ、順序 ('0'、'1'、'-1'、') で最大値が選択されることを意味します。 100' ,'2') なので、最大値は文字列型の '2' であることがわかります。
0 1 -1 100 2
3. 同様に、2 番目のステートメントは int 型で最大値 (-1, 0, 1, 2, 100) をソートして選択するため、最大値は数値型の 100 であることがわかります。
-1 0 1 2 100
4. 最大の式関数の戻り値の型は、入力パラメータの型に基づいて決定されます。ここでの差は、5 番目の入力パラメータの型によって生じる結果の違いです。
mysql=# select pg_typeof(nvl(null,0)); pg_typeof ----------- text (1 row) mysql=# select pg_typeof(0); pg_typeof ----------- 整数 (1行)
5. nvl/greatest の戻り値の型が異なる理由は、mysql 互換モードの型一致ルールによって決まります。
特定のルールについては、「UNION、CASE、および関連する構造体」を参照してください。
提案された変更
この違いのシナリオでは、戻り値の型が不確実な場合は入力パラメーターの型を明示的に指定し、結果が int で並べ替えられるように nvl(null,0) を nvl(null,0)::int に変更することをお勧めします。これは別のテーブルステートメントとは異なります。テーブルステートメントは期待どおりです。
mysql=# selectgreater(1,2,100,-1,nvl(null,0)::int); 最大 ---------- 100 (1 行)
知識分析
SQL UNION 構造は、さまざまなデータ型に一致し、統一されたデータ型の結果セットを出力します。SELECT UNION ステートメント内のすべてのクエリ結果は 1 つの列に表示される必要があるため、各 SELECT 句の要素の型が互いに一致し、統一されたデータ型に変換される必要があります。同じ要件が、UNION、ARRAY、CASE、COALESCE、IF、IFNULL、GREATEST、LEAST、NVLなどの式や関数にも広く存在します。
GaussDB (DWS) は複数の互換モードをサポートしており、互換モードごとに型一致ルールも異なります。理解を容易にするために、 mysql 互換モードのIFNULL型マッチング ルールの例を示します。これは、mysql 互換モードのGREATESTルールと一致します。
ルール 1: 不明な型を除くすべての入力が同じ型である場合、入力と同じデータ型に解決されます。
mysql=# pg_typeof(1),pg_typeof(2) を選択します。 pg_typeof | pg_typeof -----------+--------------- 整数 | 整数 (1 行) mysql=# select ifnull(1,2),pg_typeof(ifnull(1,2)); ifnull | pg_typeof --------+--------------- 1 | 整数 (1行)
ルール 2: すべての入力が不明なタイプの場合、テキスト タイプに解析されます。(定数文字列は不明な型です)
mysql=# select pg_typeof('1'),pg_typeof('2'); pg_typeof | pg_typeof -----------+--------------- 不明 | 不明 (1 行) mysql=# select ifnull('1','2'),pg_typeof(ifnull('1','2')); ifnull | pg_typeof --------+--------------- 1 | テキスト (1行)
ルール 3: 入力が未知の型であり、特定の未知ではない型である場合、入力は未知ではない型に解析されます。
mysql=# select pg_typeof(current_date),pg_typeof('20230801'); pg_typeof | pg_typeof -----------+--------------- 日付 | 不明 (1 行) mysql=# select ifnull(current_date,'20230801'),pg_typeof(ifnull(current_date,'20230801')); ifnull | pg_typeof ------------+--------------- 2023-08-10 | 日付 (1行)
ルール 4: 不明でない型が複数ある場合は、enum 型をテキスト型として扱い、比較します。
mysql=# タイプの性別を enum('boy','girl') として作成します。 CREATE TYPE mysql=# select pg_typeof('boy'::gender),pg_typeof('girl'::varchar); pg_typeof | pg_typeof -----------+------------------- 性別 | 文字の変化 (1 行) mysql=# select ifnull('boy'::gender,'girl'::varchar),pg_typeof(ifnull('boy'::gender,'girl'::varchar)); ifnull | pg_typeof --------+--------------- 男の子 | テキスト (1行)
ルール 5: 入力タイプが同じタイプ カテゴリに属する場合、そのタイプの優先度が高いタイプが選択されます。異なるタイプ カテゴリの場合は、テキスト タイプに解析されます。
-- 同類型范畴 mysql=# select pg_typeof(1),pg_typeof(2.0); pg_typeof | pg_typeof -----------+--------------- 整数 | 数値 (1 行) mysql=# select ifnull(1,2.0),pg_typeof(ifnull(1,2.0)); ifnull | pg_typeof --------+--------------- 1 | 数値 (1 行) --異なる種類范畴 mysql=# select pg_typeof(1),pg_typeof(current_date); pg_typeof | pg_typeof -----------+--------------- 整数 | 日付 (1 行) mysql=# select ifnull(1,current_date),pg_typeof(ifnull(1,current_date)); ifnull | pg_typeof --------+--------------- 1 | テキスト (1行)
ルール 6: すべての入力を選択したタイプに変換します。指定された入力から選択された型への暗黙的な変換がない場合は失敗します。
--json はテキストへの暗黙的な変換が存在しません mysql=# select pg_typeof(1),pg_typeof('{"a":1}'::json); pg_typeof | pg_typeof ----------- + ----------- integer | json (1 row) mysql=# select ifnull(1,'{"a":1}'::json),pg_typeof(ifnull(1,'{" a ":1}'::json)); エラー: IFNULL はタイプ json をテキストに変換できませんでした 行 1: select ifnull(1,'{"a":1}'::json),pg_typeof(ifnull(1, ' {"a":1}... ^ CONTEXT: 参照される列: ifnull -- 型変換を明示的に指定してみることができます mysql=# select ifnull(1,'{"a":1}'::json::)テキスト ); ifnull -------- 1 (1 行)
クリックしてフォローして、Huawei Cloudの最新テクノロジーについて初めて学びましょう~
工業情報化省: 未登録のアプリにはネットワーク アクセス サービスを提供しない Go 1.21 が正式リリースRuan Yifeng が 「 TypeScript チュートリアル」をリリース Vim の父 Bram Moolenaar 氏が病気で死去 自社開発カーネルLinus が個人的にコードをレビュー, Bcachefs ファイル システムによって引き起こされた「内紛」を鎮めることを望んでいます. ByteDance はパブリック DNS サービスを開始しました. 素晴らしい, 今月 Linux カーネル メインラインにコミットしました