【问题标题】:How to create a WHILE loop in SQL and avoid Subquery error如何在 SQL 中创建 WHILE 循环并避免子查询错误
【发布时间】:2016-09-01 04:32:04
【问题描述】:

我将为以下等式找到最佳系数(Z)

[Target] = Z * SQRT(Mu_L * POWER(Sigma_D,2) + POWER(Mu_D,2) * POWER(Sigma_L,2))

我的数据集中有大约 100,000 行不符合 Target 级别。所以,我要找到通过目标级别的最小Z 值。每一行都是相同的,因此一行可能需要Z=34.5,而另一行可能需要Z=13.5。我想编写一个代码,通过循环检查每一行,直到所有行都达到所需的目标级别,并为所有行打印最佳 Z 值。

我写了这样的代码:

While (Select [Target] From dbo.product) < 1000 
Begin
   Update dbo.product 
   SET [Z] = Z + 0.5

   Update dbo.product 
   SET [Target] = Z * SQRT(Mu_L * POWER(Sigma_D,2) + POWER(Mu_D,2) * POWER(Sigma_L,2))

   IF (Select [Target] From dbo.product) > 1000
      Break
   Else
      Continue 
End 

运行此查询后,我收到以下错误:

子查询返回超过 1 个值。当子查询跟随 =、!=、、>= 或子查询用作表达式时,这是不允许的。

您知道如何解决此错误吗?而且你相信这段代码可以解决我的问题吗?感谢您的帮助!

【问题讨论】:

  • (Select [Target] From dbo.product) 返回一个记录集,您无法将记录集与整数进行比较。
  • 您可以使用 COUNT 函数作为 COUNT(Target)
  • @ydoow 谢谢你的评论。实际上,我看到了 Microsoft 的 While 函数示例,并且进行了几乎相同的比较。 (SELECT AVG(ListPrice) FROM Production.Product) &lt; $300
  • 所以应该是While (Select MIN([Target]) From dbo.product) &lt; 1000,因为你想确保所有[Target]都是&gt;= 1000
  • 如果您要将数据库表视为电子表格,最好使用真正的电子表格产品。理想情况下,在 SQL 中,您生成基于集合的解决方案(即,听起来您应该拥有一个包含所有可能的 Z 值的表,然后将这两个表与合适的条件join您为每行选择符合您的条件的最低 Z 值)。

标签: sql sql-server


【解决方案1】:

您希望遍历dbo.product 表中的每条记录,并根据条件更新ZTarget 列。了解循环的唯一真正目的,据我所知,是找到一个值Z,它大到足以导致循环退出并进行分配。但是我们实际上可以想出一个公式来计算每条记录的Z 的值应该是多少,而无需显式地使用循环进行迭代:

[Target] = Z * SQRT(Mu_L * POWER(Sigma_D,2) + POWER(Mu_D,2) * POWER(Sigma_L,2))

[Target] 超过1000 时,循环将中断,从而导致以下不等式:

1000 < Z * SQRT(Mu_L * POWER(Sigma_D,2) + POWER(Mu_D,2) * POWER(Sigma_L,2))

但是我们可以解决Z,导致这个不等式:

Z > 1000 / SQRT(Mu_L * POWER(Sigma_D,2) + POWER(Mu_D,2) * POWER(Sigma_L,2))

唯一剩下的技巧是Z0.5 为增量,因此我们应该将向上 舍入到最接近的0.5。我们可以使用Z的这个公式来求解Target的对应值。这导致以下代码,它不需要任何循环:

GO
BEGIN
    UPDATE dbo.product 
    SET [Z] = FLOOR((1000 / SQRT(Mu_L * POWER(Sigma_D,2) + POWER(Mu_D,2) * POWER(Sigma_L,2)) + 0.4) * 2) / 2
    UPDATE dbo.product 
    SET [Target] = [Z] * SQRT(Mu_L * POWER(Sigma_D,2) + POWER(Mu_D,2) * POWER(Sigma_L,2))
END

【讨论】:

  • 非常感谢您提供这个智能解决方案。但是我需要在每次迭代时计算一些其他参数,因此我需要创建一个循环。如果您能帮我弄清楚,我将不胜感激。
  • 很公平。您收到的错误是因为您的 SELECT 返回不止一条记录(实际上是所有记录)。您需要迭代记录。
  • 现在我明白这里出了什么问题!是 select 返回所有记录。但是您知道如何迭代记录吗?
  • @Ashkan 看看here
  • 非常感谢您的帮助!
【解决方案2】:

由于要确保所有行都带有[Target] &gt;= 1000,请使用MIN 查找具有最小值的行。

While (Select MIN([Target]) From dbo.product) < 1000 
Begin
   Update dbo.product 
   SET [Z] = Z + 0.5
   WHERE [Target] < 1000

   Update dbo.product 
   SET [Target] = Z * SQRT(Mu_L * POWER(Sigma_D,2) + POWER(Mu_D,2) * POWER(Sigma_L,2))

   IF (Select MIN([Target]) From dbo.product) > 1000
      Break
   Else
      Continue 
End 

【讨论】:

  • 我这里有问题。此代码在每次迭代时更新所有 Z。这意味着最后它为所有行提供了唯一的 Z 值。虽然我正在寻找导致Target 大于1000 的每一行的最小值Z。你能帮我解决这个问题吗?
  • 所以在第一个查询中您可能只想更新[Target] 不符合您的目标的行,并且将参与更新。我已经修改了答案供您参考。您可能还希望在第二个查询中放入相同的 WHERE 子句以获得更好的性能。
  • 看来While 循环内的If (condition) Break Else Continue 是多余的。它测试与While 相同的条件。
  • @spencer7593 你是对的。如果 OP 出于某种原因将其放在那里,我只是将这些线条保留在那里。
猜你喜欢
  • 2011-05-01
  • 2012-06-10
  • 2013-08-29
  • 1970-01-01
  • 1970-01-01
  • 2014-09-14
  • 1970-01-01
  • 2016-11-02
  • 1970-01-01
相关资源
最近更新 更多