| Property Name | Default | Meaning | Since Version |
|---|---|---|---|
| spark.sql.ansi.enabled | false | 当为true时,Spark会尝试遵守ANSI SQL规范: 1。Spark SQL会对无效操作抛出运行时异常,包括整数溢出错误、字符串解析错误等。 2。Spark将使用不同的类型强制规则来解决数据类型之间的冲突。规则始终基于数据类型优先级。 | 3.0.0 |
| spark.sql.storeAssignmentPolicy | ANSI | 在不同数据类型的列中插入值时,Spark将执行类型转换。目前,Spark支持三种类型强制规则策略:ANSI、legacy和strict 1。使用ANSI策略,Spark根据ANSI SQL执行类型强制。在实践中,行为与PostgreSQL基本相同。它不允许某些不合理的类型转换,例如将字符串转换为int或将double转换为boolean。在插入数字类型列时,如果值超出目标数据类型的范围,则会引发溢出错误 2。对于遗留策略,Spark允许类型强制,只要它是有效的Cast,这是非常宽松的。例如,允许将string转换为int或将double转换为boolean。这也是仅在Spark2.x中的行为,并且与Hive兼容 3。通过严格的策略,Spark不允许在类型强制中出现任何可能的精度损失或数据截断,例如不允许将double转换为int或将decimal转换为double。 | 3.0.0 |
以下小节介绍了启用ANSI模式时算术运算、类型转换和SQL解析的行为变化。对于Spark SQL中的类型转换,有三种类型,本文将逐一介绍:强制转换、存储赋值和类型强制。
在Spark SQL中,默认情况下不检查对数字类型(十进制除外)执行的算术操作是否溢出。这意味着,如果一个操作导致溢出,其结果与Java/Scala程序中的相应操作相同(例如,如果两个integers的总和大于可表示的最大值,则结果为负数)。另一方面,Spark SQL对于小数溢出返回null。当spark.sql.ansi.enabled设置为true并且在数字和间隔(interval)算术运算中发生溢出时,它会抛出一个算术异常。
-- `spark.sql.ansi.enabled=true`
SELECT 2147483647 + 1;
org.apache.spark.SparkArithmeticException: [ARITHMETIC_OVERFLOW] integer overflow. Use 'try_add' to tolerate overflow and return NULL instead. If necessary set spark.sql.ansi.enabled to "false" to bypass this error.
== SQL(line 1, position 8) ==
SELECT 2147483647 + 1
^^^^^^^^^^^^^^
SELECT abs(-2147483648);
org.apache.spark.SparkArithmeticException: [ARITHMETIC_OVERFLOW] integer overflow. If necessary set spark.sql.ansi.enabled to "false" to bypass this error.
-- `spark.sql.ansi.enabled=false`
SELECT 2147483647 + 1;
+----------------+
|(2147483647 + 1)|
+----------------+
| -2147483648|
+----------------+
SELECT abs(-2147483648);
+----------------+
|abs(-2147483648)|
+----------------+
| -2147483648|
+----------------+
当spark.sql.ansi.enabled设置为true时,CAST语法的显式强制转换将为标准中定义的非法强制转换patterns抛出运行时异常,例如从string强制转换为integer。此外,ANSI SQL模式不允许以下类型转换,它们在ANSI模式关闭时是允许的:
下表给出了CAST表达式中源数据类型和目标数据类型的有效组合。“Y”表示该组合在语法上不受限制地有效,“N”表示该组合无效。
此表格见这里。
在上表中,所有具有新语法的CAST都标记为红色Y:
CAST(Numeric AS Numeric):如果值超出目标数据类型的范围,则引发溢出异常。
CAST(String AS (Numeric/Date/Timestamp/Timestamp_NTZ/Interval/Boolean)):如果无法将值解析为目标数据类型,则引发运行时异常。
CAST(Timestamp AS Numeric):如果自epoch以来的秒数超出目标数据类型的范围,则引发溢出异常。
CAST(Numeric AS Timestamp):如果numeric值乘以1000000(微秒/秒)超出Long类型的范围,则引发溢出异常。
CAST(Array AS Array):如果元素的转换中有异常,则引发异常。
CAST(Map AS Map):如果在key和value的转换上有任何异常,则引发异常。
CAST(Struct AS Struct):如果在struct字段的转换上存在异常,则引发异常。
CAST(Numeric AS String):将十进制值强制转换为字符串时,始终使用纯字符串表示法,而不是在需要指数时使用科学表示法
CAST(Interval AS Numeric):如果day-time interval的微秒数或year-month interval的月数超出目标数据类型的范围,则引发溢出异常。
CAST(Numeric AS Interval):如果目标interval的结束单位的numeric时间超出Int类型(year-month intervals)或Long类型(day-time intervals)的范围,则引发溢出异常。
-- Examples of explicit casting
-- `spark.sql.ansi.enabled=true`
SELECT CAST('a' AS INT);
org.apache.spark.SparkNumberFormatException: [CAST_INVALID_INPUT] The value 'a' of the type "STRING" cannot be cast to "INT" because it is malformed. Correct the value as per the syntax, or change its target type. Use `try_cast` to tolerate malformed input and return NULL instead. If necessary set "spark.sql.ansi.enabled" to "false" to bypass this error.
== SQL(line 1, position 8) ==
SELECT CAST('a' AS INT)
^^^^^^^^^^^^^^^^
SELECT CAST(2147483648L AS INT);
org.apache.spark.SparkArithmeticException: [CAST_OVERFLOW] The value 2147483648L of the type "BIGINT" cannot be cast to "INT" due to an overflow. Use `try_cast` to tolerate overflow and return NULL instead. If necessary set "spark.sql.ansi.enabled" to "false" to bypass this error.
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: [INCOMPATIBLE_DATA_FOR_TABLE.CANNOT_SAFELY_CAST] Cannot write incompatible data for table `spark_catalog`.`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|
+---+
当将带有小数的十进制数转换为以SECOND作为结束单位的间隔类型(如INTERVAL HOUR TO SECOND)时,Spark将小数部分舍入到“nearest neighbor”,除非两个邻居距离相等,在这种情况下,四舍五入。
如开头所述,当spark.sql.storeAssignmentPolicy被设置为ANSI(这是默认值)时,Spark SQL在表插入上遵循ANSI存储分配规则。下表给出了表插入中源数据类型和目标数据类型的有效组合。
此表格见这里。
在插入表时,Spark会在数值溢出时抛出异常。
CREATE TABLE test(i INT);
INSERT INTO test VALUES (2147483648L);
org.apache.spark.SparkArithmeticException: [CAST_OVERFLOW_IN_TABLE_INSERT] Fail to insert a value of "BIGINT" type into the "INT" type column `i` due to an overflow. Use `try_cast` on the input value to tolerate overflow and return NULL instead.
当spark.sql.ansi.enabled设置为true时,Spark SQL使用一些规则来管理如何解决数据类型之间的冲突。这种冲突解决的核心是类型优先级列表,它定义了给定数据类型的值是否可以隐式地提升到另一个数据类型。
| Data type | precedence list(从最窄到最宽) |
|---|---|
| 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 | Decimal -> Float* -> Double |
| Float | Float -> Double |
| Double | Double |
| Date | Date -> Timestamp_NTZ -> Timestamp |
| Timestamp | Timestamp |
| String | String, Long -> Double, Date -> Timestamp_NTZ -> Timestamp , Boolean, Binary ** |
| Binary | Binary |
| Boolean | Boolean |
| Interval | Interval |
| Map | Map*** |
| Array | Array*** |
| Struct | Struct*** |

一组类型中最不常见的类型是该组类型的所有元素从优先级列表中可访问的最窄(narrowest)类型。
最不常见的类型解析用于:
-- The coalesce function accepts any set of argument types as long as they share a least common type.
-- 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
> SELECT typeof(coalesce(1, '2147483648'))
BIGINT
> SELECT typeof(coalesce(1.0, '2147483648'))
DOUBLE
> SELECT typeof(coalesce(DATE'2021-01-01', '2022-01-01'))
DATE
在ANSI模式下(spark.sql.ansi.enabled=true),Spark SQL的函数调用:
> SET spark.sql.ansi.enabled=true;
-- implicitly cast Int to String type
> SELECT concat('total number: ', 1);
total number: 1
-- implicitly cast Timestamp to Date type
> select datediff(now(), current_date);
0
-- implicitly cast String to Double type
> SELECT ceil('0.1');
1
-- special rule: implicitly cast NULL to Date type
> SELECT year(null);
NULL
> CREATE TABLE t(s string);
-- Can't store String column as Numeric types.
> SELECT ceil(s) from t;
Error in query: cannot resolve 'CEIL(spark_catalog.default.t.s)' due to data type mismatch
-- Can't store String column as Date type.
> select year(s) from t;
Error in query: cannot resolve 'year(spark_catalog.default.t.s)' due to data type mismatch
在ANSI模式下,某些SQL函数的行为可能会有所不同(spark.sql.ansi.enabled=true)。
在ANSI模式下,某些SQL操作符的行为可能会有所不同(spark.sql.ansi.enabled=true)。
当ANSI模式打开时,它会为无效操作抛出异常。你可以使用以下SQL函数来抑制此类异常。
当spark.sql.ansi.enabled和spark.sql.ansi.enforceReservedKeywords都为true时,Spark SQL将使用ANSI模式解析器。
使用ANSI模式解析器,Spark SQL有两种关键字: