Impala:选择仅返回表2中具有max(date)但小于表1中的日期的行的数据

时间:2017-06-13 05:47:09

标签: sql common-table-expression impala row-number correlated-subquery

我的情况几乎与以下主题中讨论的示例相同。

select data where date is max(date) less than x

我有表格

s_currency

cdate                          ratio                     currency                                            
-------------------------------------------------------------------
2017-06-06 00:00:00.0          1                         USD                                                 
2017-06-05 00:00:00.0          1                         USD                                                 
2017-06-04 00:00:00.0          1                         USD      

s_transaction

tdate                          amount                    currency                                            
-------------------------------------------------------------------
2017-06-05 00:00:00.0          100                       USD                                                 
2017-06-08 00:00:00.0          55                        USD      
2017-06-08 00:00:00.0          55                        USD      
2017-06-08 00:00:00.0          60                        USD      

我想要的结果是

tdate                          amount    currency    ratio     cdate                                          
--------------------------------------------------------------------------
2017-06-05 00:00:00.0          100        USD          1      2017-06-05 00:00:00.0                                
2017-06-08 00:00:00.0          55         USD          1      2017-06-06 00:00:00.0
2017-06-08 00:00:00.0          55         USD          1      2017-06-06 00:00:00.0    
2017-06-08 00:00:00.0          60         USD          1      2017-06-06 00:00:00.0                                    

其中cdate应基于tdate,即其最新货币日期等于或早于交易日期。

另一篇文章中的解决方案在select子句中使用子查询,但它在Impala中不起作用。我尝试使用CTE并与子查询连接,但它们都没有返回所需的结果。以下是我构建的一些查询及其结果

 SELECT tdate, amount, t1.currency, ratio, cdate FROM s_transaction t1 , s_currency t2 
        WHERE t1.currency = t2.currency AND 
        t2.cdate = (select max(cdate) from s_currency 
                     where currency = t1.currency and cdate <= t1.tdate);

但这会加入表并返回所有货币的交易少于交易日期,所以我得到了

   tdate                         amount       currency     ratio        cdate
------------------------------------------------------------------------------------------
2017-06-08 00:00:00.0          60           USD          1            2017-06-05 00:00:00.0          
2017-06-08 00:00:00.0          60           USD          1            2017-06-06 00:00:00.0          
2017-06-08 00:00:00.0          60           USD          1            2017-06-04 00:00:00.0          
2017-06-05 00:00:00.0          100          USD          1            2017-06-05 00:00:00.0          
2017-06-05 00:00:00.0          100          USD          1            2017-06-04 00:00:00.0          
2017-06-08 00:00:00.0          55           USD          1            2017-06-05 00:00:00.0          
2017-06-08 00:00:00.0          55           USD          1            2017-06-06 00:00:00.0          
2017-06-08 00:00:00.0          55           USD          1            2017-06-04 00:00:00.0          
2017-06-08 00:00:00.0          55           USD          1            2017-06-05 00:00:00.0          
2017-06-08 00:00:00.0          55           USD          1            2017-06-06 00:00:00.0          
2017-06-08 00:00:00.0          55           USD          1            2017-06-04 00:00:00.0  

所以,我摆脱了max(cdate)并使用了限制1的订单,但是Impala抛出了一个错误,即不支持的相关子查询带有LIMIT子句。

我尝试过使用CTE并写了

with lastupdate as (
select t2.currency, ratio, max(cdate) as cdate from s_currency t2 join s_transaction t1
on cdate <= tdate and t2.currency = t1.currency group by t2.currency, ratio limit 1
) select t11.*, lst.ratio, lst.cdate
from s_transaction t11 join lastupdate lst        

但是在这里,CTE选择一个值并将其用于所有交易,所以我得到了

tdate                         amount       currency     ratio        cdate
-------------------------------------------------------------------------------------------
2017-06-05 00:00:00.0          100          USD          1            2017-06-06 00:00:00.0          
2017-06-08 00:00:00.0          55           USD          1            2017-06-06 00:00:00.0          
2017-06-08 00:00:00.0          60           USD          1            2017-06-06 00:00:00.0          
2017-06-08 00:00:00.0          55           USD          1            2017-06-06 00:00:00.0          

05年6月的交易,应该有一个cdate为05年6月。

我甚至尝试在子查询中使用row_number()函数,但是它无法解析t1.tdate来比较日期的值。

我如何实现我的目标?

3 个答案:

答案 0 :(得分:0)

我在mysql db中测试了以下SQL语句,它可以工作。

select  tdate,amount,b.currency,ratio,max(cdate) from s_transaction a,s_currency b where a.tdate>=b.cdate group by tdate

答案 1 :(得分:0)

我遇到了类似的问题,因此将其发布在这里以供参考。

TL,DR:我们需要使用Impala 的窗口函数来过滤子查询结果。以下是 SQL:

SELECT t.* FROM 
(
   SELECT rank() OVER (ORDER BY cdate DESC) daterank, c1.cdate, t1.*
   FROM s_currency c1, s_transaction t1
   WHERE c1.currency = t1.currency 
   AND c1.cdate <= t1.tdate
) t
WHERE t.daterank = 1;

详情:

问题是 Impala 不支持 coorelated 子查询中的复杂操作。

来自 Impala 官方网站:https://impala.apache.org/docs/build/html/topics/impala_limit.html & https://impala.apache.org/docs/build/html/topics/impala_subqueries.html

  • 限制

    1. EXISTS 和 IN 运算符中使用的相关子查询不能包含 LIMIT 子句
    2. 对于 EXISTS 和 NOT EXISTS 子句,任何将外部查询块中的值与另一个表进行比较的子查询都必须至少使用一个相等比较,而不仅仅是其他类型的比较,例如小于、大于、BETWEEN 或 != .
  • 那么什么是相关子查询?

    相关子查询将来自外部查询块的一个或多个值与子查询的 WHERE 子句中引用的值进行比较。外部 WHERE 子句评估的每一行都可以使用不同的值集进行评估。这些类型的子查询受限于它们可以在内表和外表的列之间进行的比较类型

  • 我发现在这种情况下,Impala 的窗口函数很有用,特别是使用限制(限制 1)或小于(限制 2)从子查询中获得最大等效的排名

    https://impala.apache.org/docs/build/html/topics/impala_analytic_functions.html

答案 2 :(得分:0)

您可以将 CTE 与 Lead 一起使用来创建每个货币比率对其有效的范围,然后在不使用联接中的子查询的情况下加入该范围。我发现它的执行速度比使用排名快得多。

WITH startend as (
    SELECT
        currency,
        ratio,
        cdate as starttime,
        ISNULL( LEAD(cdate) OVER ( PARTITION BY currency ORDER BY cdate), '9999-12-31') endtime
    FROM s_currency)
SELECT
    tdate,
    amount,
    t.currency,
    ratio,
    c.starttime as cdate
FROM s_transaction t
INNER JOIN startend c
    ON t.currency = c.currency
    AND tdate >= starttime
    AND tdate < endtime

不要忘记将所有连接条件添加到分区子句中。