SQL SELECT排除包含任何值列表的行?

时间:2015-07-09 01:33:00

标签: sql postgresql jdbc where-in

我找到了许多关于SELECT的问题和答案,排除了值为" NOT IN"子查询(例如this)。但是如何排除值列表而不是子查询?

我想搜索时间戳在某个范围内但排除某些特定日期时间的行。在英语中,那将是:

  

选择今天中午和下午2点之间记录的所有ORDER行,但以下时间除外:今天12:34,今天12:55,今天13:05。

SQL可能类似于:

SELECT * 
FROM order_
WHERE recorded_ >= ?
AND recorded_ < ?
AND recorded_ NOT IN ( list of date-times… )
;

这个问题的两个部分:

  • 如何编写SQL以排除具有任何值列表的行?
  • 如何在JDBC中为PreparedStatement设置任意数量的参数?
    (任意数字是要排除的值列表的计数)

2 个答案:

答案 0 :(得分:3)

传递数组

快速且安全的NULL替代方案是对未通过的数组LEFT JOIN

SELECT o.*
FROM   order_ o
LEFT  JOIN unnest(?::timestamp[]) x(recorded_) USING (recorded_)
WHERE  o.recorded_ >= ?
AND    o.recorded_ <  ?
AND   x.recorded_ IS NULL;

通过这种方式,您可以准备单个语句并将任意数量的时间戳作为数组传递。

只有在您无法输入参数时才需要显式转换::timestamp[](就像在prepared statements中一样)。该数组以单text(或timestamp[])字面值传递:

'{2015-07-09 12:34, 2015-07-09 12:55, 2015-07-09 13:05}', ...

或者将CURRENT_DATE放入查询并传递时间以添加outlined by @drake 。有关向time添加interval / date的更多信息:

传递个别值

您还可以使用VALUES表达式或任何其他方法来创建特殊的值表。

SELECT o.*
FROM   order_ o
LEFT  JOIN (VALUES (?::timestamp), (?), (?) ) x(recorded_)
                                         USING (recorded_)
WHERE  o.recorded_ >= ?
AND    o.recorded_ <  ?
AND    x.recorded_ IS NULL;

传递:

'2015-07-09 12:34', '2015-07-09 12:55', '2015-07-09 13:05', ...

这样您只能传递预定数量的时间戳

旁白

对于最多100个参数(或您的max_function_args设置),您可以使用带有VARIADIC参数的服务器端函数:

我知道你知道timestamp特征,但是对于普通大众来说:对于时间戳来说,相等匹配可能会很棘手,因为那些时间最多可以有6个小数位,你需要匹配准确

相关

答案 1 :(得分:1)

SELECT * 
FROM order_
WHERE recorded_ BETWEEN (CURRENT_DATE + time '12:00' AND CURRENT_DATE + time '14:00')
      AND recorded_ NOT IN (CURRENT_DATE + time '12:34', 
                            CURRENT_DATE + time '12:55',
                            CURRENT_DATE + time '13:05')
 ;