﻿<?xml version='1.0' encoding='UTF-8'?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>SQLServerCentral / SQL Server 2005 / T-SQL (SS2K5)  / Filling Buckets / Latest Posts</title><generator>InstantForum.NET v2.9.0</generator><description>SQLServerCentral</description><link>http://www.sqlservercentral.com/Forums/</link><webMaster>notifications@sqlservercentral.com</webMaster><lastBuildDate>Sun, 26 May 2013 00:01:12 GMT</lastBuildDate><ttl>20</ttl><item><title>RE: Filling Buckets</title><link>http://www.sqlservercentral.com/Forums/Topic568498-338-1.aspx</link><description>This is called the Bin Packing problem and there is a lot of literature on it. Now the bad news; it is known to be an NP-Complete, so any solution we find will be sub-optimal. CREATE TABLE Buckets (bucket_nbr INTEGER NOT NULL PRIMARY KEY, bucket_capacity INTEGER NOT NULL, bucket_contents INTEGER NOT NULL, CHECK (bucket_contents BETWEEN 0 AND bucket_capacity));INSERT INTO Buckets (bucket_nbr, bucket_capacity, bucket_contents)VALUES (1, 10, 1), (2, 5, 4), (3, 10, 0), (4, 10, 0);Want to make a rule that we fill from left to right? So we can find the available capacity at any point in the sequence of buckets.CREATE VIEW Running_Total (bucket_nbr, available_capacity_tot)ASSELECT bucket_nbr,       SUM (bucket_capacity – bucket_contents)        OVER (ORDER BY bucket_nbr              ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)  FROM Buckets; Now we can find the bucket range, (1 to n) &amp;lt;= 21 and fill them. But the better way is to look for the "best fit" bucket and use it; we often get lucky and can do it with one bucket. The better solutions are (ugh!) procedural and hueristic.</description><pubDate>Fri, 14 Sep 2012 10:21:56 GMT</pubDate><dc:creator>CELKO</dc:creator></item><item><title>RE: Filling Buckets</title><link>http://www.sqlservercentral.com/Forums/Topic568498-338-1.aspx</link><description>This is called the Bin Packing problem and there is a lot of literature on it. Now the bad news; it is known to be an NP-Complete, so any solution we find will be sub-optimal.</description><pubDate>Fri, 14 Sep 2012 09:44:09 GMT</pubDate><dc:creator>CELKO</dc:creator></item><item><title>RE: Filling Buckets</title><link>http://www.sqlservercentral.com/Forums/Topic568498-338-1.aspx</link><description>[quote][b]dwain.c (9/11/2012)[/b][hr][quote][b]ChrisM@Work (9/11/2012)[/b][hr]Using the sample data provided, a simple calculation rCTE works fine with both positive and negative numbers.[code="sql"]DECLARE @AmountToAllocate INT = 21 ;WITH Calculator AS (SELECT 	BucketID, TotalSize, Amount,	AmountLeftToAllocate = CASE 		WHEN @AmountToAllocate &amp;gt; (TotalSize - Amount) THEN @AmountToAllocate - (TotalSize - Amount)		WHEN @AmountToAllocate &amp;lt; 0 AND ABS(@AmountToAllocate) &amp;gt; Amount THEN Amount + @AmountToAllocate 		ELSE 0 END,	NewAmount = CASE 		WHEN @AmountToAllocate &amp;gt; (TotalSize - Amount) THEN TotalSize		WHEN @AmountToAllocate &amp;lt; 0 AND ABS(@AmountToAllocate) &amp;gt; Amount THEN 0				ELSE Amount + @AmountToAllocate END  FROM dbo.BucketsWHERE BucketID = 1UNION ALLSELECT 	tr.BucketID, tr.TotalSize, tr.Amount,  	AmountLeftToAllocate = CASE 		WHEN lr.AmountLeftToAllocate &amp;gt; (tr.TotalSize - tr.Amount) THEN lr.AmountLeftToAllocate - (tr.TotalSize - tr.Amount)		WHEN lr.AmountLeftToAllocate &amp;lt; 0 AND ABS(lr.AmountLeftToAllocate) &amp;gt; tr.Amount THEN tr.Amount + lr.AmountLeftToAllocate 		ELSE 0 END,	NewAmount = CASE 		WHEN lr.AmountLeftToAllocate &amp;gt; (tr.TotalSize - tr.Amount) THEN tr.TotalSize		WHEN lr.AmountLeftToAllocate &amp;lt; 0 AND ABS(lr.AmountLeftToAllocate) &amp;gt; tr.Amount THEN 0				ELSE tr.Amount + lr.AmountLeftToAllocate END  FROM dbo.Buckets trINNER JOIN Calculator lr ON lr.BucketID + 1 = tr.BucketID )SELECT  	BucketID, 	TotalSize,	Amount = NewAmount, 	OldAmount = Amount FROM Calculator[/code][/quote]Nice one Chris!  For some reason I just couldn't wrap my head around solving it that way.[/quote]Cheers buddy. It took two goes, the first was rubbish :-D</description><pubDate>Thu, 13 Sep 2012 02:41:14 GMT</pubDate><dc:creator>ChrisM@Work</dc:creator></item><item><title>RE: Filling Buckets</title><link>http://www.sqlservercentral.com/Forums/Topic568498-338-1.aspx</link><description>[quote][b]ChrisM@Work (9/11/2012)[/b][hr]Using the sample data provided, a simple calculation rCTE works fine with both positive and negative numbers.[code="sql"]DECLARE @AmountToAllocate INT = 21 ;WITH Calculator AS (SELECT 	BucketID, TotalSize, Amount,	AmountLeftToAllocate = CASE 		WHEN @AmountToAllocate &amp;gt; (TotalSize - Amount) THEN @AmountToAllocate - (TotalSize - Amount)		WHEN @AmountToAllocate &amp;lt; 0 AND ABS(@AmountToAllocate) &amp;gt; Amount THEN Amount + @AmountToAllocate 		ELSE 0 END,	NewAmount = CASE 		WHEN @AmountToAllocate &amp;gt; (TotalSize - Amount) THEN TotalSize		WHEN @AmountToAllocate &amp;lt; 0 AND ABS(@AmountToAllocate) &amp;gt; Amount THEN 0				ELSE Amount + @AmountToAllocate END  FROM dbo.BucketsWHERE BucketID = 1UNION ALLSELECT 	tr.BucketID, tr.TotalSize, tr.Amount,  	AmountLeftToAllocate = CASE 		WHEN lr.AmountLeftToAllocate &amp;gt; (tr.TotalSize - tr.Amount) THEN lr.AmountLeftToAllocate - (tr.TotalSize - tr.Amount)		WHEN lr.AmountLeftToAllocate &amp;lt; 0 AND ABS(lr.AmountLeftToAllocate) &amp;gt; tr.Amount THEN tr.Amount + lr.AmountLeftToAllocate 		ELSE 0 END,	NewAmount = CASE 		WHEN lr.AmountLeftToAllocate &amp;gt; (tr.TotalSize - tr.Amount) THEN tr.TotalSize		WHEN lr.AmountLeftToAllocate &amp;lt; 0 AND ABS(lr.AmountLeftToAllocate) &amp;gt; tr.Amount THEN 0				ELSE tr.Amount + lr.AmountLeftToAllocate END  FROM dbo.Buckets trINNER JOIN Calculator lr ON lr.BucketID + 1 = tr.BucketID )SELECT  	BucketID, 	TotalSize,	Amount = NewAmount, 	OldAmount = Amount FROM Calculator[/code][/quote]Nice one Chris!  For some reason I just couldn't wrap my head around solving it that way.</description><pubDate>Tue, 11 Sep 2012 18:10:26 GMT</pubDate><dc:creator>dwain.c</dc:creator></item><item><title>RE: Filling Buckets</title><link>http://www.sqlservercentral.com/Forums/Topic568498-338-1.aspx</link><description>Using the sample data provided, a simple calculation rCTE works fine with both positive and negative numbers.[code="sql"]DECLARE @AmountToAllocate INT = 21 ;WITH Calculator AS (SELECT 	BucketID, TotalSize, Amount,	AmountLeftToAllocate = CASE 		WHEN @AmountToAllocate &amp;gt; (TotalSize - Amount) THEN @AmountToAllocate - (TotalSize - Amount)		WHEN @AmountToAllocate &amp;lt; 0 AND ABS(@AmountToAllocate) &amp;gt; Amount THEN Amount + @AmountToAllocate 		ELSE 0 END,	NewAmount = CASE 		WHEN @AmountToAllocate &amp;gt; (TotalSize - Amount) THEN TotalSize		WHEN @AmountToAllocate &amp;lt; 0 AND ABS(@AmountToAllocate) &amp;gt; Amount THEN 0				ELSE Amount + @AmountToAllocate END  FROM dbo.BucketsWHERE BucketID = 1UNION ALLSELECT 	tr.BucketID, tr.TotalSize, tr.Amount,  	AmountLeftToAllocate = CASE 		WHEN lr.AmountLeftToAllocate &amp;gt; (tr.TotalSize - tr.Amount) THEN lr.AmountLeftToAllocate - (tr.TotalSize - tr.Amount)		WHEN lr.AmountLeftToAllocate &amp;lt; 0 AND ABS(lr.AmountLeftToAllocate) &amp;gt; tr.Amount THEN tr.Amount + lr.AmountLeftToAllocate 		ELSE 0 END,	NewAmount = CASE 		WHEN lr.AmountLeftToAllocate &amp;gt; (tr.TotalSize - tr.Amount) THEN tr.TotalSize		WHEN lr.AmountLeftToAllocate &amp;lt; 0 AND ABS(lr.AmountLeftToAllocate) &amp;gt; tr.Amount THEN 0				ELSE tr.Amount + lr.AmountLeftToAllocate END  FROM dbo.Buckets trINNER JOIN Calculator lr ON lr.BucketID + 1 = tr.BucketID )SELECT  	BucketID, 	TotalSize,	Amount = NewAmount, 	OldAmount = Amount FROM Calculator[/code]</description><pubDate>Tue, 11 Sep 2012 07:24:42 GMT</pubDate><dc:creator>ChrisM@Work</dc:creator></item><item><title>RE: Filling Buckets</title><link>http://www.sqlservercentral.com/Forums/Topic568498-338-1.aspx</link><description>Thanks for the links guys. Obviously wasn't looking hard enough.:-)</description><pubDate>Tue, 11 Sep 2012 05:08:20 GMT</pubDate><dc:creator>faiselj</dc:creator></item><item><title>RE: Filling Buckets</title><link>http://www.sqlservercentral.com/Forums/Topic568498-338-1.aspx</link><description>The first looks more like a bin packing problem to me:[url]http://sqlblog.com/blogs/hugo_kornelis/archive/2007/11/30/bin-packing-part-1-setting-a-baseline.aspx[/url]There's a series of 5 articles by Hugo Kornelis at this link (to the first).  Very complicated, but the fastest solutions typically involve a set-based loop of some sort.You didn't mention if speed is an issue for you.  The CURSOR will work OK as long as you don't have too many buckets to fill.PM me if you would like more information.</description><pubDate>Tue, 11 Sep 2012 04:03:41 GMT</pubDate><dc:creator>dwain.c</dc:creator></item><item><title>RE: Filling Buckets</title><link>http://www.sqlservercentral.com/Forums/Topic568498-338-1.aspx</link><description>I think this is a running totals problem, have a look here[url]http://www.sqlservercentral.com/articles/T-SQL/68467/[/url]If you are using SQL Server 2012, you can use the built-in windowing functions[code="sql"]DECLARE @ToAllocate INT = 21;WITH CTE AS (SELECT TotalSize,       Amount,       BucketID,       TotalSize - Amount AS Remaining,       SUM(TotalSize - Amount) OVER (ORDER BY BucketID ROWS UNBOUNDED PRECEDING)  AS Remaining_RunningTotalFROM dbo.Buckets)SELECT TotalSize,       Amount,       BucketID,       CASE WHEN Remaining_RunningTotal &amp;lt;= @ToAllocate            THEN Remaining            ELSE @ToAllocate - Remaining_RunningTotal + Remaining       END AS AmountToAddFROM CTEWHERE Remaining_RunningTotal - Remaining &amp;lt; @ToAllocateORDER BY BucketID;[/code]</description><pubDate>Tue, 11 Sep 2012 03:53:35 GMT</pubDate><dc:creator>Mark-101232</dc:creator></item><item><title>RE: Filling Buckets</title><link>http://www.sqlservercentral.com/Forums/Topic568498-338-1.aspx</link><description>Interesting. I have been working on a similar task. This is to take away sales figures (already sold items) from a set of monthly sales forecast figures.eg. Sales Forecast for an item:Month 1: 200, Sales 450 (already sold items in Month 1)Month 2: 100 (no Sales beyond current month, only future orders)Month 3: 100Month 4: 120I have to remove 450 from the month buckets, starting at M1.So the update forecast would be:M1: 0M2: 0M3: 0M4: 70There is an added constraint, which is to only only make adjustments up to a certain number of months in the future. eg. If Months to Consider = 4 then the result would be as above.But if Months to Consider = 3 then the result for Month 4 would remain at the original 120. The extra 70 from the Sales would become part of the original Sales forecast.Orders (not shown) as opposed to Sales also affect the forecast.Got no code to show, but it is similar to what you have shown.But I ended up using a cursor around the procedure because I have 6,000 item forecasts to process. (I couldn't work out how to do the sub-selects without the cursor, and was running out of time).I have not found anything much better than what you have shown. If I had some more time I would investigate, as I can't help feeling there may be some "Tally table" solution to this.</description><pubDate>Tue, 11 Sep 2012 03:17:18 GMT</pubDate><dc:creator>faiselj</dc:creator></item><item><title>Filling Buckets</title><link>http://www.sqlservercentral.com/Forums/Topic568498-338-1.aspx</link><description>A customer had reported an issue with one of our stored procedures so I took a look and found that the developer had used a cursor to implement his solution.  Taking the “all cursors are evil” view I thought that I had better rewrite it, however it wasn’t as easy as I hoped.The problem can be simplified to...  You have a set of buckets, with varying sizes, so of which are already full/partly full.  You are then given some more water which you are to use to fill the buckets on a first-come-first-served basis.This can be modelled with the following table[code]create table dbo.Buckets ( TotalSize		int not null,  Amount		int not null,  BucketID		int not null,constraint pk_Buckets primary key (BucketID),constraint ck_Buckets_Amount check ( Amount between 0 and TotalSize))go[/code]WhereTotalSize = the total amount the bucket can holdAmount = the amount currently in the bucketBucketID = unique id for the bucket, and is used to determine the order of the buckets ExampleSo if, we had the following 4 buckets[code]insert into dbo.Buckets (TotalSize,Amount,BucketID)select 10, 1, 1goinsert into dbo.Buckets (TotalSize,Amount,BucketID)select 5, 4, 2goinsert into dbo.Buckets (TotalSize,Amount,BucketID)select 10, 0, 3goinsert into dbo.Buckets (TotalSize,Amount,BucketID)select 10, 0, 4go[/code]and we had to allocate 21 units of water we would end up withBucket 1=10Bucket 2 =5Bucket 3 =10Bucket 4 =1[b]My Solution[/b]The solution I came up was to use two update statements, the first one to handle the buckets which would be completely filled, and the second one to partially fill the final bucket.  This seemed to be working well, until I went to look at the issue reported by the customer - they also needed the ability to empty the buckets as well.  My best solution so far (below), is to use another two update statements with an “if” statement to control which are to be used.So my questions are1.	Is this a “standard” problem with a well known solution?2.	Is there a better solution,  as I have to use an ‘if’ statement and double-subselects.3.     Is updating the @AmountToAllocate variable in an update statement a good idea?thanks in advanceDavidMy sql is...[code]-- The amount of water was have to allocatedeclare @AmountToAllocate intset @AmountToAllocate = 21-- 'Before'select * from dbo.Buckets-- If the amount is positive then we are filling the bucketsif @AmountToAllocate &amp;gt; 0 begin	-- Fill these buckets completely,  decrease our "amount to allocate" as we go.        -- We update just the buckets then we can completely full.  If we filled the following bucket then        -- we would have exceed the amount of water we have been given to allocate.	update dbo.Buckets	   set Amount = TotalSize, 		   @AmountToAllocate = @AmountToAllocate - (TotalSize - Amount)	 where Amount != TotalSize	   and BucketID &amp;lt;= ( select max(B2.BucketID)				 		  from dbo.Buckets B2						 where @AmountToAllocate &amp;gt;=  ( select sum(TotalSize - Amount)												 from dbo.Buckets B3												where B3.BucketID &amp;lt;= B2.BucketID											)					  )	-- Part fill the remaining bucket	update dbo.Buckets	   set Amount = Amount + @AmountToAllocate	 where BucketID = ( select min(B.BucketID)						  from dbo.Buckets B						 where B.Amount != B.TotalSize)endelsebegin--We have a negative amount so we are emptying the buckets	-- Complete empty buckets	update dbo.Buckets	   set Amount = 0, 		   @AmountToAllocate = @AmountToAllocate + Amount	 where Amount != 0	   and BucketID &amp;gt;= ( select min(B2.BucketID)				 		  from dbo.Buckets B2						 where abs(@AmountToAllocate) &amp;gt;=  ( select sum(Amount)												from dbo.Buckets B3												where B3.BucketID &amp;gt;= B2.BucketID													       )					  )	-- Part empty the remaining bucket	update dbo.Buckets	   set Amount = Amount - abs(@AmountToAllocate)	 where BucketID = ( select max(B.BucketID)						  from dbo.Buckets B						 where B.Amount != 0)end--'After'select * from dbo.Buckets[/code]</description><pubDate>Fri, 12 Sep 2008 07:42:56 GMT</pubDate><dc:creator>David Betteridge</dc:creator></item></channel></rss>