有包含
的表格ID Qty
----------
1 2
2 4
3 1
4 5
现在,如果我必须选择数量总和等于10的行, 我怎么能这样做?
喜欢2 + 4 + 1 = 7 但如果我加5然后12
所以忽略2,然后 4 + 1 + 5 = 10
我怎样才能实现这个目标?
编辑:
我想要任何可能的组合,总结得到价值。 假设7然后任何总计最多7行 如果8,那么任何行总计达到8
希望行/行的组合等于给定值。
答案 0 :(得分:5)
您要解决的问题称为subset sum问题。不幸的是,它是NP-complete。
这意味着,无论您使用SQL还是其他任何语言来解决问题,您都只能解决问题的非常小的实例,即表中只有少数条目的实例。否则,运行时将变得过多,因为它随着表中的行数呈指数增长。这样做的原因是找到解决方案基本上没有比尝试所有可能的组合更好的方法。
如果可以接受近似解,那么有一个多项式时间算法,在维基百科页面上有描述。
答案 1 :(得分:2)
如果你想要它总是三个加到10的数字,那么这个
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
如果你想要2或4或5个数字,那么你无法在SQL中真正做到这一点
编辑:
答案 2 :(得分:1)
当您在SQL中处理此类任务时,您需要使用游标方法。
游标允许您逐行执行操作,这就是您所需要的,例如:
这是任务
#TBL_ALL
#TBL_FINAL
#TBL_ALL
总和不能执行一个例子:
测试表:
/****** 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
测试数据:
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;
商店程序:
结果:
分组表:
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
未使用的数字:
id qty
1 2
8 2
3 1
13 1
6 11
希望有所帮助
SP,适用于无法访问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;
PS 我不是SQL专业人员,如果我做了一些奇怪的事情,请不要理我这么做,并记住这是一个性能浪费,也许有人可以使用没有游标的循环, more in this page
答案 3 :(得分:0)
如果你总是添加3个数字,就像gbn所说的那样,如果没有那么你必须检查ur行的每个组合,这给你的number_of_rows功率组合提供了u 2,我不知道在一个查询中如何在sql上完成。如果你在sql中使用循环确定它可能,但你应该找到一些好的算法来完成这个任务。