Помогите с запросом - PullRequest
1 голос
/ 15 апреля 2010

На основании следующей таблицы

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 - (Общее усилие) для имени. Идентификатор новой строки может быть любым. Если общее усилие больше 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 ]

4 голосов
/ 15 апреля 2010

Я думаю, что первая часть могла бы быть сделана следующим образом:

 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) 

Для этого вместо того, чтобы вносить изменения в ваши фактические данные, добавляется строка с отрицательным числом для Имени, если общее усилие составляет> 40 часов, или положительным значение, если оно <40 часов. Это кажется гораздо безопаснее для вашей целостности данных, чем возиться с исходными значениями. </p>

2 голосов
/ 15 апреля 2010

В 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
0 голосов
/ 15 апреля 2010
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
        )
0 голосов
/ 15 апреля 2010

Это даст вам имена, которые нужно изменить:

SELECT Name, SUM(Effort)
FROM Table
GROUP BY Name
HAVING SUM(Effort) < 40

Выберите это во временной таблице, добавьте столбец для 40 - SUM, затем создайте оператор вставки из этого. Гораздо лучше, чем курсор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...