式の評価における型変換
式内の演算子が異なる型の演算子と共に使用される場合、演算子間の互換性を持たせるためにデータ型の変換が行われます。
変換方法
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ビット整数のすべてを正確に表現できないためです。
型のダウングレード
比較の意味が満たされる条件の下で、可能な限り定数を列型に合わせてアライメントし、効率的な比較を実現します。現在、標準的な意味解釈ではほとんどすべての列と定数の比較をサポートしていますが、残りの場合は列にキャストを追加して比較します。詳細は後述します:
intまたはuint列では、定数との比較として、文字列型、decimal型、浮動小数点数(double/float)型が整数型列へのダウングレードをサポートします。decimal列では、浮動小数点数(double/float)型がdecimal列へのダウングレードをサポートします。year列では、数値型(整数型、浮動小数点数、decimal型を含む)、文字列型、時刻型(date、datetime、timestamp、timeなどを含む)がyear列へのダウングレードをサポートします。date列では、文字列型、datetime、timestamp、time型がdate列へのダウングレードをサポートします。timestamp列では、datetimeがtimestampへのダウングレードをサポートします。
現在、列にキャストを追加するシナリオ(配列、XML、JSONなどの複雑な型は除く)は以下のとおりです:
float列では、float列の比較は常にdoubleで行われるため、float列と任意の定数を比較する際にはcastが追加されます。fixed double列、つまりテーブル作成時に精度を指定したもの、例えばdouble(10, 2)のような場合、定数の小数点以下の桁数が多いときは精度を揃えて比較するため、列にキャストが追加されます。- 文字列列では、文字と数値型の比較は数値順で行われるため、列は数値型に
castされます。また、文字セットの比較ルールによっても列にcastが追加される場合があります。
非標準比較
非標準比較のロジックは、データ型のダウングレード処理と類似しており、定数と列の比較のみを処理し、列同士の比較については処理しません。現在、整数定数と文字列の比較のみをサポートしています。なお、range に設定した場合、ソート結果が標準的な意味合いと一致しない可能性があるため、データの特性に応じて選択する必要があります。
以下の例で説明します:
t1という名前のテーブルを作成し、biz_day列を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)