2014-04-19 90 views
3
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 

考慮這種情況......需要T-SQL查詢查找所有可能的方式

我有1000 $,我想買上面列出的東西。

我想花的全部金額

所以我需要一個查詢這給多少單位中的所有產品將耗資1000 $

任何幫助嗎?

+0

您是否想在t-sql中實現[揹包問題](http://en.wikipedia.org/wiki/Knapsack_problem)的解決方案? – flup

+1

不清楚您是否想知道每件產品有多少可以1000美元購買,或者您可以購買1000美元的產品的數量和組合。 – Frazz

+0

我可以用1000美元購買多少種和哪些產品組合。應該是所有產品的混合物。 – vignesh

回答

0

這是硬編碼,有一點flexiblity。讓我的系統運行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 
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/

公告標題,他們也發現這是一個不恰當使用的數據庫。 我建議你用另一種語言來解決這個問題。

+0

我不能打開那...被重定向到其他地方...它要求我上傳圖片..沒有別的.. – vignesh

+1

+1我猜@flup是正確的不在數據庫中這樣做 - 你可以CLR程序instaead http://msdn.microsoft.com/en-us/library/8bs2ecf4(v=vs.110).aspx – gotqn

+0

@vignesh不確定,鏈接在這裏工作正常。但重要的是,即使這位作者認爲sql在實現時不是這樣。 – flup

1

可以通過將當前項目的空間限制爲尚未使用的資金來刪除大量數據。

在我的家庭系統上,運行需要2600毫秒和2800毫秒。
在SQLFiddle上,前幾次運行可能需要更多,然後穩定在1800毫秒左右。

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 s的消失了,樣品臺取代轉動
    • 產品中的CROSS JOIN ŝCTE都走了,他們仍然在主選擇不過四少CROSS JOIN總是好的
  • TOP消失了,WHERE contition需要只顯示了完美的解決方案的護理
  • 在主選擇我們現在有LEFT JOIN ... 沒了他們仍然CROSS JOIN,將LEFT JOIN被使用,因爲CROSS JOIN沒有使用限制的行數的ON條款

它是如何工作的
未轉讓的產品價格可以通過'name'(列名)獲得產品價格。
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
  • 計數器使用計數單位從0到9999,或者您想要花費的較便宜的金額除以較便宜的物品的價格所徵收的限制。
  • 每件物品都有自己的CTE來計算該項目在您的首都內的任何次數的價格。
  • 產品CTE交叉連接,以檢查每個組合的限制內的金錢。
  • TOP 10在這裏,因爲可以有多個組合使用確切的金額。

這只是說,是的,你可以在SQLServer中設計一個解決方案,但它並不那麼困難,但這並不意味着你應該這樣做。

0

如果我正確理解問題的陳述,那麼這是一個非常簡單的查詢:

select product, price, floor(1000/price) as QtyToBuy 
+0

這是用於單件產品..我問過我可以用1000美元購買多少種產品和哪些組合產品 – vignesh

+0

你可能會考慮修改這個問題來澄清這一點。 「組合」一詞應該放在那裏。 – Metaphor