[英]Need T-SQL Query find all possible ways
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 美元
有什么帮助吗?
您所指的问题也称为背包问题。 您可以使用一系列算法来解决这个问题。 最著名的是动态规划,它要求权重是整数,所以你必须以美分来衡量。 它们都不容易在 t-sql 中实现。
我实际上在 sql server 中找到了某人实现的链接: http : //sqlinthewild.co.za/index.php/2011/02/22/and-now-for-a-completely-inproper-use-of-sql-服务器/
注意标题,他们也认为这是对数据库的不当使用。 我建议你用不同的语言解决这个问题。
通过将当前项目的空间限制为尚未花费的钱,可以删除大量数据。
在我的家庭系统上运行需要 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
现在是一个临时表,它只计算一次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 演示,总金额为 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
总金额为 100$ 的SQLFiddle Demo (运行大约需要 2 秒)
总金额为 200 美元的SQLFiddle Demo (运行大约需要 6 秒)
1000$ 的演示可能会导致超时
这是如何工作的
这只是说是的,您可以在 SQLServer 中设计一个解决方案,它甚至没有那么困难,但这并不意味着您应该这样做。
如果我正确理解了问题陈述,那么这是一个非常简单的查询:
select product, price, floor(1000 / price) as QtyToBuy
这是硬编码的,几乎没有灵活性。 我的系统运行了 2 分钟。 但可能会有所帮助,抱歉,如果不是。 fnGenerate_Numbers 是一个表函数,它返回参数范围内的整数。 这样做的方法。
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.