Reb*_*cca 21 sql-server t-sql string-aggregation
我有一个表,其中包含记录 ID、组 ID(将 1 个或多个记录链接到一个组中)以及每个记录的哈希值。
CREATE TABLE HashTable(
RecordID VARCHAR(255),
GroupIdentifier VARCHAR(255),
Hash VARCHAR (255),
GroupHashList VARCHAR(4000)
)
Run Code Online (Sandbox Code Playgroud)
(我知道这不是一个有效的表,但它只是用于本示例的临时表)。
我想为每个组生成一个哈希值,所以我认为最简单的方法是连接组中每个记录的哈希值。RecordID 是唯一的,但这些记录所涉及的内容不一定是唯一的,因此散列可能是重复的。这样做的目的是标记完全重复的组,即该组中的所有记录都是另一组中的所有记录的重复项的组。如果 GUI 要将组中的所有成员识别为重复组,则需要该组的所有成员具有相同的哈希值。
我使用 STRING_AGG 连接组中记录的各个散列,并按散列对它们进行排序,以确保我为重复的组获得相同的字符串。我实际上并不关心哈希的顺序是什么,只要每次都相同即可。当我将其作为 SELECT 查询运行时,它工作正常,并且我可以看到重复组的相同字符串。当我采用相同的 SELECT 查询并将其放入 UPDATE 查询中时,顺序似乎丢失了。
SELECT STRING_AGG([Hash],';') WITHIN GROUP (ORDER BY [Hash] ASC)
FROM HashTable
GROUP BY [GroupIdentifier]
Run Code Online (Sandbox Code Playgroud)
这给出了结果(对于一对重复组的示例):
CREATE TABLE HashTable(
RecordID VARCHAR(255),
GroupIdentifier VARCHAR(255),
Hash VARCHAR (255),
GroupHashList VARCHAR(4000)
)
Run Code Online (Sandbox Code Playgroud)
当我将相同的代码放入 UPDATE 查询中时,它无法正确对它们进行排序:
UPDATE HashTable
SET GroupHashList = c.HashList
FROM HashTable
INNER JOIN (
SELECT (STRING_AGG([Hash],';') WITHIN GROUP (ORDER BY [Hash] ASC)) AS [HashList],
[GroupIdentifier]
FROM HashTable
GROUP BY [GroupIdentifier]) c
ON c.[GroupIdentifier] = HashTable.[GroupIdentifier]
Run Code Online (Sandbox Code Playgroud)
写入表中的相同两组的结果:
SELECT STRING_AGG([Hash],';') WITHIN GROUP (ORDER BY [Hash] ASC)
FROM HashTable
GROUP BY [GroupIdentifier]
Run Code Online (Sandbox Code Playgroud)
我缺少什么?
我第一次得到的是
Hash1; Hash2; Hash3
Hash1; Hash2; Hash3
Run Code Online (Sandbox Code Playgroud)
但是当它在 UPDATE 查询中时,我得到
Hash1; Hash2; Hash3
Hash1; Hash3; Hash2
Run Code Online (Sandbox Code Playgroud)
更新查询是按记录 ID 排序的,尽管我不知道这是否是巧合。(https://dbfiddle.uk/CPG1-z2l)
Cha*_*ace 25
这似乎是优化器中的一个错误。
优化器意识到连接是自连接,正在将其转换为窗口聚合。STRING_AGG
尽管它不能作为窗口聚合使用,但它仍然可以做到这一点。该规则称为GenGbApplySimple,并允许将自连接转换为窗口聚合。到目前为止,这并没有什么特别的问题。
问题在于聚合超过了错误的值。它聚合的是外部价值而不是内部价值。
如果您为两个引用指定不同的别名,那么仔细检查查询计划就会发现该错误。
STRING_AGG([dbo].[HashTable].[Hash] as [HT1].[Hash],'')
WITHIN GROUP (ORDER BY [HT2].[Hash])
Run Code Online (Sandbox Code Playgroud)
另一个问题是与该规则一起使用的聚合(例如MIN
、MAX
、AVG
)没有WITHIN GROUP
要满足的顺序,因此替换计划不考虑它。这似乎STRING_AGG
并不适合GbApply规则,或者需要进行工作才能使其兼容(满足排序请求)。
如下所示,排序仅按相关列排序GroupIdentifier
,而不是Hash
按 中使用的列排序WITHIN GROUP
。
STRING_AGG([dbo].[HashTable].[Hash] as [HT1].[Hash],'')
WITHIN GROUP (ORDER BY [HT2].[Hash])
Run Code Online (Sandbox Code Playgroud)
如果您是sysadmin
,则可以使用以下未记录的 来为查询关闭此规则OPTION
。
<OrderBy>
<OrderByColumn Ascending="1">
<ColumnReference
Database="[...]"
Schema="[dbo]"
Table="[HashTable]"
Alias="[HT1]"
Column="GroupIdentifier">
</ColumnReference>
</OrderByColumn>
</OrderBy>
Run Code Online (Sandbox Code Playgroud)
作为解决方法,防止应用此优化的一个选项是使用分组OUTER APPLY
OPTION (QUERYRULEOFF GenGbApplySimple)
Run Code Online (Sandbox Code Playgroud)
这使您可以通过Stream Aggregate进行非常简单的自连接。
我强烈建议您将此作为错误提交给 Microsoft。
您也可以留下反馈,但这通常不会导致具体的回复。
顺便说一句,在编写多表语句时,您应该遵循Conor CunninghamUPDATE
建议的别名规则:
非 ANSI FROM 子句(您在此处使用的)具有特定的绑定行为,这些行为可能符合您的预期,也可能不是您所期望的。我建议您首先将哈希表的 3 个引用别名化为不同的,然后确保您明确引用了您想要的引用。(我猜测)它可能与您想象的不同,并因此为您提供了不需要的输出。
归档时间: |
|
查看次数: |
8347 次 |
最近记录: |