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

Converting this SQL query to use a window function, or any other more efficient method

我有一个巨大的 table,其中包含有关所有客户的详细信息,例如他们的 phone 电话号码和电子邮件地址。 table 每个客户可以有多行,表示他们在某个时候更改了他们的 phone 电话号码、电子邮件地址或其他个人资料信息。每行都有一个日期列,表示个人资料更改发生的日期(即当天他们的个人资料状态)。

我想提取每个客户曾经在其个人资料中拥有的每个 phone 号码和电子邮件地址,并添加一个布尔标志(称为 'live')以指示哪个 phone号码和电子邮件当前与他们的个人资料相关联(即我们在 table 中为该客户提供的最新记录)。

这是我当前的查询:

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

我认为这是非常低效的,因为数百万不同的客户有数十亿行。我怎样才能改进这个查询来实现我想要的(可能是 window 函数?),或者 soem 会以完全不同的方式更好地实现我想要的吗?

您可以使用row_number()设置标志:

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 支持布尔值,因此您不需要 CASE 表达式。但我保留了它,因为你的代码中有它。

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

CASE
    WHEN day = MAX(temp.day) over (partition by customer_id)
    THEN true
    ELSE false
END AS live

显示每个phone每个客户的个人资料中的每个phone号码和电子邮件地址,意思是这两个资源的每个组合仅一次 使用以下查询。

这里是样本日期

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 
;

查询

with live as (
select CUSTOMER_ID, PHONE_NUMBER, EMAIL, DAY ,
case when row_number() over (partition by CUSTOMER_ID order by day DESC) = 1 then 1 else 0 end is_live
from tab) 
select   CUSTOMER_ID, PHONE_NUMBER, EMAIL, 
case when max(is_live) = 1 then 'true' else 'false' end is_life
from live
group by CUSTOMER_ID, PHONE_NUMBER, EMAIL

产生

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

子查询标识最后一个(实时)记录,GROUP BY 查询仅生成资源的唯一组合(与您的解决方案中的 SELECT DISTINCT 类似,但更好 - 如果相同则失败资源在实时和非实时行中)。