Неожиданные результаты SQL: строка против прямого SQL - PullRequest
2 голосов
/ 29 мая 2011

Рабочий SQL

Следующий код работает должным образом, возвращая два столбца данных (номер строки и допустимое значение):

sql_amounts := '
  SELECT
    row_number() OVER (ORDER BY taken)::integer,
    avg( amount )::double precision
  FROM
    x_function( '|| id || ', 25 ) ca,
    x_table m
  WHERE
    m.category_id = 1 AND
    m.location_id = ca.id AND
    extract( month from m.taken ) = 1 AND
    extract( day from m.taken ) = 1
  GROUP BY
    m.taken
  ORDER BY
    m.taken';

FOR r, amount IN EXECUTE sql_amounts LOOP
  SELECT array_append( v_row, r::integer ) INTO v_row;
  SELECT array_append( v_amount, amount::double precision ) INTO v_amount;
END LOOP;

Неработающий SQL

Следующий код не работает должным образом;первый столбец - номер строки, второй столбец - NULL.

FOR r, amount IN
  SELECT
    row_number() OVER (ORDER BY taken)::integer,
    avg( amount )::double precision
  FROM
    x_function( id, 25 ) ca,
    x_table m
  WHERE
    m.category_id = 1 AND
    m.location_id = ca.id AND
    extract( month from m.taken ) = 1 AND
    extract( day from m.taken ) = 1
  GROUP BY
    m.taken
  ORDER BY
    m.taken
LOOP
  SELECT array_append( v_row, r::integer ) INTO v_row;
  SELECT array_append( v_amount, amount::double precision ) INTO v_amount;
END LOOP;

Вопрос

Почему нерабочий код возвращает значение NULL для второго столбца, еслисам запрос возвращает два допустимых столбца?(Этот вопрос носит в основном академический характер; если есть способ выразить запрос, не прибегая к переносу его в текстовую строку, это было бы полезно знать.)

Полный код

http://pastebin.com/hgV8f8gL

Программное обеспечение

PostgreSQL 8,4

Спасибо.

Ответы [ 2 ]

1 голос
/ 31 мая 2011

От Тома Лейна:

Я думаю, проблема в том, что вы предполагаете, что «количество» будет ссылаться на столбец таблицы запроса, когда на самом деле это локальная переменная функции plpgsql.Вторая интерпретация будет иметь приоритет, если вы не квалифицируете ссылку на столбец с именем / псевдонимом таблицы.

Примечание: PG 9.0 выдаст ошибку по умолчанию, если есть неопределенность этого типа.

1 голос
/ 29 мая 2011

Два утверждения не являются строго эквивалентными.

Если предположить, что id = 4, первый из них планируется / готовится на каждом проходе и ведет себя так:

prepare dyn_stmt as '... x_function( 4, 25 ) ...'; execute dyn_stmt;

Другой получаетзапланировано / подготовлено только на первом проходе и ведет себя примерно так:

prepare stc_stmt as '... x_function( $1, 25 ) ...'; execute stc_stmt(4);

(Цикл фактически заставит его подготовить курсор к вышеперечисленному, но это не только вопрос ради нас.)

Ряд факторов может привести к тому, что оба результата дадут разные результаты.

  • Изменения пути поиска до вызова процедуры будут игнорироваться при втором вызове.В частности, если это заставляет x_table указывать на что-то другое.
  • Постоянные всех видов и вызовы неизменяемых функций являются «жестко привязанными» в плане второго вызова.

Рассмотримэто как иллюстрация этих побочных эффектов:

deallocate all;
begin;
prepare good as select now();
prepare bad as select current_timestamp;
execute good; -- yields the current timestamp
execute bad;  -- yields the current timestamp
commit;
execute good; -- yields the current timestamp
execute bad;  -- yields the timestamp at which it was prepared

Почему эти два не возвращают одинаковые результаты в вашем случае, будет зависеть от контекста (вы разместили только часть своей функции pl / pgsql, поэтомуТрудно сказать), но я думаю, что вы столкнулись с вариантом вышеупомянутой проблемы.

...