如何为间隙和孤岛问题编写查询?
How to write a query for a gaps and islands problem?
这是一个缺口和孤岛问题。
Meter_id |Realtimeclock |I_Y|I_B|I_X|
201010 |27-09-2018 00:00:00|1.0|2.0|3.0|
201010 |27-09-2018 00:30:00|1.0|2.0|3.0|
201010 |27-09-2018 01:00:00|1.0|2.0|3.0|
201010 |27-09-2018 01:30:00|1.0|2.0|3.0|
201010 |27-09-2018 02:00:00|1.0| 0 |3.0|
201010 |27-09-2018 02:30:00|1.0| 0 |0 |
201010 |27-09-2018 03:00:00|1.0|2.0|3.0|
201010 |27-09-2018 03:30:00|1.0|2.0|3.0|
201011 |27-09-2018 00:00:00|1.0|2.0|3.0|
201011 |27-09-2018 00:30:00|1.0|2.0|3.0|
201010 |28-09-2018 03:00:00|1.0|2.0|3.0|
201010 |28-09-2018 03:30:00|1.0|2.0|3.0|
201011 |28-09-2018 04:00:00|1.0| 0 |0 |
201011 |28-09-2018 00:00:00|1.0|2.0|3.0|
201011 |28-09-2018 00:30:00|1.0|2.0|3.0|
一种方法使用行号差异法:
select * from (
WITH cte1 AS (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY Meter_id ORDER BY Realtimeclock) rn
FROM yourTable t
),
cte2 AS (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY Meter_id ORDER BY Realtimeclock) rn
FROM yourTable t
WHERE I_B <> 0
),
cte3 AS (
SELECT t1.*,
t1.rn - t2.rn AS diff
FROM cte1 t1
INNER JOIN cte2 t2
ON t1.Meter_id = t2.Meter_id AND t1.Realtimeclock = t2.Realtimeclock
)
SELECT
Meter_id,
MIN(Realtimeclock) AS start_time,
MAX(Realtimeclock) AS end_time,
COUNT(I_Y) AS I_Y,
COUNT(I_B) AS I_B,
COUNT(I_X) AS I_X,ROW_NUMBER() OVER (PARTITION BY meter_id ORDER BY meter_id ) AS Spell
FROM cte3
GROUP BY
Meter_id,
diff);
输出应该打印成 ,请让我知道代码中需要的任何更改。
根据上面的 table 我需要日间咒语作为开始时间和结束时间,基于条件 I_Y、I_B、I_X,它是非零值计数table。
在这里我们看到 201010 meter_id 的开始时间有两个法术,因为它们之间存在时间间隔。同样,它必须显示所有法术以及日期和时间戳。
Meter_id |start_time |End_time |I_Y|I_B|I_X|spell
201010 |27-09-2018 00:00:00|27-09-2018 01:30:00|4 |4 |4 |1
201010 |27-09-2018 03:00:00|27-09-2018 03:30:00|4 |4 |4 |2
201011 |27-09-2018 00:00:00|27-09-2018 00:30:00|2 |2 |2 |1
201010 |28-09-2018 03:00:00|27-09-2018 03:30:00|2 |2 |2 |1
201011 |28-09-2018 00:00:00|28-09-2018 00:30:00|2 |2 |2 |1
抛出运行时间错误如下,
[错误] 执行 (35:22):ORA-01830:日期格式图片在转换整个输入字符串之前结束
嗨蒂姆,
请看一下it.It对我会有很大的帮助。
在给出 t运行c(realtimeclock) 而不是 TO_DATE(realtimeclock) ..
后清除以上内容
感谢蒂姆的帮助。
您只需对当前方法稍作修改,即可在日期上添加一个分区(除了 meter_id
)。然后,在最后的查询中,添加一个 COUNT
来计算给定仪表和日期的法术数量。
WITH cte1 AS (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY Meter_id, TO_DATE(Realtimeclock)
ORDER BY Realtimeclock) rn
FROM yourTable t
),
cte2 AS (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY Meter_id, TO_DATE(Realtimeclock)
ORDER BY Realtimeclock) rn
FROM yourTable t
WHERE I_B <> 0
),
cte3 AS (
SELECT t1.*,
t1.rn - t2.rn AS diff
FROM cte1 t1
INNER JOIN cte2 t2
ON t1.Meter_id = t2.Meter_id AND t1.Realtimeclock = t2.Realtimeclock
)
SELECT
Meter_id,
MIN(Realtimeclock) AS start_time,
MAX(Realtimeclock) AS end_time,
COUNT(I_Y) AS I_Y,
COUNT(I_B) AS I_B,
COUNT(I_X) AS I_X,
COUNT(*) OVER (PARTITION BY TO_DATE(Realtimeclock), Meter_id
ORDER BY MIN(Realtimeclock)) AS spell
FROM cte3
GROUP BY
Meter_id,
TO_DATE(Realtimeclock),
diff;
请注意,此答案假定永远不会 运行 从一个日历日转移到下一个日历日。如果这可能发生,并且您需要考虑到这一点,那么您应该告诉我们计算此类事件的逻辑是什么。
在 SQL 服务器中再次演示,虽然上面的查询是 Oracle 代码并且应该 运行 没有任何问题。
这是一个缺口和孤岛问题。
Meter_id |Realtimeclock |I_Y|I_B|I_X|
201010 |27-09-2018 00:00:00|1.0|2.0|3.0|
201010 |27-09-2018 00:30:00|1.0|2.0|3.0|
201010 |27-09-2018 01:00:00|1.0|2.0|3.0|
201010 |27-09-2018 01:30:00|1.0|2.0|3.0|
201010 |27-09-2018 02:00:00|1.0| 0 |3.0|
201010 |27-09-2018 02:30:00|1.0| 0 |0 |
201010 |27-09-2018 03:00:00|1.0|2.0|3.0|
201010 |27-09-2018 03:30:00|1.0|2.0|3.0|
201011 |27-09-2018 00:00:00|1.0|2.0|3.0|
201011 |27-09-2018 00:30:00|1.0|2.0|3.0|
201010 |28-09-2018 03:00:00|1.0|2.0|3.0|
201010 |28-09-2018 03:30:00|1.0|2.0|3.0|
201011 |28-09-2018 04:00:00|1.0| 0 |0 |
201011 |28-09-2018 00:00:00|1.0|2.0|3.0|
201011 |28-09-2018 00:30:00|1.0|2.0|3.0|
一种方法使用行号差异法:
select * from (
WITH cte1 AS (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY Meter_id ORDER BY Realtimeclock) rn
FROM yourTable t
),
cte2 AS (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY Meter_id ORDER BY Realtimeclock) rn
FROM yourTable t
WHERE I_B <> 0
),
cte3 AS (
SELECT t1.*,
t1.rn - t2.rn AS diff
FROM cte1 t1
INNER JOIN cte2 t2
ON t1.Meter_id = t2.Meter_id AND t1.Realtimeclock = t2.Realtimeclock
)
SELECT
Meter_id,
MIN(Realtimeclock) AS start_time,
MAX(Realtimeclock) AS end_time,
COUNT(I_Y) AS I_Y,
COUNT(I_B) AS I_B,
COUNT(I_X) AS I_X,ROW_NUMBER() OVER (PARTITION BY meter_id ORDER BY meter_id ) AS Spell
FROM cte3
GROUP BY
Meter_id,
diff);
输出应该打印成 ,请让我知道代码中需要的任何更改。
根据上面的 table 我需要日间咒语作为开始时间和结束时间,基于条件 I_Y、I_B、I_X,它是非零值计数table。 在这里我们看到 201010 meter_id 的开始时间有两个法术,因为它们之间存在时间间隔。同样,它必须显示所有法术以及日期和时间戳。
Meter_id |start_time |End_time |I_Y|I_B|I_X|spell
201010 |27-09-2018 00:00:00|27-09-2018 01:30:00|4 |4 |4 |1
201010 |27-09-2018 03:00:00|27-09-2018 03:30:00|4 |4 |4 |2
201011 |27-09-2018 00:00:00|27-09-2018 00:30:00|2 |2 |2 |1
201010 |28-09-2018 03:00:00|27-09-2018 03:30:00|2 |2 |2 |1
201011 |28-09-2018 00:00:00|28-09-2018 00:30:00|2 |2 |2 |1
抛出运行时间错误如下,
[错误] 执行 (35:22):ORA-01830:日期格式图片在转换整个输入字符串之前结束
嗨蒂姆,
请看一下it.It对我会有很大的帮助。
在给出 t运行c(realtimeclock) 而不是 TO_DATE(realtimeclock) ..
后清除以上内容感谢蒂姆的帮助。
您只需对当前方法稍作修改,即可在日期上添加一个分区(除了 meter_id
)。然后,在最后的查询中,添加一个 COUNT
来计算给定仪表和日期的法术数量。
WITH cte1 AS (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY Meter_id, TO_DATE(Realtimeclock)
ORDER BY Realtimeclock) rn
FROM yourTable t
),
cte2 AS (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY Meter_id, TO_DATE(Realtimeclock)
ORDER BY Realtimeclock) rn
FROM yourTable t
WHERE I_B <> 0
),
cte3 AS (
SELECT t1.*,
t1.rn - t2.rn AS diff
FROM cte1 t1
INNER JOIN cte2 t2
ON t1.Meter_id = t2.Meter_id AND t1.Realtimeclock = t2.Realtimeclock
)
SELECT
Meter_id,
MIN(Realtimeclock) AS start_time,
MAX(Realtimeclock) AS end_time,
COUNT(I_Y) AS I_Y,
COUNT(I_B) AS I_B,
COUNT(I_X) AS I_X,
COUNT(*) OVER (PARTITION BY TO_DATE(Realtimeclock), Meter_id
ORDER BY MIN(Realtimeclock)) AS spell
FROM cte3
GROUP BY
Meter_id,
TO_DATE(Realtimeclock),
diff;
请注意,此答案假定永远不会 运行 从一个日历日转移到下一个日历日。如果这可能发生,并且您需要考虑到这一点,那么您应该告诉我们计算此类事件的逻辑是什么。
在 SQL 服务器中再次演示,虽然上面的查询是 Oracle 代码并且应该 运行 没有任何问题。