Skip to content

TPC-DS Q74 — Store/Web Customer Year-over-Year Growth

TPC-DS benchmark · all TPC-DS queries

Finds customers where web sales growth exceeds store sales growth

Representative SQL

Rendered for DataFusion with default parameters.

WITH year_total AS (
SELECT
c_customer_id AS customer_id,
c_first_name AS customer_first_name,
c_last_name AS customer_last_name,
d_year AS year,
MAX(ss_net_paid) AS year_total,
's' AS sale_type
FROM customer, store_sales, date_dim
WHERE
c_customer_sk = ss_customer_sk
AND ss_sold_date_sk = d_date_sk
AND d_year IN (2001, 2001 + 1)
GROUP BY
c_customer_id,
c_first_name,
c_last_name,
d_year
UNION ALL
SELECT
c_customer_id AS customer_id,
c_first_name AS customer_first_name,
c_last_name AS customer_last_name,
d_year AS year,
MAX(ws_net_paid) AS year_total,
'w' AS sale_type
FROM customer, web_sales, date_dim
WHERE
c_customer_sk = ws_bill_customer_sk
AND ws_sold_date_sk = d_date_sk
AND d_year IN (2001, 2001 + 1)
GROUP BY
c_customer_id,
c_first_name,
c_last_name,
d_year
)
SELECT
t_s_secyear.customer_id,
t_s_secyear.customer_first_name,
t_s_secyear.customer_last_name
FROM year_total AS t_s_firstyear, year_total AS t_s_secyear, year_total AS t_w_firstyear, year_total AS 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.year = 2001
AND t_s_secyear.year = 2001 + 1
AND t_w_firstyear.year = 2001
AND t_w_secyear.year = 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 NULL
END > CASE
WHEN t_s_firstyear.year_total > 0
THEN t_s_secyear.year_total / t_s_firstyear.year_total
ELSE NULL
END
ORDER BY
2,
1,
3
LIMIT 100

Representative DataFrame

Expression family (Polars, PySpark, DataFusion), default parameters.

def q74_expression_impl(ctx: DataFrameContext) -> Any:
col = ctx.col
lit = ctx.lit
params = get_parameters(74)
year, aggregate, order_by = _q74_parameters(params)
customer, store_sales, web_sales, date_dim = _tables(ctx, "customer", "store_sales", "web_sales", "date_dim")
def year_total(sales: Any, customer_key: str, date_key: str, amount: str, the_year: int, alias: str) -> Any:
totals = (
sales.join(customer, left_on=customer_key, right_on="c_customer_sk", how="inner")
.join(date_dim.filter(col("d_year") == lit(the_year)), left_on=date_key, right_on="d_date_sk", how="inner")
.group_by(list(_Q74_OUTPUT))
.agg(
getattr(col(amount), aggregate)().alias(alias),
col(amount).count().alias("_values"),
)
)
total = ctx.when(col("_values") > lit(0)).then(col(alias)).otherwise(lit(None))
return totals.with_columns(total.alias(alias)).drop("_values")
ss_y1 = year_total(store_sales, "ss_customer_sk", "ss_sold_date_sk", "ss_net_paid", year, "ss_y1_total")
ss_y2 = year_total(store_sales, "ss_customer_sk", "ss_sold_date_sk", "ss_net_paid", year + 1, "ss_y2_total")
ws_y1 = year_total(web_sales, "ws_bill_customer_sk", "ws_sold_date_sk", "ws_net_paid", year, "ws_y1_total")
ws_y2 = year_total(web_sales, "ws_bill_customer_sk", "ws_sold_date_sk", "ws_net_paid", year + 1, "ws_y2_total")
result = (
ss_y2.join(ss_y1.select("c_customer_id", "ss_y1_total"), on="c_customer_id", how="inner")
.join(ws_y1.select("c_customer_id", "ws_y1_total"), on="c_customer_id", how="inner")
.join(ws_y2.select("c_customer_id", "ws_y2_total"), on="c_customer_id", how="inner")
)
result = result.filter(
(col("ss_y1_total") > 0)
& (col("ws_y1_total") > 0)
& (
ctx.when(col("ws_y1_total") > 0).then(col("ws_y2_total") / col("ws_y1_total")).otherwise(None)
> ctx.when(col("ss_y1_total") > 0).then(col("ss_y2_total") / col("ss_y1_total")).otherwise(None)
)
)
return result.select(list(_Q74_OUTPUT)).sort(order_by, nulls_last=True).head(100)

Get the exact query for your run

The blocks above are representative. To get the precise statement a specific platform runs at a specific scale, substitute your own values:

from benchbox import TPCDS
bench = TPCDS(scale_factor=1.0) # your scale factor
queries = bench.get_queries(dialect="datafusion") # your platform's dialect; drop dialect= for native SQL
print(queries['74'])

Parameter substitution for this benchmark is seed-driven; pass a seed for a specific stream’s parameters:

print(bench.get_query(74, scale_factor=1.0, seed=42, dialect="datafusion"))

For the DataFrame form:

from benchbox.core.query_catalog import get_dataframe_render
print(get_dataframe_render('tpcds', '74', family="expression").source) # or family="pandas"

Source

Query text: _sources/tpc-ds/query_templates/query74.tpl