sql - 关联独立事件的序列 - 计算时间交点

标签 sql sql-server powerbi dax

我们正在构建一个 PowerBI 报告解决方案,我 (well Stack) 解决了 one problem企业想出了一个新的报告想法。不确定处理它的最佳方法,因为我对 PowerBI 知之甚少,而且企业似乎需要非常复杂的报告。

我们有两个来自不同数据源的事件序列。它们都包含发生在车辆上的独立事件。一个描述车辆所在的位置 - 另一个描述具有事件原因代码的事件事件。企业希望报告因各种原因在每个地点花费的时间。车辆可以完全独立于发生的事件事件而改变位置 - 事件实际上是日期时间并且在一天中的随机点发生。每种类型的事件都有开始时间/结束时间和车辆 ID。

车辆定位事件

+------------------+-----------+------------+-----------------+----------------+
| LocationDetailID | VehicleID | LocationID |  StartDateTime  |  EndDateTime   |
+------------------+-----------+------------+-----------------+----------------+
|                1 |         1 |          1 |        2012-1-1 |      2016-1-1  |
|                2 |         1 |          2 |        2016-1-1 |      2016-4-1  |
|                3 |         1 |          1 |        2016-4-1 |      2016-11-1 |
|                4 |         2 |          1 |        2011-1-1 |      2016-11-1 |
+------------------+-----------+------------+-----------------+----------------+

车辆状态事件

+---------+---------------+-------------+-----------+--------------+
| EventID | StartDateTime | EndDateTime | VehicleID | ReasonCodeID |
+---------+---------------+-------------+-----------+--------------+
|       1 | 2012-1-1      | 2013-1-1    |         1 |            1 |
|       2 | 2013-1-1      | 2015-1-1    |         1 |            3 |
|       3 | 2015-1-1      | 2016-5-1    |         1 |            4 |
|       4 | 2016-5-1      | 2016-11-1   |         1 |            2 |
|       5 | 2015-9-1      | 2016-2-1    |         2 |            1 |
+---------+---------------+-------------+-----------+--------------+

无论如何,我是否可以将两个流关联在一起并计算每个位置每个 ReasonCode 每辆车的总时间?这似乎要求我能够将这两个事件联系起来 - 因此位置的变化可能会在给定的 ReasonCode 中途发生。

计算示例ReasonCodeID 4

  • VehicleID 1 在位置 ID 1 从 2012-1-1 到 2016-1-1 和 2016-4-1 至 2016-11-1
  • VehicleID 1 在 2016-1-1 的位置 ID 2 至 2016-4-1
  • 从 2015-1-1 到 VehcileID 1 的 ReasonCodeID 为 4 2016-5-1

因此,位置 1 中的第一个 Period 与 ReasonCodeID 4 的 365 天(2015-1-1 到 2016-1-1)相交。位置 1 的第二个周期与 30 天相交(2016-4-1 到 2016-5-1)。 在位置2与ReasonCodeID为4的91天相交(2016-1-1到2016-4-1

期望的输出如下。

+-----------+--------------+------------+------------+
| VehicleID | ReasonCodeID | LocationID | Total Days |
+-----------+--------------+------------+------------+
|         1 |            1 |          1 |        366 |
|         1 |            3 |          1 |        730 |
|         1 |            4 |          1 |        395 |
|         1 |            4 |          2 |         91 |
|         1 |            2 |          1 |        184 |
|         2 |            1 |          1 |        154 |
+-----------+--------------+------------+------------+

我创建了一个显示结构 here 的 SQL fiddle

车辆有相关的表格,我确信企业会希望它们按车辆类别等分组,但如果我能理解在这种情况下如何计算交叉点,这将为我提供其余报告的基础。

最佳答案

我认为这个解决方案需要一个CROSS JOIN 实现。两个表之间的关系是 Many to Many 这意味着创建第三个表来桥接 LocationEventsVehicleStatusEvents 表所以我想指定关系在表达式中可能更容易。

我在两个表之间使用 CROSS JOIN,然后过滤结果仅获取两个表中 VehicleID 列相同的那些行。我还过滤了 VehicleStatusEvents 范围日期与 LocationEvents 范围日期相交的行。

过滤完成后,我将添加一列来计算每个交叉点之间的天数。最后,该度量总结了每个 VehicleID、ReasonCodeID 和 LocationID 的天数。

为了实现 CROSS JOIN,您必须重命名两个表中任意一个的 VehicleIDStartDateTimeEndDateTime。这是避免歧义列名错误所必需的。

我将列重命名如下:

VehicleID :LocationVehicleIDStatusVehicleID
StartDateTime :LocationStartDateTimeStatusStartDateTime
EndDateTime :LocationEndDateTimeStatusEndDateTime

在此之后,您可以在 Total Days 度量中使用 CROSSJOIN:

Total Days =
SUMX (
    FILTER (
        ADDCOLUMNS (
            FILTER (
                CROSSJOIN ( LocationEvents, VehicleStatusEvents ),
                LocationEvents[LocationVehicleID] = VehicleStatusEvents[StatusVehicleID]
                    && LocationEvents[LocationStartDateTime] <= VehicleStatusEvents[StatusEndDateTime]
                    && LocationEvents[LocationEndDateTime] >= VehicleStatusEvents[StatusStartDateTime]
            ),
            "CountOfDays", IF (
                [LocationStartDateTime] <= [StatusStartDateTime]
                    && [LocationEndDateTime] >= [StatusEndDateTime],
                DATEDIFF ( [StatusStartDateTime], [StatusEndDateTime], DAY ),
                IF (
                    [LocationStartDateTime] > [StatusStartDateTime]
                        && [LocationEndDateTime] >= [StatusEndDateTime],
                    DATEDIFF ( [LocationStartDateTime], [StatusEndDateTime], DAY ),
                    IF (
                        [LocationStartDateTime] <= [StatusStartDateTime]
                            && [LocationEndDateTime] <= [StatusEndDateTime],
                        DATEDIFF ( [StatusStartDateTime], [LocationEndDateTime], DAY ),
                        IF (
                            [LocationStartDateTime] >= [StatusStartDateTime]
                                && [LocationEndDateTime] <= [StatusEndDateTime],
                            DATEDIFF ( [LocationStartDateTime], [LocationEndDateTime], DAY ),
                            BLANK ()
                        )
                    )
                )
            )
        ),
        LocationEvents[LocationID] = [LocationID]
            && VehicleStatusEvents[ReasonCodeID] = [ReasonCodeID]
    ),
    [CountOfDays]
)

然后在 Power BI 中,您可以使用此度量构建矩阵(或任何其他可视化):

enter image description here

如果您不完全理解度量表达式,这里是 T-SQL 翻译:

SELECT
    dt.VehicleID,
    dt.ReasonCodeID,
    dt.LocationID,
    SUM(dt.Diff) [Total Days]
FROM 
(
    SELECT
        CASE
            WHEN a.StartDateTime <= b.StartDateTime AND a.EndDateTime >= b.EndDateTime  -- Inside range
               THEN DATEDIFF(DAY, b.StartDateTime, b.EndDateTime)
            WHEN a.StartDateTime > b.StartDateTime AND a.EndDateTime >= b.EndDateTime  -- |-----|*****|....|
               THEN DATEDIFF(DAY, a.StartDateTime, b.EndDateTime)
            WHEN a.StartDateTime <= b.StartDateTime AND a.EndDateTime <= b.EndDateTime  -- |...|****|-----|
               THEN DATEDIFF(DAY, b.StartDateTime, a.EndDateTime)
            WHEN a.StartDateTime >= b.StartDateTime AND a.EndDateTime <= b.EndDateTime  -- |---|****|-----
               THEN DATEDIFF(DAY, a.StartDateTime, a.EndDateTime)
        END Diff,
        a.VehicleID,
        b.ReasonCodeID,
        a.LocationID --a.StartDateTime, a.EndDateTime, b.StartDateTime, b.EndDateTime
    FROM LocationEvents a
        CROSS JOIN VehicleStatusEvents b
    WHERE a.VehicleID = b.VehicleID
        AND 
        (
            (a.StartDateTime <= b.EndDateTime)
                AND (a.EndDateTime >= b.StartDateTime)
        )
) dt
GROUP BY dt.VehicleID,
         dt.ReasonCodeID,
         dt.LocationID

请注意,在 T-SQL 中,您也可以使用 INNER JOIN 运算符。

如果这有帮助,请告诉我。

关于sql - 关联独立事件的序列 - 计算时间交点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40452027/

相关文章:

sql - 在单个查询中计算空值和非空值

mysql按顺序计数零

mysql - 非常大的表 JOIN 与 GROUP BY

sql - 周四到周三的每周数据Sql查询

azure - 流分析提示发送到 power bi 的数据类型

c# - 在多个文件中搜索多个字符串的 Grep 工具

c# - 没有 NHibernate 的 NHibernate Hilo 序列

sql - SQLSERVER 中的 ListAGG

powerbi - Power BI/DAX 中的 COUNTIF 等效项?

Python 解决方案 - 使用 Python 将 Microsoft BI 工作表导出到 Excel