当您在 SQL 中处理此类任务时,您需要使用游标方法。
游标让您可以逐行执行操作,而这正是您所需要的,例如:
这是任务
- 选择所有表进入临时表
#TBL_ALL
- 逐行循环,当行总和为 10 时,从临时表中删除并插入到最终临时表中
#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;
存储过程:
http://pastebin.com/EFeZcKXf
结果:
分组表:
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;
P.S.我不是 SQL 专业人士,所以如果我做了一些奇怪的事情,请多多包涵,并记住这是一种性能浪费,也许有人可以使用没有游标的循环, more in this page