【问题标题】:plpgsql syntax error at or near ";"";" 处或附近的 plpgsql 语法错误
【发布时间】:2014-07-07 21:08:23
【问题描述】:

我查看了this 类似的问题,但我认为我的问题可能有所不同。如果我正确理解他们的问题,则问题是由上游语法错误引起的。
在我的例子中,语法错误非常接近程序的开头,它给了我一个可能出错的狭窄窗口,但在我看来一切正常。

代码:

DO $$
DECLARE topic_cursor CURSOR FOR SELECT * FROM "socialMediaModel_topic" WHERE "active_search"=True;

BEGIN
OPEN topic_cursor;
FETCH FIRST FROM topic_cursor;
LOOP
    SELECT "topic" FROM topic_cursor AS "c_topic";
    SELECT "topic_id" FROM topic_cursor AS "c_id";
    SELECT "active_search" FROM topic_cursor AS "c_active";

    INSERT INTO "socialMediaModel_datacollection" ("name", "active")
        VALUES (c_topic, c_active);
    INSERT INTO "socialMediaModel_datacollectiontopic" ("data_collection_id_id", "topic_id_id")
        VALUES ((SELECT "data_collection_id" FROM "DataCollection" where name=c_topic), c_id);

    FETCH NEXT FROM topic_cursor;

END LOOP;
CLOSE topic_cursor;

UPDATE "socialMediaModel_topic" SET "active_search" = False WHERE "active_search"=True;
COMMIT;
END$$;

错误:

ERROR:  syntax error at or near ";"
LINE 9:  FETCH FIRST FROM topic_cursor;
                                  ^
********** Error **********

ERROR: syntax error at or near ";"
SQL state: 42601
Character: 247

在编写此脚本时,我几乎完全遵循了这些资源:

数据库:PostgreSQL 9.1
编辑:pgAdmin III 查询工具

如果我遗漏了一些非常明显的东西,我会提前道歉。我整天盯着这个剧本,所以我的大脑可能有点乱。

【问题讨论】:

  • 也许“从 yyy INTO 变量名中获取 xxx”?顺便说一句:我认为您需要光标来执行此任务。
  • 您的前三个select 语句应为fetch,如您添加的第一个链接的示例所示。您不能在光标上使用 select。并且使用for some_rec in select ... loop 编写的代码要少得多(请参阅页面末尾第一个链接中的示例)
  • data_collection_id_id, topic_id_id?错别字?

标签: sql postgresql cursor plpgsql window-functions


【解决方案1】:

程序解决方案

您的代码存在许多问题。
这应该可以工作,而且速度更快:

DO
$do$
DECLARE
   rec record;
BEGIN
   FOR rec IN
      SELECT s.*, d.data_collection_id
      FROM   "socialMediaModel_topic" s
      LEFT   JOIN "DataCollection"    d ON d.name = s.topic
      WHERE  active_search
   LOOP
      INSERT INTO "socialMediaModel_datacollection" (name, active)
      VALUES (rec.topic, rec.active_search);
      INSERT INTO "socialMediaModel_datacollectiontopic"
                              (data_collection_id_id, topic_id_id)
      VALUES (rec.data_collection_id, rec.topic_id);
   END LOOP;

   UPDATE "socialMediaModel_topic"
   SET    active_search = FALSE
   WHERE  active_search;
END
$do$;

要点

  • FETCH syntax 不正确。

  • DO 语句中没有 COMMIT。整个事情在单个事务中自动运行,就像一个函数一样。

  • 您没有条件终止循环。

  • 几乎不需要显式游标。使用 FOR loop 的更方便(通常更快)的隐式光标。

  • 我建议不要在 Postgres 中使用 CaMeL 案例标识符。仅使用合法的小写标识符。

基于集合的解决方案

整个过程方法不如使用data-modifying CTEs 的基于集合的方法:

WITH ins1 AS (
   INSERT INTO "socialMediaModel_datacollection" (name, active)
   SELECT topic, active_search
   FROM   "socialMediaModel_topic"
   WHERE  active_search
   )
, ins2 AS (
   INSERT INTO "socialMediaModel_datacollectiontopic"
                           (data_collection_id_id, topic_id_id)
   SELECT d.data_collection_id, s.topic_id
   FROM   "socialMediaModel_topic" s
   LEFT   JOIN "DataCollection"    d ON d.name = s.topic
   WHERE  s.active_search
   )
UPDATE "socialMediaModel_topic"
SET    active_search = FALSE
WHERE  active_search;

或者,如果您有并发写入负载,请使用FOR UPDATE 来避免竞争条件:

WITH sel AS (
   SELECT s.topic_id, s.topic, s.active_search, d.data_collection_id
   FROM   "socialMediaModel_topic" s
   LEFT   JOIN "DataCollection"    d ON d.name = s.topic
   WHERE  s.active_search
   FOR    UPDATE
   )
, ins1 AS (
   INSERT INTO "socialMediaModel_datacollection" (name, active)
   SELECT topic, active_search FROM sel
   )
, ins2 AS (
   INSERT INTO "socialMediaModel_datacollectiontopic"
                           (data_collection_id_id, topic_id_id)
   SELECT d.data_collection_id, s.topic_id FROM sel
   )
UPDATE "socialMediaModel_topic"
SET    active_search = FALSE
WHERE  active_search;

更多关于 CTE 中 SELECT ... FOR UPDATE 的信息:
Should I include SELECTs in a transaction?

类似问题/答案:
How to improve performance of a function with cursors in PostgreSQL?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-26
    • 2014-04-26
    • 1970-01-01
    • 2017-02-20
    • 2013-09-10
    • 2017-06-10
    • 2015-02-23
    • 1970-01-01
    相关资源
    最近更新 更多