with year_total as (
select c_customer_id customer_id
,c_first_name customer_first_name
,c_last_name customer_last_name
,c_preferred_cust_flag customer_preferred_cust_flag
,c_birth_country customer_birth_country
,c_login customer_login
,c_email_address customer_email_address
,d_year dyear
,sum(ss_ext_list_price-ss_ext_discount_amt) year_total
,'s' sale_type
from customer
,store_sales
,date_dim
where c_customer_sk = ss_customer_sk
and ss_sold_date_sk = d_date_sk
group by c_customer_id
,c_first_name
,c_last_name
,c_preferred_cust_flag
,c_birth_country
,c_login
,c_email_address
,d_year
union all
select c_customer_id customer_id
,c_first_name customer_first_name
,c_last_name customer_last_name
,c_preferred_cust_flag customer_preferred_cust_flag
,c_birth_country customer_birth_country
,c_login customer_login
,c_email_address customer_email_address
,d_year dyear
,sum(ws_ext_list_price-ws_ext_discount_amt) year_total
,'w' sale_type
from customer
,web_sales
,date_dim
where c_customer_sk = ws_bill_customer_sk
and ws_sold_date_sk = d_date_sk
group by c_customer_id
,c_first_name
,c_last_name
,c_preferred_cust_flag
,c_birth_country
,c_login
,c_email_address
,d_year
)
select
t_s_secyear.customer_id
,t_s_secyear.customer_first_name
,t_s_secyear.customer_last_name
,t_s_secyear.customer_preferred_cust_flag
from year_total t_s_firstyear
,year_total t_s_secyear
,year_total t_w_firstyear
,year_total t_w_secyear
where t_s_secyear.customer_id = t_s_firstyear.customer_id
and t_s_firstyear.customer_id = t_w_secyear.customer_id
and t_s_firstyear.customer_id = t_w_firstyear.customer_id
and t_s_firstyear.sale_type = 's'
and t_w_firstyear.sale_type = 'w'
and t_s_secyear.sale_type = 's'
and t_w_secyear.sale_type = 'w'
and t_s_firstyear.dyear = 2001
and t_s_secyear.dyear = 2001+1
and t_w_firstyear.dyear = 2001
and t_w_secyear.dyear = 2001+1
and t_s_firstyear.year_total > 0
and t_w_firstyear.year_total > 0
and case when t_w_firstyear.year_total > 0 then t_w_secyear.year_total / t_w_firstyear.year_total else 0.0 end
> case when t_s_firstyear.year_total > 0 then t_s_secyear.year_total / t_s_firstyear.year_total else 0.0 end
order by t_s_secyear.customer_id
,t_s_secyear.customer_first_name
,t_s_secyear.customer_last_name
,t_s_secyear.customer_preferred_cust_flag
limit 100;
以下是对这条较为复杂的 SQL 查询语句的详细解释:
### 1. 整体结构与目的
这条 SQL 语句整体上是先通过公共表达式(CTE,Common Table Expression,这里使用 `WITH` 关键字定义)构建了一个名为 `year_total` 的临时结果集,然后基于这个临时结果集进行多表关联、筛选以及排序等操作,最终目的是从客户销售数据中找出满足特定条件的客户记录,并按照一定规则排序后返回前 100 条记录。它主要用于对比分析不同销售渠道(实体店销售和网络销售)下客户在不同年份的销售金额变化情况,筛选出符合增长比例条件的客户信息。
### 2. `year_total` 公共表达式(CTE)部分
- **第一个 `SELECT` 语句(实体店销售相关)**:
- **选择的字段**:
- 从 `customer` 表选取了 `c_customer_id`(客户 ID)、`c_first_name`(客户名字)、`c_last_name`(客户姓氏)、`c_preferred_cust_flag`(客户是否为优选客户标志)、`c_birth_country`(客户出生国家)、`c_login`(客户登录名)、`c_email_address`(客户邮箱地址)这些字段,同时选取了 `date_dim` 表中的 `d_year`(年份)字段,另外通过计算 `sum(ss_ext_list_price - ss_ext_discount_amt)` 得出每个客户在对应年份的实体店销售总金额,并起别名为 `year_total`,还定义了一个常量 `'s'` 作为 `sale_type`(表示销售类型为实体店销售)。
- **表关联与分组条件**:
- 通过 `c_customer_sk = ss_customer_sk` 和 `ss_sold_date_sk = d_date_sk` 分别将 `customer` 表、`store_sales` 表以及 `date_dim` 表进行关联,确保能获取到正确的客户、对应销售记录以及销售日期相关信息。然后按照选取的多个客户相关字段以及 `d_year` 字段进行分组,这样就可以针对每个客户在每年分别统计其实体店销售总金额。
- **`UNION ALL` 操作**:将上述从实体店销售数据统计的结果与下面从网络销售数据统计的结果合并在一起,不会去除重复行(如果有重复的话),形成一个包含实体店销售和网络销售两种渠道数据的综合临时结果集(也就是 `year_total`)。
- **第二个 `SELECT` 语句(网络销售相关)**:
- **选择的字段**:结构与前面实体店销售相关的 `SELECT` 语句类似,同样选取了多个客户相关字段以及 `date_dim` 表的 `d_year` 字段,不过这里计算网络销售总金额的方式是 `sum(ws_ext_list_price - ws_ext_discount_amt)`,并同样定义 `sale_type` 为 `'w'`,表示网络销售类型。
- **表关联与分组条件**:通过 `c_customer_sk = ws_bill_customer_sk` 和 `ws_sold_date_sk = d_date_sk` 将 `customer` 表、`web_sales` 表以及 `date_dim` 表进行关联,然后按照相应字段进行分组,以统计每个客户每年的网络销售总金额。
### 3. 主查询部分(外层 `SELECT` 语句)
- **选择的字段**:从临时结果集 `year_total` 中选取了部分客户相关的字段,包括 `customer_id`、`customer_first_name`、`customer_last_name`、`customer_preferred_cust_flag`,用于展示最终符合条件的客户基本信息。
- **表关联条件**:
- 将 `year_total` 这个临时结果集进行多次自关联,分别起了别名 `t_s_firstyear`(可理解为实体店销售第一年相关数据)、`t_s_secyear`(实体店销售第二年相关数据)、`t_w_firstyear`(网络销售第一年相关数据)、`t_w_secyear`(网络销售第二年相关数据)。通过一系列相等条件关联这些别名表,如 `t_s_secyear.customer_id = t_s_firstyear.customer_id` 等,确保所选取的数据是针对同一个客户在不同销售渠道、不同年份的记录。
- **筛选条件**:
- **销售类型筛选**:限定了 `t_s_firstyear.sale_type = 's'`、`t_w_firstyear.sale_type = 'w'`、`t_s_secyear.sale_type = 's'`、`t_w_secyear.sale_type = 'w'`,明确区分了不同别名表对应的是实体店销售还是网络销售类型。
- **年份筛选**:设置了 `t_s_firstyear.dyear = 2001`、`t_s_secyear.dyear = 2001 + 1`(即 2002 年)、`t_w_firstyear.dyear = 2001`、`t_w_secyear.dyear = 2001 + 1`,这样就聚焦在了 2001 年(作为第一年)和 2002 年(作为第二年)的数据对比上,查看客户在这两年间不同销售渠道的销售金额变化情况。
- **销售金额大于 0 筛选**:通过 `t_s_firstyear.year_total > 0` 和 `t_w_firstyear.year_total > 0` 要求客户在 2001 年不管是实体店销售还是网络销售渠道,都要有大于 0 的销售金额,排除那些没有产生销售的客户记录。
- **销售金额增长比例筛选(通过 `CASE WHEN` 语句实现)**:
- `case when t_w_firstyear.year_total > 0 then t_w_secyear.year_total / t_w_firstyear.year_total else 0.0 end > case when t_s_firstyear.year_total > 0 then t_s_secyear.year_total / t_s_firstyear.year_total else 0.0 end` 这个条件比较复杂,它的核心是分别计算网络销售和实体店销售渠道下客户从 2001 年到 2002 年销售金额的增长比例(如果第一年销售金额大于 0 则进行计算,否则赋值为 0.0),然后筛选出网络销售增长比例大于实体店销售增长比例的客户记录。
- **排序条件**:按照 `t_s_secyear.customer_id`、`t_s_secyear.customer_first_name`、`t_s_secyear.customer_last_name`、`t_s_secyear.customer_preferred_cust_flag` 这些字段对结果进行排序,使得结果集呈现出有规律的排列,方便查看符合条件的客户信息。
- **结果集限制**:使用 `LIMIT 100`,只返回前 100 条满足上述所有条件的记录,避免返回过多数据。
总的来说,这条 SQL 语句先是综合统计了不同销售渠道下客户每年的销售金额情况,然后通过复杂的关联、筛选条件找出在特定年份内网络销售增长比例优于实体店销售增长比例的客户,并展示相关客户基本信息,限制只返回前 100 条这样的记录用于进一步分析。
评论