如何在使用 SQLCLR 用户定义的聚合函数 (UDA) 时提高查询性能
Posted
技术标签:
【中文标题】如何在使用 SQLCLR 用户定义的聚合函数 (UDA) 时提高查询性能【英文标题】:How to improve query Performance while using SQLCLR user defined aggregate function(UDA) 【发布时间】:2021-08-01 14:44:04 【问题描述】:我们在使用 SQLCLR 用户定义的聚合函数 (UDA) 时发现了性能问题,
这是我们的场景: 我们需要计算 2 列:键和值,其值如下:
key | value | |
---|---|---|
Row_1 | a/b/c/d/e | 1/2/3/2/1 |
Row_2 | a/b/c/d/e | 2/0/1/2/3 |
Row_3 | a/b/c/d/e | 2/3/4/1/2 |
我们需要一个聚合函数来得到每个指标的总和,在上面的例子中,我们想要得到这样的结果:
key | value | |
---|---|---|
Result | a/b/c/d/e | 5/5/8/5/6 |
我们无法使用本机聚合函数来获得这种结果,因此我们使用 SQLCLR UDA 来获得这种结果。我们发现当 SQLCLR UDA 与 GROUP BY 子句一起使用时,此 UDA 的性能很差。
经过一番调查,我们发现原因如下:
-
当我们使用 SQLCLR UDA 时,必须使用 StreamAggregate 操作,并且会引入昂贵的排序运算符,从而降低 UDA 的性能。
当我们使用 SQLCLR UDA 时,只能使用行模式来计算排序和聚合运算符中的结果。
所以,我的问题:
-
在使用用户定义的聚合函数时,用户是否有机会强制 SQL Server 使用哈希聚合运算符而不是流运算符?
在使用用户定义的聚合函数时,用户是否有机会使用排序运算符?
在使用用户定义的聚合函数时,用户是否有机会使用批处理模式?
【问题讨论】:
【参考方案1】:当数据库违反第一范式时,您将永远不会获得任何性能......因为这意味着没有关系数据库......并且关系引擎专门设计用于快速处理关系数据而不是非关系数据。
这不是 UDA 性能的问题,而是您的设计导致严重的计数器性能问题。
第一范式表示表中的列必须始终具有单个(标量)值。你放了一个违反第一范式的值列表。
只需通过添加子表重新设计数据库并将键和值放入该子表中即可获得性能!
你可以试试这个方法:
CREATE TABLE T_CHILD
(ID_CHILD INT IDENTITY PRIMARY KEY,
ID_ROW VARCHAR(32), --REFERENCES T_FATHER (ID_ROW),
KEY_CHILD VARCHAR(16),
VALUE_CHILD INT);
GO
INSERT INTO T_CHILD
SELECT ID_ROW, k.value, V_EMP.value
FROM T_FATHER
OUTER APPLY STRING_SPLIT([key], '/') AS k
OUTER APPLY STRING_SPLIT([value], '/') AS v;
【讨论】:
感谢您的回复和建议。我们正在使用列存储索引,所以也许我不需要子表来执行此操作。但问题是,如果我们想在这个表中添加更多的键和值,我们将需要更多的管理工作,而不仅仅是将键和值存储在一个列表中(这样,我们只需要将键和值添加到列表)。 列存储索引与值列表无关,永远不会从非关系方法创建关系数据库。您必须重新设计表结构。这是唯一的方法! 你的建议很有道理,但可扩展性也是我们非常关心的一个指标,这也是我们选择使用列表的原因。 可扩展性有限,因为您无法拥有没有限制的 SQL 数据。 CHAR/NCHAR 限制为 8000,NCHAR(NVARCHAR 限制为 4000...而表中的行数没有限制!并考虑通过分区的可扩展性,这对于您的错误方式来说并不容易... @othree 关于您对可扩展性的担忧:请记住您工作的环境。 RDBMS 经过高度调整以高效处理——读取、写入、排序、比较、连接等 —离散值,行数,数百万,甚至数十亿行。字符串解析虽然可以在必要时完成,但并不是 RDBMS 的主要关注点。如果您的目标是可扩展性,那么您需要发挥您正在使用的系统的优势。因此,SQLpro 的建议。现代 RDBMS 可以扩展超出许多开发人员的预期,但仅限用作 RDBMS,而不是应用程序代码。以上是关于如何在使用 SQLCLR 用户定义的聚合函数 (UDA) 时提高查询性能的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spark SQL 中定义和使用用户定义的聚合函数?