找到缺失的值

时间:2008-12-31 15:59:08

标签: sql

我有一个表,有两个重要的列DocEntry,WebId

示例数据类似于

DocEntry  WebId
1         S001
2         S002
3         S003
4         S005

现在我们可以注意到,在WebId列中,缺少S004。我们如何通过查询找到这些丢失的数字。

进一步解释:

如果缺少任何数字,则网络ID应按递增顺序,如S001,S002,S003,S004,S005,而不是丢失的数字。我没有任何单独的表可用条目,因为这是不切实际的。我要逐月找到丢失的数字,将每个月的起始值和结束值作为边界,而不是找到丢失的数字(如果有的话)。

7 个答案:

答案 0 :(得分:5)

一种非常简单的方法:)

mysql> select * from test;
+----------+-------+
| DocEntry | WebId |
+----------+-------+
| 1        | S001  |
| 2        | S002  |
| 3        | S003  |
| 4        | S005  |
| 5        | S006  |
| 6        | S007  |
| 7        | S008  |
| 8        | S010  |
+----------+-------+
8 rows in set (0,00 sec)

mysql> SELECT right(t1.webid,3) +1 as missing_WebId FROM test t1 left join test t2 on right(t1.webid,3)+1 = right(t2.webid,3) where t2.webid is null;
+---------------+
| missing_WebId |
+---------------+
| 4             |
| 9             |
| 11            |
+---------------+
3 rows in set (0,01 sec)
祝你好运, 莫里斯

答案 1 :(得分:1)

有一个生成整数的标准技巧,需要你创建一个10行实用程序表,即:

create table Pivot (i int)

insert into Pivot values (0)
insert into Pivot values (1)
insert into Pivot values (2) 

/* ... down to */

insert into Pivot values (9)

一旦你完成了这个,那么,例如

select u.i + 10*t.i + 100*h.i from Pivot u, Pivot t, Pivot h

会得到0到999的所有数字。

添加一个where子句来限制你在一个范围之间,一些字符串函数会让你进入上面Robs回答中的PossibleEntries表。

答案 2 :(得分:1)

旁白:为什么一般人(Rahul不是唯一一个,在任何想象中)从问题中省略了他们的表名?

以关系方式进行操作非常困难,因为它本身依赖于数据的排序和关系代数在(无序)集合上的工作。我认为我们应该假设DocID列没有任何意义,它不能用于帮助解决问题。

在该示例中,您有S003和S005并且缺少S004。我们如何判断缺失值?据推测,因为有一个比较操作告诉我们'小于','相等','大于',还因为有一个差异函数告诉我们S003和S005之间的差距是2.让我们假设' >”和朋友进行比较(在这里用于字符串),并且你可以生成一个存储过程webid_diff(),它接受两个WebID值并返回差异。

然后,您可以编写一个查询,例如:

SELECT a.webid, MIN(b.webid) AS min_next
    FROM AnonymousTable AS a, AnonymousTable AS b
    WHERE a.webid < b.webid
    GROUP BY a.webid;

这使用表和它自身之间的非等值连接来查找每个项目的最小后继WebID值。

以此为核心,我们可以过滤结果,只选择WebID和Min_Next之间的差距大于1的那些行。所以,我认为我们得到了(第一次尝试)

SELECT x.webid, y.min_next, webid_diff(x.webid, y.min_next) AS gap
    FROM AnonymousTable AS x,
         (SELECT a.webid, MIN(b.webid) AS min_next
             FROM AnonymousTable AS a, AnonymousTable AS b
             WHERE a.webid < b.webid
             GROUP BY a.webid
         ) AS y
    WHERE x.webid = y.webid
      AND webid_diff(x.webid, y.min_next) > 1;

外层的联接实际上是否对我们有用?我不这么认为,所以我们可以删除它,导致(第二次尝试)

SELECT y.webid, y.min_next, webid_diff(y.webid, y.min_next) AS gap
    FROM (SELECT a.webid, MIN(b.webid) AS min_next
             FROM AnonymousTable AS a, AnonymousTable AS b
             WHERE a.webid < b.webid
             GROUP BY a.webid
         ) AS y
    WHERE webid_diff(y.webid, y.min_next) > 1;

这确实有效。试图将webid_diff()函数放入内部查询会给我带来问题 - 至少GAP表达式必须包含在GROUP BY子句中,但那样会给出错误的答案。

HAVING子句用于将过滤条件应用于聚合,因此看起来好像查询可以简化为:

SELECT a.webid, MIN(b.webid) AS min_next, webid_diff(a.webid, b.webid) AS gap
    FROM AnonymousTable AS a, AnonymousTable AS b
    WHERE a.webid < b.webid
    GROUP BY a.webid
    HAVING webid_diff(a.webid, b.webid) > 1;

然而,这不起作用(对我来说,使用我的DBMS - IBM Informix Dynamic Server),因为webid_diff()不是聚合。

这是我用于webid_diff()函数的代码(你必须调整以适应你的DBMS的语法)和辅助webid_num()函数:

CREATE FUNCTION webid_num(a CHAR(4)) RETURNING INTEGER;
    DEFINE i INTEGER;
    LET i = substr(a, 2, 3);
    RETURN i;
END FUNCTION;

CREATE FUNCTION webid_diff(a CHAR(4), b CHAR(4)) RETURNING INTEGER;
    DEFINE i, j INTEGER;
    LET i = webid_num(a);
    LET j = webid_num(b);
    RETURN (j - i);
END FUNCTION;

答案 3 :(得分:0)

你需要通过“缺失”来定义你的意思。您不能指望您的数据库服务器能够理解这个抽象概念。也许存储过程是最好的方法,因为那时你可以更精确地定义你的逻辑。

答案 4 :(得分:0)

除非你已经定义了一个特定的数字布局(它看起来像你),否则要有一个包含所有可能性的表格(虽然效率不是很高),你可以这样做:

获取一个包含名称PossibleEntries的所有可能性的表,然后执行以下操作:

SELECT pe.WebID from PossibleEntries pe 
WHERE pe.WebID Not In (Select WebID from SampleData)

我认为这应该有效,但我不知道效率如何。 我同意上述内容,如果数字不是连续的,你将无法做到这一点。

答案 5 :(得分:0)

就个人而言,我会用PHP或者你用SQL编写的任何编程语言来做这件事。如果你不能拥有一个包含所有可能值的单独表(顺便说一句,为什么不呢?)那么我采取的方法是直接查询以获取表中的值:

select WebID from table order by WebID;

然后使用一个简单的循环来查找哪些缺失。例如,在php中:

$values = Array();
$query = "select WebID from table order by WebID;";
$dataset = mysql_query ($query) or die (mysql_error());
while ($data = mysql_fetch_assoc($dataset))
{
  $values[$data['WebID'] = 1;
}

$last_line = $data['WebID'];
$matches = Array();
ereg("S([0-9]+)", $last_line, $matches))

$max_value = $matches[0];
$missing = Array();

for ($count = 0; $count < $max_value; $count ++)
{
  if (!isset($values[$count])
  { 
    echo "value $count is missing\n";
    $missing[$count] = true;
  }
}

我还没有对它进行过测试,但是如果您确实使用了PHP,那么这可能就是您想要的。

答案 6 :(得分:0)

我的猜测是你的数据库有一个严重的设计缺陷,因为看起来你的WebID实际上至少有两列你已经组合在一起。数字部分显然具有某种意义,因为你希望它是顺序的,但如果是这样,那么“S”是什么意思?由于这种设计缺陷,您的问题的解决方案将变得比它需要的更复杂。此外,您声明存储对数据库很重要的数据并不“实际”是一个很大的危险信号。

除此之外,以下查询应该为您提供任何缺失值:

SELECT
     (
          SELECT
               SUBSTRING(MAX(T4.WebID), 1, 1) +
               RIGHT('000' + CAST(CAST(SUBSTRING(MAX(T4.WebID), 2, 3) AS INT) + 1 AS VARCHAR), 3)
           FROM My_Table T4
           WHERE T4.WebID < T1.WebID
     ) AS min_range,
     SUBSTRING(T1.WebID, 1, 1) + RIGHT('000' + CAST(CAST(SUBSTRING(T1.WebID, 2, 3) AS INT) - 1 AS VARCHAR), 3) AS max_range
FROM
     My_Table T1
LEFT OUTER JOIN My_Table T2 ON
     T2.WebID = SUBSTRING(T1.WebID, 1, 1) +
                RIGHT('000' + CAST(CAST(SUBSTRING(T1.WebID, 2, 3) AS INT) - 1 AS VARCHAR), 3)

WHERE
     T2.WebID IS NULL AND
     T1.WebID <> (SELECT MIN(WebID) FROM My_Table)

它为每个缺失值范围提供了一个开始和结束,而不是每个缺失值的明确列表。为了得到这个,你需要一张cindi和Rob所涵盖的数字表。