복붙노트

[SQL] 다양한 SELECT 쿼리의 결과를 반환하는 PL / pgSQL의 함수 리팩토링

SQL

다양한 SELECT 쿼리의 결과를 반환하는 PL / pgSQL의 함수 리팩토링

나는 잘 텍스트 형태로 형성되는 PostgreSQL의 SELECT 쿼리를 출력하는 기능을 썼다. 지금은 텍스트 더 이상 출력하지 않지만, 실제로 데이터베이스에 대해 생성 된 SELECT 문을 실행하고 결과를 반환 - 단지 자체는 것 쿼리처럼.

CREATE OR REPLACE FUNCTION data_of(integer)
  RETURNS text AS
$BODY$
DECLARE
   sensors varchar(100);   -- holds list of column names
   type    varchar(100);   -- holds name of table
   result  text;           -- holds SQL query
       -- declare more variables

BEGIN
      -- do some crazy stuff

      result := 'SELECT\r\nDatahora,' || sensors ||
      '\r\n\r\nFROM\r\n' || type ||
      '\r\n\r\nWHERE\r\id=' || $1 ||'\r\n\r\nORDER BY Datahora;';

      RETURN result;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
ALTER FUNCTION data_of(integer) OWNER TO postgres;

센서는 테이블 타입 열 이름의리스트를 보유하고있다. 이러한 선언과 함수의 과정에서 채워진다. 결국, 그들은 값을 같이 개최 :

기본 테이블의 정의.

변수 센서 유형에 해당 테이블에 대해 여기에 표시된 모든 열을 개최한다. 예를 들어 : 유형 인 경우 pcdmet 다음 센서가 될 것 'datahora, dirvento, precipitacao, pressaoatm, radsolacum, tempar, umidrel, velvento'

변수는 결과에 저장되어있는 SELECT 문을 작성하는 데 사용됩니다. 처럼:

SELECT Datahora, column1, column2, column3
FROM   myTable
WHERE  id=20
ORDER  BY Datahora;

지금, 내 기능은 텍스트로이 문을 반환합니다. 나는 붙여 복사 및 pgAdmin 또는 psql를 통해 그것을 실행합니다. 나는이 자동화 자동으로 쿼리를 실행하고 결과를 반환합니다. 내가 어떻게 할 수 있습니까?

해결법

  1. ==============================

    1.(나는 마지막 읽기 계속에 가장 적합한 저장!) 당신은 동적 SQL을 실행합니다. 교장에, EXECUTE의 도움으로 plpgsql에서 그의 간단한. 당신은 커서를 필요가 없습니다 - 사실, 대부분의 시간은 당신이 더 떨어져 명시 적 커서 않고 있습니다. 검색과 SO에 대한 예제를 찾을 수 있습니다.

    (나는 마지막 읽기 계속에 가장 적합한 저장!) 당신은 동적 SQL을 실행합니다. 교장에, EXECUTE의 도움으로 plpgsql에서 그의 간단한. 당신은 커서를 필요가 없습니다 - 사실, 대부분의 시간은 당신이 더 떨어져 명시 적 커서 않고 있습니다. 검색과 SO에 대한 예제를 찾을 수 있습니다.

    아직 정의되지 않은 유형의 레코드를 반환 할 : 당신으로 실행 문제. 함수는 (OUT 또는 INOUT 파라미터 또는) 반품 절 리턴 타입을 선언해야한다. 귀하의 경우에 당신은 번호, 이름과 반환 열 유형이 다양하기 때문에, 익명의 기록으로 후퇴 할 것입니다. 처럼:

    CREATE FUNCTION data_of(integer)
      RETURNS SETOF record AS ...
    

    그러나이 특히 유용하지 않습니다. 이 방법은 함수의 모든 호출에 열 정의 목록을 제공해야 할 것이다. 처럼:

    SELECT * FROM data_of(17)
    AS foo (colum_name1 integer
          , colum_name2 text
          , colum_name3 real);
    

    사전에 열을 모를 때 그러나 당신은 어떻게도, 이런 짓을 했을까? 당신은 JSON, jsonb, hstore 또는 XML과 같은 덜 구조화 된 문서 데이터의 유형에 의존 수 :

    그러나이 질문의 목적을 위해의 당신은 가능한 한 개인, 올바르게 입력 및 명명 된 열을 반환한다고 가정하자.

    열 datahora 내가 데이터 유형 타임 스탬프를 가정하고 다양한 이름과 데이터 유형이 더 많은 열이 항상 있다는 것을거야, A가 주어진 것 같다.

    이름은 우리가 반환 형식의 일반적인 이름에 찬성 포기 것이다. 유형 우리도 포기하고, 모든 데이터 유형이 텍스트에 캐스팅 될 수 있기 때문에 텍스트에 대한 모든 캐스팅 것입니다.

    CREATE OR REPLACE FUNCTION data_of(_id integer)
      RETURNS TABLE (datahora timestamp, col2 text, col3 text) AS
    $func$
    DECLARE
       _sensors text := 'col1::text, col2::text';  -- cast each col to text
       _type    text := 'foo';
    BEGIN
       RETURN QUERY EXECUTE '
          SELECT datahora, ' || _sensors || '
          FROM   ' || quote_ident(_type) || '
          WHERE  id = $1
          ORDER  BY datahora'
       USING  _id;
    
    END
    $func$ LANGUAGE plpgsql;
    

    버전 9.1 이상 당신은 더 단순화하기 위해 형식을 ()를 사용할 수 있습니다 :

    RETURN QUERY EXECUTE format('
       SELECT datahora, %s  -- identifier passed as unescaped string
       FROM   %I            -- assuming the name is provided by user
       WHERE  id = $1
       ORDER  BY datahora'
      ,_sensors, _type)
    USING  _id;
    

    또, 각각의 열 이름이 제대로 탈출 할 수 있고 깨끗한 방법이 될 것입니다.

    당신의 반환 형식은이 같은 질문 업데이트 후에 본다

    우리는 값의 변수 번호를 저장할 수있는이 경우 배열 유형에 함수 I 리조트의 반환 유형을 정의해야한다. 당신이 너무 결과에서 이름을 구문 분석 할 수 있도록 또한, 나는 열 이름을 가진 배열을 반환 :

    CREATE OR REPLACE FUNCTION data_of(_id integer)
      RETURNS TABLE (datahora timestamp, names text[], values float8[] ) AS
    $func$
    DECLARE
       _sensors text := 'col1, col2, col3';  -- plain list of column names
       _type    text := 'foo';
    BEGIN
       RETURN QUERY EXECUTE format('
          SELECT datahora
               , string_to_array($1)  -- AS names
               , ARRAY[%s]            -- AS values
          FROM   %s
          WHERE  id = $2
          ORDER  BY datahora'
        , _sensors, _type)
       USING  _sensors, _id;
    END
    $func$  LANGUAGE plpgsql;
    

    실제로 링크 된 페이지에있는 테이블의 인스턴스 하나 (테이블의 모든 열을 반환하려는 경우,이 간단한, 다형성 유형의 매우 강력한 솔루션을 사용 :

    CREATE OR REPLACE FUNCTION data_of(_tbl_type anyelement, _id int)
      RETURNS SETOF anyelement AS
    $func$
    BEGIN
       RETURN QUERY EXECUTE format('
          SELECT *
          FROM   %s  -- pg_typeof returns regtype, quoted automatically
          WHERE  id = $1
          ORDER  BY datahora'
        , pg_typeof(_tbl_type))
       USING  _id;
    END
    $func$ LANGUAGE plpgsql;
    

    요구:

    SELECT * FROM data_of(NULL::pcdmet, 17);
    

    다른 테이블 이름으로 호출 pcdmet 교체합니다.

  2. ==============================

    2.당신은 아마 커서를 반환 할 수 있습니다. 이 같은 것을 (나는 그것을 시도하지 않은) 시도 :

    당신은 아마 커서를 반환 할 수 있습니다. 이 같은 것을 (나는 그것을 시도하지 않은) 시도 :

    CREATE OR REPLACE FUNCTION data_of(integer)
      RETURNS refcursor AS
    $BODY$
    DECLARE
          --Declaring variables
          ref refcursor;
    BEGIN
          -- make sure `sensors`, `type`, $1 variable has valid value
          OPEN ref FOR 'SELECT Datahora,' || sensors ||
          ' FROM ' || type ||
          ' WHERE nomepcd=' || $1 ||' ORDER BY Datahora;';
          RETURN ref;
    END;
    $BODY$
    LANGUAGE 'plpgsql' VOLATILE;
    ALTER FUNCTION data_of(integer) OWNER TO postgres;
    
  3. ==============================

    3.내가 말할 죄송하지만 질문은 매우 명확하지 않다. 그러나 당신은 함수가 반환하는 커서 변수를 생성하고 사용하는 방법 자체 포함 된 예를 찾을 수 아래. 희망이 도움이!

    내가 말할 죄송하지만 질문은 매우 명확하지 않다. 그러나 당신은 함수가 반환하는 커서 변수를 생성하고 사용하는 방법 자체 포함 된 예를 찾을 수 아래. 희망이 도움이!

    begin;
    
    create table test (id serial, data1 text, data2 text);
    
    insert into test(data1, data2) values('one', 'un');
    insert into test(data1, data2) values('two', 'deux');
    insert into test(data1, data2) values('three', 'trois');
    
    create function generate_query(query_name refcursor, columns text[])
    returns refcursor 
    as $$
    begin
      open query_name for execute 
        'select id, ' || array_to_string(columns, ',') || ' from test order by id';
      return query_name;
    end;
    $$ language plpgsql;
    
    select generate_query('english', array['data1']);
    fetch all in english;
    
    select generate_query('french', array['data2']);
    fetch all in french;
    move absolute 0 from french; -- do it again !
    fetch all in french;
    
    select generate_query('all_langs', array['data1','data2']);
    fetch all in all_langs;
    
    -- this will raise in runtime as there is no data3 column in the test table
    select generate_query('broken', array['data3']);
    
    rollback;
    
  4. ==============================

    4.

    # copy paste me into bash shell directly
    clear; IFS='' read -r -d '' sql_code << 'EOF_SQL_CODE'
    CREATE OR REPLACE FUNCTION func_get_all_users_roles()
      -- define the return type of the result set as table
      -- those datatypes must match the ones in the src
      RETURNS TABLE (
                     id           bigint
                   , email        varchar(200)
                   , password     varchar(200)
                   , roles        varchar(100)) AS
    $func$
    BEGIN
       RETURN QUERY 
       -- start the select clause
       SELECT users.id, users.email, users.password, roles.name as roles
       FROM user_roles
       LEFT JOIN roles ON (roles.guid = user_roles.roles_guid)
       LEFT JOIN users ON (users.guid = user_roles.users_guid)
       -- stop the select clause
    ;
    END
    $func$  LANGUAGE plpgsql;
    EOF_SQL_CODE
    # create the function
    psql -d db_name -c "$sql_code"; 
    
    # call the function 
    psql -d db_name -c "select * from func_get_all_users_roles() "
    
  5. from https://stackoverflow.com/questions/11740256/refactor-a-pl-pgsql-function-to-return-the-output-of-various-select-queries by cc-by-sa and MIT license