使用 shredded/flattened xml 字段更快地选择大型 table

Faster selects on large table with shredded/flattened xml field

给定一个 MS SQL EXPRESS 2008 R2 Table 有如下记录

ArchiveId   ScheduleId  Data
391063      62          <Data>....</Data>
391064      62          <Data>....</Data>
391065      63          <Data>....</Data>

数据字段中的 XML 结构如下

<Data>
  <Value>1.0</Value>
  <Value>2.0</Value>
  <Value>3.0</Value>
  <Value>4.0</Value>
</Data>

完成 select 的最快方法是什么

理想情况下,我正在寻找格式的数据;

ArchiveId   ScheduleId  Value1  Value2  etc
391063      62          1.0     2.0
391064      62          1.1     2.1

我试过用

select 
    ArchiveId, 
    ScheduleId , 
    v.value('(Value/text())[1]', 'float') as value1 , 
    v.value('(Value/text())[2]', 'float') as value2 , 
    v.value('(Value/text())[3]', 'float') as value3 , 
    v.value('(Value/text())[4]', 'float') as value4  
from 
    Archives 
    cross apply [data].nodes('//Data') x(v) 
where 
    ScheduleId = 2499

和直接 .values() 和 .queries()

select 
    ArchiveId,
    ScheduleId,
    Data.value('(/Data/Value/text())[1]', 'float') as value1,
    Data.value('(/Data/Value/text())[2]', 'float') as value2,
    Data.value('(/Data/Value/text())[3]', 'float') as value3,
    Data.value('(/Data/Value/text())[4]', 'float') as value4
from 
    Archives 
where 
    ScheduleId = 2499
order by 
    ArchiveId asc

两者都有效,但在大型数据集上确实很慢,我想知道是否有更快的方法在非常大的行上执行此类操作。我意识到无论做什么都需要一段时间,但这样做时我最好的选择是什么。

此处有许多示例,但它们都具有更复杂或动态的数据结构,或者具有某种基于 xml 内容本身的复杂 selection 要求。

我拥有的数据始终是相同的结构(一个数据节点和 x 值节点)并且 selection 标准 不是 xml 根本。

我只是在寻找最简单的方法来拉回大量记录,同时将 xml 展平为列。

编辑: 本质上,我们将图形数据存储在 xml 中,以便稍后绘制折线图。重要的是,虽然相同 ScheduleId 的元素数量始终相同,但不同的 ScheduleId 将具有不同数量的值 elements.e.g。

如果您可以向 Archives table 添加字段,则可以创建持久计算字段作为 XML 数据的函数。因此,例如,您创建一个字段 value1 并将其设置为等于 Data.value('(/Data/Value/text())[1]', 'float'),然后在该列上设置持久化标志。这样,当添加或更新记录时,它将被解析一次,然后它有自己的数据字段,您可以 select 输出。

尽管不能直接使用 XML 方法,但您必须使用 udf 才能付诸实践。但它看起来像这样:

GO
create table TempArchive
(
  ArchiveId integer not null,
  ScheduleId integer not null,
  [Data] xml not null,
  CONSTRAINT PK_Archive
  PRIMARY KEY CLUSTERED (ArchiveId)
  WITH (IGNORE_DUP_KEY = OFF)
)
GO
create function udf_getdatacolumn
(
    @data xml,
    @index as int
) RETURNS float
with schemabinding
as
begin
return @data.value('(/Data/Value/text())[sql:variable("@index")][1]', 'float')
end
GO
alter table TempArchive add value1 as (dbo.udf_getdatacolumn(data, 1)) persisted
alter table TempArchive add value2 as (dbo.udf_getdatacolumn(data, 2)) persisted
alter table TempArchive add value3 as (dbo.udf_getdatacolumn(data, 3)) persisted
alter table TempArchive add value4 as (dbo.udf_getdatacolumn(data, 4)) persisted
GO
insert into TempArchive values (1, 2, '<Data>
  <Value>1.0</Value>
  <Value>2.0</Value>
  <Value>3.0</Value>
  <Value>4.0</Value>
</Data>')
GO
select ArchiveId, ScheduleId, Value1, Value2, Value3, Value4 
from TempArchive
GO

Returns:

ArchiveId   ScheduleId  Value1     Value2     Value3     Value4
----------- ----------- ---------- ---------- ---------- ----------
1           2           1          2          3          4

(1 row(s) affected)

请记住,对于大量数据,首次添加这些计算列时会花费很长时间。我建议在投入生产之前对其进行测试。它还会增加 table.

的大小

最好的解决方案可能是允许您的系统将具有 XML 列的数据按原样添加到存档 table 中,然后按计划将数据移至用于报告的标准化 table 结构。您可以设置 SQL 代理作业或创建一些服务程序以将数据移动或复制到您的报告数据库。一旦数据出现在您的报告 table 中,您可以:

  • 清除存档中的记录table
  • 将存档 table 记录移动到具有相同结构的另一个 table/database。
  • 创建一个在记录添加到报告时标记的字段 table/database。

您可以选择符合您要求的一个。

您的报告 table 基本上会分成两个或三个 table。 ArchiveTable 由 (ArchiveId, ScheduleId) 组成。然后你的 ArchiveDataPointTable 由 (ArchiveId, ValueId, DataPointValue) .如果您想要数据点的标签,您还可以创建一个 ValuesTable of (ValueId, ValueDescription)。然后,您的图表报告可以 运行 关闭仅包含您需要的数据点的数据透视查询。因为不会进行字符串解析,而且所有的值都是数字,所以应该很快。