0

以下のエラーが発生しました。このエラーは 2 回目の実行でのみ発生し、プランニング キャッシュが発生します。

ERROR: el tipo del parámetro 11 (character varying) no coincide aquel con que fue preparado el plan (text)
Where: PL/pgSQL function graficar(character varying,character varying,character varying) line 22 at asignación

これは、以下の関数の変数または値が一致しないことを意味しますcharacter varying/text行 22. 定義された明示的なテキスト値はありません。クエリ文字列の連結で発生すると思います。

CREATE OR REPLACE FUNCTION graficar(tabla character varying, columna character varying, valor character varying) RETURNS SETOF resultado As $$
    DECLARE
        r resultado;
        i record;
        query character varying = '';
        limite character varying = '';
    BEGIN
    IF columna <> '' THEN
       limite = ' where ' || columna || ' =  $1';
    END IF;

        IF tabla = 'edad' THEN
            query =   'WITH ranges AS ( SELECT (ten*10)::text ||''-''||(ten*10+9)::text AS range, ten*10 AS r_min, ten*10+9 AS r_max FROM generate_series(0,9) AS t(ten)) SELECT r.range as nombre, count(s.*) as cuenta FROM ranges r  LEFT JOIN ( select * from persona '|| limite ||' ) as s ON  s.edad BETWEEN r.r_min AND r.r_max GROUP BY r.range HAVING range NOT IN (''0-9'') ORDER BY r.range;';
        ELSEIF tabla = 'ingreso' THEN
           query =   'WITH ranges AS ( SELECT (ten*10)::text|| ''-''||((ten*10+199))::text || '' mil'' AS range, ten*10/1000 AS r_min, (ten*10+199999)/1000 AS r_max FROM generate_series(0,(SELECT max(ingreso)/10000 FROM persona), 20) AS t(ten)) SELECT r.range as nombre, count(s.*) as cuenta FROM ranges r  LEFT JOIN ( select * from persona '|| limite ||' ) as s ON s.ingreso BETWEEN r.r_min AND r.r_max GROUP BY r.range  ORDER BY r.range;';
        ELSE
            query = 'select p.nombre, count( e.* ) as cuenta from ' || tabla::regclass ||' p left join ( select * from persona ' || limite ||' ) as e on p.nombre = e.'|| tabla::regclass ||' group by p.nombre ';
    END IF;

    FOR i IN EXECUTE query USING valor LOOP  -- I DONT KNOW EXACTLY WHERE LINE 22 I SUPOSSE IS THIS
        r = (i.nombre, i.cuenta);
        RETURN NEXT r;
    END LOOP;
    END
$$ LANGUAGE plpgsql;
4

2 に答える 2

1

EXECUTE実行するたびに再計画することになっているため、エラーは計画キャッシュの問題から発生するべきではありません。

それとは別に、この関数には 2 つの潜在的なエラーがあるようです。

1)columna<>''が false の場合、動的クエリにパラメータはありませんが、EXECUTEは で値を送信しようとしますUSING valor

2)columna<>''が true のcharacter varying場合、列がこの値と暗黙的に比較できない型であっても、この列を型の値と比較しています。おそらくこれには、テキストへの明示的なキャストが必要です。

limite = ' where ' || columna || '::text =  $1';

valorパラメーターを型にする(textまたは保持するが、構文character varyingを使用する)。CAST

于 2013-09-23T18:41:18.870 に答える
1
CREATE OR REPLACE FUNCTION graficar(tabla text, columna text, valor text)
  RETURNS SETOF resultado As
$func$
DECLARE
   _query text;
   limite text := '';
BEGIN
IF columna <> '' THEN
   limite := format(' AND %I = %L', columna, valor); -- properly escaped
END IF;

_query :=
   CASE tabla
   WHEN 'edad' THEN
   $q$WITH ranges AS (
         SELECT concat(ten, '0-', ten, '9') AS range
               ,ten*10 AS r_min, ten*10+9 AS r_max
         FROM   generate_series(1,9) ten)
      SELECT r.range AS nombre, count(p.*)::int AS cuenta
      FROM   ranges       r
      LEFT   JOIN persona p ON p.edad BETWEEN r.r_min AND r.r_max$q$
      || limite || '
      GROUP  BY r.range
      ORDER  BY r.range'

   WHEN 'ingreso' THEN
   $q$WITH ranges AS (
         SELECT concat(ten, '0-', ten*10 + 199, ' mil') AS range
               ,ten*10/1000 AS r_min, (ten*10+199999)/1000 AS r_max
         FROM   generate_series(0,(SELECT max(ingreso)/10000 FROM persona)
                                                             , 20) AS ten)
      SELECT r.range AS nombre, count(p.*)::int AS cuenta
      FROM   ranges r
      LEFT   JOIN persona p ON p.ingreso BETWEEN r.r_min AND r.r_max$q$
      || limite || '
      GROUP  BY r.range
      ORDER  BY r.range'

   ELSE
      format(
   $q$SELECT t.nombre, count(p.*)::int AS cuenta
      FROM   %1$I t
      LEFT   JOIN persona p on p.%1$I = t.nombre$q$ || limite || '
      GROUP  BY t.nombre'
      , tabla)
   END;

RETURN QUERY EXECUTE _query;

END
$func$ LANGUAGE plpgsql;

主なポイント:

  • 簡単にするためtextに の代わりに使用します。character varying

  • concat()フォーマットを簡単にするために使用します。Postgres 9.1 以降が必要です。

  • 人間が読める形式を使用してください! あなたが投稿した文字列はほとんど維持できません。

  • とにかく最後1にケースを除外するため、最初のケースから始まる番号を生成します。0したがって、冗長なHAVING句を削除します。

  • plpgsql:=の代入演算子はありません=- これは通常は機能しますが、文書化されていない機能であり、将来のバージョンでなくなる可能性があります。

  • ドル相場を適切に使用してください。

  • RETURN QUERY最後に構造全体の代わりに単純なインを使用しLOOPます。

  • 変数名として使用しないでくださいquery。plpgsql の予約語です。代わりに代用_query

  • @Danielで説明されている潜在的な型の不一致を回避するには、クエリで文字列リテラルとして指定します。これはルールのまれな例外です。通常、優れたアプローチは、あなたが持っていたように句で値を渡すことです。ただし、潜在的にさまざまな型に対応するには、任意の型に自動的に強制変換できる型指定されていない文字列リテラルを指定するのが最善の選択です。このようにして、式は引き続き検索可能であり、列に潜在的に存在するインデックスを使用できます。valorUSING

  • すべての識別子と文字列を適切にエスケープすることで、SQL インジェクションを回避します。主に使用してformat()います。Postgres 9.1 以降が必要です。詳細については、dba.SE に関するこの関連する回答を参照してください。

于 2013-09-23T21:31:30.137 に答える