简体   繁体   English

合并来自多个 CTE 的数据 PostgreSQL

[英]Combining data from multiple CTEs PostgreSQL

I have the following query:我有以下查询:

CREATE TYPE udt_insert_count AS
(
    insert_proxy_count int,
    insert_city_count int,
    insert_isp_count int,
    update_proxy_count int
);

CREATE OR REPLACE FUNCTION fn_insert_proxies(isps json, cities json, proxies json)
RETURNS udt_insert_count AS $func$
DECLARE result_count udt_insert_count;
BEGIN
    WITH isp_count AS
    (
        INSERT INTO isp (id, name)
        SELECT * FROM json_populate_recordset(null::udt_isp, isps) as udt_isps
        ON CONFLICT (id)
        DO NOTHING RETURNING xmax
    ),
    city_count AS
    (
        INSERT INTO city (proxy_address, latitude, longitude, name, sub_division1, sub_division1_code, sub_division2, sub_division2_code, postal_code, accuracy_radius, timezone)
        SELECT * FROM json_populate_recordset(null::udt_city, cities)
        ON CONFLICT (proxy_address)
        DO NOTHING RETURNING xmax
    ),
    proxy_count AS
    (
        INSERT INTO proxy as p (address, port, country_code, type_id, access_type_id, provider_id, isp_id, speed, uptime, created_date, modified_date)
        SELECT *, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP FROM json_populate_recordset(null::udt_proxy, proxies)
        ON CONFLICT ON CONSTRAINT proxy_address_port_uc
        DO UPDATE SET speed = p.speed, uptime = p.uptime, modified_date = CURRENT_TIMESTAMP RETURNING xmax
    )
    
    SELECT SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END)
    INTO result_count.insert_city_count
    FROM city_count;
    
    SELECT SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END), SUM(CASE WHEN xmax::text::int > 0 THEN 1 ELSE 0 END)
    INTO result_count.insert_proxy_count, result_count.update_proxy_count
    FROM proxy_count;
    
    SELECT SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END)
    INTO result_count.insert_isp_count
    FROM isp_count;
    
    RETURN result_count;
END
$func$  LANGUAGE plpgsql;

Basically what I want to do is to gather the insert and update counts into three different CTEs isp_count, city_count and proxy_count and then I would like to select them into the declared type.基本上我想做的是将插入和更新计数收集到三个不同的 CTE isp_count、city_count 和 proxy_count 中,然后我想将它们 select 放入声明的类型中。

However when I execute this I get: relation "proxy_count" does not exist但是,当我执行此操作时,我得到: relation "proxy_count" does not exist

And I am sure that this is not the way to do it.而且我确信这不是做到这一点的方法。

Is there any way I could do this in 1 go?有什么办法可以在 1 go 中做到这一点? : :

 SELECT SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END)
 INTO result_count.insert_city_count
 FROM city_count;

 SELECT SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END), SUM(CASE WHEN xmax::text::int > 0 THEN 1 ELSE 0 END)
 INTO result_count.insert_proxy_count, result_count.update_proxy_count
 FROM proxy_count;

 SELECT SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END)
 INTO result_count.insert_isp_count
 FROM isp_count;

Is there any reason not to just decouple the CTEs?有什么理由不去耦合 CTE 吗? You don't use them in the same SELECT statements.您不会在相同的 SELECT 语句中使用它们。

CREATE OR REPLACE FUNCTION fn_insert_proxies(isps json, cities json, proxies json)
RETURNS udt_insert_count AS $func$
DECLARE result_count udt_insert_count;
BEGIN
    WITH city_count AS
    (
        INSERT INTO city (proxy_address, latitude, longitude, name, sub_division1, sub_division1_code, sub_division2, sub_division2_code, postal_code, accuracy_radius, timezone)
        SELECT * FROM json_populate_recordset(null::udt_city, cities)
        ON CONFLICT (proxy_address)
        DO NOTHING RETURNING xmax
    )
    SELECT SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END)
    INTO result_count.insert_city_count
    FROM city_count;

    WITH proxy_count AS
    (
        INSERT INTO proxy as p (address, port, country_code, type_id, access_type_id, provider_id, isp_id, speed, uptime, created_date, modified_date)
        SELECT *, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP FROM json_populate_recordset(null::udt_proxy, proxies)
        ON CONFLICT ON CONSTRAINT proxy_address_port_uc
        DO UPDATE SET speed = p.speed, uptime = p.uptime, modified_date = CURRENT_TIMESTAMP RETURNING xmax
    )
    SELECT SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END),
           SUM(CASE WHEN xmax::text::int > 0 THEN 1 ELSE 0 END)
    INTO result_count.insert_proxy_count, result_count.update_proxy_count
    FROM proxy_count;

    WITH isp_count AS
    (
        INSERT INTO isp (id, name)
        SELECT * FROM json_populate_recordset(null::udt_isp, isps) as udt_isps
        ON CONFLICT (id)
        DO NOTHING RETURNING xmax
    )
    SELECT SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END)
    INTO result_count.insert_isp_count
    FROM isp_count;

    RETURN result_count;
END
$func$  LANGUAGE plpgsql;

As an aside, I recommend using COUNT(*) FILTER (WHERE xmax = 0) instead of SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END)顺便说一句,我建议使用COUNT(*) FILTER (WHERE xmax = 0)而不是SUM(CASE WHEN xmax = 0 THEN 1 ELSE 0 END)

I found your question interesting, so I took a look in the CTE documentation , where it has an interesting line:我发现您的问题很有趣,所以我查看了 CTE文档,其中有一行很有趣:

The sub-statements in WITH are executed concurrently with each other and with the main query. WITH 中的子语句彼此并发执行,并与主查询同时执行。 Therefore, when using data-modifying statements in WITH, the order in which the specified updates actually happen is unpredictable.因此,在 WITH 中使用数据修改语句时,指定更新实际发生的顺序是不可预测的。 All the statements are executed with the same snapshot (see Chapter 13), so they cannot “see” one another's effects on the target tables.所有语句都使用相同的快照执行(请参阅第 13 章),因此它们无法“看到”彼此对目标表的影响。

Unfortunately don't have access to PostgreSQL right now, but will update later if I can play with it.不幸的是,现在无法访问 PostgreSQL,但如果我可以玩的话,稍后会更新。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM