ANSI 準拠

Spark 3.0 から、Spark SQL は SQL 標準に準拠する2つの実験的なオプションを導入します: spark.sql.ansi.enabled および spark.sql.storeAssignmentPolicy (詳細は以下の表をみてください)。

spark.sql.ansi.enabledtrueに設定された場合、Spark SQLはHive準拠ではなくANSI準拠のダイアレクトを使います。例えば、SQLオペレータ/関数への入力が無効な場合、Sparkはnullの結果を返す代わりに実行時に例外を投げます。一部のANSIダイアレクト機能は、ANSI SQL標準から直接のものではない場合がありますが、それらの動作はANSI SQLの様式と一致しています。

さらに、Spark SQL には、テーブルに行を挿入する時の暗黙のキャスト動作を制御するための独立したオプションがあります。キャスト動作は、標準でストア割り当てルールとして定義されています。

spark.sql.storeAssignmentPolicyANSI に設定されている場合、Spark SQL は ANSI ストア割り当てルールに準拠します。デフォルト値は ANSI のため、これは別の構成です。一方で構成 spark.sql.ansi.enabled はデフォルトで無効です。

プロパティ名 デフォルト 意味 これ以降のバージョンから
spark.sql.ansi.enabled false (実験的) true の場合、Spark は ANSI SQL 仕様に準拠しようとします:1. 整数/10進数フィールドの操作でオーバーフローが発生した場合、Spark は実行時例外を投げます。
2. Spark は ANSI SQL の予約済みキーワードを SQL パーサの識別子として使うことに注意してください。Spark will forbid using the reserved keywords of ANSI SQL as identifiers in the SQL parser.
3.0.0
spark.sql.storeAssignmentPolicy ANSI (実験的) 値を異なるデータ型の列に挿入する場合、Sparkは型の変換を実行します。現在のところ、型強制ルールの3つのポリシーがサポートされます: ANSI、legacy、strict。ANSI ポリシーでは、Spark は ANSI SQL に従って型強制を実行します。実際には、PostgreSQLとほとんど同じ挙動です。文字列を int、または double を boolean に変換するなど、特定の不合理な型変換を許可しません。legacy ポリシーでは、Spark は有効なキャストである限り、型強制を許可します。これは非常に緩やかなものです。例えば、文字列を int、または double を boolean に変換することができます。これも、Spark 2.x での唯一の動作で、Hive と互換性があります。strict ポリシーでは、Spark は型強制で起こりえる精度の損失あるいはデータの切り捨てを許可しません。例えば、double を int、あるいは decimal を double に変換することはできません。 3.0.0

以下のサブセクションでは、ANSI モードが有効な場合の算術演算、型変換、SQL解析の動作の変更について説明します。Spark SQLの型変換は3種類あります。この記事ではキャスト、ストア割り当て、型強制を1つずつ紹介します。

Arithmetic オペレーション

Spark SQL では、数値型(10進数を除く)で実行される算術演算は、デフォルトではオーバーフローがチェックされません。つまり、操作によってオーバーフローが発生した場合、結果は Java/Scala プログラムの対応する操作と同じものになります (例えば、2つの整数の合計が表現可能な最大値よりも大きい場合、結果は負の数になります)。一方、Spark SQL は10進数のオーバーフローに対して null を返します。spark.sql.ansi.enabledtrue に設定されて、数値および間隔の算術演算でオーバーフローが発生すると、実行時に算術例外が投げられます。

-- `spark.sql.ansi.enabled=true`
SELECT 2147483647 + 1;
java.lang.ArithmeticException: integer overflow

SELECT abs(-2147483648);
java.lang.ArithmeticException: integer overflow

-- `spark.sql.ansi.enabled=false`
SELECT 2147483647 + 1;
+----------------+
|(2147483647 + 1)|
+----------------+
|     -2147483648|
+----------------+

SELECT abs(-2147483648);
+----------------+
|abs(-2147483648)|
+----------------+
|     -2147483648|
+----------------+

Cast

spark.sql.ansi.enabledtrue に設定されている場合、CAST 構文による明示的なキャストは、標準で定義されたキャストパターンの違反のため実行時例外を投げます。例えば、文字列から数値。

Spark ANSIモードのCAST句は、ISO/IEC 9075-2:2011 Information technology — Database languages - SQL — Part 2: Foundation (SQL/Foundation)のセクション6.13 “cast specification”の構文規則に従います。ただし、ANSI規格では許可されていない次の単純な型変換を特別に許可します:

CAST式のソースデータとターゲットデータ型の有効な組み合わせは、次の表に示されています。“Y” は組み合わせが制限なしで構文的に有効であることを示し、“N” は組み合わせが無効であることを示します。

Source\Target Numeric 文字列 Date Timestamp Interval 真偽値 Binary 配列 Map Struct
Numeric Y Y N N N Y N N N N
文字列 Y Y Y Y Y Y Y N N N
Date N Y Y Y N N N N N N
Timestamp N Y Y Y N N N N N N
Interval N Y N N Y N N N N N
真偽値 Y Y N N N Y N N N N
Binary N Y N N N N Y N N N
配列 N Y N N N N N Y N N
Map N Y N N N N N N Y N
Struct N Y N N N N N N N Y

上記の表では、ランタイム例外を引き起こす可能性のある全てのCASTが赤のYでマークされています:

-- 明示的なキャストの例

-- `spark.sql.ansi.enabled=true`
SELECT CAST('a' AS INT);
java.lang.NumberFormatException: invalid input syntax for type numeric: a

SELECT CAST(2147483648L AS INT);
java.lang.ArithmeticException: Casting 2147483648 to int causes overflow

SELECT CAST(DATE'2020-01-01' AS INT)
org.apache.spark.sql.AnalysisException: cannot resolve 'CAST(DATE '2020-01-01' AS INT)' due to data type mismatch: cannot cast date to int.
To convert values from date to int, you can use function UNIX_DATE instead.

-- `spark.sql.ansi.enabled=false` (This is a default behaviour)
SELECT CAST('a' AS INT);
+--------------+
|CAST(a AS INT)|
+--------------+
|          null|
+--------------+

SELECT CAST(2147483648L AS INT);
+-----------------------+
|CAST(2147483648 AS INT)|
+-----------------------+
|            -2147483648|
+-----------------------+

SELECT CAST(DATE'2020-01-01' AS INT)
+------------------------------+
|CAST(DATE '2020-01-01' AS INT)|
+------------------------------+
|                          null|
+------------------------------+

-- Examples of store assignment rules
CREATE TABLE t (v INT);

-- `spark.sql.storeAssignmentPolicy=ANSI`
INSERT INTO t VALUES ('1');
org.apache.spark.sql.AnalysisException: Cannot write incompatible data to table '`default`.`t`':
- Cannot safely cast 'v': string to int;

-- `spark.sql.storeAssignmentPolicy=LEGACY` (This is a legacy behaviour until Spark 2.x)
INSERT INTO t VALUES ('1');
SELECT * FROM t;
+---+
|  v|
+---+
|  1|
+---+

ストアの割り当て

冒頭で述べたように、spark.sql.storeAssignmentPolicyANSI(デフォルト値)に設定された場合、Spark SQL はテーブル挿入に関するANSIストア割り当てルールに準拠します。テーブル挿入におけるソースデータ型とターゲットデータ型の有効な組み合わせは、次の表に示されています。

Source\Target Numeric 文字列 Date Timestamp Interval 真偽値 Binary 配列 Map Struct
Numeric Y Y N N N N N N N N
文字列 N Y N N N N N N N N
Date N Y Y Y N N N N N N
Timestamp N Y Y Y N N N N N N
Interval N Y N N N* N N N N N
真偽値 N Y N N N Y N N N N
Binary N Y N N N N Y N N N
配列 N N N N N N N Y** N N
Map N N N N N N N N Y** N
Struct N N N N N N N N N Y**

* Sparkはinterval型テーブルカラムをサポートしません。

** 配列/マップ/構造体型の場合、データ型チェックルールはそのコンポーネント要素に再帰的に適用されます。

テーブルの挿入中に、Sparkは数値のオーバーフロー時に例外を投げます。

CREATE TABLE test(i INT);

INSERT INTO test VALUES (2147483648L);
java.lang.ArithmeticException: Casting 2147483648 to int causes overflow

型強制

タイプの昇格と優先順位

spark.sql.ansi.enabledtrueに設定されている場合、Spark SQLは、データ型間の競合の解決方法を管理するいくつかのルールを使います。この競合解決の中心となるのは、特定のデータ型の値を別のデータ型に暗黙的に昇格できるかどうかを定義する型優先順位リストです。

データタイプ 優先順位リスト(最も狭いものから最も広いものへ)
Byte Byte -> Short -> Int -> Long -> Decimal -> Float* -> Double
Short Short -> Int -> Long -> Decimal-> Float* -> Double
Int Int -> Long -> Decimal -> Float* -> Double
Long Long -> Decimal -> Float* -> Double
小数 Decimal -> Float* -> Double
Float Float -> Double
Double Double
Date Date -> Timestamp
Timestamp Timestamp
文字列 文字列
Binary Binary
真偽値 真偽値
Interval Interval
Map Map**
配列 Array**
Struct Struct**

* For least common type resolution float is skipped to avoid loss of precision.

** 複合型の場合、優先順位リストはそのコンポーネント要素に再帰的に適用されます。

文字列リテラルと型指定されていないNULLには、特別な規則が適用されます。NULLは他の型に昇格できますが、文字列リテラルは任意の単純なデータ型に昇格できます。

これは優先順位リストを有向ツリーとしてグラフィカルに表現したものです: 型優先順位リスト

最も一般的ではない型の解決

型のセットの中で最も一般的ではない型は、型のセットの全ての要素が優先順位リストから到達できる最も狭い型です。

もっとも一般的ではない型の解決は、以下の目的で使われます:

-- coalesce関数は、最も一般的ではない型を共有している限り、引数型の任意のセットを受け入れます。
-- The result type is the least common type of the arguments.
> SET spark.sql.ansi.enabled=true;
> SELECT typeof(coalesce(1Y, 1L, NULL));
BIGINT
> SELECT typeof(coalesce(1, DATE'2020-01-01'));
Error: Incompatible types [INT, DATE]

> SELECT typeof(coalesce(ARRAY(1Y), ARRAY(1L)));
ARRAY<BIGINT>
> SELECT typeof(coalesce(1, 1F));
DOUBLE
> SELECT typeof(coalesce(1L, 1F));
DOUBLE
> SELECT (typeof(coalesce(1BD, 1F)));
DOUBLE

-- The substring function expects arguments of type INT for the start and length parameters.
> SELECT substring('hello', 1Y, 2);
he
> SELECT substring('hello', '1', 2);
he
> SELECT substring('hello', 1L, 2);
Error: Argument 2 requires an INT type.
> SELECT substring('hello', str, 2) FROM VALUES(CAST('1' AS STRING)) AS T(str);
Error: Argument 2 requires an INT type.

SQL 関数

一部のSQL関数の動作はANSIモード(spark.sql.ansi.enabled=true) で異なる場合があります。

SQL オペレータ

一部のSQLオペレータの動作はANSIモード(spark.sql.ansi.enabled=true) で異なる場合があります。

ANSIモードで役立つ機能

ANSIモードがオンの場合、無効な操作に対して例外が投げられます。以下のSQLの機能を使って、そのような例外を抑制することができます。

SQL キーワード

spark.sql.ansi.enabled が true の場合、Spark SQL は ANSI モードパーサを使います。このモードでは、Spark SQL には2種類のキーワードがあります:

ANSI モードが無効の場合、Spark SQL には2種類のキーワードがあります:

デフォルトでは、spark.sql.ansi.enabled は false です。

以下は Spark SQL の全てのキーワードのリストです。

キーワード Spark SQL
ANSI モード
Spark SQL
デフォルトモード
SQL-2016
ADD 予約無し 予約無し 予約無し
AFTER 予約無し 予約無し 予約無し
ALL 予約 予約無し 予約
ALTER 予約無し 予約無し 予約
ANALYZE 予約無し 予約無し 予約無し
AND 予約 予約無し 予約
ANTI 予約無し 厳格な予約無し 予約無し
ANY 予約 予約無し 予約
ARCHIVE 予約無し 予約無し 予約無し
ARRAY 予約無し 予約無し 予約
AS 予約 予約無し 予約
ASC 予約無し 予約無し 予約無し
AT 予約無し 予約無し 予約
AUTHORIZATION 予約 予約無し 予約
BETWEEN 予約無し 予約無し 予約
BOTH 予約 予約無し 予約
BUCKET 予約無し 予約無し 予約無し
BUCKETS 予約無し 予約無し 予約無し
BY 予約無し 予約無し 予約
CACHE 予約無し 予約無し 予約無し
CASCADE 予約無し 予約無し 予約無し
CASE 予約 予約無し 予約
CAST 予約 予約無し 予約
CHANGE 予約無し 予約無し 予約無し
CHECK 予約 予約無し 予約
CLEAR 予約無し 予約無し 予約無し
CLUSTER 予約無し 予約無し 予約無し
CLUSTERED 予約無し 予約無し 予約無し
CODEGEN 予約無し 予約無し 予約無し
COLLATE 予約 予約無し 予約
COLLECTION 予約無し 予約無し 予約無し
COLUMN 予約 予約無し 予約
カラム 予約無し 予約無し 予約無し
COMMENT 予約無し 予約無し 予約無し
COMMIT 予約無し 予約無し 予約
COMPACT 予約無し 予約無し 予約無し
COMPACTIONS 予約無し 予約無し 予約無し
COMPUTE 予約無し 予約無し 予約無し
CONCATENATE 予約無し 予約無し 予約無し
CONSTRAINT 予約 予約無し 予約
COST 予約無し 予約無し 予約無し
CREATE 予約 予約無し 予約
CROSS 予約 厳格な予約無し 予約
CUBE 予約無し 予約無し 予約
CURRENT 予約無し 予約無し 予約
CURRENT_DATE 予約 予約無し 予約
CURRENT_TIME 予約 予約無し 予約
CURRENT_TIMESTAMP 予約 予約無し 予約
CURRENT_USER 予約 予約無し 予約
DATA 予約無し 予約無し 予約無し
DATABASE 予約無し 予約無し 予約無し
DATABASES 予約無し 予約無し 予約無し
DAY 予約無し 予約無し 予約無し
DBPROPERTIES 予約無し 予約無し 予約無し
DEFINED 予約無し 予約無し 予約無し
DELETE 予約無し 予約無し 予約
DELIMITED 予約無し 予約無し 予約無し
DESC 予約無し 予約無し 予約無し
DESCRIBE 予約無し 予約無し 予約
DFS 予約無し 予約無し 予約無し
DIRECTORIES 予約無し 予約無し 予約無し
DIRECTORY 予約無し 予約無し 予約無し
DISTINCT 予約 予約無し 予約
DISTRIBUTE 予約無し 予約無し 予約無し
DIV 予約無し 予約無し not a keyword
DROP 予約無し 予約無し 予約
ELSE 予約 予約無し 予約
END 予約 予約無し 予約
ESCAPE 予約 予約無し 予約
ESCAPED 予約無し 予約無し 予約無し
EXCEPT 予約 厳格な予約無し 予約
EXCHANGE 予約無し 予約無し 予約無し
EXISTS 予約無し 予約無し 予約
EXPLAIN 予約無し 予約無し 予約無し
EXPORT 予約無し 予約無し 予約無し
EXTENDED 予約無し 予約無し 予約無し
EXTERNAL 予約無し 予約無し 予約
EXTRACT 予約無し 予約無し 予約
FALSE 予約 予約無し 予約
FETCH 予約 予約無し 予約
FIELDS 予約無し 予約無し 予約無し
FILTER 予約 予約無し 予約
FILEFORMAT 予約無し 予約無し 予約無し
FIRST 予約無し 予約無し 予約無し
FOLLOWING 予約無し 予約無し 予約無し
FOR 予約 予約無し 予約
FOREIGN 予約 予約無し 予約
FORMAT 予約無し 予約無し 予約無し
FORMATTED 予約無し 予約無し 予約無し
FROM 予約 予約無し 予約
FULL 予約 厳格な予約無し 予約
FUNCTION 予約無し 予約無し 予約
FUNCTIONS 予約無し 予約無し 予約無し
GLOBAL 予約無し 予約無し 予約
GRANT 予約 予約無し 予約
GROUP 予約 予約無し 予約
グループ 予約無し 予約無し 予約
HAVING 予約 予約無し 予約
HOUR 予約無し 予約無し 予約無し
IF 予約無し 予約無し not a keyword
IGNORE 予約無し 予約無し 予約無し
IMPORT 予約無し 予約無し 予約無し
IN 予約 予約無し 予約
INDEX 予約無し 予約無し 予約無し
INDEXES 予約無し 予約無し 予約無し
INNER 予約 厳格な予約無し 予約
INPATH 予約無し 予約無し 予約無し
INPUTFORMAT 予約無し 予約無し 予約無し
INSERT 予約無し 予約無し 予約
INTERSECT 予約 厳格な予約無し 予約
INTERVAL 予約無し 予約無し 予約
INTO 予約 予約無し 予約
IS 予約 予約無し 予約
ITEMS 予約無し 予約無し 予約無し
JOIN 予約 厳格な予約無し 予約
KEYS 予約無し 予約無し 予約無し
LAST 予約無し 予約無し 予約無し
LATERAL 予約 厳格な予約無し 予約
LAZY 予約無し 予約無し 予約無し
LEADING 予約 予約無し 予約
LEFT 予約 厳格な予約無し 予約
LIKE 予約無し 予約無し 予約
LIMIT 予約無し 予約無し 予約無し
LINES 予約無し 予約無し 予約無し
LIST 予約無し 予約無し 予約無し
LOAD 予約無し 予約無し 予約無し
LOCAL 予約無し 予約無し 予約
LOCATION 予約無し 予約無し 予約無し
LOCK 予約無し 予約無し 予約無し
LOCKS 予約無し 予約無し 予約無し
LOGICAL 予約無し 予約無し 予約無し
MACRO 予約無し 予約無し 予約無し
MAP 予約無し 予約無し 予約無し
MATCHED 予約無し 予約無し 予約無し
MERGE 予約無し 予約無し 予約無し
MINUTE 予約無し 予約無し 予約無し
MINUS 予約無し 厳格な予約無し 予約無し
MONTH 予約無し 予約無し 予約無し
MSCK 予約無し 予約無し 予約無し
NAMESPACE 予約無し 予約無し 予約無し
NAMESPACES 予約無し 予約無し 予約無し
NATURAL 予約 厳格な予約無し 予約
いいえ 予約無し 予約無し 予約
NOT 予約 予約無し 予約
NULL 予約 予約無し 予約
NULLS 予約無し 予約無し 予約無し
OF 予約無し 予約無し 予約
互換性のためのものでダミーの変数です。 予約 厳格な予約無し 予約
ONLY 予約 予約無し 予約
OPTION 予約無し 予約無し 予約無し
オプション 予約無し 予約無し 予約無し
OR 予約 予約無し 予約
ORDER 予約 予約無し 予約
OUT 予約無し 予約無し 予約
OUTER 予約 予約無し 予約
OUTPUTFORMAT 予約無し 予約無し 予約無し
OVER 予約無し 予約無し 予約無し
OVERLAPS 予約 予約無し 予約
OVERLAY 予約無し 予約無し 予約無し
OVERWRITE 予約無し 予約無し 予約無し
PARTITION 予約無し 予約無し 予約
PARTITIONED 予約無し 予約無し 予約無し
PARTITIONS 予約無し 予約無し 予約無し
PERCENT 予約無し 予約無し 予約無し
PIVOT 予約無し 予約無し 予約無し
PLACING 予約無し 予約無し 予約無し
POSITION 予約無し 予約無し 予約
PRECEDING 予約無し 予約無し 予約無し
PRIMARY 予約 予約無し 予約
PRINCIPALS 予約無し 予約無し 予約無し
PROPERTIES 予約無し 予約無し 予約無し
PURGE 予約無し 予約無し 予約無し
QUERY 予約無し 予約無し 予約無し
RANGE 予約無し 予約無し 予約
RECORDREADER 予約無し 予約無し 予約無し
RECORDWRITER 予約無し 予約無し 予約無し
RECOVER 予約無し 予約無し 予約無し
REDUCE 予約無し 予約無し 予約無し
REFERENCES 予約 予約無し 予約
REFRESH 予約無し 予約無し 予約無し
REGEXP 予約無し 予約無し not a keyword
RENAME 予約無し 予約無し 予約無し
REPAIR 予約無し 予約無し 予約無し
REPLACE 予約無し 予約無し 予約無し
RESET 予約無し 予約無し 予約無し
RESPECT 予約無し 予約無し 予約無し
RESTRICT 予約無し 予約無し 予約無し
REVOKE 予約無し 予約無し 予約
RIGHT 予約 厳格な予約無し 予約
RLIKE 予約無し 予約無し 予約無し
ROLE 予約無し 予約無し 予約無し
ROLES 予約無し 予約無し 予約無し
ROLLBACK 予約無し 予約無し 予約
ROLLUP 予約無し 予約無し 予約
ROW 予約無し 予約無し 予約
ROWS 予約無し 予約無し 予約
SCHEMA 予約無し 予約無し 予約無し
SCHEMAS 予約無し 予約無し not a keyword
SECOND 予約無し 予約無し 予約無し
SELECT 予約 予約無し 予約
SEMI 予約無し 厳格な予約無し 予約無し
SEPARATED 予約無し 予約無し 予約無し
SERDE 予約無し 予約無し 予約無し
SERDEPROPERTIES 予約無し 予約無し 予約無し
SESSION_USER 予約 予約無し 予約
SET 予約無し 予約無し 予約
SETS 予約無し 予約無し 予約無し
SHOW 予約無し 予約無し 予約無し
SKEWED 予約無し 予約無し 予約無し
SOME 予約 予約無し 予約
SORT 予約無し 予約無し 予約無し
SORTED 予約無し 予約無し 予約無し
START 予約無し 予約無し 予約
STATISTICS 予約無し 予約無し 予約無し
STORED 予約無し 予約無し 予約無し
STRATIFY 予約無し 予約無し 予約無し
STRUCT 予約無し 予約無し 予約無し
SUBSTR 予約無し 予約無し 予約無し
SUBSTRING 予約無し 予約無し 予約無し
SYNC 予約無し 予約無し 予約無し
TABLE 予約 予約無し 予約
テーブル 予約無し 予約無し 予約無し
TABLESAMPLE 予約無し 予約無し 予約
TBLPROPERTIES 予約無し 予約無し 予約無し
TEMP 予約無し 予約無し not a keyword
TEMPORARY 予約無し 予約無し 予約無し
TERMINATED 予約無し 予約無し 予約無し
THEN 予約 予約無し 予約
TIME 予約 予約無し 予約
TO 予約 予約無し 予約
TOUCH 予約無し 予約無し 予約無し
TRAILING 予約 予約無し 予約
TRANSACTION 予約無し 予約無し 予約無し
TRANSACTIONS 予約無し 予約無し 予約無し
TRANSFORM 予約無し 予約無し 予約無し
TRIM 予約無し 予約無し 予約無し
TRUE 予約無し 予約無し 予約
TRUNCATE 予約無し 予約無し 予約
TRY_CAST 予約無し 予約無し 予約無し
TYPE 予約無し 予約無し 予約無し
UNARCHIVE 予約無し 予約無し 予約無し
UNBOUNDED 予約無し 予約無し 予約無し
UNCACHE 予約無し 予約無し 予約無し
UNION 予約 厳格な予約無し 予約
UNIQUE 予約 予約無し 予約
UNKNOWN 予約 予約無し 予約
UNLOCK 予約無し 予約無し 予約無し
UNSET 予約無し 予約無し 予約無し
UPDATE 予約無し 予約無し 予約
USE 予約無し 予約無し 予約無し
USER 予約 予約無し 予約
USING 予約 厳格な予約無し 予約
VALUES 予約無し 予約無し 予約
VIEW 予約無し 予約無し 予約無し
VIEWS 予約無し 予約無し 予約無し
WHEN 予約 予約無し 予約
WHERE 予約 予約無し 予約
WINDOW 予約無し 予約無し 予約
WITH 予約 予約無し 予約
YEAR 予約無し 予約無し 予約無し
ZONE 予約無し 予約無し 予約無し
TOP
inserted by FC2 system