FOR循环声明部分中的语法错误

时间:2014-08-10 19:43:07

标签: python sql postgresql plpgsql psycopg2

我试图在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';

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

2 个答案:

答案 0 :(得分: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变量 自动声明为任何内容。
  • 替换不必要的子查询。
  • 第二个错误的直接原因msg: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

答案 1 :(得分:0)

如何将它作为单个选择进行短语。像这样:

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;

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