52

我正在尝试使用来自 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)
4

6 回答 6

51

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

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)
于 2013-11-12T22:29:11.227 回答
35

尝试这个!
query() 然后 value()
在 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)

到目前为止,它是更简化的查询。
看看它是否有效

于 2014-03-28T11:14:01.727 回答
23

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)
于 2014-03-31T14:04:23.027 回答
14
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)
于 2013-11-29T20:18:25.947 回答
9

遇到类似的问题,发现如果您在 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

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

于 2018-02-27T19:05:03.333 回答
5

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

根据 Microsoft 的SQL 文档

在以下示例中,XML 实例存储在 xml 类型的变量中。value() 方法从 XML 中检索 ProductID 属性值。然后将该值分配给一个 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 和静态类型。

然后按照该链接将我们带到:

如前所述,类型推断经常推断出比用户所知道的正在传递的数据类型更广泛的类型。在这些情况下,用户必须重写查询。一些典型案例包括:

...

  • 该类型推断出比数据实际包含的基数更高的基数。这种情况经常发生,因为xml 数据类型可以
    包含多个顶级元素
    ,而 XML 模式集合无法限制这一点。为了减少静态类型并
    保证确实最多传递一个值,您
    应该使用位置谓词[1]
    。例如,要将顶级 a
    元素下的元素 b 的属性 c 的值加 1,则必须编写(/a/b/@c)[1]+1. 此外,DOCUMENT
    关键字可以与 XML 模式集合一起使用。
于 2019-08-14T12:59:52.243 回答