PostgreSQL中的循环表

esbemjvw  于 2023-03-17  发布在  PostgreSQL
关注(0)|答案(2)|浏览(125)

我需要创建一个物化视图,以便从pg_tables循环所有工作列表。
首先,我创建了一个查询,然后尝试创建一个for循环。
这是我的第一个代码没有for循环。

SELECT 
count(visit_num) AS total_claim_count,
count(user_id) as assigned_count,
(count(visit_num)-count(user_id)) AS not_assigned_count,
count(case when doe <= (SELECT doe - INTERVAL '30 days') and user_id is not null then 1 end ) AS Assigned_Last_30_Days,
(count(user_id)-count(case when doe <= (SELECT doe - INTERVAL '30 days') and user_id is not null then 1 end )) AS not_Assigned_last_30_days,
count(case when doe >= (SELECT doe - INTERVAL '30 days') and user_id is not null then 1 end ) AS Assigned_before_30_Days,
count(case when doe > (SELECT doe - INTERVAL '30 days') and user_id is null then 1 end ) AS not_Assigned_before_30_days,
count(CASE WHEN date_worked is not null then 1 end) followed_up,
count(CASE WHEN last_denied_date is not null then 1 end) denials,
count(case when status_and_action_code='No Status - Need to follow-up' then 1 end) AS no_status
FROM Worklist_123456

这是代码与循环我尝试。但它是行不通的。

DO $$

DECLARE
    pg_tables varchar(1000000);
    tbl_name record;
    tablename varchar(1000000);
    
begin
For tbl_name IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename LIKE 'worklist¥_%' ESCAPE '¥'

loop

    tablename := tbl_name.tablename;

    SELECT 
    count(visit_num) AS total_claim_count,
    count(user_id) as assigned_count,
    (count(visit_num)-count(user_id)) AS not_assigned_count,
    count(case when doe <= (SELECT doe - INTERVAL '30 days') and user_id is not null then 1 end ) AS Assigned_Last_30_Days,
    (count(user_id)-count(case when doe <= (SELECT doe - INTERVAL '30 days') and user_id is not null then 1 end )) AS not_Assigned_last_30_days,
    count(case when doe >= (SELECT doe - INTERVAL '30 days') and user_id is not null then 1 end ) AS Assigned_before_30_Days,
    count(case when doe > (SELECT doe - INTERVAL '30 days') and user_id is null then 1 end ) AS not_Assigned_before_30_days,
    count(CASE WHEN date_worked is not null then 1 end) followed_up,
    count(CASE WHEN last_denied_date is not null then 1 end) denials,
    count(case when status_and_action_code='No Status - Need to follow-up' then 1 end) AS no_status
    FROM tablename;

END LOOP;

END; $$;

它给出了这样的错误。

ERROR:  column reference "tablename" is ambiguous
LINE 1: SELECT tablename FROM pg_tables WHERE schemaname = 'public' ...
               ^
DETAIL:  It could refer to either a PL/pgSQL variable or a table column.
QUERY:  SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename LIKE 'worklist¥_%' ESCAPE '¥'
CONTEXT:  PL/pgSQL function inline_code_block line 9 at FOR over SELECT rows
SQL state: 42702

任何人都可以帮助我解决这个问题。谢谢。

bjg7j2ky

bjg7j2ky1#

正如@Frank Heikens所指出的,您是在用动态SQL术语思考问题:demo

DO $proc$
DECLARE tbl_name record;
BEGIN
FOR tbl_name IN SELECT tablename 
                FROM  pg_tables 
                WHERE schemaname = 'public' 
                AND   tablename LIKE 'worklist¥_%' ESCAPE '¥'
LOOP execute format($dynamic_sql_query$
        INSERT INTO anon_proc_output
        SELECT 
            %1$L             AS source_table --first argument (1$), as a string Literal (L)
          , count(visit_num) AS total_claim_count
          , count(user_id)   AS assigned_count
          ,(count(visit_num) 
           -count(user_id) ) AS not_assigned_count
          , count(user_id)filter(where doe <= (SELECT doe - INTERVAL '30 days') )   AS Assigned_Last_30_Days
          ,(count(user_id) 
           -count(user_id)filter(where doe <= (SELECT doe - INTERVAL '30 days') ) ) AS not_Assigned_last_30_days
          , count(user_id)filter(where doe >= (SELECT doe - INTERVAL '30 days') )   AS Assigned_before_30_Days
          , count(*)filter(where doe > (SELECT doe - INTERVAL '30 days') 
                           and user_id is null)                                     AS not_Assigned_before_30_days
          , count(date_worked)      AS followed_up
          , count(last_denied_date) AS denials
          , count(*)filter(where status_and_action_code='No Status - Need to follow-up') AS no_status
        FROM %1$s; --still the same first argument, hence the 1$, but this time as-is, hence the s
        $dynamic_sql_query$
      , tbl_name.tablename);--this is used to replace %1$L %1$s
END LOOP;
END $proc$;
  1. count(case...)正在模拟聚合FILTER子句。
  2. doe>=(SELECT doe-INTERVAL '30 days')没有什么意义,你要检查的是这个值是否大于它自己,或者说是减小了,这实际上就是some_number>=(some_number-1)
  3. count(arg)不计算arg为空的行:
    count ( "any" ) → bigint计算输入值不为空的输入行数。
  • 因此count(case when arg is not null then 1 end)count(arg)相同,
  • count(case when (condition) and arg is not null then 1 end)count(arg)filter(where (condition))相同。

您可能需要重新考虑在何处以及为什么使用count(arg),而不是使用count(*)count(case when arg is not null then 1 end)

pgky5nke

pgky5nke2#

∮ ∮ ∮ ∮
发布alternative解决方案separately,以演示来自@Frank Heikens的其他建议。
您通常会选择一个预先存在的特性来区分分区,比如给定分区应该包含的日期范围、数据源位置标识符、用户组。我添加一个虚拟列作为键,只是为了说明这个想法:demo

create table worklists (like worklist_123457 including all, worklist_name text)
    partition by list(worklist_name);

alter table worklist_123456 add column worklist_name text default 'worklist_123456';
alter table worklist_123457 add column worklist_name text default 'worklist_123457';

alter table worklists attach partition worklist_123456 for values in ('worklist_123456');
alter table worklists attach partition worklist_123457 for values in ('worklist_123457');

然后,您的循环变成了主集合聚合表上的简单group by

SELECT 
    worklist_name    AS source_table
  , count(visit_num) AS total_claim_count
  , count(user_id)   AS assigned_count
  ,(count(visit_num) 
   -count(user_id) ) AS not_assigned_count
  , count(user_id)filter(where doe <= (SELECT doe - INTERVAL '30 days') )   AS Assigned_Last_30_Days
  ,(count(user_id) 
   -count(user_id)filter(where doe <= (SELECT doe - INTERVAL '30 days') ) ) AS not_Assigned_last_30_days
  , count(user_id)filter(where doe >= (SELECT doe - INTERVAL '30 days') )   AS Assigned_before_30_Days
  , count(*)filter(where doe > (SELECT doe - INTERVAL '30 days') 
                   and user_id is null)                                     AS not_Assigned_before_30_days
  , count(date_worked)      AS followed_up
  , count(last_denied_date) AS denials
  , count(*)filter(where status_and_action_code='No Status - Need to follow-up') AS no_status
FROM worklists
group by worklist_name;

相关问题