背景故事:我有一个数据库,其中包含卡车司机的数据点,其中也包含。在卡车上时,驾驶员可以具有“ driverstatus”状态。我要做的是按驾驶员,卡车将这些状态分组。
到目前为止,我已经尝试使用LAG / LEAD来提供帮助。这样做的原因是,我可以告诉驾驶员状态何时发生更改,然后可以将该行标记为具有该状态的最后日期时间。
这本身是不够的,因为我需要按状态和日期对状态进行分组。为此,我有了诸如DENSE_RANK之类的东西,但是我无法设法获得有关ORDER BY子句的正确信息。
这是我的测试数据,这是我中许多人排名排名的尝试。
/****** Script for SelectTopNRows command from SSMS ******/
DECLARE @SomeTable TABLE
(
loginId VARCHAR(255),
tractorId VARCHAR(255),
messageTime DATETIME,
driverStatus VARCHAR(2)
);
INSERT INTO @SomeTable (loginId, tractorId, messageTime, driverStatus)
VALUES('driver35','23533','2018-08-10 8:33 AM','2'),
('driver35','23533','2018-08-10 8:37 AM','2'),
('driver35','23533','2018-08-10 8:56 AM','2'),
('driver35','23533','2018-08-10 8:57 AM','1'),
('driver35','23533','2018-08-10 8:57 AM','1'),
('driver35','23533','2018-08-10 8:57 AM','1'),
('driver35','23533','2018-08-10 9:07 AM','1'),
('driver35','23533','2018-08-10 9:04 AM','1'),
('driver35','23533','2018-08-12 8:07 AM','3'),
('driver35','23533','2018-08-12 8:37 AM','3'),
('driver35','23533','2018-08-12 9:07 AM','3'),
('driver35','23533','2018-06-12 8:07 AM','2'),
('driver35','23533','2018-06-12 8:37 AM','2'),
('driver35','23533','2018-06-12 9:07 AM','2')
;
SELECT *, DENSE_RANK() OVER(PARTITION BY
loginId, tractorId, driverStatus
ORDER BY messageTime ) FROM @SomeTable
;
理想情况下,我的最终结果应该是这样的:
loginId tractorId startTime endTime driverStatus
driver35 23533 2018-08-10 8:33 AM 2018-08-10 8:56 AM 2
driver35 23533 2018-08-10 8:57 AM 2018-08-10 9:07 AM 1
driver35 23533 2018-08-12 8:07 AM 2018-08-12 9:07 AM 3
对此表示任何帮助。
答案 0 :(得分:1)
WITH drivers_data AS
(
SELECT *,
row_num = ROW_NUMBER()
OVER (PARTITION BY loginId,
tractorId,
CAST(messageTime AS date),
driverStatus
ORDER BY messageTime),
row_num_all = ROW_NUMBER()
OVER (PARTITION BY loginId,
tractorId
ORDER BY messageTime),
first_date = FIRST_VALUE (messageTime)
OVER (PARTITION BY loginId,
tractorId,
CAST(messageTime AS date),
driverStatus
ORDER BY messageTime),
last_date = LAST_VALUE (messageTime)
OVER (PARTITION BY loginId,
tractorId,
CAST(messageTime AS date),
driverStatus
ORDER BY messageTime
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
FROM @t
)
SELECT loginId, tractorId, first_date, last_date, driverStatus
FROM drivers_data
WHERE row_num = 1
ORDER BY row_num_all;
+==========+===========+=====================+=====================+==============+ | loginId | tractorId | first_date | last_date | driverStatus | |==========|===========|=====================|=====================|==============| | driver35 | 23533 | 2018-10-08 08:33:00 | 2018-10-08 08:56:00 | 2 | |----------|-----------|---------------------|---------------------|--------------| | driver35 | 23533 | 2018-10-08 08:57:00 | 2018-10-08 09:07:00 | 1 | |----------|-----------|---------------------|---------------------|--------------| | driver35 | 23533 | 2018-12-06 08:07:00 | 2018-12-06 09:07:00 | 2 | |----------|-----------|---------------------|---------------------|--------------| | driver35 | 23533 | 2018-12-08 08:07:00 | 2018-12-08 09:07:00 | 3 | +----------+-----------+---------------------+---------------------+--------------+
我将尝试解释这里发生的事情:
FIRST_VALUE
是我们的便捷功能。它只是检索第一个日期时间出现。LAST_VALUE
窗口函数。但是使用它很棘手,需要更多的解释。如您所见,我明确地使用了特殊的框架ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
。但为什么?让我解释。让我们使用默认框架获取日期10/8/2018
和状态2
的一部分输出。我们得到以下结果:+==========+===========+=====================+=====================+==============+ | loginId | tractorId | first_date | last_date | driverStatus | |==========|===========|=====================|=====================|==============| | driver35 | 23533 | 2018-10-08 08:33:00 | 2018-10-08 08:33:00 | 2 | |----------|-----------|---------------------|---------------------|--------------| | driver35 | 23533 | 2018-10-08 08:33:00 | 2018-10-08 08:37:00 | 2 | |----------|-----------|---------------------|---------------------|--------------| | driver35 | 23533 | 2018-10-08 08:33:00 | 2018-10-08 08:56:00 | 2 | +----------+-----------+---------------------+---------------------+--------------+
如您所见,最后日期是不正确!发生这种情况是因为LAST_VALUE
使用默认帧RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-这意味着最后一行始终是窗口中的当前行。这是幕后故事。创建三个窗口。每行都有自己的窗口。然后,它从窗口中检索最后一行:
第一行的窗口
+==========+===========+=====================+=====================+==============+ | loginId | tractorId | first_date | last_date | driverStatus | |==========|===========|=====================|=====================|==============| | driver35 | 23533 | 2018-10-08 08:33:00 | 2018-10-08 08:33:00 | 2 | +----------+-----------+---------------------+---------------------+--------------+
第二行的窗口
+==========+===========+=====================+=====================+==============+ | loginId | tractorId | first_date | last_date | driverStatus | |==========|===========|=====================|=====================|==============| | driver35 | 23533 | 2018-10-08 08:33:00 | 2018-10-08 08:33:00 | 2 | |----------|-----------|---------------------|---------------------|--------------| | driver35 | 23533 | 2018-10-08 08:33:00 | 2018-10-08 08:37:00 | 2 | +----------+-----------+---------------------+---------------------+--------------+
第三行窗口
+==========+===========+=====================+=====================+==============+ | loginId | tractorId | first_date | last_date | driverStatus | |==========|===========|=====================|=====================|==============| | driver35 | 23533 | 2018-10-08 08:33:00 | 2018-10-08 08:33:00 | 2 | |----------|-----------|---------------------|---------------------|--------------| | driver35 | 23533 | 2018-10-08 08:33:00 | 2018-10-08 08:37:00 | 2 | |----------|-----------|---------------------|---------------------|--------------| | driver35 | 23533 | 2018-10-08 08:33:00 | 2018-10-08 08:56:00 | 2 | +----------+-----------+---------------------+---------------------+--------------+
因此,解决此问题的方法是更改框架:我们不需要从开头移至当前行,而是从当前行移至结尾。因此,UNBOUNDED FOLLOWING
仅表示此-当前窗口的最后一行。
下一个是WHERE row_num = 1
。这很简单:由于所有行都具有关于首个日期和最后日期的相同信息,因此我们只需要第一行。
最后一部分是ORDER BY row_num_all
。这是您正确订购的地方。
您所需要的输出有问题。
对于日期8/10/18 8:57 AM
和状态1
,最后一个日期必须为10/8/2018 9:07 AM
-而不是您所提到的10/8/2018 9:04 AM
。
日期12/6/2018
和状态2
的输出也缺失。
以下是FIRST_VALUE
和LAST_VALUE
的工作方式的说明。
所有三个数字均包含以下部分:
这是引擎盖下发生的事情:
partition
列。RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
。这意味着该行将在分区的开始位置和当前行之间获得窗口。如果您不提及框架,则会使用默认框架。row 1
至row 2
列中并用颜色标记。行号对应于row_num_all
字段。
要获取第一个日期,我们可以使用方便的FIRST_VALUE
窗口函数。
如您所见,我们在这里使用默认框架。这意味着对于每一行,窗口将在窗口的开始与当前行之间。对于第一次约会,这正是我们需要的。每行将从第一行获取值。第一个日期在“ first_date”字段中。
现在,我们需要计算最后日期。最后日期在分区的最后一行中,因此我们可以使用LAST_VALUE
窗口函数。
如前所述,如果不提及框架,则使用默认框架。正如您在图上看到的那样,框架始终在当前行结束-这是不正确,因为我们需要最后一个窗口行的日期。 last_date
字段向我们显示了不正确的结果-它反映了当前行的日期。
要解决获取最后日期的问题,我们需要更改LAST_VALUE
将在其上操作的框架:ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
。如您所见,每行的窗口现在位于当前行和分区末尾之间。在这种情况下,LAST_VALUE
将正确地从窗口的最后一行获取日期。现在last_date
字段中的结果正确。
答案 1 :(得分:1)
以下解决方案标识每次在每个@classmethod
def from_tuple(cls, location):
return cls(*location)
/ driverStatus
组合内一个孤岛开始(当loginID
发生变化时),然后为该孤岛分配一个“ id”号。 / p>
此后,只需一个简单的tractorID
/ min
就可以找到该岛的开始和结束时间。
答案:
max
当您保留样本数据的最后三条记录时(因为这不在问题的预期输出中,就像JohnyL所说的那样),此查询将产生问题的确切输出。
答案 2 :(得分:0)
SELECT
t.loginId,
t.tractorId,
startTime = MIN(messageTime),
endTime = MAX(messageTime),
driverStatus
FROM @someTable t
GROUP BY loginId, tractorId, driverStatus
ORDER BY MIN(messageTime);
结果:
loginId tractorId startTime endTime driverStatus
-------------- ---------- ----------------------- ----------------------- ------------
driver35 23533 2018-10-08 08:33:00.000 2018-10-08 08:56:00.000 2
driver35 23533 2018-10-08 08:57:00.000 2018-10-08 09:07:00.000 1
driver35 23533 2018-12-08 08:07:00.000 2018-12-08 09:07:00.000 3