【问题标题】:Keep nulls with two IN()使用两个 IN() 保留空值
【发布时间】:2014-11-24 10:05:37
【问题描述】:

我正在重构非常旧的代码。目前,PHP 为每个值生成一个单独的选择。假设loc 包含1,2data 包含a,b,它会生成

select val from tablename where loc_id=1 and data_id=a;
select val from tablename where loc_id=1 and data_id=b;
select val from tablename where loc_id=2 and data_id=a;
select val from tablename where loc_id=2 and data_id=b;

...等等,它们要么返回一个值,要么什么都不返回。这意味着我总是有 n(loc_id)*n(data_id) 结果,包括空值,这是后续处理所必需的。知道顺序后,这用于生成 HTML 表。 data_id 和 loc_id 理论上都可以扩展到数千个(这在表中显然不是很好,但这是另一个问题)。

           +-----------+-----------+
           | data_id 1 | data_id 2 |
+----------+-----------+-----------+
| loc_id 1 |     -     |  999.99   |
+----------+-----------+-----------+
+ loc_id 2 |   888.88  |     -     |
+----------+-----------+-----------+

为了加快速度,我正在考虑用单个查询替换它:

select val from tablename where loc_id in (1,2) and data_id in (a,b) order by loc_id asc, data_id asc;

得到类似(如下)的结果并迭代构建我的表。

Rownum  VAL
------- --------
1       null
2       999.99
3       777.77
4       null

不幸的是,这种方法会从结果集中删除空值,所以我最终得到了

Rownum  VAL
------- --------
1       999.99
2       777.77

请注意,data_id 或 loc_id 可能都没有任何匹配项,在这种情况下,我仍然需要 null, null

所以我不知道哪个值匹配哪个。如果我添加 loc_id 和 data_id,我可以在 php 中匹配预期的 loc_id/data_id 组合......但这会变得混乱。

总的来说还是个 SQL 新手,这绝对是我第一次在 PostgreSQL 上工作,所以希望这不是太明显......当我发布这篇文章时,我正在寻找两种方法来解决这个问题:any in array[] 和 @987654334 @s。如果发现任何新内容会更新。

tl;博士问题

如何执行where loc_id in (1,2) and data_id in (a,b) 并保留空值,以便始终获得 n(loc)*n(data) 结果?

【问题讨论】:

  • “我不知道哪个值匹配哪个”——即使有 NULL,你也不知道。你还没有指定order
  • 正确,我没有在上面指定。我会有一个 loc_id asc, data_id asc 的订单。
  • (loc_id, data_id) 在您的表中定义是唯一的吗? (意味着您最多有 4 个没有 NULL 值的条目。)

标签: arrays postgresql join


【解决方案1】:

您可以通过两个步骤在单个查询中实现:

  1. 在输出中生成一个包含所有所需行的矩阵。
  2. LEFT [OUTER] JOIN 到实际行。

对于表格中的每个单元格,您至少有一行。
如果(loc_id, data_id) 是唯一的,您将得到恰好一行。

SELECT t.val
FROM        (VALUES (1), (2))     AS l(loc_id)
CROSS  JOIN (VALUES ('a'), ('b')) AS d(data_id) -- generate total grid of rows
LEFT   JOIN tablname t USING (loc_id, data_id) -- attach matching rows (if any)
ORDER  BY l.loc_id, d.data_id;

适用于 any 数量的列和 any 数量的值。

对于您的简单案例:

SELECT t.val
FROM  (
   VALUES
      (1, 'a'), (1, 'b')
    , (2, 'a'), (2, 'b')
   ) AS ld (loc_id, data_id) -- total grid of rows
LEFT   JOIN tablname t USING (loc_id, data_id) -- attach matching rows (if any)
ORDER  BY ld.loc_id, ld.data_id;

【讨论】:

    【解决方案2】:
    where (loc_id in (1,2) or loc_id is null)
    and  (data_id in (a,b) or data_id is null)
    

    【讨论】:

      【解决方案3】:

      选择您用于过滤的字段,以便您知道值的来源:

      select loc,data,val from tablename where loc in (1,2) and data in (a,b);
      

      您也不会以这种方式获得nulls,但它不再是问题了。您知道缺少哪些字段,并且您知道那些是nulls。

      【讨论】:

      • 是的,如果我添加 loc_id 和 data_id ,那将是“我在 php 中匹配预期的 loc_id/data_id 组合的方法......但这会变得混乱。”我在想。希望避免这种情况,但如果这是唯一的方法......
      猜你喜欢
      • 2021-05-20
      • 2021-06-26
      • 2018-08-29
      • 2018-06-01
      • 2023-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-15
      相关资源
      最近更新 更多