列出基于数量的行数

时间:2015-12-09 20:07:36

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

我在SQL Server 2008上运行了一个查询,我遇到的问题是它超出了maxrecursion的限制,我不知道另一种方法。

让我解释一下:此查询列出了基于MfgPN的每个ShipQuantity的行数。

例如,如果MfgPNXYZShipQuantity为5,则会返回5行......依此类推其他MfgPN库...等

这个工作正常,直到最近我收到的记录有ShipQuantity超过100K,而且它崩溃了,因为我相信maxrecursion只允许高达32K。

请告诉我是否有其他方法可以返回超过100K的行?

提前感谢您的帮助。

这是代码。

WITH feedInfo AS 
(
    SELECT 
        df1.dfID, MfgPN, LinkID, ShipQuantity, 
        df1.ShipDescription, df1.Description, 1 AS Number 
    FROM 
        EXT_Feed df1
    WHERE 
        1 = 1
        AND df1.mfgPN IN ('XYZ') 
        AND df1.InvoiceDate = '2015-12-07'
        AND df1.dfID = '2666'

    UNION ALL

    SELECT 
        df2.dfID, df2.MfgPN, df2.LinkID, df2.ShipQuantity,
        df2.ShipDescription, df2.Description, feedInfo.number + 1 AS Number 
    FROM 
        EXT_Feed df2
    INNER JOIN 
        feedInfo ON df2.dfID = feedInfo.dfID
    WHERE 
        1 = 1
        AND number < feedInfo.ShipQuantity
        --AND df2.MfgPN = feedInfo.MfgPN
        AND df2.mfgPN IN ('XYZ')
        AND df2.InvoiceDate = '2015-12-07'
        AND df2.dfID = '2666'
)
SELECT * 
FROM feedInfo
OPTION (maxrecursion 30000)

3 个答案:

答案 0 :(得分:1)

我可以提出2个解决方案。首先是修复,第二是更好。使用适合你的。

  1. MAXRECURSION 0
  2. 如果您确定您的查询正确结束,请不要太害怕MAXRECURSION 0

    此查询在22秒后在我的笔记本上执行:

    WITH SampleValue AS(
    SELECT
        id = 1
        ,ShipQuantity = 1000000 -- One million iterations is not problem
    ),
    REC AS(
        SELECT id, ShipQuantity, number = 1
        FROM SampleValue
        UNION ALL
        SELECT 
                id, ShipQuantity, number = REC.number + 1
            FROM 
                REC 
            WHERE
                ShipQuantity > number
    )
    SELECT TOP(10) * FROM (
        SELECT * FROM Rec
    )S
    ORDER BY number DESC    
    OPTION (MAXRECURSION 0)
    
    1. 更好的解决方案
    2. 在数据库中创建表NUMBERS,并使用值1..1000000填充它。 这是报告数据库中的常用技术。

      CREATE TABLE NUMBERS(number int PRIMARY KEY);
      
      WITH REC AS(
          SELECT NUMBER = 1
          UNION ALL
          SELECT 
                  number = REC.number + 1
              FROM 
                  REC 
              WHERE
                  NUMBER < 1000000
      )
      INSERT INTO NUMBERS
      SELECT NUMBER FROM REC
      OPTION (MAXRECURSION 0);
      

      之后,您的查询中不需要递归。你可以写:

      SELECT 
              df1.dfID, MfgPN, LinkID, ShipQuantity, 
              df1.ShipDescription, df1.Description, N.Number 
          FROM 
              EXT_Feed df1
              JOIN Numbers N ON df1.ShipQuantity <=N.Number
      

答案 1 :(得分:0)

每个递归查询都可以写成循环(小心)。您的递归查询有一个锚点部分和一个递归部分;它可以重构为:

  1. 从CTE结构中取出两个查询。
  2. 更改第一个(锚点)以将其结果放入#temp表格
  3. 更改第二个(递归),使其将#temp表连接到&#34; child&#34;记录而不是递归地调用CTE(它只是内连接#temp代替内连接feedinfo)
  4. 添加一个跟踪&#34;级别的计数器&#34;从0开始
  5. 将第二个查询换行到while循环中,以便在返回记录时继续运行。
  6. 你最终应该使用这种模式(伪代码):

    select <original anchor query>, level = 1 into #temp
    set @level = 0
    
    while records are returned (perhaps @@rowcount > 0)
    begin
      set @level += 1
      insert into #temp select #temp join <original recursive query> where level = @level
    end 
    

答案 2 :(得分:0)

将其作为一种可能的解决方案。我们的想法是将一组详细信息或订单行插入到另一个表中,该表等于输入的数量。这是最适合作为触发器或作为插入详细信息表的单独存储过程的事物之一。

这是一个触发式解决方案。我基本上接受了你的CTE并限制了基于QTY的递归并将其粘贴在AFTER INSERT触发器中。

@Html.DropDownList("countries", Model)

这是一个存储过程解决方案。因为你正在运行一个报告,然后是一个插入(如果我正确地遵循了这个问题。基本上是相同的事情,但是使用shipID作为参数来循环调用SP。

    --CREATE TABLES
    if object_id('shippingTest','U') is not null
        drop table shippingTest;

    create table shippingTest(shipID int identity, mfrID int, shipQty int);
    GO


    if object_id('shippingTestDetail','U') is not null
        drop table shippingTestDetail

    create table shippingTestDetail (detailID int identity, shipID int, mfrID int, shipQty int, detailLineNum int);
    GO

    --CREATE TRIGGER ON INSERT
    CREATE TRIGGER tr_shippingTest_Insert_shippingTestDetail 
    ON shippingTest
    AFTER INSERT 
    AS 
    BEGIN 
        SET NOCOUNT ON;

        with cteQty --original cte with recursion limited by shipQty
        as
        (
            select shipID, mfrID, shipQty, 1 as lineNum
            from inserted
            union all
            select i.shipID, i.mfrID, i.shipQty, c.lineNum+1 as lineNum
            from inserted i
                JOIN cteQty c on c.shipID=i.shipID
            where c.lineNum < i.shipQty
        )
        insert shippingTestDetail
        select shipID, mfrID, shipQty, lineNum
        from cteQty
        order by shipID
    END
    GO

    --INSERT VALUES
    insert shippingTest
    values (111,1),(222,2),(555,5)

    select * from shippingTest
    select * from shippingTestDetail order by shipid