Xquery 获取连续的不同值
Xquery Get Consecutive Distinct Values
试图从以下 XML 中获得连续的不同。
<x>
AB
</x>
<x>
AB
</x>
<x>
AB
</x>
<x>
AC
</x>
<x>
AB
</x>
预期结果:
AB AC AB
当前结果:
AB AC
代码:
SELECT * ,STUFF(( TheParts.query(' for $x in distinct-values(/x/text()) return <x>{concat(" | ", $x)}</x> ').value('.', 'varchar(max)') ), 1, 1, '') AS Hierarchy
FROM Table
不同的值与整个集合不同,但我只想在连续的情况下获得不同的值。
请尝试以下解决方案。它不是基于 XQuery 的。
遗憾的是,MS SQL Server 的 XQuery 仅支持 XQuery 1.0 标准的一个子集。
翻滚 window 和 window 变量 功能是 XQuery 3.0/3.1 标准的一部分。
在这里查看:window clause
请对我的建议投赞成票:SQL Server vNext (post 2019) and NoSQL functionality
SQL
DECLARE @xml XML =
N'<root>
<x>AB</x>
<x>AB</x>
<x>AC</x>
<x>AC</x>
<x>AB</x>
</root>';
WITH rs AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS seq
, c.value('text()[1]','CHAR(2)') AS R
FROM @xml.nodes('/root/x') AS t(c)
), cte AS
(
SELECT *, SUM(IIF(R <> ns, 1, 0)) OVER (ORDER BY seq) AS series
FROM (
SELECT series.*,
LAG(R) OVER (ORDER BY seq) AS ns
FROM rs AS series
) q
)
--SELECT * FROM cte ORDER BY seq;
SELECT MIN(R) AS R, series, MIN(cte.seq) AS seq_min, MAX(cte.seq) AS seq_max
FROM cte
GROUP BY cte.series
ORDER BY MAX(cte.seq) ASC;
输出
+----+--------+---------+---------+
| R | series | seq_min | seq_max |
+----+--------+---------+---------+
| AB | 0 | 1 | 2 |
| AC | 1 | 3 | 4 |
| AB | 2 | 5 | 5 |
+----+--------+---------+---------+
我们有一个纯 XQuery 解决方案。
SQL
SELECT REPLACE(CAST('<x>AB</x>
<x>AB</x>
<x>AB</x>
<x>AC</x>
<x>AB</x>' as xml)
.query('
for $v in x
let $n := x[. >> $v][1]
return if ($v/text()[1] = $n/text()[1]) then ()
else data($v)
').value('.', 'VARCHAR(MAX)'), SPACE(1), ' | ') AS Result;
输出
+--------------+
| Result |
+--------------+
| AB | AC | AB |
+--------------+
select
cast('<x>AB</x>
<x>AB</x>
<x>AB</x>
<x>AC</x>
<x>AB</x>' as xml).query('
for $v in x
let $n := x[. >> $v][1]
where not($v/text() = $n/text())
return (string($v/text()[1]))
')
select stuff(
cast('<x>AB</x>
<x>AB</x>
<x>AB</x>
<x>AC</x>
<x>AB</x>' as xml).query('
for $v in x/text()
let $n := x[. >> $v][1]/text()
where not($v = $n)
return text{concat(" | ", $v)}
').value('.', 'nvarchar(max)'), 1, 3, '')
select stuff(
cast('<x>AB</x>
<x>AB</x>
<x>AB</x>
<x></x>
<x></x>
<x></x>
<x></x>
<x></x>
<x></x>
<x>AC</x>
<x>AB</x>' as xml).query('
for $v in x
let $n := x[. >> $v][1]
where not($v/text()= $n/text() or (empty($v/text()) and empty($n/text())))
return text{concat(" | ", $v/text()[1])}
').value('.', 'nvarchar(max)'), 1, 3, '')
与 xquery 中的想法相同的更快的方法:查找序列的最后一个值。 fiddle
select s.hierarchy
from t
cross apply
(
select string_agg(isnull(x, ''), ' | ') within group(order by rn) as hierarchy
from
(
select rn, x, lead(x) over(order by rn) as n
from
(
select row_number() over(order by n.x) as rn, n.x.value('text()[1]', 'varchar(20)') as x
from t.TheParts.nodes('x') as n(x)
) as d
) as l
where isnull(x, char(1)) <> isnull(n, char(1))
) as s
试图从以下 XML 中获得连续的不同。
<x>
AB
</x>
<x>
AB
</x>
<x>
AB
</x>
<x>
AC
</x>
<x>
AB
</x>
预期结果:
AB AC AB
当前结果:
AB AC
代码:
SELECT * ,STUFF(( TheParts.query(' for $x in distinct-values(/x/text()) return <x>{concat(" | ", $x)}</x> ').value('.', 'varchar(max)') ), 1, 1, '') AS Hierarchy
FROM Table
不同的值与整个集合不同,但我只想在连续的情况下获得不同的值。
请尝试以下解决方案。它不是基于 XQuery 的。
遗憾的是,MS SQL Server 的 XQuery 仅支持 XQuery 1.0 标准的一个子集。
翻滚 window 和 window 变量 功能是 XQuery 3.0/3.1 标准的一部分。
在这里查看:window clause
请对我的建议投赞成票:SQL Server vNext (post 2019) and NoSQL functionality
SQL
DECLARE @xml XML =
N'<root>
<x>AB</x>
<x>AB</x>
<x>AC</x>
<x>AC</x>
<x>AB</x>
</root>';
WITH rs AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS seq
, c.value('text()[1]','CHAR(2)') AS R
FROM @xml.nodes('/root/x') AS t(c)
), cte AS
(
SELECT *, SUM(IIF(R <> ns, 1, 0)) OVER (ORDER BY seq) AS series
FROM (
SELECT series.*,
LAG(R) OVER (ORDER BY seq) AS ns
FROM rs AS series
) q
)
--SELECT * FROM cte ORDER BY seq;
SELECT MIN(R) AS R, series, MIN(cte.seq) AS seq_min, MAX(cte.seq) AS seq_max
FROM cte
GROUP BY cte.series
ORDER BY MAX(cte.seq) ASC;
输出
+----+--------+---------+---------+
| R | series | seq_min | seq_max |
+----+--------+---------+---------+
| AB | 0 | 1 | 2 |
| AC | 1 | 3 | 4 |
| AB | 2 | 5 | 5 |
+----+--------+---------+---------+
我们有一个纯 XQuery 解决方案。
SQL
SELECT REPLACE(CAST('<x>AB</x>
<x>AB</x>
<x>AB</x>
<x>AC</x>
<x>AB</x>' as xml)
.query('
for $v in x
let $n := x[. >> $v][1]
return if ($v/text()[1] = $n/text()[1]) then ()
else data($v)
').value('.', 'VARCHAR(MAX)'), SPACE(1), ' | ') AS Result;
输出
+--------------+
| Result |
+--------------+
| AB | AC | AB |
+--------------+
select
cast('<x>AB</x>
<x>AB</x>
<x>AB</x>
<x>AC</x>
<x>AB</x>' as xml).query('
for $v in x
let $n := x[. >> $v][1]
where not($v/text() = $n/text())
return (string($v/text()[1]))
')
select stuff(
cast('<x>AB</x>
<x>AB</x>
<x>AB</x>
<x>AC</x>
<x>AB</x>' as xml).query('
for $v in x/text()
let $n := x[. >> $v][1]/text()
where not($v = $n)
return text{concat(" | ", $v)}
').value('.', 'nvarchar(max)'), 1, 3, '')
select stuff(
cast('<x>AB</x>
<x>AB</x>
<x>AB</x>
<x></x>
<x></x>
<x></x>
<x></x>
<x></x>
<x></x>
<x>AC</x>
<x>AB</x>' as xml).query('
for $v in x
let $n := x[. >> $v][1]
where not($v/text()= $n/text() or (empty($v/text()) and empty($n/text())))
return text{concat(" | ", $v/text()[1])}
').value('.', 'nvarchar(max)'), 1, 3, '')
与 xquery 中的想法相同的更快的方法:查找序列的最后一个值。 fiddle
select s.hierarchy
from t
cross apply
(
select string_agg(isnull(x, ''), ' | ') within group(order by rn) as hierarchy
from
(
select rn, x, lead(x) over(order by rn) as n
from
(
select row_number() over(order by n.x) as rn, n.x.value('text()[1]', 'varchar(20)') as x
from t.TheParts.nodes('x') as n(x)
) as d
) as l
where isnull(x, char(1)) <> isnull(n, char(1))
) as s