m__*_*m__ 4 sql-server sql-server-2012
为了清楚起见,在 2012-06-20 重写了这个问题,检查旧版本的编辑
我为某些设备上的几个传感器收集数据。我想在图形中以曲线的形式为用户显示这些数据,即我想绘制测量点并在它们之间画线。用户可以通过在图表中缩放和平移来决定他想要显示值的日期范围。
测量值的数量开始增加,此时我有许多值在500'000'000范围内,并且还在计数。对于单个传感器,到目前为止,我最多可以找到大约2'000'000 个值,但它很可能会增加。这些值不是以均匀间隔(即每秒)记录的,而是在可以测量更改时记录的(请参阅下面日期列中的差异)。
Date SensorValue
----- -----------
10 123
30 118
70 114
85 115
90 116
95 117
Run Code Online (Sandbox Code Playgroud)
由于此图形的显示区域为1000 像素,因此获取所有这些值毫无意义。相反,我尝试获取最多1000 个值,并将它们均匀分布在用户放大的日期范围内。确切地返回日期范围内的哪些值并不重要,只要它们在日期方面均匀分布(记住这一点很重要,因为它们不是以均匀间隔记录的)。
我将这些值存储在一个非常简单的表中,如下所示,只有一个(聚集)索引。请注意,Date
存储为整数(“unix 时间”)而不是datetime2
字段。该表按Date
是否重要进行分区。
CREATE TABLE [dbo].[SensorValues](
[DeviceId] [int] NOT NULL,
[SensorId] [int] NOT NULL,
[SensorValue] [int] NOT NULL,
[Date] [int] NOT NULL,
CONSTRAINT [PK_SensorValues] PRIMARY KEY CLUSTERED
(
[DeviceId] ASC,
[SensorId] ASC,
[Date] DESC
);
Run Code Online (Sandbox Code Playgroud)
我正在寻找两件事之一,要么是一个改进的查询,它可以更快地返回这些值,要么是一种关于如何提取这些值的不同方法。如果有什么不清楚的,请继续问我,我会尽力回答。
为了实现这一点,我计算了我正在获取数据的日期范围的时间差,并将其除以我最多想要获取的值的数量。我将其存储在@resolution
下面调用的变量中。
当我查询数据库时,它会按日期除以计算并分组匹配的行,@resolution
并为每组挑选一个值。我知道这不是一个万无一失的算法。在某些情况下,它返回的值比请求的多,但对我来说这并不重要,因为它至少将值的数量保持在可管理的水平。
对于具有少量数据(数万个值)的传感器,这相当快,但是当涉及具有大量数据(数百万行)的传感器时,它会变慢并需要几秒钟(至少)进行查询数据库。应该如何以更好的方式查询这些信息?
SELECT sv.SensorValue, sv.Date
FROM SensorValues sv
INNER JOIN (SELECT MAX(svInner.Date) AS svInner.Date
FROM SensorValues svInner
WHERE svInner.SensorId = @sensorid
AND svInner.DeviceId = @deviceid
AND svInner.Date BETWEEN @startdate AND @stopdate
AND svInner.Sensorvalue != -32767
AND svInner.SensorValue != -32768
GROUP BY svInner.Date / @resolution) j ON sv.Date = j.Date
WHERE sv.SensorId = @sensorid
AND sv.DeviceId = @deviceid
AND sv.SensorValue != -32767
AND sv.SensorValue != -32768
ORDER BY sv.Date DESC
Run Code Online (Sandbox Code Playgroud)
示例:如果我想从上面的表格示例中选择时间 0 到 95 之间的 3 个值,我会执行以下操作。
我计算@resolution
to(95-0)/3=31
然后对于时间 0 和 95 之间的每一行,计算Date/@resolution
(到处都是整数除法):
Date SensorValue Date/@resolution
----- ----------- ----------------
10 123 0
30 118 0
70 114 2
85 115 2
90 116 2
95 117 3
Run Code Online (Sandbox Code Playgroud)
然后我通过使用从每个组中选择一个值MAX(Date)
。
Date SensorValue Date/@resolution
----- ----------- ----------------
30 118 0
90 116 2
95 117 3
Run Code Online (Sandbox Code Playgroud)
从技术上讲,要显示第 n 个值,您可以使用诸如RANK
、DENSE_RANK
或 之类的排名函数ROW_NUMBER
。哪一个完全取决于,但你描述的最匹配ROW_NUMBER
:
with cte as (
select row_number() over (order by Date) as rn, *
from table)
where rn % 1000 = 0;
Run Code Online (Sandbox Code Playgroud)
但是您是说出于性能原因想要这样做,避免遍历数百万行。这样的查询已经造成了损害,它从磁盘读取所有行并必须对它们进行排序,因此性能代价已经支付。
选择随机数据样本的更好替代方法是使用TABLESAMPLE
子句,请参阅使用 TABLESAMPLE 限制结果集:
select *
from table tablesample (100 rows);
Run Code Online (Sandbox Code Playgroud)
TABLESAMPLE 会更高效,因为它实际上避免了读取所有数据,它只对表中的一些页面进行采样并返回采样页面中的所有行。
但请考虑,如果您使用 TABLESAMPLE,您的 WHERE 子句将在采样后应用。因此,样本可能不包含您感兴趣的设备/传感器的任何行。对于具有小数据的传感器尤其如此。
归档时间: |
|
查看次数: |
9017 次 |
最近记录: |