【问题标题】:Start and end dates in different rows不同行中的开始日期和结束日期
【发布时间】:2018-11-15 08:32:03
【问题描述】:

我将事件记录存储在一个表中,开始和结束日期时间记录为单独的记录。

存储记录的表。

CREATE TABLE `avl_data` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`imei_number` bigint(20) unsigned NOT NULL DEFAULT '0',
`latitude` double NOT NULL DEFAULT '0',
`longitude` double NOT NULL DEFAULT '0',
`report_id` tinyint(4) NOT NULL DEFAULT '0',
`rtc_date` datetime NOT NULL,
`ibutton_id` varchar(25) DEFAULT 'N/A',
`odometer` bigint(20) NOT NULL DEFAULT '0',
`speed` smallint(6) NOT NULL DEFAULT '0',
`vin_number` varchar(255) DEFAULT 'N/A',
`date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`time_report` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `imei_number` (`imei_number`),
KEY `imei_rtc` (`imei_number`,`rtc_date`),
CONSTRAINT `avl_data_ibfk_1` FOREIGN KEY (`imei_number`) REFERENCES `assets` (`imei_number`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=64916466 DEFAULT CHARSET=utf8 |`

这是我迄今为止尝试过的。

    select concat(ass.label_1, " ", ass.label_2, " ", ass.label_3)                                         as "Vehicle",
       @start := case
                   when a.report_id = 103 then convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait') end as "Start",
       @end := case
                 when a.report_id = 104 then convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait') end   as "End",
       TIMEDIFF(@start, @end)                                                                   as 'Duration',
       a.speed                                                                                  as 'Speed',
       a.latitude                                                                               as 'Latitude',
       a.longitude                                                                              as 'Longitude'
from avl_data a
       inner join assets ass on a.imei_number = ass.imei_number
where a.imei_number = 356158069811103
  and rtc_date >= '2018-10-01 00:00:00'
  and rtc_date <= '2018-10-31 23:59:59'
  and a.report_id in (103, 104)
order by a.rtc_date asc;

分别产生开始和事件记录,第一条记录是事件开始,第二条记录是事件停止。

+-------------------------------+---------------------+---------------------+----------+-------+-----------+-----------+
| Vehicle                       | Start               | End                 | Duration | Speed | Latitude  | Longitude |
+-------------------------------+---------------------+---------------------+----------+-------+-----------+-----------+
| Mitsubishi Outlander 14/74080 | 2018-10-01 08:29:26 | NULL                | NULL     |   128 | 29.045856 | 48.113764 |
| Mitsubishi Outlander 14/74080 | NULL                | 2018-10-01 08:30:17 | NULL     |   114 | 29.031169 | 48.121516 |
|

理想情况下,我想要一行,即

+-------------------------------+---------------------+---------------------+----------+-------+-----------+-----------+
| Vehicle                       | Start               | End                 | Duration | Speed | Latitude  | Longitude |
+-------------------------------+---------------------+---------------------+----------+-------+-----------+-----------+
| Mitsubishi Outlander 14/74080 | 2018-10-01 08:29:26 | 2018-10-01 08:30:17 | 00:01:17 |   128 | 29.045856 | 48.113764 | 

感谢@Thorsten,这对我很有用,领导功能完美无缺。

select a.report_id as "ID",
       any_value(case when a.report_id = 103 then concat(ass.label_1, ' ', ass.label_2, ' ', ass.label_3) end) as "Vehicle",
       any_value(case when a.report_id = 103 then convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait') end) as "Start",
       any_value(case when a.report_id = 103 then lead(convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait')) over () end) as "End",
       any_value(case when a.report_id = 103 then SEC_TO_TIME(TIMESTAMPDIFF(SECOND , convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait'), lead(convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait')) over ())) end) as "Duration",
       any_value(case when a.report_id = 103 then a.speed end) as "Speed",
       any_value(case when a.report_id = 103 then a.latitude end) as "Latitude",
       any_value(case when a.report_id = 103 then a.longitude end) as "Longitude"
from avl_data a
       join assets ass on a.imei_number = ass.imei_number
where a.imei_number = 356158069811103
  and a.rtc_date >= '2018-10-01 00:00:00'
  and a.rtc_date <= '2018-10-31 23:59:59'
  and a.report_id in (103, 104); 

结果,虽然有办法删除现在的空行吗?

+-----+-------------------------------+---------------------+---------------------+----------+-------+-----------+-----------+
| ID  | Vehicle                       | Start               | End                 | Duration | Speed | Latitude  | Longitude |
+-----+-------------------------------+---------------------+---------------------+----------+-------+-----------+-----------+
| 103 | Mitsubishi Outlander 14/74080 | 2018-10-01 08:29:26 | 2018-10-01 08:30:17 | 00:00:51 |   128 | 29.045856 | 48.113764 |
| 104 | NULL                          | NULL                | NULL                | NULL     |  NULL |      NULL |      NULL |
| 103 | Mitsubishi Outlander 14/74080 | 2018-10-01 08:43:45 | 2018-10-01 08:44:14 | 00:00:29 |   136 | 29.067756 | 48.110384 |
| 104 | NULL                          | NULL                | NULL                | NULL     |  NULL |      NULL |      NULL |
| 103 | Mitsubishi Outlander 14/74080 | 2018-10-01 09:31:36 | 2018-10-01 09:31:44 | 00:00:08 |   135 | 29.056563 | 48.108851 |
| 104 | NULL                          | NULL                | NULL                | NULL     |  NULL |      NULL |      NULL |
| 103 | Mitsubishi Outlander 14/74080 | 2018-10-01 09:32:02 | 2018-10-01 09:33:54 | 00:01:52 |   149 | 29.048803 | 48.112581 |
| 104 | NULL                          | NULL                | NULL                | NULL     |  NULL |      NULL |      NULL |
| 103 | Mitsubishi Outlander 14/74080 | 2018-10-01 09:41:57 | 2018-10-01 09:42:35 | 00:00:38 |   131 | 29.036886 | 48.108733 |
| 104 | NULL                          | NULL                | NULL                | NULL     |  NULL |      NULL |      NULL |
| 103 | Mitsubishi Outlander 14/74080 | 2018-10-01 19:48:09 | 2018-10-01 19:48:20 | 00:00:11 |   126 | 29.034386 | 48.119706 |
| 104 | NULL                          | NULL                | NULL                | NULL     |  NULL |      NULL |      NULL |

【问题讨论】:

  • 这里的大多数人都希望样本数据等为格式化文本,而不是图像(或图像链接)。
  • 欢迎来到 Stack Overflow!请提供相关且最少的样本数据,以展示您的要求和预期输出。请参阅此链接以了解如何构建一个好的 SQL 问题:Why should I provide an MCVE for what seems to me to be a very simple SQL query?
  • PS:为什么不能按所有列对结果进行分组并选择MIN(CASE WHEN when a.report_id = 103 then rtcdate end)MIN(CASE WHEN when a.report_id = 104 then rtcdate end)
  • 您根据什么规则决定要在您的单个所需结果行中显示的车辆、速度、纬度和经度?您当前的查询是否总是正好有两行,一行有开始,一行有结束?如果没有,如果有更多行怎么办?
  • 只需要End记录中的End日期时间,第二条记录中的所有其他信息都可以丢弃,我使用in子句所以只返回开始和结束记录。

标签: mysql sql


【解决方案1】:

您可以使用聚合从两行中获取一行。由于您需要一些来自 report_id = 103 的值和一些来自 report_id = 104 的值,您可以使用 CASE WHEN 来获取一个值或另一个值。并且由于只有一个 103 行和一个 104 行,因此您的聚合函数是 ANY_VALUE 的伪聚合。

select
  any_value(case when a.report_id = 103 then concat(ass.label_1, ' ', ass.label_2, ' ', ass.label_3) end) as "Vehicle",
  any_value(case when a.report_id = 103 then convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait') end) as "Start",
  any_value(case when a.report_id = 104 then convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait') end) as "End",
  TIMEDIFF(
    any_value(case when a.report_id = 103 then convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait') end),
    any_value(case when a.report_id = 104 then convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait') end)
  ) as "Duration",
  any_value(case when a.report_id = 103 then a.speed end) as "Speed",
  any_value(case when a.report_id = 103 then a.latitude end) as "Latitude",
  any_value(case when a.report_id = 103 then a.longitude end) as "Longitude"
from avl_data a
join assets ass on a.imei_number = ass.imei_number
where a.imei_number = 356158069811103
  and rtc_date >= date '2018-10-01'
  and rtc_date < date '2018-11-01'
  and a.report_id in (103, 104);

替代方案是连接:

select
  a103."Vehicle",
  a103."Start",
  a104."End",
  TIMEDIFF(a103."Start", a104."End") as "Duration",
  a103."Latitude",
  a103."Longitude",
from
(
  select
    concat(ass.label_1, ' ', ass.label_2, ' ', ass.label_3) as "Vehicle",
    convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait') end as "Start",
    speed as "Speed",
    latitude as "Latitude",
    longitude as "Longitude"
  from avl_data a
  join assets ass on a.imei_number = ass.imei_number
  where a.imei_number = 356158069811103
    and rtc_date >= '2018-10-01 00:00:00'
    and rtc_date <= '2018-10-31 23:59:59'
    and a.report_id = 103
) a103
cross join
(
  select convert_tz(a.rtc_date, 'UTC', 'Asia/Kuwait' as "End"
  from avl_data a
  join assets ass on a.imei_number = ass.imei_number
  where a.imei_number = 356158069811103
    and rtc_date >= '2018-10-01 00:00:00'
    and rtc_date <= '2018-10-31 23:59:59'
    and a.report_id = 104
) a104;

【讨论】:

  • 感谢@Thorsten,但这似乎仍然将结束日期时间留在第二行
  • 哪个查询? 第一个查询只产生一行,因为它是在没有 GROUP BY 子句的情况下聚合 (ANY_VALUE)。 第二个查询只返回一行,因为您说结果中只有 103 行和 104 行。交叉连接 1 x 1 得到 1。
  • 如果我误导你,我很抱歉结果中有多个 103、104。
  • 那103行与哪104行结合呢?还是我们必须忽略某些行?如果有,是哪个?
  • 每隔一行似乎都包含上一行所需的结束日期。
【解决方案2】:

两行之间是否有一个公共键?

如果是这样,那么只需将 avl_data 表的 2 个实例连接在一起

类似

select convert_tz(aStart.rtc_date, 'UTC', tzone) as "Start", 
    convert_tz(aEnd.rtc_date, 'UTC', tzone) as "End",
from avl_data aStart
inner join avl_data aEnd on aStart.Key = aEnd.Key;

其中 Key 是公共密钥。

如果您没有公用键并且依赖于一行接着另一行并且您有一个主键序列,那么这样的事情可能会起作用

select convert_tz(aStart.rtc_date, 'UTC', tzone) as "Start", 
    convert_tz(aEnd.rtc_date, 'UTC', tzone) as "End",
from avl_data aStart
inner join avl_data aEnd on aStart.reportId + 1 = aEnd.reportId;

但是我不建议这样做,因为对于整个表来说,一个 ID 比前一个大 1 可能不是 100% 可靠的。

这样的事情可能会给你答案,但它会产生额外的结果,因为它也会有以 End 开头的结果

select aStart.imei_number as "Imei",
        convert_tz(aStart.rtc_date, 'UTC', tzone) as "Start", 
        convert_tz(MIN(aEnd.rtc_date), 'UTC', tzone) as "End"
from avl_data aStart
inner join avl_data aEnd on aStart.imei_number = aEnd.imei_number
                    and  aStart.rtc_date < aEnd.rtc_date
group by aStart.imei_number, aStart.rtc_date, aStart.report_id
order by aStart.imei_number, aStart.rtc_date;

您可以将其用作子查询并用排名值 ref https://stackoverflow.com/a/1895127/3805124 包装它,然后删除偶数,因为它们应该是从结束记录开始的额外的。

【讨论】:

  • 嗨,PhilS,不幸的是,我在行之间没有公用键,我也不能使用 id,因为它们很可能不像你提到的那样是连续的
  • 我添加了另一个查询,可能会帮助您找到答案。
猜你喜欢
  • 2012-08-19
  • 1970-01-01
  • 2019-04-28
  • 2022-01-23
  • 2015-04-07
  • 1970-01-01
  • 1970-01-01
  • 2020-10-05
  • 1970-01-01
相关资源
最近更新 更多