为什么我的T-SQL (WHILE)不起作用?

3

在我的代码中,我需要测试指定的列是否为空,并且最接近0(它可以保存从0到50的数字),因此我尝试了下面的代码。 它应该从0开始,对于每个值测试查询。 当@Results为空时,它应该返回。 但是,它不起作用。 仍然打印0。

declare @hold int
declare @Result int
set @hold0
set @Result=0

WHILE (@Result!=null)
BEGIN
select @Result=(SELECT Hold from Numbers WHERE Name='Test' AND Hold=@hold)
set @hold=@hold+1
END

print @hold
4个回答

4
首先,您不能测试NULL的相等性。NULL表示未知值,因此您不知道它是否(或不)等于任何特定的值。请使用@result IS NOT NULL代替@Result!=NULL
其次,如果可以尽量避免在SQL中使用这种顺序处理方式。SQL是用来处理集合而不是顺序处理事物的。您可以使用一个简单的SQL命令完成所有这些工作,而且运行速度可能会更快:
SELECT
    MIN(hold) + 1
FROM
    Numbers N1
WHERE
    N1.name = 'Test' AND
    NOT EXISTS
    (
        SELECT
            *
        FROM
            Numbers N2
        WHERE
            N2.name = 'Test' AND
            N2.hold = N1.hold + 1
    )

上述查询基本上告诉SQL Server:“在名称为Test且行名为“Test”的保留值比当前所有行的最小保留值加1还要大的表Numbers中,给我最小的保留值加1(MIN(hold) + 1),并且这样的行不存在(整个“NOT EXISTS”部分)”。对于以下行:
Name      Hold
--------  ----
Test      1
Test      2
NotTest   3
Test      20

SQL Server会找到所有名字为“Test”的行(1、2、20),然后找出哪些行没有一个名字为Test且hold = hold + 1的行。对于1,存在一个名为Test的行;对于2,也存在一个名为Test的行。对于Test,2,不存在Test,3,因此它仍然是潜在的结果。对于Test,20,不存在Test,21,所以我们得到:

Name      Hold
--------  ----
Test      2
Test      20

现在SQL Server查找MIN(hold)并获得2,然后加1,因此您将得到3。

SQL Server可能不会完全按照我描述的操作执行。 SQL语句告诉SQL Server您要查找的内容,但不告诉它如何获取它。 SQL Server有自由使用任何它认为最有效的方法来获取答案。

关键是始终以集合的方式思考,以及这些集合是如何通过JOIN连接在一起,通过WHERE条件或在联接中的ON条件进行过滤,并在必要时进行分组和聚合(MIN,MAX,AVG等)。


是的,我发布的代码允许这样做。在SQL中,除非您使用ORDER BY告诉SQL Server使用什么顺序,否则没有任何东西是“有序”的。我猜你的意思是它们不一定是连续的?根据您提供的数据,我的解决方案将返回3。 - Tom H
真的吗?我对 SQL 还不是很有经验。它找到 Hold + 1 的最小值并测试是否不存在更高的值。但如果有呢?由于它不在循环中,我不知道它在这种情况下如何继续。 - SnakeeP
谢谢,非常感谢您的帮助! - SnakeeP
你可以创建一个foreach循环 - 只需查看我的答案。你离成功很近了! - Andreas Rehm
1
我的观点不是说你不能用循环来做,而是你不应该用循环来做。我所说的关于没有“continues”或“loops”的陈述是指SQL SELECT语句 - 认为SELECT语句执行一些过程性的逐行处理是错误的。它是一种基于集合的语言,因此在大多数情况下这种范例并不成立。 - Tom H
显示剩余2条评论

1

你试过了吗?

WHILE (@Result is not null)
BEGIN
select @Result=(SELECT Hold from Numbers WHERE Name='Test' AND Hold=@hold)
set @hold=@hold+1
END

0

试试这个:

declare @hold int
declare @Result int
set @hold=0
set @Result=0
declare @max int
SELECT @max=MAX(Hold) FROM Numbers

WHILE (@hold <= @max)
BEGIN
   select @Result=(SELECT Hold from Numbers WHERE Name='Test' AND Hold=@hold)

   set @hold=@hold+1
END

print @hold

在 T-SQL 中,while 循环有些棘手 - 你也可以使用它来循环遍历(临时)表格 - 使用:

-- Foreach with T-SQL while
DECLARE @tempTable TABLE (rownum int IDENTITY (1, 1) Primary key NOT NULL, Number int)
declare @RowCnt int
declare @MaxRows int
select @RowCnt = 1
select @MaxRows=count(*) from @tempTable
declare @number int

while @RowCnt <= @MaxRows
begin
    -- Number from given RowNumber
    SELECT @number=Number FROM @tempTable where rownum = @RowCnt

    -- next row
    Select @RowCnt = @RowCnt + 1
end

0

这是Tom H.查询的更高级版本:

SELECT MIN(N1.hold) + 1
FROM Numbers N1
LEFT OUTER JOIN Numbers N2
    ON N2.Name = N1.Name AND N2.hold = N1.hold + 1
WHERE N1.name = 'Test' AND N2.name IS NULL

如果你对SQL不熟悉,它可能不太直观,但是它使用相同的逻辑。对于那些更熟悉SQL的人来说,它可以更容易地看到N1和N2之间的关系。根据您的DBMS,查询优化器也可能更容易处理它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接