将此SQL查询转换为使用窗口函数或任何其他更有效的方法

将此SQL查询转换为使用窗口函数或任何其他更有效的方法,sql,optimization,aggregate,window-functions,presto,Sql,Optimization,Aggregate,Window Functions,Presto,我有一个巨大的表格,其中包含所有客户的详细信息,例如他们的电话号码和电子邮件地址。对于每个客户,该表可以有多行,表示他们在某个时候更改了电话号码、电子邮件地址或其他配置文件信息。每一行都有一个day列,指示概要文件更改发生的日期(即那天的概要文件状态) 我想提取每个客户在其个人资料中拥有的每个电话号码和电子邮件地址,并添加一个布尔标志(称为“live”),指示当前与他们的个人资料关联的电话号码和电子邮件(即,我们在该客户的表中拥有的最新记录) 以下是我当前的查询: SELECT DISTINCT

我有一个巨大的表格,其中包含所有客户的详细信息,例如他们的电话号码和电子邮件地址。对于每个客户,该表可以有多行,表示他们在某个时候更改了电话号码、电子邮件地址或其他配置文件信息。每一行都有一个day列,指示概要文件更改发生的日期(即那天的概要文件状态)

我想提取每个客户在其个人资料中拥有的每个电话号码和电子邮件地址,并添加一个布尔标志(称为“live”),指示当前与他们的个人资料关联的电话号码和电子邮件(即,我们在该客户的表中拥有的最新记录)

以下是我当前的查询:

SELECT DISTINCT
    customer_id,
    phone_number,
    email AS email_address,
    CASE
        WHEN day = (
            SELECT
                MAX(temp.day)
            FROM
                customer AS temp
            WHERE
                customer.customer_id = temp.customer_id
        )
        THEN true
        ELSE false
    END AS live
FROM
    customer

考虑到数百万不同的客户有数十亿行,我认为这是非常低效的。如何改进此查询以实现我想要的功能(可能是窗口功能?),或者soem以完全不同的方式更好地实现我想要的功能?

您可以使用
行编号()
设置标志:

SELECT 
    customer_id,
    phone_number,
    email AS email_address,
    (ROW_NUMBER() over(partition by customer_id ORDER BY day DESC) = 1) as is_live
FROM customer
我不确定Presto是否将条件理解为布尔值-如果不理解,您可以:

CASE WHEN ROW_NUMBER() over(partition by customer_id ORDER BY day DESC) = 1
    THEN true
    ELSE false
END as is_live

如果记录是唯一的,则可以使用:

SELECT customer_id, phone_number, email AS email_address,
       (CASE WHEN RANK() OVER (PARTITION BY customer_id ORDER BY day DESC) = 1
             THEN true ELSE false
        END) as is_live
FROM customer c;

Presto支持布尔运算,因此不需要使用
大小写
表达式。但是我保留了它,因为您的代码中有它。

这是您最初的相关子查询,转换为一个组Max,与RANK版本的结果相同:

CASE
    WHEN day = MAX(temp.day) over (partition by customer_id)
    THEN true
    ELSE false
END AS live
要显示每个客户在其个人资料中拥有的每个电话号码和电子邮件地址,即这两种资源的每个组合仅显示一次,请使用以下查询

这是样品日期

create table tab as
select 1 customer_id, 'A' phone_number, '1@1' email, DATE'2020-01-01' day  from dual union all
select 1 customer_id, 'B' phone_number, '1@1' email, DATE'2020-02-01' day  from dual union all
select 1 customer_id, 'A' phone_number, '1@1' email, DATE'2020-03-01' day  from dual union all
select 2 customer_id, 'C' phone_number, '1@1' email, DATE'2020-01-01' day  from dual union all
select 2 customer_id, 'C' phone_number, '1@1' email, DATE'2020-04-01' day  from dual 
;
质疑

哪种产品

CUSTOMER_ID P EMA IS_LI
----------- - --- -----
          1 B 1@x false
          1 A 1@x true 
          2 C 2@x true 

子查询标识最后一条(活动)记录,
分组依据
查询仅生成资源的唯一组合(类似,但最好是在解决方案中选择不同的,如果相同的资源位于活动行和非活动行中,则会失败)。

当包含“日”列时,它们是唯一的,因为每个客户每天最多有一条记录。这可以吗?你能解释一下为什么我会用RANK()代替ROW_NUMBER()?@KOB。在这种情况下,它们可能是等价的。然而,问题的措辞向我暗示,数据中可能有联系。如果一个客户有多条最近日期的记录,那么所有记录都会被标记为
true
,因为您没有指定如何断开联系。我更喜欢这个答案,因为据我所知,行是否唯一并不重要。使用RANK()而不是ROW_NUMBER()会有什么好处吗?@KOB:
RANK()
允许联系(也就是说,如果您每个
客户id
日期都有多条记录)。如果没有关联,则
ROW\u NUMBER()
RANK()
都是相同的。此查询无法处理重用电话号码时的情况,即A->B->A将生成3行,而不是预期的两行B+A(活动)。@MarmiteBomber:OP不想筛选行,他们想标记行。此外,
row_number()
只为每个
customer_id
@MarmiteBomber提供一个
1
:我想提取每个客户在其个人资料中拥有的每个电话号码和电子邮件地址,并添加一个布尔标志(称为“live”)指出当前与他们的个人资料相关联的电话号码和电子邮件对我来说听起来很清楚,请澄清:如果客户的第一个电话号码
a
,然后将其更改为
B
,最后再次将其恢复为
a
(电子邮件保持不变),您希望该客户看到三行还是只有两行?@MarmiteBomber是的,将有三行@KOB,如果您想保留所有行,您应该从查询中删除
不同的
。。。
CUSTOMER_ID P EMA IS_LI
----------- - --- -----
          1 B 1@x false
          1 A 1@x true 
          2 C 2@x true