在WHERE子句中将VARCHAR转换为INT / NUMBER

时间:2018-05-01 23:15:00

标签: sql postgresql casting

我在PostgreSQL数据库中有一个表ACTIVITIES_TACTIVITY_CODEVARCHAR列:

ID (INT)  |  ACTIVITY_CODE (VARCHAR) | ACTIVITY_TEXT (VARCHAR)
-------------------------------------------------------------
..            ..                      ..
7             10012                   ..
8             10013                   ..
9             10014                   ..
10            10015                   ..
11            SPECIAL                 ..

我有一个连续的伪数字块10012..10015,我需要使用BETWEEN a AND b运算符或>= n1 and <= n2进行选择。

但并非ACTIVITY_CODE中的所有条目都是数值 我试过这个:

select * from activities_t  where activity_code::int >= 10012 and 
                                  activity_code::int <= 10015;

但得到了错误

ERROR:  invalid input syntax for integer: SPECIAL

2 个答案:

答案 0 :(得分:4)

SELECT *
FROM   activities_t
WHERE  CASE WHEN activity_code ~ '^\d+$'  -- only digits
            THEN activity_code::int BETWEEN 10012 AND 10015 END;

CASE包装器可以避免异常。仅考虑仅由数字组成的字符串。 (所以也没有空白填充!)
不需要ELSE,因为CASE默认为NULL且只有TRUE符合WHERE条款。

当然,这不能在activity_code上使用任何普通索引。

如果您需要更好的性能,请清理您的表格设计,或者,如果这不是一个选项,请创建表达式索引:

CREATE INDEX foo_idx ON activities_t((CASE WHEN activity_code ~ '^\d+$'
                                           THEN activity_code::int END));

并匹配查询中的表达式:

SELECT *, CASE WHEN activity_code ~ '^\d+$' THEN activity_code::int END
FROM   activities_t
WHERE  CASE WHEN activity_code ~ '^\d+$'
            THEN activity_code::int END BETWEEN 10012 AND 10015;

这与第一个查询略有不同,如果它不会触发索引扫描,则会略贵一些。

答案 1 :(得分:0)

只是添加 @Erwin Brandstetter 所写的内容,如果在Spring Data中使用此内容,则会有关于使用@Query中的::运算符的投诉。

要解决此问题,请转义::,如此示例所示, https://stackoverflow.com/a/43982706/1005607

\\:\\:

或者修改查询以使用cast函数。

@Query(value="select * from activities_t where " + 
             "   case when  activity_code ~ '^\d+$' " +  // only digits   
             "   then cast(activity_code as int) between 10012 and 10015 end ", 
       nativeQuery=true)
public List<ActivityT> getByActivityCodes();