我有一个SQL相关的问题,我会喜欢一些帮助,因为一个合适的答案一直困扰我 .
Background
我正在使用一个供应商产品,该产品具有作为后端的Oracle数据库 . 我有能力编写任何特殊的SQL来查询底层表,但我不能对它们的底层结构(或数据模型本身)进行任何更改 . 我感兴趣的表目前有大约1M行,基本上跟踪用户会话 . 它有4列感兴趣: session_id
(每个会话是主键和唯一), user_name
, start_date
(跟踪会话开始的日期)和 stop_date
(跟踪会话结束的日期) . 我的目标是根据月,日和小时执行活动会话的数据聚合,给出设置的开始日期和结束日期 . 我需要创建一个视图(或3个单独的视图),它可以自己执行聚合,也可以作为中间对象,然后我可以从中查询和执行聚合 . 我理解最终的SQL /视图实际上可能需要3个不同的视图(一个用于一个月,一个用于一天,一个用于一小时),但在我看来,无论时间段如何,概念(一旦实现)应该是相同的 .
Current table example
表名= web_session
| Session_id | user_name | start_date | stop_date
----------------------------------------------------------------------------
| 1 | joe | 4/20/2017 10:42:10 PM | 4/21/2017 2:42:10 AM |
| 2 | matt | 4/20/2017 5:43:10 PM | 4/20/2017 5:59:10 PM |
| 3 | matt | 4/20/2017 3:42:10 PM | 4/20/2017 5:42:10 PM |
| 4 | joe | 4/20/2017 11:20:10 AM | 4/20/2017 4:42:10 PM |
| 5 | john | 4/20/2017 8:42:10 AM | 4/20/2017 11:42:10 AM |
| 6 | matt | 4/20/2017 7:42:10 AM | 4/20/2017 11:42:10 PM |
| 7 | joe | 4/19/2017 11:20:10 PM | 4/20/2017 1:42:10 AM |
Ideal Output For Hour View
例如,-12:00可以是0或24
| Date | HR | active_sessions | distinct_users |
------------------------------------------------------------
| 4/21/2017 | 2 | 1 | 1 |
| 4/21/2017 | 1 | 1 | 1 |
| 4/20/2017 | 0 | 1 | 1 |
| 4/20/2017 | 23 | 1 | 1 |
| 4/20/2017 | 22 | 1 | 1 |
| 4/20/2017 | 17 | 2 | 1 |
| 4/20/2017 | 16 | 2 | 2 |
| 4/20/2017 | 15 | 2 | 2 |
| 4/20/2017 | 14 | 1 | 1 |
| 4/20/2017 | 13 | 1 | 1 |
| 4/20/2017 | 12 | 1 | 1 |
| 4/20/2017 | 11 | 3 | 3 |
| 4/20/2017 | 10 | 2 | 2 |
| 4/20/2017 | 9 | 2 | 2 |
| 4/20/2017 | 8 | 2 | 2 |
| 4/20/2017 | 7 | 1 | 1 |
| 4/20/2017 | 1 | 1 | 1 |
| 4/20/2017 | 0 | 1 | 1 |
| 4/19/2017 | 23 | 1 | 1 |
End Goal and Other Options
我最终尝试使用此输出实现的是填充折线图,该折线图显示两个日期之间的月,日或小时(在示例输出中使用)的活动会话数 . 在小时示例中,将沿着X轴使用与HR组合的日期,并且沿Y轴使用活动会话 . 如果用户悬停在图表上的点上,则可以使用不同的用户计数 . FYI活动会话是在间隔期间的任何时间点打开的会话总数 . 不同用户是间隔期间不同用户的总数 . 如果我在同一小时内登录和关闭两次,那么它将是2个活动会话,但只有1个不同的用户 .
Alternative Solutions
这似乎是一个问题,必须提前几次,但从我的所有谷歌搜索和堆栈溢出研究,我似乎无法找到正确的方法 . 如果我正在考虑查询或理想输出不正确,我会打开替换建议,这使我可以获得所需的输出,以在前端适当地填充图表 .
Some SQL I Have Tried (Good Faith Effort)
我尝试了很多查询,但我会从这个开始,因为它是我得到的最接近但是非常慢(不可用),它仍然不会产生我需要的结果 .
Select * FROM (
SELECT
u.YearDt, u.MonthDt, u.DayDt, u.HourDt, u.MinDt,
COUNT(Distinct u.session_id) as unique_sessions,
COUNT(Distinct u.user_name) as unique_users,
LISTAGG(u.user_name, ', ') WITHIN GROUP (ORDER BY u.user_name ASC) as users
FROM
(SELECT EXTRACT(year FROM l.start_date) as YearDt,
EXTRACT(month FROM l.start_date) as MonthDt,
EXTRACT(day FROM l.start_date) as DayDt,
EXTRACT(HOUR FROM CAST(l.start_date AS TIMESTAMP)) as HourDt,
EXTRACT(MINUTE FROM CAST(l.start_date AS TIMESTAMP)) as MinDt,
l.session_id,
l.user_name,
l.start_date as act_date,
1 as is_start
FROM web_session l
UNION ALL
SELECT EXTRACT(year FROM l.stop_date) as YearDt,
EXTRACT(month FROM l.stop_date) as MonthDt,
EXTRACT(day FROM l.stop_date) as DayDt,
EXTRACT(HOUR FROM CAST(l.stop_date AS TIMESTAMP)) as HourDt,
EXTRACT(MINUTE FROM CAST(l.stop_date AS TIMESTAMP)) as MinDt,
l.session_id,
l.user_name,
l.stop_date as act_date,
0 as is_start
FROM web_session l
) u
GROUP BY CUBE ( u.YearDt, u.MonthDt, u.DayDt, u.HourDt, u.MinDt)
) c
3 回答
您可以使用CTE(查询1)或相关的分层查询(查询2)来生成时间范围内的小时数,然后进行聚合 . 这只需要一次表扫描:
SQL Fiddle
Oracle 11g R2 Schema Setup :
Query 1 :
Results :
Execution Plan :
Query 2 :
Results :
Execution Plan :
马特,
您需要做的是生成时间维,作为静态表或在运行时动态生成:
然后外部连接到您的
web_sessions
表:您可以更改group by子句以获取您感兴趣的各种聚合 .
在上面的代码中,我将start_date截断为
ON
子句中的小时,以便开始时间将包含在结果中,否则在该小时内不会完全计算在一小时内没有开始的会话 .我觉得这样的事情会起作用:
我可能没有
LEFT JOIN
100%正确的条件 .希望这可以帮助 .