如果在使用聚合函数时存在重复项,如何仅更新其中的 1 行

How to update only 1 of the rows if there are duplicates while using aggregate functions

我正在尝试创建一个基于 SUM(TotalTime) 增加午餐时间的存储过程。我的困境在于重复的记录。它增加了两次午餐。请参阅下面的屏幕截图。我希望根据总工作时间,每位员工每天只添加一次午餐。这是我目前所拥有的。

CREATE TABLE TimeCards 
(
    [Counter] [int] IDENTITY(1,1) NOT NULL,
    EmployeeID nvarchar(50),
    Date DateTime,
    Lunch decimal(10,2),
    TotalTime decimal(10,2)
)

INSERT INTO TimeCards (EmployeeID, Date, Lunch, TotalTime)
VALUES ('1001', '2021-02-04 00:00:00.000', Null, 8) 
, ('136', '2021-02-04 00:00:00.000', Null, 4)
, ('136', '2021-02-04 00:00:00.000', Null, 4)
, ('418', '2021-02-04 00:00:00.000', Null, 5)
, ('418', '2021-02-04 00:00:00.000', Null, 5)
, ('511', '2021-02-04 00:00:00.000', Null, 5)
, ('511', '2021-02-04 00:00:00.000', Null, 6)

UPDATE TimeCards 
SET Lunch = CASE 
               WHEN SUMTotalTime BETWEEN 5.501 AND 11 THEN .5
               WHEN SUMTotalTime BETWEEN 11.01 AND 16 THEN 1
               WHEN SUMTotalTime >= 16.01 THEN 1.5
            END 
FROM 
    (SELECT 
         tc.EmployeeID, Date, MAX(totaltime) AS totaltime,  
         SUM(TotalTime) AS SUMTotalTime 
     FROM 
         TimeCards tc
     GROUP BY 
         tc.EmployeeID, Date 
     HAVING 
         SUM(Totaltime) > 5.5) grouped 
 WHERE 
     TimeCards.totaltime = grouped.totaltime 
     AND TimeCards.Date = grouped.Date 
     AND TimeCards.EmployeeID = grouped.EmployeeID 
     AND grouped.SUMTotalTime > 5.5 
     AND Lunch IS NULL

看来您想要的是可更新的 CTE:

WITH CTE AS(
    SELECT TC.EmployeeID,
           TC.[Date], --If this is a date, why is it a datetime?
           TC.Lunch,
           TC.TotalTime,
           SUM(TC.TotalTime) OVER (PARTITION BY TC.EmployeeID, TC.Date) AS TotalHours,
           ROW_NUMBER() OVER (PARTITION BY TC.EmployeeID, TC.Date ORDER BY Counter DESC) AS RN
    FROM dbo.TimeCards TC)
UPDATE CTE
SET Lunch = CASE WHEN TotalHours BETWEEN 5.501 AND 11 THEN .5
                 WHEN TotalHours BETWEEN 11.01 AND 16 THEN 1
                 WHEN TotalHours >= 16.01 THEN 1.5
            END 
WHERE TotalHours > 5.5
  AND Lunch IS NULL
  AND RN = 1;

db<>fiddle

如果某人已经可以添加午餐,并且它可能不是“最后”行,您可以检查“最多午餐”是否为 NULL

WITH CTE AS(
    SELECT TC.EmployeeID,
           TC.[Date], --If this is a date, why is it a datetime?
           TC.Lunch,
           TC.TotalTime,
           SUM(TC.TotalTime) OVER (PARTITION BY TC.EmployeeID, TC.Date) AS TotalHours,
           MAX(Lunch) OVER (PARTITION BY TC.EmployeeID, TC.Date) AS MaxLunch,
           ROW_NUMBER() OVER (PARTITION BY TC.EmployeeID, TC.Date ORDER BY Counter DESC) AS RN
    FROM dbo.TimeCards TC)
UPDATE CTE
SET Lunch = CASE WHEN TotalHours BETWEEN 5.501 AND 11 THEN .5
                 WHEN TotalHours BETWEEN 11.01 AND 16 THEN 1
                 WHEN TotalHours >= 16.01 THEN 1.5
            END 
WHERE TotalHours > 5.5
  AND MaxLunch IS NULL
  AND RN = 1;

db<>fiddle

只是为了完整性,因为我认为我激怒了 Larnu,他似乎不想接受你的评论 ;)

只需更改他答案的 ROW_NUMBER() 部分,使其在检查 [=14= 之前根据 Lunch 值是否为 NULL 选择“要更新的行” ] 值...

WITH CTE AS(
    SELECT TC.EmployeeID,
           TC.[Date], --If this is a date, why is it a datetime?
           TC.Lunch,
           TC.TotalTime,
           SUM(TC.TotalTime)
             OVER (PARTITION BY TC.EmployeeID, TC.Date
                  )
                    AS TotalHours,
           ROW_NUMBER()
             OVER (PARTITION BY TC.EmployeeID, TC.Date
                   ORDER BY CASE WHEN Lunch IS NULL THEN 1 ELSE 0 END, Counter DESC
                  )
                    AS RN
    FROM dbo.TimeCards TC)
UPDATE CTE
SET Lunch = CASE WHEN TotalHours BETWEEN 5.501 AND 11 THEN .5
                 WHEN TotalHours BETWEEN 11.01 AND 16 THEN 1
                 WHEN TotalHours >= 16.01 THEN 1.5
            END 
WHERE TotalHours > 5.5
  AND RN = 1;

此外,我强烈建议不要对小数、日期等连续值使用 BETWEEN。在您的场景中,CASE 将匹配第一个 true 条件,因此您可以这样做。 ..

SET Lunch = CASE WHEN TotalHours > 16.0 THEN 1.5
                 WHEN TotalHours > 11.0 THEN 1
                 WHEN TotalHours >  5.5 THEN .5

即使你确实需要范围,我也推荐这个...

SET Lunch = CASE WHEN TotalHours >  5.5 AND TotalHours <= 11.0 THEN .5
                 WHEN TotalHours > 11.0 AND TotalHours <= 16.0 THEN 1
                 WHEN TotalHours > 16.0                        THEN 1.5

它稍长一些,但更明确、更健壮。