给出了很多折扣后的产品最终价格

时间:2017-07-25 08:45:23

标签: sql sql-server price discount

我有两张桌子。 一张Ids及其价格表,以及每张Id的第二张折扣表。 在折扣表中,Id可以有很多折扣,我需要知道Id的最终价格。

查询它的最佳方式是什么(在一个查询中)? 对于每个id的许多折扣(不仅是示例中下面提到的2),查询应该是通用的

例如 表一

id  price   
1   2.00   
2   2.00   
3   2.00   

表二

id  Discount   
1   0.20   
1   0.30   
2   0.40   
3   0.50   
3   0.60   

最终结果:

id  OrigPrice   PriceAfterDiscount  
1   2.00        1.12     
2   2.00        1.20      
3   2.00        0.40      

4 个答案:

答案 0 :(得分:10)

以下是另一种方法:

SELECT T1.ID, T1.Price, T1.Price * EXP(SUM(LOG(1 - T2.Discount)))
FROM T1 INNER JOIN T2 ON T1.ID = T2.ID
GROUP BY T1.ID, T1.Price

EXP / LOG技巧只是另一种做乘法的方法。

如果T1中的条目没有T2的折扣,您可以将INNER JOIN更改为LEFT JOIN。您最终会得到以下结果:

ID   Price   Discount
4    2.00    NULL

您的逻辑可以在折扣价格列中考虑空值并取代原始价格,或者只为这些添加0折扣记录。

答案 1 :(得分:4)

通常可以使用LOG/EXP函数进行操作,但这很复杂。 这是一个基本的例子:

declare @p table(id int, price money)
declare @d table(id int, discount money)

insert into @p values
(1, 2),
(2, 2),
(3, 2)

insert into @d values
(1, 0.2),
(1, 0.3),
(2, 0.4),
(3, 0.5),
(3, 0.6)


select p.id, 
       p.price, 
       p.price * ca.discount as PriceAfterDiscount  
from @p p
cross apply (select EXP(SUM(LOG(1 - discount))) as discount FROM @d where id = p.id) ca

对于更简单(基于游标的方法),您将需要递归CTE,但在这种情况下,您需要Discounts表中的一些唯一排序列才能正确运行它。这在@ Tanner的回答中有所体现。

最后,您可以使用常规cursor

来解决这个问题

答案 2 :(得分:2)

我相信这会产生预期的结果,使用CTE来迭代折扣。以下解决方案可以单独重新运行。

编辑,在CTE的第一部分中包含可能没有在输出中应用任何折扣的数据。

CREATE TABLE #price
(
    id INT,
    price DECIMAL(5, 2)
);

CREATE TABLE #discount
(
    id INT,
    discount DECIMAL(5, 2)
);

INSERT INTO #price
(
    id,
    price
)
VALUES
(1, 2.00),
(2, 2.00),
(3, 2.00),
(4, 3.50); -- no discount on this item

INSERT INTO #discount
(
    id,
    discount
)
VALUES
(1, 0.20),
(1, 0.30),
(2, 0.40),
(3, 0.50),
(3, 0.60);

-- new temporary table to add a row number to discounts so we can iterate through them
SELECT d.id,
       d.discount,
       ROW_NUMBER() OVER (PARTITION BY id ORDER BY d.discount) rn
INTO #GroupedDiscount
FROM #discount AS d;

-- note left join in first part of cte to get prices that aren't discounted included
WITH cte
AS (SELECT p.id,
           p.price,
           CASE
               WHEN gd.discount IS NULL THEN
                   p.price
               ELSE
                   CAST(p.price * (1.0 - gd.discount) AS DECIMAL(5, 2))
           END AS discountedPrice,
           gd.rn
    FROM #price AS p
        LEFT JOIN #GroupedDiscount AS gd
            ON gd.id = p.id
               AND gd.rn = 1
    UNION ALL
    SELECT cte.id,
           cte.price,
           CAST(cte.discountedPrice * (1.0 - gd.discount) AS DECIMAL(5, 2)) AS discountedPrice,
           cte.rn + 1 AS rn
    FROM cte
        INNER JOIN #GroupedDiscount AS gd
            ON gd.id = cte.id
               AND gd.rn = cte.rn + 1
   )
SELECT cte.id,
       cte.price,
       MIN(cte.discountedPrice) AS discountedPrice
FROM cte
GROUP BY id,
         cte.price;

DROP TABLE #price;
DROP TABLE #discount;
DROP TABLE #GroupedDiscount;

结果:

id  price   discountedPrice
1   2.00    1.12
2   2.00    1.20
3   2.00    0.40
4   3.50    3.50  -- no discount

答案 3 :(得分:1)

正如其他人所说,EXP(SUM(LOG()))是进行计算的方法。从另一个角度来看,这基本上是相同的方法:

WITH CTE_Discount AS 
(
    SELECT Id, EXP(SUM(LOG(1-Discount))) as TotalDiscount
    FROM TableTwo 
    GROUP BY id
)
SELECT t1.id, CAST(Price * COALESCE(TotalDiscount,1) AS Decimal(18,2)) as FinalPRice
FROM TableOne t1
LEFT JOIN CTE_Discount d ON t1.id = d.id

SQLFIddle Demo