MySQL - 仅计算特定日期之间的唯一实例

时间:2014-05-12 12:31:21

标签: mysql sql mysql-workbench

我一直在寻找其他几个SO问题,但我无法从中找出解决方案。首先,描述,然后我从其他线程中缺少。 (抬头:我非常清楚我们数据库的非规范化结构,这是我之前在会议中讨论的内容,但这是我们拥有的和我必须合作的内容。)

背景说明

我们有一台机器可以生产25个位置的产品。这些产品的生产数据记录在一个表格中,其中包括记录每个位置的电流和电压。这仅在机器实际生产产品时记录(即在机器中有产品)。没有产品的时间,没有记录任何东西。

该机器可以在两种不同的生产模式下运行:全面生产和R& D生产。完全生产意味着产品被连续插入,以便每个实例始终都有产品(即机器中始终存在25种产品)。第二种模式,R& D生产,一次只生产一种产品(即一种产品进入机器,逐一经历25个实例,当这一个完成时,第二个产品进入机器)。

澄清:每当产品出现时,每个位置每秒记录一次数据,这意味着当完整生产运行时每秒25个实例。当R& D模式运行时,位置1将连续20秒具有~20个实例,位置2将在接下来的20个连续秒内具有~20个实例,依此类推。

表格结构

Productiondata:

  • id(autoincrement)
  • 的productID
  • 位置
  • 时间(记录数据的时间戳)
  • current(安培)
  • 电压(伏特)

问题

我们想要计算机器的正常运行时间,但我们想要将生产模式和R& D模式的正常运行时间分开,我们希望每周将这些数据分开。

猜测解决方案

由于我们每秒都会记录实例,因此我可以计算表格中DISTINCT时间值实例的数量,以找出生产和R& D模式的总运行时间。为了找到R& D模式,我可以肯定地说,只要有一个只有一个条目的时间实例,我就会以R& D模式运行(生产模式将有25个实例)。

到目前为止的进展

我有以下查询,它总结了所有不同的实例,以找到生产和R& D模式:

SELECT YEARWEEK(time) AS YWeek, COUNT(DISTINCT time) AS Time_Seconds, ROUND(COUNT(DISTINCT time)/3600, 1) AS Time_Hours 
FROM Database.productiondata
WHERE YEARWEEK(time) >= YEARWEEK(curdate()) - 21
GROUP BY YWeek;

此查询会查找表中有多少个DISTINCT时间实例,并按周计算数字和组。

问题

上面的查询计算了表中存在的实例数量,但我想只查找UNIQUE实例。基本上,我试图找到像IF计数(时间)= 1的东西,然后计算那个实例,IF计数(时间)> 1然后根本不计算它(DISTINCT仍然计算这个)。

我查看了其他几个SO线程,但几乎都解释了如何使用DISTINCT找到唯一值,这只能完成我正在寻找的一半。我得到的最接近的是this,它使用了HAVING子句。我目前陷入以下困境:

SELECT YEARWEEK(time) as YWeek, COUNT(Distinct time) As Time_Seconds, ROUND(COUNT(Distinct time)/3600, 1) As Time_Hours
FROM 
(SELECT * FROM Database.productiondata
WHERE time > '2014-01-01 00:00:00'
GROUP BY time
HAVING count(time) = 1) as temptime
GROUP BY YWeek
ORDER BY YWeek;

这里的问题是我们在嵌套的select子句中有一个GROUP BY时间,这需要永远(今年只有约500万行,所以我可以理解)。我的意思是,从语法上来说,我认为这是正确的,但它需要永远的exectue。甚至EXPLAIN这个时候了。

这就是我的地方。这是正确的方法还是有更聪明的方法/需要更少的查询时间/避免按时间分组?

编辑:作为示例,我们有这个表格(格式化道歉,不知道如何在SO上制作表格格式)

id    position    time
1     1           1
2     2           1
3     5           1
4     19          1
...   ...         ...
25    7           1
26    3           2
27    6           2
...   ...         ...

此表显示正在进行生产运行时的样子。如您所见,在表中记录数据时,没有通用结构可以获得第一个条目;会发生的是每25秒记录25个位置,然后根据PLC为每个位置发送数据的速度将数据添加到表中。下表显示了表在研究模式下运行时的样子。

id    position    time
245   1           1
246   1           2
247   1           3
...   ...         ...
269   1           25
270   2           26
271   2           27
...   ...         ...

由于所有数据都合并到一个表中,我们想知道当COUNT(时间)恰好等于1时有多少个实例,或者我们可以在COUNT(时间)严格更大时查找每个实例比1。

EDIT2:作为对Alan的回复,建议给了我

YWeek    Time_Seconds    Time_Hours
201352   1               0.0
201352   1               0.0
201352   1               0.0
...      ...             ...
201352   1               0.0  (1000 row limit)

而我想要的输出是

Yweek    Time_Seconds    Time_Hours
201352   2146            35.8
201401   5789            96.5
...      ...             ...
201419   8924            148.7

EDIT3 :到目前为止,我已经收集了尝试和结果here,并在查询上方显示了灰色描述。

2 个答案:

答案 0 :(得分:1)

您可以通过取消子选择来获得更好的结果:

SELECT YEARWEEK(time) as YWeek, 
       COUNT(time) As Time_Seconds, 
       ROUND(COUNT(time)/3600, 1) As Time_Hours
FROM Database.productiondata
WHERE time > '2014-01-01 00:00:00'
GROUP BY YWeek
HAVING count(time) = 1)
ORDER BY YWeek;

我假设time上有index,但如果没有,您可以通过添加一个来提高性能。

<强>更新

根据最近添加的样本数据,我不确定您的方法是否正确。 time列似乎是INT代表秒,而您将DATETIME视为具有YEARWEEK的{​​{1}}。下面我在SQL中有一个工作示例,它完全按照您的要求time实际上是DATETIME列:

DECLARE @table TABLE
    (
      id INT ,
      [position] INT ,
      [time] DATETIME
    )


INSERT  INTO @table
VALUES  ( 1, 1, DATEADD(week, -1, GETDATE()) )
INSERT  INTO @table
VALUES  ( 1, 1, DATEADD(week, -2, GETDATE()) )
INSERT  INTO @table
VALUES  ( 1, 1, DATEADD(week, -2, GETDATE()) )
INSERT  INTO @table
VALUES  ( 1, 1, DATEADD(week, -2, GETDATE()) )
INSERT  INTO @table
VALUES  ( 1, 1, DATEADD(week, -2, GETDATE()) )
INSERT  INTO @table
VALUES  ( 1, 1, DATEADD(week, -3, GETDATE()) )
INSERT  INTO @table
VALUES  ( 1, 1, DATEADD(week, -3, GETDATE()) )

SELECT  CAST(DATEPART(year, [time]) AS VARCHAR)
        + CAST(DATEPART(week, [time]) AS VARCHAR) AS YWeek ,
        COUNT([time]) AS Time_Seconds ,
        ROUND(COUNT([time]) / 3600, 1) AS Time_Hours
FROM    @table
WHERE [time] > '2014-01-01 00:00:00'
GROUP BY DATEPART(year, [time]) ,
        DATEPART(week, [time])
HAVING COUNT([time]) > 0
ORDER BY YWeek;

答案 1 :(得分:1)

SELECT pd1.* 
FROM Database.productiondata pd1
LEFT JOIN Database.productiondata pd2 ON pd1.time=pd2.time AND pd1.id<pd2.id
WHERE pd1.time > '2014-01-01 00:00:00' AND pd2.time > '2014-01-01 00:00:00'
  AND pd2.id IS NULL

您可以LEFT JOIN到同一张桌子,只留下没有相关的行

更新查询使用SQL小提琴

SELECT pd1.* From productiondata pd1
left Join productiondata pd2
ON pd1.time = pd2.time and pd1.id < pd2.id
Where pd1.time > '2014-01-01 00:00:00' and pd2.id IS NULL;