连接没有公共列的sql表而不排序

Posted

技术标签:

【中文标题】连接没有公共列的sql表而不排序【英文标题】:Joining sql tables with no common columns without ordering 【发布时间】:2021-09-24 08:13:07 【问题描述】:

我的数据采用 2 个逗号分隔的字符串形式

DECLARE @ids nvarchar(max) = '1,2,3'
DECLARE @guids nvarchar(max) = 
'0000000-0001-0000-0000-000000000000,
`0000000-0022-0000-0000-000000000000`,
`0000000-0013-0000-0000-000000000000'`

我需要根据它们在字符串中的位置将它们作为单独的列放在表格中

Table1
| Id | Guid                                |
| 1  | 0000000-0001-0000-0000-000000000000 |
| 2  | 0000000-0022-0000-0000-000000000000 |
| 3  | 0000000-0013-0000-0000-000000000000 |

我可以通过使用将两个字符串拆分到单独的表中

DECLARE @split_ids
(value nvarchar(max))

DECLARE @xml xml
SET @xml = N'<root><r>' + replace(@ids, ',' ,'</r><r>') + '</r></root>'

INSERT INTO @split_ids(Value)
SELECT r.value('.','nvarchar(max)')
FROM @xml.nodes('//root/r') as records(r)

我试过了

 SELECT t1.*, t2.*
 FROM (SELECT t1.*, row_number() OVER (ORDER BY [Value]) as seqnum
  from cte_Ids t1
 ) t1 FULL OUTER  JOIN
 (SELECT t2.*, row_number() OVER (ORDER BY [Value]) as seqnum
  from cte_barcodes t2
 ) t2
 ON t1.seqnum = t2.seqnum;

但是按Value 对表格进行排序,我的数据是随机的,无法排序。

有没有一种方法可以根据表格的行号而不先对表格进行排序来连接表格?

或者还有其他方法可以将字符串中的数据插入到表中吗?

【问题讨论】:

这能回答你的问题吗? Split multiple comma separated columns into rows 使用 over (order by select 1) 或其他一些常量 - 它可能会保留原始顺序。 你需要的是一个观察序数位置的分离器,比如delimitedsplit8k_Lead @Arvo 我得到:Windowed functions, aggregates and NEXT VALUE FOR functions do not support integer indices as ORDER BY clause expressions. 错误 也许 (order by (select 1)) 然后 - 这只是进一步研究的想法,而不是答案:) 【参考方案1】:

您不需要将输入数据拆分和/或插入到单独的表中。在这种情况下,您只需解析输入字符串并获取子字符串及其序号位置(基于 XML 的方法或拆分器函数是可能的解决方案)。

但如果您使用 SQL Server 2016+,则基于 JSON 的方法也是一种选择。想法是将字符串转换为有效的 JSON 数组(1,2,3 转换为 [1,2,3]),用OPENJSON() 解析数组并加入从OPENJSON() 调用返回的表。与文档中的 explained 一样,OPENJSON() 函数返回的列(使用默认模式时)是 keyvaluetype,对于 JSON 数组,key 列包含指定数组中元素的索引。

DECLARE @ids nvarchar(max) = N'1,2,3'
DECLARE @guids nvarchar(max) =  N'0000000-0001-0000-0000-000000000000,0000000-0022-0000-0000-000000000000,0000000-0013-0000-0000-000000000000'

SELECT j1.[value] AS Id, j2.[value] AS Guid
FROM OPENJSON(CONCAT('[', @ids, ']')) j1
JOIN OPENJSON(CONCAT('["', REPLACE(@guids, ',', '","'), '"]')) j2 ON j1.[key] = j2.[key]

结果:

Id  Guid
1   0000000-0001-0000-0000-000000000000
2   0000000-0022-0000-0000-000000000000
3   0000000-0013-0000-0000-000000000000

【讨论】:

【参考方案2】:

您需要对初始顺序进行行编号,这意味着您应该在窗口函数order_by 子句中使用一些常量表达式。 SQL server 不允许直接使用常量,但over(order_by (select 1)) 是允许的:

SELECT t1.*, t2.*
 FROM (SELECT t1.*, row_number() OVER (ORDER BY (select 1)) as seqnum
  from cte_Ids t1
 ) t1 FULL OUTER  JOIN
 (SELECT t2.*, row_number() OVER (ORDER BY (select 1)) as seqnum
  from cte_barcodes t2
 ) t2
 ON t1.seqnum = t2.seqnum;

请注意,这并不能保证初始顺序(它将未指定),但通常它的行为是正确的 :)

【讨论】:

(ORDER BY (select 1)) 警告 - 这是合乎逻辑的废话。它告诉引擎以它想要的任何方式对它们进行排序,并且该顺序可以从执行到执行而变化。如果这确实是目标,它也不是“随机的”。在负载较轻的机器上,多次执行可能会产生相同的输出——根据定义,这不是随机的。 @SMor 绝对正确 :) 戈登解决方案可能是正确的;当人们可以立即看到结果时,我的“无顺序”可以用于临时查询;不建议将这种方法用于生产系统!今天不断的话,等sql升级什么的就断了。【参考方案3】:

其中一种解决方案是从两个变量中循环解析逗号分隔值(使用WHILE)。然后,您可以将在相同迭代值中提取的那些作为一行插入到表中。

【讨论】:

【参考方案4】:

一种解决方案使用递归 CTE:

with cte as (
      select cast(null as nvarchar(max)) as id, cast(null as nvarchar(max)) as guid, @ids + ',' as rest_ids, @guids + ',' as rest_guids, 0 as lev
      union all
      select left(rest_ids, charindex(',', rest_ids) - 1),
            left(rest_guids, charindex(',', rest_guids) - 1),
             stuff(rest_ids, 1,  charindex(',', rest_ids), ''),
             stuff(rest_guids, 1,  charindex(',', rest_guids), ''),
             lev + 1
      from cte
      where rest_ids <> ''
     )
select id, guid
from cte
where lev > 0;

Here 是一个 dbfiddle。

【讨论】:

以上是关于连接没有公共列的sql表而不排序的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PL/SQL 中连接两个表而不创建新表

Mysql连接表而不重复属于同一行的行

SQL 排序

从没有按日期排序的公共列的两个表中打印结果

Sequelize连接表而不选择

将数据从 Excel 文件导入 SQL 表而不重复?