【问题标题】:Get rows in SQL by summing up a until certain value is exceeded and stop retrieving通过总结 a 直到超过某个值并停止检索来获取 SQL 中的行
【发布时间】:2022-01-19 22:56:43
【问题描述】:

当值超过某个点时,我必须从数据库中返回行。

我应该得到足够多的行来总结一个大于我的数量的值并停止检索行。

这可能吗?它有意义吗? 这可以转移到 LINQ for EF core 中吗?

我目前遇到了返回所有行的查询...

SELECT [i].[InventoryArticleId], [i].[ArticleId], [i].[ArticleQuantity], [i].[InventoryId]
      FROM [InventoryArticle] AS [i]
      INNER JOIN [Article] AS [a] ON [i].[ArticleId] = [a].[ArticleId]
      WHERE (([i].[ArticleId] = 1) AND ([a].[ArticlePrice] <= 1500)) 
      AND ((
          SELECT COALESCE(SUM([i0].[ArticleQuantity]), 0)
          FROM [InventoryArticle] AS [i0]
          INNER JOIN [Article] AS [a0] ON [i0].[ArticleId] = [a0].[ArticleId]
          WHERE ([i0].[ArticleId] = 1) AND ([a0].[ArticlePrice] < 1500)) > 10) 

预期结果是一行。如果数字大于 34,则应添加更多行。

【问题讨论】:

  • 没有什么比样本数据和预期结果更能说明问题了。请阅读How to askMinimal, Reproducible Example
  • Aside - [delimiters] 只需要用于保留字或特殊字符,将它们用于everything只会增加噪音并使查询不可读;您的查询中没有任何内容需要分隔。
  • 查询是EF核心生成的,我没有写,我只是切换值进行测试。我不太擅长 SQL。感谢您的提示...
  • 查询是由 EF 核心生成的 - 好吧,有道理!它看起来非常无用,并且不必要地两次点击相同的表,这不是必需的,完全忽略并清楚地定义示例数据和所需的结果和逻辑可能更好。
  • 步骤:创建一个子查询,按分区(文章 ID?)保持数量的运行总和,然后使用外部查询查找 TOP 1 记录 > 按您的 runningtotal 升序排序的所需数量。至于如何获得运行总数:stackoverflow.com/questions/860966/… 现在您可以在视图或过程中执行此操作,并让用户传递所需的文章/数量以使其更容易...

标签: sql sql-server database


【解决方案1】:

我的方法是:

  1. 筛选符合条件的记录。
  2. 计算总和。
  3. 确定运行总计满足您的条件的第一条记录。
  4. 对截至该点的所有符合条件的记录执行最终选择。

类似下面的一些精简的例子:

-- Some useful generated data
DECLARE @Inventory TABLE (InventoryArticleId INT, ArticleId INT, ArticleQuantity INT)
INSERT @Inventory(InventoryArticleId, ArticleId, ArticleQuantity)
SELECT TOP 1000
    InventoryArticleId = N.n,
    ArticleId = N.n % 5,
    ArticleQuantity = 5 * N.n
FROM (
    -- Generate a range of integers
    SELECT n =  ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
    FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
         (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
         (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
         (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
    ORDER BY 1
) N
ORDER BY N.n

SELECT * FROM @Inventory

DECLARE @ArticleId INT = 2
DECLARE @QuantityNeeded INT = 500

;
WITH isum as (
    SELECT i.*, runningTotalQuantity = SUM(i.ArticleQuantity) OVER(ORDER BY i.InventoryArticleId)
    FROM @Inventory i
    WHERE i.ArticleId = @ArticleId
)
SELECT isum.*
FROM (
    SELECT TOP 1 InventoryArticleId
    FROM isum
    WHERE runningTotalQuantity >= @QuantityNeeded
    ORDER BY InventoryArticleId
) selector
JOIN isum ON isum.InventoryArticleId <= selector.InventoryArticleId
ORDER BY isum.InventoryArticleId 

结果:

InventoryArticleId ArticleId ArticleQuantity runningTotalQuantity
2 2 10 10
7 2 35 45
12 2 60 105
17 2 85 190
22 2 110 300
27 2 135 435
32 2 160 595

运行总计计算、选择器和最终选择中的所有 ORDER BY 子句必须一致且明确(无重复)。如果需要更复杂的顺序或偏好,可能需要在计算运行总数之前为符合条件的记录分配一个排名值。

【讨论】:

  • 感谢@TN 提供的解决方案,我会查找并尝试一下。
【解决方案2】:

您可以使用带窗口的SUM 来计算运行总和ArticleQuantity。它可能比自加入更有效率。

诀窍是您需要所有行的运行总和上一行小于要求。

您可以使用ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDINGROWS 子句。但接下来你需要处理第一行可能出现的NULLs。

无论如何,即使是常规的运行总和也应始终使用ROWS UNBOUNDED PRECEDING,因为默认值为RANGE UNBOUNDED PRECEDING,这有细微的不同,可能会导致错误的结果,并且速度会变慢。

DECLARE @requirement int = 10;

SELECT
  i.InventoryArticleId,
  i.ArticleId,
  i.ArticleQuantity,
  i.InventoryId
FROM (
    SELECT
      i.*,
      RunningSum = SUM(i.ArticleQuantity) OVER (PARTITION BY i.ArticleId ORDER BY i.InventoryArticleId ROWS UNBOUNDED PRECEDING)
    FROM InventoryArticle i
    INNER JOIN Article a ON i.ArticleId = a.ArticleId
    WHERE i.ArticleId = 1
      AND a.ArticlePrice <= 1500
) i
WHERE i.RunningSum - i.ArticleQuantity < @requirement;

您可能希望选择更好的排序子句。

EF Core 不能使用窗口函数,除非你专门为它定义了SqlExpression

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多