【问题标题】:SQL SELECT that excludes rows with any of a list of values?SQL SELECT 排除具有任何值列表的行?
【发布时间】:2015-09-27 04:32:54
【问题描述】:

我发现了很多关于 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 设置任意数量的参数?
    (任意数量是要排除的值列表的计数)

【问题讨论】:

  • 您的 SQL 将按照说明排除值。 stackoverflow.com/questions/178479/… 回答 NOT IN 列表。
  • 你想要recorded_ &lt;&gt; ALL( ? ),参数类型为timestamp[]。不过,不确定如何通过 JDBC 输入。
  • @NickBarnes: NOT IN 实际上与 &lt;&gt; ALL(): stackoverflow.com/a/31192557/939860 相同。两者的问题:如果表达式的任一侧都涉及 NULL,它们就会失败。该表达式的计算结果为 NULL;但只有 TRUE 通过 WHERE 条件:stackoverflow.com/a/19528722/939860
  • 如 cmets 中所述,NOT IN (...) 等同于 &lt;&gt; ALL (ARRAY[...])。您可以使用createArrayOf 在 JDBC 中创建一个数组,然后将其作为参数传递。

标签: sql postgresql jdbc where-in


【解决方案1】:

传递数组

一个快速且 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 个小数位,您需要完全匹配 /em>。

相关

【讨论】:

    【解决方案2】:
    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')
     ;
    

    【讨论】:

    • 我正在使用 Java 和 JDBC。我可以在括号中简单地列出 java.sql.Timestamp 实例的列表吗?像这样:NOT IN ( ? , ? , ? ) with `preparedStatement.setTimestamp( 3 , timestampObject1 ); ",重复 4、5 等等?
    猜你喜欢
    • 2011-07-26
    • 2016-12-23
    • 2013-03-01
    • 1970-01-01
    • 2021-05-28
    • 2015-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多