【问题标题】:Create a sequence between multiple functions在多个函数之间创建序列
【发布时间】:2016-08-31 00:12:04
【问题描述】:

问题:如何在多个函数之间创建序列?

我有各种创建 xml 数据的函数,每个函数都可以创建多组“Party”节点。所有功能都从同一个父节点开始。我希望输出如下所示,其中每一方,无论它来自什么函数,都具有连续的序列号。期望的输出:

<PARTIES>
   <PARTY SequenceNumber="1" label="PARTY_1">
   ...
   <PARTY SequenceNumber="2" label="PARTY_2">
   ...
   <PARTY SequenceNumber="3" label="PARTY_3">
   ...
</PARTIES>

现在我正在通过一个返回 xml 的函数输出我的 xml,并且我想要排序的函数在 PARTIES 节点下分组在一起:

SELECT  [dbo].[GetFunction1Xml](@Id),
        [dbo].[GetFunction2Xml](@Id),
        [dbo].[GetFunction3Xml](@Id)
FOR XML PATH(''), ROOT('PARTIES'), TYPE

每个函数都从不同的地方收集信息,可能如下所示:

ALTER GetFunction1XML
...
RETURNS XML (
SELECT  [label] = 'PARTY_' + CONVERT(NVARCHAR,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)))  
        [Var1] = ....,
        [Var2] = ....,
FROM [Table]
FOR XML PATH('PARTY'), TYPE)
END;

我尝试使用序列,但它不允许在用户定义的函数中使用。

CREATE SEQUENCE Party_Seq
AS INTEGER
START WITH 1
INCREMENT BY 1
MINVALUE 1
NO CYCLE; 

我还在每个函数中尝试了以下操作,因为如果我要让两个参与方在同一个函数中通过 UNION ALL 连接,它就可以工作。但是每次都重新启动到 PARTY_1,因为所有各方都在不同的功能中。

SELECT  [@label] = 'PARTY_' + CONVERT(NVARCHAR,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)))

因此,例如,如果我用 1 个通用函数替换 2 个函数,它看起来像这样,它会正确打印出信息;但是我有太多的功能可以做到这一点。

ALTER GetGenericFunctionXML
...
RETURNS XML (

SELECT  [@seq] = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) 
        [@label] = 'PARTY_' + CONVERT(NVARCHAR,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)))  
        [Var1] = [food].[fruit],
        [Var2] = [food].[meat]

FROM ( SELECT 'Apple' AS [fruit],
              'Bacon' AS [meat]
        FROM [Table1]

       UNION ALL

       SELECT 'Grape',
              'Pork'
        FROM [Table2] 
     ) AS [food]

FOR XML PATH('PARTY'), TYPE)
END;

输出:

 <PARTIES>
   <PARTY SequenceNumber="1" label="PARTY_1">
     <Var1>Apple</Var1>
     <Var2>Bacon</Var2>
   <PARTY SequenceNumber="2" label="PARTY_2">
     <Var1>Grape</Var1>
     <Var2>Pork</Var2>
   <PARTY SequenceNumber="3" label="PARTY_3">
 </PARTIES>

我也尝试将参数传递给函数,但由于它们是函数,因此无法输出值(我相信只有存储过程可以做到这一点。如果我错了,请纠正我。)。

【问题讨论】:

  • 您应该从您的 UDF 中发布示例数据和代码。没有这些,就不清楚你在做什么。我不知道你的 ID 是如何获得 11、12 和 13 的。
  • @btberry 这些是示例值。我已将它们更改为上面的 1、2 和 3。这就是我想要实现但我无法实现的目标。
  • 您使用 UDF 有什么原因吗?像您在这里拥有的标量 UDF 将始终存在性能问题,我只是看不出您正在做什么使其成为可取或需要的。将该代码放入 cte 或子查询或其他内容中。如果出于某种原因您真的必须做 UDF,或者如果它只是一个有趣的难题,请解释一下。
  • @btberry 不,没有理由。我不知道它存在性能问题。我宁愿做性能更好、效果更好的事情。所以你是说如果我把所有的东西都包在一个 cte 里会更好?
  • @btberry 我不同意... multi-statement-table-valued-functionsAS BEGIN...END 的语法)存在严重的性能问题,但是返回一个 XML 的标量函数应该没问题。最要注意的是,调用没有完全内联,因此优化器可能找不到最佳执行计划。这取决于您为 XML 提取的数据类型...

标签: sql sql-server xml sequence user-defined-functions


【解决方案1】:

你可以用FLWOR解决这个问题

CREATE FUNCTION dbo.f1() RETURNS XML AS
BEGIN
    RETURN 
    '<PARTY label="PARTY_f1a">
          <Var1>f1a.1</Var1>
          <Var2>f1a.2</Var2>
     </PARTY>
     <PARTY label="PARTY_f1b">
          <Var1>f1b.1</Var1>
          <Var2>f1b.2</Var2>
     </PARTY>
     <PARTY label="PARTY_f1c">
          <Var1>f1c.1</Var1>
          <Var2>f1c.2</Var2>
     </PARTY>';
END
GO
CREATE FUNCTION dbo.f2() RETURNS XML AS
BEGIN
    RETURN 
    '<PARTY label="PARTY_f2a">
          <Var1>f2a.1</Var1>
          <Var2>f2a.2</Var2>
     </PARTY>
     <PARTY label="PARTY_f2b">
          <Var1>f2b.1</Var1>
          <Var2>f2b.2</Var2>
     </PARTY>';
END
GO

--查询从这里开始

WITH AllPartyNodes AS
(
    SELECT
        (
        SELECT dbo.f1()
              ,dbo.f2()
        FOR XML PATH(''),TYPE
        ) AS AllTogether
)
,NumberedSequences AS
(
    SELECT  ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS SequenceNr
           ,The.Party.query('.') AS TheNode
    FROM AllPartyNodes
    CROSS APPLY AllTogether.nodes('/PARTY') AS The(Party)
)
SELECT TheNode.query('let $p:=/PARTY[1]
                      let $lbl:=$p/@label
                      let $nr:=sql:column("SequenceNr")
                      return
                         <PARTY seq="{$nr}" label="{$lbl}" >
                         {$p/*}
                         </PARTY>'
                        ) AS [node()]
FROM NumberedSequences
FOR XML PATH(''),ROOT('PARTIES')

GO
DROP FUNCTION dbo.f1;
DROP FUNCTION dbo.f2;

更新另一种方法

您可以提取数据并重建它。

将其放在我的“NumberedSequence”CTE 下方

,TheData AS
(
    SELECT *
          ,TheNode.value('(PARTY/@label)[1]','nvarchar(max)') AS Label
          ,TheNode.query('PARTY/*') AS InnerNodes 
    FROM NumberedSequences
)
SELECT SequenceNr AS [@seq]
      ,Label AS [@label]
      ,InnerNodes AS [node()]
FROM TheData
FOR XML PATH('PARTY'),ROOT('PARTIES')

更新 2

与主查询相同

CREATE FUNCTION dbo.f1() RETURNS XML AS
BEGIN
    RETURN 
    '<PARTY label="PARTY_f1a">
          <Var1>f1a.1</Var1>
          <Var2>f1a.2</Var2>
     </PARTY>
     <PARTY label="PARTY_f1b">
          <Var1>f1b.1</Var1>
          <Var2>f1b.2</Var2>
     </PARTY>
     <PARTY label="PARTY_f1c">
          <Var1>f1c.1</Var1>
          <Var2>f1c.2</Var2>
     </PARTY>';
END
GO
CREATE FUNCTION dbo.f2() RETURNS XML AS
BEGIN
    RETURN 
    '<PARTY label="PARTY_f2a">
          <Var1>f2a.1</Var1>
          <Var2>f2a.2</Var2>
     </PARTY>
     <PARTY label="PARTY_f2b">
          <Var1>f2b.1</Var1>
          <Var2>f2b.2</Var2>
     </PARTY>';
END
GO

--The main query as function
CREATE FUNCTION dbo.f3() RETURNS XML AS
BEGIN
DECLARE @Result XML;

WITH AllPartyNodes AS
(
    SELECT
        (
        SELECT dbo.f1()
              ,dbo.f2()
        FOR XML PATH(''),TYPE
        ) AS AllTogether
)
,NumberedSequences AS
(
    SELECT  ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS SequenceNr
           ,The.Party.query('.') AS TheNode
    FROM AllPartyNodes
    CROSS APPLY AllTogether.nodes('/PARTY') AS The(Party)
)
SELECT @Result=
(
    SELECT TheNode.query('let $p:=/PARTY[1]
                          let $lbl:=$p/@label
                          let $nr:=sql:column("SequenceNr")
                          return
                             <PARTY seq="{$nr}" label="{$lbl}" >
                             {$p/*}
                             </PARTY>'
                            ) AS [node()]
    FROM NumberedSequences
    FOR XML PATH(''),ROOT('PARTIES'), TYPE
)
RETURN @Result;
END
GO

SELECT dbo.f3();
GO

DROP FUNCTION dbo.f1;
DROP FUNCTION dbo.f2;
DROP FUNCTION dbo.f3;

【讨论】:

  • 我尝试运行它,但出现了一些错误。在 NumberedSequences [AllTogether] 中是无效的对象名称。是不是因为超出了前面SELECT的范围?
  • @DRockClimber,您是否复制了全部内容、函数创建和查询?
  • 我在一个新的查询窗口中运行了您的查询并且它有效。然后我把它放在一个 Alter Function 中,它产生了很多错误。我认为这与此有关。我有Returns XML AS BEGIN RETURN (With AllPartyNodes AS...,这会在 With 语句中产生错误。 @Shnugo
  • 哇,谢谢它的工作!现在我只需要在此基础上再接再厉。嘿,我一直在寻找 $ 以及 := 运算符的作用,但除了您发布的链接之外,我似乎找不到它。我想了解那些做什么。你有任何其他文件吗?
  • 忘记在上面标记。 @Shnugo
猜你喜欢
  • 2023-01-14
  • 2015-05-20
  • 2017-09-29
  • 1970-01-01
  • 2021-12-21
  • 1970-01-01
  • 2015-05-26
  • 2013-01-31
  • 1970-01-01
相关资源
最近更新 更多