SlideShare a Scribd company logo
예제로 살펴보는
포스트그레스큐엘의 독특한
SQL
다른 데이터베이스랑 호환되지 않아요
PgDay.Seoul 2021, 김상기
PgDay.Seoul 2021
Ver. 14 새 SQL
● WITH RECURSIVE ... (...)
SEARCH DEPTH FIRST BY order_column SET column_name
SELECT … ORDER BY column_name
● WITH RECURSIVE ... (...)
CYCLE cycle_check_column SET is_cycle USING column_name
SELECT … WHERE is_cycle = false ORDER BY column_name
● jsonb_data['key1']['subkey2']::int + 1
● SELECT range_agg(range_type_column) FROM table
2
PgDay.Seoul 2021
Ver. 14 새 SQL - WITH RECURSIVE
3
WITH RECURSIVE t AS (
SELECT *, addrname AS conname
FROM addrcodes
WHERE addrid = '380704'
UNION ALL
SELECT a.*, a.addrname || ' ' || t.conname
FROM addrcodes a, t
WHERE a.addrid = t.upaddr
)
SEARCH DEPTH FIRST BY addrname SET path
CYCLE addrname SET is_cycle USING path2
SELECT conname
FROM t
WHERE is_cycle = false
ORDER BY path DESC
FETCH FIRST 1 ROW ONLY
PgDay.Seoul 2021
Ver. 14 새 SQL - jsonb Subscripting
"public.t_jsonb" 테이블
필드명 | 종류 | NULL허용 | 초기값
--------------+-------+----------+--------
jsonb_column | jsonb | |
postgres=# SELECT jsonb_pretty(jsonb_column)
FROM t_jsonb
jsonb_pretty
{
"key1": {
"subkey1": 1,
"subkey2": 3
}
}
4
postgres=# SELECT
jsonb_column['key1']['subkey2'] FROM t_jsonb;
jsonb_column
--------------
3
postgres=# UPDATE t_jsonb SET
jsonb_column['key1']['subkey2'] =
to_jsonb((jsonb_column['key1']['subkey2'])::in
t + 1) where jsonb_column['key1']['subkey1'] =
'1'; -- 문자열은 '문자열'(X), '"문자열"'(O)
postgres=# select ...;
jsonb_pretty
{
"key1": {
"subkey1": 1,
"subkey2": 4
}
}
PgDay.Seoul 2021
Ver. 14 새 SQL - multirange 자료형 1
5
이 초록 영역을 어떻게 구할 것인가? multirange!
dTS *multirange
PgDay.Seoul 2021
Ver. 14 새 SQL - multirange 자료형 2
6
postgres=# SELECT lower(unnest), upper(unnest) - 1
FROM (SELECT unnest(
'{[2021-08-01, 2021-10-01)}'::datemultirange
- range_agg(daterange(startdate, enddate, '[]')))
FROM place_term
WHERE startdate > '2021-08-01' AND enddate < '2021-09-30') AS t;
lower | ?column?
------------+------------
2021-08-01 | 2021-08-03
2021-09-27 | 2021-09-30
(2개 행)
PgDay.Seoul 2021
대한민국 구석구석 자료 소개
7
● 한국관광공사에서 제공하는 공공 그래프 데이터베이스 자료
출처: http://data.visitkorea.or.kr/linked_open_data
● 이것을 PostgreSQL용으로 변환하고, 관계형 데이터베이스 모델로 바꿨다.
● 엔터티 관계도:
https://github.com/i0seph/visitkorea_for_pg/blob/master/graph2rdbms/visitkor
ea-erd.pdf
● flask 로 만든 샘플 코드는 그 위에
PgDay.Seoul 2021
예제 구성 - 첫화면
8
검색: 인덱스를 사용하는 %검색%
분류 검색: 계층형 쿼리와 그 외
추천 장소: 임의 뽑기 최적화
오늘 행사 중인 축제: 범위 자료형 검색
PgDay.Seoul 2021
예제 구성 - 설명 화면
9
분류와 지역: 계층형 쿼리
각 항목: key-value 자료 구조처리
인근 추천 장소: 위경도 인덱스 탐색 및 거리
계산
PgDay.Seoul 2021
SQL 1: DISTINCT ON
10
● 1:N 관계에서 N 자료 가운데 하나만 뽑기
○ 전통적으로 row_number() 윈도우 함수를 이용한 인라인뷰를 만들고, 그 값이 1인 것을 뽑음
SELECT …. FROM (
SELECT …, row_number() OVER (PARTITION BY … ORDER BY …) as no
FROM …) AS t WHERE no = 1
○ 이 복잡한 쿼리가 DISTINCT ON으로 해결 가능함 (sql_search_list.py)
SELECT DISTINCT ON (a.place_name, a.place_id)
a.place_id, a.place_name, b.imgurl
FROM place a LEFT JOIN place_images b ON a.place_id = b.place_id
WHERE place_name ~* to_regexp('자연 휴양림')
ORDER BY a.place_name, a.place_id, b.imgurl
FETCH FIRST 50 ROWS ONLY
PgDay.Seoul 2021
SQL 2: 문자열 검색
11
● 전통 기법: LIKE, ILIKE
○ 인덱스를 사용하지 않아 테이블 전체 탐색을 함, WHERE place_name LIKE %자연휴양림%
○ 단어 분리 상황에서 AND 연산 중복 비용 발생, WHERE place_name LIKE %자연% AND place_name
LIKE %휴양림%
● pg_trgm 확장 모듈과 정규식을 이용한 문제 풀기 (sql_search_list.py)
1. CREATE EXTENSION pg_trgm
2. CREATE INDEX place_name_i ON place USING gist_trgm_ops (place_name)
3. CREATE OR REPLACE FUNCTION to_regexp(text) RETURNS text LANGUAGE sql
IMMUTABLE AS $function$
SELECT string_agg('(?=.*' || a || ')','') FROM
UNNEST(tsvector_to_array(to_tsvector('simple', $1))) a
$function$
4. SELECT ...
FROM place a LEFT JOIN place_images b ON a.place_id = b.place_id
WHERE a.place_name ~* to_regexp('자연 휴양림')
...
PgDay.Seoul 2021
SQL 3: 배열 검색
12
● 전통 기법: IN
○ WHERE col IN (1,2,3,4)
● pg의 또 다른 문법: ANY
○ WHERE col = ANY(ARRAY[1,2,3,4])
○ 더 복잡한 문법을 왜 쓰지? SQLAlchemy 모듈에서 변수 바인딩이 쉬워짐
○ sql_place_list.py 참조
SELECT addrid FROM addrcodes WHERE upaddr = ANY(:locastr)
query_string으로 받은 문자열 loca 값을 loca.split() 함수로 배열 locastr 으로 바꾸고
그것을 그대로 쿼리로 넘겨 넘겨주면, SQLAlchemy 모듈이 알아서 처리해줌
○ 대부분 응용 프로그램 pg용 DB 드라이버들이 이 배열처리를 편하게 쓸 수 있도록
각자의 방법을 제공함
PgDay.Seoul 2021
SQL 4: 실무 예제와 쿼리 최적화
13
SELECT DISTINCT ON (a.place_id, a.place_name)
a.place_id, a.place_name, c.imgurl
FROM place a,
(SELECT place_id FROM place
WHERE loca IN (SELECT addrid FROM addrcodes WHERE upaddr = ANY (ARRAY['380300']))
AND cate IN ( WITH RECURSIVE t AS (
SELECT * FROM tourism WHERE uptour = ANY (ARRAY['A05', 'B02'])
UNION ALL
SELECT a.* FROM tourism a, t WHERE a.uptour = t.tourid)
SELECT tourid FROM t WHERE length(tourid) = 9)
ORDER BY place_name FETCH FIRST 50 ROWS ONLY) b
LEFT JOIN place_images c ON b.place_id = c.place_id
WHERE a.place_id = b.place_id
ORDER BY a.place_name, a.place_id, c.imgurl;
● 전라남도 곡성군 음식점과 숙박 시설 찾는 쿼리
PgDay.Seoul 2021
SQL 5: TABLESAMPLE - 임의 자료 탐색
14
● 전통적인 ORDER BY random() LIMIT 10 구문은 테이블 전체를 탐색한다.
● TABLESAMPLE {SYSTEM|BERNOULLI} (퍼센트)
○ SYSTEM: 임의 블록으로 가서 그 해당 자료 수 만큼 추출 (퍼센트 값이 아주 작다면 하나의
블록 읽기만 하는데, 그 안에 비슷한 자료들이 몰려 있으면 임의 추출의 의미가 퇴색함)
○ BERNOULLI: 블록 기준으로 해당 퍼센트 확률만큼 블록을 선택하고 거기서 자료를 추출
(확률값이 낮을 수록 더 많은 블록을 뒤지게 된다. 반면 임의 추출 품질은 좋아짐)
● place_images 테이블(블록수: 약 1,400개) 대상 베르누이 샘풀링 확률 보면,
○ 1% = 18, 0.1% = 43, 0.01% = 600 정도의 블록을 읽음으로 예제 코드에서는 0.05로 설정함
○ 이처럼 베르누이 확률을 사용할 경우는 비용과 품질 사이 적정값을 찾아야 함
SELECT DISTINCT ON (place_id) place_id, imgurl
FROM ( SELECT place_id, imgurl FROM place_images
TABLESAMPLE BERNOULLI (0.05) LIMIT 20
) a ORDER BY place_id, imgurl LIMIT 10
PgDay.Seoul 2021
SQL 6: 범위 자료형 탐색 - 오늘 방문하면 되는 곳
15
● 전통적인 범위, 기간 검색은 시작값(lower)과 마침값(upper)을 저장하고, >=,
<, BETWEEN 연산으로 처리 함.
○ 시작날짜가 오늘보다 작거나 같고, 마침날짜가 오늘보다 크거나 같은 것
startdate <= current_date and enddate >= current_date
● 범위자료형을 쓰면
○ 행사기간들 중에 오늘이 포함된 것
term @> current_date
● 전통적인 모델링을 바꿀 수 없다면 (sql_festa_list.py)
○ 범위자료형 변환 함수를 사용하는 함수 기반 인덱스를 만들어 쓴다
CREATE INDEX term_range_i ON place_term USING gist (daterange(startdate, enddate, '[]'));
SELECT * FROM place_term
WHERE daterange(startdate, enddate, '[]') @> CURRENT_DATE
AND daterange(date_trunc('month' , current_timestamp)::date, (date_trunc('month' ,
current_timestamp) + interval '1 month')::date) @> daterange(startdate, enddate, '[]')
PgDay.Seoul 2021
SQL 7: key-value 자료 처리 - json
16
● 처음부터 jsonb, json 자료형으로 DB에 넣고 꺼내기
○ json 양식 검사 문제
○ jsonb 인 경우 key 정렬 문제
○ 해당 칼럼이 toast에 저장되는 문제
● DB에는 관계형으로 저장되고, 응용프로그램에서 json으로 다루기
○ 어디서 json으로 변환할 것인가?
■ DB측: json_build_*(), json_agg() 함수를 사용
■ APP측: DB result row의 유연한 json 변환 작업을 제공해야함
PgDay.Seoul 2021
SQL 7: key-value 자료 처리 - 예제
17
● 예제에서는 N:N 관계형 모델링 자료에 대한 처리를 다룬다.
● DB 측 ● 응용프로그램 측(python flask)
SELECT b.attname, a.v
FROM place_attrib a JOIN attnames b ON a.attid = b.attid
WHERE place_id = 130679;
attname | v
----------------------+------------------------------------
규모 | 지상 3층
신용카드사용여부 | 불가능
전화번호 | 033-462-2303
유모차대여서비스 | 불가능
주소 | 강원도 인제군 북면 만해로 91
이용요금 | 무료
애완동물동반가능여부 | 불가능
우편번호 | 24606
이용시간 | 09:00~17:00
쉬는날 | 매주 월요일, 1월 1일, 설/추석 당일
주차시설 | 주차 가능
@app.route('/ajax/getattrib/<int:place_id>')
def get_attribute(place_id):
mod = __import__('sql_get_attribute')
d = sql(mod.query, {'place_id': place_id})
return jsonify([dict(row) for row in d.fetchall()])
PgDay.Seoul 2021
SQL 8: 위경도 처리
18
● 위경도값 처리를 위한 작업
○ WGS84 좌표계를 사용하는 위경도값이라면,
가장 단순한 방법으로 earthdistance 확장 모듈을 사용하는 것이다.
CREATE EXTENSION earthdistance CASCADE;
ALTER TABLE place ADD position earth GENERATED ALWAYS AS (ll_to_earth(lat, long)) STORED;
-- INSERT 작업에서는 이 position 칼럼의 값으로 default를 쓴다
CREATE INDEX CONCURRENTLY place_position_i ON place USING gist (position);
-- 자료 찾기, :y1 = 현재 위도, :x1 = 현재 경도, :place_id = 현재 장소번호
-- 현재 위치에서 2Km 안에 있는 다른 장소들 찾기
SELECT place_id,
round((earth_distance(position, ll_to_earth(:y1, :x1))::numeric / 1000)::numeric, 2)::float as
distance
FROM place
WHERE earth_box(ll_to_earth(:y1, :x1), 2000) @> position AND a.place_id <> :place_id
PgDay.Seoul 2021
그 외 SQL 들
19
● 형변환자: ::
● {INSERT|UPDATE|DELETE} … RETURNING …
● JOIN UPDATE: UPDATE … FROM
● JOIN DELETE: DELETE FROM … USING …
● INSERT or UPDATE: INSERT INTO … ON CONFLICT …
● 명령어 VAULES: SELECT * FROM (VALUES(1,2,3)) t
● LATERAL 예약어: SELECT … FROM a, LATERAL (SELECT * FROM b where b.col =
a.col) …
● LISTEN & NOTIFY
PgDay.Seoul 2021
참고 자료
20
● python flask
○ flask.palletsprojects.com
● jquery
○ jquery.com
● PostgreSQL
○ postgresql.org & postgresql.kr
● 발표를 위한 샘플 코드
○ https://github.com/i0seph/visitkorea_for_pg
● PostgreSQL 한국 사용자 모임
○ https://www.facebook.com/groups/postgres.kr

More Related Content

PDF
[Pgday.Seoul 2020] SQL Tuning
PDF
[Pgday.Seoul 2017] 7. PostgreSQL DB Tuning 기업사례 - 송춘자
PDF
[pgday.Seoul 2022] 서비스개편시 PostgreSQL 도입기 - 진소린 & 김태정
PDF
Mvcc in postgreSQL 권건우
PDF
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱
PDF
[pgday.Seoul 2022] PostgreSQL구조 - 윤성재
PDF
PostgreSQL 공간관리 살펴보기 이근오
PDF
[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱
[Pgday.Seoul 2020] SQL Tuning
[Pgday.Seoul 2017] 7. PostgreSQL DB Tuning 기업사례 - 송춘자
[pgday.Seoul 2022] 서비스개편시 PostgreSQL 도입기 - 진소린 & 김태정
Mvcc in postgreSQL 권건우
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱
[pgday.Seoul 2022] PostgreSQL구조 - 윤성재
PostgreSQL 공간관리 살펴보기 이근오
[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱

What's hot (20)

PPTX
MySQL_MariaDB-성능개선-202201.pptx
PDF
[Pgday.Seoul 2021] 2. Porting Oracle UDF and Optimization
PDF
Backup and-recovery2
PDF
Pgday bdr 천정대
PDF
PostgreSQL Deep Internal
PDF
Linux tuning to improve PostgreSQL performance
PDF
MySQL 상태 메시지 분석 및 활용
PDF
Deep dive into PostgreSQL statistics.
PDF
ClickHouse in Real Life. Case Studies and Best Practices, by Alexander Zaitsev
PDF
[APJ] Common Table Expressions (CTEs) in SQL
 
PPTX
MySQL8.0_performance_schema.pptx
PDF
Optimizing Delta/Parquet Data Lakes for Apache Spark
PPTX
PDF
PostgreSQL replication
PDF
Mastering PostgreSQL Administration
 
PPTX
Postgresql Database Administration Basic - Day1
PPTX
Mongo DB 성능최적화 전략
PDF
Understanding PostgreSQL LW Locks
PDF
[Pgday.Seoul 2017] 6. GIN vs GiST 인덱스 이야기 - 박진우
PDF
MySQL Administrator 2021 - 네오클로바
MySQL_MariaDB-성능개선-202201.pptx
[Pgday.Seoul 2021] 2. Porting Oracle UDF and Optimization
Backup and-recovery2
Pgday bdr 천정대
PostgreSQL Deep Internal
Linux tuning to improve PostgreSQL performance
MySQL 상태 메시지 분석 및 활용
Deep dive into PostgreSQL statistics.
ClickHouse in Real Life. Case Studies and Best Practices, by Alexander Zaitsev
[APJ] Common Table Expressions (CTEs) in SQL
 
MySQL8.0_performance_schema.pptx
Optimizing Delta/Parquet Data Lakes for Apache Spark
PostgreSQL replication
Mastering PostgreSQL Administration
 
Postgresql Database Administration Basic - Day1
Mongo DB 성능최적화 전략
Understanding PostgreSQL LW Locks
[Pgday.Seoul 2017] 6. GIN vs GiST 인덱스 이야기 - 박진우
MySQL Administrator 2021 - 네오클로바
Ad

Similar to [Pgday.Seoul 2021] 1. 예제로 살펴보는 포스트그레스큐엘의 독특한 SQL (20)

PDF
PostgreSQL로 배우는 SQL 기초
PDF
PostGIS 시작하기
PDF
[Pgday.Seoul 2018] PostgreSQL 11 새 기능 소개
PPTX
[Foss4 g2013 korea]postgis와 geoserver를 이용한 대용량 공간데이터 기반 일기도 서비스 구축 사례
PPTX
공간SQL을 이용한 공간자료분석 기초실습
PDF
공간데이터베이스(Spatial db)
PDF
[Pgday.Seoul 2017] 8. PostgreSQL 10 새기능 소개 - 김상기
PDF
일주일만에 끝내는 MySQL
PPTX
엘라스틱서치 이해하기 20160613
PPTX
Mongodb and spatial
PDF
파이썬 데이터베이스 연결 2탄
PPTX
Db활용가이드
PDF
Partner Story(Megazone): 금융사 실전 프로젝트 DeepDive
PDF
MongoDB 하루만에 끝내기
PPTX
공간정보거점대학 PostGIS 고급과정
PPTX
Gpdb best practices v a01 20150313
PDF
TABLE ACCESS 패턴을 이용한 SQL 튜닝_Wh oracle
PPTX
파이썬 sqlite 이해하기
PDF
Hive 입문 발표 자료
PDF
[Pgday.Seoul 2017] 1. PostGIS의 사례로 본 PostgreSQL 확장 - 장병진
PostgreSQL로 배우는 SQL 기초
PostGIS 시작하기
[Pgday.Seoul 2018] PostgreSQL 11 새 기능 소개
[Foss4 g2013 korea]postgis와 geoserver를 이용한 대용량 공간데이터 기반 일기도 서비스 구축 사례
공간SQL을 이용한 공간자료분석 기초실습
공간데이터베이스(Spatial db)
[Pgday.Seoul 2017] 8. PostgreSQL 10 새기능 소개 - 김상기
일주일만에 끝내는 MySQL
엘라스틱서치 이해하기 20160613
Mongodb and spatial
파이썬 데이터베이스 연결 2탄
Db활용가이드
Partner Story(Megazone): 금융사 실전 프로젝트 DeepDive
MongoDB 하루만에 끝내기
공간정보거점대학 PostGIS 고급과정
Gpdb best practices v a01 20150313
TABLE ACCESS 패턴을 이용한 SQL 튜닝_Wh oracle
파이썬 sqlite 이해하기
Hive 입문 발표 자료
[Pgday.Seoul 2017] 1. PostGIS의 사례로 본 PostgreSQL 확장 - 장병진
Ad

More from PgDay.Seoul (17)

PDF
[pgday.Seoul 2022] PostgreSQL with Google Cloud
PDF
[Pgday.Seoul 2020] 포스트그레스큐엘 자국어화 이야기
PDF
[Pgday.Seoul 2019] AppOS 고성능 I/O 확장 모듈로 성능 10배 향상시키기
PDF
[Pgday.Seoul 2019] Citus를 이용한 분산 데이터베이스
PDF
[Pgday.Seoul 2019] Advanced FDW
PDF
[Pgday.Seoul 2018] PostgreSQL 성능을 위해 개발된 라이브러리 OS 소개 apposha
PDF
[Pgday.Seoul 2018] PostgreSQL Authentication with FreeIPA
PDF
[Pgday.Seoul 2018] 이기종 DB에서 PostgreSQL로의 Migration을 위한 DB2PG
PDF
[Pgday.Seoul 2018] AWS Cloud 환경에서 PostgreSQL 구축하기
PDF
[Pgday.Seoul 2018] Greenplum의 노드 분산 설계
PDF
[Pgday.Seoul 2018] replacing oracle with edb postgres
PDF
[Pgday.Seoul 2017] 5. 테드폴허브(올챙이) PostgreSQL 확장하기 - 조현종
PDF
[Pgday.Seoul 2017] 4. Composite Type/JSON 파라미터를 활용한 TVP구현(with C#, JAVA) - 지현명
PDF
[Pgday.Seoul 2017] 3. PostgreSQL WAL Buffers, Clog Buffers Deep Dive - 이근오
PDF
PostgreSQL 9.6 새 기능 소개
PDF
pg_hba.conf 이야기
PPTX
Pg report 20161010_02
[pgday.Seoul 2022] PostgreSQL with Google Cloud
[Pgday.Seoul 2020] 포스트그레스큐엘 자국어화 이야기
[Pgday.Seoul 2019] AppOS 고성능 I/O 확장 모듈로 성능 10배 향상시키기
[Pgday.Seoul 2019] Citus를 이용한 분산 데이터베이스
[Pgday.Seoul 2019] Advanced FDW
[Pgday.Seoul 2018] PostgreSQL 성능을 위해 개발된 라이브러리 OS 소개 apposha
[Pgday.Seoul 2018] PostgreSQL Authentication with FreeIPA
[Pgday.Seoul 2018] 이기종 DB에서 PostgreSQL로의 Migration을 위한 DB2PG
[Pgday.Seoul 2018] AWS Cloud 환경에서 PostgreSQL 구축하기
[Pgday.Seoul 2018] Greenplum의 노드 분산 설계
[Pgday.Seoul 2018] replacing oracle with edb postgres
[Pgday.Seoul 2017] 5. 테드폴허브(올챙이) PostgreSQL 확장하기 - 조현종
[Pgday.Seoul 2017] 4. Composite Type/JSON 파라미터를 활용한 TVP구현(with C#, JAVA) - 지현명
[Pgday.Seoul 2017] 3. PostgreSQL WAL Buffers, Clog Buffers Deep Dive - 이근오
PostgreSQL 9.6 새 기능 소개
pg_hba.conf 이야기
Pg report 20161010_02

[Pgday.Seoul 2021] 1. 예제로 살펴보는 포스트그레스큐엘의 독특한 SQL

  • 1. 예제로 살펴보는 포스트그레스큐엘의 독특한 SQL 다른 데이터베이스랑 호환되지 않아요 PgDay.Seoul 2021, 김상기
  • 2. PgDay.Seoul 2021 Ver. 14 새 SQL ● WITH RECURSIVE ... (...) SEARCH DEPTH FIRST BY order_column SET column_name SELECT … ORDER BY column_name ● WITH RECURSIVE ... (...) CYCLE cycle_check_column SET is_cycle USING column_name SELECT … WHERE is_cycle = false ORDER BY column_name ● jsonb_data['key1']['subkey2']::int + 1 ● SELECT range_agg(range_type_column) FROM table 2
  • 3. PgDay.Seoul 2021 Ver. 14 새 SQL - WITH RECURSIVE 3 WITH RECURSIVE t AS ( SELECT *, addrname AS conname FROM addrcodes WHERE addrid = '380704' UNION ALL SELECT a.*, a.addrname || ' ' || t.conname FROM addrcodes a, t WHERE a.addrid = t.upaddr ) SEARCH DEPTH FIRST BY addrname SET path CYCLE addrname SET is_cycle USING path2 SELECT conname FROM t WHERE is_cycle = false ORDER BY path DESC FETCH FIRST 1 ROW ONLY
  • 4. PgDay.Seoul 2021 Ver. 14 새 SQL - jsonb Subscripting "public.t_jsonb" 테이블 필드명 | 종류 | NULL허용 | 초기값 --------------+-------+----------+-------- jsonb_column | jsonb | | postgres=# SELECT jsonb_pretty(jsonb_column) FROM t_jsonb jsonb_pretty { "key1": { "subkey1": 1, "subkey2": 3 } } 4 postgres=# SELECT jsonb_column['key1']['subkey2'] FROM t_jsonb; jsonb_column -------------- 3 postgres=# UPDATE t_jsonb SET jsonb_column['key1']['subkey2'] = to_jsonb((jsonb_column['key1']['subkey2'])::in t + 1) where jsonb_column['key1']['subkey1'] = '1'; -- 문자열은 '문자열'(X), '"문자열"'(O) postgres=# select ...; jsonb_pretty { "key1": { "subkey1": 1, "subkey2": 4 } }
  • 5. PgDay.Seoul 2021 Ver. 14 새 SQL - multirange 자료형 1 5 이 초록 영역을 어떻게 구할 것인가? multirange! dTS *multirange
  • 6. PgDay.Seoul 2021 Ver. 14 새 SQL - multirange 자료형 2 6 postgres=# SELECT lower(unnest), upper(unnest) - 1 FROM (SELECT unnest( '{[2021-08-01, 2021-10-01)}'::datemultirange - range_agg(daterange(startdate, enddate, '[]'))) FROM place_term WHERE startdate > '2021-08-01' AND enddate < '2021-09-30') AS t; lower | ?column? ------------+------------ 2021-08-01 | 2021-08-03 2021-09-27 | 2021-09-30 (2개 행)
  • 7. PgDay.Seoul 2021 대한민국 구석구석 자료 소개 7 ● 한국관광공사에서 제공하는 공공 그래프 데이터베이스 자료 출처: http://data.visitkorea.or.kr/linked_open_data ● 이것을 PostgreSQL용으로 변환하고, 관계형 데이터베이스 모델로 바꿨다. ● 엔터티 관계도: https://github.com/i0seph/visitkorea_for_pg/blob/master/graph2rdbms/visitkor ea-erd.pdf ● flask 로 만든 샘플 코드는 그 위에
  • 8. PgDay.Seoul 2021 예제 구성 - 첫화면 8 검색: 인덱스를 사용하는 %검색% 분류 검색: 계층형 쿼리와 그 외 추천 장소: 임의 뽑기 최적화 오늘 행사 중인 축제: 범위 자료형 검색
  • 9. PgDay.Seoul 2021 예제 구성 - 설명 화면 9 분류와 지역: 계층형 쿼리 각 항목: key-value 자료 구조처리 인근 추천 장소: 위경도 인덱스 탐색 및 거리 계산
  • 10. PgDay.Seoul 2021 SQL 1: DISTINCT ON 10 ● 1:N 관계에서 N 자료 가운데 하나만 뽑기 ○ 전통적으로 row_number() 윈도우 함수를 이용한 인라인뷰를 만들고, 그 값이 1인 것을 뽑음 SELECT …. FROM ( SELECT …, row_number() OVER (PARTITION BY … ORDER BY …) as no FROM …) AS t WHERE no = 1 ○ 이 복잡한 쿼리가 DISTINCT ON으로 해결 가능함 (sql_search_list.py) SELECT DISTINCT ON (a.place_name, a.place_id) a.place_id, a.place_name, b.imgurl FROM place a LEFT JOIN place_images b ON a.place_id = b.place_id WHERE place_name ~* to_regexp('자연 휴양림') ORDER BY a.place_name, a.place_id, b.imgurl FETCH FIRST 50 ROWS ONLY
  • 11. PgDay.Seoul 2021 SQL 2: 문자열 검색 11 ● 전통 기법: LIKE, ILIKE ○ 인덱스를 사용하지 않아 테이블 전체 탐색을 함, WHERE place_name LIKE %자연휴양림% ○ 단어 분리 상황에서 AND 연산 중복 비용 발생, WHERE place_name LIKE %자연% AND place_name LIKE %휴양림% ● pg_trgm 확장 모듈과 정규식을 이용한 문제 풀기 (sql_search_list.py) 1. CREATE EXTENSION pg_trgm 2. CREATE INDEX place_name_i ON place USING gist_trgm_ops (place_name) 3. CREATE OR REPLACE FUNCTION to_regexp(text) RETURNS text LANGUAGE sql IMMUTABLE AS $function$ SELECT string_agg('(?=.*' || a || ')','') FROM UNNEST(tsvector_to_array(to_tsvector('simple', $1))) a $function$ 4. SELECT ... FROM place a LEFT JOIN place_images b ON a.place_id = b.place_id WHERE a.place_name ~* to_regexp('자연 휴양림') ...
  • 12. PgDay.Seoul 2021 SQL 3: 배열 검색 12 ● 전통 기법: IN ○ WHERE col IN (1,2,3,4) ● pg의 또 다른 문법: ANY ○ WHERE col = ANY(ARRAY[1,2,3,4]) ○ 더 복잡한 문법을 왜 쓰지? SQLAlchemy 모듈에서 변수 바인딩이 쉬워짐 ○ sql_place_list.py 참조 SELECT addrid FROM addrcodes WHERE upaddr = ANY(:locastr) query_string으로 받은 문자열 loca 값을 loca.split() 함수로 배열 locastr 으로 바꾸고 그것을 그대로 쿼리로 넘겨 넘겨주면, SQLAlchemy 모듈이 알아서 처리해줌 ○ 대부분 응용 프로그램 pg용 DB 드라이버들이 이 배열처리를 편하게 쓸 수 있도록 각자의 방법을 제공함
  • 13. PgDay.Seoul 2021 SQL 4: 실무 예제와 쿼리 최적화 13 SELECT DISTINCT ON (a.place_id, a.place_name) a.place_id, a.place_name, c.imgurl FROM place a, (SELECT place_id FROM place WHERE loca IN (SELECT addrid FROM addrcodes WHERE upaddr = ANY (ARRAY['380300'])) AND cate IN ( WITH RECURSIVE t AS ( SELECT * FROM tourism WHERE uptour = ANY (ARRAY['A05', 'B02']) UNION ALL SELECT a.* FROM tourism a, t WHERE a.uptour = t.tourid) SELECT tourid FROM t WHERE length(tourid) = 9) ORDER BY place_name FETCH FIRST 50 ROWS ONLY) b LEFT JOIN place_images c ON b.place_id = c.place_id WHERE a.place_id = b.place_id ORDER BY a.place_name, a.place_id, c.imgurl; ● 전라남도 곡성군 음식점과 숙박 시설 찾는 쿼리
  • 14. PgDay.Seoul 2021 SQL 5: TABLESAMPLE - 임의 자료 탐색 14 ● 전통적인 ORDER BY random() LIMIT 10 구문은 테이블 전체를 탐색한다. ● TABLESAMPLE {SYSTEM|BERNOULLI} (퍼센트) ○ SYSTEM: 임의 블록으로 가서 그 해당 자료 수 만큼 추출 (퍼센트 값이 아주 작다면 하나의 블록 읽기만 하는데, 그 안에 비슷한 자료들이 몰려 있으면 임의 추출의 의미가 퇴색함) ○ BERNOULLI: 블록 기준으로 해당 퍼센트 확률만큼 블록을 선택하고 거기서 자료를 추출 (확률값이 낮을 수록 더 많은 블록을 뒤지게 된다. 반면 임의 추출 품질은 좋아짐) ● place_images 테이블(블록수: 약 1,400개) 대상 베르누이 샘풀링 확률 보면, ○ 1% = 18, 0.1% = 43, 0.01% = 600 정도의 블록을 읽음으로 예제 코드에서는 0.05로 설정함 ○ 이처럼 베르누이 확률을 사용할 경우는 비용과 품질 사이 적정값을 찾아야 함 SELECT DISTINCT ON (place_id) place_id, imgurl FROM ( SELECT place_id, imgurl FROM place_images TABLESAMPLE BERNOULLI (0.05) LIMIT 20 ) a ORDER BY place_id, imgurl LIMIT 10
  • 15. PgDay.Seoul 2021 SQL 6: 범위 자료형 탐색 - 오늘 방문하면 되는 곳 15 ● 전통적인 범위, 기간 검색은 시작값(lower)과 마침값(upper)을 저장하고, >=, <, BETWEEN 연산으로 처리 함. ○ 시작날짜가 오늘보다 작거나 같고, 마침날짜가 오늘보다 크거나 같은 것 startdate <= current_date and enddate >= current_date ● 범위자료형을 쓰면 ○ 행사기간들 중에 오늘이 포함된 것 term @> current_date ● 전통적인 모델링을 바꿀 수 없다면 (sql_festa_list.py) ○ 범위자료형 변환 함수를 사용하는 함수 기반 인덱스를 만들어 쓴다 CREATE INDEX term_range_i ON place_term USING gist (daterange(startdate, enddate, '[]')); SELECT * FROM place_term WHERE daterange(startdate, enddate, '[]') @> CURRENT_DATE AND daterange(date_trunc('month' , current_timestamp)::date, (date_trunc('month' , current_timestamp) + interval '1 month')::date) @> daterange(startdate, enddate, '[]')
  • 16. PgDay.Seoul 2021 SQL 7: key-value 자료 처리 - json 16 ● 처음부터 jsonb, json 자료형으로 DB에 넣고 꺼내기 ○ json 양식 검사 문제 ○ jsonb 인 경우 key 정렬 문제 ○ 해당 칼럼이 toast에 저장되는 문제 ● DB에는 관계형으로 저장되고, 응용프로그램에서 json으로 다루기 ○ 어디서 json으로 변환할 것인가? ■ DB측: json_build_*(), json_agg() 함수를 사용 ■ APP측: DB result row의 유연한 json 변환 작업을 제공해야함
  • 17. PgDay.Seoul 2021 SQL 7: key-value 자료 처리 - 예제 17 ● 예제에서는 N:N 관계형 모델링 자료에 대한 처리를 다룬다. ● DB 측 ● 응용프로그램 측(python flask) SELECT b.attname, a.v FROM place_attrib a JOIN attnames b ON a.attid = b.attid WHERE place_id = 130679; attname | v ----------------------+------------------------------------ 규모 | 지상 3층 신용카드사용여부 | 불가능 전화번호 | 033-462-2303 유모차대여서비스 | 불가능 주소 | 강원도 인제군 북면 만해로 91 이용요금 | 무료 애완동물동반가능여부 | 불가능 우편번호 | 24606 이용시간 | 09:00~17:00 쉬는날 | 매주 월요일, 1월 1일, 설/추석 당일 주차시설 | 주차 가능 @app.route('/ajax/getattrib/<int:place_id>') def get_attribute(place_id): mod = __import__('sql_get_attribute') d = sql(mod.query, {'place_id': place_id}) return jsonify([dict(row) for row in d.fetchall()])
  • 18. PgDay.Seoul 2021 SQL 8: 위경도 처리 18 ● 위경도값 처리를 위한 작업 ○ WGS84 좌표계를 사용하는 위경도값이라면, 가장 단순한 방법으로 earthdistance 확장 모듈을 사용하는 것이다. CREATE EXTENSION earthdistance CASCADE; ALTER TABLE place ADD position earth GENERATED ALWAYS AS (ll_to_earth(lat, long)) STORED; -- INSERT 작업에서는 이 position 칼럼의 값으로 default를 쓴다 CREATE INDEX CONCURRENTLY place_position_i ON place USING gist (position); -- 자료 찾기, :y1 = 현재 위도, :x1 = 현재 경도, :place_id = 현재 장소번호 -- 현재 위치에서 2Km 안에 있는 다른 장소들 찾기 SELECT place_id, round((earth_distance(position, ll_to_earth(:y1, :x1))::numeric / 1000)::numeric, 2)::float as distance FROM place WHERE earth_box(ll_to_earth(:y1, :x1), 2000) @> position AND a.place_id <> :place_id
  • 19. PgDay.Seoul 2021 그 외 SQL 들 19 ● 형변환자: :: ● {INSERT|UPDATE|DELETE} … RETURNING … ● JOIN UPDATE: UPDATE … FROM ● JOIN DELETE: DELETE FROM … USING … ● INSERT or UPDATE: INSERT INTO … ON CONFLICT … ● 명령어 VAULES: SELECT * FROM (VALUES(1,2,3)) t ● LATERAL 예약어: SELECT … FROM a, LATERAL (SELECT * FROM b where b.col = a.col) … ● LISTEN & NOTIFY
  • 20. PgDay.Seoul 2021 참고 자료 20 ● python flask ○ flask.palletsprojects.com ● jquery ○ jquery.com ● PostgreSQL ○ postgresql.org & postgresql.kr ● 발표를 위한 샘플 코드 ○ https://github.com/i0seph/visitkorea_for_pg ● PostgreSQL 한국 사용자 모임 ○ https://www.facebook.com/groups/postgres.kr