可以通过将当前项目的空间限制为尚未花费的钱来删除大量数据。
在我的家庭系统上运行需要 2.6 到 2.8 秒。
在 SQLFiddle 上,前几次运行可能需要更多时间,然后稳定在 1.8 秒左右。
WITH Unit(N) AS (
SELECT N
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) t(N)
), Counter(N) AS (
SELECT u.n + 10*te.n + 100*hu.n
FROM Unit u CROSS JOIN Unit te CROSS JOIN Unit hu
WHERE u.n + 10*te.n + 100*hu.n <= (SELECT 1000 / Min(Price) FROM Sample))
SELECT N INTO #Counter FROM Counter;
WITH Products AS (
SELECT [Pen], [DVD], [PenDrive], [Mouse], [TV]
FROM (SELECT product, price FROM sample) s PIVOT
(MAX(price) FOR product IN ([Pen], [DVD], [PenDrive], [Mouse], [TV])) p
)
SELECT cP.N Pen, cD.N DVD, cPe.N PenDrive, cM.N Mouse
, CAST((1000 - p.pen * cP.N - p.DVD * cD.N
- p.PenDrive * cPe.N - p.Mouse * cM.N) / p.TV as INT) TV
, Money = p.pen * cP.N + p.DVD * cD.N + p.PenDrive * cPe.N
+ p.Mouse * cM.N
+ p.TV * CAST((1000 - p.pen * cP.N - p.DVD * cD.N
- p.PenDrive * cPe.N - p.Mouse * cM.N) / p.TV as INT)
From Products p
LEFT Join #Counter cP ON cP.N <= (1000 / p.Pen)
LEFT Join #Counter cD ON cD.N <= ((1000 - p.pen * cP.N) / p.DVD)
LEFT Join #Counter cPe
ON cPe.N <= ((1000 - p.pen * cP.N - p.DVD * cD.N) / p.PenDrive)
LEFT Join #Counter cM
ON cM.N <= ((1000 - p.pen * cP.N - p.DVD * cD.N
- p.PenDrive * cPe.N) / p.Mouse)
WHERE p.pen * cP.N + p.DVD * cD.N
+ p.PenDrive * cPe.N + p.Mouse * cM.N
+ p.TV * CAST((1000 - p.pen * cP.N - p.DVD * cD.N - p.PenDrive * cPe.N
- p.Mouse * cM.N) / p.TV as INT) = 1000
发生了什么变化
-
#Counter 现在是一个临时表,它只计算一次
- 各种产品
CTEs 不见了,取而代之的是旋转的示例表
- Products CTE 中的
CROSS JOIN 已消失,它们仍保留在主选择中,但少四个 CROSS JOIN 总是好的
-
TOP 消失了,WHERE 条件只负责显示完美的解决方案
- 在主选择中,我们现在有
LEFT JOIN... 不它们仍然是CROSS JOIN,使用LEFT JOIN,因为CROSS JOIN 没有ON用于限制行数的子句
工作原理
未透视的产品价格可以通过“名称”(列名)获取产品价格。
FROM 块的工作方式类似于 4 个缩进的 FOR,其中 (1000 - 已花费) / price 子句将计数器限制为不超过 1000$ 的值。
最后一个产品总是按差价计算(还有多少美元未花费/价格),完全放弃CROSS JOIN
SQLFiddle Demo 总金额为 1000 美元。
提供的数据有3531个解决方案
旧答案
如果您想让您的服务器在午餐时间一直运行,那么这里是一个愚蠢的解决方案。
请注意,此解决方案探索了问题的所有空间,因此性能充其量只能是蹩脚的。
WITH Base(N) AS(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1)
, Unit(N) AS (
SELECT Row_Number() Over (ORDER BY (SELECT NULL)) - 1
FROM Base)
, Counter(N) AS (
SELECT u.n + 10*te.n + 100*hu.n + 1000*th.n
FROM Unit u
CROSS JOIN Unit te --tens
CROSS JOIN Unit hu --hundreds
CROSS JOIN Unit th --thousands
WHERE u.n + 10*te.n + 100*hu.n + 1000*th.n <= (SELECT 1000 / Min(Price)
FROM Sample))
, Pens AS (
SELECT product, Price = price * N, Quantity = N
FROM sample CROSS JOIN Counter
WHERE product = 'Pen' AND N <= 1000 / Price)
, DVDs AS (
SELECT product, Price = price * N, Quantity = N
FROM sample CROSS JOIN Counter
WHERE product = 'DVD' AND N <= 1000 / Price)
, Pendrives AS (
SELECT product, Price = price * N, Quantity = N
FROM sample CROSS JOIN Counter
WHERE product = 'Pendrive' AND N <= 1000 / Price)
, Mouses AS (
SELECT product, Price = price * N, Quantity = N
FROM sample CROSS JOIN Counter
WHERE product = 'Mouse' AND N <= 1000 / Price)
, TVs AS (
SELECT product, Price = price * N, Quantity = N
FROM sample CROSS JOIN Counter
WHERE product = 'TV' AND N <= 1000 / Price
)
SELECT TOP 10
Pen = p.Quantity
, DVD = d.Quantity
, Pendrive = pe.Quantity
, Mouse = m.Quantity
, TV = t.Quantity
, Price = p.Price + d.price + pe.price + m.price + t.price
FROM pens p
CROSS JOIN DVDs d
CROSS JOIN Pendrives pe
CROSS JOIN Mouses m
CROSS JOIN TVs t
WHERE p.Price + d.price + pe.price + m.price + t.price <= 1000
ORDER BY p.Price + d.price + pe.price + m.price + t.price DESC
SQLFiddle Demo 总金额为 100$(运行大约需要 2 秒)
SQLFiddle Demo 总金额为 200$(运行大约需要 6 秒)
1000 美元的演示可能会导致超时
这是如何工作的
- 基地作为单位的基地。
- 从 0 到 9 的单位计数
- 计数器使用 Unit 从 0 到 9999 计数,或者您想花费的较便宜的钱除以较便宜商品的价格所施加的限制。
- 每件商品都有其 CTE,可以在您的首都内多次计算该商品的价格。
- 产品 CTE 交叉连接,以检查金额限制内的每个组合。
- TOP 10 之所以出现,是因为可以有不止一种组合使用确切的金额。
这只是说是的,你可以在 SQLServer 中设计一个解决方案,它甚至没有那么困难,但这并不意味着你应该这样做。