使用 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 的最快方法是什么
- 查询将需要 return 超过 2M+ 行!
- 由 ScheduleId
编辑 return
- return "data" 列
Value
节点切碎成列
- 并包括每行的 id 字段 archiveid 和 scheduleid returned
- 每个 ScheduleId "Value" 元素的数量是可变的,但对于给定的 ScheduleId
总是相同的
<Data>
节点中只有 <Value>
个节点,它们总是数字
- table 最多可以有 5000 万行,目前仅在 ScheduleId(Non-Unique Non-Clustered) 和 ArchiveId(PK Clustered)
上建立索引
理想情况下,我正在寻找格式的数据;
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。
- 所有 ScheduleId=1 都有 3 个值元素(time_X、var1_Y、var2_Y)
- 所有 ScheduleId=2 都有 2 个值元素(time_X、var1_Y)
- 所有ScheduleId=3都有33个值元素(time_X、var1_Y、……)
等等
如果您可以向 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
)。然后,您的图表报告可以 运行 关闭仅包含您需要的数据点的数据透视查询。因为不会进行字符串解析,而且所有的值都是数字,所以应该很快。
给定一个 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 的最快方法是什么
- 查询将需要 return 超过 2M+ 行!
- 由 ScheduleId 编辑 return
- return "data" 列
Value
节点切碎成列 - 并包括每行的 id 字段 archiveid 和 scheduleid returned
- 每个 ScheduleId "Value" 元素的数量是可变的,但对于给定的 ScheduleId 总是相同的
<Data>
节点中只有<Value>
个节点,它们总是数字- table 最多可以有 5000 万行,目前仅在 ScheduleId(Non-Unique Non-Clustered) 和 ArchiveId(PK Clustered) 上建立索引
理想情况下,我正在寻找格式的数据;
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。
- 所有 ScheduleId=1 都有 3 个值元素(time_X、var1_Y、var2_Y)
- 所有 ScheduleId=2 都有 2 个值元素(time_X、var1_Y)
- 所有ScheduleId=3都有33个值元素(time_X、var1_Y、……) 等等
如果您可以向 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
)。然后,您的图表报告可以 运行 关闭仅包含您需要的数据点的数据透视查询。因为不会进行字符串解析,而且所有的值都是数字,所以应该很快。