SQL 服务器 - 在插入列之前对组合数字字符串进行排序
SQL Server - Ordering Combined Number Strings Prior To Column Insert
我有 2 个字符串列(数千行),每个字符串中都有有序的数字(每个字符串中可以有零到十个数字)。示例:
+------------------+------------+
| ColString1 | ColString2 |
+------------------+------------+
| 1;3;5;12; | 4;6' |
+------------------+------------+
| 1;5;10 | 2;26; |
+------------------+------------+
| 4;7; | 3; |
+------------------+------------+
最终结果是合并这两列,对数字进行排序
升序,然后将每个数字放入单独的列(最小,第二小等)。
例如Colstring1 是 1;3;5;12;
而 ColString2 是 4;6;
需要 return 1;3;4;5;6;12;
然后我使用 xml 分配到列中。
使用 xml 一切正常,除了对数字进行排序的步骤(即我得到 1;3;5;12;4;6; 当我组合字符串时,即不按升序排列).
我试过先将它们放入 JSON 数组中进行排序,我想我可以做一个 top[1] 等,但那没有用。
关于如何合并 2 列并在插入列之前对其进行排序的任何帮助:
到目前为止的步骤:
示例数据:
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, ColString1 VARCHAR(50), ColString2 VARCHAR(50));
INSERT INTO @tbl (ColString1, ColString2)
VALUES
('1;3;5;12;', '4;6;'),
('1;5;10;', '2;26;'),
('14;', '3;8;');
XML 方法(组合字符串并放入列中但顺序不正确):
;WITH Split_Numbers (xmlname)
AS
(
SELECT
CONVERT(XML,'<Names><name>'
+ REPLACE ( LEFT(ColString1+ColString2,LEN(ColString1+ColString2) - 1),';', '</name><name>') + '</name></Names>') AS xmlname
FROM @tbl
)
SELECT
xmlname.value('/Names[1]/name[1]','int') AS Number1,
xmlname.value('/Names[1]/name[2]','int') AS Number2,
xmlname.value('/Names[1]/name[3]','int') AS Number3,
xmlname.value('/Names[1]/name[4]','int') AS Number4,
xmlname.value('/Names[1]/name[5]','int') AS Number5
--etc for additional columns
FROM Split_Numbers
当前输出:数字顺序不正确,
+---------+---------+---------+---------+---------+
| Number1 | Number2 | Number3 | Number4 | Number5 |
+---------+---------+---------+---------+---------+
| 1 | 3 | 5 | 12 | 4 |
| 1 | 5 | 10 | 2 | 26 |
| 14 | 3 | 8 | NULL | NULL |
+---------+---------+---------+---------+---------+
期望输出:数字按升序排列。
+---------+---------+---------+---------+---------+
| Number1 | Number2 | Number3 | Number4 | Number5 |
+---------+---------+---------+---------+---------+
| 1 | 3 | 4 | 5 | 6 |
| 1 | 2 | 5 | 10 | 26 |
| 3 | 8 | 14 | NULL | NULL |
+---------+---------+---------+---------+---------+
JSON 方法:将列组合成一个 JSON 数组,但在 JSON 格式时我仍然无法正确排序。
REPLACE ( CONCAT('[', LEFT(ColString1+ColString2,LEN(ColString1+ColString2) - 1), ']') ,';',',')
如果有任何方法可以在输入前订购 xml 或 JSON 字符串,我们将不胜感激。如果有更简单的解决方案,很乐意考虑替代方法。
您可以使用 string_agg()
和 string_split()
:
select t.*, newstring
from t cross apply
(select string_agg(value, ',') order by (value) as newstring
from (select s1.value
from unnest(colstring1, ',') s1
union all
select s2.value
from unnest(colstring2, ',') s2
) s
) s;
也就是说,您可能应该努力修复数据模型。在字符串中存储数字是不好的。在一个字符串中存储多个值是非常糟糕的。如果数字是对其他表的外国引用,那就是坏坏坏坏坏坏
在等待 DDL 和示例数据填充等期间,这里有一个概念性的例子供您参考。它使用 XQuery 及其 FLWOR 表达式。
CTE 完成了大部分繁重的工作:
- 将两列值连接成一个字符串。
CONCAT()
函数防止 NULL 值。
- 将其转换为 XML 数据类型。
- 通过将元素的值转换为 FLWOR 表达式中的
int
数据类型,对 XML 元素进行排序。
- 过滤掉 XML 个没有合法值的元素。
其余的都是微不足道的。
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100));
INSERT INTO @tbl (col1, col2)
VALUES
('1;3;5;12;', '4;6;'),
('1;5;10;', '2;26;');
-- DDL and sample data population, end
DECLARE @separator CHAR(1) = ';';
;WITH rs AS
(
SELECT *
, CAST('<root><r><![CDATA[' +
REPLACE(CONCAT(col1, col2), @separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML).query('<root>
{
for $x in /root/r[text()]
order by xs:int($x)
return $x
}
</root>') AS sortedXML
FROM @tbl
)
SELECT ID
, c.value('(r[1]/text())[1]','INT') AS Number1
, c.value('(r[2]/text())[1]','INT') AS Number2
, c.value('(r[3]/text())[1]','INT') AS Number3
-- continue with the rest of the columns
FROM rs CROSS APPLY sortedXML.nodes('/root') AS t(c);
输出
+----+---------+---------+---------+
| ID | Number1 | Number2 | Number3 |
+----+---------+---------+---------+
| 1 | 1 | 3 | 4 |
| 2 | 1 | 2 | 5 |
+----+---------+---------+---------+
我有 2 个字符串列(数千行),每个字符串中都有有序的数字(每个字符串中可以有零到十个数字)。示例:
+------------------+------------+
| ColString1 | ColString2 |
+------------------+------------+
| 1;3;5;12; | 4;6' |
+------------------+------------+
| 1;5;10 | 2;26; |
+------------------+------------+
| 4;7; | 3; |
+------------------+------------+
最终结果是合并这两列,对数字进行排序
升序,然后将每个数字放入单独的列(最小,第二小等)。
例如Colstring1 是 1;3;5;12;
而 ColString2 是 4;6;
需要 return 1;3;4;5;6;12;
然后我使用 xml 分配到列中。
使用 xml 一切正常,除了对数字进行排序的步骤(即我得到 1;3;5;12;4;6; 当我组合字符串时,即不按升序排列).
我试过先将它们放入 JSON 数组中进行排序,我想我可以做一个 top[1] 等,但那没有用。
关于如何合并 2 列并在插入列之前对其进行排序的任何帮助:
到目前为止的步骤: 示例数据:
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, ColString1 VARCHAR(50), ColString2 VARCHAR(50));
INSERT INTO @tbl (ColString1, ColString2)
VALUES
('1;3;5;12;', '4;6;'),
('1;5;10;', '2;26;'),
('14;', '3;8;');
XML 方法(组合字符串并放入列中但顺序不正确):
;WITH Split_Numbers (xmlname)
AS
(
SELECT
CONVERT(XML,'<Names><name>'
+ REPLACE ( LEFT(ColString1+ColString2,LEN(ColString1+ColString2) - 1),';', '</name><name>') + '</name></Names>') AS xmlname
FROM @tbl
)
SELECT
xmlname.value('/Names[1]/name[1]','int') AS Number1,
xmlname.value('/Names[1]/name[2]','int') AS Number2,
xmlname.value('/Names[1]/name[3]','int') AS Number3,
xmlname.value('/Names[1]/name[4]','int') AS Number4,
xmlname.value('/Names[1]/name[5]','int') AS Number5
--etc for additional columns
FROM Split_Numbers
当前输出:数字顺序不正确,
+---------+---------+---------+---------+---------+
| Number1 | Number2 | Number3 | Number4 | Number5 |
+---------+---------+---------+---------+---------+
| 1 | 3 | 5 | 12 | 4 |
| 1 | 5 | 10 | 2 | 26 |
| 14 | 3 | 8 | NULL | NULL |
+---------+---------+---------+---------+---------+
期望输出:数字按升序排列。
+---------+---------+---------+---------+---------+
| Number1 | Number2 | Number3 | Number4 | Number5 |
+---------+---------+---------+---------+---------+
| 1 | 3 | 4 | 5 | 6 |
| 1 | 2 | 5 | 10 | 26 |
| 3 | 8 | 14 | NULL | NULL |
+---------+---------+---------+---------+---------+
JSON 方法:将列组合成一个 JSON 数组,但在 JSON 格式时我仍然无法正确排序。
REPLACE ( CONCAT('[', LEFT(ColString1+ColString2,LEN(ColString1+ColString2) - 1), ']') ,';',',')
如果有任何方法可以在输入前订购 xml 或 JSON 字符串,我们将不胜感激。如果有更简单的解决方案,很乐意考虑替代方法。
您可以使用 string_agg()
和 string_split()
:
select t.*, newstring
from t cross apply
(select string_agg(value, ',') order by (value) as newstring
from (select s1.value
from unnest(colstring1, ',') s1
union all
select s2.value
from unnest(colstring2, ',') s2
) s
) s;
也就是说,您可能应该努力修复数据模型。在字符串中存储数字是不好的。在一个字符串中存储多个值是非常糟糕的。如果数字是对其他表的外国引用,那就是坏坏坏坏坏坏
在等待 DDL 和示例数据填充等期间,这里有一个概念性的例子供您参考。它使用 XQuery 及其 FLWOR 表达式。
CTE 完成了大部分繁重的工作:
- 将两列值连接成一个字符串。
CONCAT()
函数防止 NULL 值。 - 将其转换为 XML 数据类型。
- 通过将元素的值转换为 FLWOR 表达式中的
int
数据类型,对 XML 元素进行排序。 - 过滤掉 XML 个没有合法值的元素。
其余的都是微不足道的。
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100));
INSERT INTO @tbl (col1, col2)
VALUES
('1;3;5;12;', '4;6;'),
('1;5;10;', '2;26;');
-- DDL and sample data population, end
DECLARE @separator CHAR(1) = ';';
;WITH rs AS
(
SELECT *
, CAST('<root><r><![CDATA[' +
REPLACE(CONCAT(col1, col2), @separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML).query('<root>
{
for $x in /root/r[text()]
order by xs:int($x)
return $x
}
</root>') AS sortedXML
FROM @tbl
)
SELECT ID
, c.value('(r[1]/text())[1]','INT') AS Number1
, c.value('(r[2]/text())[1]','INT') AS Number2
, c.value('(r[3]/text())[1]','INT') AS Number3
-- continue with the rest of the columns
FROM rs CROSS APPLY sortedXML.nodes('/root') AS t(c);
输出
+----+---------+---------+---------+
| ID | Number1 | Number2 | Number3 |
+----+---------+---------+---------+
| 1 | 1 | 3 | 4 |
| 2 | 1 | 2 | 5 |
+----+---------+---------+---------+