【问题标题】:Selecting elements that don't exist选择不存在的元素
【发布时间】:2009-03-25 16:10:01
【问题描述】:

我正在开发一个必须为元素分配数字代码的应用程序。这些代码不是连续的,我的想法是在有相关元素之前不要将它们插入数据库,但我想在 sql 问题中找到未分配的代码,我不知道该怎么做。

有什么想法吗? 谢谢!!!

编辑 1

表格可以这么简单:

code | element 
-----------------
3    | three 
7    | seven 
2    | two

我想要这样的东西:1、4、5、6。没有任何其他表格。

编辑 2

感谢您的反馈,您的回答非常有帮助。

【问题讨论】:

  • 请发布您的表格结构。

标签: sql select


【解决方案1】:

如果没有分配代码,这将返回NULL

SELECT  assigned_codes.code
FROM    codes 
LEFT JOIN
        assigned_codes
ON      assigned_codes.code = codes.code
WHERE   codes.code = @code

这将返回所有未分配的代码:

SELECT  codes.code
FROM    codes 
LEFT JOIN
        assigned_codes
ON      assigned_codes.code = codes.code
WHERE   assigned_codes.code IS NULL

没有纯粹的SQL 方式可以完全按照您的意愿去做。

Oracle 中,您可以执行以下操作:

SELECT  lvl
FROM    (
        SELECT  level AS lvl
        FROM    dual
        CONNECT BY
                level <=
                (
                SELECT  MAX(code)
                FROM    elements
                )
        )
LEFT OUTER JOIN
        elements
ON      code = lvl
WHERE   code IS NULL

PostgreSQL,您可以执行以下操作:

SELECT  lvl
FROM    generate_series(
        1,
        (
        SELECT  MAX(code)
        FROM    elements
        )) lvl
LEFT OUTER JOIN
        elements
ON      code = lvl
WHERE   code IS NULL

【讨论】:

  • 我已经在想,也许不能如我所愿。
【解决方案2】:

与使用纯 SQL 无法做到这一点的断言相反,这里有一个反例说明如何做到这一点。 (请注意,我并没有说这很容易 - 但是,它是可能的。)假设表的名称是 value_listcodevalue 如编辑所示(为什么每个人都忘记包括问题中的表名?):

SELECT b.bottom, t.top
    FROM (SELECT l1.code - 1 AS top
            FROM value_list l1
            WHERE NOT EXISTS (SELECT * FROM value_list l2
                                 WHERE l2.code = l1.code - 1)) AS t,    
         (SELECT l1.code + 1 AS bottom
            FROM value_list l1
            WHERE NOT EXISTS (SELECT * FROM value_list l2
                                 WHERE l2.code = l1.code + 1)) AS b
    WHERE b.bottom <= t.top
      AND NOT EXISTS (SELECT * FROM value_list l2
                         WHERE l2.code >= b.bottom AND l2.code <= t.top);

from 子句中的两个并行查询生成的值分别位于表中值范围的间隙的顶部和底部。然后限制这两个列表的叉积,使得底部不大于顶部,并且在底部和顶部之间的原始列表中没有值。

在样本数据上,这会产生范围 4-6。当我添加一个额外的行(9,'9')时,它还生成了 8-8 范围。显然,对于“无穷大”的合适定义,您还有另外两个可能的范围:

  • -infinity .. MIN(code)-1
  • MAX(code)+1 .. +infinity

注意:

  1. 如果您经常使用它,您的列表中通常不会有太多空白。
  2. 只有当您从表中删除行(或者您在插入数据时忽略此查询或其相关查询返回的范围)时,才会出现间隙。
  3. 重用标识符通常是个坏主意,因此实际上这种努力可能是错误的。

但是,如果您想这样做,这里有一种方法。

【讨论】:

  • 假设这个表只包含一行代码为 1,000,000。要填补空白,您需要在结果行集中有 999,999 行。与 1 行表的所有可能连接将最多生成 1 行,无论您使用多少个连接。
  • 或者您需要在结果集中有一行,标识值 1..999,999 可供重用。正如我所指出的,我给出的查询在表中的(多个)行之间找到了漏洞;必须有不止一行,它才有机会返回任何东西。
  • 对于其他可能性,您可以使用 UNION 与 SELECT 1 AS bottom, MIN(code)-1 AS top FROM values_list HAVING MIN(code) > 1 用于较低范围和相应的语句为上限。
  • “不能在纯 SQL 中完成”的意思是在单个查询中生成所有缺失的行。您不能在缺少按需生成给定行数的虚拟行集(主要系统是 MySQL 和 SQL Server)的方法的 RDBMS 中执行此操作。
【解决方案3】:

这与 Quassnoi 发表的想法相同。 我只是用 T-SQL 之类的代码将所有想法联系在一起。

DECLARE
    series @table(n int)

DECLARE
    max_n int,
    i int

SET i = 1
-- max value in elements table
SELECT
    max_n = (SELECT MAX(code) FROM elements)

-- fill @series table with numbers from 1 to n
WHILE i < max_n BEGIN
    INSERT INTO @series (n) VALUES (i)

    SET i = i + 1
END

-- unassigned codes -- these without pair in elements table
SELECT
    n
FROM
    @series AS series
    LEFT JOIN
        elements
    ON
        elements.code = series.n
WHERE
    elements.code IS NULL

编辑: 当然,这不是理想的解决方案。如果您有很多元素或经常检查不存在的代码,这可能会导致性能问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-15
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    • 2011-02-17
    • 1970-01-01
    相关资源
    最近更新 更多