存储具有可变列数的大型SQL数据集

时间:2015-05-07 22:44:31

标签: sql sql-server

在America's Cup游艇中,我们生成大型数据集,其中在每个时间戳(例如100Hz)我们需要存储100-1000个通道的传感器数据(例如速度,负载,压力)。我们将其存储在MS SQL Server中,并且需要能够检索数据的通道子集以进行分析,并执行查询,例如测试中特定传感器的最大压力,或整个赛季。

要存储的通道集对于数千个时间戳保持不变,但是随着新传感器的添加,重命名等等,日常会发生变化......并且取决于测试,比赛或模拟,频道数量可能差异很大。

构建SQL表的教科书方式可能是:

选项1

ChannelNames
+-----------+-------------+
| ChannelID | ChannelName |
+-----------+-------------+
| 50        | Pressure    |
| 51        | Speed       |
| ...       | ...         |
+-----------+-------------+

Sessions
+-----------+---------------+-------+----------+
| SessionID |   Location    | Boat  | Helmsman |
+-----------+---------------+-------+----------+
| 789       | San Francisco | BoatA |  SailorA |
| 790       | San Francisco | BoatB |  SailorB |
| ...       | ...           | ...   |          |
+-----------+---------------+-------+----------+

SessionTimestamps
+-------------+-------------+------------------------+
| SessionID   | TimestampID | DateTime               |
+-------------+-------------+------------------------+
| 789         |       12345 | 2013/08/17 10:30:00:00 |
| 789         |       12346 | 2013/08/17 10:30:00:01 |
| ...         |       ...   | ...                    |
+-------------+-------------+------------------------+

ChannelData
+-------------+-----------+-----------+
| TimestampID | ChannelID | DataValue |
+-------------+-----------+-----------+
| 12345       | 50        | 1015.23   |
| 12345       | 51        | 12.23     |
| ...         | ...       | ...       |
+-------------+-----------+-----------+

这种结构整洁但效率低下。每个DataValue需要三个存储字段,每个时间戳我们需要插入100-1000行。

如果我们总是拥有相同的频道,那么每个时间戳和结构使用一行更为明智:

选项2

+-----------+------------------------+----------+-------+----------+--------+-----+
| SessionID | DateTime               | Pressure | Speed | LoadPt   | LoadSb | ... |
+-----------+------------------------+----------+-------+----------+--------+-----+
| 789       | 2013/08/17 10:30:00:00 | 1015.23  | 12.23 | 101.12   | 98.23  | ... |
| 789       | 2013/08/17 10:30:00:01 | 1012.51  | 12.44 | 100.33   | 96.82  | ... |
| ...       | ...                    | ...      |       |          |        |     |
+-----------+------------------------+----------+-------+----------+--------+-----+

然而,频道每天都在变化,并且在几个月内,列数会增长和增长,大多数单元格都会变空。我们可以为每个新的Session创建一个新表,但是使用表名作为变量感觉不对,并且最终会导致数万个表 - 而且,在一个季节中查询变得非常困难,数据存储在多个表中。

另一种选择是:

选项3

+-----------+------------------------+----------+----------+----------+----------+-----+
| SessionID | DateTime               | Channel1 | Channel2 | Channel3 | Channel4 | ... |
+-----------+------------------------+----------+----------+----------+----------+-----+
| 789       | 2013/08/17 10:30:00:00 | 1015.23  |    12.23 | 101.12   | 98.23    | ... |
| 789       | 2013/08/17 10:30:00:01 | 1012.51  |    12.44 | 100.33   | 96.82    | ... |
| ...       | ...                    | ...      |          |          |          |     |
+-----------+------------------------+----------+----------+----------+----------+-----+

从Channel列ID到通道名称的查找 - 但是这需要EXEC或eval来执行预构造的查询以获得我们想要的通道 - 因为SQL不是设计为将列名称作为变量。从好的方面来说,我们可以在频道改变时重新使用列,但仍然会有很多空单元格,因为表格必须与我们遇到的最大数量的频道一样宽。使用SPARSE表可能对此有所帮助,但我对上面的EXEC / eval问题感到不舒服。

这个问题的正确解决方案是什么,可以实现存储,插入和查询的效率?

2 个答案:

答案 0 :(得分:2)

我会选择选项1。

首先是数据完整性,优化(如果需要) - 第二。

其他选项最终会有很多NULL值,而其他问题则源于未正常化。管理数据和进行有效查询将很困难。

此外,桌子可以有一个limit on the number of columns - 1024,所以如果你有1000个传感器/通道,你已经危险地接近极限了。即使你使表wide table(允许30,000列),表中行的大小仍有限制 - 每行8,060字节。并且有一定的performance considerations

在这种情况下,我不会使用宽表,即使我确定每行的数据永远不会超过8060字节,并且不断增加的通道数永远不会超过30,000。

我没有看到在选项1中插入100 - 1000行而在其他选项中插入1行时出现问题。要做到这一点INSERT有效地做出1000个单独的INSERT声明,请批量处理。在我系统的不同位置,我使用以下两种方法:

1)构建一个长INSERT语句

INSERT INTO ChannelData (TimestampID, ChannelID, DataValue) VALUES
(12345, 50, 1015.23),
(12345, 51, 12.23),
...
(), (), (), (), ........... ();

包含1000行并在一个事务中以普通INSERT执行,而不是1000个事务(检查syntax details)。

2)拥有一个接受table-valued parameter的存储过程。调用此类过程将1000行作为表传递。

CREATE TYPE [dbo].[ChannelDataTableType] AS TABLE(
    [TimestampID] [int] NOT NULL,
    [ChannelID] [int] NOT NULL,
    [DataValue] [float] NOT NULL
)
GO

CREATE PROCEDURE [dbo].[InsertChannelData]
    -- Add the parameters for the stored procedure here
    @ParamRows dbo.ChannelDataTableType READONLY
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    BEGIN TRANSACTION;
    BEGIN TRY

        INSERT INTO [dbo].[ChannelData]
            ([TimestampID],
            [ChannelID],
            [DataValue])
        SELECT
            TT.[TimestampID]
            ,TT.[ChannelID]
            ,TT.[DataValue]
        FROM
            @ParamRows AS TT
        ;

        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
    END CATCH;

END
GO

如果可能,在插入之前累积来自多个时间戳的数据以使批量更大。您应该尝试使用您的系统并找到批次的最佳大小。我使用存储过程批量约10K行。

如果你的数据来自传感器每秒100次,那么我首先会将传入的原始数据转储到一些非常简单的CSV文件中,并且有一个并行的后台进程,可以将它以块的形式插入到数据库中。换句话说,为传入数据设置一些缓冲区,这样如果服务器无法处理传入的卷,您就不会丢失数据。

根据您的评论,当您说某些频道可能更有趣并且多次查询时,而其他频道则不那么有趣,这里有一个我会考虑的优化。除了为所有频道设置一个表ChannelData之外,还有另一个表InterestingChannelDataChannelData将拥有整套数据,以防万一。 InterestingChannelData只有最有趣的频道的子集。它应该小得多,查询它应该花费更少的时间。无论如何,这是在正确规范化结构之上构建的优化(非规范化/数据复制)。

答案 1 :(得分:1)

你的流程是这样的:

  1. 白天生成数据
  2. 之后分析数据
  3. 如果这些是单独的活动,那么您可能需要考虑使用不同的“插入”和“选择”模式。您可以创建一个快速插入船上的模式,然后批量上传此数据到分析优化模式。这需要转换步骤(例如,您将通用列名称映射到有用的列名称)

    这与数据仓库和数据集市一致。在这种设计中,您可以批量加载和优化模式以进行报告。您当前的每日上传是否有很多窗口?

相关问题