【问题标题】:Update more than one column with single Case When logic - SQL Server使用单个 Case When 逻辑更新多个列 - SQL Server
【发布时间】:2017-08-23 12:46:12
【问题描述】:

我需要使用硬编码数据创建一个带有自定义元素的以下 XML。

表格架构:StudentMark

CREATE TABLE [dbo].[StudentMark]
(
    [StudentMarkId] int IDENTITY(1,1) NOT NULL,
    [StudentId] uniqueidentifier NOT NULL,
    [SubjectId] uniqueidentifier NOT NULL,
    [Score] int NOT NULL,
    [GeneratedOn] datetime2(2) NOT NULL,
    [IsPass] bit NULL,
    [Result] varchar(100) NULL,
    CONSTRAINT [PK_StudentMark] 
       PRIMARY KEY CLUSTERED ([StudentMarkId] ASC)
) ON [PRIMARY]

样本种子数据

INSERT INTO [dbo].[StudentMark] ([StudentId], [SubjectId], [GeneratedOn], [Score])
VALUES ('FC3CB475-B480-4129-9190-6DE880E2D581', '0D72F79E-FB48-4D3E-9906-B78A9D105081', '2017-08-10 10:10:15', 95),
       ('0F4EF48C-93E3-41AA-8295-F6B0E8D8C3A2', '0D72F79E-FB48-4D3E-9906-B78A9D105081', '2017-08-10 10:10:15', 60),
       ('0F4EF48C-93E3-41AA-8295-F6B0E8D8C3A2', 'AB172272-D2E9-49E1-8040-6117BB6743DB', '2017-08-16 09:06:20', 25),
       ('FC3CB475-B480-4129-9190-6DE880E2D581', 'AB172272-D2E9-49E1-8040-6117BB6743DB', '2017-08-16 09:06:20', 45);

需求:我需要根据单一逻辑更新几列[IsPass][Result]

查询 #1

UPDATE SM
SET SM.[Result] = CASE WHEN SM.[Score] >= 75 THEN N'OUTSTANDING'
     WHEN SM.[Score] >= 60 AND [Score] < 75 THEN N'VERY GOOD'
     WHEN SM.[Score] >= 50 AND [Score] < 60 THEN N'GOOD'
     WHEN SM.[Score] >= 40 AND [Score] < 50 THEN N'AVERAGE'
     ELSE N'FAIL' END
FROM [dbo].[StudentMark] SM

查询 #2

UPDATE SM
SET SM.[IsPass] = CASE WHEN SM.[Result] = N'FAIL' THEN 0 ELSE 1 END
FROM [dbo].[StudentMark] SM

我如何将这两个查询合并到一个 UPDATE 查询中而不 复制CASE WHEN

请帮助我。

【问题讨论】:

  • 通常多次存储相同的信息(分数、分数作为文本、通过/失败)是一个坏主意,因为如果您更新数据,那么此时可能会遗漏某些内容,然后数据不匹配.在其他地方(应用层、视图、计算列)仅使用分数 + 逻辑可能值得考虑

标签: sql sql-server sql-update sql-server-2016 case-when


【解决方案1】:

类似这样的:

WITH DataSource ([StudentMarkId], [Result]) AS
(
    SELECT [StudentMarkId]
          ,CASE WHEN SM.[Score] >= 75 THEN N'OUTSTANDING'
             WHEN SM.[Score] >= 60 AND [Score] < 75 THEN N'VERY GOOD'
             WHEN SM.[Score] >= 50 AND [Score] < 60 THEN N'GOOD'
             WHEN SM.[Score] >= 40 AND [Score] < 50 THEN N'AVERAGE'
            ELSE N'FAIL' END
    FROM [dbo].[StudentMark]
)
UPDATE [dbo].[StudentMark]
SET SM.[Result] = DS.[Result]
   ,SM.[IsPass] = CASE WHEN DS.[Result] = N'FAIL' THEN 0 ELSE 1 END
FROM [dbo].[StudentMark] SM
INNER JOIN DataSource DS
    ON SM.[StudentMarkId] = DS.[StudentMarkId];

【讨论】:

    【解决方案2】:

    你可以简单地重复这个逻辑:

    UPDATE SM
        SET SM.[Result] = (CASE WHEN SM.[Score] >= 75 THEN N'OUTSTANDING'
                                WHEN SM.[Score] >= 60 THEN N'VERY GOOD'
                                WHEN SM.[Score] >= 50 THEN N'GOOD'
                                WHEN SM.[Score] >= 40 THEN N'AVERAGE'
                                ELSE N'FAIL'
                           END),
            SM.IsPass = (CASE WHEN SM.Score >= 40 THEN 1 ELSE 0 END)
        FROM [dbo].[StudentMark] SM;
    

    注意:CASE 表达式保证按顺序计算,因此每行只需要比较一次。

    不过,我要指出,更好的选择可能是放弃 IsPass 作为表中的列并使其成为计算列:

    alter table [dbo].[StudentMark]
        add IsPass as (CASE WHEN Result <> N'FAIL' THEN 1 ELSE 0 END);
    

    这可确保值始终正确。事实上,您可以对 Result 执行相同的操作,因此两者都已计算。

    【讨论】:

      【解决方案3】:
      UPDATE SM
      SET SM.[Result] = CASE WHEN SM.[Score] >= 75 THEN N'OUTSTANDING'
           WHEN SM.[Score] >= 60 AND [Score] < 75 THEN N'VERY GOOD'
           WHEN SM.[Score] >= 50 AND [Score] < 60 THEN N'GOOD'
           WHEN SM.[Score] >= 40 AND [Score] < 50 THEN N'AVERAGE'
           ELSE N'FAIL' END, SM.[IsPass] = CASE WHEN SM.[Score] < 40 THEN 0 ELSE 1 END
      FROM [dbo].[StudentMark] SM
      

      您可以通过添加,(comma)轻松做到这一点

      【讨论】:

      • 我已经尝试过这个逻辑,但它总是将[IsPass] 更新为1。这就是我发布这个问题的原因。
      • 哦!!,您最初是在单个查询中尝试两个事务。您需要使用两个查询。或者您可以像我在编辑的答案中所做的那样稍微更改逻辑。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-22
      • 2014-08-29
      • 2013-01-15
      • 2023-01-09
      • 2011-06-03
      相关资源
      最近更新 更多