Filling boxes

February 25, 2011 6:50 am

I don’t have much time right now but I’ll clean up this post later. For now just enjoy the snippet.

DECLARE @Threshold int
DECLARE @input TABLE (
	ID int,
	name varchar(10),
	size int,
	groupNum int
)

SET @Threshold = 500

INSERT INTO @input (name, size)
	SELECT 'rrkedt', 67
	UNION ALL
	SELECT 'zugmto', 114
	UNION ALL
	SELECT 'jpddyv', 121
	UNION ALL
	SELECT 'lzrphx', 239
	UNION ALL
	SELECT 'jvltsm', 212
	UNION ALL
	SELECT 'yvowre', 107
	UNION ALL
	SELECT 'sxpchv', 244
	UNION ALL
	SELECT 'ighgvn', 249
	UNION ALL
	SELECT 'kgomhv', 173
	UNION ALL
	SELECT 'eshsya', 104
	UNION ALL
	SELECT 'xhqfpj', 175
	UNION ALL
	SELECT 'epptoi', 146
	UNION ALL
	SELECT 'ainkze', 218
	UNION ALL
	SELECT 'tbpukv', 148
	UNION ALL
	SELECT 'visdid', 30
	UNION ALL
	SELECT 'cpjhkn', 27
	UNION ALL
	SELECT 'rapqxl', 221
	UNION ALL
	SELECT 'fakkki', 176
	UNION ALL
	SELECT 'mgkuos', 69
	UNION ALL
	SELECT 'hlnvjc', 190
	UNION ALL
	SELECT 'yrlmfh', 244
	UNION ALL
	SELECT 'pjrnhx', 95
	UNION ALL
	SELECT 'fnggir', 11
	UNION ALL
	SELECT 'pnkxhd', 88
	UNION ALL
	SELECT 'jtmahw', 192
	UNION ALL
	SELECT 'kwapjv', 5
	UNION ALL
	SELECT 'chpsde', 30
	UNION ALL
	SELECT 'fwptfk', 96
	UNION ALL
	SELECT 'znjsiu', 228
	UNION ALL
	SELECT 'etamhm', 112
	UNION ALL
	SELECT 'tjzuhv', 219
	UNION ALL
	SELECT 'npzgdg', 107
	UNION ALL
	SELECT 'ripcml', 99
	UNION ALL
	SELECT 'rggrtk', 8
	UNION ALL
	SELECT 'momslh', 204
	UNION ALL
	SELECT 'fotckt', 142
	UNION ALL
	SELECT 'rqurdw', 155
	UNION ALL
	SELECT 'veonhb', 64
	UNION ALL
	SELECT 'rkhrjc', 146
	UNION ALL
	SELECT 'jfvxri', 241
	UNION ALL
	SELECT 'qhsdyq', 208
	UNION ALL
	SELECT 'uwndum', 15
	UNION ALL
	SELECT 'aokicj', 60
	UNION ALL
	SELECT 'iytjto', 26
	UNION ALL
	SELECT 'ovyzic', 222
	UNION ALL
	SELECT 'lpixws', 92
	UNION ALL
	SELECT 'bnhuod', 223
	UNION ALL
	SELECT 'emszvg', 248
	UNION ALL
	SELECT 'udznjm', 68
	UNION ALL
	SELECT 'kdxjql', 45
	UNION ALL
	SELECT 'biggy', 501
	UNION ALL
	SELECT 'cust1', 278
	union all
	SELECT 'a', 250
	UNION ALL
	SELECT 'b', 249
	UNION ALL
	SELECT 'c', 249
	UNION ALL
	SELECT 'd', 2
	UNION ALL
	SELECT 'e', 125
	UNION ALL
	SELECT 'f', 125
	UNION ALL
	SELECT 'g', 1
	UNION ALL
	SELECT 'h', 75
	UNION ALL
	SELECT 'i', 1
	UNION ALL
	SELECT 'j', 75

--select * from @input 



DECLARE @Concatchar char = ','
	,	@CurrentHistory varchar(max)
	,	@GroupNum int = 0
	,	@LongestPath int
DECLARE @Paths TABLE (
	length int,
	history varchar(max),
	size int
)
DECLARE @R TABLE (
	length int,
	id int,
	size int,
	history varchar(max)
)

RAISERROR('start', 0,0) WITH NOWAIT
/*
;WITH r as (
	SELECT length=1, name, size, history = cast(@Concatchar+name+@Concatchar as varchar(max))
		FROM @input
	UNION ALL
	SELECT length=r.length+1, name= i.name, size=r.size+i.size, history=r.history + i.name+@Concatchar
		FROM r
		JOIN @input i ON r.name < i.name 
			WHERE i.size + r.size <= @Threshold 
)

INSERT INTO @Paths (length, history, size)
	SELECT length, history, size
		FROM r
--3740068 rows

WHILE EXISTS(SELECT * FROM @Paths)
BEGIN
	SELECT TOP(1) @CurrentHistory = history
		FROM @Paths
	ORDER BY size desc, length, history
	
	UPDATE @input 
		SET groupNum = @GroupNum 
			WHERE @CurrentHistory LIKE '%' + @Concatchar + name + @Concatchar + '%'
			
	DELETE p
		FROM @Paths p
		JOIN @input i ON i.groupNum = @GroupNum AND p.history LIKE '%' + @Concatchar + i.name + @Concatchar + '%'
		
	SET @GroupNum = @GroupNum + 1
END
*/

--fill in ID column with unique ints

UPDATE i
	SET i.id = r.id 
	FROM (
		SELECT id=ROW_NUMBER() OVER (ORDER BY size desc), name, size
			FROM @input 
	) r
	JOIN @input i on i.name = r.name and i.size = r.size 
	
--if anything is over the threshold, put each in their own group now
UPDATE i
	SET i.groupNum = r.groupNum 
	FROM (
		SELECT groupNum=ROW_NUMBER() OVER (ORDER BY id), id
			FROM @input 
				WHERE size >= @Threshold 
	) r
	JOIN @input i on i.id = r.id

SELECT @GroupNum=ISNULL(MAX(groupNum),0)+1 FROM @input 

INSERT @R (length, id, size, history)
	SELECT 1, id, size, cast(@Concatchar + CAST(id as varchar) + @Concatchar as varchar(max))
		FROM @input 
			WHERE groupNum IS NULL
SET @LongestPath = 1
			
WHILE (SELECT SUM(size) FROM @input WHERE groupNum IS NULL) > @Threshold
BEGIN

	/*SELECT * FROM @R 
		WHERE length = @LongestPath 	
	*/
	INSERT @R (length, id, size, history)
		SELECT r.length + 1, i.id, r.size + i.size, r.history + CAST(i.id as varchar) + @Concatchar
			FROM @R r
			JOIN @input i ON r.id < i.id 
				WHERE i.groupNum IS NULL 
					AND i.size + r.size <= @Threshold
					
	--if we didn't find any new paths just quit
	IF @LongestPath = (SELECT TOP(1) length FROM @R ORDER BY length DESC)
		BREAK;
			
	SELECT TOP(1) @LongestPath = length 
		FROM @R 
	ORDER BY length DESC
	
	WHILE EXISTS(SELECT * FROM @R WHERE size = @Threshold )
	BEGIN
		SELECT TOP(1) @CurrentHistory = history
			FROM @R
				WHERE size = @Threshold
		ORDER BY length, history
		
		UPDATE @input 
			SET groupNum = @GroupNum 
				WHERE @CurrentHistory LIKE '%' + @Concatchar + CAST(id as varchar) + @Concatchar + '%'
				
		DELETE r
			FROM @R r
			JOIN @input i ON i.groupNum = @GroupNum AND r.history LIKE '%' + @Concatchar + cast(i.id as varchar) + @Concatchar + '%'
			
		RAISERROR('group found', 1, @GroupNum) WITH NOWAIT
		SET @GroupNum = @GroupNum + 1
	END
END

WHILE EXISTS(SELECT * FROM @R)
BEGIN
	SELECT TOP(1) @CurrentHistory = history
		FROM @R
	ORDER BY size desc, length, history

	RAISERROR(@CurrentHistory, 1, 0) WITH NOWAIT
		
	UPDATE @input 
		SET groupNum = @GroupNum 
			WHERE @CurrentHistory LIKE '%' + @Concatchar + cast(id as varchar) + @Concatchar + '%'
			
	DELETE r
		FROM @R r
		JOIN @input i ON i.groupNum = @GroupNum AND r.history LIKE '%' + @Concatchar + cast(i.id as varchar) + @Concatchar + '%'
			
	RAISERROR('group found', 1, @GroupNum) WITH NOWAIT
	SET @GroupNum = @GroupNum + 1
END

RAISERROR('end', 0,0) WITH NOWAIT

select * from @input 
order by groupNum 

select groupNum, SUM(Size)
	from @input 
GROUP BY groupNum 

Tags:

zlangner