帮助查询

时间:2010-04-15 18:28:23

标签: sql sql-server sql-server-2005 tsql

基于下表

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

我正在考虑打开游标,保持总工作量的计数器,并基于逻辑插入另一个临时表中的现有行和新行。

我不确定这是否是解决此问题的有效方法。我想知道是否有更好的方法。

4 个答案:

答案 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
        )