长复杂 select 语句中的匿名函数或等效函数

Anonymous function or equivalent within long complex select statement

提问人:dgo 提问时间:11/10/2023 最后编辑:dgo 更新时间:11/11/2023 访问量:76

问:

我们正在将一个 SQL 命令移植到一个新系统,它是一个非常长的 select 语句,中间有一个复杂的字符串解析函数。

下面是一些伪代码:

SELECT 
    812_ACT_RM.R1_ACT,
    812_ACT_RM.R1_FIRST,
    -- ... many more lines
JOIN 
    (SELECT 
         W_N9R.W_N9_IDSF, 
         dbo.fn_parseformattedstring(1, W_N9R.W_N9_IDSF) AS KOX
    )
    -- ... much more

如果你在接近末尾的地方看到,有一个函数。它很复杂 - 解析多行字符串并使用 charindex 等提取文本。由于我不确定的原因,该功能无法移植到新系统 - 需要明确的是 - 这是新平台的要求。就本问题而言,允许使用该命令,但不允许使用外部函数。

是否可以以某种方式在查询中内联此函数?

更新

函数如下:

create function [dbo].fn_parseformattedstring(@format_id int,@str_in as varchar(max))
returns varchar(200)
as
begin
declare @str_out as varchar(max)
declare @idx integer
declare @idy integer

set @str_out = ''
If @format_id = 1 -- Date(s): {October 4, 5, 6 and December 8, 2010}  Time:
begin
        set @idx = CHARINDEX('Date(s):', @str_in)
        If @idx > 0 
        begin
           If len(@str_in) >  @idx + len('Date(s):')
           begin
              set @idx = @idx+ len('Date(s):')
              set @idy = CHARINDEX('Time',@str_in,@idx)  -- added @idx parameter
              If @idy > 0  
                set @str_out = RTRIM(LTRIM(SUBSTRING(@str_in, @idx, @idy-@idx)))
            end
        end
                
        set @idx = CHARINDEX('Date:', @str_in)
        If @idx > 0 
        begin
           If len(@str_in) >  @idx + len('Date:')
           begin
              set @idx = @idx+ len('Date:')
              set @idy = CHARINDEX('Time',@str_in,@idx)
              If @idy > 0  
                set @str_out = RTRIM(LTRIM(SUBSTRING(@str_in, @idx, @idy-@idx)))
            end
        end 
            
        set @idx = CHARINDEX('Dates:', @str_in)
        If @idx > 0 
        begin
           If len(@str_in) >  @idx + len('Dates:')
           begin
              set @idx = @idx+ len('Dates:')
              set @idy = CHARINDEX('Time',@str_in,@idx)
              If @idy > 0  
                set @str_out = RTRIM(LTRIM(SUBSTRING(@str_in, @idx, @idy-@idx)))
            end
        end 
            
end

If @format_id = 2 -- Time: {8:30 AM - 3:30 PM}  Location:
begin

  set @idx = CHARINDEX('Time(s):',@str_in)
  If @idx > 0
  begin
    If len(@str_in) >  @idx + len('Time(s):')
    begin
       set @idx = @idx+len('Time(s):')
       set @idy = CHARINDEX('Location:',@str_in,@idx)
       If @idy > 0  
        set @str_out = RTRIM(LTRIM(SUBSTRING(@str_in, @idx, @idy-@idx)))
     end
   end
   
  set @idx = CHARINDEX('Time:',@str_in)
  If @idx > 0 
  begin
    If len(@str_in) >  @idx + len('Time:')
    begin
       set @idx = @idx+len('Time:')
       set @idy = CHARINDEX('Location',@str_in,@idx)
       If @idy > 0  
        set @str_out = RTRIM(LTRIM(SUBSTRING(@str_in, @idx, @idy-@idx)))
     end
   end
   
  set @idx = CHARINDEX('Times:',@str_in)
  If @idx > 0 
  begin
    If len(@str_in) >  @idx + len('Times:')
    begin
     set @idx = @idx+len('Times:')
     set @idy = CHARINDEX('Location',@str_in,@idx)
     If @idy > 0  
     set @str_out = RTRIM(LTRIM(SUBSTRING(@str_in, @idx, @idy-@idx)))
     end
   end
   
   
end
   return  cast(@str_out as varchar(200))

我敢肯定这不是最好的方法。这是旧代码,我不是 sql 开发人员。我只是想让新系统在请求帮助时工作。

sql sql-server 函数 t-sql

评论

0赞 Eric 11/10/2023
尝试创建它时是否有错误消息?
0赞 dgo 11/10/2023
@Eric - 不 - 我只是不知道正确的方法。有许多不同的变量。是否可以在 select 语句中定义临时变量?
1赞 Eric 11/10/2023
为什么不能在新环境中重新创建函数?
1赞 Xedni 11/10/2023
如果您无法在新环境中重新创建函数,我也看不出您如何内联它。这似乎表明一些特定于平台的语法(如正则表达式)在起作用。任何可以内联的东西,都可以抽象成一个函数。为了回答您关于匿名函数的问题,没有 sql 本身不支持 lambda 函数。不过,您可以使用某种程度来模拟它们。CROSS APPLY
1赞 Dale K 11/10/2023
简单的答案,没有。

答:

-1赞 JohnneyDarkness 11/10/2023 #1

不使用外部应用的紧密但不精确的在线替换(精细解决方案):

SELECT 
    812_ACT_RM.R1_ACT,
    812_ACT_RM.R1_FIRST,
    -- ... many more lines
JOIN 
    (SELECT 
        W_N9R.W_N9_IDSF, 
        RTRIM(
            LTRIM(
                IIF
                (
                    CHARINDEX('DATE', W_N9R.W_N9_IDSF) <> 0 
                    AND CHARINDEX(':', W_N9R.W_N9_IDSF, CHARINDEX('DATE', W_N9R.W_N9_IDSF)+1) <> 0 
                    AND CHARINDEX('TIME',W_N9R.W_N9_IDSF) > CHARINDEX(':', W_N9R.W_N9_IDSF, CHARINDEX('DATE', W_N9R.W_N9_IDSF)+1)
                    AND CHARINDEX('TIME',W_N9R.W_N9_IDSF)-(CHARINDEX(':', W_N9R.W_N9_IDSF, CHARINDEX('DATE', W_N9R.W_N9_IDSF)+1)+1) <> 0
                    ,
                    SUBSTRING(
                        W_N9R.W_N9_IDSF
                        ,
                        CHARINDEX(':', W_N9R.W_N9_IDSF, CHARINDEX('DATE', W_N9R.W_N9_IDSF)+1)+1
                        ,
                        CHARINDEX('TIME',W_N9R.W_N9_IDSF)-(CHARINDEX(':', W_N9R.W_N9_IDSF, CHARINDEX('DATE', W_N9R.W_N9_IDSF)+1)+1))    
                    , 

                    ''
                )
            )
        )
    )

这将解析格式错误的字符串,例如:

'日期z:{2010 年 10 月 4 日、5 日、6 日和 12 月 8 日} 时间:{8:30 AM - 3:30 PM} 位置:',即使它不应该。

评论

1赞 Dale K 11/10/2023
OP特别表示,他们无法在新环境中创建该功能。并不是说它不能像写的那样工作。
2赞 Charlieface 11/10/2023 #2

你基本上可以做 UDF 内联所做的事情:将其转换为一系列条件。CROSS APPLY

您可以删除函数的整个后半部分,因为您的调用已将第一个参数固定为 。1

SELECT 
    812_ACT_RM.R1_ACT,
    812_ACT_RM.R1_FIRST,
    -- ... many more lines
JOIN (
    SELECT 
      W_N9R.W_N9_IDSF, 
      formatted.KOX
    FROM SomeTable W_N9R
    OUTER APPLY (    -- outer apply usually forces immediate calculation, as opposed to cross apply which tends to defer
        SELECT TOP (1)
          v4.KOX
        FROM (VALUES
            ('Date(s):'),
            ('Date:'),
            ('Dates:')
        ) v1(dateText)
        CROSS APPLY (
            SELECT
              dateIndex = NULLIF(CHARINDEX(v1.dateText, W_N9R.W_N9_IDSF), 0)
        ) v2
        CROSS APPLY (
            SELECT timeIndex = NULLIF(CHARINDEX('Time', W_N9R.W_N9_IDSF, v2.dateIndex + LEN(v1.dateIndex)), 0)
            WHERE v2.dateIndex > 0
              AND LEN(W_N9R.W_N9_IDSF) > v2.dateIndex + LEN(v1.dateText)
        ) v3
        CROSS APPLY (
            SELECT KOX = RTRIM(LTRIM(SUBSTRING(W_N9R.W_N9_IDSF, v2.dateIndex + LEN(v1.dateText), v3.timeIndex - v2.dateIndex - LEN(v1.dateText))))
        ) v4
        WHERE v4.KOX IS NOT NULL
    ) formatted;