【问题标题】:XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'XQuery [value()]: 'value()' 需要一个单例(或空序列),找到类型为 'xdt:untypedAtomic *' 的操作数
【发布时间】:2013-11-12 21:46:53
【问题描述】:

我正在尝试使用来自 XML 的选择将行插入到表中。我想我很接近了。我哪里错了?

declare @xmldata xml;
set @xmldata = '<Database>
                  <PurchaseDetails>
                    <PurchaseDetail>
                      <Upc>72594206916</Upc>
                      <Quantity>77</Quantity>
                      <PurchaseDate>9/2010</PurchaseDate>
                      <PurchaseCity>Dallas</PurchaseCity>
                      <PurchaseState>TX</PurchaseState>
                    </PurchaseDetail>
                    <PurchaseDetail>
                      <Upc>72594221854</Upc>
                      <Quantity>33</Quantity>
                      <PurchaseDate>12/2013</PurchaseDate>
                      <PurchaseCity>Nashville</PurchaseCity>
                      <PurchaseState>TN</PurchaseState>
                    </PurchaseDetail>
                  </PurchaseDetails>
                </Database>'

insert into PurchaseDetails
(Upc, Quantity, PurchaseDate, PurchaseCity, PurchaseState)
select
    x.Rec.value('Upc','char(11)'),
    x.Rec.value('Quantity','int'),
    x.Rec.value('PurchaseDate','varchar(7)'),
    x.Rec.value('PurchaseCity','varchar(50)'),
    x.Rec.value('PurchaseState','char(2)')
from @xmlData.nodes('//Database/PurchaseDetails/PurchaseDetail') as x(Rec)

【问题讨论】:

    标签: xml tsql xpath


    【解决方案1】:

    一位同事之前曾解决过类似的问题。这是我们想出的。不直观!

    insert into PurchaseDetails
    (Upc, Quantity, PurchaseDate, PurchaseCity, PurchaseState)
    select
        pd.value('Upc[1]','char(11)'),
        pd.value('Quantity[1]','int'),
        pd.value('PurchaseDate[1]','varchar(7)'),
        pd.value('PurchaseCity[1]','varchar(50)'),
        pd.value('PurchaseState[1]','char(2)')
    from @xmlData.nodes('//Database/PurchaseDetails') as x(Rec)
    cross apply @xmlData.nodes('//Database/PurchaseDetails/PurchaseDetail') as i(pd)
    

    【讨论】:

    • 完美运行;知道 [1] 背后的原因是什么吗?
    • @jvenema,问题背后的原因是 XQuery 总是返回一个集合,如果你不使用 [] 在它的末尾。因此,在您的情况下 pd.Value('sth') 返回一个集合,尽管 From 子句中的节点函数仅将一行传递给 Value 函数顺便说一句,Mikel 的答案也是正确的,而且更重要。
    • 知道交叉应用在做什么吗? x(Rec) 未在查询中引用,但取出该部分使其不起作用。
    • 这出奇地难找,效果很好..我不明白它是如何处理未知数量的记录的,它是递归的吗?
    【解决方案2】:

    试试这个!
    查询()然后值()
    在 SQL Server 中运行它并 100% 工作
    先放一个点 (.),然后是子标签。
    PurchaseDetail 标签存在 2 次,因此点 (.) 替换了第一个和第二个标签。
    点可以防止在 XQuery 上使用 [1]。
    圆点代表第一个和第二个 PurchaseDetail 标签。

    INSERT INTO PurchaseDetails(Upc, Quantity, PurchaseDate, PurchaseCity, PurchaseState)
    SELECT col.query('./Upc').value('.', 'char(11)'),
        col.query('./Quantity').value('.', 'int'),
        col.query('./PurchaseDate').value('.', 'varchar(7)'),
        col.query('./PurchaseCity').value('.', 'varchar(50)'),
        col.query('./PurchaseState').value('.', 'char(2)')
    FROM @xmlData.nodes('/Database/PurchaseDetails/PurchaseDetail') as ref(col)
    

    到目前为止,这是更简化的查询。
    看看有没有效果

    【讨论】:

      【解决方案3】:

      insert into PurchaseDetails(Upc, Quantity, PurchaseDate, PurchaseCity, PurchaseState)
      select T.X.value('(Upc/text())[1]', 'char(11)'),
             T.X.value('(Quantity/text())[1]', 'int'),
             T.X.value('(PurchaseDate/text())[1]', 'varchar(7)'),
             T.X.value('(PurchaseCity/text())[1]', 'varchar(50)'),
             T.X.value('(PurchaseState/text())[1]', 'char(2)')
      from @xmlData.nodes('/Database/PurchaseDetails/PurchaseDetail') as T(X)
      

      【讨论】:

      • 这是最优雅和最有效的解决方案,应该是公认的答案。
      【解决方案4】:
      select
          x.Rec.query('./Upc').value('.','char(11)')
          ,x.Rec.query('./Quantity').value('.','int')
          ,x.Rec.query('./PurchaseDate').value('.','varchar(7)')
          ,x.Rec.query('./PurchaseCity').value('.','varchar(50)')
          ,x.Rec.query('./PurchaseState').value('.','char(2)')
      from @xmlData.nodes('/Database/PurchaseDetails/PurchaseDetail') as x(Rec)
      

      【讨论】:

        【解决方案5】:

        遇到类似问题,发现如果您在 XQuery 中引用的 xml 中有额外的嵌套层,@birdus 的答案不起作用,例如假设有一个稍微不同的 XML 形状,如果你有

        T.x.value('PurchasePlace/PurchaseCity[1]','varchar(50)')
        

        你仍然会得到单例错误。尽管@birdus 的解决方案确实适用于这种特定情况,但结合了@birdus 和@Mikael-Eriksson 的最佳解决方案的更普遍适用的解决方案是:

        insert into PurchaseDetails(Upc, Quantity, PurchaseDate, PurchaseCity, PurchaseState)
        select T.X.value('(Upc)[1]', 'char(11)'),
        T.X.value('(Quantity)[1]', 'int'),
        T.X.value('(PurchaseDate)[1]', 'varchar(7)'),
        T.X.value('(PurchaseCity)[1]', 'varchar(50)'),
        T.X.value('(PurchaseState)[1]', 'char(2)')
        from @xmlData.nodes('/Database/PurchaseDetails/PurchaseDetail') as T(X)
        

        此组合的@birdus 省略了/text(),这是多余的,但在元素选择器周围添加了@Mikael-Eriksson 的括号,以允许多个元素选择器,如我的修改示例中所示:

        T.x.value('(PurchasePlace/PurchaseCity)[1]','varchar(50)')
        

        其中的原因,一些人问过,不是@birdus 的版本在此处讨论的任何示例中返回的不是单例,而是它可能。每Microsoft Docs

        如果编译器无法确定运行时是否保证单例,则需要单例的位置步骤、函数参数和运算符将返回错误。

        【讨论】:

        • 我也遇到了类似的问题,但为了它的价值,我删除了/text(),它在我的数据集中造成了严重的时间滞后。使用/text(),将 55K 记录插入表中需要 25 秒。没有/text(),我在 53 秒时停止了查询,因为额外的时间不值得。
        • 用括号括起来 xpath 对我有用,然后是第一个实例属性: XmlPayLoad.value( '(/root/child/wantedvalue)[1]','varchar' )
        【解决方案6】:

        为了解决为什么在 XQuery 字符串文字中需要位置谓词(即[1])的问题,就像@pbz 所指出的那样,需要一个单例,因此必须保证。要为@pbz 的答案添加更多内容,请参见下文。

        根据微软的SQL Docs

        在以下示例中,XML 实例存储在 xml 类型的变量中。 value() 方法检索 ProductID 属性值 从 XML。然后将该值分配给一个 int 变量。

        DECLARE @myDoc xml  
        DECLARE @ProdID int  
        SET @myDoc = '<Root>  
        <ProductDescription ProductID="1" ProductName="Road Bike">  
        <Features>  
          <Warranty>1 year parts and labor</Warranty>  
          <Maintenance>3 year parts and labor extended maintenance is available</Maintenance>  
        </Features>  
        </ProductDescription>  
        </Root>'  
        
        SET @ProdID =  @myDoc.value('(/Root/ProductDescription/@ProductID)[1]', 'int' )  
        SELECT @ProdID  
        

        值 1 作为结果返回。

        虽然 XML 实例中只有一个 ProductID 属性,静态类型规则要求您明确指定路径表达式返回单例。因此,额外的[1] 在路径表达式的末尾指定。 更多信息 关于静态类型,见XQuery and Static Typing

        点击该链接,我们将前往:

        如前所述,类型推断经常推断出一个类型是 比用户了解的数据类型更广泛 被通过。在这些情况下,用户必须重写查询。 有些 典型案例包括:

        ...

        • 类型推断出比数据实际包含的基数更高。这种情况经常发生,因为xml数据类型可以
          包含多个顶级元素
          ,以及一个 XML 模式集合 不能限制这一点。为了减少静态类型并
          保证确实最多有一个值被传递,您
          应该使用位置谓词[1]
          。例如,将 1 添加到 顶级a下元素b的属性c的值
          元素,你必须写(/a/b/@c)[1]+1。此外,文档
          关键字可以与 XML 模式集合一起使用。

        【讨论】:

          猜你喜欢
          • 2020-01-31
          • 2010-11-21
          • 2020-05-11
          • 2020-07-03
          • 2021-06-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-10
          • 1970-01-01
          相关资源
          最近更新 更多