基于下表
ID Effort Name
-------------------------
1 1 A
2 1 A
3 8 A
4 10 B
5 4 B
6 1 B
7 10 C
8 3 C
9 30 C
我想检查一个名字的总工作量是否小于40,然后为该名称添加一个努力= 40 - (总努力)的行。新行的ID可以是任何内容。如果总工作量大于40,则将其中一行的数据固定为40。
因此在应用上面的逻辑表之后将是
ID Effort Name
-------------------------
1 1 A
2 1 A
3 8 A
10 30 A
4 10 B
5 4 B
6 1 B
11 25 B
7 10 C
8 3 C
9 27 C
我正在考虑打开游标,保持总工作量的计数器,并基于逻辑插入另一个临时表中的现有行和新行。
我不确定这是否是解决此问题的有效方法。我想知道是否有更好的方法。
答案 0 :(得分:4)
我认为第一部分可以通过这种方式完成:
INSERT INTO tbl(Effort, Name)
SELECT 40 - SUM(Effort), Name
FROM tbl
GROUP BY Name
HAVING SUM(Effort) < 40)
第二部分更难。也许你可以这样做呢?
INSERT INTO tbl(Effort, Name)
SELECT 40 - SUM(Effort), Name
FROM tbl
GROUP BY Name
HAVING SUM(Effort) <> 40)
这样做,而不是更改您的实际数据,如果总费用为&gt;,则为名称添加一行否定号码。 40小时,或正值,如果&lt; 40个小时对于数据完整性而言,这似乎比弄乱原始值更安全。
答案 1 :(得分:2)
在SQL Server 2008
中,可以使用单个MERGE
语句完成此操作:
DECLARE @efforts TABLE (id INT NOT NULL PRIMARY KEY, effort INT NOT NULL, name CHAR(1))
INSERT
INTO @efforts
VALUES (1, 1, 'A'),
(2, 1, 'A'),
(3, 8, 'A'),
(4, 10, 'B'),
(5, 4, 'B'),
(6, 1, 'B'),
(7, 10, 'C'),
(8, 3, 'C'),
(9, 30, 'C'),
(10, 60, 'C')
SELECT *
FROM @efforts
ORDER BY
name, id
;WITH total AS
( SELECT *
FROM @efforts e
UNION ALL
SELECT ROW_NUMBER() OVER(ORDER BY name) +
(
SELECT MAX(id)
FROM @efforts
),
40 - SUM(effort),
name
FROM @efforts
GROUP BY
name
HAVING SUM(effort) < 40
),
source AS
(
SELECT *,
(
SELECT SUM(effort)
FROM total ep
WHERE ep.name = e.name
AND ep.id <= e.id
) AS ce,
COALESCE(
(
SELECT SUM(effort)
FROM total ep
WHERE ep.name = e.name
AND ep.id < e.id
), 0) AS cp
FROM total e
)
MERGE
INTO @efforts e
USING source s
ON e.id = s.id
WHEN MATCHED AND 40 BETWEEN cp AND ce THEN
UPDATE
SET e.effort = s.effort + 40 - ce
WHEN MATCHED AND cp > 40 THEN
DELETE
WHEN NOT MATCHED BY TARGET THEN
INSERT (id, effort, name)
VALUES (id, effort, name);
SELECT *
FROM @efforts
ORDER BY
name, id
在SQL Server 2005
中,您需要两个语句(在一个事务中):
DECLARE @efforts TABLE (id INT NOT NULL PRIMARY KEY, effort INT NOT NULL, name CHAR(1))
INSERT
INTO @efforts
VALUES (1, 1, 'A')
INSERT
INTO @efforts
VALUES (2, 1, 'A')
INSERT
INTO @efforts
VALUES (3, 8, 'A')
INSERT
INTO @efforts
VALUES (4, 10, 'B')
INSERT
INTO @efforts
VALUES (5, 4, 'B')
INSERT
INTO @efforts
VALUES (6, 1, 'B')
INSERT
INTO @efforts
VALUES (7, 10, 'C')
INSERT
INTO @efforts
VALUES (8, 3, 'C')
INSERT
INTO @efforts
VALUES (9, 30, 'C')
INSERT
INTO @efforts
VALUES (10, 60, 'C')
;WITH total AS
(
SELECT *,
COALESCE(
(
SELECT SUM(effort)
FROM @efforts ep
WHERE ep.name = e.name
AND ep.id <= e.id
), 0) AS cp
FROM @efforts e
)
DELETE
FROM total
WHERE cp > 40
INSERT
INTO @efforts
SELECT (
SELECT MAX(id)
FROM @efforts
) +
ROW_NUMBER() OVER (ORDER BY name),
40 - SUM(effort),
name
FROM @efforts
GROUP BY
name
HAVING SUM(effort) < 40
SELECT *
FROM @efforts
ORDER BY
name, id
答案 2 :(得分:0)
这将为您提供需要修改的名称:
SELECT Name, SUM(Effort)
FROM Table
GROUP BY Name
HAVING SUM(Effort) < 40
在临时表中选择此项,为40 - SUM添加一列,然后从中创建一个insert语句。比光标好多了。
答案 3 :(得分:0)
This will do the first part:
Insert Into dbo.Test (Name, Effort)
Select t.Name, 40 - SUM(t.Effort)
From dbo.Test t
Group By t.Name
Having SUM(t.Effort) < 40
这将是第二部分:
Update a
Set a.Effort = a.Effort - b.AmountToDeduct
From dbo.Test a
Join (
Select t.Name, (40 - SUM(t.Effort)) as 'AmountToDeduct'
From dbo.Test t
Group By t.Name
Having SUM(t.Effort) > 40
)b on a.Name = b.Name
Where a.ID = (Select MAX(c.ID)
From dbo.Test c
Where c.Name = a.Name
)