【问题标题】:SQL query from XML Column stored as nvarchar来自存储为 nvarchar 的 XML 列的 SQL 查询
【发布时间】:2021-08-24 13:05:23
【问题描述】:

我需要有关 sql 查询的帮助。我有一个带有列的表,其中将 xml 存储为 nvarchar。

XmlCurves
<?xml version="1.0" encoding="Windows-1252"?>

所有的 XML 看起来像:

   <?xml version="1.0" encoding="Windows-1252"?>
<Curve xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Anchor>Begin</Anchor>
  <LoadOnlyNOKCurves>false</LoadOnlyNOKCurves>
  <Teachmode>false</Teachmode>
  <ActualCurve>
    <FloatPoints>0;4.965425|0.01004791;5.438642|0.01076508;5.50529|0.01112366;5.485298</FloatPoints>
  </ActualCurve>
  <AbsOffset>
    <X>88.21842</X>
    <Y>0</Y>
  </AbsOffset>
  <UpperLimit>
    <FloatPoints>9.456265E-05;5.023251|0.008893617;5.502031</FloatPoints>
  </UpperLimit>
  <LowerLimit>
    <FloatPoints>0.0008510638;4.906321|0.01408511;5.495711</FloatPoints>
  </LowerLimit>
  <ULViolationCurveIdx>-1</ULViolationCurveIdx>
  <ULViolationULIdx>-1</ULViolationULIdx>
  <LLViolationCurveIdx>-1</LLViolationCurveIdx>
  <LLViolationLLIdx>-1</LLViolationLLIdx>
  <SIO_X>
    <Id>1</Id>
    <Alias>Position</Alias>
    <Unit>mm</Unit>
  </SIO_X>
  <SIO_Y>
    <Id>2</Id>
    <Alias>Force</Alias>
    <Unit>kN</Unit>
  </SIO_Y>
</Curve>

我需要构建 SQL 查询,它在第一个标签 FloatPoints 中选择数据:&lt;FloatPoints&gt;0;4.965425|0.01004791;5.438642|0.01076508;5.50529|0.01112366;5.485298&lt;/Floatpoint&gt;

解释:

<FloatPoints>x1;y1|x2;y2|x3;y3|x4;y4</FloatPoints>

因此,我想要这样的结果:

x1 y1 x2 y2 x3 y3 x4 y4
0 4.965425 0.01004791 5.438642 0.01076508 5.50529 0.01112366 5.485298

如果可能的话。

【问题讨论】:

  • “在哪里存储 xml 作为 nvarchar。” 当有 xml 数据类型时,为什么要将 XML 存储在 nvarchar 中。
  • 另外,您真的只想要 8 列,其中包含 varchar'number' 吗?如果是这样,我不明白你为什么需要阅读 XML 这样做,你可以这样做 SELECT 'number' AS x1, 'number' AS y1, ..., 'number' AS x4, 'number' AS y4;
  • @Larnu 这是由于该数据来自生成数据的软件。不幸的是,我无法影响此属性。最大使用集成服务,但是解析还是会有问题。我需要给定标记(FloatPoints)中该列(XmlCurves)的给定值。我不在乎哪条路。但是这些值只存储在此列中,即 XML。但它被保留为 nvarchar :-( 我需要给定标记 (FloatPoints) 中该列 (XmlCurves) 中的给定值。我不在乎哪种方式。
  • 那么您的实际预期结果是什么?
  • @Charlieface db<>fiddle

标签: sql sql-server xml


【解决方案1】:

这个答案有几个假设:

  • XML 始终是有效的xml 值。
  • xy 坐标总是有 4 个
  • x 和 y 值的长度不能超过 127 个字符。
  • 节点ActualCurve中想要的值
  • 您使用的版本支持TRANSLATE(如果不支持,请使用嵌套的REPLACEs)

有了这个假设,您可以执行以下操作:

  1. nvarchar 转换为有效的xml
  2. 提取Curve/ActualCurve/FloatPoints的值
  3. 替换字符以创建一个可以使用PARSENAME将单独的部分分隔成的值
  4. 使用CHARINDEX 获取字符串的正确左/右部分
  5. CONVERTfloat
DECLARE @XMLLikeString nvarchar(MAX) = '<?xml version="1.0" encoding="Windows-1252"?>
<Curve xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Anchor>Begin</Anchor>
  <LoadOnlyNOKCurves>false</LoadOnlyNOKCurves>
  <Teachmode>false</Teachmode>
  <ActualCurve>
    <FloatPoints>0;4.965425|0.01004791;5.438642|0.01076508;5.50529|0.01112366;5.485298</FloatPoints>
  </ActualCurve>
  <AbsOffset>
    <X>88.21842</X>
    <Y>0</Y>
  </AbsOffset>
  <UpperLimit>
    <FloatPoints>9.456265E-05;5.023251|0.008893617;5.502031</FloatPoints>
  </UpperLimit>
  <LowerLimit>
    <FloatPoints>0.0008510638;4.906321|0.01408511;5.495711</FloatPoints>
  </LowerLimit>
  <ULViolationCurveIdx>-1</ULViolationCurveIdx>
  <ULViolationULIdx>-1</ULViolationULIdx>
  <LLViolationCurveIdx>-1</LLViolationCurveIdx>
  <LLViolationLLIdx>-1</LLViolationLLIdx>
  <SIO_X>
    <Id>1</Id>
    <Alias>Position</Alias>
    <Unit>mm</Unit>
  </SIO_X>
  <SIO_Y>
    <Id>2</Id>
    <Alias>Force</Alias>
    <Unit>kN</Unit>
  </SIO_Y>
</Curve>';

SELECT CONVERT(float,LEFT(xy1,CHARINDEX(';',xy1)-1)) AS x1, 
       CONVERT(float,STUFF(xy1,1,CHARINDEX(';',xy1),'')) AS y1,
       CONVERT(float,LEFT(xy2,CHARINDEX(';',xy2)-1)) AS x2, 
       CONVERT(float,STUFF(xy2,1,CHARINDEX(';',xy2),'')) AS y2,
       CONVERT(float,LEFT(xy3,CHARINDEX(';',xy3)-1)) AS x3, 
       CONVERT(float,STUFF(xy3,1,CHARINDEX(';',xy3),'')) AS y3,
       CONVERT(float,LEFT(xy4,CHARINDEX(';',xy4)-1)) AS x4, 
       CONVERT(float,STUFF(xy4,1,CHARINDEX(';',xy4),'')) AS y4
FROM (VALUES(@XMLLikeString))V(XMLLikeString)
     CROSS APPLY (VALUES(TRY_CONVERT(xml,CONVERT(varchar(MAX),V.XMLLikeString))))TC(XML)
     CROSS APPLY (VALUES(TC.XML.value('(Curve/ActualCurve/FloatPoints/text())[1]','varchar(4000)')))AC(FloatPoints)
     CROSS APPLY (VALUES(TRANSLATE(AC.FloatPoints,'.|',',.')))T(FloatPoints)
     CROSS APPLY (VALUES(REPLACE(PARSENAME(T.FloatPoints,4),',','.'),REPLACE(PARSENAME(T.FloatPoints,3),',','.'),REPLACE(PARSENAME(T.FloatPoints,2),',','.'),REPLACE(PARSENAME(T.FloatPoints,1),',','.')))PN(xy1,xy2,xy3,xy4);

db<>fiddle

【讨论】:

    【解决方案2】:

    撇开编码不谈,问题是在解析 FloatPoints 标记时保留项目位置。不幸的是,string_split 不能保证保留项目位置,因此您可能需要一个自定义拆分器(此处为 DelimitedSplit8K_LEAD)来返回项目位置。

    select id,
       Max(case t2.itemnumber*100 + t3.itemnumber when 101 then cast(t3.Item as decimal(10,6)) end) x1,
       Max(case t2.itemnumber*100 + t3.itemnumber when 102 then cast(t3.Item as decimal(10,6)) end) y1,
       Max(case t2.itemnumber*100 + t3.itemnumber when 201 then cast(t3.Item as decimal(10,6)) end) x2,
       Max(case t2.itemnumber*100 + t3.itemnumber when 202 then cast(t3.Item as decimal(10,6)) end) y2
    ...
    from (select id, cast(txn_message as xml) x
          from tbl) a
    cross apply a.x.nodes('Curve/ActualCurve/FloatPoints') t(n)
    cross apply DelimitedSplit8K_LEAD(t.n.value('.[1]', 'varchar(200)') , '|') t2
    cross apply DelimitedSplit8K_LEAD(t2.Item, ';') t3
    group by id;
    

    db<>fiddle

    包括DelimitedSplit8K_LEAD

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-11
      • 2020-09-22
      • 1970-01-01
      • 2011-04-16
      • 2016-12-29
      • 2016-10-18
      • 1970-01-01
      相关资源
      最近更新 更多