CASE (Transact-SQL)

适用于:SQL ServerAzure SQL 数据库Azure SQL 托管实例Azure Synapse Analytics分析平台系统(PDW)Microsoft Fabric 中的 SQL 分析终结点Microsoft Fabric 中的仓库Microsoft Fabric 预览版中的 SQL 数据库

计算条件列表,并返回多个可能的结果表达式之一。

CASE 表达式有两种格式:

  • 简单CASE表达式将表达式与一组简单表达式进行比较,以确定结果。

  • 搜索CASE的表达式计算一组布尔表达式以确定结果。

这两种格式都支持可选 ELSE 参数。

CASE 可用于允许使用有效表达式的任意语句或子句。 例如,可以在CASE语句(如SELECTDELETEUPDATESET)和子句(如<select_list>INWHEREORDER BY和)中使用HAVING

Transact-SQL 语法约定

Syntax

SQL Server、Azure SQL 数据库和 Azure Synapse Analytics 的语法。

-- Simple CASE expression:
CASE input_expression
     WHEN when_expression THEN result_expression [ ...n ]
     [ ELSE else_result_expression ]
END

-- Searched CASE expression:
CASE
     WHEN Boolean_expression THEN result_expression [ ...n ]
     [ ELSE else_result_expression ]
END

适用于并行数据仓库的语法。

CASE
     WHEN when_expression THEN result_expression [ ...n ]
     [ ELSE else_result_expression ]
END

Arguments

input_expression

使用简单 CASE 格式时计算的表达式。 input_expression 是任何有效的表达式

WHEN when_expression

使用简单 格式时要与 input_expression 进行比较的简单表达式。 when_expression 是任何有效的表达式 。 input_expression 及每个 when_expression 的数据类型必须相同或必须是隐式转换的数据类型 。

然后 result_expression

当input_expression等于when_expression计算结果TRUE计算结果为Boolean_expressionTRUE返回的表达式。 result expression 是任何有效的表达式

ELSE else_result_expression

如果没有比较运算的计算结果为 TRUE,则返回的表达式 。 如果省略此参数,并且没有比较运算的计算结果 TRUECASENULL返回。 else_result_expression 是任何有效的表达式 。 else_result_expression 及任何 result_expression 的数据类型必须相同或必须是隐式转换的数据类型 。

WHEN Boolean_expression

使用 CASE 搜索格式时所计算的布尔表达式。 Boolean_expression 是任何有效的布尔表达式 。

返回类型

从 result_expressions 和可选 else_result_expression 的类型集中返回优先级最高的类型 。 有关详细信息,请参阅 数据类型优先级

返回值

简单 CASE 表达式

简单 CASE 表达式通过将第一个表达式与每个 WHEN 子句中的表达式进行比较来运行等效性。 如果这些表达式等效,则返回子句中的 THEN 表达式。

  • 仅用于等同性检查。

  • 按指定的顺序计算每个子句input_expression = when_expression WHEN

  • 返回计算结果TRUE为的第一个input_expression = when_expression的result_expression

  • 如果未 input_expression = when_expression 计算结果 TRUE,则 SQL Server 数据库引擎返回 else_result_expression (如果指定了 ELSE 子句)或 NULLELSE 指定任何子句的值。

搜索的 CASE 表达式

  • 按指定的顺序计算每个WHEN子句Boolean_expression

  • 返回计算结果TRUE为的第一个Boolean_expression的result_expression

  • 如果未 Boolean_expression 计算结果 TRUE,则数据库引擎返回 else_result_expression (如果指定了子 ELSE 句)或 NULLELSE 指定子句的值。

Remarks

SQL Server 仅允许在 CASE 表达式中嵌套 10 个级别。

CASE 表达式不能用于控制 Transact-SQL 语句、语句块、用户定义函数以及存储过程的执行流。 有关流控制方法的列表,请参阅 Flow 控制

CASE 表达式按顺序评估其条件并在满足第一个条件时停止。 在某些情况下,将会先计算表达式,然后 CASE 表达式会将表达式的结果作为其输入接收。 在计算这些表达式时可能会出现错误。 首先计算出现在表达式的参数CASE中的WHEN聚合表达式,然后提供给CASE表达式。 例如,以下查询在生成聚合值 MAX 时生成除以零错误。 此步骤在计算 CASE 表达式之前发生。

WITH Data (value)
AS (
    SELECT 0
    UNION ALL
    SELECT 1
    )
SELECT CASE
        WHEN MIN(value) <= 0 THEN 0
        WHEN MAX(1 / value) >= 100 THEN 1
        END
FROM Data;
GO

只应取决于对标量表达式(包括返回标量的非相关子查询)的条件的计算 WHEN 顺序,而不依赖于聚合表达式。

还必须确保至少一个或ELSE子句中的THEN表达式不是NULL常量。 虽然 NULL 可以从多个结果表达式返回,但并非所有结果表达式都可以显式为 NULL 常量。 如果所有结果表达式都使用 NULL 常量,则返回错误 8133。

Examples

本文中的代码示例使用 AdventureWorks2022AdventureWorksDW2022 示例数据库,可以从 Microsoft SQL Server 示例和社区项目 主页下载该数据库。

A. 使用带有 CASE 简单表达式的 SELECT 语句

SELECT 语句中,CASE 简单表达式只能用于等同性检查,而不进行其他比较。 下面的示例使用 CASE 表达式更改产品系列类别的显示,以使这些类别更易于理解。

USE AdventureWorks2022;
GO

SELECT ProductNumber,
    Category = CASE ProductLine
        WHEN 'R' THEN 'Road'
        WHEN 'M' THEN 'Mountain'
        WHEN 'T' THEN 'Touring'
        WHEN 'S' THEN 'Other sale items'
        ELSE 'Not for sale'
        END,
    Name
FROM Production.Product
ORDER BY ProductNumber;
GO

B. 使用带有 CASE 搜索表达式的 SELECT 语句

SELECT 语句中,CASE 搜索表达式允许根据比较值替换结果集中的值。 下面的示例根据产品的价格范围将标价显示为文本注释。

USE AdventureWorks2022;
GO

SELECT ProductNumber,
    Name,
    "Price Range" = CASE
        WHEN ListPrice = 0 THEN 'Mfg item - not for resale'
        WHEN ListPrice < 50 THEN 'Under $50'
        WHEN ListPrice >= 50 AND ListPrice < 250 THEN 'Under $250'
        WHEN ListPrice >= 250 AND ListPrice < 1000 THEN 'Under $1000'
        ELSE 'Over $1000'
        END
FROM Production.Product
ORDER BY ProductNumber;
GO

C. 在 ORDER BY 子句中使用 CASE

以下示例使用 CASE 子句中的 ORDER BY 表达式根据给定列值确定行的排序顺序。 在第一个示例中,会计算 SalariedFlag 表中 HumanResources.Employee 列的值。 SalariedFlag 设置为 1 的员工将按 BusinessEntityID 以降序顺序返回。 SalariedFlag 设置为 0 的员工将按 BusinessEntityID 以升序顺序返回。

SELECT BusinessEntityID,
    SalariedFlag
FROM HumanResources.Employee
ORDER BY CASE SalariedFlag
        WHEN 1 THEN BusinessEntityID
        END DESC,
    CASE
        WHEN SalariedFlag = 0 THEN BusinessEntityID
        END;
GO

在第二个示例中,当 TerritoryName 列等于“United States”时,结果集会按 CountryRegionName 列排序,对于所有其他行则按 CountryRegionName 排序。

SELECT BusinessEntityID,
    LastName,
    TerritoryName,
    CountryRegionName
FROM Sales.vSalesPerson
WHERE TerritoryName IS NOT NULL
ORDER BY CASE CountryRegionName
        WHEN 'United States' THEN TerritoryName
        ELSE CountryRegionName
        END;
GO

D. 在 UPDATE 语句中使用 CASE

以下示例使用CASE语句中的UPDATE表达式来确定为设置为 0 的员工SalariedFlagVacationHours设置的值。 如果 VacationHours 减去 10 小时后会得到一个负值,则 VacationHours 将增加 40 小时;否则 VacationHours 将增加 20 小时。 子 OUTPUT 句用于显示假期值前后。

USE AdventureWorks2022;
GO

UPDATE HumanResources.Employee
SET VacationHours = (
        CASE
            WHEN ((VacationHours - 10.00) < 0) THEN VacationHours + 40
            ELSE (VacationHours + 20.00)
            END
        )
OUTPUT Deleted.BusinessEntityID,
    Deleted.VacationHours AS BeforeValue,
    Inserted.VacationHours AS AfterValue
WHERE SalariedFlag = 0;
GO

E. 在 SET 语句中使用 CASE

以下示例在CASE表值函数dbo.GetContactInfo的语句中使用SET表达式。 在 AdventureWorks2022 数据库中,与人员有关的所有数据都存储在 Person.Person 表中。 例如,该人员可能是员工、供应商代表或客户。 该函数返回给定BusinessEntityID的姓名(FirstName)和姓氏(LastName)以及该人员的联系人类型。 CASE语句中的SET表达式根据列在EmployeeVendorCustomer或表中是否存在BusinessEntityID来确定要为列ContactType显示的值。

USE AdventureWorks2022;
GO

CREATE FUNCTION dbo.GetContactInformation (
    @BusinessEntityID INT
)
RETURNS
    @retContactInformation TABLE (
        BusinessEntityID INT NOT NULL,
        FirstName NVARCHAR (50) NULL,
        LastName NVARCHAR (50) NULL,
        ContactType NVARCHAR (50) NULL,
        PRIMARY KEY CLUSTERED (BusinessEntityID ASC))
AS
-- Returns the first name, last name and contact type for the specified contact.
BEGIN
    DECLARE @FirstName NVARCHAR(50),
        @LastName NVARCHAR(50),
        @ContactType NVARCHAR(50);

    -- Get common contact information
    SELECT @BusinessEntityID = BusinessEntityID,
        @FirstName = FirstName,
        @LastName = LastName
    FROM Person.Person
    WHERE BusinessEntityID = @BusinessEntityID;

    SET @ContactType = CASE
            -- Check for employee
            WHEN EXISTS (
                    SELECT *
                    FROM HumanResources.Employee AS e
                    WHERE e.BusinessEntityID = @BusinessEntityID
                    )
                THEN 'Employee'
            -- Check for vendor
            WHEN EXISTS (
                    SELECT *
                    FROM Person.BusinessEntityContact AS bec
                    WHERE bec.BusinessEntityID = @BusinessEntityID
                    )
                THEN 'Vendor'
            -- Check for store
            WHEN EXISTS (
                    SELECT *
                    FROM Purchasing.Vendor AS v
                    WHERE v.BusinessEntityID = @BusinessEntityID
                    )
                THEN 'Store Contact'
            -- Check for individual consumer
            WHEN EXISTS (
                    SELECT *
                    FROM Sales.Customer AS c
                    WHERE c.PersonID = @BusinessEntityID
                    )
                THEN 'Consumer'
            END;

    -- Return the information to the caller
    IF @BusinessEntityID IS NOT NULL
        BEGIN
            INSERT @retContactInformation
            SELECT @BusinessEntityID,
                   @FirstName,
                   @LastName,
                   @ContactType;
        END
    RETURN;
END
GO

SELECT BusinessEntityID,
    FirstName,
    LastName,
    ContactType
FROM dbo.GetContactInformation(2200);
GO

SELECT BusinessEntityID,
    FirstName,
    LastName,
    ContactType
FROM dbo.GetContactInformation(5);
GO

F. 在 HAVING 子句中使用 CASE

以下示例使用 CASE 子句中的 HAVING 表达式来限制语句返回的 SELECT 行。 该语句将返回 HumanResources.Employee 表中针对每个职位的每小时薪金。 该 HAVING 条款将职务限制为工资最高的雇员持有的职务超过40美元,或者最高工资率超过15美元的非付费雇员。

USE AdventureWorks2022;
GO

SELECT JobTitle,
    MAX(ph1.Rate) AS MaximumRate
FROM HumanResources.Employee AS e
INNER JOIN HumanResources.EmployeePayHistory AS ph1
    ON e.BusinessEntityID = ph1.BusinessEntityID
GROUP BY JobTitle
HAVING (
    MAX(CASE
            WHEN SalariedFlag = 1 THEN ph1.Rate
            ELSE NULL
        END) > 40.00
    OR MAX(CASE
            WHEN SalariedFlag = 0 THEN ph1.Rate
            ELSE NULL
        END) > 15.00
)
ORDER BY MaximumRate DESC;
GO

G. 使用嵌套 CASE 表达式对结果进行分类

下面的示例使用嵌套 CASE 表达式对基于 ListPrice的产品进行分类。 如果产品列表价格超过 1,000 美元,则将其视为 High-end。 其余产品在嵌套 表达式中分类,并基于 < a0/>

USE AdventureWorks2022;
GO

SELECT 
    ProductNumber,
    Name,
    ListPrice,
    PriceCategory = 
        CASE 
            WHEN ListPrice > 1000 THEN 'High-end'
            ELSE 
                CASE ProductLine
                    WHEN 'R' THEN
                        CASE 
                            WHEN ListPrice > 500 THEN 'Premium Road'
                            ELSE 'Standard Road'
                        END
                    WHEN 'M' THEN
                        CASE 
                            WHEN ListPrice > 500 THEN 'Premium Mountain'
                            ELSE 'Standard Mountain'
                        END
                    WHEN 'T' THEN 'Touring'
                    ELSE 'Other'
                END
        END
FROM Production.Product
ORDER BY ListPrice DESC;

示例:Azure Synapse Analytics 和 Analytics Platform System (PDW)

H. 将 SELECT 语句和 CASE 表达式结合使用

SELECT在语句中,表达式CASE允许根据比较值在结果集中替换值。 下面的示例使用 CASE 表达式更改产品系列类别的显示,以使这些类别更易于理解。 当值不存在时,将显示文本 Not for sale

SELECT ProductAlternateKey,
    Category = CASE ProductLine
        WHEN 'R' THEN 'Road'
        WHEN 'M' THEN 'Mountain'
        WHEN 'T' THEN 'Touring'
        WHEN 'S' THEN 'Other sale items'
        ELSE 'Not for sale'
        END,
    EnglishProductName
FROM dbo.DimProduct
ORDER BY ProductKey;
GO

I. 在 UPDATE 语句中使用 CASE

以下示例使用CASE语句中的UPDATE表达式来确定为设置为 0 的员工SalariedFlagVacationHours设置的值。 如果 VacationHours 减去 10 小时后会得到一个负值,则 VacationHours 将增加 40 小时;否则 VacationHours 将增加 20 小时。

UPDATE dbo.DimEmployee
SET VacationHours = (
        CASE
            WHEN ((VacationHours - 10.00) < 0) THEN VacationHours + 40
            ELSE (VacationHours + 20.00)
            END
        )
WHERE SalariedFlag = 0;
GO