在需要字符串文字的地方使用SQL参数
我试图将顶级解决方案的最顶级代码从转换为SQL用户定义函数。但是,当我尝试在value函数的第一个参数中使用一个变量作为数字4时,它会呕吐 所以这是可行的:在需要字符串文字的地方使用SQL参数,sql,sql-server,xml,tsql,delimited-text,Sql,Sql Server,Xml,Tsql,Delimited Text,我试图将顶级解决方案的最顶级代码从转换为SQL用户定义函数。但是,当我尝试在value函数的第一个参数中使用一个变量作为数字4时,它会呕吐 所以这是可行的: SELECT CAST('<x>' + REPLACE(@Input,',','</x><x>') + '</x>' AS XML).value('/x[4]','int') …但每当我尝试替换'/x[4]'以使用变量代替4时,我会收到如下消息 XML数据类型方法值的参数1必须是字符串文字
SELECT CAST('<x>' + REPLACE(@Input,',','</x><x>') + '</x>' AS XML).value('/x[4]','int')
…但每当我尝试替换'/x[4]'以使用变量代替4时,我会收到如下消息
XML数据类型方法值的参数1必须是字符串文字
这是我目前为止完整的用户定义函数。。。只是学习如何:
USE [DBName]
GO
CREATE FUNCTION fx_SegmentN
(@Input AS VARCHAR(100),
@Number AS VARCHAR(1))
RETURNS varchar(100)
AS
BEGIN
DECLARE @ValueStringLiteral varchar(14)
SET @ValueStringLiteral = '/x[' + @Number + ']'
RETURN '' +
CASE
WHEN @Number <1
THEN ('ERROR')
WHEN @Number = 1
THEN (LEFT(@Input, CHARINDEX('-', @Input, 1)-1))
WHEN @Number > 1
--THEN (SELECT CAST('<x>' + REPLACE(@Input,',','</x><x>') + '</x>' AS XML).value('/x[4]','int')) --THIS LINE WORKS
THEN (SELECT CAST('<x>' + REPLACE(@Input,',','</x><x>') + '</x>' AS XML).value('/x[' + @Number + ']','int')) --THIS ONE DOES NOT
ELSE
(NULL)
END + ''
END
这是一个丑陋的黑客,但它的工作:
DECLARE @Query AS NVARCHAR(MAX) = N'SELECT CAST(''<x>'' + REPLACE(''' + @Input + ''' ,'','',''</x><x>'') + ''</x>'' AS XML).value(''/x['+ @Number + ']'',''int'')'
EXEC sp_executesql @Query
试试这个:
CREATE FUNCTION fx_SegmentN
(
@Input AS VARCHAR(100)
,@Number AS VARCHAR(1)
) RETURNS varchar(100)
AS
BEGIN
DECLARE @XML XML;
DECLARE @value VARCHAR(100);
SET @XML = CAST('<x>' + REPLACE(@Input,',','</x><x>') + '</x>' AS XML);
WITH DataSource ([rowID], [rowValue]) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY T.c ASC)
,T.c.value('.', 'VARCHAR(100)')
FROM @XML.nodes('./x') T(c)
)
SELECT @value = [rowValue]
FROM DataSource
WHERE [rowID] = @Number;
RETURN @value;
END
GO
SELECT dbo.fx_SegmentN ('1a,2b,3c,4d,5e,6f,7g,8h', 3);
你可能会对感兴趣,其实很简单:
declare @x xml = N'
<x>2345</x>
<x>vsaaef</x>
<x>fxcfs</x>
<x>Number 4</x>
<x>vxcv</x>
<x>111</x>
';
declare @Position int = 4;
select t.c.value('./text()[1]', 'nvarchar(100)')
from @x.nodes('/x[position() = sql:variable("@Position")]') t(c);
为什么要将数字作为字符串传递?您在问题中找到并链接的答案为您提供了解决方案:当然,您可以使用变量作为分隔符和位置使用sql:列直接从查询的值检索位置:DECLARE@dlmt NVARCHAR10=N';声明@pos INT=2;选择CASTN+REPLACE@input,@dlmt,N+N作为XML.value'/x[sql:variable@pos][1] ’,'nvarcharmax'可能的复制品是非常昂贵的,首先创建一组所有可能的元素,只是为了扔掉它并返回我们需要的一个。。。此外,您的函数在@input中使用了禁用字符。多行标量函数是性能杀手。。。此外,它是没有文件记录的,因此不保证,这个行数黑客将在任何情况下工作,并将继续在未来的版本工作。真的不是最好的解决办法。清楚地显示了如何为此使用sql:variable或sql:column。@Shnugo肯定-标量函数不好,在sql Server 2019之前没有并行性。无论如何,输入的最大长度为100,更具体地说,如果我们进行这些操作的性能测试,我们可能最终只会对数据进行标准化。是的,@Shnugo,真是太遗憾了:希望如此-@Shnugo,你的评论说服我使用比我尝试的方法更有效的方法。因此,我使用了下面列出的选项,当然您可以使用变量作为分隔符和位置使用sql:column直接从查询的值检索位置,同时我希望原始答案首先清楚地表明您的偏好,谢谢!这将与@input中的禁用字符中断,甚至不需要。中清楚地显示了如何使用sql:variable或sql:column解决此问题。这在原则上是正确的,但代价不必要。这提供了一个更好的方法。对于您的案例,这是选择@x.value'/x[sql:variable@Position]/文本[1],“nvarchar100”。@Shnugo,可能吧,尽管我还没有比较执行计划。但是如果答案已经知道了,那么问这个问题有什么意义呢?我遗漏了一些细节吗?将它标记为复制品,然后就可以使用它了。无意冒犯。。。链接问题中公认的答案并不是最佳答案。这就是为什么我没有将其链接为副本。。。OP没有在那里找到现有的解决方案。。。