关键字 CASE 附近的语法不正确(SQL 函数)

Incorrect syntax near the keyword CASE (SQL Function)

我正在将一个大型 Access 应用程序移植到 SQL 服务器,在此过程中,我一直致力于将所有 VBA 函数重写为 SQL 函数。我无法执行以下创建函数语句。貌似我使用 CASE 不正确?感谢任何帮助,这是我第一次尝试 SQL 函数。

CREATE FUNCTION dbo.[NAL_PolicyCode]
(
@gm1 float, 
@gm2 float = 1,
@gm3 float = 1,
@typ varchar(10) = ''
)
RETURNS VarChar(10)
AS BEGIN
DECLARE @gm float;
SET @gm = @gm1;
SET @gm = CASE 
    WHEN @gm2 < @gm THEN @gm2 END 
SET @gm = CASE
    WHEN @gm3 < @gm THEN @gm3 END 

SET @gm = Round(@gm, 3);

IF(@typ = 'MS')
    BEGIN
        (CASE WHEN @gm < 0.03 THEN 
                    PolicyCode = '1B'
             WHEN @gm < 0.05 THEN
                    PolicyCode = '1C'
             WHEN @gm < 0.08 THEN
                    PolicyCode = '1D'
             WHEN @gm < 0.12 THEN
                    PolicyCode = '1E'
             WHEN @gm < 0.16 THEN
                    PolicyCode = '1F'
             WHEN @gm < 0.24 THEN
                    PolicyCode = '1G'
             WHEN @gm < 0.29 THEN
                    PolicyCode = '1H'
             WHEN @gm < 0.47 THEN
                    PolicyCode = '1J'
             ELSE
                    PolicyCode = '1K'
                    END) 
    END
ELSE IF(@typ = 'PL')
    BEGIN
        (CASE WHEN @gm < 0.35 THEN
                PolicyCode = "8"
             WHEN @gm < 0.45 THEN
                PolicyCode = "P"
             WHEN @gm < 0.58 THEN
                PolicyCode = "V"
             WHEN @gm < 0.7 THEN
                PolicyCode = "4"
            ELSE
                PolicyCode = "R"
                END) 
    END
ELSE
    BEGIN
        (CASE WHEN @gm < 0.16 THEN
                PolicyCode = "Y"
             WHEN @gm < 0.24 THEN
                PolicyCode = "Z"
             WHEN @gm < 0.29 THEN
                PolicyCode = "X"
            WHEN @gm < 0.36 THEN
                PolicyCode = "9"
             WHEN @gm < 0.41 THEN
                PolicyCode = "J"
             WHEN @gm < 0.47 THEN
                PolicyCode = "N"
             WHEN @gm < 0.55 THEN
                PolicyCode = "D"
             WHEN @gm < 0.63 THEN
                PolicyCode = "S"
             WHEN @gm < 0.75 THEN
                PolicyCode = "T"
            ELSE
                PolicyCode = "U"
                END) 
    END
END;

你有:

IF(@typ = 'MS')
    BEGIN
        (CASE WHEN @gm < 0.03 THEN 
                    PolicyCode = '1B'
         . . .

您正在使用的构造称为 "control flow",因为它处于 T-SQL 块编程级别。在 T-SQL 中没有 case 作为控制流子句。据推测,您打算这样:

IF(@typ = 'MS')
    BEGIN
        SET @PolicyCode = (CASE WHEN @gm < 0.03 THEN PolicyCode = '1B'
                                  . . .

您可能需要这样的东西:

CREATE FUNCTION dbo.[NAL_PolicyCode]
(
@gm1 float, 
@gm2 float = 1,
@gm3 float = 1,
@typ varchar(10) = ''
)
RETURNS VarChar(10)

BEGIN

DECLARE
@GM float,
@PolicyCode varchar(10)


set @GM =(  select min(gm) from (
            select @gm1 AS gm union
            select @gm2 union
            select @gm3) x
            );
SET @gm = Round(@gm, 3);

SET @PolicyCode = (
SELECT 
CASE WHEN @typ = 'MS' THEN 
        (
    CASE     WHEN @gm < 0.03 THEN  '1B'
             WHEN @gm < 0.05 THEN '1C'
             WHEN @gm < 0.08 THEN '1D'
             WHEN @gm < 0.12 THEN '1E'
             WHEN @gm < 0.16 THEN '1F'
             WHEN @gm < 0.24 THEN '1G'
             WHEN @gm < 0.29 THEN '1H'
             WHEN @gm < 0.47 THEN '1J'
             ELSE'1K' END
        )
WHEN @typ = 'PL' THEN 
        (
    CASE     WHEN @gm < 0.35 THEN '8'
             WHEN @gm < 0.45 THEN 'P'
             WHEN @gm < 0.58 THEN 'V'
             WHEN @gm < 0.7 THEN '4'
            ELSE 'R'   END 
        )
ELSE 
        (
CASE        WHEN @gm < 0.16 THEN 'Y'
             WHEN @gm < 0.24 THEN 'Z'
             WHEN @gm < 0.29 THEN 'X'
            WHEN @gm < 0.36 THEN '9'
             WHEN @gm < 0.41 THEN 'J'
             WHEN @gm < 0.47 THEN 'N'
             WHEN @gm < 0.55 THEN 'D'
             WHEN @gm < 0.63 THEN 'S'
             WHEN @gm < 0.75 THEN 'T'
            ELSE 'U'
                END
        )
END
)

RETURN @PolicyCode;

END

停止。首先,不要使用双引号作为字符串分隔符。您混合使用了单引号和双引号 - 这是一个 巨大的 问题。事实上,我建议你完全停下来,花一些时间学习 tsql 和良好的编程习惯。

停止。因为将代码从一个平台直接移植到另一个平台通常会出现细微错误(很难find/fix)和性能不佳。另外,代码往往会很难understand/use/debug。对于这个特定问题,将这些范围放在 table 中并从中加入 to/select 可能更有意义。我猜想人们可能已经在 Access 中采用了这种方式——这不会激发人们对架构或代码设计的信心。使用 table,您还可以(并且应该)创建适当的约束以防止无效数据渗入您的数据库。一般来说,充当查找 table 的标量值函数的性能会很差。

为了帮助说明这个概念,下面使用 table 变量来存储您的逻辑引用的值。请注意加入最后一个 select 语句所需的神秘逻辑。我会回到那个。

set nocount on;
declare @PolicyCodes table (Typ varchar(2) not null, PolicyCode varchar(2) not null, 
    MinVal decimal(2,2) not null, MaxVal decimal(2,2) not null);

insert @PolicyCodes (Typ, PolicyCode, MinVal, MaxVal)
values 
    ('MS', '1B', 0,    0.02),  ('MS', '1C', 0.03, 0.04), 
    ('MS', '1D', 0.05, 0.07),  ('MS', '1E', 0.08, 0.11),
    ('MS', '1K', 0.12, 0.99),
    ('PL', '8',  0,    0.34),  ('PL', 'P',  0.35, 0.44),
    ('PL', 'R',  0.45, 0.99),
    ('??', 'Y',  0,    0.15),  ('??', 'Z',  0.16, 0.23),
    ('??', 'Z',  0.24, 0.99);
--select * from @PolicyCodes order by Typ, MinVal, MaxVal;

with cte as (
select * from (values 
   (1, 'MS', 0.20, 0.04, 0.99), --1C
   (2, 'MS', 0.99, 0.70, 0.11), --1E
   (3, 'MS', 0.05, 0.06, 0.07), --1D
   (4, '??', 0.16, 0.17, 0.00)  --Y
) as TestVals (ID, Typ, gm1, gm2, gm3)
)
select cte.*, PC.* 
from cte inner join @PolicyCodes as PC
on cte.Typ = PC.Typ
and case
   when cte.gm1 < cte.gm2 and cte.gm1 < cte.gm3 then cte.gm1 
   when cte.gm2 < cte.gm3 then cte.gm2
   else cte.gm3 end between PC.MinVal and PC.MaxVal
order by cte.ID;

首先,请注意这只是您逻辑的一部分。缩小规模以进行讨论和调查;在你尝试整个事情之前先咬一口。如果我在尝试消化您的代码时出现拼写错误或错误,您将需要调整此逻辑。

其次,请注意我没有使用浮点数或您的舍入逻辑。这对我来说似乎很可疑。也许这在 Access 中有意义——我对它没有直接的了解。但四舍五入意味着通用汽车的价值有一个更受限制的领域。不要传播错误的决定。是的 - 这确实增加了迁移的工作量,但最终会更好。

当您看到变量或列的名称具有重复模式时,这通常表示存在架构问题。当 "setting" 某些东西时,选择三个值中的哪一个用于查找可能没问题 - 但最有可能在将行保存到数据库中之前完成。 "GM" 不是一个特别好命名的变量,因此它没有提供任何关于它的含义或使用方式的线索。也许有一个 "primary" 代码用于建立关系和其他由于某些未定义原因需要保留的辅助代码。如果不了解系统,那是不可能知道的。

这应该会让您了解如何做事。但这只是概念的演示。