create table #sample (
product varchar(100),
Price float
)
insert into #sample values ('Pen',10)
insert into #sample values ('DVD',29)
insert into #sample values ('Pendrive',45)
insert into #sample values ('Mouse',12.5)
insert into #sample values ('TV',49)
select * from #sample
考虑这种情况......
我有1000美元,我想购买上面列出的东西。
我想花掉全部金额
所以我需要一个查询,它会显示所有产品中的单位成本为1000美元
任何帮助?
答案 0 :(得分:3)
您所指的问题也称为knapsack problem。您可以使用一系列算法来解决此问题。最着名的是动态编程,它要求权重是整数,因此您必须以美分为单位。它们都不容易在t-sql中实现。
我实际上找到了某人在sql server中实现的链接:http://sqlinthewild.co.za/index.php/2011/02/22/and-now-for-a-completely-inappropriate-use-of-sql-server/
注意标题,他们也发现它不适合使用数据库。 我建议您用其他语言解决此问题。
答案 1 :(得分:1)
通过将当前项目的空间限制为尚未花费的资金,可以删除大量数据。
在我的家庭系统上运行需要2600毫秒到2800毫秒 在SQLFiddle上,前几次运行可能需要更多,然后它稳定在1800ms左右。
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
现在是临时表,只计算一次CTE
已消失,取而代之的是已旋转的样本表
CROSS JOIN
消失了,它们保留在主要选择中,但少了CROSS JOIN
总是好的TOP
已消失,WHERE
合同只会显示完美的解决方案LEFT JOIN
... nope 它们仍然是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美元的演示可能会导致超时
这项工作如何
只是说是的,你可以在SQLServer中设计一个解决方案,它甚至不是那么困难,但这并不意味着你应该这样做。
答案 2 :(得分:0)
如果我正确理解了问题陈述,那么这是一个非常简单的查询:
select product, price, floor(1000 / price) as QtyToBuy
答案 3 :(得分:0)
这是硬编码的,几乎没有灵活性。我的系统运行2分钟。但可能会有所帮助,如果不是,那就很抱歉。 fnGenerate_Numbers是一个表函数,它返回参数范围内的整数。 Ways to do that.
DECLARE @Max INT,
@Pens money,
@Dvds money,
@Pendrives money,
@Mouses money,
@Tvs money
SELECT @Max = 1000,
@Pens = 10,
@Dvds = 29,
@Pendrives = 45,
@Mouses = 12.5,
@Tvs = 49
;WITH Results AS
(
SELECT p.n pens, d.n dvds, pd.n pendrives, m.n mouses, t.n tvs, tot.cost
FROM fnGenerate_Numbers(0, @Max/@Pens) p -- Pens
CROSS JOIN fnGenerate_Numbers(0, @Max/@Dvds) d -- DVDs
CROSS JOIN fnGenerate_Numbers(0, @Max/@Pendrives) pd -- Pendrives
CROSS JOIN fnGenerate_Numbers(0, @Max/@Mouses) m -- Mouses
CROSS JOIN fnGenerate_Numbers(0, @Max/@Tvs) t -- Tvs
CROSS APPLY (SELECT p.n * @Pens + d.n * @Dvds + pd.n * + @Pendrives + m.n * @Mouses + t.n * @Tvs cost) tot
WHERE tot.cost < @Max
), MaxResults AS
(
SELECT
MAX(pens) pens,
dvds,
pendrives,
mouses,
tvs
FROM Results
GROUP BY
dvds,
pendrives,
mouses,
tvs
)
SELECT mr.*, r.cost FROM MaxResults mr
INNER JOIN Results r ON mr.pens = r.pens
AND mr.dvds = r.dvds
AND mr.pendrives = r.pendrives
AND mr.mouses = r.mouses
AND mr.tvs = r.tvs
ORDER BY cost