SQL Server UDF 数组输入和输出

Posted

技术标签:

【中文标题】SQL Server UDF 数组输入和输出【英文标题】:SQL Server UDF array inputs and outputs 【发布时间】:2016-08-03 19:00:05 【问题描述】:

我有一组 CODE_1-10 列,其中包含诊断代码。我想创建一组变量 CODE_GROUP_1-17,它们指示某些特定诊断代码集中的一个是否与任何 CODE_1-10 变量匹配。例如,如果 CODE_1-10 中的任何一个匹配“123”或“456”,则 CODE_GROUP_1 = 1,如果 CODE_1-10 中的任何一个匹配“789”、“111”、“333”、“444”或“,则 CODE_GROUP_2 = 1富”。

这是一个使用值构造函数的示例。

    CASE WHEN (SELECT count(value.val)
       FROM    (VALUES (CODE_1)
                   ,   (CODE_2)
                   ,   (CODE_3)
                   ,   (CODE_4)
                   ,   (CODE_5)
                   ,   (CODE_6)
                   ,   (CODE_7)
                   ,   (CODE_8)
                   ,   (CODE_9)
                   ,   (CODE_10)
               ) AS value(val)
     WHERE value.val in ('123', '456')
   ) > 0 THEN 1 ELSE 0 END AS CODE_GROUP_1,

   CASE WHEN (SELECT count(value.val)
       FROM    (VALUES (CODE_1)
                   ,   (CODE_2)
                   ,   (CODE_3)
                   ,   (CODE_4)
                   ,   (CODE_5)
                   ,   (CODE_6)
                   ,   (CODE_7)
                   ,   (CODE_8)
                   ,   (CODE_9)
                   ,   (CODE_10)
               ) AS value(val)
     WHERE value.val in ('789','111','333','444','foo')
   ) > 0 THEN 1 ELSE 0 END AS CODE_GROUP_2

我想知道是否还有另一种更有效的方法。有没有办法制作一个接受 CODE_1-10 数组并输出一组列 CODE_GROUP_1-17 的 CLR UDF?

【问题讨论】:

SQL Server 中没有数组这样的东西。您是否考虑过表值参数 (TVP)?您可以从应用程序(例如 DataTable)中传入结构化数据,而无需进行任何混乱的解构/重建。 如何构建表值参数以包含一个包含 CODE_1-10 的小表,而无需复制粘贴“VALUES(...) ...”17 次不同的时间? 好吧,假设您已经有一个表类型并声明了一个名为 @tvp 的局部变量,您可以说:;WITH x AS (SELECT TOP (10) rn = ROW_NUMBER() OVER (ORDER BY number) FROM master.dbo.spt_values) INSERT @tvp(col) SELECT 'CODE_' + RTRIM(rn) FROM x; 如果您想查看它的作用,请在没有 @987654324 的情况下运行它@ 少量。我只提到了 TVP,因为它听起来就像您的值数组应该来自应用程序,并且您在这里对其进行硬编码以进行演示。 我会调查的。我的值数组是一个巨大的表中的列,名为 CODE_1-10。我想将此映射函数应用于每一行,并将结果输出到 select 语句中的列。 【参考方案1】:

你至少可以避免像这样重复FROM (VALUES ...)

SELECT
  CODE_GROUP_1 = COUNT(DISTINCT CASE WHEN val IN ('123', '456')                  THEN 1 END),
  CODE_GROUP_2 = COUNT(DISTINCT CASE WHEN val IN ('789','111','333','444','foo') THEN 1 END),
  ...
FROM
  (
    VALUES
      (CODE_1),
      (CODE_2),
      (CODE_3),
      (CODE_4),
      (CODE_5),
      (CODE_6),
      (CODE_7),
      (CODE_8),
      (CODE_9),
      (CODE_10)
  ) AS value(val)

如果CODE_1CODE_2等是列名,则可以在CROSS APPLY中将上述查询作为派生表使用:

SELECT
  ...
FROM
  dbo.atable  -- table containing CODE_1, CODE_2 etc.
  CROSS APPLY
  (
    SELECT ...  -- the above query
  ) AS x
;

【讨论】:

这不是我想要做的,而是一个更好的解决方案。谢谢! 顺便说一下,COUNT(DISTIINCT ... ) 这么多次实际上是我的查询时间的两倍多。当我改为使用交叉应用方法但将 COUNT(DISTINCT ...) 替换为 MAX(CASE ... ELSE 0 END) 时,查询时间从 21 分钟变为 30 秒。 @SeanOgden:MAX 确实是一个更好的替代品。我自己多次看到并使用过它,但昨晚我在写这个答案时从未想到过这个想法。谢谢提醒。【参考方案2】:

您可以创建 2 个新表,并将列附加为行吗?因此,如果您需要保留 1-10 值和 dx 代码以及您需要的任何关键字段,则一个表将是带有源列的 dxCode,另一个表将是带有 17 个组的 dxGroup,如果您是源 groupID需要它,以及您的目标 dx 值。 然后要确定哪些代码属于哪些组,您可以加入您的 dx 字段。

【讨论】:

如果我理解正确,您的解决方案将需要扩展原始表,每行输入数据最多包含 10 行。关键字段不小,而且很多,而且表很大,所以这个不太理想。 好吧,这个术语是“规范化”,但是是的,如果需要,您将拥有 PK 字段、sourceColumnID 字段以及一个或多个值字段。这个想法是在尝试确定哪些值在哪个集合中之前,将 10 列(或 17 个组)中的每一列中的各个非空值合并。 是的,我知道规范化,我只是不确定你在说什么,因为描述令人困惑。无论如何,就我而言,这个解决方案并不是最好的选择。感谢您的回复! 你试过了吗?这是数据库服务器经过优化以执行的。在大范围内,这是您唯一的选择。在小范围内,保持原样并不重要。

以上是关于SQL Server UDF 数组输入和输出的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Excel 数组公式传递给 VBA UDF?

BigQuery UDF Array<TIMESTAMP> 返回。无法强制输出值输入 TIMESTAMP

sql server udf,返回与输入表达式相同的类型

SQL Server用户自定义函数(UDF)

数组 UDF 不断重复单个值

用于 JSON 输出整数数组的 SQL Server 2016