式の評価における型変換
式内の演算子が異なる型のオペランドと共に使用される場合、オペランド間の互換性を持たせるためにデータ型変換が発生します。
変換方式
OceanBaseデータベースは、明示的なデータ型変換と暗黙的なデータ型変換をサポートしています。
暗黙的なデータ型変換は、ある操作で指定されたデータ型のパラメータが必要な場合に、ステートメントの実際のパラメータ値がその指定データ型ではないときに発生します。このような場合、OceanBaseデータベースは実際のパラメータ値を指定データ型に変換した後、後続の操作を実行します。
以下の例のように、OceanBaseデータベースは必要に応じて文字列を数値に自動的に変換し、その逆も同様です。
obclient> SELECT 1+'1';
+-------+
| 1+'1' |
+-------+
| 2 |
+-------+
1 row in set
OceanBaseデータベースにおける明示的なデータ型変換は、CAST 関数を使用して実現します。以下の例を参照してください:
obclient> SELECT 31.4, CAST(31.4 AS TIME);
+------+--------------------+
| 31.4 | CAST(31.4 AS TIME) |
+------+--------------------+
| 31.4 | 00:00:31 |
+------+--------------------+
1 row in set
CAST関数の詳細については、CASTを参照してください。
変換ルール
比較演算の変換ルール
1つまたは2つのパラメータが
NULLの場合、比較結果はNULLとなりますが、<=>比較演算子は例外です。NULL <=> NULLの結果は真であり、変換は不要です。比較演算において、両方のパラメータが文字列の場合、それらは文字列として比較されます。
両方のパラメータが整数の場合、それらは整数として比較されます。
数字と比較しない場合、16進数値はバイナリ文字列と見なされます。
1つまたは複数のテーブルからの単一行のサブクエリは、定数と見なされません。例えば、サブクエリが返す整数を
DATETIME値と比較する場合、両方のオペランドは整数として扱われます。整数は時間値に変換されないため、オペランドをDATETIME値と比較するには、CAST()を使用してサブクエリの値を明示的にDATETIMEに変換してください。一方のパラメータが
TIMESTAMPまたはDATETIME列で、もう一方のパラメータが定数の場合、その定数は比較実行前にタイムスタンプに変換されます。安全性のため、比較時は常に完全な日付時刻、日付、または時間の文字列を使用してください。例えば、BETWEENを日付または時間値と共に使用する場合、最適な結果を得るには、CAST()を使用して値を必要なデータ型に明示的に変換してください。一方のパラメータが10進数値の場合、比較の種類はもう一方のパラメータによって決まります。もう一方のパラメータが10進数または整数値の場合、パラメータは10進数値として比較されます。もう一方のパラメータが浮動小数点数値の場合、浮動小数点数値として比較されます。
上記以外の場合、パラメータは浮動小数点(実数)として比較されます。例えば、文字列と数値オペランドの比較は、浮動小数点数としての比較として行われます。
浮動小数点数と
INTEGER型の大きな値との比較は近似されます。これは、整数が比較前に倍精度浮動小数点数に変換され、64ビット整数を正確に表現できないためです。
型のダウングレード
比較の意味論を満たす条件の下で、定数を可能な限り列型に合わせることで、効率的な比較を実現します。現在、標準的な意味論に基づくほとんどの列と定数の比較をサポートしています。残りの場合は、列に cast を追加した比較となり、後ほど個別に説明します:
intまたはuint列は、定数として文字型、decimal型、浮動小数点数(double/float)型が整数型列にダウングレードされます。decimal列は、浮動小数点数(double/float)型がdecimal列にダウングレードされます。year列は、数値型(整数型、浮動小数点数、decimal型を含む)、文字型、時間型(date、datetime、timestamp、timeなどを含む)がyear列にダウングレードされます。date列は、文字型、datetime、timestamp、time型がdate列にダウングレードされます。timestamp列は、datetimeがtimestampにダウングレードされます。
現在、列に cast を追加するシナリオ(array、xml、json などの複雑な型は除く)は以下のとおりです:
float列は、float列の比較は常にdoubleで行われるため、float列と任意の定数を比較する場合はcastが追加されます。fixed double列、つまり作成時に ps を指定した列、例えばdouble(10, 2)の場合、定数の小数点以下の桁数が多いと精度が揃わず、列にcastが追加されます。- 文字列は、文字と数値型の比較は数値順で行われるため、列を数値型に
castする必要があります。また、文字セットの比較ルールによっては、列にcastが追加される場合もあります。
非標準比較
非標準比較のロジックは型のダウングレード処理と似ており、定数と列の比較のみを処理し、列と列の直接比較は処理しません。現在、整数定数と文字列の比較のみをサポートしています。なお、range に設定した場合、ソート結果が標準的な意味論に従わない可能性があるため、データの特性に応じて設定を選択する必要があります。
以下の例を参照してください:
t1という名前のテーブルを作成しました。このテーブルにはbiz_day列が1つあり、型はvarchar(10)です。obclient> CREATE TABLE t1 (biz_day varchar(10)); Query OK, 0 rows affected (0.216 sec)デフォルトの実行計画を確認します。
obclient> EXPLAIN SELECT * FROM t1 WHERE biz_day BETWEEN 20200101 AND 20200201; +-------------------------------------------------------------------------------------------------------------------------------------------------+ | Query Plan | +-------------------------------------------------------------------------------------------------------------------------------------------------+ | ====================================================== | | |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | | ------------------------------------------------------ | | |0 |COLUMN TABLE FULL SCAN|T1 |2 |3 | | | ====================================================== | | Outputs & filters: | | ------------------------------------- | | 0 - output([T1.BIZ_DAY]), filter([cast(T1.BIZ_DAY, NUMBER(-1, -85)) >= 20200101], [cast(T1.BIZ_DAY, NUMBER(-1, -85)) <= 20200201]), rowset=16 | | access([T1.BIZ_DAY]), partitions(p0) | | is_index_back=false, is_global_index=false, filter_before_indexback[false,false], | | range_key([T1.__pk_increment]), range(MIN ; MAX)always true | +-------------------------------------------------------------------------------------------------------------------------------------------------+ 11 rows in set (0.004 sec)- クエリプラン:デフォルトでは、クエリオプティマイザーは
biz_day列を文字列型から数値型に変換してから比較します。 - フィルター条件:
filter([cast(T1.BIZ_DAY, NUMBER(-1, -85)) >= 20200101], [cast(T1.BIZ_DAY, NUMBER(-1, -85)) <= 20200201])は、biz_dayを数値型に変換した後で範囲比較を行うことを意味します。
- クエリプラン:デフォルトでは、クエリオプティマイザーは
non_standard_comparison_levelをequalに設定して実行計画を確認します。obclient> EXPLAIN SELECT /*+opt_param('non_standard_comparison_level', 'equal')*/ * FROM t1 WHERE biz_day BETWEEN 20200101 AND 20200201; +----------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Query Plan | +----------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ====================================================== | | |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | | ------------------------------------------------------ | | |0 |COLUMN TABLE FULL SCAN|t1 |1 |3 | | | ====================================================== | | Outputs & filters: | | ------------------------------------- | | 0 - output([t1.biz_day]), filter([cast(t1.biz_day, DECIMAL(-1, -1)) >= cast(20200101, DECIMAL(20, 0))], [cast(t1.biz_day, DECIMAL(-1, -1)) <= cast(20200201, | | DECIMAL(20, 0))]), rowset=16 | | access([t1.biz_day]), partitions(p0) | | is_index_back=false, is_global_index=false, filter_before_indexback[false,false], | | range_key([t1.__pk_increment]), range(MIN ; MAX)always true | +----------------------------------------------------------------------------------------------------------------------------------------------------------------+ 12 rows in set (0.003 sec)- クエリプラン:
non_standard_comparison_levelをequalに設定すると、クエリオプティマイザーはbiz_day列を文字列型から10進数型に変換してから比較します。 - フィルター条件:
filter([cast(t1.biz_day, DECIMAL(-1, -1)) >= cast(20200101, DECIMAL(20, 0))], [cast(t1.biz_day, DECIMAL(-1, -1)) <= cast(20200201, DECIMAL(20, 0))])は、biz_dayを10進数型に変換した後で範囲比較を行うことを意味します。betweenは範囲クエリであるため、equalの等価比較ルールは定数型を変換しません。
- クエリプラン:
non_standard_comparison_levelをrangeに設定して実行計画を確認します。obclient> EXPLAIN SELECT /*+opt_param('non_standard_comparison_level', 'range')*/ * FROM t1 WHERE biz_day BETWEEN 20200101 AND 20200201; +-------------------------------------------------------------------------------------------------------------------------------------------------------+ | Query Plan | +-------------------------------------------------------------------------------------------------------------------------------------------------------+ | ====================================================== | | |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | | ------------------------------------------------------ | | |0 |COLUMN TABLE FULL SCAN|t1 |1 |3 | | | ====================================================== | | Outputs & filters: | | ------------------------------------- | | 0 - output([t1.biz_day]), filter([t1.biz_day >= demote_cast(20200101, VARCHAR(10))], [t1.biz_day <= demote_cast(20200201, VARCHAR(10))]), rowset=16 | | access([t1.biz_day]), partitions(p0) | | is_index_back=false, is_global_index=false, filter_before_indexback[false,false], | | range_key([t1.__pk_increment]), range(MIN ; MAX)always true | +-------------------------------------------------------------------------------------------------------------------------------------------------------+ 11 rows in set (0.004 sec)- クエリプラン:
non_standard_comparison_levelをrangeに設定すると、クエリオプティマイザーは整数定数を文字列型に変換してから比較します。 - フィルター条件:
filter([t1.biz_day >= demote_cast(20200101, VARCHAR(10))], [t1.biz_day <= demote_cast(20200201, VARCHAR(10))])は、整数定数を文字列に変換した後で範囲比較を行うことを意味します。非標準比較後は照合順序で比較されます。例えば、'202002010'は照合順序では['20200101', '20200201']に属しますが、数値順序の範囲[20200101, 20200201]には属しません。
- クエリプラン:
その他の関連する変換ルール
文字列から浮動小数点数への変換方法と整数から浮動小数点数への変換方法は必ずしも同じではありません。結果はコンパイラのバージョンなどの要因の影響を受ける可能性があります。このような問題を回避する方法の一つは、値が暗黙的に浮動小数点数に変換されないようにするために
CAST()を使用することです。数値または時刻値を暗黙的に文字列に変換すると、
character_set_connectionおよびcollation_connectionシステム変数によって決定される文字セットと照合順序を持つ値が生成されます。これは、このような変換が非バイナリ文字列(
CHAR、VARCHAR、またはLONGTEXT値)を生成することを意味します。接続の文字セットをバイナリに設定すると、変換結果はバイナリ文字列(BINARY、VARBINARY、またはLONGBLOB値)になります。整数式の評価時の型変換にはいくつかの違いがあります。例えば、整数式をCREATE TABLEステートメントの一部として使用する場合、式の結果型に応じて、作成される新しいテーブルには
INTまたはBIGINT型の列が含まれます:CREATE TABLE t SELECT integer_expr;式の最大長が
INTに適さない場合はBIGINTが使用されます。しかし、十分に長い式を使用することで、INTではなくBIGINTの使用を強制できます:obclient> CREATE TABLE t SELECT 000000000000000000000; Query OK, 1 row affected
暗黙的なデータ型変換ルール
データ型変換が意味をなす場合、OceanBaseデータベースは値を自動的にあるデータ型から別のデータ型に変換します。
以下の表は、すべてのデータ型の暗黙的変換マトリックスであり、変換の方向や文脈を考慮する必要はありません。
データ型 |
BOOL |
INT |
SMALLINT |
MEDIUMINT |
BIGINT |
SERIAL |
DECIMAL |
NUMERIC |
FLOAT |
DOUBLE |
BIT |
DATETIME |
TIMESTAMP |
DATE |
TIME |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| YEAR | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| CHAR | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| VARCHAR | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| BINARY | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| VARBINARY | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| TEXT | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| TINYBLOB | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| MEDIUMBLOB | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| LONGBLOB | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| TINYTEXT | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| MEDIUMTEXT | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| LONGTEXT | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| ENUM | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| SET | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| BLOB | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
暗黙的なデータ型変換の例
obclient> SELECT CAST(BOOL_COLUMN AS YEAR) FROM YOUR_TABLE;
+---------------------------+
| CAST(BOOL_COLUMN AS YEAR) |
+---------------------------+
| 2001 |
+---------------------------+
2 rows in set (0.001 sec)