【问题标题】:Subtotaling with Ranges and Limited Compatibility具有范围和有限兼容性的小计
【发布时间】:2016-01-21 21:34:44
【问题描述】:

在 SQL Server 2008 R2 上,我正在尝试创建一个存储过程,该过程将根据用户输入的范围为用户提供商品小计。我将不胜感激。

但是,我遇到了一个问题,查询显示所有正在计算的商品,因此用户以最终超出其输入范围的值结束。我将在底部包含一些示例数据以及我尝试过的数据。

这是一个示例(为了清楚起见,我在此示例中删除了一些列):

table

如您所知,对于 2000 到 10000 之间的范围,商品 998-32 的小计将超过 10000,但仍会显示,因为每个 PO NO 单独小于 10000。

这是一些示例数据:

DROP TABLE ##mytable;
CREATE TABLE ##mytable(
   Commodity             VARCHAR(15) NOT NULL
  ,PO_NO                 INTEGER  NOT NULL
  ,LINE_NO               INTEGER  NOT NULL
  ,PO_Line_Description   VARCHAR(82)
  ,Commodity_Description VARCHAR(60) NOT NULL
  ,Fiscal_Year           INTEGER  NOT NULL
  ,Vendor_ID             INTEGER  NOT NULL
  ,Vendor_Name           VARCHAR(20) NOT NULL
  ,QUANTITY              INTEGER  NOT NULL
  ,UNIT_COST             NUMERIC(7,2) NOT NULL
  ,Line_Amount           NUMERIC(7,2) NOT NULL
);
INSERT INTO ##mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-18',1448923,1,'Face Scholastic book order attached.','Sale of Surplus and Obsolete Books',2015,47650,'SCHOLASTIC INC',1,9999.8,9999.8);
INSERT INTO ##mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-32',1416311,2,'First 12 months maintenance agreement to be billed quarterly at .0039 per b/w copy','Sale of Surplus and Obsolete Copy Machines',2015,341479,'RICOH USA, INC',1,5148,5148);
INSERT INTO ##mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-32',1424377,1,NULL,'Sale of Surplus and Obsolete Copy Machines',2015,300590,'KONICA MINOLTA/ CIT',1,2894.58,2894.58);
INSERT INTO ##mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-32',1404031,1,'1st (12) months of (36) month Lease payment for (1) MPC4503 copier.','Sale of Surplus and Obsolete Copy Machines',2015,341479,'RICOH USA, INC',1,2050.68,2050.68);
INSERT INTO ##mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-75',1401552,1,'Blanket order for 50 teachers - each teacher not to exceed $100.00.','Sale of Surplus and Obsolete Paper and Paper Products',2015,27536,'KNOWLEDGE TREE',1,5000,5000);
INSERT INTO ##mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-78',1521390,1,'PL02286>PRESSURE PLUMBER INSTANT DRAIN OPENER 24 SHOT CARTRIDGE','Sale of Surplus and Obsolete Plumbing Equipment and Supplies',2015,402985,'TECH MECH SUPPLY LLC',480,8,3840);

我尝试使用 SUM() OVER (PARTITION BY [ ] ORDER BY [ ] ROWS UNBOUNDED PRECEDING),但显然带有 ROW 的 OVER 子句是在 SQL Server 2012 中引入的,不适用于 2008 R2。

我也尝试过使用 GROUP BY ROLLUP,但我收到一条消息,上面写着“CUBE() 和 ROLLUP() 分组结构在当前兼容模式下是不允许的。它们只在 100 或更高模式下允许。”当我问我们的 DBA 时,他说我们不能从 90 模式转移到 100 模式,因为很多事情都会中断。

所以现在我被下面的查询困住了,它有前面提到的问题,给我我想要的数据的问题,以及最终将超出我指定范围的数据的小计。

附:我还注意到,如果商品的 PO NO 在我选择的范围内,但也有 PO NO 的成本超出此范围,则此查询将为我提供所选范围内的 PO NO,这是非常具有误导性的,因为它仍然是一种商品如果全部计算在内,则超出所选范围。它们根本不应该与真正在我选择范围内的商品一起列入结果。

ALTER PROCEDURE [dbo].[POreport] (
    @Param1 INT
    ,@Param2 INT
    ,@Param3 INT
    )
AS
BEGIN
    SET NOCOUNT ON;

    SELECT DISTINCT ROW_NUMBER() OVER (
            ORDER BY T.Product_ID
                ,T.PO_NO
                ,T.LINE_NO
            ) AS [RowID]
        ,ISNULL(T.PRODUCT_ID, 'NULL') AS [Commodity]
        ,ISNULL(T.PO_NO, 'NULL') AS [PO NO]
        ,ISNULL(T.LINE_NO, 'NULL') AS [LINE NO]
        ,QUOTENAME(T.DESCRIPTION, '"') AS [PO Line Description]
        ,QUOTENAME(C.DESCRIPTION, '"') AS [Commodity Description]
        ,ISNULL(T.FY, 'NULL') AS [Fiscal Year]
        ,PH.Vendor_ID AS [Vendor ID]
        ,QUOTENAME(V.Vendor_Name, '"') AS [Vendor Name]
        ,T.QUANTITY
        ,T.UNIT_COST
        ,T.QUANTITY * T.UNIT_COST AS [Line Amount]
        ,(
            SELECT CAST(0.00 AS NUMERIC(10, 2))
            ) AS Sub_Total_Cost
    INTO ##TmpPOReport
    FROM dbo.DBVW_FI_REQ_PO_ITEMS T
    INNER JOIN dbo.FI_VENDOR FV ON T.INST_ID = FV.INST_ID
    INNER JOIN dbo.FI_REQ_PO_HEADER PH ON T.PO_NO = PH.PO_NO
    INNER JOIN dbo.FI_VENDOR V ON PH.VENDOR_ID = V.VENDOR_ID
    INNER JOIN dbo.FI_COMMODITY C ON T.PRODUCT_ID = C.FI_COMMODITY_CODE
    WHERE T.INST_ID = 'SC00'
        AND T.FY = @Param1
        AND V.VENDOR_TYPE = 'V'
        AND T.PO_NO IS NOT NULL
        AND (
            T.PRODUCT_ID <> ''
            AND T.PRODUCT_ID IS NOT NULL
            )
        AND T.QUANTITY * T.UNIT_COST BETWEEN @Param2
            AND @Param3
    GROUP BY T.PRODUCT_ID
        ,T.PO_NO
        ,T.LINE_NO
        ,T.DESCRIPTION
        ,C.DESCRIPTION
        ,T.FY
        ,PH.Vendor_ID
        ,V.Vendor_Name
        ,T.QUANTITY
        ,T.UNIT_COST
        ,PH.Created_Date
    ORDER BY Commodity

    DECLARE @PID VARCHAR(15) = 00
        ,@QUANTITY INT
        ,@UNIT_COST NUMERIC(10, 2)
        ,@PrevID VARCHAR(15)
        ,@RowID BIGINT
        ,@PrevRowID BIGINT
        ,@RowAmount NUMERIC(10, 2)
        ,@SubTotal NUMERIC(10, 2) = 0.00

    SET NUMERIC_ROUNDABORT OFF;

    WHILE EXISTS (
            SELECT TOP 1 *
            FROM ##TmpPOReport
            WHERE Sub_Total_Cost = 0.00
            )
    BEGIN
        SET @RowAmount = (
                SELECT TOP 1 (QUANTITY * UNIT_COST)
                FROM ##TmpPOReport
                WHERE Sub_Total_Cost = 0.00
                )

        SELECT TOP 1 @PID = Commodity
            ,@RowID = RowID
        FROM ##TmpPOReport
        WHERE Sub_Total_Cost = 0.00

        IF (@PID = @PrevID)
            AND (@RowID <> @PrevRowID)
        BEGIN
            SET @SubTotal += @RowAmount;

            UPDATE T
            SET Sub_Total_Cost = @SubTotal
            FROM ##TmpPOReport T
            WHERE T.Commodity = @PID
                AND RowID = @RowID

            SET @PrevID = @PID;
            SET @PrevRowID = @RowID
        END
        ELSE
        BEGIN
            SET @SubTotal = @RowAmount;

            UPDATE T
            SET Sub_Total_Cost = @SubTotal
            FROM ##TmpPOReport T
            WHERE T.Commodity = @PID
                AND RowID = @RowID

            SET @PrevID = @PID;
            SET @PrevRowID = @RowID
        END
    END

    SET NUMERIC_ROUNDABORT ON;

    SELECT *
    FROM ##TmpPOReport
    WHERE [Line Amount] BETWEEN @Param2
            AND @Param3

    DROP TABLE ##TmpPOReport
END

谢谢!

【问题讨论】:

    标签: sql stored-procedures sql-server-2008-r2


    【解决方案1】:

    抱歉,如果不清楚。如果您想复制SUM OVER ROWS,则需要使用下面的 CTE 或类似的子查询。这不是一个优化的查询,但它会给你商品的运行总和。它使用RANK() OVER PARTITION 获取商品中每个 PO 的实例或行号。

        DECLARE @mytable TABLE(
           Commodity             VARCHAR(15) NOT NULL
          ,PO_NO                 INTEGER  NOT NULL
          ,LINE_NO               INTEGER  NOT NULL
          ,PO_Line_Description   VARCHAR(82)
          ,Commodity_Description VARCHAR(60) NOT NULL
          ,Fiscal_Year           INTEGER  NOT NULL
          ,Vendor_ID             INTEGER  NOT NULL
          ,Vendor_Name           VARCHAR(20) NOT NULL
          ,QUANTITY              INTEGER  NOT NULL
          ,UNIT_COST             NUMERIC(7,2) NOT NULL
          ,Line_Amount           NUMERIC(7,2) NOT NULL
        );
        INSERT INTO @mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-18',1448923,1,'Face Scholastic book order attached.','Sale of Surplus and Obsolete Books',2015,47650,'SCHOLASTIC INC',1,9999.8,9999.8);
        INSERT INTO @mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-32',1416311,2,'First 12 months maintenance agreement to be billed quarterly at .0039 per b/w copy','Sale of Surplus and Obsolete Copy Machines',2015,341479,'RICOH USA, INC',1,5148,5148);
        INSERT INTO @mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-32',1424377,1,NULL,'Sale of Surplus and Obsolete Copy Machines',2015,300590,'KONICA MINOLTA/ CIT',1,2894.58,2894.58);
        INSERT INTO @mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-32',1404031,1,'1st (12) months of (36) month Lease payment for (1) MPC4503 copier.','Sale of Surplus and Obsolete Copy Machines',2015,341479,'RICOH USA, INC',1,2050.68,2050.68);
        INSERT INTO @mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-75',1401552,1,'Blanket order for 50 teachers - each teacher not to exceed $100.00.','Sale of Surplus and Obsolete Paper and Paper Products',2015,27536,'KNOWLEDGE TREE',1,5000,5000);
        INSERT INTO @mytable(Commodity,PO_NO,LINE_NO,PO_Line_Description,Commodity_Description,Fiscal_Year,Vendor_ID,Vendor_Name,QUANTITY,UNIT_COST,Line_Amount) VALUES ('998-78',1521390,1,'PL02286>PRESSURE PLUMBER INSTANT DRAIN OPENER 24 SHOT CARTRIDGE','Sale of Surplus and Obsolete Plumbing Equipment and Supplies',2015,402985,'TECH MECH SUPPLY LLC',480,8,3840);
    
    
        DECLARE @FiscalYear INT
        DECLARE @LowTotalCost INT
        DECLARE @HighTotalCost INT
    
        SET @FiscalYear=2015
        SET @LowTotalCost=1000
        SET @HighTotalCost=10000
    
    
    
    
    SELECT
        *
    FROM
    (       
    
        SELECT Commodity,PO_NO,QUANTITY,UNIT_COST,Line_Amount,
            RunningTotal=
            (
                SELECT SUM(TotalCost) FROM 
                (
                    SELECT Commodity,TotalCost =(MT.QUANTITY * MT.UNIT_COST),
                        Instance=RANK()OVER(PARTITION BY Commodity ORDER BY Commodity,PO_NO DESC)
                    FROM 
                        @mytable MT
                    WHERE
                        (MT.QUANTITY * MT.UNIT_COST) BETWEEN @LowTotalCost AND @HighTotalCost
                        AND
                        (MT.Fiscal_Year=@FiscalYear)
                )AS Y 
                WHERE
                    (Y.Commodity=X.Commodity) AND(Y.Instance<=X.Instance)
            )
        FROM
        (
            SELECT Commodity,PO_NO,QUANTITY,UNIT_COST,Line_Amount,
                TotalCost = (MT.QUANTITY * MT.UNIT_COST),
                Instance=RANK()OVER(PARTITION BY Commodity ORDER BY Commodity,PO_NO DESC)
            FROM 
                @mytable MT
            WHERE
                --IS FILTER HERE -->(MT.QUANTITY * MT.UNIT_COST) BETWEEN @LowTotalCost AND @HighTotalCost
                --AND
                (MT.Fiscal_Year=@FiscalYear)
        )AS X
    )AS Z
    WHERE
    
        (Commodity IN   
        (
            SELECT Commodity FROM
            (
                SELECT 
                    Commodity, 
                    Total=SUM(QUANTITY * UNIT_COST) 
                FROM 
                    @mytable mt2 
                WHERE 
                    mt2.Fiscal_Year=@FiscalYear 
                GROUP BY 
                    Commodity 
            )AS A
            WHERE
                A.Total BETWEEN @LowTotalCost AND @HighTotalCost
        )
        )
    SELECT 
        *
    FROM 
        @mytable MT
    

    【讨论】:

    • 从我看到的使用你的结果来看,它给了我和我上面一样的东西:puu.sh/kYgYq/1f5bc85410.png 遇到了同样的误导性问题。如果我为商品选择一个范围,它会给我不完整的累计。例如,当我选择 1000 到 10000 之间的商品时,商品 998-32 会显示 8042.58 和 2894.58,而 998-32 确实是 10093.26(超过 10000)。有没有办法为每种商品添加一个列,该列是每种商品的最高运行总量,以便我可以从中选择我的范围?谢谢。
    • 我在商品 IN() 上添加了最后一个 WHERE 子句,以支持您评论中的问题。但是,此时我会转换并使用 CTE,因为它更具可读性。
    • 经过测试,运行总计似乎工作正常。但是,我确实注意到如果商品编号相同而不考虑财政年度,它会添加商品小计。例如,对于商品 035-10,无论 2014 年、2015 年还是 2016 年,它将添加所有运行总计。有没有办法将运行总计分为财政年度和商品,以便在 2014 年为 035-10 和在 2015 年为 035-10 等运行总计?
    • 或者一种将运行总计分组到财政年度和商品的方法?
    猜你喜欢
    • 2012-09-11
    • 1970-01-01
    • 2014-07-17
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多