【发布时间】:2018-02-13 03:29:11
【问题描述】:
我的数据库中有一个表格,我可以通过这种方式记录来自多个传感器的读数:
CREATE TABLE [test].[readings] (
[timestamp_utc] DATETIME2(0) NOT NULL, -- 48bits
[sensor_id] INT NOT NULL, -- 32 bits
[site_id] INT NOT NULL, -- 32 bits
[reading] REAL NOT NULL, -- 64 bits
PRIMARY KEY([timestamp_utc], [sensor_id], [site_id])
)
CREATE TABLE [test].[sensors] (
[sensor_id] int NOT NULL ,
[measurement_type_id] int NOT NULL,
[site_id] int NOT NULL ,
[description] varchar(255) NULL ,
PRIMARY KEY ([sensor_id], [site_id])
)
我想轻松地从所有这些读数中进行统计。
我想做的一些查询:
Get me all readings for site_id = X between date_hour1 and date_hour2
Get me all readings for site_id = X and sensor_id in <list> between date_hour1 and date_hour2
Get me all readings for site_id = X and sensor measurement type = Z between date_hour1 and date_hour2
Get me all readings for site_id = X, aggregated (average) by DAY between date_hour1 and date_hour2
Get me all readings for site_id = X, aggregated (average) by DAY between date_hour1 and date_hour2 but in UTC+3(这应该给出与之前的查询不同的结果,因为现在日期的开始和结束移动了 3 小时)
Get me min, max, std, mean for all readings for site_id = X between date_hour1 and date_hour2
到目前为止,我一直在使用 Java 来查询数据库并在本地执行所有这些处理。但这最终会有点慢,并且代码编写和维护都很混乱(太多的 cicles、执行重复任务的通用函数、大型/冗长的代码库等)...
更糟糕的是,表readings 很大(因此主键很重要,它也是一个性能指标),也许我应该为此使用 TimeSeries 数据库(有什么好的吗?) .我正在使用 SQL Server。
最好的方法是什么?我觉得我正在重新发明轮子,因为所有这些都是一种分析应用程序......
我知道这些查询听起来很简单,但是当您尝试对所有这些进行参数化时,您最终可能会遇到这样的怪物:
-- Sums all device readings, returns timestamps in localtime according to utcOffset (if utcOffset = 00:00, then timestamps are in UTC)
CREATE PROCEDURE upranking.getSumOfReadingsForDevices
@facilityId int,
@deviceIds varchar(MAX),
@beginTS datetime2,
@endTS datetime2,
@utcOffset varchar(6),
@resolution varchar(6) -- NO, HOURS, DAYS, MONTHS, YEARS
AS BEGIN
SET NOCOUNT ON -- http://stackoverflow.com/questions/24428928/jdbc-sql-error-statement-did-not-return-a-result-set
DECLARE @deviceIdsList TABLE (
id int NOT NULL
);
DECLARE @beginBoundary datetime2,
@endBoundary datetime2;
SELECT @beginBoundary = DATEADD(day, -1, @beginTS);
SELECT @endBoundary = DATEADD(day, 1, @endTS);
-- We shift sign from the offset because we are going to convert the zone for the entire table and not beginTS endTS themselves
SELECT @utcOffset = CASE WHEN LEFT(@utcOffset, 1) = '+' THEN STUFF(@utcOffset, 1, 1, '-') ELSE STUFF(@utcOffset, 1, 1, '+') END
INSERT INTO @deviceIdsList
SELECT convert(int, value) FROM string_split(@deviceIds, ',');
SELECT SUM(reading) as reading,
timestamp_local
FROM (
SELECT reading,
upranking.add_timeoffset_to_datetime2(timestamp_utc, @utcOffset, @resolution) as timestamp_local
FROM upranking.readings
WHERE
device_id IN (SELECT id FROM @deviceIdsList)
AND facility_id = @facilityId
AND timestamp_utc BETWEEN @beginBoundary AND @endBoundary
) as innertbl
WHERE timestamp_local BETWEEN @beginTS AND @endTS
GROUP BY timestamp_local
ORDER BY timestamp_local
END
GO
这是一个查询,它接收站点 ID(在本例中为 facilityId)、传感器 ID 列表(在本例中为 deviceIds)、开始和结束时间戳,然后是字符串中的 UTC 偏移量,如“+ xx:xx" 或 "-xx:xx",以分辨率结束,该分辨率基本上说明了 SUM 将如何聚合结果(考虑到 UTC 偏移量)。
而且由于我使用的是 Java,乍一看我可以使用 Hibernate 之类的,但我觉得 Hibernate 不是为这些类型的查询而设计的。
【问题讨论】:
-
你是说“我不会写 SQL 查询”吗?
-
这与Java无关,请考虑去掉Java标签
-
就像您的第一个查询字面意思是
SELECT * FROM Sensors s JOIN Readings r on r.sensor_id = s.sensor_id WHERE s.site_id = X AND r.timestamp_utc > early_timestamp AND r.timestamp_utc < late_timestamp- 这是您正在寻找的那种东西吗? -
不,这些查询只是基本示例。我在问是否有比这更好的方法:prntscr.com/gh4w44
-
我不知道,我认为您需要考虑您的实际问题是什么。我会说我认为您最好使用参数化语句编写单个查询而不是数据库过程。这些似乎都不是复杂的东西——这些查询中的每一个都是非常简单的开箱即用 SQL。但就目前而言,看起来您确实是在要求 SO 为您编写查询,这不太可能发生。帮助别人和做免费的咨询工作是有区别的。
标签: java sql database statistics time-series