You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用函数简化PostgreSQL中重复的CTE代码?

PostgreSQL重复CTE封装优化方案

一、创建带参数的自定义函数(完美匹配“封装重复逻辑”需求)

根据你的CTE结构,可分为4类重复逻辑,分别创建函数,原查询仅需替换CTE定义部分,核心关联逻辑无需改动:

1. 旧表(2018-2019)销售统计函数

对应sales_2018、sales_2019,封装查询gh_consolidated.bestellungen_artikel的逻辑:

CREATE OR REPLACE FUNCTION get_sales_old(p_year integer)
RETURNS TABLE(article_id integer, article_number text, netto numeric, quantity integer)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY
    SELECT
        ba.article_id,
        ba.article_number,
        SUM(ba.position_total)::numeric AS netto,
        SUM(ba.quantity)::integer AS quantity
    FROM gh_consolidated.bestellungen_artikel ba
    LEFT JOIN gh_consolidated.vt_myzeit t ON t.ansi = ba.ordertime::date
    WHERE t.year_reporting::double precision = p_year
      AND ba.order_status <> 'Storniert / Abgelehnt'
    GROUP BY ba.article_id, ba.article_number;
END;
$$;

调用替换:原CTE的sales_2018 as (...)改为:

, sales_2018 AS (SELECT * FROM get_sales_old(2018))

2. 新表(2020+)销售统计函数

对应sales_2020、sales_2021、sales_2022,封装查询02_business_layer.orderposition的逻辑:

CREATE OR REPLACE FUNCTION get_sales_new(p_year integer)
RETURNS TABLE(article_number text, sales numeric, quantity integer)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY
    SELECT
        op.article_number,
        SUM(op.sales_price_net_eur_inkl_order_pos_discount)::numeric AS sales,
        SUM(op.quantity)::integer AS quantity
    FROM "02_business_layer".orderposition op
    WHERE op.orderyear = p_year::text
      AND op.auftragsstatus_stf1 NOT IN ('Abgesagt')
    GROUP BY op.article_number;
END;
$$;

调用替换:原CTE的sales_2020 as (...)改为:

, sales_2020 AS (SELECT * FROM get_sales_new(2020))

3. 新表销售YTD(年初至今)统计函数

对应sales_2021_ytd、sales_2022_ytd、sales_2023_ytd:

CREATE OR REPLACE FUNCTION get_sales_ytd(p_year integer)
RETURNS TABLE(article_number text, sales numeric, quantity integer)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY
    SELECT
        op.article_number,
        SUM(op.sales_price_net_eur_inkl_order_pos_discount)::numeric AS sales,
        SUM(op.quantity)::integer AS quantity
    FROM "02_business_layer".orderposition op
    WHERE op.orderyear = p_year::text
      AND op.auftragsstatus_stf1 NOT IN ('Abgesagt')
      AND op.order_creationdate <= NOW() - INTERVAL '1 year' * (EXTRACT(YEAR FROM NOW()) - p_year)
    GROUP BY op.article_number;
END;
$$;

调用替换:原CTE的sales_2021_ytd as (...)改为:

, sales_2021_ytd AS (SELECT * FROM get_sales_ytd(2021))

4. 营收统计函数(含常规与YTD)

4.1 旧表(2018-2019)营收函数

对应revenue_2018、revenue_2019:

CREATE OR REPLACE FUNCTION get_revenue_old(p_year integer)
RETURNS TABLE(article_id integer, article_number text, revenue numeric, quantity integer)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY
    SELECT
        ba.article_id,
        ba.article_number,
        SUM(ba.position_total)::numeric AS revenue,
        SUM(ba.quantity)::integer AS quantity
    FROM gh_consolidated.bestellungen_artikel ba
    LEFT JOIN gh_consolidated.vt_myzeit t ON t.ansi = ba.delivered_earliest::date
    WHERE ba.delivered_earliest IS NOT NULL
      AND ba.order_status <> 'Storniert / Abgelehnt'
      AND t.year_reporting::integer = p_year
    GROUP BY ba.article_id, ba.article_number;
END;
$$;

4.2 新表(2020+)营收函数

对应revenue_2020、revenue_2021、revenue_2022:

CREATE OR REPLACE FUNCTION get_revenue_new(p_year integer)
RETURNS TABLE(article_number text, revenue numeric, quantity integer)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY
    SELECT
        op.article_number,
        SUM(op.sales_price_net_eur_inkl_order_pos_discount)::numeric AS revenue,
        SUM(op.quantity)::integer AS quantity
    FROM "02_business_layer".orderposition op
    WHERE DATE_PART('year', op.delivered_earliest_date_new) = p_year
      AND op.auftragsstatus_stf1 NOT IN ('Abgesagt')
    GROUP BY op.article_number;
END;
$$;

4.3 新表营收YTD函数

对应revenue_2021_ytd、revenue_2022_ytd、revenue_2023_ytd:

CREATE OR REPLACE FUNCTION get_revenue_ytd(p_year integer)
RETURNS TABLE(article_number text, revenue numeric, quantity integer)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY
    SELECT
        op.article_number,
        SUM(op.sales_price_net_eur_inkl_order_pos_discount)::numeric AS revenue,
        SUM(op.quantity)::integer AS quantity
    FROM "02_business_layer".orderposition op
    WHERE DATE_PART('year', op.delivered_earliest_date_new) = p_year
      AND op.auftragsstatus_stf1 NOT IN ('Abgesagt')
      AND op.delivered_earliest_date_new <= NOW() - INTERVAL '1 year' * (EXTRACT(YEAR FROM NOW()) - p_year)
    GROUP BY op.article_number;
END;
$$;

二、参数化CTE(无需创建数据库对象,临时优化)

如果不想创建函数,可先用通用CTE聚合全量数据,再通过过滤得到对应年份结果,适合临时调整:

-- 先聚合所有新表销售数据
, all_sales_new AS (
    SELECT
        op.article_number,
        op.orderyear::integer AS year,
        SUM(op.sales_price_net_eur_inkl_order_pos_discount) AS sales,
        SUM(op.quantity) AS quantity
    FROM "02_business_layer".orderposition op
    WHERE op.auftragsstatus_stf1 NOT IN ('Abgesagt')
    GROUP BY op.article_number, op.orderyear
)
-- 原CTE替换为过滤逻辑
, sales_2020 AS (SELECT article_number, sales, quantity FROM all_sales_new WHERE year = 2020)
, sales_2021 AS (SELECT article_number, sales, quantity FROM all_sales_new WHERE year = 2021)

此方式无需创建函数,但需先聚合全量数据,适合数据量较小的场景。


内容的提问来源于stack exchange,提问作者Teatimeman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 09:28:15