提问人:dgo 提问时间:11/10/2023 最后编辑:dgo 更新时间:11/11/2023 访问量:76
长复杂 select 语句中的匿名函数或等效函数
Anonymous function or equivalent within long complex select statement
问:
我们正在将一个 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 开发人员。我只是想让新系统在请求帮助时工作。
答:
-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;
评论
CROSS APPLY