web-dev-qa-db-ja.com

エラー:「sql」は既知の変数ではありません

これは、私がこれまでに聞いた中で最も馬鹿げた質問の1つであるに違いありませんが、SQLスクリプトの中に、実行をブロックしている何か本当にひどい問題があるはずです。

次のサンプルCLI構文を使用してcobertura.sqlファイルを呼び出しています。

psql -h localhost -U myUser -f cobertura.sql myDB

ただし、次のエラーが表示されます。

psql:cobertura.sql:29: ERROR:  "sql " is not a known variable
LINE 14: sql := format('insert into cobertura_tmp select count(*) as ... cobertura.sql file:
DO language plpgsql $$
declare 
    eq  record;
    sql varchar;
BEGIN

create table if not exists cobertura_tmp  (num integer, realtime char(1), lat numeric, lng numeric);

truncate table cobertura_tmp;
for eq in select imei_equipo as imei  from cliente_avl_equipo where id_cliente in (select id from cliente where nombre ilike '%enangab%') limit 3

loop

sql := format('insert into cobertura_tmp select count(*) as num, tipo as realtime, round(CAST(latitud as numeric), 4) as lat ,round(CAST(longitud as numeric), 4) as lng   from reports.avl_historico_%s where latitud between -38.67405472 and -36.75131149 and longitud between  -73.08429161 and -69.65333954 group by tipo, round(CAST(latitud as numeric), 4),round(CAST(longitud as numeric), 4)', eq.imei);

execute sql;

end loop;

update cobertura_tmp set num= -1* num where realtime='S';

create table if not exists cobertura_tmp_resumen  (num integer, lat numeric, lng numeric);
truncate cobertura_tmp_resumen;
--    select sum(num) as num , lat, lng into cobertura_tmp_resumen from cobertura_tmp group by lat,lng;

--    drop table if exists cobertura_tmp;

END;
$$;

同じスクリプトは、Posticoバージョン1.3.2(2318)を使用してMac OSXからリモートでスムーズに実行されます。

質問:sqlセクションにあるdeclare変数を見つけないのはなぜですか?

2
Gonzalo Vasquez

あなたのエラーメッセージは言う:

_psql:cobertura.sql:29: ERROR:  "sql " is not a known variable
LINE 14: sql := format('insert into cobertura_tmp select count(*) as ...
_

よく見て:_"sql "_、_"sql"_ではない

つまり、無害なスペース文字の代わりに、 "sql"の直後に卑劣で目に見えない文字があるということです。それはあなたの質問にはありません、あなたがここであなたの質問にそれをコピーして貼り付けたときにおそらく翻訳で失われました。元のコードには次のようなものが含まれています。

_sql := format ...
_

あなた見えますか?番号? seeできないからです。 (この特定のケースでは、スペースが少し広いことに気付くかもしれません-お使いのブラウザー、フォント、文字セットが私のものと同じ結果を生成する場合。)"ideographic space"、Unicode _U+3000_、HTML _&#12288_。単なるランダムな例です。 Unicodeにはそのようなさまざまな文字があります。単純なスペースに置き換えて、問題を解決してください。

dbfiddle ここ

nicodeについてこれは嫌いです。文字が多すぎて、ほとんどの場合人々を混乱させるだけです...

簡単なテスト:

_SELECT ascii(' ');   -- 32    -- plain space
SELECT ascii(' ');  -- 12288 -- not a plain space
SELECT ' ' = ' ';   -- f     -- not the same!
_

文字列にany非ASCII文字があるかどうかの簡単なテスト:

_SELECT octet_length(string) = char_length(string)  -- f
FROM  (SELECT text 'sql := format' AS string) t;
_

エンコードUTF8を想定すると、非ASCII文字は2〜4バイトを占有しますが、単純なASCII文字は1バイトを占有します。 octet_length()

文字列のバイト数

一方、char_length()length()と同じ)は文字の数を返します。すべてASCIIの場合、どちらも同じ結果を返します。 octet_length()がより大きな数値を返す場合は、不審な文字があります。何も意味する必要はありません。文字列内のアクセント記号付きの文字は、その文字に適しています。

または、容疑者を強調表示できるエディターを使用します。


、それをしている間、あなたのplpgsqlコードは次のようにかなり効率的になります:

_DO
$$
DECLARE
   _imei text;
   _sql  text;
BEGIN

IF to_regclass('pg_temp.cobertura_tmp') IS NULL  -- if tmp table does not exist
THEN
   CREATE TABLE cobertura_tmp (
      num      integer
    , realtime char(1)
    , lat      numeric
    , lng      numeric
    );
ELSE
   TRUNCATE TABLE cobertura_tmp;
END IF;

FOR _imei IN
   SELECT e.imei_equipo::text  -- imei is NOT NULL & *safe* against SQL injection
   FROM   cliente c
   JOIN   cliente_avl_equipo e ON e.id_cliente = c.id  -- assuming c.id is UNIQUE
   WHERE  c.nombre ILIKE '%enangab%'
   LIMIT  3
LOOP
   _sql := format(
   $s$
   INSERT INTO cobertura_tmp (num, realtime, lat, lng)
   SELECT count(*)::int * (CASE WHEN a.tipo = 'S' THEN -1 ELSE 1 END) -- AS num
        , a.tipo                         -- AS realtime
        , round(a.latitud::numeric, 4)   -- AS lat
        , round(a.longitud::numeric, 4)  -- AS lon
   FROM   reports.avl_historico_%s a
   WHERE  a.latitud  BETWEEN -38.67405472 AND -36.75131149
   AND    a.longitud BETWEEN -73.08429161 AND -69.65333954
   GROUP  BY 2,3,4
   $s$
 , _imei);

   EXECUTE _sql;
END LOOP;

/* integrated above to avoid costly extra update:
UPDATE cobertura_tmp
SET    num = -1 * num
WHERE  realtime = 'S';
*/

-- ... etc

END
$$;
_

詳細はたくさんありますが、それは質問のトピックではありませんです。私はあなたが一時テーブルをまったく必要としないと思います...

コードを改善したい場合は、試してください。関連:

7