如何在 Hive/SQL 的 where/have 子句中使用 min()(以避免子查询)

Posted

技术标签:

【中文标题】如何在 Hive/SQL 的 where/have 子句中使用 min()(以避免子查询)【英文标题】:How to use min() in where/having clause (to avoid subquery) in Hive/SQL 【发布时间】:2012-10-31 20:10:14 【问题描述】:

我有一个很大的事件表。每个用户我想在最早的 B 类事件之前计算 A 类事件的发生。

我正在寻找一个优雅的查询。使用了 Hive,所以我不能做子查询

Timestamp Type User 
...        A    X
...        A    X
...        B    X
...        A    X
...        A    X

...        A    Y
...        A    Y
...        A    Y
...        B    Y
...        A    Y

想要的结果:

User Count_Type_A 
X    2
Y    3

我无法通过以下方式获得“截止”时间戳

Select User, min(Timestamp) 
Where Type=B 
Group BY User;

但是,我如何在下一个查询中使用该信息,我想执行以下操作:

SELECT User, count(Timestamp) 
WHERE Type=A AND Timestamp<min(User.Timestamp_Type_B) 
GROUP BY User;

到目前为止,我唯一的想法是首先确定截止时间戳,然后对所有 A 类事件进行连接,然后从结果表中进行选择,但这感觉不对,而且看起来很丑。

我还在考虑这可能是 Hive 的错误类型问题/分析,我应该考虑手写 map-reduce 或 pig 代替。

请帮我指出正确的方向。

【问题讨论】:

【参考方案1】:

第一次更新:

针对 Cilvic 对此答案的第一条评论,我已根据 https://issues.apache.org/jira/browse/HIVE-556 的 cmets 中建议的解决方法将我的查询调整为以下内容:

SELECT [User], COUNT([Timestamp]) AS [Before_First_B_Count]
FROM [Dataset] main
CROSS JOIN (SELECT [User], min([Timestamp]) [First_B_TS] FROM [Dataset]
    WHERE [Type] = 'B'
    GROUP BY [User]) sub 
WHERE main.[Type] = 'A'
AND (sub.[User] = main.[User]) 
AND (main.[Timestamp] < sub.[First_B_TS])
GROUP BY main.[User]

原文:

试一试:

SELECT [User], COUNT([Timestamp]) AS [Before_First_B_Count]
FROM [Dataset] main
JOIN (SELECT [User], min([Timestamp]) [First_B_TS] FROM [Dataset]
    WHERE [Type] = 'B'
    GROUP BY [User]) sub 
        ON (sub.[User] = main.[User]) AND (main.[Timestamp] < sub.[First_B_TS])
WHERE main.[Type] = 'A'
GROUP BY main.[User]

我尽力遵循 hive 语法。如果您有任何问题,请告诉我。我想知道您为什么希望/需要避免子查询。

【讨论】:

我发现hive(可惜不支持cwiki.apache.org/Hive/languagemanual-joins.html 我很抱歉。我又做了一次尝试。请让我知道它是否有效。简而言之,我将 ON 语句移至 WHERE 子句,并使 (INNER) JOIN 成为 CROSS JOIN。【参考方案2】:

一般来说,我 +1 coge.soft 的解决方案。再次供大家参考:

SELECT [User], COUNT([Timestamp]) AS [Before_First_B_Count]
FROM [Dataset] main
JOIN (SELECT [User], min([Timestamp]) [First_B_TS] FROM [Dataset]
    WHERE [Type] = 'B'
    GROUP BY [User]) sub 
        ON (sub.[User] = main.[User]) AND (main.[Timestamp] < sub.[First_B_TS])
WHERE main.[Type] = 'A'
GROUP BY main.[User]

不过,有几点需要注意:

    当没有 B 事件时会发生什么?假设您想要计算每个用户的所有 A 事件,在这种情况下,解决方案中指定的内部联接将不起作用,因为子表中没有该用户的条目。为此,您需要更改为左外连接。

    该解决方案还执行 2 次数据传递 - 一次填充子表,另一次将子表与主表连接。根据您对性能和效率的概念,您可以通过单次数据传递来完成此操作。您可以使用 Hive 的 distribute by 功能按用户分发数据,并编写一个自定义减速器,使用 Hive's transform functionality 以您喜欢的语言进行计数计算。

【讨论】:

@Cilvic ,上面的第二点是你为什么试图避免子查询(根据你的问题的标题)? @coge.soft 不确定我明白你的意思。我认为您提出的解决方案有效。但与此同时,它有点难以理解/阅读。我希望找到更优雅/更容易阅读的东西。我需要先更好地理解分发方式。

以上是关于如何在 Hive/SQL 的 where/have 子句中使用 min()(以避免子查询)的主要内容,如果未能解决你的问题,请参考以下文章

T-SQL 过于昂贵的查询,在 where/have 条件和复合主键中选择

Where have the cheapest adidas Yeezy Boost 350 V2

了解如何在 Spark 中执行 Hive SQL

如何在 Hive SQL 中声明和使用变量?

如何在 Hive SQL 中获取季度至今?

如何在 Hive SQL 中按日期范围独家加入?