Reputation: 113
There is table contain
ID Qty
----------
1 2
2 4
3 1
4 5
Now if i had to choose rows where sum of Qty equals to 10, How can i do this ?
like 2+4+1 = 7 but if i add 5 then 12
so ignore 2, then 4+1+5 = 10
How can i achieve this ?
Edit:
I want any possible combination which sums up to gives value. suppose 7 then any rows which sums up to 7 like wise if 8 then any rows sums up to 8
want row/rows having combination equals to given value.
Upvotes: 9
Views: 12028
Reputation: 75103
When you are dealing with such task in SQL you need to go to the cursors approach.
Cursors let you perform row by row operations and that's what you need, for example:
This are the tasks
#TBL_ALL
#TBL_FINAL
#TBL_ALL
sum cant be performedAn example:
Test table:
/****** Object: Table [dbo].[tblExample] Script Date: 06/09/2011 11:25:27 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[tblExample](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Qty] [int] NOT NULL,
CONSTRAINT [PK_tblExample] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Test Data:
INSERT INTO tblExample SELECT 2;
INSERT INTO tblExample SELECT 4;
INSERT INTO tblExample SELECT 1;
INSERT INTO tblExample SELECT 5;
INSERT INTO tblExample SELECT 5;
INSERT INTO tblExample SELECT 11;
INSERT INTO tblExample SELECT 1;
INSERT INTO tblExample SELECT 2;
INSERT INTO tblExample SELECT 3;
INSERT INTO tblExample SELECT 4;
INSERT INTO tblExample SELECT 7;
INSERT INTO tblExample SELECT 9;
INSERT INTO tblExample SELECT 1;
INSERT INTO tblExample SELECT 2;
Store procedure:
Results:
Grouped table:
ids qty group
12 9 1
7 1 1
11 7 2
9 3 2
4 5 3
5 5 3
2 4 4
10 4 4
14 2 4
Numbers not used:
id qty
1 2
8 2
3 1
13 1
6 11
Hope it helps
SP for those who cant access PasteBin
CREATE PROCEDURE getGroups
(
@groupByQty int, -- grouping number
@numberRuns int -- how many loops
-- usage: getGroups 10, 10
)
AS
SET NOCOUNT ON;
-- declare all variables
DECLARE @rowId int,
@rowQty int,
@rowTotal int,
@groupId int,
@totalRuns int,
@continue bit
-- set up our final temporary table
CREATE TABLE #TBL_COUNT
(
ids NVARCHAR(4000),
qty int,
[group] int
)
-- initializate variable
SET @groupId = 1;
SET @continue = 1;
SET @totalRuns = 0;
SELECT Id, Qty INTO #TBL_ALL FROM tblExample ORDER BY Qty DESC;
WHILE @totalRuns <= @numberRuns
BEGIN
-- declare the cursor
DECLARE Product CURSOR FOR SELECT Id, Qty FROM #TBL_ALL ORDER BY Qty DESC;
OPEN Product;
FETCH Product INTO @rowId, @rowQty;
PRINT ' ';
PRINT '### Run: ' + CAST(@totalRuns AS nvarchar(10)) + ' #################################################################';
PRINT 'Grouping Table by ' + CAST(@groupByQty AS nvarchar(10)) + ' | group id = ' + CAST(@groupId AS nvarchar(10));
-- Retrieve and process the first row
SELECT Top 1 @rowId = Id, @rowQty = Qty FROM #TBL_ALL ORDER BY Qty DESC;
PRINT 'First Row: id = ' + CAST(@rowId AS nvarchar(10)) + ' | qty = ' + CAST(@rowQty AS nvarchar(10));
-- sum it up and see if we have @groupByQty
SELECT @rowTotal = ISNULL(SUM(qty),0) FROM #TBL_COUNT WHERE [group] = @groupId;
PRINT 'Current sum in #TBL_COUNT: @groupId = '+ CAST(@groupId AS nvarchar(10)) +' | @rowTotal = ' + CAST(@rowTotal AS nvarchar(10)) + ' | (@rowTotal + @rowQty) = ' + CAST((@rowTotal + @rowQty) AS nvarchar(10));
IF @rowQty > @groupByQty
BEGIN
PRINT ' x First row has an unused number';
END
ELSE
BEGIN
-- handle result
IF (@rowTotal + @rowQty) = @groupByQty
BEGIN
PRINT '+++ Current sum is ' + CAST(@groupByQty AS nvarchar(10)) + ' +++';
-- save number
INSERT INTO #TBL_COUNT SELECT @rowId, @rowQty, @groupId;
PRINT '### Inserted final # into #TBL_COUNT : id = ' + CAST(@rowId AS nvarchar(10)) + ' | qty = ' + CAST(@rowQty AS nvarchar(10)) + ' | group = ' + CAST(@groupId AS nvarchar(10));
-- remove from table as we use it already
DELETE FROM #TBL_ALL WHERE Id = @rowId;
-- we got 10, let's change our Groupping
SET @groupId = (@groupId + 1);
PRINT 'New group id: ' + CAST(@groupId AS nvarchar(10));
END
ELSE
BEGIN
IF (@rowTotal + @rowQty) < @groupByQty
BEGIN
PRINT '### Inserted into #TBL_COUNT : id = ' + CAST(@rowId AS nvarchar(10)) + ' | qty = ' + CAST(@rowQty AS nvarchar(10)) + ' | group = ' + CAST(@groupId AS nvarchar(10));
-- save number
INSERT INTO #TBL_COUNT SELECT @rowId, @rowQty, @groupId;
-- remove from table as we use it already
DELETE FROM #TBL_ALL WHERE Id = @rowId;
END
ELSE
BEGIN
PRINT ' x Unmatch number, will handle this latter';
END
END
END
-- start the main processing loop
WHILE @@Fetch_Status = 0
BEGIN
FETCH Product INTO @rowId, @rowQty;
PRINT '@@Fetch_Status = ' + CAST(@@Fetch_Status AS nvarchar(100));
IF @@Fetch_Status < 0
BEGIN
BREAK
END
-- we have the values of our row, let's use them
PRINT 'Fetched Row: id = ' + CAST(@rowId AS nvarchar(10)) + ' | qty = ' + CAST(@rowQty AS nvarchar(10));
-- sum it up and see if we have @groupByQty
SELECT @rowTotal = ISNULL(SUM(qty),0) FROM #TBL_COUNT WHERE [group] = @groupId;
PRINT 'Current sum in #TBL_COUNT: @groupId = '+ CAST(@groupId AS nvarchar(10)) +' | @rowTotal = ' + CAST(@rowTotal AS nvarchar(10)) + ' | (@rowTotal + @rowQty) = ' + CAST((@rowTotal + @rowQty) AS nvarchar(10));
-- handle result
IF (@rowTotal + @rowQty) = @groupByQty
BEGIN
PRINT '+++ Current sum is ' + CAST(@groupByQty AS nvarchar(10)) + ' +++';
-- save number
INSERT INTO #TBL_COUNT SELECT @rowId, @rowQty, @groupId;
PRINT '### Inserted final # into #TBL_COUNT : id = ' + CAST(@rowId AS nvarchar(10)) + ' | qty = ' + CAST(@rowQty AS nvarchar(10)) + ' | group = ' + CAST(@groupId AS nvarchar(10));
-- remove from table as we use it already
DELETE FROM #TBL_ALL WHERE Id = @rowId;
-- we got 10, let's change our Groupping
SET @groupId = (@groupId + 1);
PRINT 'New group id: ' + CAST(@groupId AS nvarchar(10));
-- start again
BREAK;
END
ELSE
BEGIN
IF (@rowTotal + @rowQty) < @groupByQty
BEGIN
PRINT '### Inserted into #TBL_COUNT : id = ' + CAST(@rowId AS nvarchar(10)) + ' | qty = ' + CAST(@rowQty AS nvarchar(10)) + ' | group = ' + CAST(@groupId AS nvarchar(10));
-- save number
INSERT INTO #TBL_COUNT SELECT @rowId, @rowQty, @groupId;
-- remove from table as we use it already
DELETE FROM #TBL_ALL WHERE Id = @rowId;
END
ELSE
BEGIN
PRINT ' x Unmatch number, will handle this latter';
END
END
END -- END WHILE @@Fetch_Status = 0
SET @totalRuns = @totalRuns + 1;
-- Close and dealocate
CLOSE Product;
DEALLOCATE Product;
END -- END WHILE totalRuns <= @numberRuns
-- let's sum our last group and remove it if it's not @groupByQty
SELECT @rowTotal = ISNULL(SUM(qty),0) FROM #TBL_COUNT WHERE [group] = @groupId;
IF @rowTotal <> @groupByQty
BEGIN
SET IDENTITY_INSERT #TBL_ALL ON
INSERT INTO #TBL_ALL (Id, Qty) SELECT Ids, Qty FROM #TBL_COUNT WHERE [group] = @groupId;
DELETE FROM #TBL_COUNT WHERE [group] = @groupId;
END
SET NOCOUNT OFF;
-- Show and Delete temp tables
SELECT * FROM #TBL_COUNT;
SELECT * FROM #TBL_ALL;
DROP TABLE #TBL_COUNT;
DROP TABLE #TBL_ALL;
P.S. I'm not a SQL Professional, so bear with me if I did something weird, and keep in mind that this is a performance waste, maybe someone can use the the Loop without Cursors, more in this page
Upvotes: 2
Reputation: 24170
The problem you want to solve is called the subset sum problem. Unfortunately, it is NP-complete.
This means that, whether you use SQL or any other language to solve it, you will only be able to solve very small instances of the problem, i.e. ones with only a few entries in the table. Otherwise, the runtime will become excessive, since it grows exponentially with the number of rows in the table. The reason for this is that there is essentially no better way of finding the solution than to try all possible combinations.
If an approximate solution is acceptable, there is a polynomial time algorithm, which is described on the Wikipedia page.
Upvotes: 6
Reputation: 432451
If you want it to be exactly always three numbers that add to 10, then this
SELECT
*
FROM
MyTable t1
JOIN
MyTable t2 ON t1.ID <> t2.ID
JOIN
MyTable t3 ON t1.ID <> t3.ID AND t2.ID <> t3.ID
WHERE
t1.Qty + t2.Qty + t3.Qty = 10
If you want 2 or 4 or 5 numbers then you can't really do it in SQL
Edit:
Upvotes: 3
Reputation: 3681
If u add always 3 numbers its like gbn said, if not then u have to check every combination of ur rows which gives u 2 to the number_of_rows power cobinations and i dont see how can it be done on sql in one query. If u use loop in sql sure its possible but u should find some good algorithm to finish this task.
Upvotes: -1