【问题标题】:SQL Server: select newest rows who's sum matches a valueSQL Server:选择总和匹配值的最新行
【发布时间】:2017-03-22 14:46:31
【问题描述】:

这是一张桌子……

ID    QTY    DATE    CURRENT_STOCK
----------------------------------
1     1      Jan     30
2     1      Feb     30
3     2      Mar     30
4     6      Apr     30
5     8      May     30
6     21     Jun     30

我需要返回总数量等于或超过当前库存水平的最新行,不包括达到此总数后的任何其他行,因此我希望只看到这些行...

ID    QTY    DATE    CURRENT_STOCK
----------------------------------
4     6      Apr     30
5     8      May     30
6     21     Jun     30

我假设我需要一个 CTE(公用表表达式)并查看了 this question,但看不到如何将其转换为我的要求。

帮忙!?

【问题讨论】:

  • 这是一个很好的起点。 spaghettidba.com/2015/04/24/…
  • 请解释你的输出
  • 你从一个月的 desc 顺序开始求和 QTY,当它达到 >= 30 时你停止
  • 最新行的逻辑是什么?

标签: sql sql-server sql-server-2008


【解决方案1】:
Declare @YourTable table (ID int,QTY int,DATE varchar(25), CURRENT_STOCK int)
Insert Into @YourTable values
(1     ,1      ,'Jan'     ,30),
(2     ,1      ,'Feb'     ,30),
(3     ,2      ,'Mar'     ,30),
(4     ,6      ,'Apr'     ,30),
(5     ,8      ,'May'     ,30),
(6     ,21     ,'Jun'     ,30)


Select A.*
 From  @YourTable A
 Where ID>= (
                Select LastID=max(ID)
                 From  @YourTable A
                 Cross Apply (Select RT = sum(Qty) from @YourTable where ID>=A.ID) B
                 Where B.RT>=CURRENT_STOCK
             )

退货

ID  QTY DATE    CURRENT_STOCK
4   6   Apr     30
5   8   May     30
6   21  Jun     30

【讨论】:

    【解决方案2】:

    使用您提供的数据集的一种方法

    if object_id('tempdb..#Test') is not null drop table #Test
    create table #Test (ID int, QTY int, Date_Month nvarchar(5), CURRENT_STOCK int)
    
    insert into #Test (ID, QTY, Date_Month, CURRENT_STOCK)
    values
    (1, 1, 'Jan', 30),
    (2, 1, 'Feb', 30),
    (3, 2, 'Mar', 30),
    (4, 6, 'Apr', 30),
    (5, 8, 'May', 30),
    (6, 21, 'Jun', 30)
    
    if object_id('tempdb..#Finish') is not null drop table #Finish
    create table #Finish (ID int, QTY int, Date_Month nvarchar(5), CURRENT_STOCK int)
    
    declare @rows int = (select MAX(ID) from #Test)
    declare @stock int = (select MAX(CURRENT_STOCK) from #Test)
    declare @i int = 1
    declare @Sum int = 0
    while @rows > @i
    BEGIN
        select @Sum = @Sum + QTY from #Test where ID = @rows 
    
        IF (@SUM >= @stock)
        BEGIN
            set @i = @rows + 1 -- to exit loop
        END
    
        insert into #Finish (ID, QTY, Date_Month, CURRENT_STOCK)
        select ID, QTY, Date_Month, CURRENT_STOCK from #Test where ID = @rows 
    
        set @rows = @rows - 1
    END
    
    select * from #Finish
    

    【讨论】:

      【解决方案3】:

      设置测试数据

      -- Setup test data
      CREATE TABLE #Stock
          ([ID] int, [QTY] int, [DATE] varchar(3), [CURRENT_STOCK] int)
      ;
      
      INSERT INTO #Stock
          ([ID], [QTY], [DATE], [CURRENT_STOCK])
      VALUES
          (1, 1, 'Jan', 30),
          (2, 1, 'Feb', 30),
          (3, 2, 'Mar', 30),
          (4, 6, 'Apr', 30),
          (5, 8, 'May', 30),
          (6, 21, 'Jun', 30)
      ;
      

      SQL Server 2012+ 解决方案

      如果您有支持完整窗口函数语法的更新版本的 SQL Server,您可以这样做:

      -- Calculate a running total of qty by Id descending
      ;WITH stock AS (
          SELECT *
                -- This calculates the SUM over a 'window' of rows based on the first
                -- row in the result set through the current row, as specified by the
                -- ORDER BY clause
                ,SUM(qty) OVER(ORDER BY Id DESC 
                               ROWS BETWEEN UNBOUNDED PRECEDING 
                                  AND CURRENT ROW) AS TotalQty
          FROM #Stock
      ),
      -- Identify first row in mininum set that matches or exceeds CURRENT_STOCK
      first_in_set AS (
          SELECT TOP 1 *    
          FROM stock
          WHERE TotalQty >= CURRENT_STOCK
      )
      -- Fetch matching set
      SELECT *
      FROM #stock
      WHERE Id >= (SELECT Id FROM first_in_set)
      

      SQL Server 2008 解决方案

      对于仅对窗口函数有基本支持的SQL Server 2008,您可以使用CROSS APPLY计算运行总数:

      -- Calculate a running total of qty by Id descending
      ;WITH stock AS (
          SELECT *
                -- This window function causes the results of this query
                -- to be sorted in descending order by Id
                ,ROW_NUMBER() OVER(ORDER BY Id DESC) AS sort_order   
          FROM #Stock s1
          -- CROSS APPLY 'applies' the query (or UDF) to every row in a result set
          -- This CROSS APPLY query produces a 'running total'
          CROSS APPLY (
              SELECT SUM(Qty) AS TotalQty
              FROM #Stock s2
              WHERE s2.Id >= s1.id
          ) total_calc
          WHERE TotalQty >= s1.CURRENT_STOCK
      ),
      -- Identify first row in mininum set that matches or exceeds CURRENT_STOCK
      first_in_set AS (
          SELECT TOP 1 Id
          FROM stock
          WHERE sort_order = 1
      )
      -- Fetch matching set
      SELECT *
      FROM #stock
      WHERE Id >= (SELECT Id 
                   FROM first_in_set)
      

      【讨论】:

      • sql-server-2008 标记表明它不支持带有order by 子句的sum() over()
      • 很好@SqlZim 我没有注意到版本标签。不幸的是,SQL Server 2008 不支持这个解决方案。我会留下这个答案,以防它对更新版本的人有所帮助。
      • 啊,在我发布更新之前,我还没有看到@JohnCapelletti 的答案。它与我的 SQL Server 2008 答案几乎相同,但更简短。你应该接受他的回答。
      最近更新 更多