SQL Query按时间对项目进行分组,但只有在彼此靠近的情况下?

Posted

技术标签:

【中文标题】SQL Query按时间对项目进行分组,但只有在彼此靠近的情况下?【英文标题】:SQL Query to group items by time, but only if near each other? 【发布时间】:2013-04-04 18:11:01 【问题描述】:

我正在尝试编写一条 SQL 语句来从数据库中提取样本值。该表包含与 CNC 机床中的刀具更换有关的值。我当前的语句正确地提取了值,但前提是给定程序中出现了一次该工具。如果该工具出现多次,则时间值对应于从第一次加载到最后一次加载。只有一个 TIME 列,通过查找它的第一次和最后一次出现,我可以确定工具的进出时间。

基本示例:

Raw Data:
Tool_Number    TIME    
100            12:00
100            12:01
100            12:02
100            12:03

Current Query Returns: 
Tool_Number    TIME_IN     TIME_OUT    
100            12:00       12:03

但当该工具多次出现时,事情会变得很棘手,因为我无法再使用 TOP 和 DISTINCT 规则。

Raw Data:
Tool_Number    TIME    
100            12:00
100            12:01
100            12:02
100            12:03
200            12:04
200            12:05
100            12:06
100            12:07

Current Query Returns: 
Tool_Number    TIME_IN     TIME_OUT    
100            12:00       12:07
200            12:04       12:05

Ideal Query Returns:
Tool_Number    TIME_IN     TIME_OUT    
100            12:00       12:03
200            12:04       12:05
100            12:06       12:07

我们正在进行时间分析,当然这严重影响了总时间值。 当前查询是:

SELECT * FROM (SELECT DISTINCT SPINDLE_POT FROM TBL_SPINDLE_DATA_M1 
WHERE TIME BETWEEN '4/3/20131:24:13 PM' AND '4/3/2013 3:07:33 PM') AS A 

CROSS APPLY

((SELECT TOP 1 TIME FROM TBL_SPINDLE_DATA_M1 B WHERE B.SPINDLE_POT = A.SPINDLE_POT AND
TIME BETWEEN '4/3/2013 1:24:13 PM' AND '4/3/2013 3:07:33 PM') AS NEWTABLE1

JOIN

(SELECT TOP 1 TIME FROM TBL_SPINDLE_DATA_M1 B WHERE B.SPINDLE_POT = A.SPINDLE_POT 
AND TIME BETWEEN '4/3/2013 1:24:13 PM' AND '4/3/2013 3:07:33 PM' ORDER BY TIME DESC) 
AS NEWTABLE2 ON (0=0))

我绝不是任何一种 SQL 查询专家!上面的查询可能非常错误,但它确实返回了我需要的内容。有没有办法对相似的项目进行分组,但如果它们的索引不相互接触,就足够公正地不将它们分组?

【问题讨论】:

由于使用了APPLY,我假设使用 SQL Server,但您应该确保使用适当的数据库标记您的问题。 谢谢,我也在使用 SQL 2012。标签已更新。 值被视为两个单独的“批次”而不是一个的标准究竟是什么?两者之间是否不能有时间间隔,即只要条目以连续方式(按分钟)出现,就被视为一批?还是两者之间不得有任何其他工具条目(时间方面)? 工具一次只能加载一个,因此“批次”必须连续(按时间)是相同的工具编号才能组合在一起。或者如您所说,两者之间不得有任何其他工具条目(时间方面)。 【参考方案1】:

这被称为“岛屿问题”,我认为这是一个解决方案(信用 Itzik Ben Gan)

select  tool_number,
        min(time) 'in',
        max(time) 'out',
        count(*)
from    (
    select  tool_number,
            time,
            ROW_NUMBER() OVER (ORDER BY time) - ROW_NUMBER() OVER (PARTITION BY Tool_Number ORDER BY time) AS Grp
    from    #temp
    ) as a
group by grp, tool_number
order by min(time)

【讨论】:

不错的解决方案,作为奖励将适用于 2005 年以上。我添加了 LAG/LEAD 解决方案只是因为我可以(2012 年的问题非常少见)并且因为它似乎导致了一个更好的计划(我的解决方案有一种,而你的解决方案是三种)。【参考方案2】:

这是使用LAG/LEAD的另一种方法:

DECLARE @rawdata TABLE(Tool_Number INT, [Time] TIME(0));

INSERT @rawdata VALUES
(100,'12:00'), (100,'12:01'), (100,'12:02'), (100,'12:03'),
(200,'12:04'), (200,'12:05'),
(100,'12:06'), (100,'12:07');

;WITH x AS
(
  SELECT Tool_Number, [Time], 
    s = CASE Tool_number WHEN LAG(Tool_number,1) OVER (ORDER BY [Time]) 
        THEN 0 ELSE 1 END,
    e = CASE Tool_number WHEN LEAD(Tool_number,1) OVER (ORDER BY [Time]) 
        THEN 0 ELSE 1 END
  FROM @rawdata
),
y AS 
(
  SELECT Tool_Number, s, [Time], e = LEAD([Time],1) OVER (ORDER BY [Time]) 
  FROM x WHERE 1 IN (s,e)
)
SELECT Tool_number, TIME_IN = [Time], TIME_OUT = e 
FROM y 
WHERE s = 1
ORDER BY TIME_IN;

结果:

Tool_number  TIME_IN   TIME_OUT
-----------  --------  --------
100          12:00:00  12:03:00
200          12:04:00  12:05:00
100          12:06:00  12:07:00

【讨论】:

这很好用,而且比我拼凑的当前“损坏”的实现要快得多。非常感谢! 不错。太糟糕了doesn't work correctly when an island consists of a single row。 不知道是否有很多方法可以解决这个问题,但是将y中的e表达式更改为e = CASE e WHEN 1 THEN [Time] ELSE LEAD([Time],1) OVER (ORDER BY [Time]) ENDseems to work。 啊,好消息@AndriyM,我没有考虑过单排岛!让我回到我的剧本,看看有没有其他的角度。 很高兴知道,尽管对于我的数据集这不是问题,因为不应出现单个数据行。如果他们这样做了,那是我不想要的坏数据。

以上是关于SQL Query按时间对项目进行分组,但只有在彼此靠近的情况下?的主要内容,如果未能解决你的问题,请参考以下文章

SQL - 如何在一年中的每个日期按年龄和状态对项目进行分组/计数 - 第 2 部分

按一个变量排序,按另一个分组,然后在 R 中的 SQL Query 中选择第一行

如何仅按某个列值的前几个字母对 SQL 查询进行分组?

按日期对 Pandas DataFrame 进行分组

SQL 按 Top 3 和其他对行进行分组。 (按州和其他排名前 3 名的城市的收入)

使用 Power Query 对数据进行分组和转置