我在输入中有2个字符串,例如'1,5,6'和'2,89,9',具有相同数量的元素(3或加号)。 我想要的那两个字符串作为
进行了“纵坐标连接”1 2
5 89
6 9
我想要分配一个rownumber并在2个结果集之间进行连接
SELECT a.item, b.item FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS rownumber,
* FROM dbo.Split('1,5,6',',')
) AS a
INNER JOIN
(
SELECT
ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS rownumber,
* FROM dbo.Split('2,89,9',',')
) AS b ON a.rownumber = b.rownumber
是最佳做法吗?
答案 0 :(得分:13)
当dbo.Split()
返回数据集时,您所做的任何事情都不能绝对确定地分配您想要的row_number(基于它们在字符串中的顺序)。 SQL永远不会保证没有实际与数据相关的ORDER BY
的排序。
使用技巧使用(SELECT 0)
订购, 经常 获取正确的值。可能经常非常。但这从不 保证 。偶尔你 将 得到错误的订单。
您最好的选择是重新编码dbo.Split()
以在解析字符串时分配row_number。只有这样才能100%确定row_number确实对应于项目在列表中的位置。
然后按照您的建议加入他们,并获得您想要的结果。
除此之外,这个想法对我来说似乎没什么问题。虽然您可能希望考虑FULL OUTER JOIN
,如果一个列表可能比另一个更长。
答案 1 :(得分:7)
你也可以这样做
考虑你的拆分功能:
CREATE FUNCTION Split
(
@delimited nvarchar(max),
@delimiter nvarchar(100)
) RETURNS @t TABLE
(
id int identity(1,1),
val nvarchar(max)
)
AS
BEGIN
declare @xml xml
set @xml = N'<root><r>' + replace(@delimited,@delimiter,'</r><r>') + '</r></root>'
insert into @t(val)
select
r.value('.','varchar(5)') as item
from @xml.nodes('//root/r') as records(r)
RETURN
END
GO
JOIN
将它们放在一起将是一项简单的任务。像这样:
SELECT
*
FROM
dbo.Split('1,5,6',',') AS a
JOIN dbo.Split('2,89,9',',') AS b
ON a.id=b.id
这样做的好处是您不需要任何ROW_NUMBER() OVER(ORDER BY SELECT 0)
修改强>
在评论中,使用递归拆分功能可以提高性能。也许是这样的:
CREATE FUNCTION dbo.Split (@s varchar(512),@sep char(1))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(@sep, @s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT pn,
SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces
)
GO
然后选择是这样的:
SELECT
*
FROM
dbo.Split('1,5,6',',') AS a
JOIN dbo.Split('2,89,9',',') AS b
ON a.pn=b.pn
答案 2 :(得分:0)
感谢Arion的建议。这对我来说非常有用。我稍微修改了函数以支持输入字符串的varchar(max)类型,并且分隔符字符串的最大长度为1000。另外,添加了一个参数来指示在最终返回中是否需要空字符串。
对于MatBailie的问题,因为这是一个内联函数,所以可以在调用此函数的外部查询中包含pn列。
CREATE FUNCTION dbo.Split (@s nvarchar(max),@sep nvarchar(1000), @IncludeEmpty bit)
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT convert(bigint, 1) , convert(bigint, 1), convert(bigint,CHARINDEX(@sep, @s))
UNION ALL
SELECT pn + 1, stop + LEN(@sep), CHARINDEX(@sep, @s, stop + LEN(@sep))
FROM Pieces
WHERE stop > 0
)
SELECT pn,
SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE LEN(@s) END) AS s
FROM Pieces
where start< CASE WHEN stop > 0 THEN stop ELSE LEN(@s) END + @IncludeEmpty
)
但是当我要返回的列表有超过100条记录时,我遇到了这个函数的一些问题。所以,我创建了另一个纯粹使用字符串解析函数的函数:
Create function [dbo].[udf_split] (
@ListString nvarchar(max),
@Delimiter nvarchar(1000),
@IncludeEmpty bit)
Returns @ListTable TABLE (ID int, ListValue varchar(max))
AS
BEGIN
Declare @CurrentPosition int, @NextPosition int, @Item nvarchar(max), @ID int
Select @ID = 1,
@ListString = @Delimiter+ @ListString + @Delimiter,
@CurrentPosition = 1+LEN(@Delimiter)
Select @NextPosition = Charindex(@Delimiter, @ListString, @CurrentPosition)
While @NextPosition > 0 Begin
Select @Item = Substring(@ListString, @CurrentPosition, @NextPosition-@CurrentPosition)
If @IncludeEmpty=1 or Len(LTrim(RTrim(@Item)))>0 Begin
Insert Into @ListTable (ID, ListValue) Values (@ID, LTrim(RTrim(@Item)))
Set @ID = @ID+1
End
Select @CurrentPosition = @NextPosition+LEN(@Delimiter),
@NextPosition = Charindex(@Delimiter, @ListString, @CurrentPosition)
End
RETURN
END
希望这可以提供帮助。