이동평균(Moving Average) 는 시계열 데이터를 분석할 때 이상치(Outlier), 특이값, 잡음(Noise) 의 영향을 줄이거나 제거하는 Smoothing 의 목적이나, 또는 미래 예측에 자주 사용됩니다.  개념이 이해하기 쉽고 직관적이기 때문에 실무에서 많이 사용됩니다. 주식 투자를 하는 분이라면 아마도 이동평균에 대해서 익숙할 것입니다. 

 

이동평균에는 가중치를 어떻게 부여하느냐에 따라서 단순이동평균(Simple Moving Average), 가중이동평균(Weighted Moving Average), 지수이동평균(Exponential Moving Average) 등이 있습니다. 

 

이번 포스팅에서는 PostgreSQL, Greenplum DB에서 Window Function 을 사용하여 가중치를 사용하지 않는 (혹은, 모든 값에 동일한 가중치 1을 부여한다고 볼 수도 있는) 

 

(1) 단순이동평균 계산하기 (Calculating a Simple Moving Average) 

(2) 처음 이동평균 날짜 모자라는 부분은 NULL 처리하고 단순이동평균 계산하기

(3) 누적 단순이동평균 계산하기 (Calculating a Cumulative Simple Moving Average)

 

하는 방법을 소개하겠습니다. 

 

 

PostgreSQL, Greenplum, Simple Moving Average using Window Function

 

 

먼저, 세일즈 날짜와 판매금액의 두 개 칼럼으로 구성된, 예제로 사용할 간단한 시계열 데이터(Time Series Data) 테이블을 만들어보겠습니다.  

 

-- creating a sample table
DROP TABLE IF EXISTS sales;
CREATE TABLE sales (
	sale_dt date
	, sale_amt int
) DISTRIBUTED RANDOMLY;

INSERT INTO sales VALUES 
('2021-06-01', 230)
, ('2021-06-02', 235)
, ('2021-06-03', 231)
, ('2021-06-04', 244)
, ('2021-06-05', 202)
, ('2021-06-06', 260)
, ('2021-06-07', 240)
, ('2021-06-08', 235)
, ('2021-06-09', 239)
, ('2021-06-10', 242)
, ('2021-06-11', 244)
, ('2021-06-12', 241)
, ('2021-06-13', 246)
, ('2021-06-14', 247)
, ('2021-06-15', 249)
, ('2021-06-16', 245)
, ('2021-06-17', 242)
, ('2021-06-18', 246)
, ('2021-06-19', 245)
, ('2021-06-20', 190)
, ('2021-06-21', 230)
, ('2021-06-22', 235)
, ('2021-06-23', 231)
, ('2021-06-24', 238)
, ('2021-06-25', 241)
, ('2021-06-26', 245)
, ('2021-06-27', 242)
, ('2021-06-28', 243)
, ('2021-06-29', 240)
, ('2021-06-30', 238);

SELECT * FROM sales ORDER BY sale_dt LIMIT 5;

--sale_dt        sale_amt
--2021-06-01	230
--2021-06-02	235
--2021-06-03	231
--2021-06-04	244
--2021-06-05	202

 

 

(1) 단순이동평균 계산하기 (Calculating a Simple Moving Average) 

 

현재 날짜를 기준으로 2일전~현재날짜 까지 총 3일 기간 동안의 값을 사용하여 단순 이동평균을 구해보겠습니다. 

 

moving average for last 3 days = (Xt + Xt-1 + Xt-2) / 3

 

PostgreSQL 의 9.0 이상의 버전에서는 AVG()와  OVER() 의 Window Function을 사용하여 매우 편리하게 단순이동평균 (Simple Moving Average)을 계산할 수 있습니다. 

 

시계열 데이터는 시간의 순서가 중요하므로 OVER(ORDER BY sale_dt)  에서 먼저 날짜를 기준으로 정렬을 해주어야 합니다.

 

OVER(ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) 로 2일전~현재날짜 까지 총 3일 간의 Window 를 대상으로 평균을 계산하는 것을 지정해줍니다. 

 

가령, 아래의 '2021-06-03' 일의 3일 단순이동평균값은 아래와 같이 '2021-06-01', '2021-06-02', '2021-06-03' 일의 3일치 세일즈 판매금액의 평균이 되겠습니다. 

 

* 3일 단순이동평균('2021-06-03') = (230 + 235 + 231) / 3 = 232.0

 

ROUND(avg(), 1) 함수를 사용해서 단순이동평균값에 대해 소수점 첫째자리 반올림을 할 수 있습니다. 그리고 필요 시 단순이동평균 계산할 대상을 조회할 때 WHERE 조건절을 추가할 수도 있습니다. 

 

-- Calculating a Moving Average for last 3 days using Window Function

SELECT 
	sale_dt
	, sale_amt
	, ROUND(
		AVG(sale_amt) 
		OVER(
			ORDER BY sale_dt 
			ROWS BETWEEN 2 PRECEDING AND CURRENT ROW), 1
		) AS avg_sale_amt
FROM sales
ORDER BY sale_dt;

--sale_dt   sale_amt   avg_sale_amt
--2021-06-01	230	230.0
--2021-06-02	235	232.5
--2021-06-03	231	232.0
--2021-06-04	244	236.7
--2021-06-05	202	225.7
--2021-06-06	260	235.3
--2021-06-07	240	234.0
--2021-06-08	235	245.0
--2021-06-09	239	238.0
--2021-06-10	242	238.7
--2021-06-11	244	241.7
--2021-06-12	241	242.3
--2021-06-13	246	243.7
--2021-06-14	247	244.7
--2021-06-15	249	247.3
--2021-06-16	245	247.0
--2021-06-17	242	245.3
--2021-06-18	246	244.3
--2021-06-19	245	244.3
--2021-06-20	190	227.0
--2021-06-21	230	221.7
--2021-06-22	235	218.3
--2021-06-23	231	232.0
--2021-06-24	238	234.7
--2021-06-25	241	236.7
--2021-06-26	245	241.3
--2021-06-27	242	242.7
--2021-06-28	243	243.3
--2021-06-29	240	241.7
--2021-06-30	238	240.3

 

 

날짜를 X 축으로 놓고, Y 축에는 날짜별 (a) 세일즈 금액, (b) 3일 단순이동평균 세일즈 금액 을 시계열 그래프로 나타내서 비교해보면 아래와 같습니다. 예상했던대로 '3일 단순이동평균' 세일즈 금액이 스파이크(spike) 없이 smoothing 되어있음을 확인할 수 있습니다. 

 

아래 코드는 Jupyter Notebook에서 Python 으로 Greenplum DB에 연결(connect)하여, SQL query 를 해온 결과를 Python pandas의 DataFrame으로 만들어서, matplotlib 으로 시계열 그래프를 그려본 것입니다.

(* 참고: Jupyter Notebook에서 PostgreSQL, Greenplum DB connect 하여 데이터 가져오는 방법은 https://rfriend.tistory.com/577, https://rfriend.tistory.com/579 참조)

 

## --- Jupyter Notebook ---

import pandas as pd
import matplotlib.pyplot as plt

## loading ipython, sqlalchemy, spycopg2
%load_ext sql

## Greenplum DB connection
%sql postgresql://dsuser:changeme@localhost:5432/demo
#'Connected: dsuser@demo'


## getting data from Greenplum by DB connection from jupyter notebook
%%sql sam << SELECT 
sale_dt
, sale_amt
, ROUND(
AVG(sale_amt) 
OVER(
ORDER BY sale_dt 
ROWS BETWEEN 2 PRECEDING 
AND CURRENT ROW)
  , 1
  ) AS avg_sale_amt
FROM sales
ORDER BY sale_dt;

# * postgresql://dsuser:***@localhost:5432/demo
#30 rows affected.
#Returning data to local variable sam


## converting to pandas DataFrame
sam_df = sam.DataFrame()


sam_df.head()
#sale_dt	sale_amt	avg_sale_amt
#0	2021-06-01	230	230.0
#1	2021-06-02	235	232.5
#2	2021-06-03	231	232.0
#3	2021-06-04	244	236.7
#4	2021-06-05	202	225.7

## plotting time-series plot
import matplotlib.pyplot as plt

plt.rcParams['figure.figsize'] = [14, 10]
plt.plot(sam_df.sale_dt, sam_df.sale_amt, marker='s', color='r', label='original')
plt.plot(sam_df.sale_dt, sam_df.avg_sale_amt, marker='o', color='b', label='moving average')
plt.title('Simple Moving Average', fontsize=18)
plt.xlabel('Sale Date', fontsize=14)
plt.ylabel('Sale Amount', fontsize=14)
plt.legend(fontsize=12, loc='best')
plt.show()

 

original data vs. simple moving average

 

 

 

 

(2) 처음 이동평균 날짜 모자라는 부분은 NULL 처리하고 단순이동평균 계산하기

 

위의 (1)번에서 '3일 단순이동평균' 값을 계산할 때 시계열 데이터가 시작하는 첫번째와 두번째 날짜에 대해서는 이전 데이터가 존재하지 않기 때문에 '3일치' 데이터가 부족하게 됩니다. (만약 '10일 단순이동평균'을 계산한다고 하면 처음 시작하는 9일치 데이터의 경우 '10일치' 데이터에는 모자라게 되겠지요.) 

 

위의 (1)번에서는 이처럼 '3일치' 데이터가 모자라는 '2021-06-01', '2021-06-02' 일의 경우 '3일치'가 아니라 '1일치', '2일치' 단순이동평균으로 대체 계산해서 값을 채워넣었습니다. 

 

하지만, 필요에 따라서는 '3일치 단순이동평균'이라고 했을 때 이전 데이터가 '3일치'가 안되는 경우에는 단순이동평균을 계산하지 말고 그냥 'NULL' 값으로 처리하고 싶은 경우도 있을 것입니다. 이때 (2-1) CASE WHEH 과 AVG(), OVER() window function을 사용하는 방법, (2-2) LAG(), OVER() window function 을 이용하는 방법의 두 가지를 소개하겠습니다. 

 

 

(2-1) CASE WHEH 과 AVG(), OVER() window function을 사용하여 단순이동평균 계산하고, 이동평균계산 날짜 모자라면 NULL 처리하는 방법

 

SELECT 
	sale_dt
	, sale_amt
	, CASE WHEN 
		row_number() OVER(ORDER BY sale_dt) >= 3 
		THEN 
			ROUND(
				AVG(sale_amt) 
				OVER(
					ORDER BY sale_dt 
					ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
			, 1)
		ELSE NULL END 
		AS avg_sale_amt
FROM sales 
ORDER BY sale_dt;

--sale_dt  sale_amt  avg_sale_amt
--2021-06-01	230	NULL
--2021-06-02	235	NULL
--2021-06-03	231	232.0
--2021-06-04	244	236.7
--2021-06-05	202	225.7
--2021-06-06	260	235.3
--2021-06-07	240	234.0
--2021-06-08	235	245.0
--2021-06-09	239	238.0
--2021-06-10	242	238.7
--2021-06-11	244	241.7
--2021-06-12	241	242.3
--2021-06-13	246	243.7
--2021-06-14	247	244.7
--2021-06-15	249	247.3
--2021-06-16	245	247.0
--2021-06-17	242	245.3
--2021-06-18	246	244.3
--2021-06-19	245	244.3
--2021-06-20	190	227.0
--2021-06-21	230	221.7
--2021-06-22	235	218.3
--2021-06-23	231	232.0
--2021-06-24	238	234.7
--2021-06-25	241	236.7
--2021-06-26	245	241.3
--2021-06-27	242	242.7
--2021-06-28	243	243.3
--2021-06-29	240	241.7
--2021-06-30	238	240.3

 

 

 

(2-2) LAG(), OVER() window function을 사용하여 단순이동평균 계산하고, 이동평균계산 날짜 모자라면 NULL 처리하는 방법

 

아래 LAG() 함수를 사용한 방법은 이렇게도 가능하다는 예시를 보여준 것이구요, 위의 (2-1) 과 비교했을 때 'x일 단순이동평균'에서 'x일'이 숫자가 커질 경우 수작업으로 LAG() 함수를 'x일' 날짜만큼 모두 써줘야 하는 수고를 해줘야 하고, 그 와중에 휴먼 에러가 개입될 여지도 있어서 아무래도 위의 (2-1) 방법이 더 나아보입니다. 

 

-- Calculating a Simple Moving Average using LAG() Window Function

SELECT 
	sale_dt
	, sale_amt
	, ROUND(
			(sale_amt::NUMERIC 
				+ LAG(sale_amt::NUMERIC, 1) OVER(ORDER BY sale_dt) 
				+ LAG(sale_amt::NUMERIC, 2) OVER(ORDER BY sale_dt)
			)/3
			, 1) AS avg_sale_amt
FROM sales 
ORDER BY sale_dt;

--sale_dt  sale_amt  avg_sale_amt
--2021-06-01	230	NULL
--2021-06-02	235	NULL
--2021-06-03	231	232.0
--2021-06-04	244	236.7
--2021-06-05	202	225.7
--2021-06-06	260	235.3
--2021-06-07	240	234.0
--2021-06-08	235	245.0
--2021-06-09	239	238.0
--2021-06-10	242	238.7
--2021-06-11	244	241.7
--2021-06-12	241	242.3
--2021-06-13	246	243.7
--2021-06-14	247	244.7
--2021-06-15	249	247.3
--2021-06-16	245	247.0
--2021-06-17	242	245.3
--2021-06-18	246	244.3
--2021-06-19	245	244.3
--2021-06-20	190	227.0
--2021-06-21	230	221.7
--2021-06-22	235	218.3
--2021-06-23	231	232.0
--2021-06-24	238	234.7
--2021-06-25	241	236.7
--2021-06-26	245	241.3
--2021-06-27	242	242.7
--2021-06-28	243	243.3
--2021-06-29	240	241.7
--2021-06-30	238	240.3

 

 

 

(3) 누적 단순이동평균 계산하기 (Calculating a Cumulative Simpe Moving Average)

 

처음 시작하는 날짜부터 해서 누적으로 단순이동 평균 (Cumulative Moving Average) 을 계산하고 싶을 때는 아래처럼 AVG(sale_amt) OVER(ORDER BY sale_dt ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 처럼 window 범위를 처음부터 현재까지로 설정해주면 됩니다. 

 

아래 예에서 '2021-06-05'일까지의 누적 단순이동평균 값은 아래와 같이 계산되었습니다. 

 

Cumulative simple moving average('2021-06-05') = (230 + 235 + 231 + 244 + 202) / 5 = 228.4

 

-- Calculating a Cumulative Moving Average
SELECT 
	sale_dt
	, sale_amt
	, ROUND(
		AVG(sale_amt) 
		OVER(
			ORDER BY sale_dt 
			ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
			, 1) AS avg_cum_sale_amt
FROM sales 
ORDER BY sale_dt;

--sale_dt  sale_amt  avg_cum_sale_amt
--2021-06-01	230	230.0
--2021-06-02	235	232.5
--2021-06-03	231	232.0
--2021-06-04	244	235.0
--2021-06-05	202	228.4
--2021-06-06	260	233.7
--2021-06-07	240	234.6
--2021-06-08	235	234.6
--2021-06-09	239	235.1
--2021-06-10	242	235.8
--2021-06-11	244	236.5
--2021-06-12	241	236.9
--2021-06-13	246	237.6
--2021-06-14	247	238.3
--2021-06-15	249	239.0
--2021-06-16	245	239.4
--2021-06-17	242	239.5
--2021-06-18	246	239.9
--2021-06-19	245	240.2
--2021-06-20	190	237.7
--2021-06-21	230	237.3
--2021-06-22	235	237.2
--2021-06-23	231	236.9
--2021-06-24	238	237.0
--2021-06-25	241	237.1
--2021-06-26	245	237.4
--2021-06-27	242	237.6
--2021-06-28	243	237.8
--2021-06-29	240	237.9
--2021-06-30	238	237.9

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요. :-)

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 PostgreSQL, Greenplum에서 두 개의 SELECT 문 결과에 대한 합집합(UNION, UNION ALL), 교집합(INTERSECT), 차집합(EXCEPT) 에 대해서 알아보았습니다. (참고 ==> https://rfriend.tistory.com/659 )

 

이번 포스팅에서는 Sub-Query의 결과를 WHERE 문에서 비교 조건으로 하여 사용할 수 있는 한정 술어 연산자로서 ANY, SOME, ALL, EXISTS 연산자(operator)에 대해서 알아보겠습니다. 

 

 

1. ANY, SOME 연산자

2. ALL 연산자

3. EXISTS, NOT EXISTS 연산자

 

 

 

[ Sub-Query 결과를 비교 조건으로 사용하는 한정 술어 ANY/ SOME vs. ALL 비교 ]

PostgreSQL, Greenplum, 한정술어, any, some, all

 

 

먼저, 예제로 사용할 간단한 테이블 두 개를 만들어보겠습니다. 'cust_master' 테이블은 id, age, gender 의 세 개 칼럼으로 구성되어 있고, cust_amt  테이블은 id, amt 의 두 개 칼럼으로 구성되어 있으며, 두 테이블은 id 를 공통으로 가지고 있어 서로 연결이 가능합니다. (** 이번 포스팅에서는 JOIN 은 사용하지 않고, 대신 한정 술어를 사용해서 JOIN 결과와 유사한 결과를 얻어보겠습니다.)

 

-----------------------------------------------------------------------------------
-- 한정술어: ANY/ SOME, ALL, EXISTS, NOT EXISTS operators
-----------------------------------------------------------------------------------

-- creating sample tables 
DROP TABLE IF EXISTS cust_master;
CREATE TABLE cust_master (
	id int
	, age int 
	, gender text
) DISTRIBUTED RANDOMLY;

INSERT INTO cust_master VALUES 
(1, 45, 'M')
, (2, 34, 'F')
, (3, 30, 'F')
, (4, 28, 'M')
, (5, 59, 'M')
;

DROP TABLE IF EXISTS cust_amt;
CREATE TABLE cust_amt (
	id int
	, amt int
) DISTRIBUTED RANDOMLY;

INSERT INTO cust_amt VALUES 
(1, 500)
, (2, 200)
, (3, 750)
, (8, 900)
, (9, 350)
;

 

 

 

(1) ANY, SOME 연산자

 

ANY 한정술어 연산자는 Sub-Query 의 결과 값들 중에서 어떤 값이라도 =, <>, !=, <, <=, >, >= 등의 비교 연산자의 조건을 만족하면 TRUE 를 반환하며, 그렇지 않은 경우 FALSE 를 반환합니다.  SOME 한정 술어 연산자는 ANY 연산자와 동일한 기능을 수행합니다. 

Sub-Query 는 반드시 1개의 칼럼만 반환해야 합니다.  

 

아래의 예에서는 cust_amt 테이블에서 amt > 300 인 조건을 만족하는 id 와 동일한('=') id 를 가진 값을 cust_master 테이블에서 SELECT 해본 것입니다. (JOIN 문을 사용해도 됩니다만, ANY 연산자를 사용해서 아래처럼도 가능합니다. PostgreSQL이 내부적으로 query optimization을 해서 JOIN 문을 쓰던 ANY/ SOME 연산자를 쓰던 성능은 비슷합니다.)

 

WHERE 조건문에 IN 연산자를 사용할 경우에는 ANY/SOME 연산자에서 같이 사용했던 비교 연산자 ('=') 가 없는 차이점이 있습니다. 

 

WHERE 조건절에서 ANY, SOME 연산자에 비교연산자(=, <>, !=, <, <=, >, >=) 가 같이 사용되었을 경우의 의미는 포스팅 초반의 표를 참고하세요. 

 

--------------------------------
-- ANY, SOME operator
--------------------------------

-- ANY operator compares a value to a set of values returned by a subquery.
--The ANY operator must be preceded by one of the following comparison operator =, <=, >, <, > and <>
--The ANY operator returns true if any value of the subquery meets the condition, otherwise, it returns false.

SELECT id
FROM cust_amt
WHERE amt > 300
ORDER BY id;

--id
--1
--3
--8
--9


-- ANY OPERATOR

SELECT * 
FROM cust_master 
WHERE id = ANY (
	SELECT id
	FROM cust_amt
	WHERE amt > 300
);

--id  age  gender
--1	45	M
--3	30	F


-- SOME OPERATOR

SELECT * 
FROM cust_master 
WHERE id = SOME (
	SELECT id
	FROM cust_amt
	WHERE amt > 300
);


-- IN

SELECT * 
FROM cust_master 
WHERE id IN (
	SELECT id
	FROM cust_amt
	WHERE amt > 300
);

 

 

 

(2) ALL 연산자

 

ALL 한정술어 연산자는 Sub-Query의 결과의 '모든 값과 비교' 하여 '모든 값이 조건을 만족하면 TRUE, 그렇지 않으면 FALSE'를 반환합니다. 

 

WHERE 조건절에서 ALL 연산자에 비교연산자(=, <>, !=, <, <=, >, >=) 가 같이 사용되었을 경우의 의미는 포스팅 초반의 표를 참고하세요.  가령  아래의 예에서 "WHERE age > ALL (sub-query)" 는 "WHERE age > sub-query의 MAX" 값과 같은 의미입니다.  아래의 예에서는 Sub-Query의 avg_age 가 32, 44 이므로 이중에서 MAX 값인 44보다 age가 큰 값을 cust_master 테이블에서 조회를 하겠군요. 

 

---------------------------
-- the ALL operator
---------------------------

-- the PostgreSQL ALL operator compares a value with a list of values returned by a subquery.

SELECT gender, avg(age) AS avg_age 
FROM cust_master 
GROUP BY gender;

--gender  avg_age
--F	32.0000000000000000
--M	44.0000000000000000


SELECT * 
FROM cust_master 
WHERE age > ALL (
	SELECT avg(age) AS avg_age 
	FROM cust_master 
	GROUP BY gender
);

--id  age  gender
--1	45	M
--5	59	M

 

 

 

(3) EXISTS, NOT EXISTS 연산자

 

EXISTS 연산자는 Sub-Query의 결과에서 값이 존재하는지를 평가하는 블리언 연산자입니다. 만약 Sub-Query의 결과에 단 1개의 행이라도 값이 존재하다면 EXISTS 연산자의 결과는 TRUE 가 되며, Sub-Query의 결과가 한 개의 행도 존재하지 않는다면 FALSE 가 됩니다. 

 

아래의 예에서는 cust_master 테이블과 cust_amt 의 두 개 테이블을 같이 사용해서, cust_amt 테이블의 amt > 400 인 조건을 만족하고 cust_master 와 cust_amt 테이블에서 id 가 서로 같은 값이 존재(EXISTS) 하는 cust_master 의 모든 칼럼 값을 가져온 것입니다. (JOIN 문을 사용하지 않고도 EXISTS 문을 사용해서 아래처럼 쓸 수도 있답니다. 성능은 비슷.)

 

NOT EXISTS 연산자는 EXISTS 연산자를 사용했을 때와 정반대의 값을 반환합니다.(TRUE, FALSE 가 서로 정반대임). 

 

---------------------------
-- EXISTS operator
---------------------------
-- The EXISTS operator is a boolean operator that tests for existence of rows in a subquery.
-- If the subquery returns at least one row, the result of EXISTS is TRUE. 
-- In case the subquery returns no row, the result is of EXISTS is FALSE.

SELECT *
FROM cust_master AS m 
WHERE EXISTS (
	SELECT 1
	FROM cust_amt AS a 
	WHERE m.id = a.id 
		AND a.amt > 400
	) 
ORDER BY id;

--id  age  gender
--1	45	M
--3	30	F


----------------------------------
-- NOT EXISTS operator
----------------------------------

-- in case the subquery returns no row, the result is of NOT EXISTS is TRUE
SELECT *
FROM cust_master AS m 
WHERE NOT EXISTS (
	SELECT 1
	FROM cust_amt AS a 
	WHERE m.id = a.id 
		AND a.amt > 400
	) 
ORDER BY id;

--id  age  gender
--2	34	F
--4	28	M
--5	59	M

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요. :-) 

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 여러개 테이블에서 SELECT 문으로 가져온 결과들의 합집합을 구할 때 UNION 은 중복 확인 및 처리를 하고 UNION ALL 은 중복확인 없이 여러 테이블의 모든 값을 합친다는 차이점을 소개하였습니다. (참고 => https://rfriend.tistory.com/658 )

 

이번 포스팅에서는 PostgreSQL, Greenplum DB에서 SELECT 문의 결과끼리 합치고 빼는 집합 연산자로서 

 

(1) 합집합 UNION

(2) 교집합 INTERSECT 

(3) 차집합 EXCEPT 

(4) 필요조건: 칼럼의 개수가 같아야 하고, 모든 칼럼의 데이터 유형이 동일해야 함. 

 

에 대해서 알아보겠습니다. 

 

벤다이어 그램으로 PostgreSQL, Greenplum DB에서 SELECT 문 결과에 대한 합집합 UNION, 교집합 INTERSECT, 차집합 EXCEPT 했을 때의 결과를 도식화하면 아래와 같습니다. 

 

PostgreSQL, Greenplum DB, UNION, INTERSECT, EXCEPT

 

예제로 사용할 간단한 테이블 두 개를 만들어보겠습니다.  'x1', 'x2' 의 두 개 칼럼이 있고, 두 개 모두 동일하게 'integer' 데이터 유형이어서 테이블 집합연산자인 합집합, 교집합, 차집합의 예제로 사용할 수 있습니다. 

 

-- creating sample tables 

-- Sample Table 1
DROP TABLE IF EXISTS sample_1;
CREATE TABLE sample_1 (x1 int, x2 int) 
DISTRIBUTED randomly;

INSERT INTO sample_1 VALUES (1, 11), (2, 12), (3, 13), (4, 14), (5, 15);
SELECT * FROM sample_1 ORDER BY x1;
--x1    x2
--1	11
--2	12
--3	13
--4	14
--5	15


-- Sample Table 2
DROP TABLE IF EXISTS sample_2;
CREATE TABLE sample_2 (x1 int, x2 int) 
DISTRIBUTED randomly;

INSERT INTO sample_2 VALUES (4, 14), (5, 15), (6, 16), (7, 17), (8, 18);
SELECT * FROM sample_2 ORDER BY x1;

--x1    x2
--4	14
--5	15
--6	16
--7	17
--8	18

 

 

아래의SELECT 문 결과에 대한 UNION, INTERSECT, EXCEPT query 구문은 별도의 추가 설명이 필요 없을 정도로 쉬운 내용이므로 예제 집합연산자의 결과만 제시하는 것으로 설명을 갈음하겠습니다. 

 

 

(1) 합집합 UNION

-- UNION
SELECT * FROM sample_1 
UNION 
SELECT * FROM sample_2 
ORDER BY x1;

--x1    x2
--1	11
--2	12
--3	13
--4	14
--5	15
--6	16
--7	17
--8	18

 

 

 

(2) 교집합 INTERSECT

-- INTERSECT
SELECT * FROM sample_1 
INTERSECT 
SELECT * FROM sample_2
ORDER BY x1;

--x1    x2
--4	14
--5	15

 

 

 

(3) 차집합  EXCEPT

 

두 테이블의 차집합 EXCEPT 는 먼저 SELECT 한 결과에서 나중에 SELECT 한 결과 중 중복되는 부분을 제외한 후의 나머지 결과를 반환합니다. 

참고로, Oracle, MySQL DB에서는 SELECT 문 결과에 대한 차집합은 MINUS 함수를 사용해서 구할 수 있습니다. 

 

-- EXCEPT
SELECT * FROM sample_1 
EXCEPT 
SELECT * FROM sample_2 
ORDER BY x1;

--x1    x2
--1	11
--2	12
--3	13

 

 

 

(4) 필요조건: 칼럼의 개수가 같아야 하고, 모든 칼럼의 데이터 유형이 동일해야 함. 

 

UNION, UNION ALL, INTERSECT, EXCEPT 의 집합연산자를 사용하려면 SELECT 문으로 불러온 두 테이블의 결과에서 칼럼의 개수가 서로 같아야 하고 또 모든 칼럼의 데이터 유형(Data Type)이 서로 동일해야만 합니다. 만약 칼럼의 데이터 유형이 서로 다르다면 아래와 같은 에러가 발생합니다. (아래 예에서는 'x2' 칼럼이 하나는 'integer', 또 하나는 'text' 로서 서로 다르기때문에 에러가 발생한 경우임)/ 

 

SQL Error [42804]: ERROR: UNION types integer and text cannot be matched

 

-- The data types of all corresponding columns must be compatible.

-- Sample Table 3
DROP TABLE IF EXISTS sample_3;
CREATE TABLE sample_3 (x1 int, x2 text) 
DISTRIBUTED randomly;

INSERT INTO sample_3 VALUES (10, 'a'), (20, 'b'), (30, 'c'), (40, 'd'), (50, 'f');
SELECT * FROM sample_3 ORDER BY x1;

--x1		y
--10	a
--20	b
--30	c
--40	d
--50	f

-- ERROR
SELECT * FROM sample_1 
INTERSECT 
SELECT * FROM sample_3;

--SQL Error [42804]: ERROR: UNION types integer and text cannot be matched

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!

 

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 PostgreSQL, Greenplum DB에서 JOIN 문을 사용하여 여러개의 테이블을 Key 값을 기준으로 왼쪽+오른쪽으로 연결하는 다양한 방법(INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL JOIN)을 소개하였습니다. (참고 ==> https://rfriend.tistory.com/657)

 

이번 포스팅에서는 PostgreSQL, Greenplum DB에서 UNION, UNION ALL 함수를 사용해서 여러개의 테이블을 위+아래로 합치는 방법을 소개하겠습니다.  이전의 JOIN 이 Key값 기준 연결/매칭의 개념이었다면 이번 포스팅의 UNION, UNION ALL은 합집합(union of sets) 의 개념이라고 볼 수 있습니다. 

 

(1) UNION : 중복값 제거 후 테이블을 위+아래로 합치기

(2) UNION ALL : 중복값 제거하지 않은 채 테이블을 위+아래로 합치기

(3) 전제조건: 합치려는 테이블 칼럼의 데이터 유형(data type)이 서로 같아야 함. 

 

 

UNION 은 두 테이블의 중복값을 제거한 후에 두 테이블을 위+아래로 합친 결과를 반환하는 반면에, UNION ALL 은 중복값 여부를 확인하지 않고 두 테이블의 모든 값을 위+아래로 합친 결과를 반환합니다. 

 

POSTGRESQL, GREENPLUM UNION, UNION ALL

 

 

UNION 의 경우 두 테이블의 값을 스캔 해서 두 테이블 간의 중복값 여부를 확인하는 중간 단계가 존재하기 때문에 두 테이블의 각 크기가 매우 큰 경우  UNION ALL 대비 상대적으로 연산 시간이 오래 걸립니다. 만약 위+아래로 합치려는 두 테이블의 값 간에 중복값이 없다거나 혹은 중복값 여부를 확인할 필요 없이 모두 합치기만 필요한 요건의 경우에는 UNION ALL 을 사용하는 것이 속도 면에서 유리합니다. 

 

 

간단한 예제 테이블들을 만들어서 예를 들어보겠습니다. 

 

-------------------------------------
-- UNION vs. UNION ALL
-------------------------------------

-- Sample Table 1
DROP TABLE IF EXISTS sample_1;
CREATE TABLE sample_1 (x1 int, x2 int) 
DISTRIBUTED randomly;

INSERT INTO sample_1 VALUES (1, 11), (2, 12), (3, 13), (4, 14), (5, 15);
SELECT * FROM sample_1 ORDER BY x1;
--x1 x2
--1	11
--2	12
--3	13
--4	14
--5	15


-- Sample Table 2
DROP TABLE IF EXISTS sample_2;
CREATE TABLE sample_2 (x1 int, x2 int) 
DISTRIBUTED randomly;

INSERT INTO sample_2 VALUES (4, 14), (5, 15), (6, 16), (7, 17), (8, 18);
SELECT * FROM sample_2 ORDER BY x1;

--x1 x2
--4	14
--5	15
--6	16
--7	17
--8	18

 

 

 

(1) UNION : 중복값 제거 후 테이블을 위+아래로 합치기

 

SELECT column1, column2, ... FROM table1 

UNION

SELECT column1, column2, ... FROM table2

와 같은 형식의 구문으로 두 테이블에서 중복값을 제거한 후에 위+아래로 합칠 수 있습니다. 

 

--------------------
-- (1) UNION
--------------------
SELECT * FROM sample_1 
UNION 
SELECT * FROM sample_2
ORDER BY x1;

--x1 x2
--1	11
--2	12
--3	13
--4	14
--5	15
--6	16
--7	17
--8	18

 

 

 

(2) UNION ALL : 중복값 제거하지 않은 채 테이블을 위+아래로 합치기

 

SELECT column1, column2, ... FROM table1 

UNION ALL

SELECT column1, column2, ... FROM table2

와 같은 형식의 구문으로 두 테이블에서 중복값을 제거하지 않은 상태에서 (중복값 체크 없음) 위+아래로 합칠 수 있습니다. 

 

-------------------------
-- (2) UNION ALL 
-------------------------
SELECT * FROM sample_1 
UNION  ALL 
SELECT * FROM sample_2
ORDER BY x1;

--x1 x2
--1	11
--2	12
--3	13
--4	14
--4	14 -- 중복
--5	15
--5	15 -- 중복
--6	16
--7	17
--8	18

 

 

 

(3) 전제조건: 합치려는 테이블 칼럼의 데이터 유형(data type)이 서로 같아야 함. 

 

만약 UNION, UNION ALL 로 합치려는 두 테이블의 칼럼의 데이터 유형(data type)이 서로 같지 않다면, (아래의 예에서는 sample_1 테이블의 x2 칼럼은 integer, sample_3 테이블의 x2 칼럼은 text로서 서로 다름), "ERROR: UNION types integer and text cannot be matched" 라는 에러 메시지가 발생합니다. 

 

-- (3) The data types of all corresponding columns must be compatible.

-- Sample Table 3
DROP TABLE IF EXISTS sample_3;
CREATE TABLE sample_3 (x1 int, x2 text) 
DISTRIBUTED randomly;

INSERT INTO sample_3 VALUES (10, 'a'), (20, 'b'), (30, 'c'), (40, 'd'), (50, 'f');
SELECT * FROM sample_3 ORDER BY x1;

--x1		y
--10	a
--20	b
--30	c
--40	d
--50	f

-- ERROR
SELECT * FROM sample_1 
UNION
SELECT * FROM sample_3;

--SQL Error [42804]: ERROR: UNION types integer and text cannot be matched
--  Position: 38

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요. :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 PostgreSQL, Greenplum Database에서 여러개의 테이블을 Key 값을 기준으로 JOIN 구문을 사용하여 연결하는 다양한 방법을 소개하겠습니다. 그리고 두 테이블 내 관측치 간의 모든 가능한 조합을 반환해주는 CROSS JOIN 에 대해서도 마지막에 소개하겠습니다. (DB 종류에 상관없이 join SQL query는 거의 비슷합니다.)

 

(1) INNER JOIN

(2) LEFT JOIN

(3) RIGHT JOIN

(4) FULL JOIN

(5) 3개 이상 복수개의 테이블을 JOIN 으로 연결하기

(6) CROSS JOIN

 

 

joining two tables in postgresql

 

 

먼저 예제로 사용한 간단한 2개의 테이블을 만들어보겠습니다. 두 테이블을 연결할 수 있는 공통의 Key값으로서 'id'라는 이름의 칼럼을 두 테이블이 모두 가지고 있습니다. 

 

'tbl1' 과 'tbl2'는 Key 'id'를 기준으로 id = [2, 3, 4] 가 서로 동일하게 존재하며, 'tbl1'의 id = [1]은 'tbl1'에만 존재하고, 'tbl2'의 id = [5] 는 'tbl2'에만 존재합니다. 각 JOIN 방법 별로 결과가 어떻게 달라지는지 유심히 살펴보시기 바랍니다. 

 

-- Creating two sample tables

-- sample table 1
DROP TABLE IF EXISTS tbl1;
CREATE TABLE tbl1 (
	id int
	, x text
) DISTRIBUTED RANDOMLY;

INSERT INTO tbl1 VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd');

SELECT * FROM tbl1 ORDER BY id;
--id  x
--1	a
--2	b
--3	c
--4   d


-- sample table 2
DROP TABLE IF EXISTS tbl2;
CREATE TABLE tbl2 (
	id int
	, y text
) DISTRIBUTED RANDOMLY;

INSERT INTO tbl2 VALUES (2, 'e'), (3, 'f'), (4, 'g'), (5, 'h');

SELECT * FROM tbl2 ORDER BY id;
--id  y
--2	e
--3	f
--4	g
--5	h

 

 

(1) INNER JOIN 

INNER JOIN 은 두 테이블의 Key 값을 기준으로 교집합에 해당하는 값들만 반환합니다. 두 테이블에서 Key 값이 겹치지 않는 값들은 제거되었습니다. 

 

--------------
-- INNER JOIN
--------------

SELECT a.id, a.x, b.y
FROM tbl1 AS a 
INNER JOIN tbl2 AS b 
ON a.id = b.id;
--id x y
--2	b	e
--3	c	f
--4	d	g

 

 

 

(2) LEFT OUTER JOIN

LEFT OUTER JOIN 은 왼쪽 테이블을 기준으로 Key값이 서로 같은 오른쪽 테이블의 값들을 왼쪽 테이블에 연결해줍니다. 아래의 예에서는 왼쪽의 'tbl1'의 값들은 100% 모두 있고, LEFT OUTER JOIN 으로 연결해준 오른쪽 'tbl2' 테이블의 경우 id = [5] 의 값이 제거된 채 id = [2, 3, 4] 에 해당하는 값들만 'tbl1'과 연결이 되었습니다. 그리고 왼쪽 'tbl1'에는 있지만 오른쪽 'tbl2'에는 없는 id = [1] 에 해당하는 값의 경우 y = [NULL] 값을 반환하였습니다. 

 

-------------------
-- LEFT OUTER JOIN
-------------------

SELECT a.id, x, y
FROM tbl1 AS a 
LEFT OUTER JOIN tbl2 AS b 
ON a.id = b.id;
--id x y
--1	a	[NULL]
--2	b	e
--3	c	f
--4	d	g

 

 

 

(3) RIGHT OUTER JOIN

RIGHT OUTER JOIN 은 LEFT OUTER JOIN 과 정반대라고 생각하면 이해하기 쉽습니다. 이번에는 오른쪽 테이블을 기준으로 Key 값이 같은 왼쪽 테이블의 값을 오른쪽 테이블에 연결해줍니다. 

아래 RIGHT OUTER JOIN 예에서는 오른쪽 테이블은 'tbl2'는 100% 모두 있고, 왼쪽 테이블 'tbl1'의 경우 'tbl2'와 Key 값이 동일한 id = [2, 3, 4] 에 해당하는 값들만 'tbl2'에 연결이 되었습니다. 'tbl2'에만 존재하고 'tbl1'에는 없는 id = [5] 의 경우 'tbl1'의 'x' 칼럼 값은 [NULL] 값이 됩니다. 

 

--------------------
-- RIGHT OUTER JOIN
--------------------

SELECT a.id, x, y
FROM tbl1 AS a 
RIGHT OUTER JOIN tbl2 AS b 
ON a.id = b.id;
--id x y
--2	b	e
--3	c	f
--4	d	g
--5 [NULL]	h

 

 

 

(4) FULL JOIN

FULL JOIN은 양쪽 테이블 모두를 기준으로 Key 값이 같은 값들을 연결시켜 줍니다. 이때 한쪽 테이블에만 Key 값이 존재할 경우 다른쪽 테이블의 칼럼 값에는 [NULL] 값을 반환합니다. 

제일 위에 있는 도식화 그림을 참고하시면 위의 INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL JOIN에 대해서 좀더 이해하기 쉬울 거예요. 

 

---------------
-- FULL JOIN
---------------

SELECT a.id, x, y
FROM tbl1 AS a 
FULL JOIN tbl2 AS b
ON a.id = b.id;
--id x y
--1	a	[NULL]
--2	b	e
--3	c	f
--4	d	g
--5 [NULL] h

 

 

 

(5) 3개 이상의 복수개의 테이블을 JOIN 으로 연결하기

 

위의 (1)~(4) 는 2개의 테이블을 Key 값을 기준으로 JOIN 문으로 연결한 것이었습니다. 만약 3개 이상의 복수개의 테이블을 JOIN 으로 연결하고자 한다면 아래의 예처럼 JOIN 문과 연결 Key 값을 ON 으로 이어서 써주면 됩니다.

아래의 예는 'tbl1' 테이블을 기준으로 'tbl2', 'tbl3'를 'id' Key를 기준으로 LEFT OUTER JOIN 한 것입니다. 

 

--------------------------------------------------------
-- LEFT OUTER JOIN with Multiple Tables
--------------------------------------------------------

-- creating the 3rd table
DROP TABLE IF EXISTS tbl3;
CREATE TABLE tbl3 (
	id int
	, z text
) DISTRIBUTED RANDOMLY;

INSERT INTO tbl2 VALUES (2, 'i'), (4, 'j'), (6, 'k'), (8, 'l');
SELECT * FROM tbl3 ORDER BY id;
--id  z
--2	i
--4	j
--6	k
--7	l


-- LEFT OUTER JOIN with 3 tables
SELECT a.id, x, y
FROM tbl1 AS a 
LEFT OUTER JOIN tbl2 AS b 
	ON a.id = b.id
LEFT OUTER JOIN tbl3 AS c	
	ON a.id = c.id
ORDER BY a.id;
--id x y z
--1	a	[NULL] [NULL]
--2	b	e i
--3	c	f [NULL]
--4	d	g j

 

 

 

(6) CROSS JOIN

위의 (1)~(5)까지의 JOIN은 두 테이블에 동일하게 존재하는 Key값을 기준으로 두 테이블을 연결하여 주었다면, 이제 CROSS JOIN 은 두 테이블의 모든 값들 간의 조합을 반환하며, 이때 Key 값은 필요없습니다. 가령 왼쪽 테이블에 m 개의 행이 있고, 오른쪽 테이블에 n 개의 행이 있다면 두 테이블의 CROSS JOIN은 m * n 개의 조합(combination)을 반환합니다. 

 

실수로 행의 개수가 엄청나게 많은 두 테이블을 CROSS JOIN 하게 될 경우 시간도 오래 걸리고 자칫 memory full 나서 DB가 다운되는 경우도 있습니다.  따라서 CROSS JOIN 을 할 때는 지금 하려는 작업이 CROSS JOIN 요건이 맞는 것인지 꼭 한번 더 확인이 필요하며, 소요 시간이나 메모리가 여력이 되는지에 대해서도 먼저 가늠해볼 필요가 있습니다. 

 

----------------
-- CROSS JOIN
----------------

SELECT a.id AS id_a, a.x, b.id AS id_b, b.y
FROM tbl1 AS a 
CROSS JOIN tbl2 AS b
ORDER BY a.id, b.id;
--id_a x id_b y
--1	a	2	e
--1	a	3	f
--1	a	4	g
--1	a	5	h
--2	b	2	e
--2	b	3	f
--2	b	4	g
--2	b	5	h
--3	c	2	e
--3	c	3	f
--3	c	4	g
--3	c	5	h
--4	d	2	e
--4	d	3	f
--4	d	4	g
--4	d	5	h

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

매년 5 이맘 노무현 대통령님이 생각납니다

서거하신지 벌써 12년이 흘렀는데도 날을 생각하면 가슴이 먹먹해 집니다.  

 

노무현 대통령님을 생각하면서 지난달에 읽었던 "대통령의 글쓰기" (강원국 지음,  에이치 출판사) 책에 대해 남겨봅니다

 

책은 김대중 대통령의 '국민의 정부' 노무현 대통령의 '참여 정부' 시절에 8년간 연설비서관을 했던 강원국님이 대통령의 연설문을 작성하면서 대통령으로부터 보고 듣고 배운, 사람을 움직이는 글쓰기에 대한 비법을 정리한 책입니다.

 

책의 목차 중에 보면 "왕관을 쓰려는 자, 글을 써라" 라는 제목이 있답니다. 저보고 글을 잘 쓰고, 연설도 잘 해서 왕관을 쓴 우리나라의 최고의 전직 대통령 두 분을 꼽으라면 단연 고 김대중 대통령, 노무현 대통령이겠지요. (현직 포함 세 분을 꼽으라면 현 문재인 대통령님 추가요! ^__^)

 

한 명의 대통령을 모시기도 예사롭지 않은데, 두 명의 대통령의 연설비서관을 지냈고, 게다가 그 두 명의 대통령이 우리나라에서 가장 사람들 마음을 움직이는 명연설로 유명하신 고 김대중, 노무현 대통령님라면야 강원국씨는 "대통령의 글쓰기"라는 제목의 책을 쓸 자격이 있다고 하겠습니다. 암요! 

 

글쓰기에 관해 제대로 배우고 싶은 분이라면 "대통령의 글쓰기" (강원국 지음) 책 일독을 권합니다. 유익하고, 재미있기까지 합니다. 

 

대통령의 글쓰기

 

 

강원국 저자는 고 김대중 대통령과 노무현 대통령의 연설문에 대해 비슷한 점도 많지만 차이점 또한 많다고 하였습니다. 그러면서 김대중 대통령과 노무현 대통령의 연설문 특징을 비교하기를 "예의 중시 vs. 교감 중시" 라고 하였습니다. 단 하나의 가장 훌륭한 연설문 스타일이란 없으며, 연설을 하는 사람의 색깔이 배어있는 연설문이 자연스럽고 좋은 연설문이라고 해석할 수도 있을 것 같습니다. 

 

고 노무현 대통령님께서 요리에 빗대어서 말씀해주시는 좋은 글쓰기를 하는 방법을 아래에 소개해봅니다. 하나 하나가 요리랑 연상이 되어서 이해하기 쉽고 수긍이 갑니다. :-)

 

"노무현 대통령은 언젠가 글쓰기를 음식에 비유해서 얘기한 적도 있다. 

1. 요리사는 자신감이 있어야 해. 너무 욕심부려서도 안 되겠지만. 글 쓰는 사람도 마찬가지야. 
2. 맛있는 음식을 만들려면 무엇보다 재료가 좋아야 하지. 싱싱하고 색다르고 풍성할수록 좋지. 글쓰기도 재료가 좋아야 해. 
3. 먹지도 않는 움식이 상만 채우지 않도록, 군더더기는 다 빼도록 하게. 
4. 글의 시작은 애피타이저, 글의 끝은 디저트에 해당하지. 이게 중요해. 
5. 핵심 요리는 앞에 나와야 해. 두괄식으로 써야 한단 말이지. 다른 요리로 미리 배를 불려 놓으면 정작 메인요리는 맛있게 못 먹는 법이거든. 
6. 메인요리는 일품요리가 되어야 해. 해장국이면 해장국, 삼계탕이면 삼계탕. 한정식같이 이것저것 나오는 게 아니라 하나의 메시지에 집중해서 써야 하지. 
7. 양념이 많이 들어가면 느끼하잖아. 과다한 수식이나 현학적 표현은 피하는 게 좋지. 
8. 음식 서빙에도 순서가 있다네. 글도 오락가락, 중구난방으로 쓰면 안돼. 다 순서가 있지. 
9. 음식 먹으러 갈 때 식당 분위기 파악이 필수이듯이, 그 글의 대상에 대해 잘 파악해야 해. 사람들이 일식당인 줄 알고 갔는데 짜장면이 나오면 얼마나 황당하겠어. 
10. 요리마다 다른 요리법이 있듯 글마다 다른 전개방식이 있는 법이지. 
11. 요리사가 장식이나 기교로 승부하려고 하면 곤란하네. 글도 진심이 담긴 내용으로 승부해야 해. 
12. 간이 맞는지 보는 게 글로 치면 퇴고의 과정이라 할 수 있지. 
13. 어머니가 해주는 집밥이 최고지 않나? 글도 그렇게 편안하고 자연스러워야 해. "

- 대통령의 글쓰기 (강원국 지음, 에이치 출산사), 22~23페이지 -

 

 

책의 중간 중간에 나오는 이 책의 글쓴이 강원국님의 '이야기' 코너도 재미있게 봤습니다. 특히 "이야기 둘 : 청와대 생활과 과민성대장증후군" 코너는 눈물을 흘리면서 (너무 슬퍼서가 아니라 너무 웃겨서... ㅠ_ㅠ) 읽었습니다.  웃으면 안되는 상황인데 그 모습이 상상이 되다보니 안 웃을 수가 없더라구요. 남자 소변기가 어떻게 쓰일 수 있는지 한번 보시지요. ㅋㅋ

 

"긴장의 연속이었던 8년간의 청와대 생활은 나에게 과민성대장증후군이란 달갑지 않은 선물을 선사해주기도 했다. 2002년 국장 진급 임명장 받는 날이었다. 청와대 행사라는 게 아무리 사소한 것이라도 문제가 된다. 지각을 하거나 예행연습에 불참하는 일은 있을 수 없다. 과천에서 경복궁역까지 지하철을 타고 다니던 나는 그 날도 넉넉하게 집을 나섰다. 긴장해서인지 화장실이 급해 신용산역에서 내렸다. 아니나 다를까 빈칸이 없다. 줄을 서서 기다렸다. 도저히 안 돼 칸칸마다 두드리며 호수했다. 그러나 야속하게도 물 내리는 소리는 들리지 않았다. 결심했다. 대통령 임명장을 받는 날, 사고가 나선 절대 안 됐다. 바지를 내리고 급한 대로 소변기에 앉았다. 화장실에 들어오는 사람들과 눈이 마주쳤다. 사람들이 뭔지 모르지만 귀신에 홀린 듯 순간적으로 엄청난 혼돈을 느끼며, 못 들어올 데 들어온 사람처럼 화들짝 놀라 나갔다. 난 그때 처음 알았다. 남자 소변기가 이렇게도 쓰일 수 있다는 것을."

- 대통령의 글쓰기, 64페이지

 

 

사람의 마음을 움직이는 연설문이란 어떤 것인가를 생각하다보면, 결국에는 "누가", "어떤 마음을 가지고" 그 연설을 하는가가 연설문의 "내용"이나 "형식" 못지않게 중요하다고, 아니 더 중요하다고 생각합니다.  고 김대중 대통령님과 노무현 대통령님은 소수의 힘있고 부유한 자들의 편이 아니라 다수의 서민들이 함께 더불어 잘 사는 세상을 꿈꾸고, 이를 조금씩이나마 일구어나가기 위해 거쳐온 생애가 가지는 힘, 무게, 진정성에서 우러나오는 한마디, 한마디가 국민들에게 큰 울림이 되는 것이리라 믿습니다. 

 

 

마지막으로 존경하는 고 김대중 대통령님과 사랑하는 고 노무현 대통령님의 사진으로 책에 대한 소개 글을 마칠까 합니다. 

 

두 분이 많이 그립습니다. 

 

* 사진 출처: http://www.ohmynews.com

 

728x90
반응형
Posted by Rfriend
,

저는 주로 PostgreSQL, Greenplum database를 사용해서 작업을 할 때 DBeaver SQL IDE 를 사용하곤 합니다. 전에는 PGAdmin도 많이 썼는데요, 요즘에는 DBeaver를 주로 사용하네요. 

 

고객사에 가서 프로젝트를 하게 되면 노트북에 분석에 사용하는 툴들을 설치해서 들어가게 되고, 프로젝트 종료 후에는 고객사의 보안 규정에 따라 노트북을 반출할 때는 포맷을 하게 됩니다. 이렇다 보니 포맷 후에 다시 처음부터 분석에 필요한 툴들을 새로 설치하고, 설정도 매번 새로 해줘야합니다. 

 

이럴 때마다 매번 DBeaver 에서 설정 확인하고 조정해주는 것들에 대한 소소한 팁들 정리해보았습니다. 

 

1. DBeaver 테마 설정

2. DBeaver 폰트 유형 및 폰트 크기 설정

3. DBeaver 대문자로 자동 바꿔주기 설정

4. DBeaver 행번호 표시 설정

 

 

 

1. DBeaver 테마 설정

 

DBeaver 는 편집기의 전체 Theme으로 Classic, Dark, Light, System 의 4가지를 제공합니다.  아래의 경로대로 찾아가서 각각 클릭해서 '적용'해본 후에, 본인이 가장 좋아하는 Theme으로 설정하면 됩니다.  (저는 'Light' (default) Theme이 가장 깔끔해서 기본 설정 그대로 사용하곤 합니다.)

 

 

윈도우(W) > User Interface > 모양 > Enable theming >  테마(T) > [Classic, Dark, Light, System] > Apply and Close

 

DBeaver : 윈도우(W) > 설정

 

 

DBeaver : User Interface > 모양 > 테마(T)

 

 

 

2. DBeaver 글꼴 유형(폰트 타입) 및 글꼴 크기(폰트 사이즈) 설정

 

DBeaver 새로 설치하고 나면, 매번 폰트 유형은 Arial로, 폰트 크기를 14로 키워서 사용하는 편입니다. 그런데 텍스트 글꼴 설정하는 메뉴 위치 찾기가 쉽지 않습니다. 그래서 자주 구글링을 하는 편이지요. 나중에 제가 폰트 설정하는 메뉴가 어디에 숨어있는지 참고하려고 이번 포스팅 쓰는 거랍니다. ^^;

 

윈도우(W) > User Interface > 모양 > 색상 및 글꼴 > 기본 > 텍스트 글꼴 > 편집 > Collection, Family, Typeface, Size 설정 > Apply and Close

 

DBeaver: User Interface > Font setting > Edit

 

 

Collection 에서  "All Fonts" 선택하고, Family에서 원하는 글꼴(폰트)를 선택합니다. 저는 무난하게 "Arial"을 선택했습니다. Typeface 에서 [일반체, 이탤릭체, 볼드체, 볼드 이탤릭체] 중에서 선택을 하고, 글꼴 크기 (폰트 사이즈)를 설정해주면 됩니다.  이렇게 설정을 다 해주고 나면 앞의 화면의 제일 밑에 있는 "Apply and Close"를 클릭해주면 설정이 적용됩니다. 

 

 

 

 

3. DBeaver SQL Keyword 를 대문자로 자동으로 바꿔주기 설정

 

개인마다 각자 취향이 있겠습니다만, 저는 SQL Query 구문의 가독성을 좋게 하는데 도움이 되기 때문에 SQL Keyword 문을 대문자로 하고, 그 외 테이블이나 칼럼 이름은 소문자로 구분해서 사용하곤 합니다. 그런데 이렇게 대문자와 소문자를 매번 SQL query문을 쓸 때마다 수동으로 키보드에서 CapsLock 키를 눌러가면서 바꿔줘야 한다면 상당히 번거롭습니다.  이때 SQL Keyword 구문을 자동으로 대문자로 바꿔주는 설정이 있다면 정말 편하겠지요?!

 

윈도우(W) > 편집기 > SQL 편집기 > SQL 포맷 > Keyword case > [Default, Upper, Lower, Mixed] > Apply and Close

 

DBeaver: Editor > SQL Editor > SQL format > Keyword case > Upper

 

 

 

4. DBeaver 행번호 표시 설정

 

DBeaver 의  SQL 편집기에서 행 번호를 표시하면 SQL Query가 길어졌을 때 디버깅하기에 편리하기 때문에 행번호 표시를 활성화해서 사용하곤 합니다. 

 

윈도우(W) > 편집기 > 문서 편집기 > 행 번호 표시(B) check > Apply and Close

 

DBeaver : Editor > Document Editor > Line Number

 

"행 번호 표시"를 활성화하면 아래처럼 SQL 편집기의  Script 창의 왼쪽에 "행 번호 (line nubmer)"가 표시됩니다. 

DBeaver : Line Number

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! :-)

 

728x90
반응형
Posted by Rfriend
,

-- 2021년 5월 9일 작성 --

 

 

부엌 2탄은 잠시 미루고 오늘은 안방이요. 내일 광파오븐자리 서랍장이 들어오면 좀더 예뻐진 상태로 부엌 찍어볼까해서요~..

 

안방을 올린적이 있던가요? 방들은 좀더 프라이빗 해서 사진 많이 올리기가 살짝 망설여져요. 그래도 자랑하고픈 맘도 참을 수가 없네요.. ㅎㅎ 아, 이 딜레마~~~

 

 

안방이 사실 좀 작아요... 나만의 작은 책상하나라도 구석에 두고팠지만 자리가 안되더라고요.. ㅠ.ㅠ 그냥 최대한 깔끔하게 가기로 했어요. 나만의 공간은 거실 아님 안방베란다를 노리고 있어요..

 

인테리어의 시작은... 음... 버리기 같아요. 최대한 비워야 공간이 살고 가구 갯수가 덜 늘어나요.. 많이 안 입는 옷가지를 많이 버렸네요.

 

암튼 붙박이장 끝내주게 깔끔하죠? 인쇼 디테일은 정말~~~ 보고 또봐도 감탄이예요. 서라운딩 없이 벽에 딱!맞습니다. 살면서 맨날 좋아해주고 있어요.

울팀장님이.. 이 붙박이장 들어온날, 제일 오른쪽 장 바로 빠꾸~시키시더라고요. 안쪽에 모퉁이가 조금 깨졌는데 기사분이 그냥 안보인다고 설치한거 찾아내시고는... 아마 얘기 안하셨음 전 몰랐을거예요.

 

 

붙박이장 사진입니다. 안방도 목공을 해서 수평을 정확히 맞췄어요. 바닥에 세탁기 배관이 지나가기도 하고요.. 하단엔 LED 조명도 넣어주셨어요. 예쁜데 살짝 밝아서... 무드가 좀 덜 잡혀요 ㅎㅎ 커텐박스 LED 만으로도 충분할 듯요.

붙박이장 손잡이 없습니다. 누르면 문이 튀어나와요. 손잡이 같은건 암튼 인쇼랑 웬수인듯.. 전 첨엔 있는게 편할거 같았는데, 이젠, ㅎㅎ 저도 눈이 높아져서 말이죠. 손잡이가 뭐예요? ㅋㅋ

 

 

 

오른쪽 큰 장은 스타일러 숨겼어요. 이거 싸이즈 때문에 인쇼팀장님이 다른 붙박이장 도어 사이즈도 크게 하고 5칸으로 만들자는 걸 제가 그냥 예전 쓰던 옷장 배치 생각해서 6칸으로 문 좁게 만들어 달라고 했어요.

근데 팀장님 말 들을껄~~ ㅋㅋ

암튼 울 팀장님 센스는 인테리어 기간 내내 확실히 믿을만 했어요. 게다가 자잘한 제 요구사항 화 한번 안내고 최대한 들어주고 반영해주고 좋은것들 소개해주고...

김동현 팀장님~~ 말 나온김에, 엄청 고마워요~~

나중에 우리집 이사하면 또 해주세요 꼭~! (그래도 집이 아까워서 이사는 못갈듯도 해요..ㅋ)

글고 썬구리사장님~ 울 동현팀장님 월급 마니마니 주세용 ~ 엄청 고생하던데, 일 끝나면 휴가도 보내주시도... 안그럼 사장님 실명이랑 전화번호 나온 명함을 확~ 인터넷에 올려버리는 수가 있어요~ ㅋㅋ

 

 

 

 

 

 

요렇게 찍으면 유난히 방이 작아보여요.. ㅋ

에어컨 자리 눈알몬스터는 아직도 그대로입니다. ㅠ.ㅠ

1달은 더 견뎌야 해요.

 

 

침대 헤드 위쪽 벽은 패브릭 느낌의 필름입니다. 팀장님 권하는대로 했어요..역시 현대필름.

나머지 벽은 도장입니다.

 

천장은 거실 부엌 애들방 모두 개나리벽지 입니다.

 

마침 품번 찍은거 있네요. ㅎㅎ 이건 울동생 다니는 회사라 누나의 사심 가득~~~

 

참.. 페인트 얘길 어딘가는 해야하는데..

우리집은 벤자민무어 스커프엑스인지 그거 안 했습니다. 벤자민무어 친환경페인트가 미국에서 최근 소송이 걸려있다고 들었어요... 정확히는 저도 모르니 혹시나 문제될까봐 안 올리고요. . 암튼 페인트 칠 하시는 분들도 그거 쓰면 눈이 아프다고들 하시나봐요. 그래서 울 팀장님이 요즘은 벤자민무어 안쓰고 이태리 발페인트 인지 손페인트인지 쓰신다고 하셨어요. 인쇼 유튭에도 조만간 이거관련 한번 올리실거 같은데...

벤자민무어가 근데 관리하긴 넘 좋대요. 묻은거 쓱, 닦으면 잘 지워지고, 스크래치에 강하고... 물론 안지워지는 얼룩땜에 다시 칠하려면 이색이 많이져서 한면을 다 칠해야하는 어려움이 있다네요.

반면 이 발페인트는, 강도는 벤자민무어보다 좀 약한듯 한데, 일단 냄새는 전혀없고, 덧칠해도 주변에 표가 안나요. 페인트 기사님들도 벤자민무어보다 훨 눈이나 목이 편하다고 하신대요. 흰색이다보니 아무래도 덧칠할 데가 생겨요. 울 천방지축 딸내미가 중3인데 이미 자기방 문에 쓰윽~~ 그어놨더라고요. ㅠ.ㅠ 팀장님이 여분 가져다 주신대요.

 

워워... 정신차리고..

다시 안방 가구얘기로~~

 

 

침대는 민아트 수납형 침대입니다. 그래서 이전 침대프레임보다 조금 높은데, 매트리스도 브랜드리스 높은걸로 해버려서...

남편이 첫날, 낙상방지용 보험을 들어야겠다고 하더라고요. ㅎㅎ 근데 금방 익숙해져요. 호텔느낌도 나고~ ㅋㅋ

 

민아트 침대 좋은거 같아요. 튼튼하고 예쁘네요. 사장님도 넘 친절하시고 직접 오셔서 설치해주시는데, 제품좋고 성실한 소상공인회사..아니 중소기업, 이라고 해야하나요? 전 잘 되었음 좋겠어요... 합판아니고 엘다원목이고, 바닥만 합판인거 같아요..

 

대표사진 삭제

사진 설명을 입력하세요.

서랍겸 화장대겸 협탁으로 쓸 가구 찾아 엄청 인터넷 서핑을 했어요. 원래 침대만 먼저 사고 거기에 맞춰 색깔과 재질을 맞추다보니 어렵더라고요...

근데도 화장대가 온거 보니 제 예상보단 좀 많이 크네요... 하도 고르는게 많다보니 나중엔 제대로 사이즈 가늠을 안했는지 어쨌는지... 침대 옆에 들어온날 앗, 망했다, 싶었는데 근데 자꾸보니 괜찮은거 같기도 해요.. (그렇다고 말해주세요.. plz ㅠ.ㅠ)

크기땜에 살짝 아쉽지만, 정말 튼튼하고 향이 좋은, 잘 만든 가구입니다. 가격도 이 품질에 결코 안 비싼~~겉은 다 엘다원목이고요. 서랍 안쪽은 다 삼나무라 잘때 서랍열고 자고프더라고요. 향이좋아서..

 

가구이야기, 라는 개인이 하시는 블로그에서 구입한거예요. 이거 올려도 되나요? 전 이런가구 만드시는 분 넘 좋아요.. 꼭 소개해드리고파서... (안되면 바로 알려주세요. 삭제할께요) 전혀 뭐 받은거 없음~~~!

https://m.blog.naver.com/gg_story/222045070943

 

서랍장 - 가구이야기가 직접 만든 엘다(오리나무) 서랍장

오늘은 조금 특별한 가구를 소개해 드릴까 합니다. 정말 오랜만이기도 하군요. 바쁘기도 하고, 요즘 다크소...

m.blog.naver.com

 

창가 쪽에서 찍은 사진.. 왼쪽이 안방화장실 문입니다. 무문선 깔끔이~~

시계를... 나한테 묻지도 않고 신랑이 주문을 해버렸어요. 그래도 가볍고 깔끔해하고 못안박고 투명스티커 후크로 걸수 있는 시계더라고요. 신랑이 자기도 인쇼스타일 안다고 엄청 으쓱~~해 하네요... ㅋㅋ 저도 치하를 해줘야 할것같아서 한컷 첨부해요.

 

안방이야기 끝~~~

 

728x90
반응형
Posted by Rfriend
,

-- 2021년 4월 29일 작성 -- 

 

6년 전에 구입한, 당시 나름 고가였던 미라지 식탁이 있습니다. '앞으로 너와 최소 20년은 함께하리~' 그랬는데... 인쇼덕에 저한테 하루아침에 애물단지 취급을 받고 있죠. 얌전히 잘 쓴것도 아니라 칠벗겨진 곳도 여기저기 있고 색깔이나 디자인도 이제 제 눈엔 좀 올드한거 같고... ^^ 그렇다고 버리거나 팔기엔 너무 낭비잖아요... 하루는 인쇼 스타일 제작 테이블로 하기로 했다가, 며칠지나선 맘고쳐먹고 기존꺼 쓰자고 했다가.. 그담날은 또 맘을 바꾸고... 십수번을 하다가 그냥 기존꺼 쓰기로... ㅋㅋ 인테리어 선택은 정말 어려워요. 물론 자금이 넉넉하면 걱정없겠지만...

 

페인트칠 하려는걸 인쇼팀장님이 말려서 (제 실력을 제대로 보신거죠..^^ ) 어제 그냥 식탁보를 사서 씌웠습니다. 그 기념으로 오늘은 꽃도 사와서 꾸며봤어요. 흠. 분위기가 괜찮나요?

 

ㅎㅎ 제 눈엔 괜찮습니다. 그럼 됐죠 뭐..

 

 

오늘은 저희집 부엌 비포 사진부터 애프터 사진, 싱크대 장 어떻게 구성했는지, 서랍이랑 상부장 안쪽도 보여드릴께요.

 

Before

 

싱크대가 ㄱ 자로 되어있고 모서리에 빗각벽이 있는 부엌이었어요. 저 비스듬한 벽면을 없앨수가 없는, 어떻게해도 안 예쁠것 같은 구조입니다. 작은 창문 옆으로 다용도실 문이 있어 창문 주변엔 뭘 놓을수도 없고요.

 

Before

 

싱크대 우측으로 냉장고장이 두개있었는데 참 안 이쁘게 있죠.

 

Before

 

식탁위치에 나름 이전 주인분들이 예술적으로 꾸민 아트월 입니다. 구스타프 클림트의 <키스>를 인테리어적으로 재구성한게 아닐까요?

 

 

이집이 이제 이렇게 바뀌었어요.

 

After

 

 

After

 

 

빗각벽은 기둥뒤로 가려졌고, 다용도실 가는 길은 냉장고장 뒤로 감춰져있습니다.

(냉장고장 뒷면엔 청소기랑 쌀통이 있어요. 작은 창도 계속 있고요. 다용도실은 남편 홈오피스로 바뀌었구요.)

 

 

After

 

4미터는 되는 긴 싱크대입니다.

인덕션 밑 광파오븐 자리 빈 공간을 다시 메꾸고 서랍장 긴거로 옆의 서랍장처럼 바꾸려고 해요.

 

내 평생 가장 긴 조리대 공간울 가진 싱크대입니다. 너무너무 편해요.. 그리고 너무 예쁘죠. ㅋㅋ

 

After

 

 

After

 

 

After

 

 

어느 방면에서 찍어도 뭐 이정도면 봐주만 하죠?

 

팬던트는 인쇼가 자주쓰는 스타일입니다. 심플하고 빛이 퍼지지 않고 식탁 주변만 비춰주는데, 음식이 기막히게 맛있게 보입니당. ㅎㅎ 만족하고 있어요.

 

그럼 좀 실용적인 사진들 갑니다.

 

 

After

 

 

상부장 선반을 세개 끼워서 4칸을 만들었어요. 접시나 밥그릇 국그릇 등은 너무 두껍게 쌓으면 제 키엔 꺼내기 힘들더라고요. 4칸으로 만들어놓으니 넘 편합니다. 3칸 정도는 까치발로 해서 꺼낼수도 있고 제일 많이 쓰는 아래 두칸은 편하게 사용할 수 있어요.

 

 

After

 

 

After

 

 

밥통과 인덕션 위에 후드가 안보이게 감춰져 있습니다.

식탁과 함께 하루아침에 저한테 미움받고 있는 애는 저 검정색 밥통입니다. 온통 화이트한 우리 새집에 안 어울리는 저 녀석을... 어느날 흰색으로 페인트칠 해버릴지도 모르겠어요.

 

 

After

 

 

암튼 후드가 감춰진 상부장 도어를 열면 요렇게..

전 후드를 90센티 큰걸로 했구요. 이 사이즈로 하길 잘했다 싶어요. 60센티보다 좋으네요.

아래에 하부장 옆면을 사실 10센티만 공간을 더 줘서 밥통 자리가 넓었으면 좋았을 걸, 해요. 지금은 인덕션과 거의 아슬아슬 붙어있어요. 닿지는 않아서 요리때 괜찮은데, 조금 신경은 쓰이네요. 밥통 위치를 사전에 꼭 확정해서 설계하시길~

 

전 사실 밥통 증기배출할때 증기가 상부장들로 쫘악~ 퍼지는게 그동안 이전집에서 종종 신경쓰이더라고요. 저 상부장들 고열의 스팀 매일 먹으면 빨리 삭지 않을까... 지금은 긴 후드 덕에 그거 켜놓으면 증기가 쭈욱~~, 빨려들어가서 좋더라고요.

 

후드 위쪽 우측에 센서등처럼 보이는건 아마 라인조명 이나 싱크대 led바 조절기(분배기?) 일거예요. 팀장님이 그렇게 말했던거 같아요. 신발장안에도 있고, 안방 붙박이장 안에도 있어요~

 

다음은 하부 서랍장..

 

 

After

 

 

하부장 서랍중 위의것 1개는 속속서랍을 만들어 원목수저통 맞췄어요.

 

 

After

 

 

속속서랍 닫힌거 보이시죠? 그 밑에 자잘한 조리도구 넣었구요.

 

서랍장이 90센티인데 첨엔 넘 큰거 아닌가 했는데, 안 크고 좋네요. 완전 편해요.

 

 

After

 

 

특히 아래 서랍엔 후라이팬이랑 냄비 다 들어가요. 후라이팬 정리대, 냄비뚜껑 정리대 적극 추천합니다.

 

 

 

After

 

 

하부 서랍장 두칸은 같은 비율로 안 하고 2:3 정도로 했어요. 윗칸이 더 작고 아래칸이 더 커요.아래 서랍에 후라이팬이나 냄비 양념장 칸과, 긴 간장병 식초병 등 양념장 담는 칸으로 쓰려고요. 양념망장 없애고 이걸로 했는게 완전 만족하고 있습니다.

 

양념장 서랍은 ㅎㅎ 양념통 그릇들 바꾸고 나서야 보여드릴까, 지금은 쫌 민망~

다른 서랍들도 민망하긴 마찬가지지만, 그나마 제 수준엔 양호한거라 ㅎㅎ 그래서 공개 했어요.

 

다음엔 부엌 나머지부분이랑 가전 얘기 좀 해볼께요~

 

그럼 오늘도 이만 안녕히~~

 

728x90
반응형
Posted by Rfriend
,

-- 2021년 4월 20일 작성 --

 

오랫만이예요. 집 정리를 빨리 다 끝내고 랜선 집들이 근사하게 하고팠는데, 에휴... 각 공간마다 하나 둘씩 마무리 안 된것들이 있어요. 안방과 거실엔 에어컨이 아직 못 들어와서 그자리를 몬스터 주식회사 괴물 눈같은 압력측정계가 째려보고 있고요.. 아래 요놈이요~

 

 

주방엔 광파오븐 넣을 씽크대가 여러 이유로 될듯 말듯.. 오븐에 자리를 안 내주고 있어요. 씽크대 아저씨가 길이를 잘못 계산하셔서 1센티가 모자라 어떻할지 고민중이거든요. 오븐받침대를 1센티 낮추려니 오븐 무게땜에 밑의 레일이 휠거라고 하시네요. 장을 완전히 새로 짜야하는지 아예 옆의 빈 공간에 선반을 놓고 거기에 광파오븐을 올릴지.. 논의중입니다. 광파오븐 무게가 25킬로그람이라네요. 이렇게 무거운줄 몰랐어요. 엘지 오브제는 매립형이 안 되게 출시되어 열땜에 레일을 달려니 이래저래 걸리는게 많네요. ㅠ.ㅠ

 

쇼파랑 아들방 책장도 아직 안 왔고.. ㅠ.ㅠ 소파는 5월에나 온다네요. 흑. 올 여름 안엔 집들이 할수 있겠죠?

 

그리하여 글 쓰는 의욕상실 중이었는데.. 오늘 저희집 해주신 목수님 연락이 오셨더라고요. 본인 포트폴리오에 가지고 있게 우리집 무문선 사진들좀 보내달라고.. 생각해보니 목수님들은 마감되기 전 목공밖에 못 보시니 집이 을매나 예쁜지 모르시겠더라고요. ㅎㅎ

암튼 문들이랑 사진 찍은김에 오늘은 무문선 문들 자랑질 할께용~~

 

 

 

<현관문을 지나 ~ 복도 오른편 화장실~ 부엌 싱크대쪽 가벽까지>

 

현관 방화문도 집 안쪽엔 필름으로 마감했구요.

벽이 완전 깔끔하죠~ 손잡이 있는 부분이 화장실 문입니다. 이사전 실리콘 처리해주러 오신 아저씨가 화장실 문열리는 것 보고 깜놀~. " 아 저기가 문이었어요? 화장실이 숨어있었네요?" 하시더라고요. ㅎㅎ

 

 

<현관과 옆의 벽 : 필름 마감>

 

필름 얘기 나온김에...

전 첨에 짙은색 필름을 쓸거라고 생각했는데, 팀장님은 이 색을 강력추천 하시더라고요. 인쇼에서 필름 고를때 대표님이랑 디자이너 팀장들 모두 실제로 벽에 발라놓고 테스트 한대요. 강도랑 톤앤 매너랑 여러가지 다.. 그중에 위의 것이 가장 많은 표를 얻은거라고 하더라고요. 근데 워낙 인쇼에서 신경 많이 쓰는 부분이라 제품명 공개는 어려ㅂ다고 하시네요.

암튼 하고 나니, 이 색으로 하길 잘 했다 싶어요.. 밝아서 우리집 분위기에도 잘 맞고, 다른 컬러나 소품들과도 매치가 잘 될거 같아서요.

 

 

화장실 안쪽에서 본 문 사진도 보여드릴께요~

 

 

 

여전히 깔끔허쥬? ㅋㅋ

저놈의 손잡이... 암튼 문에 뭐가 거슬리는거 엄청 싫어하는 울 팀장님이 최대한 작은 손잡이로 고른 거예요. 붙박이장들은 다 아예 손잡이가 없고요. 전 사실 기존 손잡이에 비해 쬐끔 불편한데, 익숙해지는 중이예요.

 

 

 

<거실~ 안방쪽 베란다 방화문 1>

 

<거실~ 안방쪽 베란다 방화문 1>

여긴 사진찍다보니 조명 자랑이 슬쩍 하고싶어서 더 찍어봤어요~

 

<거실~ 안방쪽 베란다 방화문2>

 

 

 

<거실~ 안방쪽 베란다 방화문3>

 

인쇼스타일 대로라면 여기도 일자로 무문선 문이 깨끗하게 들어가야 할 자리인데, 확장신고 하면서 저희 부부가 방화문으로 하겠다고 했어요.. 방화문 규정 때문에 문틀이 안으로 들어가 있어요.

 

 

 

 

 

앗. 아들방 문 닫고 다시 찍을께요~

 

 

 

매립등이 아주 예쁘게 세개 나란히 있어요. 그 밑으로 자작고 긴 장을 놓고 소품을 올릴까, 벽에 커다란 그림을 걸까.. 고민중입니다. 갤러리처럼.

 

겉에선 튀어나온 손잡이가 없고 밀고 당길때 쓰기 위해 안으로 파인 깔끔한 손잡이가 있어요. 참고로 모든 문들은 밖에서 미는걸로 되어 있습니다.

 

 

 

 

그럼 안방 안쪽도 보여드릴께요

 

 

 

 

 

 

사진 오른쪽이 안방문이고 왼쪽이 안방화장실 문입니다.

ㅎㅎ 사진이 좀 휑하고 하옇네요~~

 

목수님 얘기 나온김에, 저희집 해주신 목수님 알려드릴께요. 궁금해 하시며 댓글에 물어보신 분들도 계신데, 목수님 동의 받고 연락처 올려요.

 

서연수 목수님이시고요.

010-6350-0655예요.

우리집 예쁘게 해주셔서 감사해요~!.

 

오늘은 그럼 이만

728x90
반응형
Posted by Rfriend
,