【问题标题】:Syntax error in FOR loop declare sectionFOR 循环声明部分中的语法错误
【发布时间】:2014-08-10 19:43:06
【问题描述】:

我正在尝试在 Postgres 中使用 FOR 循环来计算我的数据库中每个地理位置的(变量)范围内的数据平均值。我正在使用 python/psycopg2。代码如下:

query ='''
DECLARE geoids RECORD;
BEGIN
    RAISE NOTICE 'Beginning average calculation';
    FOR geoids IN select*from census_blocks
        WHERE ST_contains((select geom from census_cbsa
             WHERE cbsafp10='%s'),census_blocks.geom)
        LOOP
        INSERT INTO temp_avgs VALUES
            (geoids,
            select avg(select alljobs from accessibility_results
            WHERE geoid=geoids
                AND deptime BETWEEN '%s' and '%s'
                AND threshold='%s')
        END LOOP;
END;
'''

我收到的错误是

psycopg2.ProgrammingError: syntax error at or near "RECORD"
LINE 2:  DECLARE geoids RECORD;

如果我删除此DECLARE 语句(因为我相信选择值上的LOOP 变量会自动声明为RECORD),错误变为:

psycopg2.ProgrammingError: syntax error at or near "RAISE"
LINE 4:   RAISE NOTICE 'Beginning average calculation';

我应该如何正确格式化这个查询?

【问题讨论】:

    标签: python sql postgresql plpgsql psycopg2


    【解决方案1】:

    带循环的程序解决方案

    您正在使用 PL/pgSQL 代码,但正试图将其表述为 SQL 查询。这是不可能的。

    使用DO 语句或(因为您想使用参数)创建plpgsql function

    CREATE OR REPLACE FUNCTION foo(_cbsafp10     ?type?     -- replace with ...
                                  ,_deptime_from timestamp? -- ... actual data types
                                  ,_deptime_to   timestamp?
                                  ,_threshold    ?type?)
      RETURNS void AS
    $func$
    DECLARE
       rec RECORD;
    BEGIN
    
    FOR rec IN
       SELECT b.*
       FROM   census_blocks b
       JOIN   census_cbsa   c ON ST_contains(c.geom, b.geom)
       WHERE  c.cbsafp10 = _cbsafp10
    LOOP
       INSERT INTO temp_avgs    -- you might add a target list for safety. depends ..
       SELECT rec.*, avg(alljobs)
       FROM   accessibility_results
       WHERE  geoid = rec.geoid -- assuming you join on column "geoid"?
       AND    deptime BETWEEN _deptime_from AND _deptime_to
       AND    threshold = _threshold;
    END LOOP;
    
    END
    $func$ LANGUAGE plpgsql;
    

    确保在客户端中正确转义引号!

    • 选择值上的 LOOP 变量不会自动声明为任何值。
    • 替换不必要的子查询。
    • 第二个错误消息的直接原因:RAISE 是一个 plpgsql 命令,不是一个 SQL 命令。

    卓越的基于集合的解决方案

    这将演示 plpgsql 函数的基础知识。但是,正如@Gordon 已经提供的那样,只需使用单个INSERT 语句做同样的事情。进一步解开:

    INSERT INTO temp_avgs   -- you might add a target list for safety. depends ..
    SELECT b.*, avg(alljobs)
    FROM   census_cbsa           c
    JOIN   census_blocks         b ON ST_contains(c.geom, b.geom)
    JOIN   accessibility_results a ON a.geoid = b.geoid     
    WHERE  c.cbsafp10 = %s
    AND    a.deptime BETWEEN %s AND %s
    AND    a.threshold = %s
    GROUP  BY b.geoid;    -- assuming b.geoid is the primary key
    

    【讨论】:

      【解决方案2】:

      如何将其表述为单选。像这样的:

      INSERT INTO temp_avgs(geoid, average)
         select cb.geoid, avg(aj.alljobs)
         from census_blocks cb join
              accessibility_results ar
              on cb.geoid = ar.geoid
         WHERE ST_contains((select geom from census_cbsa WHERE cbsafp10='%s'), cb.geom) and
               ar.deptime BETWEEN '%s' and '%s' AND ar.threshold='%s'
         group by cb.geoid;
      

      我不确定所有列的来源,所以表别名有点猜测。

      【讨论】:

      • 我考虑过将措辞作为单个选择的可能性,但我认为这行不通 - 数据表结构对于每个地理位置都有许多值(行)。计算整个数据列的平均值将产生 1 个数字 - 我需要每个唯一地理位置的平均值,我认为没有循环就无法完成。
      • @user3757974 。 . .糟糕,我忽略了所有重要的 group by 子句。那应该做你想做的。
      猜你喜欢
      • 1970-01-01
      • 2013-10-28
      • 2015-08-04
      • 1970-01-01
      • 2015-04-17
      • 1970-01-01
      • 1970-01-01
      • 2021-11-14
      • 1970-01-01
      相关资源
      最近更新 更多