Postgresql 함수(Stored Procedure)에서 JSON을 사용한 TVP(with C#) 0 2 4,890

by 지현명 [PostgreSQL 노하우/팁/자료] postgresql tvp [2017.01.20 02:06:21]


TVP For JSON.7z (3,369,310Bytes)

기간계 시스템(ERP/SCM/WMS...) 개발자로서 화면 GRID 데이터를 어떻게 하면 DB에 효율적으로 입력/조회 할 수 있을까 늘 고민거리입니다.

1차로 Postgresql의 Composite Type을 사용하여 TVP를 구현했으나 해당 Composite Type을 C#의 Class로 구현하여 type을 맵핑을 해야만 합니다. MS-SQL과 비슷하게 구현 할 수 있어서 상당히 나름 쓸만합니다.

이 방법의 단점 (불편한점)

1. type을 맵핑해야 한다.

2. 화면 GRID 데이터를 해당 type의 Class로 List를 만들어 줘야 한다.(이거 귀찮음.)

 

pg의 create function 메뉴얼을 보던 중 함수의 파라미터로 JSON type을 사용 할 수 있는것을 보고

c#의 Newtonsoft.Json을 사용하면 조회 하여 결과를 grid에 출력 할때나 저장 할때 유용하게 사용 할 수 있을거라 생각이 되서 메뉴얼보고 해보니 잘 작동해서 관련 내용 정리해서 올립니다.

> 조회 함수(프로시저)

멀티row의 데이터를 한줄의 json형식으로 만들어 준다. 일전에 올렸던 함수표(?)에서 보면 여러줄일 경우 RETURNS SETOF json를 하면 되는데 json 한줄로 리턴하기 때문에 json만 사용하면 됩니다.

CREATE OR REPLACE FUNCTION public.usp_get_emp (
  p_emp_nm varchar
)
RETURNS json AS
$body$
/*
 ROW TO JSON
*/
select
 array_to_json
    (
      ARRAY
      (
              select row_to_json(tmp)
              from
              (
               SELECT a.emp_id, a.emp_nm FROM public.employee a
               WHERE a.emp_nm = CASE WHEN p_emp_nm  = '' THEN a.emp_nm ELSE p_emp_nm END
              ) tmp
      )
 );
$body$
LANGUAGE 'sql';

 

>C#의 조회

private void btnSearch_Click(object sender, EventArgs e)
{
    var _param = new[] 
    {
        new NpgsqlParameter
        {
            ParameterName = "p_emp_nm",
            NpgsqlDbType = NpgsqlDbType.Varchar,
            NpgsqlValue = this.txtEmpNm.Text.Trim()
        }
    };
 
    string jsonString = SqlHelper.ExecuteReader(this.connstring, CommandType.StoredProcedure, "usp_get_emp", _param);
 
    DataTable dt = JsonConvert.DeserializeObject<DataTable>(jsonString);
    --정렬은 Client에서...
     DataView dv = new DataView(dt)
    {
        Sort = "emp_id"
    };
 
    this.dataGridView1.DataSource = dv.ToTable();
}

 

> 저장 함수(프로시저)

CREATE OR REPLACE FUNCTION public.usp_set_emp (
  p_employee json
)
RETURNS void AS
$body$
/*
2017.01.19, hm.ji, JSON을 사용한 TVP
*/

DECLARE
  text_var1 text;
  text_var2 text;
  text_var3 text;
BEGIN

--아래와 같이 2가지로 구현이 가능합니다.

--how to : 1, JSON을 테이블로 변환 했을 경우 table형식 선언
with upsert as (SELECT A.emp_id, A.emp_nm FROM json_to_recordset(p_employee) as A(emp_id INTEGER, emp_nm VARCHAR(40)) ),
--how to : 2, Composite Type (employee_udt) 사용
--with upsert as (SELECT A.emp_id, A.emp_nm FROM json_populate_recordset(null::employee_udt, p_employee) A),
del as
      (
        delete from employee
        where not exists(select 1 from  upsert a where a.emp_id = employee.emp_id)
      ),
upd as
      (
          update employee
          set emp_nm = upsert.emp_nm
          , last_chg_time = now()
          from upsert
          where upsert.emp_id = employee.emp_id
          AND upsert.emp_nm <> employee.emp_nm
      )
insert into employee
select upsert.emp_id, upsert.emp_nm, now(), now()
from upsert
where not exists(select 1 from  employee  where employee.emp_id = upsert.emp_id);
   
 EXCEPTION
  WHEN OTHERS THEN
    GET STACKED DIAGNOSTICS text_var1 = RETURNED_SQLSTATE,
       text_var2 =  MESSAGE_TEXT,
       text_var3 = PG_EXCEPTION_CONTEXT;
   RAISE EXCEPTION E'RETURNED_SQLSTATE: %,\n\nMESSAGE_TEXT: %,\n\nPG_EXCEPTION_CONTEXT: %', text_var1, text_var2, text_var3;

END;
$body$
LANGUAGE 'plpgsql'

 

>C# 저장

 

private void btnSave_Click(object sender, EventArgs e)
{
    string jsonString = JsonConvert.SerializeObject((DataTable)this.dataGridView1.DataSource);
 
    var _param = new[] 
    {
        new NpgsqlParameter
        {
            ParameterName="p_employee",
            NpgsqlDbType = NpgsqlDbType.Json,
            NpgsqlValue = jsonString
        }
    };
 
    SqlHelper.ExecuteNonQuery(this.connstring, "usp_set_emp", _param);
}

----

C#+Postgresql에서 함수(프로시저)의 TVP를 구현하는 방법중에 첫번째 올렸던 C#+Composite Type 개발 방법보다 C#+JSON이 더 깔끔하고 괜찮아 보입니다.

성능상에는 두가지 방법이 어떤 차이점이 있는지 다음에 올리겠습니다.

 

by jkson [2017.01.20 09:12:41]

Postgresql는 사용 안 해보았지만 좋은 팁들 많이 올려주셔서 다른 분들께 도움이 많이 되겠네요~


by 지현명 [2017.01.20 10:49:09]

전에 "부족한 재고까지만 보여주는 쿼리 문의" 에 답변해 주신 분이군요 ^^

MS-SQL, Oracle, Postgresql 이렇게 3개 사용하고 있는데 Postgresql 비용대비하여 괜찮은거 같습니다.

 

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입