이 섹션의 다중 페이지 출력 화면임. 여기를 클릭하여 프린트.

이 페이지의 일반 화면으로 돌아가기.

Database

데이터베이스

1 - RDBMS

관계형 데이터베이스. MySQL, PostgreSQL, SQLite

1.1 - 관계형 테이터베이스

1.1.1 - Database Index (인덱스)

인덱스(index)란?

  • 데이블에 대한 동작 속도를 높여주는 자료 구조를 말한다.
  • 인텍스는 테이블 내에 1개의 컬럼, 혹은 여러 컬럼을 이용하여 생성될 수 있다.
  • 빠른 검색 동작뿐 아니라 레코드 접근과 관련 효율적인 순서 매김 동작에 대한 기초를 제공한다.
  • 인덱스란 추가적인 쓰기 작업과 저장 공간을 활용하여 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료 구조이다.
  • 만약 우리가 책에서 원하는 내용을 찾는다고 하면, 책의 모든 페이지를 찾아 보는 것은 오랜 시간이 걸린다. 그렇게 때문에 책의 저자들은 책의 맨 앞 또는 맨 뒤에 색인을 추가하는데, 데이터베이스의 index는 책의 색인과 같다.
  • 데이터베이스에서도 테이블의 모든 데이터를 검색하면 시간이 오래 걸리기 때문에 데이터와 데이터의 위치를 포함한 자료구조를 생성하여 빠르게 조회할 수 있도록 돕고 있다.
  • 만약 index를 적용하지 않은 컬럼을 조회하면, 전체를 탐색하는 Full Scan이 수행된다. Full Scan은 전체를 비교하여 탐색하기 때문에 속도가 떨어진다.

데이터베이스에서 자료를 갬색하는 2가지 방법

  • FTS(Full Table Scan)
    • 테이블을 처음부터 끝까지 검색하는 방법이다.
  • Index Scan
    • 인데그를 검색하여 해당 자료의 테이블을 액세스하는 방법이다.

인덱스의 자료구조

  • 해시 테이블

    • 컬럼의 값으로 생성된 해시를 기반으로 인덱스를 구현한다.
    • 시간복잡도 O(1)이라 검색이 매우 빠르다.
    • 부등호(<, >)와 같은 연속적인 데이터를 위한 순차 검색이 불가능하다.
  • B+Tree

    • 자식 노드가 2개 이상인 B-Tree를 개선시킨 자료구조이다.
    • BTree의 리프노드들을 LinekdList로 연결하여 순차 검색을 용이하게 하였다.
    • 해시 테이블보다 나쁜 O(𝑙𝑜𝑔2𝑛)의 시간 복잡도를 갖지만 해시테이블보다 흔하게 사용된다.

1.1.2 - Database Transaction (트랜잭션)

Database Transaction(트랜잭션)이란?

  • 데이터베이스의 상태를 변화시키기 위해 수행하는 논리적인 작업의 단위이다.
  • 데이터베이스의 데이터를 조작하는 작업 단위(Unit of work)이다.
  • 트랜잭션에는 여러가지의 연산이 수행될 수 있다.
  • 트랜재션은 수행 중에 한 작업이라도 실패를 하면 전부 롤백이 이루어지고, 모두 성공하면 commit을 수행한다.

Transaction ACID란?

  • 원자성(Atomicity)
    • 하나의 트랜잭션이 작업이 그중에 일부분만 실행되거나 중단되지 않는 것을 보장해주는 것을 말한다.
  • 일관성(Consistency)
    • 트랜잭션이 작업이 성공적으로 완료가 되더라도 작업 이전과 같이 같은 상태를 유지하는 것을 말한다.
  • 격리성(Isolation)
    • Trasaction 작업이 수행되고 있을 때 다른 작업이 끼어들지 못하도록 보장해주는 것을 말한다.
  • 지속성(Durability)
    • 성공적으로 수행된 트랜잭션이 대해서는 영구히(Persistent) 반영되어야 함을 말한다.

Transaction을 사용시 주의할 점

  • 트랜잭션의 범위는 최소화한다.
    • 트랜잭션의 범위를 최소화 하는 것이 중요하다.
    • 데이터베이스의 커넥션의 수는 한정적이기 때문에 커넥션을 보유하는 시간을 최소화해야할 필요가 있다.
    • 그렇지 않으면, 다른 서비스들은 해당 커넥션 사용을 위해 대기해야 할 상황이 발생한다.

Transaction isolation level의 종류 및 특징은 무엇인가?

  • READ UNCOMMITTED
    • SELECT 쿼리 실행시에 다른 트랜잭션에서 COMMIT 되지 않은 데이터를 읽어올 수 있다.
    • COMMIT 되지 않은 데이터를 읽는 현상을 Dirty read라고 말한다.
    • INSERT만 진행되고 ROLLBACK 될 수 있는, 즉 COMMIT이 되지 않은 데이터를 읽을 수 있어 유의해야 한다.
  • READ COMMITTED
    • COMMITTED에서는 COMMIT이 완료된 데이터만 SELECT시에 보이는 수준을 보장하는 Level이며, 대부분 DBMS에서 Read Committed를 기본으로 설정한다.
    • Read Committed에서는 Read Uncommitted에서 발생하는 Dirty read가 발생하지 않도록 보장해 준다.
    • 트랜잭션에서 COMMIT을 수행하지 않더라도 DB에 이미 값이 반영이 되어있는 상태인데 COMMIT 이전의 데이터를 보장 받기 위해서는 COMMIT 되지 않은 쿼리를 복구하는 과정이 필요하게 된다. 즉, 이 시점에서는 Consistent Read를 수행해야 함을 의미한다.
    • Read Committed의 문제는 하나의 트랜잭션 안에서 SELECT를 수행 할 때마다 데이터가 동일하다는 보장을 해주지 않는다. 그 이유는 다른 트랜젝션에서 해당 데이터를 COMMIT 했을 경우에는 COMMIT된 데이터를 반환해 주는게 Read Committed의 특징이기 때문이다.
    • 위와 같은 이유로 Read Committed를 Non-repeatable Read라고도 한다.
  • REPEATABLE READ
    • Read Committed와는 다르게 Repeatable Read는 한 트랜잭션 안에서 반복해서 SELECT를 수행하더라도 읽어 들이는 값이 변하지 않음을 보장한다.
    • Repeatable Read 트랙잭션은 처음 SELECT를 수행한 시간을 기록한 뒤 그 이후에는 모든 SELECT마다 해당 시점을 기분으로 Consistent Read를 수행하여 준다.
    • 그러므로 트랜잭션 도중 다른 트랜잭션이 COMMIT되더라도 새로이 COMMIT된 데이터는 보이지 않게 된다.
    • 그 이유는 첫 SELECT 시에 생성된 SNAPSHOT을 읽기 때문이다.
  • SERIALIZABLE
    • Serializable은 모든 작업을 하나의 트랜잭션에서 처리하는 것과 같은 가장 높은 고립수준을 제공한다.
    • Read Committed, Repeatable Read 두개의 공통적인 이슈는 Phantom Read가 발생한다는 점이다.
      • Phantom Read란?
        • 하나의 트랜잭션에서 UPDATE 명령이 유실되거나 덮어써질수 있는 즉, UPDATE후 COMMIT하고 다시 조회를 했을때 예상과는 다른 값이 보이거나 데이터가 유실된 경우를 Phantom Read라고 한다.
    • 그와 다르게 SERIALIZABLE에서는 SELECT 쿼리가 전부 SELECT … FOR SHARE로 자동으로 변경되어 Repeatable Read에서 막을 수 없는 몇 가지 상황을 방지할 수 있게됩니다.

Commit과 Rollback

  • Commit
    • 하나의 트랜잭션이 끝났을 때, 완료된 것을 트랙잭션 관리자에게 알려준다.
  • Rollback
    • 하나의 트랜잭션 처리가 비정상적으로 종료되어 DB의 일괄성을 깨트렸을 때, 모든 연산을 취소시키는 것을 말한다.

1.1.3 - Database Normalization (정규화)

DB 정규화

  • 자료 손실이나 불필요한 정보의 도입 없이 데이터 일관성, 데이터 중복을 최소화하고 최대의 데이터 안정성 확보를 위한 안정적 자료 구조로 변환하기 위해서 하나의 테이블을 둘 이상을 분리하는 작업이다.

DB 정규화의 목적

  • 데이터 무결성 유지
    • 자료 저장에 필요한 저장 공간을 최소화하고 자료의 삽입, 갱신 및 삭제에 따른 이상 현상을 제거한다.
  • 자료 구조의 안정화성 최대화를 위해서 이다.

정규화 과정

  • 제1정규화
    • 모든 속성 값이 원자 값을 갖도록 분해한다.
  • 제2정규화
    • 제1정규형을 만족하고, 기본키가 아닌 속성이 기본키에 완전 함수 종속이도록 분해한다.
      • 여기서 완전 함수 종속이란 기본키의 부분집합이 다른 값을 결정하지 않는 것을 의미한다.
  • 재3정규화
    • 제2정규형을 만족하고, 기본키가 아닌 속성이 기본키에 직접 종속(비이행적 종속)하도록 분해한다.
      • 여기서 이행적 종속이란 A->B->C가 성립하는 것으로, 이를 A,B와 B,C로 분해하는 것이 제3정규형이다.
  • BCNF 정규화
    • 제3정규형을 만족하고, 함수 종속성 X->Y가 성립할 때 모든 결정자 X가 후보키가 되도록 분해한다.

1.1.4 - Database Tuning (튜닝)

DB Optimizer(옵티마이저)

  • SQL를 가장 빠르고 효율적으로 수행할 최적의 처리 경로를 생성해주는 DBMS 내부의 핵심엔진이다.
  • 종류
  • 규칙 기반 옵티마이저, 비용 기반 옵티마이저
  • 과정
  • 사용자가 던진 쿼리 수행을 위해, 후보군이 될 만한 실행 계획을 찾는다.
  • 오브젝트 통계 및 시스템 통계 정보를 통해 각 실행 계획의 예상 비용으 산정한다.
  • 갈 실행 계획을 비교해서 최저 비용을 갖는 하나를 선택한다.

힌트(Hint)

  • 힌트란 SQL을 튜닝하기 위한 지시구문이다.
  • 옵티마이저가 최적의 계획으로 SQL문을 처리하지 못하는 경우에 개발자 직접 최적의 실행 계획을 제공하는 것이다.
  • 힌트는 SELECT 다음에 작성할 수 있으며, INDEX, PARALLEL 등 다양한 힌트절이 있다.

데이터베이스 튜닝과 방법

  • DB 튜닝은 데이터베이스 구조나 데이터베이스 자체, 운영체제 등을 조정하여 데이터베이스 시스템의 성능을 향상시키는 작업을 의미한다.
  • 튜닝은 DB 설계 튜닝 > DBMS 튜닝 > SQL 튜닝의 단뎨로 진행할 수 있다.
  • 1단계 : DB 설계 튜닝(모델링 관점)
  • 튜닝 방법
    • 데이터베이스 설계 단계에서 성능을 고려하여 설계
    • 데이터 모델링, 인덱스 설계
    • 데이터 파일, 테이블 스페이스 설계
    • 데이터베이스 용량 산정
  • 튜닝 사례
    • 반정규화 분사 파이배치
  • 2단계 : DBMS 튜닝(환경 관점)
  • 튜닝 방법
    • 성능을 고려하여 메모리나 블록 크기 지정
    • CPU, 메모리 I/O에 관한 관점
  • 튜닝 사례
    • Buffer 크기
    • Cache 크기
  • 3단계 : SQL 튜닝(APP 관점)
  • 튜닝 방법
    • SQL 작성 시 선능 고려
    • Join, Indexing, SQL Execution Plan
  • 튜닝 사례
    • Hash / Join

1.2 - SQL 기본

SQL 기본

SQL에 대해서 설명한다

1.2.1 - SQL 기본 | SQL

SQL(Structured Query Language)은 관계형 데이터베이스 관리 시스템(RDBMS)의 데이터를 관리하기 위해 설계된 특수 목적의 프로그래밍 언어이다. 관계형 데이터베이스 관리 시스템에서 자료의 검색과 관리, 데이터베이스 스키마 생성과 수정, 데이터베이스 객체 접근 조정 관리를 위해 고안되었다. 많은 수의 데이터베이스 관련 프로그램들이 SQL을 표준으로 채택하고 있다.

명령어 종류

데이터 정의(DDL), 데이터 조작(DML), 데이터 제어(DCL)를 나눈다.

  • 데이터 정의 언어 (DDL : Data Definition Language)
  • 데이터 조작 언어 (DML : Data Manipulation Language)
  • 데이터 제어 언어 (DCL : Data Control Language)

1.2.1.1 - SQL 기본 | SQL | Comments : 주석

주석(comment)은 로직에 대한 설명이나 코드를 비활성화 할 때 사용한다. 주석은 코드에 영향을 미치지 않는다. SQL에서 사용할 수 있는 주석문은 2가지 종류가 있다.

단일 라인 주석 (Single-line comment)

한줄 라인 주석은 ‘–’ 를 사용한다.

--Select all:
SELECT * FROM customer;

블록 주석 (Multi-line Comment)

/* 에서 */까지의 모든 내용이 주석 처리 된다.

/*Select all the columns
of all the records
in the customers table:*/
SELECT * FROM customer;

1.2.2 - SQL 기본 | DDL : 데이터 정의 언어

DDL(Data Definition Language) 는 데이터 베이스 스키마를 정의 하거나 조작하기 위해 사용한다. SCHEMA, DOMAIN, TABLE, VIEW, INDEX 를 다음 명령어로 정의, 변경, 삭제한다.

1.2.2.1 - SQL 기본 | DDL : 데이터 정의 언어 | DATABASE

CREATE DATABASE

CREATE DATABASE 문은 최초 데이터베이스를 생성할 때 사용된다.

CREATE DATABASE 문법

CREATE DATABASE [데이터베이스명];

CREATE DATABASE 예제

CREATE DATABASE devkuma;

DROP DATABASE

DROP DATABASE문은 데이터베이스를 삭제할 때 사용된다.

DROP DATABASE 문법

DROP DATABASE databasename;

DROP DATABASE 예제

CREATE DATABASE devkuma;

1.2.2.2 - SQL 기본 | DDL : 데이터 정의 언어 | TABLE

테이블은 데이터베이스에서 데이터 저장의 기본 구조이다. 테이블은 컬럼(column) 및 로우(row)으로 나눈다. 각 열은 하나의 데이터를 나타내며 각 열은 하나의 데이터의 일부를 나타낸다. 예를 들어, 고객 데이터가 기록된 테이블이 있는 경우, 그 컬럼에는 이름, 주소, 생년월일 등이 포함된다. 테이블에 정의를 할 때, 그 컬럼의 제목 및 그 필드의 데이터 유형을 표시한다.

데이터는 여러 형태가 있을 수 있다. 정수의 경우(예 : 1), 실수 (예 : 0.55), 문자열 (예 : ‘sql’), 날짜/시간(예 : ‘2000-JAN-25 03:22:22’), 이진 (binary)까지 등의 형식으로 존재한다. 이런 형식들을 데이터 유형(data type)이라고 한다. 하나의 테이블에 정의를 할 때에 각 컬럼의 데이터 정의를 해야 한다. 예를 들어, ‘이름’ 필드의 데이터 유형은 char(50)로 50개의 문자열을 나타내는 것이다. 그러나 데이터베이스에 따라 데이터 유형이 다르기 때문에 그것에 주의해야 한다. 따라서 테이블에 정의를 할 때에 그 데이터베이스에 대한 설명을 참고하도록 한다.

CREATE TABLE

CREATE TABLE 문은 테이블을 생성할 때 사용된다.

CREATE TABLE 문법

CREATE TABLE "테이블명" (
    "필드명1" "데이터 유형",
    "필드명2" "데이터 유형",
    "필드명3" "데이터 유형",
    ... 
);

예를 들어, 고객 데이터를 만들 경우 아래와 같이 SQL을 입력한다.

CREATE TABLE customer (
    first_name char(50),
    last_name char(50),
    address char(50),
    city char(50),
    country char(25),
    birth_date datetime
);

DROP TABLE

DROP TABLE 문은 데이터 테이블을 삭제할 때 사용된다.

데이터베이스에서 하나의 테이블을 삭제하는 경우가 있고, 그것을 실행하지 않으면 큰 문제가 발생할 우려가 있다. 데이터베이스 관리자 (Database Administrator - DBA)가 데이터베이스 관리를 효율적으로 할 수 없기 때문입니다. 다행히 SQL에서 DROP TABLE 프로그래밍을 이용하여 테이블을 제거할 수 있다. DROP TABLE 프로그래밍은 다음과 같습니다.

DROP TABLE "테이블명";

테이블을 삭제하려면 다음과 같이 입력한다.

DROP TABLE customer;

TRUNCATE TABLE

DROP TABLE를 하여 테이블을 삭제하면 테이블 전체가 사라지게 된다. 그에 비해 TRUNCATE TABLE를 삭제를 하면 테이블의 데이터가 모두 사라지고 테이블 자체가 그대로 남는다

TRUNCATE TABLE "테이블명";

테이블의 데이터를 삭제하려면 다음과 같이 입력한다.

TRUNCATE TABLE customer;

1.2.2.3 - SQL 기본 | DDL : 데이터 정의 언어 | VIEW

뷰(View)는 가상 테이블로 볼 수 있다. 테이블과의 차이점은 테이블에 데이터가 실제로 저장되어 있지만, 뷰는 테이블에 만들어진 구조에서 데이터가 실제로 저장되지 않는다.

CREATE VIEW

뷰를 생성하는 명령문은 다음과 같다.

CREATE VIEW "VIEW_NAME" AS "SQL문";

“SQL문"는 어떠한 SQL이라도 상관없다.

VIEW 생성

예를 들면, 다음과 같은 테이블이 있다고 하자.

customer 테이블

컬럼명 자료형
first_name char(50)
last_name char(50)
address char(50)
city char(50)
country char(25)
birth_date datetime

이 테이블에서 first_name, last_name과 country 3개의 필드가 포함된 뷰를 만드는 경우 다음과 같이 입력한다.

CREATE VIEW v_customer
AS SELECT first_name, last_name, country
FROM customer;

그러면 v_customer라는 뷰가 생성된다.

v_customer 뷰

컬럼명 자료형
first_name char(50)
last_name char(50)
country char(25)

테이블 결합하여 만든 VIEW

뷰를 사용하여 두 테이블을 조인할 수도 있다. 따라서 사용자는 두 개의 서로 다른 테이블을 결합하지 않아도, 뷰로 사용하여 필요한 정보를 직접 확인할 수 있다. 가령 다음과 같은 두 테이블이 있다고 하자.

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

Geography 테이블

region_name store_name
East Boston
East New York
West Los Angeles
West San Diego

아래 명령으로 지역(Region)의 판매(Sales)가 포함된 뷰를 만들 수 있다.

CREATE VIEW v_region_sales
AS SELECT A1.region_name region, SUM(A2.sales) sales
FROM geography A1, store_Information A2
WHERE A1.store_name = A2.store_name
GROUP BY A1.region_name;

이것으로 v_region_sales라는 뷰가 생성되었다. 이 뷰에 다른 지역의 매출 데이터가 포함된다. 이 뷰에서 데이터를 검색하는 경우 다음과 같이 입력한다.

SELECT * FROM v_region_sales;

위 명령의 결과는 아래와 같다.

region sales
east 700
west 2050

DROP VIEW

DROP VIEW 명령을 사용하여 기존 뷰를 삭제한다.

DROP VIEW "[데이터베이스명.]VIEW_NAME";

1.2.2.4 - SQL 기본 | DDL : 데이터 정의 언어 | INDEX

테이블의 데이터를 조회할 때 빠르고 효과적으로 조회할 수 있도록 도와주는 역활을 한다. 대신, 조회는 빠르지만 INSERT, UPDATE등을 수행할 떄는 느려지기 때문에 주의가 필요하다. 조회가 많은 테이블을 기준으로 설정하기를 추천한다.

CREATE INDEX

CREATE INDEX index_name
ON table_name (column1, column2, ...);

DROP INDEX

DROP INDEX [ IF EXISTS ] index_name;

1.2.2.5 - SQL 기본 | DDL : 데이터 정의 언어 | Primary Key : 기본 키

기본 키(Primary Key)의 각 데이터는 테이블에서 유일한 값이다. 즉, 기본 키는 하나의 테이블에서 각 행의 데이터를 유일하게 확인하는데 사용되는 것이다. 기본 키는 원래의 데이터 내의 하나의 필드 또는 하나로 만들어진 필드(원래 데이터와 관계없는 필드)가 가능하다. 기본 키는 하나 이상의 필드를 포함할 수 있다. 기본 키가 여러 필드를 포함하는 경우, 복합 키(Composite Key)라고 한다.

기본 키는 새로운 테이블을 만들기 위해 설정된다(CREATE TABLE 절에서) 또는 기존에 있는 테이블의 구조 설정 변경(ALTER TABLE에서)에 사용될 수 있다.

테이블 생성시 기본 키 설정 방법

다음 새 테이블을 만드는데 사용되는 기본 키 설정 방법에 대한 몇 가지 예를 올려두었다.

MySQL:

CREATE TABLE customer  (
  sid INTEGER, 
  last_name VARCHAR(30), 
  first_name VARCHAR(30), 
  PRIMARY KEY (sid)
);

Oracle:

CREATE TABLE Customer (
  sid integer PRIMARY KEY, 
  last_name VARCHAR(30), 
  first_name VARCHAR(30)
);

SQL Server:

CREATE TABLE Customer (
  sid INTEGER PRIMARY KEY, 
  last_name VARCHAR(30), 
  first_name VARCHAR(30)
);

테이블의 구조 변경시 기본 키 설정 방법

다음은 기존에 있는 테이블의 구조 설정을 변경하는 데 사용되는 기본 키 설정 방법이다.

MySQL:

ALTER TABLE customer ADD PRIMARY KEY (sid);

Oracle:

ALTER TABLE customer ADD PRIMARY KEY (sid);

SQL Server:

ALTER TABLE customer ADD PRIMARY KEY (sid);

ALTER TABLE 절에서 기본 키를 설정하기 전에 기본 키로 사용되는 필드가 NOT NULL 및 설정되는지 여부를 확인하는 것에 주의한다. 즉, 그 필드에 데이터가 항상 들어있다.

1.2.2.6 - SQL 기본 | DDL : 데이터 정의 언어 | Foreign Key : 외래 키

외래 키(Foreign Key)가 하나(또는 복수) 다른 테이블의 기본 키 필드를 가리키는 데이터의 참조 무결성(referential integrity)을 확인하기 위하여 사용된다. 즉, 허용된 데이터 값만 데이터베이스에 저장되는 것이다.

외레키 예제

예를 들어, 만일 두 개의 테이블이 있다고 하자. 하나는 customer 테이블에서 모든 고객 데이터가 기록되는 것이고, 또 하나는 orders 테이블에서 고객의 주문이 모두 기록되는 것이다. 그리고 하나의 제약이 있으며, 모든 주문 데이터 고객이 customer 테이블에 존재한다. 여기서 orders 테이블에 외래 키를 설정하고 외래 키가 customer 테이블의 기본 키가 된다. 그러면 orders 테이블에 있는 고객이 모든 customer 테이블에 존재하는 것을 보장할 수 있다. 즉, orders 테이블은 모든 customer 테이블에 있는 고객의 데이터이다.

그 두 테이블의 구조는 다음과 같다.

customer 테이블

필드명 특성
sid 기본 키
last_name
first_name

orders 테이블

필드명 특성
order_id 기본 키
order_date
customer_sid 외래키
amount

위의 예제에서, orders 테이블에 있는 customer_sid 필드는 customer 테이블의 sid 필드를 가리키는 외래 키이다.

다음은 orders 테이블을 만들 때에 사용되는 외래 키의 지정 방법을 몇 가지 들어 있다.

MySQL:

CREATE TABLE orders  (
    order_id INTEGER,   
    order_date DATE,
    customer_sid INTEGER, 
    amount DOUBLE, 
    PRIMARY KEY (order_id), 
    FOREIGN KEY (customer_sid) REFERENCES customer (sid)
);

Oracle:

CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY, 
    order_date DATE, 
    customer_sid INTEGER REFERENCES customer (sid), 
    amount DOUBLE
);

SQL Server:

CREATE TABLE orders  (
    order_id integer PRIMARY KEY, 
    order_date DATETIME, 
    customer_sid INTEGER REFERENCES customer(sid), 
    amount DOUBLE
);

이어서 테이블 구조 변경으로 인해, 외래 키를 지정하는 예제를 보도록 하겠다.
여기서는 orders 테이블이 이미 만들어진 외래 키가 지정되지 않은 것을 가정한다.

MySQL:

ALTER TABLE orders 
ADD FOREIGN KEY (customer_sid) REFERENCES customer (sid);

Oracle:

ALTER TABLE orders 
ADD (CONSTRAINT fk_orders1) FOREIGN KEY (customer_sid) REFERENCES customer(sid);

SQL Server:

ALTER TABLE orders 
ADD FOREIGN KEY (customer_sid) REFERENCES customer (sid);

1.2.3 - SQL 기본 | DCL : 데이터 제어 언어

DCL (Data Control Language) 는 데이터를 제어하는 언어이다. 데이터의 보안, 무결성, 회복, 병행 수행제어 등을 정의하는데 사용한다.

  • COMMIT : 트랜잭션의 작업 결과를 반영
  • ROLLBACK : 트랜잭션의 작업을 취소 및 원래대로 복구
  • GRANT : 사용자에게 권한 부여
  • REVOKE : 사용자 권한 취소

TCL

일부에서는 DCL 에서 트랜잭션을 제어하는 명령인 COMMIT 과 ROLLBACK 만을 따로 분리해서 TCL (Transaction Control Language) 라고 표현하기도 한다.

1.2.4 - SQL 기본 | DML : 데이터 조작 언어

1.2.4.1 - SQL 기본 | DML : 데이터 조작 언어 | SELECT

SELECT 문은 데이터베이스에서 데이터를 조회하는데 사용한다.

반환된 데이터는 Result set에 저장된다.

SELECT 문법

SELECT 컬럼명1, 컬럼명2, ... FROM 테이블명;

여기에서 컬럼명1, 컬럼명2, …는 데이터를 선택할 테이블의 필드 이름이며 컬럼이라고 읽는다. table에서 사용 가능한 모든 필드를 선택하려면 다음 구문을 사용한다.

SELECT * FROM 테이블명;

SELECT 예제

Column 예제

SELECT column1, column2 FROM table_name;

예제

SELECT * FROM table_name;

한 번에 여러 컬럼을 로드할 수도 있으며, 테이블의 데이터를 몇개를 선택할 수 있다.

SELECT 데모

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

sql

SELECT store_name FROM store_information;

실행 결과

store_name
Los Angeles
San Diego
Los Angeles
Boston

1.2.4.2 - SQL 기본 | DML : 데이터 조작 언어 | DISTINCT

DISTINCT 명령문은 SELECT 문의 결과에서 중복 행을 제거한다.

SELECT 명령을 사용하여 테이블에서 하나 이상의 필드의 모든 데이터를 로드할 수 있다. 같은 값이 반복 되더라도, 모든 데이터를 로드할 수 있다. 데이터 처리시에 어떤 다른 수치가 있는 경우는 자주 발생한다. 즉, 각 수치가 나온 횟수보다는 그 테이블/필드 안에 어떤 다른 수치가 있는지 알아야 되는 것이다. 그것은 SQL에서는 이것은 쉽게 할 수 있다. SELECT 뒤에 DISTINCT를 덧붙이면 된다.

DISTINCT 문법

DISTINCT 명령은 다음과 같다.

SELECT DISTINCT 컬럼명1, 컬럼명2, ...
FROM 테이블명;

DISTINCT 예제

예를 들어 보겠다. 만일 다음 테이블 Store_Information에서 다른 가게의 이름을 찾을 경우,

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 명령을 입력하면,

SELECT DISTINCT store_name FROM store_information;

결과는 아래와 같다.

store_name
Los Angeles
San Diego
Boston

1.2.4.3 - SQL 기본 | DML : 데이터 조작 언어 | WHERE

WHERE 명령은 레코드를 검색 필터링하는데 사용된다. WHERE 명령은 지정된 조건을 충족하는 레코드만 추출하는데 사용된다.

우리는 반드시 매번 테이블의 데이터를 모두 로드하지는 않는다. 종종 데이터를 선택적으로 로드한다. 예를 들어, 매출이 $ 1,000 이상의 데이터만 가져 와야 한다면, WHERE 명령을 사용한다.

WHERE 문법

WHERE 명령은 다음과 같다.

SELECT 컬럼명1, 컬럼명2, ...
FROM 테이블명
WHERE 조건문;

WHERE 예제

만일 다음 테이블에서 매출이 $ 1,000 이상의 데이터를 가져 와야 하는 경우,

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 명령을 입력하면,

SELECT Store_Name
FROM Store_Information
WHERE Sales > 1000;

결과는 아래와 같다.

Store_Name
Los Angeles

1.2.4.4 - SQL 기본 | DML : 데이터 조작 언어 | AND, OR

앞 페이지에서는 WHERE 명령으로 테이블에서 조건의 데이터를 로드하는 방법에 대해 설명했다. 그 조건은 간단한 경우(이전 예)도 있고, 복잡한 경우도 있다. 복잡한 조건은 두개 이상의 간단한 조건을 AND 또는 OR에 의해 결합하는 것이다. 하나의 SQL 에서 갯수에 제한되지 없는 간단한 조건이 있다.

AND | OR 문법

AND | OR 명령은 다음과 같다.

SELECT "필드명"
FROM "테이블명"
WHERE "조건문"
{[AND|OR] "조건문"}+;

{}+{} 안의 조건이 하나 이상의 발생이 가능하다는 것을 의미한다. 여기에서는 AND에 조건문을 더하고, OR 조건문을 추가하는 상황이 하나 이상의 발생이 가능하다는 점을 보여준다. 또한, ()를 사용하여 조건의 우선 순서를 나타낸다.

AND | OR 예제

만일 다음 테이블에서 매출이 $ 1,000 이상의 데이터를 가져 와야 하는 경우,

만일 Store_Information 테이블에서 Sales가 $1,000 이상 또는 Sales가 $500과 $275 사이에 있는 데이터를 모두 가져 와야 하는 경우,

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
San Francisco 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 명령을 입력하면,

SELECT store_name
FROM store_information
WHERE Sales > 1000
OR (sales < 500 AND Sales > 275);

결과는 아래와 같다.

store_name
Los Angeles
San Francisco

1.2.4.5 - SQL 기본 | DML : 데이터 조작 언어 | IN, NOT IN

SQL에서 두 상황에 IN 명령을 사용하는데,이 페이지에는 그 중 하나인 WHERE와 관련된 상황에 대해 설명한다. 이 용법에는 필요한 값을 적어도 하나를 알아 두어야 한다. 그리고 알려진 모든 값을 IN 절에 입력된다.

IN, NOT IN 문법

IN 절은 다음과 같다.

SELECT "필드명"
FROM "테이블명"
WHERE "필드명" IN ('값1', '값2', ...);

괄호 안에 하나 이상의 값이 있고, 그 수치 상호간에 쉼표로 구분한다. 값은 숫자나 문자일 수 있다. 만약 괄호 안에 값이 하나만 있는 경우는 다음과 같다.

WHERE "필드명" = '값1'

IN 절의 반대로 조회할 경우는 NOT IN를 사용한다.

SELECT "필드명"
FROM "테이블명"
WHERE "필드명" NOT IN ('값1', '값2', ...);

IN 예제

예를 들어, 만일 store_information 테이블, Los Angeles 또는 San Diego가 포함된 데이터를 모두 가져오는 경우,

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 명령을 입력하면,

SELECT *
FROM store_information
WHERE store_name IN ('Los Angeles', 'San Diego');

결과는 아래와 같다.

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018

NOT IN 예제

이와 반대로 Los Angeles 또는 San Diego가 제외한 데이터를 모두 가져오는 경우,

SELECT *
FROM store_information
WHERE store_name NOT IN ('Los Angeles', 'San Diego');;

결과는 아래와 같다.

store_name sales txn_date
Boston 700 Jan-08-2018

1.2.4.6 - SQL 기본 | DML : 데이터 조작 언어 | BETWEEN

IN 명령은 하나 이상의 불연속(discrete) 값을 제한적으로 데이터베이스에서 해당 값을 가져온다. BETWEEN는 어느 정도의 범위(range)로 데이터베이스에서 해당 값을 가져온다.

BETWEEN 문법

SELECT "필드명"
FROM "테이블명"
WHERE "필드명" BETWEEN '값1' AND '값2';

여기에서 필드 값에서 값1과 값2 사이에 포함된 데이터를 조회할 수 있다.

BETWEEN 예제

예를 들어, 만일 store_information 테이블에서 January 6, 2018 및 January 10, 2018 사이에 있는 데이터를 가져 오는 경우,

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

참고 : 데이터베이스에 따라, 날짜를 저장하는 방법이 다를 수 있다. 여기에서 거론된 것은 그 저장 방법 중 하나이다.

아래와 같이 명령을 입력하면,

SELECT *
FROM Store_Information
WHERE Txn_Date BETWEEN 'Jan-06-2018' AND 'Jan-10-2018';

결과는 아래와 같다.

store_name sales txn_date
San Diego 250 Jan-07-2018
San Francisco 300 Jan-08-2018
Boston 700 Jan-08-2018

1.2.4.7 - SQL 기본 | DML : 데이터 조작 언어 | LIKE

LIKE는 WHERE 절에 사용되는 또 다른 명령이다. 기본적으로 LIKE를 사용하여, 어떤 패턴(pattern)에 따라 필요한 데이터를 찾을 수 있다.

LIKE 문법

LIKE 절은 문법은 다음과 같다.

SELECT "필드명"
FROM "테이블명"
WHERE "필드명" LIKE {패턴};

{패턴}는 와이드카드(wildcard)를 포함한다. 다음은 몇 가지 예이다.

  • ‘A_Z’ : ‘A’를 시작되고, 이어서 어떤 글자를 넣고, ‘Z’로 끝나는 문자열. ‘ABZ’와 ‘A2Z’는 이 패턴과 일치하지만 ‘AKKZ’는 그것과 일치하지 않는다(A와 Z사이에 두 글자가 한 글자가 아니기 때문).
  • ‘ABC%’ : ‘ABC’를 시작으로 하는 문자열. 예를 들면, ‘ABCD’와 ‘ABCABC’는이 패턴에 적합하다.
  • ’%XYZ’ : ‘XYZ’로 끝나는 문자열. 예를 들면, ‘WXYZ’와 ‘ZZXYZ’는 이 패턴에 적합하다.
  • ’%AN%’ : ‘AN’가 포함된 문자열. 예를 들면, ‘LOS ANGELES’와 ‘SAN FRANCISCO’는 이 패턴과 일치한다.

위의 마지막 예를 store_information 테이블에 사용하는 경우,

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 명령을 입력하면,

SELECT *
FROM Store_Information
WHERE Store_Name LIKE '%AN%';

결과는 아래와 같다.

store_name sales txn_date
LOS ANGELES 1500 Jan-05-2018
SAN DIEGO 250 Jan-07-2018
SAN FRANCISCO 300 Jan-08-2018

1.2.4.8 - SQL 기본 | DML : 데이터 조작 언어 | IS NULL, IS NOT NULL

필드 값이 비어 있는 경우 즉, NULL일때에 유무에 대한 조건이다.

IS NULL, IS NOT NULL 문법

IS NULL 조건은 다음과 같다.

SELECT "필드명"
FROM "테이블명"
WHERE "필드명" IS NULL

IS NULL 조건의 반대를 조회할 경우는 아래와 같다.

SELECT "필드명"
FROM "테이블명"
WHERE "필드명" IS NOT NULL

IS NULL 예제

아래와 같이 store_information 테이블이 있을 때, sales가 비어 있는 경우,

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston

아래와 같이 명령을 입력하면,

SELECT *
FROM store_information
WHERE sales IS NULL

결과는 아래와 조회된다.

store_name sales txn_date
Boston

IS NOT NULL 예제

sales가 비어 있는 않은 경우는

SELECT *
FROM store_information
WHERE sales IS NOT NULL

결과는 아래와 조회된다.

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018

1.2.4.9 - SQL 기본 | DML : 데이터 조작 언어 | EXISTS

EXISTS는 WHERE의 조건에서 실행한 결과가 있을 경우에 조회가 된다.

EXISTS 문법

SELECT "필드명1"
FROM "테이블명1"
WHERE EXISTS
(SELECT "필드명2" FROM "테이블명2" WHERE "조회 조건");

EXISTS 예제

아래와 같이 두개의 테이블이 있을 때, East 지역에 있는 매장의 정보를 조회하고 싶을 경우

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

geography 테이블

region_name store_name
East Boston
East New York
West Los Angeles
West San Diego

East 지역에 있는 매장의 정보를 조회하고 싶을 경우

SELECT *
FROM store_information A1
WHERE EXISTS
(SELECT * FROM geography WHERE region_name = 'East' AND store_name = A1.store_name);

결과는 아래와 같다.

store_name sales txn_date
Boston 700 Jan-08-2018

1.2.4.10 - SQL 기본 | DML : 데이터 조작 언어 | ORDER BY

ORDER BY 키워드는 결과 집합을 오름차순 또는 내림차순으로 정렬하는 데 사용된다.

ORDER BY 키워드는 레코드를 기본적으로 오름차순으로 정렬한다. 내림차순으로 레코드를 정렬하려면 DESC 키워드를 사용하면 된다.

ORDER BY 문법

SELECT 컬럼명1, 컬럼명2, ...
FROM 테이블명
ORDER BY 컬럼명1, 컬럼명2, ... ASC|DESC;

ORDER BY 예제

예제1

SELECT * FROM customers
ORDER BY country;

예제2

SELECT * FROM customers
ORDER BY country, customer_name;

예제3

SELECT * FROM customers
ORDER BY country ASC, customer_name DESC;

1.2.4.11 - SQL 기본 | DML : 데이터 조작 언어 | GROUP BY

GROUP BY 절은 같은 값끼리 그룹을 만드는 것이다.

함수의 이야기로 되돌아갑니다. SUM 명령을 사용하여 모든 Sales (판매)를 계산 했어요. 여기에서 각각의 가게 (Store_Name) 판매 (Sales)을 계산하는 경우, 어떻게하면 좋을까요. 그런 경우에는 할 일이 두 가지 : 첫째는, store_name 및 Sales 두 필드를 선택한다. 둘째는 모든 sales를 확인하기 위해 store_name에 따라 각각 계산한다. 그 프로그램은 다음과 같이됩니다.

GROUP BY 문법

SELECT "필드1", SUM("필드2")
FROM "테이블명"
GROUP BY "필드1";

예를 들어,

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 명령을 입력하면,

SELECT store_name, SUM(sales)
FROM store_Information
GROUP BY store_name;

결과는 아래와 같다.

store_name SUM(sales)
Los Angeles 1800
San Diego 250
Boston 700

필드를 하나 이상 선택하고, 적어도 그 중 하나에 함수의 계산이 포함된 경우 GROUP BY 명령을 사용하지 않으면 안된다. 이러한 경우, GROUP BY를 가진 다른 필드를 확인한다. 즉, 함수가 포함된 필드를 확인한 후에 그것을 GROUP BY 절에 넣는다.

1.2.4.12 - SQL 기본 | DML : 데이터 조작 언어 | HAVING

함수의 값에 조건을 넣는 방법을 설명한다.

예를 들어, 매출이 $1,500 이상 상점만을 조회를 하고 싶은 경우가 있다고 했을 때, WHERE 명령만으로는 할 수 없다. 그럴 경우에는 HAVING 같은 명령이 그 명령을 사용한다. 일반적으로 HAVING 절은 SQL 문장의 마지막에 있다. HAVING 절이 포함된 SQL에서 GROUP BY 절을 포함하는 것은 아니다.

HAVING 문법

HAVING 문의 문법은 아래와 같다.

SELECT "컬럼1", SUM("필드2")
FROM "테이블명"
GROUP BY "필드1"
HAVING (함수 조건);

주의 : GROUP BY 절이 반드시 필요한 것은 아니다.

HAVING 예제

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

위와 같은 테이블이 있다고 했을 때, 아래와 같이 명령문을 입력한다.

SELECT store_name, SUM(sales)
FROM store_Information
GROUP BY store_name
HAVING SUM(sales) > 1500;

위 명령문에 의한 결과는 아래와 같다.

store_name SUM(sales)
Los Angeles 1800

1.2.4.13 - SQL 기본 | DML : 데이터 조작 언어 | AS - Aliases

AS는 앨리어스(alias)의 약자이다. 테이블 또는 테이블의 필드에 임시 이름 즉, 별칭을 제공하는데 사용된다. 앨리어스는 컬럼 이름을 읽기 쉽게하기 위해 자주 사용되고, query가 실행 조회되는 동안에만 존재를 한다.

간단히 말하면, 필드 별칭은 SQL의 결과를 알기 쉽도록 사용되는 것이다. 예제에서 매출을 합하면 SUM(sales)라는 필드명이 나온다. 지금의 경우는 문제가 없지만, 만약 필드가 간단한 합계가 아니라 복잡한 합계가 된다면, 필드명은 같이 복잡해 진다. 이런 필드에 앨리어스을 사용하면 나올 수 있는 결과의 필드명이 알기 쉽게 확인할 수 있다.

두번째 별칭은 테이블 별칭이다. 테이블에 별칭을 붙이는 경우, FROM 절에 테이블 이름에 공백을 하나두고, 별칭을 지정한다. 그것은 SQL을 사용하여, 서로 다른 테이블에서 데이터를 읽어들이는데 매우 유용하다.

AS 문법

필드 및 테이블 별칭은 아래와 같이 지정한다.

SELECT "테이블 별칭"."필드1" (AS) "필드 별칭"
FROM "테이블명" (AS) "테이블 별칭";

기본적으로 이 두 별칭은 테이블명 및 필드 뒤에 공간이 하나 두고 지정한다. 또는 AS를 명시하기도 한다.

AS 예제

다음 아래 store_information 테이블을 예로 들어 보겠다.

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 GROUP BY 사용하여, 필드 별칭 및 테이블 별명을 추가한다.

SELECT A1.store_name Store, SUM(A1.sales) 'Total Sales'
FROM store_information A1
GROUP BY A1.store_name;

결과는 아래와 같다.

Store Total Sales
Los Angeles 1800
San Diego 250
Boston 700

결과에서 데이터 자체는 같지만, 필드의 제목이 바뀌었다. 이는 필드 별칭을 사용하는 것이다. 두번째 필드에서 “Sum (Sales)“이었던 제목 대신에 “Total Sales"이 명시된 제목이 표시되었다. 분명히, “Total Sales"는 “Sum(sales)“에 의해 필드의 의미를 보다 더 명확하게 보여줄 수 있는 것이다.

1.2.4.14 - SQL 기본 | DML : 데이터 조작 언어 | JOIN

JOIN는 테이블과 테이블 사이의 결합을 할 시에 사용한다.

다음은 SQL에있는 JOIN의 여러 유형이다.

  • (INNER) JOIN : 두 테이블에서 값이 일치하는 레코드를 반환한다.
  • LEFT (OUTER) JOIN : 왼쪽 테이블에서 모든 레코드를 반환하고, 오른쪽 테이블에서 일치하는 레코드를 반환한다.
  • RIGHT (OUTER) JOIN : 오른쪽 테이블에서 모든 레코드를 반환하고, 왼쪽 테이블에서 일치하는 레코드를 반환한다.
  • FULL (OUTER) JOIN : 왼쪽 또는 오른쪽 테이블의 모든 레코드를 반환한다.

SQL JOINS

추가적으로 아래와 같이 반환되는 값을 조절할 수도 있다.

SQL JOINS

JOIN 문법

INNER JOIN

SELECT column_name(s)
FROM table1
INNER JOIN table2 ON table1.column_name = table2.column_name;

LEFT (OUTER) JOIN

SELECT column_name(s)
FROM table1
LEFT JOIN table2 ON table1.column_name = table2.column_name;

RIGHT (OUTER) JOIN

SELECT column_name(s)
FROM table1
RIGHT JOIN table2 ON table1.column_name = table2.column_name;

FULL (OUTER) JOIN

SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2 ON table1.column_name = table2.column_name;

INNER JOIN 예제

SQL의 결합(JOIN)에 대한 예제이다.

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

geography 테이블

region_name store_name
East Boston
East New York
West Los Angeles
West San Diego

내부 조인 (inner join)는 두 테이블에서 일치하는 값이 있어야, 해당 데이터가 조회 된다.

지역(region_name) 별로 매출(Sales)을 조사한다. geography 테이블에 각 지역의 상점, store_information 테이블에 각 점포의 매출이 표시된다. 만약 지역별 매출을 살펴보면 그 두 테이블의 데이터를 결합한다. 자세히 보면 필드 store_name의 하나로,이 두 테이블을 조인 할 수 있는 것을 알 수 있다.

SELECT A1.region_name REGION, SUM(A2.sales) SALES
FROM geography A1 JOIN store_information A2 ON A1.store_name = A2.store_name
GROUP BY A1.region_name;

혹은

SELECT A1.region_name REGION, SUM(A2.sales) SALES
FROM geography A1, store_information A2
WHERE A1.store_name = A2.store_name
GROUP BY A1.region_name;

결과는 아래와 같다.

REGION SALES
East 700
West 2050

첫번째 줄 곳에서 SQL을 사용하여 두 필드를 선택한다. 첫째는 geography 테이블의 region_name 필드 (일명 REGION), 두 번째는 store_information 테이블의 sales 필드(일명 SALES). 여기에서 사용되는 테이블 별칭은 Geography 테이블는 A1으로 store_information 테이블의 별칭은 A2으로 지정하였다. 테이블 별칭을 사용하지 않으면, 첫번째 행은 다음과 같게 된다.

SELECT Geography.region_name REGION, SUM(Store_Information.Sales) SALES

이는 보면 알 수 있듯이 테이블 명을 모두 표시하기 길어지고 복잡해 진다. 여기에서 본 테이블 별칭의 용도는 SQL 문이 알기 쉽게 될 수 있으며, 특히 여러 테이블을 포함하는 SQL 문장의 경우 더 유용해 진다.

이어서 두번째 줄에서 FROM 절을 보면, geography 테이블의 store_name 필드와 store_information 테이블의 store_name를 JOIN으로 연결을 하고 있다.

OUTER JOIN 예제

한 테이블있는 데이터 값이 다른 테이블에 하나도 나타나지 않는 경우 OUTER JOIN(외부 조인)를 사용한다.

외부 조인는 데이터베이스에 따라 다른 경우도 있다. 예를 들어, Oracle은 테이블의 데이터가 필요하다는 것을 나타내기 위해 WHERE 절에 로드된 모든 테이블 후에 “(+)“를 넣는다.

각 상점의 매출을 조회한다. 만약 내부 조인을 하면 store_information 테이블에 존재하지 않는 ‘New York’ 매장의 데이터가 누락이 되어 버린다. 그래서 그런 경우 외부 조인을 사용한다.

여기에 사용된 Oracle의 외부 조인는 다음과 같다.

SELECT A1.store_name, SUM(A2.sales) SALES
FROM geography A1, store_information A2
WHERE A1.store_name = A2.store_name (+)
GROUP BY A1.store_name;

결과는 아래와 같다.

store_name SALES
Boston 700
New York
Los Angeles 1800
San Diego 250

두번째 테이블에 해당 데이터가 없는 경우 SQL에서 NULL 값이 들어간다. 이 예제에서 ‘New York’가 store_information 테이블에 존재하지 않기 때문에, 그 “SALES"필드가 NULL이 된다.

1.2.4.15 - SQL 기본 | DML : 데이터 조작 언어 | Subquery

하나의 SQL 문에, 또 다른 SQL 문을 넣을 수 있다. 그리고 WHERE 절 또는 HAVING 절에 다른 SQL 문을 삽입한 경우, subquery 구문이 넣을 수 있다. Subquery는 먼저 테이블의 결합에 사용된다. 그리고 때로는 subquery가 두 테이블을 조인하는 유일한 방법이다.

Subquery

Subquery은 다음과 같다.

SELECT "필드1" FROM "테이블1"
WHERE "필드2" [비교 연산자] (SELECT "필드1" FROM "테이블2" WHERE "조건");

비교 연산자는 =,>, <,> =, <= 등의 연산자가 있고, “LIKE"등과 같은 문자에 대한 연산자가 있다.

Subquery 예제

SQL의 결합에 대한 예제이다.

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

geography 테이블

region_name store_name
East Boston
East New York
West Los Angeles
West San Diego

subquery를 이용하여 West에 있는 가게의 매출을 모두 조사하는 경우, 다음 SQL로 사용할 수 있다.

SELECT SUM(sales) FROM store_information
WHERE store_name IN
(SELECT store_name FROM geography
WHERE region_name = 'West');

결과는 아래와 같다.

SUM(sales)
2050

이 예제에서는 두 개의 테이블을 직접 결합하지 않지만, West에 있는 가게의 매출을 모두 직접 계산할 수 있다. 그것은 먼저 어떤 가게가 West에 있는지 확인한다. 그리고, 그 가게의 매상을 계산하고 합계를 구한다.

1.2.4.16 - SQL 기본 | DML : 데이터 조작 언어 | UNION, UNION ALL

UNION 연산자는 두 개 이상의 SELECT 문의 결과 집합을 결합하는 데 사용된다.

UNION은 두개의 SQL 문의 결과를 결합하는데 사용되는 명령이다. 그러고 보면 UNION는 JOIN와 다소 유사하다. 이 두 명령 모두 여러 테이블에서 데이터를 가져올 수 있기 때문이다. 그러나 UNION이 제한되는 것은 두개의 SQL 문에서 만들어진 필드가 동일한 데이터 유형에 사용되어야 한다. 또한 UNION 명령을 사용하는 경우 중복 데이터는 출력되지 않는다(SELECT DISTINCT와 유사).

UNION 생성 조건

  • UNION 내의 각 SELECT 문은 같은 수의 열을 가져야 한다.
  • 열은 유사한 데이터 형식을 가져야 한다.
  • 각 SELECT 문의 열은 또한 동일한 순서로 있어야 한다.

UNION ALL도 두 SQL 문의 결과를 결합하는데 사용되는 명령이다. UNION ALL가 UNION과 다른 점은 UNION ALL은 데이터 값이 중복되더라도 조건에 일치하는 데이터를 모두 표시한다.

UNION, UNION ALL 문법

UNION는 아래와 같이 사용한다.

UNION 문법

[SQL문1]
UNION
[SQL문2];

여기서 SQL문이 두개가 들어간다.

UNION ALL는 아래와 같이 사용한다.

UNION ALL 문법

[SQL문1]
UNION ALL
[SQL문2];

ALL를 붙은거 말고는 UNION과 모두 동일하다.

UNION 예제

그럼 다음과 같은 두 테이블이 있다고 하자.

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

internet_sales 테이블

txn_date sales
Jan-07-2018 250
Jan-10-2018 535
Jan-11-2018 320
Jan-12-2018 750

여기서 매출(sales)의 날짜를 모두 확인하는 경우에는 다음의 SQL 문을 사용할 수 있다.

SELECT txn_date FROM store_information
UNION
SELECT txn_date FROM internet_sales;

실행 결과는 아래와 같다.

txn_date
Jan-05-2018
Jan-07-2018
Jan-08-2018
Jan-10-2018
Jan-11-2018
Jan-12-2018

하나 알아야 할 점은 SQL 문(또는 두 절에서 사용 가능한 경우)에 SELECT DISTINCT txn_date를 사용하면 같은 결과가 나온다.

UNION ALL 예제

그리고 상점 판매 및 인터넷 매출이 있는 날짜를 조회해 보도록 한다. 그럴 경우에 다음 SQL 문을 사용한다.

SELECT txn_date FROM store_information
UNION ALL
SELECT txn_date FROM internet_sales;

결과는 아래와 같다.

txn_date
Jan-05-2018
Jan-07-2018
Jan-08-2018
Jan-08-2018
Jan-07-2018
Jan-10-2018
Jan-11-2018
Jan-12-2018

1.2.4.17 - SQL 기본 | DML : 데이터 조작 언어 | INTERSECT

INTERSECT는 UNION 명령과 유사하고, 두 SQL 문의 결과를 처리하는데 사용되는 명령이다. 2개가 다른 점은 UNION은 기본적으로 OR이며(설정된 값이 첫번째 구문 또는 두번째 구문에 있는 경우, 해당 값이 선택되어 출력한다), INTERSECT는 AND에 가깝다(설정된 값이 첫번째 구문과 두번째 구문에 존재하는 경우에만 선택된다). UNION은 결합이고, INTERSECT 교차이다.

INTERSECT 문법

INTERSECT 명령은 다음과 같다.

[SQL문1]
INTERSECT
[SQL문2];

INTERSECT 예제

다음과 같은 두 테이블이 있다고 하자.

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

internet_sales 테이블

txn_date sales
Jan-07-2018 250
Jan-10-2018 535
Jan-11-2018 320
Jan-12-2018 750

여기서 어떤 날에 상점 판매와 인터넷 판매가 있는지 확인하기 위해 다음과 같은 SQL 문을 사용할 수 있다.

SELECT txn_date FROM store_information
INTERSECT
SELECT txn_date FROM internet_sales;

SQL 구문 실행 결과는 아래와 같다.

txn_date
Jan-07-2018

INTERSECT 명령은 다른 값이 한 번 밖에 나타나지 않는다는 점에 유의한다.

1.2.4.18 - SQL 기본 | DML : 데이터 조작 언어 | MINUS

MINUS는 두개의 SQL 문에 사용되는 명령이다. MINUS는 먼저 첫번째 SQL문 결과를 확인한다. 그리고, 그 검색한 결과가 두번째 SQL문 결과에 있는지 여부를 확인한다. 만약 있다면 그 데이터가 삭제되고 최종 결과는 나오지 않는다. 만약 두 SQL 구문의 결과가 첫번째 SQL 구문의 결과에 없을 경우 해당 데이터가 제외로 처리된다.

MINUS 문법

MINUS 명령은 다음과 같다.

[SQL문1]
MINUS
[SQL문2];

MINUS 예제

아래와 같이 두개의 테이블이 있다고 하자.

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

internet_sales 테이블

txn_date sales
Jan-07-2018 250
Jan-10-2018 535
Jan-11-2018 320
Jan-12-2018 750

여기서 어느 날이 가게의 매출이 있고 인터넷 매출이 없는지 확인하기 위해 다음과 같은 SQL 문을 사용할 수 있다.

SELECT txn_date FROM store_information
MINUS
SELECT txn_date FROM Internet_sales;

SQL 구문 실행 결과는 아래와 같다.

txn_date
Jan-05-2018
Jan-08-2018

‘Jan-05-2018’, ‘Jan-07-2018’, 그리고 ‘Jan-08-2018’는 SELECT txn_date FROM store_information에 의해 나온 결과이다. 그 중에 ‘Jan-07-2018’ 은 SELECT txn_date FROM internet_sales의 결과로 존재하는 것으로, 최종 결과는 나오지 않는다.

MINUS 명령문은 명령문 바탕으로 다른 값이 한 번 밖에 나타나지 않는다는 점에 유의하도록 한다.

1.2.4.19 - SQL 기본 | DML : 데이터 조작 언어 | INSERT

기본적으로 데이터를 테이블에 입력하는 방법이 두 가지 있다. 하나는 한번에 하나의 데이터를 등록한다. 또 하나는 한번에 여러 개의 데이터를 입력한다.

INSERT 문법

제일 기본이 되는 데이터를 등록하는 SQL는 아래와 같다.

INSERT INTO "테이블명" ("필드1", "필드2", ...)
VALUES ("값1", "값2", ...);

테이블이 생성된 필드의 순서와 입력한 값의 순서만 맞다면 필드명은 생략할 수도 있다.

INSERT INTO "테이블명"
VALUES ("값1", "값2", ...);

SELECT로 한번에 여러 개의 데이터를 입력 할 수도 있다. 앞에서 언급한 예제와 달리 SELECT 명령을 사용하여 입력하는 테이블의 데이터를 명시합니다. 그러면 데이터가 다른 테이블에서 온 건가요,라는 질문이 있으시면, 전혀 그렇지. 한 번에 여러 개의 데이터를 입력하는 SQL은 다음과 같다.

INSERT INTO table2
SELECT * FROM table1
WHERE condition;

여기서 가장 기본 형식으로 SQL 문에서 WHERE, GROUP BY 및 HAVING 등 외에 테이블의 결합이나 별명 등이 포함되는 것도 가능하다.

INSERT 예제: 한번에 1건 등록

다음과 같은 구조의 테이블이 있다고 하자.

store_information 테이블

필드명 자료형
store_name char(50)
sales float
txn_date datetime

그리고 다음에 Los Angeles 매장에 Jan-10-2018, 매출 $900 데이터를 해당 테이블에 넣는 경우 다음과 같은 SQL 문을 입력한다.

INSERT INTO store_information (store_name, sales, txn_Date)
VALUES ('Los Angeles', 900, 'Jan-10-2018');

혹은, 아래와 같이도 입력할 수 있다.

INSERT INTO store_information VALUES ('Los Angeles', 900, 'Jan-10-2018');

INSERT 예제: 한번에 여러 건 등록

2017년의 판매한 데이터를 store_information 테이블에 등록한다고 할때, 그 데이터가 sales_information 테이블에서 얻어서 넣은 경우 다음과 같은 SQL을 입력한다.

INSERT INTO store_information (store_name, sales, txn_date)
SELECT store_name, sales, txn_date
FROM sales_information
WHERE Year(txn_date) = 2017;

여기에서 SQL Server의 함수에서 날짜의 연도를 검색했는데, 데이터베이스에 따라 문법이 다르다. 예를 들어, Oracle에서는 WHERE TO_CHAR(Txn_Date, 'yyyy') = 2017와 같이 사용한다.

1.2.4.20 - SQL 기본 | DML : 데이터 조작 언어 | UPDATE

테이블의 데이터를 수정하는 경우에 UPDATE 명령을 사용한다.

UPDATE 문법

UPDATE문의 기본 SQL은 아래와 같다.

UPDATE "테이블명"
SET "필드1" = [새로운 ]
WHERE "조건";

한번에 여러 필드를 동시에 수정할 수도 있다.

UPDATE "테이블명"
SET "필드1" = [값1], "필드2" = [값2]
WHERE "조건";

UPDATE 예제

아래와 같이 테이블이 있다고 하자.

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

Los Angeles 매장에서 2018/01/08의 실제 매출이 테이블에 저장된 $300이 아니라, $500이라는 것을 뒤늦게 발견하여, 다음과 같은 SQL을 사용하여 데이터를 수정한다.

UPDATE store_information
SET Sales = 500
WHERE Store_Name = 'Los Angeles'
AND Txn_Date = 'Jan-08-2018';

테이블을 다시 조회해 보면 아래와 같이 수정된 것을 확인할 수 있다.

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 500 Jan-08-2018
Boston 700 Jan-08-2018

이 예제에서는 하나의 데이터만 WHERE 절의 조건에 일치한다. 만약 여러 데이터가 그 조건에 일치하는 경우 일치하는 모든 데이터가 수정된다.

1.2.4.21 - SQL 기본 | DML : 데이터 조작 언어 | DELETE

데이터베이스에서 일부 데이터를 직접 삭제할 수 있다. 삭제는 DELETE를 사용한다.

DELETE 문법

DELETE FROM "테이블명"
WHERE "조건";

DELETE 예제

그럼 예를 들어 설명한다. 가령 다음과 같은 테이블이 있다고 하자.

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

여기서 Los Angeles에 대한 데이터를 모두 삭제할 시에는 다음과 같은 SQL 문을 사용한다.

DELETE FROM store_information
WHERE store_name = 'Los Angeles';

이 SQL를 실행하면 테이블은 아래와 같이 된다.

store_name sales txn_date
San Diego 250 Jan-07-2018
Boston 700 Jan-08-2018

1.2.5 - SQL 기본 | 함수

데이터베이스에서 많은 데이터가 숫자로 나타난 숫자 연산 있는 것이 함수이다. 예를 들어, 그 숫자를 합을 구하거나, 평균값을 계산하는 등이다.

  • AVG(평균)
  • COUNT(카운트)
  • MAX(최대 값)
  • MIN(최소값)
  • SUM(합계)

함수 문법

함수 사용은 다음과 같다.

SELECT "함수명"("필드명")
FROM "테이블명";

1.2.5.1 - SQL 기본 | 함수 | AVG(평균)

AVG는 테이블에 조회되는 데이터의 평균을 구한다

AVG(평균) 문법

SELECT AVG("필드명")
FROM "테이블명";

AVG(평균) 예제

예를 들어, 아래 테이블의 sales 필드의 평균이 몇인지 알아내는 경우

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 명령을 입력하면,

SELECT AVG(sales)
FROM store_information

결과는 아래와 같다.

AVG(sales)
687.5000

1.2.5.2 - SQL 기본 | 함수 | COUNT(카운트)

COUNT는 테이블에 조회되는 데이터가 얼마나 있는지, 갯수를 조회할 수 있다.

COUNT(카운트) 문법

SELECT COUNT("필드명")
FROM "테이블명";

COUNT(카운트) 예제

예를 들어, 아래 테이블의 store_name 필드에 공백 데이터가 아닌 것이 몇개가 있는지 알아내는 경우

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 명령을 입력하면,

SELECT COUNT(store_name)
FROM store_information
WHERE store_name IS NOT NULL;

결과는 아래와 같다.

COUNT(store_name)
4

“IS NOT NULL"는 이 필드가 NULL 즉, 비어 있지 않는다는 것을 나타낸다.

COUNT와 DISTINCT를 같이 사용되는 경우는 테이블에서 같은 데이터는 제거하고 몇개인지 조회할 때 사용된다. 예를 들명, 테이블의 store_name가 몇 개인지를 조회하는 경우 다음과 같이 입력한다.

SELECT COUNT(DISTINCT store_name)
FROM store_information;

결과는 아래와 같다.

COUNT(DISTINCT store_name)
3

1.2.5.3 - SQL 기본 | 함수 | MAX(최대값), MIN(최소값)

MAX와 MIN는 테이블에 조회되는 데이터의 최대값 및 최소값을 구한다

MAX(최대값), MIN(최소값) 문법

최대값을 구하는 함수는 아래와 같다.

SELECT MAX("필드명")
FROM "테이블명";

최소값을 구하는 함수는 아래와 같다.

SELECT MIN("필드명")
FROM "테이블명";

AVG() 예제

예를 들어, 아래 테이블의 sales 필드의 최대값 및 최소값이 몇인지 알아내는 경우

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 명령을 입력하면,

SELECT MAX(sales), MIN(sales)
FROM store_information

결과를 아래와 구할 수 있다.

MAX(sales) MIN(sales)
1500 250

1.2.5.4 - SQL 기본 | 함수 | SUM(합계)

SUM는 테이블에 조회되는 데이터의 합계를 조회할 수 있다.

SUM(합계) 문법

SELECT SUM("필드명")
FROM "테이블명";

SUM(합계) 예제

예를 들어, 예제 테이블에 있는 sales 필드의 합계를 계산하는 경우,

store_information 테이블

store_name sales txn_date
Los Angeles 1500 Jan-05-2018
San Diego 250 Jan-07-2018
Los Angeles 300 Jan-08-2018
Boston 700 Jan-08-2018

아래와 같이 명령을 입력하면,

SELECT SUM(sales) FROM store_information;

아래와 같이 명령을 입력하면,

SUM(sales)
2750

2750은 모든 sales 필드의 합계 : 1500 + 250 + 300 + 700.

함수 외에, SQL을 사용하여 더하기(+)와 마이너스(-)의 간단한 수학 연산도 가능하다. 문자의 데이터 내용은 SQL에도 문자 처리 함수가 몇개 있다. 예를 들어, 문자열 연결(concatenation), 문자 잘라 내기(trim) 및 부분 문자열 검색(substring). 데이터베이스에 따라 함수가 다르다. 그 함수의 사용법을 확인하기 위해, 사용하는 데이터베이스의 정보를 참고하면 된다.

1.2.5.5 - SQL 기본 | 함수 | Substring : 문자열 일부 추출

SQL에서 함수 substring은 하나의 필드 데이터의 일부를 읽는데 사용된다. 데이터베이스에 의해 이 함수명이 다르다.

  • MySQL : SUBSTR(), SUBSTRING()
  • Oracle : SUBSTR()
  • SQL Server : SUBSTRING()

일반적으로 사용되는 경우는 다음과 같다. 여기서, SUBSTR()를 예로 한다.

SUBSTR(str, pos)

str에서 pos 번째 위치에서 모든 문자를 읽어들입니다. 이 프로그램이 SQL Server에 적용되지 않는다는 점에 유의하십시오.

SUBSTR(str, pos, len)

str에서 pos 번째 위치에서 len 개의 문자를 읽어 들인다.

Substring 예제

가령 다음과 같은 테이블이 있다고 하자.

geography 테이블

region_name store_name
East Boston
East New York
West Los Angeles
West San Diego

예제 1

SELECT SUBSTR(store_name, 3) 
FROM geography 
WHERE store_name = 'Los Angeles';

결과는 아래와 같다.

's Angeles'

예제 2

SELECT SUBSTR(store_name, 2, 4) 
FROM geography 
WHERE store_name = 'San Diego';

결과는 아래와 같다.

'an D'

1.2.5.6 - SQL 기본 | 함수 | Concatenate : 문자열 결합

다른 필드에서 데이터를 결합하는 경우가 있다. 각각의 데이터베이스에이 목적을 달성하기위한 방법이 있다.

  • MySQL : CONCAT()
  • Oracle : CONCAT(), ||
  • SQL Server : +

Concatenate 문법

CONCAT()은 다음과 사용한다.

CONCAT(문자열1, 문자열2, 문자열3, ...)

문자열1, 문자열2, 문자열3…를 결합한다. Oracle의 CONCAT()에는 두개의 매개 변수가 허용된다는 점에 주의한다. 즉, 한번에 두 문자열만 결합할 수 있다. 한번에 여러 문자를 결합은 ‘||‘를 사용하여 결합할 수 있다.

Concatenate 예제

예를 들어, 다음과 같은 테이블이 있다고 하자.

geography 테이블

region_name store_name
East Boston
East New York
West Los Angeles
West San Diego

예제 1

MySQL / Oracle :

SELECT CONCAT(region_name, store_name) FROM geography 
WHERE store_name = 'Boston';

결과는 아래와 같다.

'EastBoston'

예제 2

Oracle :

SELECT region_name || ' ' || store_name FROM geography 
WHERE store_name = 'Boston';

결과는 아래와 같다.

'East Boston'

예제 3

SQL Server :

SELECT region_name + ' ' + store_name FROM geography 
WHERE store_name = 'Boston';

결과는 아래와 같다.

'East Boston'

1.2.5.7 - SQL 기본 | 함수 | Trim : 문자열 공백 제거

SQL 함수 TRIM는 하나의 문자열의 시작 또는 끝 부분을 제거하는 데 사용된다. 흔히 볼 수 있는 것은 데이터베이스에 의해 이 함수명이 다르다.

  • MySQL : TRIM(), RTRIM(), LTRIM()
  • Oracle : RTRIM() LTRIM()
  • SQL Server : RTRIM(), LTRIM()

각각의 trim은 다음과 같습니다.

TRIM([[위치] [제거 문자열] FROM] 문자열)

[위치]의 입력되는 값은 LEADING(시작 부분), TRAILING(끝) 또는 BOTH(시작 부분과 끝 부분) 이다. 이 함수는 [제거 문자열]을 문자열의 시작 부분, 끝 부분 또는 시작 부분과 끝에서 제거한다. [제거 문자열]이 표시되지 않으면 공백이 제거된다.

LTRIM (문자열)

문자열의 시작 부분의 공백을 제거한다.

RTRIM (문자열)

문자열의 끝 부분의 공백을 제거한다.

Trim 예제

TRIM() 예제

SELECT TRIM('   Sample   ');

결과는 아래와 같다.

'Sample'

LTRIM() 예제

SELECT LTRIM('   Sample   ');

결과는 아래와 같다.

'Sample   '

RTRIM() 예제

SELECT RTRIM ('   Sample   ');

결과는 아래와 같다.

'   Sample'

1.2.5.8 - SQL 기본 | 함수 | Replace : 문자열 치환

SQL에서 함수 Replace는 하나의 필드 데이터의 일부를 치환하는데 사용된다. 데이터베이스에 의해 이 함수명이 다르다.

  • MySQL : REPLACE()

Replace 문법

REPLACE()은 다음과 사용한다.

REPLACE('문자열', '치환할 문자열', '치환될 문자열')

Replace 예제

SELECT REPLACE('abcdefg', 'abc', 'aaa');

결과는 아래와 같다.

| REPLACE('abcdefg', 'abc', 'aaa') |
|--|
| aaadefg |

1.3 - MySQL

MySQL은 전 세계적으로 널리 이용되고 있는 데이터베이스의 하나로 현재 Oracle에서 개발이 진행하고 있다. 여기에서 MySQL을 앞으로 사용하시는 분들을 대상으로 한 MySQL 소개하려고 한다. MySQL을 로컬 환경에 설치하는 방법과 MySQL을 사용하여 데이터베이스, 테이블, 사용자 트리거 등을 만드는 방법에 대해 간단한 예제를 사용하여 직접 체험하면서 하나 하나 설명하는 것이다.

1.3.1 - MySQL | MySQL 설치

현재 MySQL은 MySQL Enterprise Edition과 MySQL Community Server의 두 가지 버전이 있다. MySQL Enterprise는 상용 버전의 MySQL으로 공식 지원의 제공과 관리 도구를 포함되어 있다. 이에 반해 MySQL Community Server는 무료 데이터베이스이다. 여기에서는 MySQL Community Server를 Docker에서 설치 방법에 대해 설명한다.

Docker 버전 확인

당연한 말이겠지만 먼저 도커가 설치가 되어 있는지 확인을 해야 한다.

docker --version

실행하면 아래와 같이 버전이 확인이 도커가 잘 설치된 것이다.

 % docker --version
Docker version 20.10.8, build 3967b7d

MySQL Docker 이미지 다운로드

아래 명령어를 실행하여 Elasticsearch Docker 이미지를 다운로드 받는다.

docker pull mysql

실행하면 아래와 출력될 것이다.

% docker pull mysql
Using default tag: latest
latest: Pulling from library/mysql
07aded7c29c6: Pull complete
f68b8cbd22de: Pull complete
30c1754a28c4: Pull complete
1b7cb4d6fe05: Pull complete
79a41dc56b9a: Pull complete
00a75e3842fb: Pull complete
b36a6919c217: Pull complete
635b0b84d686: Pull complete
6d24c7242d02: Pull complete
5be6c5edf16f: Pull complete
cb35eac1242c: Pull complete
a573d4e1c407: Pull complete
Digest: sha256:4fcf5df6c46c80db19675a5c067e737c1bc8b0e78e94e816a778ae2c6577213d
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest

위와 같이 버전을 입력하지 않고 실행하게 되면 최신 버전의 MySQL Docker 이미지를 다운받게 된다. 특정 version을 설치하기 원한다면 아래와 같이 버전을 기입하면 된다.

docker pull mysql:{version}

MySQL Docker 컨테이너 실행

아래 명령어를 실행하여 컨테이너의 실행을 한다.

docker run --name mysql -e MYSQL_ROOT_PASSWORD=1234 -d -p 3306:3306 mysql:latest

여기서 도커 컨테이너명은 --name 옵션으로 mysql 이라고 지정하였다. 다른 이름으로 해도 상관 없다.

% docker run --name mysql -e MYSQL_ROOT_PASSWORD=1234 -d -p 3306:3306 mysql:latest
79c4333a40db5621a9cb7051fac2a8702c09d0f2706223cf65a5c448b08cb061

MySQL Docker 컨테이너 실행 확인

아래 명령어를 실행하여 컨테이너의 실행을 확인한다.

docker ps

실행되면 MySQL가 아래와 같이 실핼되고 있는 것을 확인 할 수 있다.

% docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                                  NAMES
79c4333a40db   mysql:latest   "docker-entrypoint.s…"   40 seconds ago   Up 39 seconds   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   mysql

MySQL Docker 컨테이너 시작/중지/재시작

MySQL Docker 컨테이너 중지

$ docker stop mysql

MySQL Docker 컨테이너 시작

$ docker start mysql

MySQL Docker 컨테이너 재시작

$ docker restart mysql

MySQL Docker 컨테이너 접속

docker exec -it mysql bash
% docker exec -it mysql bash
root@79c4333a40db:/# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.26 MySQL Community Server - GPL

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.01 sec)

mysql> ^C
mysql>

1.3.2 - MySQL | 콘솔창에서 데이터베이스 접속

콘솔 혹은 telnet환경에서 mysql 접속명령, 콘솔창에서 디비 접속하는 방법에 대해서 살펴보자.

기본 접속 방법

$ mysql -u {사용자명} -p{비밀번호} {데이터베이스명}

주의 : “-p{비밀번호}“에는 공백이 없다. 비밀번호가 1234라면 “-p1234"라고 입력해야 한다.

그밖에 접속 방법

mysql -u {사용자명} -p {데이터베이스명}
mysql -u root (계정과 비번을 만들지 않았을때)
mysql -u root -p(루트 비번이 있을때)

외부 파일로 저장된 쿼리 실행

외부 파일에 저장된 쿼리가 실행하는 방법은 아래와 같다.

$ mysql -u {UserName} -p{Password} {Database} < {외부파일명}

이 방법은 SQL 문으로 export된 파일을 import할때 이 명령으로 주로 사용된다.

사용 예제

외부 파일(/Users/devkuma/test.sql)를 먼저 생성한다.

SELECT * FROM store_information

mysql 명령어가 저장된 디렉토리로 이동한다.

$ cd /usr/local/mysql/bin/

명령어를 사용하여 SQL 외부 파일을 실행한다.

$ ./mysql -u root -p1234 devkuma < /Users/devkuma/test.sql 
Warning: Using a password on the command line interface can be insecure.
region_name	store_name	sales	txn_date
West	Los Angeles	1500	1999-01-05
West	San Diego	250	1999-01-07
West	Los Angeles	300	1999-01-08
East	Boston	700	1999-01-08

현재상태보기

mysql> status

사용 예제

mysql> status
--------------
./mysql  Ver 14.14 Distrib 5.6.17, for osx10.7 (x86_64) using  EditLine wrapper

Connection id:		1068
Current database:	
Current user:		root@localhost
SSL:			Not in use
Current pager:		stdout
Using outfile:		''
Using delimiter:	;
Server version:		5.6.17 MySQL Community Server (GPL)
Protocol version:	10
Connection:		Localhost via UNIX socket
Server characterset:	utf8
Db     characterset:	utf8
Client characterset:	utf8
Conn.  characterset:	utf8
UNIX socket:		/tmp/mysql.sock
Uptime:			1 hour 9 min 39 sec

Threads: 5  Questions: 4186  Slow queries: 0  Opens: 128  Flush tables: 1  Open tables: 107  Queries per second avg: 1.001
--------------

데이터베이스 목록 조회

mysql> status

사용 예제

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| devkuma            |
| mysampledata       |
| mysql              |
| performance_schema |
+--------------------+
5 rows in set (0.00 sec)

사용할 데이터베이스 선택

mysql> use {데이터베이스명}

사용 예제

mysql> use devkuma
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

테이블 목록 조회

mysql> show tables;

사용 예제

mysql> show tables;
+----------------------------+
| Tables_in_devkuma          |
+----------------------------+
| geography                  |
| store_information          |
+----------------------------+
46 rows in set (0.00 sec)

접속 나가기

mysql> exit
Bye

혹은 Ctrl+C

1.3.3 - MySQL | 버전 확인

MySQL의 버전을 확인하기 위해서는 커맨드로 확인 하는 방법과 쿼리로 확인하는 방법이 있다.

Command로 확인하는 방법

$ cd /usr/local/mysql/bin
$ ./mysql -V
./mysql  Ver 14.14 Distrib 5.6.17, for osx10.7 (x86_64) using  EditLine wrapper

또는

$ ./mysql --version
./mysql  Ver 14.14 Distrib 5.6.17, for osx10.7 (x86_64) using  EditLine wrapper

위에 커맨드 명령어는 엄밀히 말하면 mysql 클라이언트의 버전을 체크하는 방법이기에 버전 외에 설치된 클라이언트 정보도 같이 표시된다.

Query로 확인하는 방법

서버에 접속하여 아래와 같이 쿼리로 실행시킨다.

mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.6.17    |
+-----------+
1 row in set (0.00 sec)

또는

mysql> SHOW VARIABLES LIKE '%VERSION%';
+-------------------------+------------------------------+
| Variable_name           | Value                        |
+-------------------------+------------------------------+
| innodb_version          | 5.6.17                       |
| protocol_version        | 10                           |
| slave_type_conversions  |                              |
| version                 | 5.6.17                       |
| version_comment         | MySQL Community Server (GPL) |
| version_compile_machine | x86_64                       |
| version_compile_os      | osx10.7                      |
+-------------------------+------------------------------+
7 rows in set (0.00 sec)

1.3.4 - MySQL | 테이블 생성, 컬럼 추가, 삭제, 수정

테이블 생성

CREATE TABLE [테이블명](id int, name varchar(20));

테이블명 수정

RENAME TABLE [테이블명] TO [새 테이블명]
ALTER TABLE [테이블명] RENAME [변경할 테이블명];

테이블 삭제

DROP TABLE [테이블명];

컬럼 추가

ALTER TABLE [테이블명] ADD [컬럼명] [타입] [옵션]; 
ex) ALTER TABLE [테이블명] ADD [컬럼명] varchar(100) not null default '0'; 

컬럼 삭제

alter TABLE [테이블명] drop [컬럼명];

컬럼명 변경 및 타입 변경

ALTER TABLE [테이블명] change [컬럼명] [변경할컬럼명] varchar(12);

컬럼 타입 수정

ALTER TABLE [테이블명] MODIFY [컬럼명] varchar(14);

1.3.5 - MySQL | 테이블 생성 (DEFAULT timestamp 관련) 에러

MySQL 5.5 이하 버전에서는 테이블을 생성 시에 DEFAULT 값을 아래와 같이 하나의 TIMESTAMP 필드일 경우는 문제지만

CREATE TABLE table_a (
  time_a timestamp DEFAULT CURRENT_TIMESTAMP
)

두개의 TIMESTAMP를 사용하는 경우 에러가 발생된다.

CREATE TABLE table_a (
  time_a timestamp DEFAULT CURRENT_TIMESTAMP,
  time_b timestamp DEFAULT CURRENT_TIMESTAMP
)

에러 메세지는 아래와 같다.

Error Code : 1293
Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause

이는 테이블 하나에 TIMESTAMP 타입은 컬럼 한개에만 DEFAULT로 CURRENT_TIMESTAMP를 지정할 수 있다는 내용이다.

이런 경우는 아래와 같이 하나의 필드에만 DEFAULT 로 CURRENT_TIMESTAMP를 지정해 주고, 다른 하나에 컬럼에 INSERT시 CURRENT_TIMESTAMP로 초기화 시켜주어야 한다.

CREATE TABLE table_a (
  time_a timestamp DEFAULT CURRENT_TIMESTAMP,
  time_b timestamp
)

이 제한 사항은 5.6부터 해제되었기에, mysql-server-5.5를 mysql-server-5.6으로 업그레이드하면 문제가 해결된다.

리눅스 우분투를 사용중이라면 아래와 같은 명령어로 업그레이드가 가능하다.

$ sudo apt-get update
$ sudo apt-get install mysql-server-5.6

이미 5.5에서 관리하고 있던 데이터베이스가 있더라도, 위와 같이 5.6을 설치하면 자동으로 데이터베이스가 이전되므로 별도로 dump를 떠서 옮길 필요는 없다. 다만 만약의 경우를 대비해서 설치전에 미리 백업을 해두도록 하자.

1.3.6 - MySQL | 문자열

문자열 합치기

concat 함수를 이용하여 문자열 또는 컬럼의 내용을 합칠 수 있다.

함수형식

concat(문자열 또는 컬럼명, 문자열 또는 컬럼명, ...)

사용 예제

SELECT concat("abc","123") 

위 예제를 실행하면, 아래와 같이 표시된다.

abc123

왼쪽에서 문자열 자르기

left 함수를 사용하면 문자열을 왼쪽에서 부터 문자열을 자를 수 있다.

함수 형식

left(컬럼명 또는 문자열, 왼쪽에서 잘라낼 문자열의 길이)

사용 예제

SELECT left("abcdef", 3)

위 예제를 실행하면, 아래와 같이 표시된다.

abc

오른쪽에서 문자열 자르기

right 함수를 사용하면 문자열을 오른쪽에서 부터 문자열을 자를 수 있다.

함수 형식

right(컬럼명 또는 문자열, 길이)

사용 예제

SELECT right("abcdef", 3)

위 예제를 실행하면, 아래와 같이 표시된다.

def

중간에서 문자열 자르기

substring 함수를 사용하면 문자열을 중간에서 부터 문자열을 자를 수 있다.

함수 형식

substring(컬럼 또는 문자열, 시작위치, 길이);

사용 예제

SELECT substring("abcdef", 3, 2) 

위 예제를 실행하면, 아래와 같이 표시된다.

cd

1.3.7 - MySQL | WITH ROLLUP 합계

GROUP BY를 사용하면 GROUP BY 뒤에 나오는 컬럼별로 합계를 구해준다.
이때, 아쉬운 점이 있다면 항목별 합계에 전체 합계가 같이 나오게 하는 것이다.

이럴 때에 사용하는 것이 WITH ROLLUP 이다.

WITH ROLLUP는 그룹별로 합계를 한번에 구할때 사용한다.

WITH ROLLUP 문법

SELECT "필드1", SUM("필드2")
FROM "테이블명"
GROUP BY "필드1"
WITH ROLLUP

WITH ROLLUP 예제

store_information 테이블

region_name store_name sales
West Los Angeles 1500
West San Diego 250
West Los Angeles 300
East Boston 700

위와 같은 테이블이 있다고 했을 때, 아래와 같이 명령문을 입력한다.

SQL 예제 1)

SELECT store_name, SUM(sales)
FROM store_information
GROUP BY store_name
WITH ROLLUP

실행 결과

store_name SUM(sales)
Boston 700
Los Angeles 1800
San Diego 250
NULL 2750
마지막 열에 합계가 2750이 표시되고 있다.

SQL 예제 2)

SELECT region_name, store_name, SUM(sales)
FROM store_information
GROUP BY region_name, store_name
WITH ROLLUP

실행 결과

region_name store_name SUM(sales)
East Boston 700
East NULL 700
West Los Angeles 1800
West San Diego 250
West NULL 2050
NULL NULL 2750

2번째 열에 East의 합계가 700이고, 4번째 열에 East의 합계가 2050이 표시되고 있다.
그리고, 5열에 이 모든 합계인 2750이 표시된다.

1.3.8 - MySQL | NULL 값 정렬 순서 변경하기

ORDER BY 절에서 정렬을 하다보면 값이 NULL 인 데이터가 맨위에 표시되는 경우가 있다.
이를 해결하는 방법에 대해 아래 보겠다.

문법

SELECT "필드"
FROM "테이블명"
ORDER BY "필드" IS NULL [ASC|DESC]

예제

store_information 테이블

region_name store_name sales
West Los Angeles 1500
West San Diego 250
West Los Angeles
East Boston 700

SQL 예제) NULL이 맨위로 정렬된다.

위와 같은 테이블이 있다고 했을 때, 아래와 같이 SQL문을 작성하고 실행하면,

SELECT store_name, sales
FROM store_information
ORDER BY sales ASC

위와 같이 NULL값이 맨위로 정렬된다.

store_name sales
Los Angeles NULL
San Diego 250
Boston 700
Los Angeles 1500

SQL 예제) NULL을 맨아래로 정렬한다.

NULL 값을 뒤로 정렬시키기 위해서는 아래와 같이 SQL문을 작성하고 실행하면,

SELECT store_name, sales
FROM store_information
ORDER BY sales IS NULL ASC, sales ASC

위와 같이 NULL값이 맨아래로 정렬된다.

store_name sales
San Diego 250
Boston 700
Los Angeles 1500
Los Angeles NULL

1.4 - PostgreSQL

PostgreSQL는 세계적으로 널리 이용되고 있는 데이터베이스이다. 여기에서는 PostgreSQL을 앞으로 사용하시는 분들을 대상으로 PostgreSQL 소개하려고 한다. PostgreSQL의 설치 방법과 PostgreSQL을 사용하여 데이터베이스를 구축하는 방법에 대한 간단한 예제를 사용하여 직접 체험하면서 하나 하나 설명하는 것이다.

1.4.1 - PostgreSQL | PostgreSQL 설치

PostgreSQL의 다운로드 방법과 Windows 환경에서의 설치에 대해 설명한다.

아래 사이트에서 각 OS에 맞게 다운로드 받아서 설치한다.

1.4.1.1 - PostgreSQL | PostgreSQL 설치 | PostgreSQL 다운로드 및 설치

PostgreSQL의 다운로드 및 설치하는 방법을 설명한다. 2020년 3월 현재 최신 버전은 PostgreSQL 12.2이다. 이번에는 Windows10 (64bit)에 설치한다.

PostgreSQL 다운로드

다음 URL에서 PostgreSQL을 다운로드한다.

Postgresql.org Download

화면 상단에 표시되어 있는 Download를 클릭한다.

Postgresql.org Download

Binary packages에 있는" Windows" 링크를 클릭한다.

Postgresql.org Download

Windows 용 다운로드 페이지가 표시되고, “Download the installer” 링크를 클릭한다.

Postgresql.org Download

버전 별 다운로드를 할 수 있는 화면이 표시되고, 여기서 PostgreSQL 12.2 Windows x86-64를 사용하기에 해당 위치에 있는 Download를 클릭한다.

그러면 다운로드가 시작된다. 적당한 위치에 저장하면 된다.

PostgreSQL 설치

계속해서 설치한다. 다운로드를 한 postgresql-12.2-1-windows-x64.exe 파일을 더블 클릭하면 다음과 같은 창이 표시된다. 여기서 Next를 클릭한다.

Postgresql Setup

설치 위치를 지정한다. 디폴트로 그대로 두어도 괜찮고, 변경하고 싶다면 설치 디렉토리를 지정한다. 여기서는 디폴트 그대로 설치하도록 한다. Next를 클릭한다.

Postgresql Setup

설치할 구성 요소를 선택한다. 여기서는 모두 체크한 상태로 Next를 클릭한다.

Postgresql Setup

데이터베이스 등 생성된 데이터를 설치할 디렉토리를 지정한다. 변경하려면 디렉토리를 지정한다. 이번에도 디폴트 그대로 놔둬었다. Next를 클릭한다.

Postgresql Setup

슈퍼 유저 postgres 계정의 암호를 설정한다. 확인을 위해 암호를 두번 입력한다. 설정이 끝나면 Next를 클릭한다.

Postgresql Setup

PostgreSQL 와 연결을 하기 위한 포트 번호를 지정한다. 일반적으로 디폴트 5432 으로도 상관 없지만 변경하여도 된다. 설정이 끝나면 Next를 클릭한다.

Postgresql Setup

새 데이터베이스 클러스터를 생성할 때에 로케일을 지정한다. 처음은 [Default locale] 설정되어 있는데, 이를 Korean, Korea을 선택하고, Next를 클릭한다.

Postgresql Setup

여기까지의 설정 내용을 확인합니다. Next를 클릭하십시오.

Postgresql Setup

설치 전 최종 확인을 하고, 설치를 해도 된다면 Next를 클릭한다.

Postgresql Setup

설치가 시작되면 설치 파일이 생성된다.

Postgresql Setup

다음과 같이 표시되면 PostgreSQL 설치가 완료된다.

Postgresql Setup

Stack Builder라고 적힌 왼쪽의 체크를 된 상태로 Finish를 클릭하면 드라이버 등 추가 응용 프로그램을 설치하기 위한 스택 작성기를 시작한다. 여기서는 하지 않을 것이기에 체크를 해제하고 Finish를 클릭한다.

여기까지 PostgreSQL의 다운로드 및 설치 방법에 대해 설명을 마치도록 하겠다.

1.4.1.2 - PostgreSQL | PostgreSQL 설치 | PATH 설정

명령 프롬프트에서 PostgreSQL에 연결하는 경우에는 PATH를 설정하면 편리하다. 여기에서는 여기에 PATH의 설정 방법에 대해 설명한다.

PATH 설정

명령 라인으로 실행하는 PostgreSQL 프로그램은 PostgreSQL을 설치 한 디렉토리 안에서 bin 디렉토리에 포함되어 있다.

C:\Program Files\PostgreSQL\12\bin>dir/w
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: XXXX-XXXX

 C:\Program Files\PostgreSQL\12\bin 디렉터리

[.]                           [..]                          clusterdb.exe                 createdb.exe
createuser.exe                dropdb.exe                    dropuser.exe                  ecpg.exe
icudt53.dll                   icuin53.dll                   icuio53.dll                   icule53.dll
iculx53.dll                   icutest53.dll                 icutu53.dll                   icuuc53.dll
initdb.exe                    isolationtester.exe           libcrypto-1_1-x64.dll         libcurl.dll
libcurl.lib                   libecpg.dll                   libecpg_compat.dll            libiconv-2.dll
libintl-8.dll                 libpgtypes.dll                libpq.dll                     libssl-1_1-x64.dll
libwinpthread-1.dll           libxml2.dll                   libxslt.dll                   oid2name.exe
pgbench.exe                   pg_archivecleanup.exe         pg_basebackup.exe             pg_checksums.exe
pg_config.exe                 pg_controldata.exe            pg_ctl.exe                    pg_dump.exe
pg_dumpall.exe                pg_isolation_regress.exe      pg_isready.exe                pg_receivewal.exe
pg_recvlogical.exe            pg_regress.exe                pg_regress_ecpg.exe           pg_resetwal.exe
pg_restore.exe                pg_rewind.exe                 pg_standby.exe                pg_test_fsync.exe
pg_test_timing.exe            pg_upgrade.exe                pg_waldump.exe                postgres.exe
psql.exe                      reindexdb.exe                 stackbuilder.exe              vacuumdb.exe
vacuumlo.exe                  wxbase28u_net_vc_custom.dll   wxbase28u_vc_custom.dll       wxbase28u_xml_vc_custom.dll
wxmsw28u_adv_vc_custom.dll    wxmsw28u_aui_vc_custom.dll    wxmsw28u_core_vc_custom.dll   wxmsw28u_html_vc_custom.dll
wxmsw28u_xrc_vc_custom.dll    zic.exe                       zlib1.dll
              69개 파일          56,286,000 바이트
               2개 디렉터리  442,429,300,736 바이트 남음

C:\Program Files\PostgreSQL\12\bin>

그러면 PATH를 설정해보도록 하자. 여기서는 Windows10 환경에서 설정하려고 한다.

먼저, 탐색기창을 열어서 “내 PC"를 선택하고 마우스 오른쪽 버튼을 클릭한다.

Postgresql path

“시스템"화면이 표시되면 왼쪽 메뉴에 “고급 시스템 설정"을 클릭한다.

Postgresql path

“시스템 속성"화면이 표시되면 아래 부근에 “환경 변수"를 클릭한다.

Postgresql path

“환경 변수 편집” 화면이 표시되는데 이 화면에서 PATH을 설정한다.

“시스템 변수"에서 “변수"가 “Path"라고 쓰여진 것이 있는지를 찾는다. 찾았으면 “Path"라고 적힌 부분을 한 번 클릭하여 선택하고 “편집” 버튼을 클릭한다.

Postgresql path

“환경 변수 편집” 화면이 표시된다.

오른쪽 상단의 ‘새로 만들기’를 클릭하면 왼쪽 목록의 마지막에 새로운 항목을 추가 할 수 있게 되고 여기에 PostgreSQL의 bin 디렉토리리인 “C:\Program Files\PostgreSQL\12” 을 입력한다. (설치된 PostgreSQL의디렉토리에 맞게 입력한다). 입력이 끝나면 “확인"를 클릭한다.

Postgresql path

이것으로 PATH 설정에 PostgreSQL로 사용하는 PATH를 추가가 완료되었다.

PATH가 제대로 설정되었는지 확인

그럼 설정이 잘되어 있는 확인해 보자. PATH 설정을 한 이후에는 명령 프롬프트를 새로 실행하여 열지 않으면 설정이 적용되지 않기에 명령 프롬프트를 새로 열도록 한다. 그리고 다음과 같이 실행한다.

$ psql --version
C:\>psql --version
psql (PostgreSQL) 12.2

C:\>

PostgreSQL 버전이 표시되면 PATH 설정은 제대로 된것이다.

혹시 아래와 같이 “‘psql’은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.“라고 표시된다면 PATH 설정이 제대로 되지 않은 것이니 다시 확인해 보도록 한다.

C:\>psql --version
'psql'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는
배치 파일이 아닙니다.

C:\>

PostgreSQL를 명령 라인에서 사용을 하기 위한 PATH를 설정하는 방법에 대해 설명하였다.

1.4.1.3 - PostgreSQL | PostgreSQL 설치 | Windows 서비스 시작 및 중지

PostgreSQL을 Windows 환경에 설치하면 자동으로 Windows 서비스로 등록되어 “실행 중"인 상태로 된다. 여기에서는 PostgreSQL가 어떻게 Winodws 서비스에 등록되어 있는지를 확인하고 PostgreSQL을 중지하거나 다시 시작하는 방법에 대해 설명한다.

Windows 서비스 목록에서 확인

먼저 PostgreSQL가 Windows 서비스로 등록되어 있는지 확인한다. Windows 서비스 목록을 표시하려면 바탕 화면 왼쪽 하단의 시작 메뉴를 마우스 오른쪽 버튼을 클릭하여, 표시되는 메뉴 중에서 “컴퓨터 관리"를 클릭한다.

컴퓨터 관리

“컴퓨터 관리"화면이 표시되면, 왼쪽 메뉴의 “서비스 및 응용 프로그램"에 있는 “서비스"를 클릭한다. “서비스"에 대한 정보가 표시된다. 그리고 표시된 목록에서 “postgresql-x64-12"을 찾아 더블 클릭한다.

컴퓨터 관리-서비스

그러면 아래와 같이 Windows 서비스로 등록되어 있는 PostgreSQL에 대한 속성이 표시된다.

PostgreSQL 속성

PostgreSQL 서비스 시작 및 중지

“시작 유형"을 “자동"으로 설정되어 있어서 Windows가 시작될 때 자동으로 PostgreSQL도 서비스로 시작되도록 되어 있다. 만약 수동으로 PostgreSQL을 시작하고 싶은 경우는 “시작 유형"을 “수동"으로 변경하면 된다.

PostgreSQL 속성

PostgreSQL을 정지시키려면, “서비스 상태"에 있는 “정지"를 클릭한다.

PostgreSQL 속성-중지

정지 한 PostgreSQL을 시작하려고 한다면, “서비스 상태"에 있는 “시작"을 클릭한다.

PostgreSQL 속성-시작

“실행 파일 경로"에는 어떤 명령어로 PostgreSQL가 시작되는지 확인 할 수 있다.

C:\Program Files\PostgreSQL\12\bin\pg_ctl.exe" runservice -N "postgresql-x64-12" -D "C:\Program Files\PostgreSQL\12\data" -w

여기까지 PostgreSQL가 Windows 서비스에 어떻게 등록되어 있는지 확인하고 Windows 서비스로 PostgreSQL을 시작하거나 중지하는 방법에 대해 설명하였다.

1.4.1.4 - PostgreSQL | PostgreSQL 설치 | PostgreSQL 문서 참조

PostgreSQL을 사용하는데 도움이 되는 문서를 참조하는 방법에 대해 설명한다.

PostgreSQL 관련 온라인 문서

PostgreSQL 문서를 참조하려면 다음 URL에 액세스한다.

화면 상단에 표시되는 “Documentation"을 클릭한다.

postgresql.org

“Documentation” 페이지가 표시되고, 화면 오른쪽” Online Manuals"을 보면, 버전마다 링크가 포함되어 있는 것을 볼 수 있다. 설치한 PostgreSQL 버전은 12 이었으므로 12이라고 쓰여진 링크를 클릭한다.

Documentation

*“Translated Manuals"를 보면 번역된 언어 목록를 볼 수 있다. 안타깝게도 한국어는 목록에 없다. *

PostgreSQL 12.2 문서가 표시된다.

1.4.2 - PostgreSQL | psql을 사용하여 PostgreSQL에 연결

PostgreSQL에서는 데이터베이스와 테이블을 만들고 데이터를 추가하고 검색하기 위한 도구로 커멘드 라인 기반에서 사용할 수있는 psql이라는 도구를 제공한다. 여기에서 psql을 사용하여 PostgreSQL에 연결하는 방법에 대해 설명한다.

psql 명령어는 {postgres 설치 경로}/bin 디렉토리에 존재한다.

1.4.2.1 - PostgreSQL | psql을 사용하여 PostgreSQL에 연결 | PostgreSQL 연결과 해제

psql을 사용하여 PostgreSQL에 접속하는 방법과 PostgreSQL에서 연결을 해제하는 방법에 대해 설명한다.

PostgreSQL에 연결하기

psql는 컨멘드 라인 기반으로 PostgreSQL에 작성된 데이터베이스에 연결하거나 테이블에서 데이터를 검색 등을 할 수 있는 도구이다. Windows 환경에서 psql을 이용하려면 명령 프롬프트에서 해야 한다.

먼저 명령 프롬프트를 시작한다.

psql을 사용하여 PostgreSQL에 연결하려면 다음과 같이 실행한다. (실제로는 PostgreSQL로 작성된 지정된 데이터베이스에 연결한다).

psql -h {호스트명} -p {포트 번호} -U {사용자명} -d {데이터베이스명}

호스트명은 PostgreSQL가 실행중인 호스트명 또는 IP 주소이다. 기본값은 localhost 이기에 로컬 호스트에서 실행중인 PostgreSQL에 접속하는 경우는 생략 할 수 있다.

포트 번호는 PostgreSQL가 사용하는 포트 번호이다. 기본 값이 PostgreSQL 설치시 설정한 값인 5432로 되어 있기에 다른 포트 번호를 사용하는 경우가 아니라면 생략 할 수 있다.

사용자명은 PostgreSQL을 설치 한 직후에는 수퍼 유저로 postgres 역할 밖에 생성되지 않으므로 -U postgres로 지정한다. 사용자명을 생략하게 되면 OS의 사용자명이 사용된다.

※ PostgreSQL에서는 일반적으로 사용자라는 것과 그룹이라는 것을 정리하여 역할이라고 부른다.

데이터베이스 명에는 연결할 데이터베이스 이름을 지정한다. 생략하게 되면 사용자명과 동일한 이름의 데이터베이스에 연결한다. (postgres 데이터베이스는 자동으로 생성되며, postgres 역할로 접속한 경우는 postgres 데이터베이스에 연결된다.)

모든 지정하여 PostgreSQL에 접속하는 경우는 다음과 같다.

psql -h localhost -p 5432 -U postgres -d postgres

locahost이라면 호스트명 지정을 생략할 수 잇고, 포트 번호를 생략 할 수도 있다. 그리고 사용자명과 같은 데이터베이스에 연결한다면 데이터베이스명을 생략 할 수 있으므로, 다음과 같이 실행해도 위에 동일하다.

psql -U postgres

그러면 실제로 실행해 보자.

C:\>psql -U postgres
postgres 사용자의 암호:

연결하려고 하는 사용자의 로그인 비밀번호를 입력을 위해 대기가 상태가 된다. postgres의 비밀번호는 PostgreSQL 설치시 지정한 것이다. 입력을 한 후에 [Ener] 키를 누른다. 제대로 입력 하였다면 다음과 같은 화면이 나타난다.

C:\>psql -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=#

연결 중에는 “데이터베이스명=#“으로 표시된다. 여기서는 postgres 데이터베이스에 연결되어 있기에 “postgres=#“로 표시되고 있다.

psql을 종료하고 PostgreSQL에서 연결 해제

psql을 종료하고 PostgreSQL에서 연결 해제하려면 다음과 같이 수행한다.

\q
C:\>psql -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=# \q

C:\>

sql이 종료되고 PostgreSQL에서 연결이 해제되었다.

psql을 사용하여 PostgreSQL에 연결하는 방법과 연결을 해제하는 방법에 대해 설명하였다.

1.4.2.2 - PostgreSQL | psql을 사용하여 PostgreSQL에 연결 | psql 옵션 사용법과 옵션 목록

psql을 실행할 때에 연결에 사용하는 사용자명 및 데이터베이스를 지정하는 옵션 이외에도 몇 가지 옵션을 제공한다. 여기에서 psql을 실행할 때 지정할 수 있는 옵션의 사용법과 옵션의 목록에 대해 설명한다.

psql을 실행할 때에 자주 사용하는 옵션은 PostgreSQL에 연결할 때 사용하는 -h와 -U 옵션이지만, 그외 몇 가지 옵션을 제공하고 있다.

버전 확인

-V 옵션은 PostgreSQL 버전을 표시할 수 있다.

psql -V
C:\>psql -V
psql (PostgreSQL) 12.2

C:\>

옵션 목록 및 도움말 표시

-? 옵션 목록 및 도움말을 표시하는 옵션이다.

C:\>psql -?
psql은 PostgreSQL 대화식 터미널입니다.

사용법:
  psql [OPTION]... [DBNAME [USERNAME]]

일반 옵션:
  -c, --command=COMMAND    하나의 명령(SQL 또는 내부 명령)만 실행하고 끝냄
  -d, --dbname=DBNAME      연결할 데이터베이스 이름(기본 값: "devkuma")
  -f, --file=FILENAME      파일 안에 지정한 명령을 실행하고 끝냄
  -l, --list               사용 가능한 데이터베이스 목록을 표시하고 끝냄
  -v, --set=, --variable=NAME=VALUE
                           psql 변수 NAME을 VALUE로 설정
                           (예, -v ON_ERROR_STOP=1)
  -V, --version            버전 정보를 보여주고 마침
  -X, --no-psqlrc          시작 파일(~/.psqlrc)을 읽지 않음
  -1 ("one"), --single-transaction
                           명령 파일을 하나의 트랜잭션으로 실행
  -?, --help[=options]     이 도움말을 표시하고 종료
      --help=commands      psql 내장명령어(\문자로 시작하는)를 표시하고 종료
      --help=variables     특별 변수들 보여주고, 종료

입출력 옵션:
  -a, --echo-all           스크립트의 모든 입력 표시
  -b, --echo-errors        실패한 명령들 출력
  -e, --echo-queries       서버로 보낸 명령 표시
  -E, --echo-hidden        내부 명령이 생성하는 쿼리 표시
  -L, --log-file=FILENAME  세션 로그를 파일로 보냄
  -n, --no-readline        확장된 명령행 편집 기능을 사용중지함(readline)
  -o, --output=FILENAME    쿼리 결과를 파일(또는 |파이프)로 보냄
  -q, --quiet              자동 실행(메시지 없이 쿼리 결과만 표시)
  -s, --single-step        단독 순차 모드(각 쿼리 확인)
  -S, --single-line        한 줄 모드(줄 끝에서 SQL 명령이 종료됨)

출력 형식 옵션:
  -A, --no-align           정렬되지 않은 표 형태의 출력 모드
      --csv                CSV (쉼표-분리 자료) 테이블 출력 모드
  -F, --field-separator=STRING
                           unaligned 출력용 필드 구분자 설정(기본 값: "|")
  -H, --html               HTML 표 형태 출력 모드
  -P, --pset=VAR[=ARG]     인쇄 옵션 VAR을 ARG로 설정(\pset 명령 참조)
  -R, --record-separator=STRING
                           unaligned 출력용 레코드 구분자 설정
                           (기본 값: 줄바꿈 문자)
  -t, --tuples-only        행만 인쇄
  -T, --table-attr=TEXT    HTML table 태그 속성 설정(예: width, border)
  -x, --expanded           확장된 표 형태로 출력
  -z, --field-separator-zero
                           unaligned 출력용 필드 구분자를 0 바이트로 지정
  -0, --record-separator-zero
                           unaligned 출력용 레코드 구분자를 0 바이트로 지정

연결 옵션들:
  -h, --host=HOSTNAME      데이터베이스 서버 호스트 또는 소켓 디렉터리
                           (기본값: "로컬 소켓")
  -p, --port=PORT          데이터베이스 서버 포트(기본 값: "5432")
  -U, --username=USERNAME  데이터베이스 사용자 이름(기본 값: "devkuma")
  -w, --no-password        암호 프롬프트 표시 안 함
  -W, --password           암호 입력 프롬프트 보임(자동으로 처리함)

자세한 내용을 보려면 psql 내에서 "\?"(내부 명령) 또는 "\help"(SQL
명령)를 입력하거나 PostgreSQL
설명서에서 psql 섹션을 참조하십시오.

오류보고: <pgsql-bugs@lists.postgresql.org>.

C:\>

결과 표시 방법을 HTML 테이블 태그 사용하도록 지정

psql을 사용하여 PostgreSQL에 접속한 뒤의 표시 방법에 관한 것도 있다. 예를 들면, PostgreSQL에 접속한 후에 데이터베이스를 목록을 표시하면 다음과 같이 표시된다.

C:\>psql -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=# \l
                                      데이터베이스 목록
   이름    |  소유주  | 인코딩 |     Collate      |      Ctype       |      액세스 권한
-----------+----------+--------+------------------+------------------+-----------------------
 postgres  | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 template0 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
 template1 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
(3개 행)


postgres=#

-H 옵션을 사용하면 psql을 실행 PostgreSQL에 접속하면 동일한 데이터베이스 목록을 검색하면 HTML 테이블 태그를 사용하여 형식으로 출력할 수도 있다.

C:\>psql -H -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=# \l
<table border="1">
  <caption>데이터베이스 목록</caption>
  <tr>
    <th align="center">이름</th>
    <th align="center">소유주</th>
    <th align="center">인코딩</th>
    <th align="center">Collate</th>
    <th align="center">Ctype</th>
    <th align="center">액세스 권한</th>
  </tr>
  <tr valign="top">
    <td align="left">postgres</td>
    <td align="left">postgres</td>
    <td align="left">UTF8</td>
    <td align="left">Korean_Korea.949</td>
    <td align="left">Korean_Korea.949</td>
    <td align="left">&nbsp; </td>
  </tr>
  <tr valign="top">
    <td align="left">template0</td>
    <td align="left">postgres</td>
    <td align="left">UTF8</td>
    <td align="left">Korean_Korea.949</td>
    <td align="left">Korean_Korea.949</td>
    <td align="left">=c/postgres<br />
postgres=CTc/postgres</td>
  </tr>
  <tr valign="top">
    <td align="left">template1</td>
    <td align="left">postgres</td>
    <td align="left">UTF8</td>
    <td align="left">Korean_Korea.949</td>
    <td align="left">Korean_Korea.949</td>
    <td align="left">=c/postgres<br />
postgres=CTc/postgres</td>
  </tr>
</table>
<p>(3개 행)<br />
</p>

postgres=#

여기서 언급한 데이터베이스 목록을 검색뿐 아니라, 다른 검색도 HTML 형태로 테이블 태그를 사용하여 형식으로 출력이 된다.

postgres=# select now()
postgres-# ;
<table border="1">
  <tr>
    <th align="center">now</th>
  </tr>
  <tr valign="top">
    <td align="left">2020-03-15 02:30:16.292241+09</td>
  </tr>
</table>
<p>(1개 행)<br />
</p>

postgres=#

파일을 이용한 SQL 실행

-f 옵션을 사용하면 파일을 이용해서 SQL를 실행 시킬 수도 있다.

먼저 아래와 같이 SQL 파일을 작성한다.

D:\>copy con file.sql
select now();^Z
        1개 파일이 복사되었습니다.

작성한 파일을 아래와 같이 실행시키면 출력 결과를 볼 수 있다.

D:\>psql -U postgres -f file.sql
postgres 사용자의 암호:
              now
-------------------------------
 2020-03-15 02:39:17.327284+09
(1개 행)



D:\>

이와 같이 psql의 옵션에는 여러가지 기능을 제공하고 있기에, 나중에 옵션 목록을 확인하여 무엇을 할 수 있는지를 확인해 두면 유용하게 사용할 수 있을 것이다.

1.4.2.3 - PostgreSQL | psql을 사용하여 PostgreSQL에 연결 | psql 메타 명령의 목록과 실행 방법

psql을 사용하여 PostegreSQL에 연결하여 실행 가능한 psql 메타 명령의 사용법과 명령의 목록에 대해 설명한다.

psql 명령의 실행 방법

psql는 PostgreSQL에 접속하여 실행 가능한 명령이 많이 제공되고 있다. 명령의 목록을 확인하려면 다음과 같이 수행한다. psql 명령 목록과 사용법이 표시된다. (명령 목록은이 페이지의 마지막에 기재되어 있다.)

\?
postgres=# \?
일반
  \copyright             PostgreSQL 사용법 및 저작권 정보 표시
  \crosstabview [칼럼들] 쿼리를 실행하고, 피봇 테이블 형태로 자료를 보여줌
  \errverbose            최대 자세히 보기 상태에서 최근 오류를 다 보여줌
  \g [FILE] 또는 ;       쿼리 실행(및 결과를 파일 또는 |파이프로 보냄)
  \gdesc                 쿼리를 실행하지 않고 그 결과 칼럼과 자료형을 출력
  \gexec                 쿼리를 실행하고, 그 결과를 각각 실행 함
  \gset [PREFIX]         쿼리 실행 뒤 그 결과를 psql 변수로 저장
  \gx [FILE]             \g 명령과 같으나, 출력을 확장 모드로 강제함
  \q                     psql 종료
  \watch [SEC]           매 초마다 쿼리 실행

도움말
  \? [commands]          psql 역슬래시 명령어 설명
  \? options             psql 명령행 옵션 도움말 보기
  \? variables           psql 환경 설정 변수들에 설명 보기
  \h [NAME]              SQL 명령 구문 도움말, 모든 명령을 표시하려면 * 입력

psql 명령은 Windows의 경우 백 슬래시()에서 시작하는 것으로, 예를 들어 psql를 종료하고 PostgreSQL과의 연결을 끊을 때 사용하는 \q도 psql 명령 중 하나이다 .

\q
C:\>psql -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=# \q

C:\>

psql 명령을 사용하여 생성된 데이터베이스 목록을 검색 할 수 있다.

\l
postgres=# \l
postgres=# \l
                                      데이터베이스 목록
   이름    |  소유주  | 인코딩 |     Collate      |      Ctype       |      액세스 권한
-----------+----------+--------+------------------+------------------+-----------------------
 devkuma   | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 postgres  | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 template0 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
 template1 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
(4개 행)

현재 연결된 데이터베이스와 다른 데이터베이스에 연결할 수 있다.

\c devkuma
postgres=# \c devkuma
접속정보: 데이터베이스="devkuma", 사용자="postgres".
devkuma=#

psql 명령 목록

일반:

명령어 설명
\copyright PostgreSQL 사용법 및 저작권 정보 표시
\crosstabview [칼럼들] 쿼리를 실행하고, 피봇 테이블 형태로 자료를 보여줌
\errverbose 최대 자세히 보기 상태에서 최근 오류를 다 보여줌
\g [FILE] 또는 ; 쿼리 실행(및 결과를 파일 또는| 파이프로 보냄)
\gdesc 쿼리를 실행하지 않고 그 결과 칼럼과 자료형을 출력
\gexec 쿼리를 실행하고, 그 결과를 각각 실행 함
\gset [PREFIX] 쿼리 실행 뒤 그 결과를 psql 변수로 저장
\gx [FILE] \g 명령과 같으나, 출력을 확장 모드로 강제함
\q psql 종료
\watch [SEC] 매 초마다 쿼리 실행

도움말:

명령어 설명
? [commands] psql 역슬래시 명령어 설명
? options psql 명령행 옵션 도움말 보기
? variables psql 환경 설정 변수들에 설명 보기
\h [NAME] SQL 명령 구문 도움말, 모든 명령을 표시하려면 * 입력

쿼리 버퍼:

명령어 설명
\e [FILE] [LINE] 외부 편집기로 쿼리 버퍼(또는 파일) 편집
\ef [FUNCNAME [LINE]] 외부 편집기로 해당 함수 내용 편집
\ev [VIEWNAME [LINE]] 외부 편집기로 해당 뷰 정의 편집
\p 쿼리 버퍼의 내용 표시
\r 쿼리 버퍼 초기화(모두 지움)
\w FILE 쿼리 버퍼를 파일에 기록

입력/출력:

명령어 설명
\copy … 클라이언트 호스트에 있는 자료를 SQL COPY 명령 실행
\echo [STRING] 문자열을 표준 출력에 기록
\i FILE 파일에서 명령 실행
\ir FILE \i 명령과 같으나, 경로가 현재 위치 기준 상대적
\o [FILE] 모든 쿼리 결과를 파일 또는 | 파이프로 보냄
\qecho [STRING] 문자열을 쿼리 출력 스트림에 기록(\o 참조)

조건문:

명령어 설명
\if EXPR 조건문 시작
\elif EXPR else if 구문 시작
\else 조건문의 그 외 조건
\endif 조건문 끝

정보보기:

(옵션: S = 시스템 개체 표시, + = 추가 상세 정보)

명령어 설명
\d[S+] 테이블, 뷰 및 시퀀스 목록
\d[S+] NAME 테이블, 뷰, 시퀀스 또는 인덱스 설명
\da[S] [PATTERN] 집계 함수 목록
\dA[+] [PATTERN] 접근 방법 목록
\db[+] [PATTERN] 테이블스페이스 목록
\dc[S+] [PATTERN] 문자셋 변환자 목록
\dC[+] [PATTERN] 자료형 변환자 목록
\dd[S] [PATTERN] 다른 곳에서는 볼 수 없는 객체 설명을 보여줌
\dD[S+] [PATTERN] 도메인 목록
\ddp [PATTERN] 기본 접근권한 목록
\dE[S+] [PATTERN] 외부 테이블 목록
\det[+] [PATTERN] 외부 테이블 목록
\des[+] [PATTERN] 외부 서버 목록
\deu[+] [PATTERN] 사용자 매핑 목록
\dew[+] [PATTERN] 외부 데이터 래퍼 목록
\df[anptw][S+] [PATRN] [agg/normal/procedures/trigger/window] | 함수 목록
\dF[+] [PATTERN] 텍스트 검색 구성 목록
\dFd[+] [PATTERN] 텍스트 검색 사전 목록
\dFp[+] [PATTERN] 텍스트 검색 파서 목록
\dFt[+] [PATTERN] 텍스트 검색 템플릿 목록
\dg[S+] [PATTERN] 롤 목록
\di[S+] [PATTERN] 인덱스 목록
\dl 큰 개체 목록, \lo_list 명령과 같음
\dL[S+] [PATTERN] 프로시져 언어 목록
\dm[S+] [PATTERN] materialized 뷰 목록
\dn[S+] [PATTERN] 스키마 목록
\do[S] [PATTERN] 연산자 목록
\dO[S+] [PATTERN] collation 목록
\dp [PATTERN] 테이블, 뷰 및 시퀀스 액세스 권한 목록
\dP[itn+] [PATTERN] 파티션 릴레이션 목록 [인덱스/테이블만] [n=nested]
\drds [PATRN1 [PATRN2]] 데이터베이스별 롤 설정 목록
\dRp[+] [PATTERN] 복제 발행 목록
\dRs[+] [PATTERN] 복제 구독 목록
\ds[S+] [PATTERN] 시퀀스 목록
\dt[S+] [PATTERN] 테이블 목록
\dT[S+] [PATTERN] 데이터 형식 목록
\du[S+] [PATTERN] 롤 목록
\dv[S+] [PATTERN] 뷰 목록
\dx[+] [PATTERN] 확장 모듈 목록
\dy [PATTERN] 이벤트 트리거 목록
\l[+] [PATTERN] 데이터베이스 목록
\sf[+] 함수이름 함수 정의 보기
\sv[+] 뷰이름 뷰 정의 보기

출력 형식:

명령어 설명
\a 정렬되지 않은 출력 모드와 정렬된 출력 모드 전환
\C [STRING] 테이블 제목 설정 또는 값이 없는 경우 설정 안 함
\f [STRING] unaligned 출력에 대해 필드 구분자 표시 또는 설정
\H HTML 출력 모드 전환(현재 off)
\pset [이름 [값]] 테이블 출력 옵션 설정
(border|columns|csv_fieldsep|expanded|fieldsep|
fieldsep_zero|footer|format|linestyle|null|
numericlocale|pager|pager_min_lines|recordsep|
recordsep_zero|tableattr|title|tuples_only|
unicode_border_linestyle|unicode_column_linestyle|
unicode_header_linestyle)
\t [on|off] 행만 표시(현재 off)
\T [STRING] HTML <table> 태그 속성 설정 또는 비었는 경우 설정 안 함
\x [on|off|auto] 확장된 출력 전환 (현재 off)

연결:

명령어 설명
\c[onnect] {[DBNAME|- USER|- HOST|- PORT|-] | conninfo} 새 데이터베이스에 접속 (현재 “postgres”)
\conninfo 현재 데이터베이스 접속 정보 보기
\encoding [ENCODING] 클라이언트 인코딩 표시 또는 설정
\password [USERNAME 사용자 암호를 안전하게 변경

운영 체제:

명령어 설명
\cd [DIR] 현재 작업 디렉터리 변경
\setenv NAME [VALUE] 환경 변수 지정 및 해제
\timing [on|off] 명령 실행 시간 전환(현재 off)
! [COMMAND] 셸 명령 실행 또는 대화식 셸 시작

변수:

명령어 설명
\prompt [TEXT] NAME 사용자에게 내부 변수를 설정하라는 메시지 표시
\set [NAME [VALUE]] 내부 변수 설정 또는 미지정 경우 모든 변수 목록 표시
\unset NAME 내부 변수 설정 해제(삭제)

큰 개체:

명령어 설명
\lo_export LOBOID FILE
\lo_import FILE [COMMENT]
\lo_list
\lo_unlink LOBOID 큰 개체 작업

1.4.2.4 - PostgreSQL | psql을 사용하여 PostgreSQL에 연결 | psql에서 SQL 명령 실행

psql에서 SELECT 문이나 CREATE 문 등 SQL 명령을 실행하는 방법에 대해 설명한다.

SQL 명령을 실행하기

데이터베이스와 테이블을 만들거나 테이블에 저장된 데이터를 검색하거나 하려면 psql에서 SQL 명령을 실행해야 한다 (실제로는 SQL 명령이 서버로 전송되어 처리된다). SQL 명령을 실행하려면 먼저 명령 프롬프트를 시작하고 psql을 사용하여 PostgreSQL에 연결한다.

C:\Users\kimkc>psql -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=#

다음과 같은 프롬프트가 표시된다.

postgres=#

SQL 명령을 실행하려면 다음과 같이 입력한다.

postgres=# SQL문;

SQL 명령을 입력 할 때는 마지막에 세미콜론 (;)을 입력한 후 [Enter] 키를 누르면 SQL 명령이 실행된다. 세미콜론을 입력하기 전에 그냥 [Enter] 키를 눌러도 분할하여 입력을 하는 것만으로 실행되지 않는다.

그러면 실제로 해보도록 하자. 새 데이터베이스를 만든다. 다음과 같이 입력 한 후에 [Enter] 키를 누른다.

postgres=# create database sample;
postgres=# create database sample;
CREATE DATABASE
postgres=#

SQL 명령이 서버로 전송되어 처리되었다.

SQL 명령을 분할하여 입력하기

SQL 명령이 긴 경우에는 분할하여 입력 할 수 있다. 그러면 실제로 해보도록 하자. 이번 예로 테이블을 만들 SQL 명령을 분할하여 입력 해 본다. 먼저 create 만 입력하고 [Enter] 키를 누른다.

postgres=# create
postgres-#

SQL 명령을 실행할 때 마지막에 세미콜론을 입력하고 Enter 키를 누른다. 세미콜론이 입력되기 전에 [Enter] 키를 누르면 입력이 분할 된 것으로 간주더;ㄴ다.

분할된 입력을 할 때 프롬프트 표시가 postgres=#에서 postgres-#으로 바껴서 구별 할 수 있게 되었다.

그럼 다음 table staff 입력하고 [Enter] 키를 누른다.

postgres=# create
postgres-# table staff
postgres-#

마지막으로 (id integer, name character varying (10)); 입력하고 [Enter] 키를 누른요. 세미콜론 (;)이 입력된 후 [Enter] 키를 눌렸기 때문에 SQL 명령의 입력이 완료되었다고 판단되는 SQL 명령이 실행된다.

postgres=# create
postgres-# table staff
postgres-# (id integer, name character varying(10));
CREATE TABLE
postgres=#

SQL 명령이 실행되어 새로운 테이블이 생성되었다.

이렇게 긴 SQL 명령은 입력 도중 [Enter] 키를 누르면 분할하여 입력 할 수 있다.

분할하여 입력하는 도중에 취소하기

SQL 명령을 분할하여 입력하고있을 때 중간에 SQL 명령의 입력을 취소하고자 하는 경우에는 psql 명령 \r을 입력하고 Enter 키를 누른다.

실제로 해보록 하자. create table까지 입력하고 [Enter] 키를 누른다.

postgres=# create table
postgres-#

여기에서 입력을 취소하기 위해 \r을 입력하고 Enter 키를 누른다.

postgres=# create table
postgres-# \r
쿼리 버퍼 초기화 (비웠음).
postgres=#

이것으로 SQL 명령의 입력이 취소된다.

psql에서 SQL 명령을 실행하는 방법에 대해 설명하였다.

1.4.3 - PostgreSQL | PostgreSQL 설정 파일

PostgreSQL에 관한 설정은 기본 설정을 위한 postgresql.conf 파일, 호스트 기반으로 연결 권한을 설정하는 pg_hba.conf 파일, OS 사용자 이름 및 데이터베이스 사용자 이름 매핑하는 pg_ident.conf 파일 이렇게 3개가 존재한다. 여기에서는 각각의 설정 파일 작성 방법에 대해 설명한다.

1.4.3.1 - PostgreSQL | PostgreSQL 설정 파일 | postgresql.conf 파일 설정 방법

postgresql.conf 파일은 PostgreSQL에 대한 기본 설정하는 파일이다. 여기에서는 postgresql.conf 파일의 설정 방법에 대해 설명한다.

postgresql.conf 파일 위치

postgresql.conf은 기본적으로 PostgreSQL을 설치한 data 디렉토리에 저장되어 있다.

C:\Program Files\PostgreSQL\12\data>dir
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: XXXX-XXXX

 C:\Program Files\PostgreSQL\12\data 디렉터리

2020-07-08  오전 12:00    <DIR>          .
2020-07-08  오전 12:00    <DIR>          ..
2020-07-08  오전 12:28    <DIR>          base
2020-07-08  오전 12:00                45 current_logfiles
2020-07-08  오전 12:20    <DIR>          global
2020-07-08  오전 12:00    <DIR>          log
2020-03-10  오전 12:24    <DIR>          pg_commit_ts
2020-03-10  오전 12:24    <DIR>          pg_dynshmem
2020-03-10  오전 12:24             4,156 pg_hba.conf
2020-03-10  오전 12:24             1,678 pg_ident.conf
2020-07-08  오전 12:38    <DIR>          pg_logical
2020-03-10  오전 12:24    <DIR>          pg_multixact
2020-07-07  오후 11:20    <DIR>          pg_notify
2020-03-10  오전 12:24    <DIR>          pg_replslot
2020-03-10  오전 12:24    <DIR>          pg_serial
2020-03-10  오전 12:24    <DIR>          pg_snapshots
2020-07-07  오후 11:20    <DIR>          pg_stat
2020-07-08  오후 11:35    <DIR>          pg_stat_tmp
2020-03-10  오전 12:24    <DIR>          pg_subtrans
2020-03-10  오전 12:24    <DIR>          pg_tblspc
2020-03-10  오전 12:24    <DIR>          pg_twophase
2020-03-10  오전 12:24                 3 PG_VERSION
2020-03-10  오전 12:24    <DIR>          pg_wal
2020-03-10  오전 12:24    <DIR>          pg_xact
2020-03-10  오전 12:24                90 postgresql.auto.conf
2020-03-10  오전 12:24            27,377 postgresql.conf <------------- 여기 저장되어 있다.
2020-07-07  오후 11:20                91 postmaster.opts
2020-07-07  오후 11:20                70 postmaster.pid
               8개 파일              33,510 바이트
              20개 디렉터리  424,537,530,368 바이트 남음

C:\Program Files\PostgreSQL\12\data>

postgresql.conf 파일은 텍스트 파일으로 되어 있어, 내용을 확인하거나 편집하려면 텍스트 편집기에서 파일을 열 수 있다.

# -----------------------------
# PostgreSQL configuration file
# -----------------------------
#
# This file consists of lines of the form:
#
#   name = value
#
# (The "=" is optional.)  Whitespace may be used.  Comments are introduced with
# "#" anywhere on a line.  The complete list of parameter names and allowed
# values can be found in the PostgreSQL documentation.
#
# The commented-out settings shown in this file represent the default values.
# Re-commenting a setting is NOT sufficient to revert it to the default value;
# you need to reload the server.
#
# This file is read on server startup and when the server receives a SIGHUP
# signal.  If you edit the file on a running system, you have to SIGHUP the
# server for the changes to take effect, run "pg_ctl reload", or execute
# "SELECT pg_reload_conf()".  Some parameters, which are marked below,
# require a server shutdown and restart to take effect.
#
# Any parameter can also be given as a command-line option to the server, e.g.,
# "postgres -c log_connections=on".  Some parameters can be changed at run time
# with the "SET" SQL command.
#
# Memory units:  kB = kilobytes        Time units:  ms  = milliseconds
#                MB = megabytes                     s   = seconds
#                GB = gigabytes                     min = minutes
#                TB = terabytes                     h   = hours
#                                                   d   = days

... 이하 생략 ...

postgresql.conf 설정

각 항목은 “매개 변수 이름 = 설정 값” 형식으로 되어 있다. 예를 들어 postgresql.conf 파일을 보면 다음과 같은 내용을 볼 수 있다.

# - Connection Settings -

listen_addresses = '*'		# what IP address(es) to listen on;
					# comma-separated list of addresses;
					# defaults to 'localhost'; use '*' for all
					# (change requires restart)
port = 5432				# (change requires restart)
max_connections = 100			# (change requires restart)

첫 번째 listen_addresses 매개 변수는 PostgreSQL에 대한 클라이언트의 연결을 허용 할 호스트와 IP 주소를 설정한다. 현재는 '*'가 설정되어 있으며, 모든 클라이언트의 연결을 허용한다는 것을 뜻한다. (실제 어떤 데이터베이스에 연결을 허용할지 등의 자세한 설정은 pg_hba.conf 파일로 설정한다. 이 설정은 그 이전 단계이다.)

# 다음에 작성된 문장은 모두 주석으로 처리된다. 설정에 대한 주석을 작성하거나 파라메타에 대한 설정을 해제할 경우에 사용된다. 예를 들어 설정 파일을 보면 다음과 같은 내용이 있다.

# - Authentication -

#authentication_timeout = 1min		# 1s-600s
#password_encryption = md5		# md5 or scram-sha-256
#db_user_namespace = off

매개 변수에 대한 설명이 있지만 앞에 #이 있기 때문에 현재는 주석이 되고 설정이 되어 있지 않는다. 매개 변수에 대한 설정을 할 경우 첫 번째 #을 제거하고 필요에 따라 값을 변경하면 된다. (물론 #이 붙은 문장은 그 상태로 처음부터 기술해도 상관 없다). 예를 들어, authentication_timeout 매개 변수에 대한 설정을 해보자.

# - Authentication -

authentication_timeout = 1min		# 1s-600s
#password_encryption = md5		# md5 or scram-sha-256
#db_user_namespace = off

설정 내용을 변경하는 경우에는 PostgreSQL을 다시 시작 하지 않으면 설정 내용이 반영되지 않는 것과 바로 반영이 되는 것이 있다. 설정 파일 (change requires restart)라고 작성되 있는 것은 다시 시작해야 한다.

각각의 매개 변수에 대해서는 이후 설정이 필요할 때 설명하겠다.

postgresql.conf의 설정 방법에 대해 설명하였다.

1.4.3.2 - PostgreSQL | PostgreSQL 설정 파일 | pg_hba.conf 파일 설정 방법

pg_hba.conf 파일은 PostgreSQL에 접속하는 클라이언트에 대한 인증 설정을 설명하는 파일이다. 여기에서는 pg_hba.conf 파일의 설정 방법에 대해 설명한다.

pg_hba.conf 파일 위치

pg_hba.conf는 postgresql.conf와 마찬가지로 기본적으로 PostgreSQL을 설치한 data 디렉토리에 저장되어 있다.

C:\Program Files\PostgreSQL\12\data>dir
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: XXXX-XXXX

 C:\Program Files\PostgreSQL\12\data 디렉터리

2020-07-08  오전 12:00    <DIR>          .
2020-07-08  오전 12:00    <DIR>          ..
2020-07-08  오전 12:28    <DIR>          base
2020-07-08  오전 12:00                45 current_logfiles
2020-07-08  오전 12:20    <DIR>          global
2020-07-08  오전 12:00    <DIR>          log
2020-03-10  오전 12:24    <DIR>          pg_commit_ts
2020-03-10  오전 12:24    <DIR>          pg_dynshmem
2020-03-10  오전 12:24             4,156 pg_hba.conf <------------- 여기 저장되어 있다.
2020-03-10  오전 12:24             1,678 pg_ident.conf
2020-07-08  오전 12:38    <DIR>          pg_logical
2020-03-10  오전 12:24    <DIR>          pg_multixact
2020-07-07  오후 11:20    <DIR>          pg_notify
2020-03-10  오전 12:24    <DIR>          pg_replslot
2020-03-10  오전 12:24    <DIR>          pg_serial
2020-03-10  오전 12:24    <DIR>          pg_snapshots
2020-07-07  오후 11:20    <DIR>          pg_stat
2020-07-08  오후 11:35    <DIR>          pg_stat_tmp
2020-03-10  오전 12:24    <DIR>          pg_subtrans
2020-03-10  오전 12:24    <DIR>          pg_tblspc
2020-03-10  오전 12:24    <DIR>          pg_twophase
2020-03-10  오전 12:24                 3 PG_VERSION
2020-03-10  오전 12:24    <DIR>          pg_wal
2020-03-10  오전 12:24    <DIR>          pg_xact
2020-03-10  오전 12:24                90 postgresql.auto.conf
2020-03-10  오전 12:24            27,377 postgresql.conf
2020-07-07  오후 11:20                91 postmaster.opts
2020-07-07  오후 11:20                70 postmaster.pid
               8개 파일              33,510 바이트
              20개 디렉터리  424,537,530,368 바이트 남음

C:\Program Files\PostgreSQL\12\data>

pg_hba.conf 파일은 텍스트 파일으로 되어 있어, 내용을 확인하거나 편집하려면 텍스트 편집기에서 파일을 열 수 있다.

# PostgreSQL Client Authentication Configuration File
# ===================================================
#
# Refer to the "Client Authentication" section in the PostgreSQL
# documentation for a complete description of this file.  A short
# synopsis follows.
#
# This file controls: which hosts are allowed to connect, how clients
# are authenticated, which PostgreSQL user names they can use, which
# databases they can access.  Records take one of these forms:
#
# local      DATABASE  USER  METHOD  [OPTIONS]
# host       DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
# hostssl    DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
# hostnossl  DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
#
# (The uppercase items must be replaced by actual values.)
#
# The first field is the connection type: "local" is a Unix-domain
# socket, "host" is either a plain or SSL-encrypted TCP/IP socket,
# "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a
# plain TCP/IP socket.
#
# DATABASE can be "all", "sameuser", "samerole", "replication", a
# database name, or a comma-separated list thereof. The "all"

... 이하 생략 ...

pg_hba.conf 설정

pg_hba.conf 파일은 클라이언트의 주소와 역할 이름을 지정하고 모든 데이터베이스에 연결을 허용할지 여부를 설정하는데 사용한다. 현재 pg_hba.conf에 설정되어 있는 내용을 확인하면 다음과 같다.

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
host    replication     all             127.0.0.1/32            md5
host    replication     all             ::1/128                 md5

한 줄에 하나의 설정으로 되어 있다. 예를 들어, 다음 설정에는 클라이언트의 IP 주소가 127.0.0.1/32의 모든 역할에 대해 복제(replication)를 제외한 모든 데이터베이스에 인증 방식 md5를 사용하여 연결을 허용한다는 의미이다.

# IPv4 local connections:
host    all             all             127.0.0.1/32            md5

127.0.0.1/32하는 것은 로컬 루프백 주소라고 하는 주소에서 자신을 나타내는 IP 주소이다. 이 경우에는 PostgreSQL가 실행중인 서버의 IP 주소이다. localhost도 같은 의미로 사용된다. 또한 IPv6의 로컬 루프백 주소 :: 1/128이다.

그러면 설정하는 항목에 대해 하나 하나 살펴 보겠다.

각 설정은 다음 형식 중 하나이다.

local      DATABASE  USER  METHOD  [OPTIONS]
host       DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
hostssl    DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
hostnossl  DATABASE  USER  ADDRESS  METHOD  [OPTIONS]

TYPE은 local, host, hostssl, hostnossl 중 하나를 설정한다. local은 Unix 도메인 소켓을 사용하는 경우에 사용되는 것으로, 이번에는 Windows 환경이므로 사용하지 않는다. host, hostssl, hostnossl는 모두 TCP / IP를 사용하여 연결하는 경우에 사용하고 host를 지정한 경우 SSL 또는 비 SSL로 연결, hostssl를 사용하면 SSL로 연결, hostnossl 비 SSL로의 연결을 허용한다.

DATABASE에 연결할 수 있는 데이터베이스 이름을 지정한다. 복수 지정하는 경우는 쉼표 (,)로 구분하여 작성한다. 또한 특수한 값으로 all을 지정했을 경우는 복제(replication)를 제외한 모든 데이터베이스를 의미하고 sameuser를 지정한 경우는 사용자 이름과 동일한 이름의 데이터베이스를 의미한다. 또한 samerole을 지정한 경우 연결하는 사용자가 연결하는 데이터베이스와 동일한 이름의 역할의 구성원이어야 한다. 다른 파일에 데이터베이스 이름의 목록을 작성하는 경우 @파일 이름 형식으로 지정할 수도 있다.

USER에 연결할 수 있는 역할 이름을 지정한다. 복수 지정하는 경우는 쉼표(,)로 구분하여 작성한다. 또한 특수한 값 all을 지정하면 모든 역할을 의미한다. 또한 역할 이름 앞에 +를 붙이면 그룹으로 사용되고 있는 역할 이름을 의미한다. 이 경우 이 그룹으로 역할의 구성원이 역할에 연결이 허용된다. 다른 파일에 역할 이름의 목록을 작성하는 경우 @파일 이름 형식으로 지정할 수도 있다.

ADDRESS에 연결할 수 있는 호스트 이름 또는 IP 주소 범위를 지정한다. IP 주소는 IPv4에서 지정하는 경우 172.20.143.0/24와 같은 형식으로 IPv6를 지정하는 경우는fe80::7a31:c1ff:0000:0000/96와 같은 형식으로 지정한다. 그리고 특수한 값 all을 지정하면 모든 IP 주소를 의미하고 samehost로 지정한 경우는 PostgreSQL가 실행중인 서버와 동일한 IP 주소를 의미한다. samenet로 지정한 경우는 서버와 동일한 네트워크를 의미한다.

ADDRESS에서 호스트 이름을 지정하는 경우는 blog.devkuma.com처럼 별도의 호스트 이름을 지정한다. 그리고 .devkuma.com과 같이 지정한 경우 devkuma.com의 모든 호스트 (blog.example.com 등)을 의미한다.

METHOD에는 인증 방법을 지정한다. 설정 값과 인증 방식은 다음을 참조한다.

설정값 설명
trust 모든 역할 이름에 암호없이 연결 가능
reject 연결을 거부
scram-sha-256 암호 인증 가장 안전하지만 일부 클라이언트에서 지원되지 않음
md5 암호 인증
password 암호 인증 암호를 평문으로 전송
gss GSSAPI를 이용한 싱글 사인온
sspi SSPI를 이용한 싱글 사인온
ident Ident 인증
peer Peer 인증
ldap LDAP 인증
radius RADIUS 인증
cert SSL 클라이언트 인증서를 사용한 인증
pam PAM 인증
bsd BSD 인증

예를 들어 192.168.1.0/24 네트워크에서 데이터베이스 mydb에 대한 연결을 사용자 devkuma에 대해 인증 방식 md5에서 허용하는 경우는 다음과 같이 작성한다.

host mydb devkuma 192.168.1.0/24 md5

그리고 pg_hba.conf 파일에서의 설정 내용에 관계없이 postgresql.conf 파일의 listen_addresses 매개 변수로 클라이언트의 IP 주소 또는 호스트가 PostgreSQL에 접속을 허용하지 않으면 연결할 수 없다. “postgresql.conf 설정"을 참조한다.

그러면 시습을 해보자. 현재 클라이언트의 IP 주소가 서버와 동일한 IP 주소 상황에서 postgres 역할에 대해 mydb 데이터베이스에 대한 액세스를 허용해 본다. pg_hba.conf에서 설정되어 기존 내용은 주석으로 처리하고 다음 줄을 추가하였다. 설정 파일의 저장이 끝나면 일단 PostgreSQL을 다시 시작한다.

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# IPv4 local connections:
# host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
# host    all             all             ::1/128                 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
# host    replication     all             127.0.0.1/32            md5
# host    replication     all             ::1/128                 md5

host    mydb            postgres        ::1/128            md5

먼저 연결을 허용하는 설정했던 mydb 데이터베이스에 연결을 해본다. 명령 프롬프트를 시작하고 다음과 같이 실행한다.

psql -U postgres -d mydb

사용자 암호 입력하도록 나오고, postgres 역할의 암호를 입력하면 연결이 된다.

C:\>psql -U postgres -d mydb
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

mydb=# 

일단 PostgreSQL 접속을 종료하고, 이번에는 postgres 데이터베이스에 연결을 시도하기 위해 다음과 같이 실행한다.

psql -U postgres -d postgres

다음과 같은 오류가 표시되어 연결에 실패한다.

C:\>psql -U postgres -d postgres
psql: 오류: 서버 접속 실패: 치명적오류:  호스트 "::1", 사용자 "postgres", 데이터베이스 "postgres", SSL 중지 연결에 대한 설정이 pg_hba.conf 파일에 없습니다.

C:\>

따라서 pg_hba.conf 파일에서 연결을 허용하여 데이터베이스에 연결이 가능하지만, 허가를 하지 않은 데이터베이스에 연결하려고 하면 오류가 발생한다.

pg_hba.conf의 설정 방법에 대해 설명하였다.

1.4.3.3 - PostgreSQL | PostgreSQL 설정 파일 | pg_ident.conf 파일 설정 방법

pg_ident.conf 파일은 클라이언트의 인증 방식으로 Ident 인증을 사용하는 경우, ident 사용자 이름을 PostgreSQL의 역할 이름에 매핑하는데 사용하는 파일이다. 여기에서는 pg_ident.conf 파일의 설정 방법에 대해 설명한다.

pg_ident.conf 파일 위치

pg_ident.conf는 postgresql.conf와 마찬가지로 기본적으로 PostgreSQL을 설치한 data 디렉토리에 저장되어 있다.

C:\Program Files\PostgreSQL\12\data>dir
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: XXXX-XXXX

 C:\Program Files\PostgreSQL\12\data 디렉터리

2020-07-08  오전 12:00    <DIR>          .
2020-07-08  오전 12:00    <DIR>          ..
2020-07-08  오전 12:28    <DIR>          base
2020-07-08  오전 12:00                45 current_logfiles
2020-07-08  오전 12:20    <DIR>          global
2020-07-08  오전 12:00    <DIR>          log
2020-03-10  오전 12:24    <DIR>          pg_commit_ts
2020-03-10  오전 12:24    <DIR>          pg_dynshmem
2020-03-10  오전 12:24             4,156 pg_hba.conf
2020-03-10  오전 12:24             1,678 pg_ident.conf <------------- 여기 저장되어 있다.
2020-07-08  오전 12:38    <DIR>          pg_logical
2020-03-10  오전 12:24    <DIR>          pg_multixact
2020-07-07  오후 11:20    <DIR>          pg_notify
2020-03-10  오전 12:24    <DIR>          pg_replslot
2020-03-10  오전 12:24    <DIR>          pg_serial
2020-03-10  오전 12:24    <DIR>          pg_snapshots
2020-07-07  오후 11:20    <DIR>          pg_stat
2020-07-08  오후 11:35    <DIR>          pg_stat_tmp
2020-03-10  오전 12:24    <DIR>          pg_subtrans
2020-03-10  오전 12:24    <DIR>          pg_tblspc
2020-03-10  오전 12:24    <DIR>          pg_twophase
2020-03-10  오전 12:24                 3 PG_VERSION
2020-03-10  오전 12:24    <DIR>          pg_wal
2020-03-10  오전 12:24    <DIR>          pg_xact
2020-03-10  오전 12:24                90 postgresql.auto.conf
2020-03-10  오전 12:24            27,377 postgresql.conf
2020-07-07  오후 11:20                91 postmaster.opts
2020-07-07  오후 11:20                70 postmaster.pid
               8개 파일              33,510 바이트
              20개 디렉터리  424,537,530,368 바이트 남음

C:\Program Files\PostgreSQL\12\data>

pg_ident.conf 파일은 텍스트 파일으로 되어 있어, 내용을 확인하거나 편집하려면 텍스트 편집기에서 파일을 열 수 있다.

# PostgreSQL User Name Maps
# =========================
#
# Refer to the PostgreSQL documentation, chapter "Client
# Authentication" for a complete description.  A short synopsis
# follows.
#
# This file controls PostgreSQL user name mapping.  It maps external
# user names to their corresponding PostgreSQL user names.  Records
# are of the form:
#
# MAPNAME  SYSTEM-USERNAME  PG-USERNAME
#
# (The uppercase quantities must be replaced by actual values.)
#
# MAPNAME is the (otherwise freely chosen) map name that was used in
# pg_hba.conf.  SYSTEM-USERNAME is the detected user name of the
# client.  PG-USERNAME is the requested PostgreSQL user name.  The
# existence of a record specifies that SYSTEM-USERNAME may connect as
# PG-USERNAME.
#
# If SYSTEM-USERNAME starts with a slash (/), it will be treated as a
# regular expression.  Optionally this can contain a capture (a
# parenthesized subexpression).  The substring matching the capture
# will be substituted for \1 (backslash-one) if present in
# PG-USERNAME.

... 이하 생략 ...

pg_ident.conf 설정

pg_ident.conf 파일의 마지막 부분을 보면 다음과 같은 부분이 있다.

# Put your actual configuration here
# ----------------------------------

# MAPNAME       SYSTEM-USERNAME         PG-USERNAME

현재는 아무것도 설정이 추가되지 않은 상태이다. 매핑을 추가하는 경우 MAPNAME에 매핑에 대한 임의의 이름을 설정하고 SYSTEM-USERNAME에 ident 사용자 이름, 그리고 PG-USERNAME에 PostgreSQL의 역할 이름을 지정한다. 예를 들어 다음과 같이 작성한다.

# Put your actual configuration here
# ----------------------------------

# MAPNAME       SYSTEM-USERNAME         PG-USERNAME
mymap           kim                   kc

그리고 “pg_hba.conf 파일의 설정 방법"에 작성된 pg_hba.conf 파일에 다음과 같은 작성한다.

방금 매핑을 추가할 때 지정한 MAPNAME를 지정하고 있다.

이제 ident에서 kim로 인증 된 사용자가 kc로 PostgreSQL에 접속이 허용된다.

단지 내 환경에서 직접 테스트 할 수 없었기 때문에 올바르게 설정되어 있는지 확인할 수 없다.

pg_ident.conf 설정 방법에 대해 설명하였다.

1.4.4 - PostgreSQL | PostgreSQL 기본 구성

PostgreSQL를 이용하는데있어서 숫자 나 문자열의 작성 방법 등 기본적인 구문에 대해 설명한다.

1.4.4.1 - PostgreSQL | PostgreSQL 기본 구성 | 문자열이나 숫자 값을 입력하는 방법

문자열 상수의 입력 방법

문자열 작성은 작은 따옴표 (’)로 묶어 작성한다.

'문자열'

문자열에서 작은 따옴표를 넣으로면 2개의 작은 따옴표를 연속으로 작성한다.

devkuma=# select 'My father''s car';
    ?column?
-----------------
 My father's car
(1개 행)

문자열 중에 특별히 취급해야하는 문자는 작은 따옴표를 문자로 작성하면 된다. \ 등은 그대로 문자로 처리된다.

문자열은 줄 바꿈(빈칸)을 사이에 두고 이어서 작성하게 되면 하나의 문자열로 처리된다.

devkuma=# select 'Hello'
devkuma-# 'World';
  ?column?
------------
 HelloWorld
(1개 행)

문자열 ‘Hello’와 문자열 ‘World’는 줄 바꿈을 사이에 두고 나뉘어져 있지만 하나의 문자열로 처리된다.

이스케이프 문자열

키보드에서 입력할 수 없는 문자를 문자열에 표시하려는 경우 또는 특별한 문자를 입력하려면, PostgreSQL에서는 이스케이프 문자열을 사용한다. 이스케이프 문자열은 작은 따옴표 앞에 E 또는 e를 넣는다.

E'문자열'
e'문자열'

이스케이프 문자열 중에서는 다음과 같은 특수 문자를 입력할 수 있다.

\b      백스페이스 문자
\f      페이지 나누기
\n      개행
\r      캐리지 리턴
\t      탭 문자
\o      8진수 바이트 값
\xh     16 진수 바이트 값
\uxxxx  16 또는 32 비트의 16 진수 Unicode 문자 값

이스케이프 문자열에서 \을 문자로 사용하는 경우 \\와 같이 2개의 이어서 작성한다. 또한 작은 따옴표를 문자로 작성하는 경우 일반 문자열처럼 ''와 같이 2개를 이이서 작성하는 이외에 \'로도 작성할 수 있다.

아래에서 탭 문자를 포함해서 표시하고 있다.

postgres=# select e'문자\t열';
  ?column?
------------
 문자    열
(1개 행)

숫자 상수 입력 방법

수를 작성하는 경우 그대로 수를 작성하면 된다.

7
105
3.512

소수점을 작성하는 경우, 전후에 적어도 1개의 수가 필요하다.

.552
8.

지수 기호 e를 사용하여 작성할 수도 있다. 그런 경우는 적어도 1개의 e의 뒤에 숫자가 필요하다.

8e5
1.41e-3

숫자 앞에 + 또는 -를 선택한 경우 + 또는 - 숫자의 일부가 아닌 연산자로 처리된다.

-42
+602

비트 문자열 상수의 입력 방법

비트 문자열 상수를 작성하는 경우 일반 문자열 앞에 B 또는 b를 작성한다. 문자열로 사용할 수 있는 문자는 0 또는 1뿐이다.

B'01 '
b'1001 '

2진수 대신 16진수 표기로 작성할 수 있다. 일반 문자열 앞에 X 또는 x를 넣는다. 문자열로 사용할 수 있는 문자는 0에서 9 및 A ~ F이다.

X'3F '
x'A37E '

PostgreSQL에서 문자열이나 숫자 등의 값을 입력 할 경우 방법에 대해 설명하였다.

1.4.4.2 - PostgreSQL | PostgreSQL 기본 구성 | 식별자와 키워드

PostgreSQL의 식별자와 키워드에 대해 설명한다.

식별자와 키워드

예를 들어, 다음과 같이 SQL 명령을 예로 살펴 보겠다.

SELECT * FROM MYTBL;

이때 SELECT와 FROM 같이 SQL에서 의미를 가지고 있는 단어는 키워드라고 하고, 테이블 이름에 사용되는 MYTBL은 식별자라고 한다.

키워드는 따옴표를 붙이지 않는 경우는 식별자로 사용할 수 없다. 예를 들어, 테이블명으로 키워드 SELECT를 사용하여 테이블을 만들려고 하면 오류가 발생한다.

postgres=# create table select (id integer);
오류:  구문 오류, "select" 부근
줄 1: create table select (id integer);
                   ^
postgres=#

식별자와 키워드는 인용 식별자 이외는 대소 문자를 구분하지 않는다. 그러기에 다음 두 SQL 명령은 같은 의미이다.

SELECT * FROM MYTBL;
select * from mytbl;

식별자와 키워드는 첫 번째 문자는 문자 또는 밑줄(_)로 시작하고, 두번째 이후는 문자는 문자, 밑줄, 숫자, 달러 기호 ($)를 사용할 수 있다. 단지 식별자에 달러 기호는 사용할 수 없는 데이터베이스도 있으므로 자주 사용하지 않는 것이 좋다.

인용(따옴표) 식별자

SELECT 또는 UPDATE 등 SQL로 의미를 가진 단어는 키워드로 PostgreSQL에 등록되어 있다. 키워드는 식별자는 기본적으로 사용할 수 없다. 단지 키워드와 일반적 사용할 수 없는 공백을 포함한 값은 인용함으로써 식별자로 데이터베이스 이름과 테이블 이름에 사용할 수 있다.

인용 식별자 사용하려면 문자열을 큰 따옴표 (")로 둘러 쌓아야 한다. 예를 들어, 테이블 이름으로 키워드 SELECT를 사용하여 테이블을 만들려면 다음과 같이 인용 식별자를 사용한다.

postgres=# create table "select"(id integer);
CREATE TABLE
postgres=#

이번에는 오류가 발생하지 않고 테이블을 만들 수 있었다. 여기서 테이블 이름은 SELECT 대신 “SELECT"으로 한 점이 앞에서와는 달랐다.

인용 식별자으로는 어떤 문자도 포함할 수 있다. 큰 따옴표 자신을 문자로 사용하는 경우는 2개 겹쳐서 ("") 작성한다.

PostgreSQL로 등록 된 키워드 목록

PostgreSQL로 등록되어 있는 키워드는 다음과 같다.

ALL
ANALYSE
ANALYZE
AND
ANY
ARRAY
AS
ASC
ASYMMETRIC
AUTHORIZATION (함수 또는 형식으로 사용 가능)
BINARY (함수 또는 형식으로 사용 가능)
BOTH
CASE
CAST
CHECK
COLLATE
COLLATION (함수 또는 형식으로 사용 가능)
COLUMN
CONCURRENTLY (함수 또는 형식으로 사용 가능)
CONSTRAINT
CREATE
CROSS (함수 또는 형식으로 사용 가능)
CURRENT_CATALOG
CURRENT_DATE
CURRENT_ROLE
CURRENT_SCHEMA (함수 또는 형식으로 사용 가능)
CURRENT_TIME
CURRENT_TIMESTAMP
CURRENT_USER
DEFAULT
DEFERRABLE
DESC
DISTINCT
DO
ELSE
END
EXCEPT
FALSE
FETCH
FOR
FOREIGN
FREEZE (함수 또는 형식으로 사용 가능)
FROM
FULL (함수 또는 형식으로 사용 가능)
GLOBAL
GRANT
GROUP
HAVING
ILIKE (함수 또는 형식으로 사용 가능)
IN
INLINE
INNER (함수 또는 형식으로 사용 가능)
INTERSECT
INTO
IS (함수 또는 형식으로 사용 가능)
ISNULL (함수 또는 형식으로 사용 가능)
LATERAL
LEFT (함수 또는 형식으로 사용 가능)
LIKE (함수 또는 형식으로 사용 가능)
LIMIT
LOCALTIME
LOCALTIMESTAMP
NATURAL (함수 또는 형식으로 사용 가능)
NOT
NOTNULL (함수 또는 형식으로 사용 가능)
NULL
OFFSET
ON
ONLY
OR
ORDER
OUTER (함수 또는 형식으로 사용 가능)
OVERLAPS (함수 또는 형식으로 사용 가능)
PLACING
PRIMARY
REFERENCES
RETURNING
RIGHT (함수 또는 형식으로 사용 가능)
SELECT
SESSION_USER
SIMILAR (함수 또는 형식으로 사용 가능)
SOME
SYMMETRIC
TABLE
TABLESAMPLE (함수 또는 형식으로 사용 가능)
THEN
TO
TRAILING
TRUE
UNION
UNIQUE
USER
USING
VARIADIC
VERBOSE (함수 또는 형식으로 사용 가능)
WHEN
WHERE
WINDOW
WITH

여기까지 PostgreSQL의 식별자와 키워드에 대해 설명하였다.

1.4.4.3 - PostgreSQL | PostgreSQL 기본 구성 | 주석(comment) 작성

PostgreSQL로 SQL을 작성 할 때 주석을 작성하는 방법에 대해 설명한다.

주석을 작성하는 방법

SQL 문을 작성할 때, 주석을 하고자하는 경우에는 하이픈을 두개 연속해서 작성(--) 한 곳에서 문장의 끝까지가 주석이다.

-- 한줄이 주석이다.

그리고, 여러 줄의 코멘트를 작성하려면 /*에서 */까지의 부분을 주석이다.

/* 여기에서부터 주석이다.
여러 줄의 주석이 가능하다.
*/

주석은 실행이 되면 완전히 무시가 되어서 psql을 커멘드 라인 기반에서 SQL 명령을 실행하면 주석 부분은 아무 의미가 없지만, 다른 파일에 SQL 명령을 작성 두었다가 파일을 로드하고 실행하는 경우 등에 사용할 수 있다.

그럼 실제로 해보도로 하자. 텍스트 편집기를 열고 다음과 같이 작성한다.

/*
테이블 생성 및 데이터 추가
2020/10/26
*/

-- 테이블 만들기
create table friends (id integer, name varchar (10));

-- 데이터 추가
insert into friends values (1, 'kimkc');
insert into friends values (2, 'hwang.yh');
insert into friends values (5, 'lim.yt');

작성한 파일을 이번에는 test.sql라는 이름으로 c:\dev 디렉토리에 저장한다.

현재 psql의 문자 코드를 확인하면 UHC로 되어 있으므로, 파일 문자 코드은 EUC_KR로 저장한다.

postgres=# \encoding
UHC
postgres=# 

그러면 psql을 사용하여 PostgreSQL의 mydb 데이터베이스에 연결한다.

C:\Users\kimkc>psql -U postgres -d mydb
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

mydb=#

저장된 파일을 로드하고 실행하려면 psql 명령어로 \i를 사용하여 다음과 같이 실행한다.

mydb=# \i c:/dev/test.sql
CREATE TABLE
INSERT 0 1
INSERT 0 1
INSERT 0 1
mydb=#

파일에 작성한 코멘트 부분은 무시되고 그 이외의 부분이 성공적으로 실행되었다. 확인을 위해 psql에서 다음과 같이 SQL 명령을 실행해 본다.

mydb=# select * from friends;
 id |   name
----+----------
  1 | kimkc
  2 | hwang.yh
  5 | lim.yt
(3개 행)

테이블에 작성되어 있고, 데이터가 3건 추가되어 있는 것을 확인할 수 있다.

여기까지 PostgreSQL로 주석을 작성하는 방법에 대해 설명하였다.

1.4.4.4 - PostgreSQL | PostgreSQL 기본 구성 | 논리 연산자 사용(AND, OR, NOT)

PostgreSQL에서는 논리 연산자로 AND, OR, NOT의 세 가지가 준비되어 있다. 여기에서는 논리 연산자의 사용법에 대해 설명한다.

※ PostgreSQL에서 참이거나 거짓을 나타내는 값이 무엇인가에 대해서는 “논리 값 데이터 형식(boolean)“을 참조하도록 한다.

논리곱 (AND)

AND는 논리곱이다. 좌변과 우변이 TRUE, FALSE, NULL이었을 때, 어떻게 판단되는지는 다음 목록을 보도록 하자.

TRUE   AND  TRUE   -> TRUE
TRUE   AND  FALSE  -> FALSE
TRUE   AND  NULL   -> NULL
FALSE  AND  FALSE  -> FALSE
FALSE  AND  NULL   -> FALSE
NULL   AND  NULL   -> NULL

전체 표현식이 TRUE가 되는 것은 좌변과 우변이 모두 TRUE 인 경우 뿐이다. 또한 TRUE AND NULLNULL인데 비해 FALSE AND NULLFALSE가 되는 점에 주의한다.

그럼 실제로 해보도록 하자. 다음과 같은 테스트 테이블을 만든다.

mydb=# create table test(flag1 boolean,  flag2 boolean);
CREATE TABLE
mydb=#

다음과 같은 데이터를 테이블에 추가한다.

mydb=# insert into test values (TRUE, TRUE), (TRUE, FALSE), (TRUE, NULL), (FALSE, FALSE), (FALSE, NULL), (NULL, NULL);
INSERT 0 6
mydb=# select * from test;
 flag1 | flag2
-------+-------
 t     | t
 t     | f
 t     |
 f     | f
 f     |
       |
(6개 행)

그러고 SELECT 명령을 사용하여 논리곱으로 좌변과 우변의 값에 의해 전체가 어떻게 판단되는지를 해보자.

mydb=# select flag1, flag2, flag1 and flag2 as "flag1 and flag2" from test;
 flag1 | flag2 | flag1 and flag2
-------+-------+-----------------
 t     | t     | t
 t     | f     | f
 t     |       |
 f     | f     | f
 f     |       | f
       |       |
(6개 행)

이것으로 좌변과 우변의 값에 의해 논리곱 결과가 어떻게 되지는 알 수 있다.

논리합(OR)

OR은 논리합입니다. 좌변과 우변이 TRUE, FALSE, NULL이었을 때, 어떻게 판단되는지는 다음 목록을 보도록 하자.

TRUE   OR  TRUE   -> TRUE
TRUE   OR  FALSE  -> TRUE
TRUE   OR  NULL   -> TRUE
FALSE  OR  FALSE  -> FALSE
FALSE  OR  NULL   -> NULL
NULL   OR  NULL   -> NULL

좌변과 우변의 적어도 어느 하나가 TRUE 인 경우 전체 표현식이 TRUE이다. 또한 TRUE OR NULLTRUE 인 반면 FALSE AND NULLNULL이 되는 점에주의한다.

그럼 실제로 해보도록 하자. 다음의 같이 논리곱으로 사용한 테이블을 그대로 사용하여 SELECT 명령을 사용하여 논리합의 좌변과 우변에 의해 어떻게 판단되는지 해보자.

mydb=# select flag1, flag2, flag1 or flag2 as "flag1 or flag2" from test;
 flag1 | flag2 | flag1 or flag2
-------+-------+----------------
 t     | t     | t
 t     | f     | t
 t     |       | t
 f     | f     | f
 f     |       |
       |       |
(6개 행)

이것으로 좌변과 우변의 값에 의해 논리합 결과가 어떻게 되지는 알 수 있다.

부정 (NOT)

NOT 연산자는 부정이다. 우변이 TRUE, FALSE, NULL이었을 때, 어떻게 판단되는지는 다음 목록을 보도록 하자.

NOT  TRUE   -> FALSE
NOT  FALSE  -> TRUE
NOT  NULL   -> NULL

우변이 TRUE이면 FALSE가 되고, 우변이 FALSE이면 TRUE가 된다. 또한 NOT NULL은 NULL이 된다.

그럼 실제로 해보도록 하자. 다음과 같은 테스트 테이블을 만든다.

mydb=# create table test2( flag boolean);
CREATE TABLE
mydb=#

다음과 같은 데이터를 테이블에 추가한다.

mydb=# insert into test2 values (TRUE),(FALSE),(NULL);
INSERT 0 3
mydb=#

그러면 SELECT 명령을 사용하여 부정의 우변의 값에 의해 결과가 어떻게 판단되는지를 해보자.

mydb=# select flag, not flag as "not flag" from test2;
 flag | not flag
------+----------
 t    | f
 f    | t
      |
(3개 행)

이것으로 우변의 값에 의해 부정의 결과가 어떻게 되지는 알 수 있다.

여기까지 PostgreSQL에서 제공되는 논리 연산자의 사용법에 대해 설명하였다.

1.4.4.5 - PostgreSQL | PostgreSQL 기본 구성 | 비교 연산자 사용 (<, >, =, BETWEEN, IS NULL)

비교 연산자는 두 값을 비교하여 크거나 작거나 같은지 다른지 등의 비교 판단을 한다. 여기에서 비교 연산자의 사용법에 대해 설명한다.

크거나 작거나 같은지 비교

비교 연산자는 왼쪽과 오른쪽을 비교하여 결과로서 boolean 형의 값을 돌려준다.

크기를 비교하는 비교 연산자 <, <= , >, >= 이렇게 4가지가 준비되어 있다.

<    왼쪽은 오른쪽보다 작다
>    외쪽이 오른쪽보다 크다
<=   왼쪽은 오른쪽보다 작거나 같다
>=   왼쪽은 오른쪽보다 크거나 같다

같은지에 대한 비교 연산자 =, <>, != 이렇게 3가지가 준비되어 있다.

=    왼쪽과 오른쪽은 동일하다
<>   왼쪽과 오른쪽은 동일하지 않다
!=   왼쪽과 오른쪽은 동일하지 않다

그럼 실제로 해보도록 하자. 다음과 같은 테스트 테이블을 만든다.

mydb=# create table customer(name varchar(10),  old integer);
CREATE TABLE
mydb=#

다음과 같은 데이터를 테이블에 추가한다.

mydb=# insert into customer values  ( 'kimkc', 41),  ( 'devkuma', 20),  ( 'yunho', 32),  ( 'yongtea', 34),  ( 'jiwoo', 12);
INSERT 0 5
mydb=#

그러고 SELECT 명령을 사용하여 old 컬럼의 값이 20보다 크거나 작거나, 아니면 같은지에 대해 알아 보겠다.

mydb=# select name, old,
mydb-#   old <20 as "20보다 작다",
mydb-#   old > 20 as "20보다 크다",
mydb-#   old = 20 as "20와 같다."
mydb-# from customer;
  name   | old | 20보다 작다 | 20보다 크다 | 20와 같다.
---------+-----+-------------+-------------+------------
 kimkc   |  41 | f           | t           | f
 devkuma |  20 | f           | f           | t
 yunho   |  32 | f           | t           | f
 yongtea |  34 | f           | t           | f
 jiwoo   |  12 | t           | f           | f
(5개 행)

비교 연산자를 사용하여 컬럼에 저장되는 값을 평가한 결과를 가져 왔다.

범위 내에 있는지 비교 (BETWEEN)

between 조건은 비교 대상의 값이 지정된 범위 내에 있는지 여부를 비교하고 결과로서 boolean 형의 값을 반환한다.

a BETWEEN x AND y       x 이상 y 이하의 범위에 있다
a NOT BETWEEN x AND y   x 이상 y 이하의 범위에 없다

비교 대상 값 a가 x 이상이고 y 이하이면 TRUE이다. NOT이 있다면 x 이상이고 y 이하가 아닌 경우 TRUE이다.

between 조건은 비교 연산자와 논리 연산자를 사용하여 다음과 같이 작성할 수 있다. 아래 2개 식는 같은 의미이다.

a >= x AND a <= y
a BETWEEN x AND y

또한 not between 조건도 비교 연산자와 논리 연산자를 사용하여 다음과 같이 작성할 수 있다. 아래 2개 식는 같은 의미이다.

a <x OR a> y
a NOT BETWEEN x AND y

a BETWEEN x AND y와 같이 지정한 경우에 x가 y보다 큰 값이 지정되면, x 이상 y 이하의 값은 존재하지 않기 때문에 항상 FALSE이다. SYMMETRIC을 지정하면 x와 y의 값을 정렬하여, 작은 값에서 큰 값의 범위 내에 있는지를 판단한다.

a BETWEEN SYMMETRIC x AND y
a NOT BETWEEN SYMMETRIC x AND y

비교 대상 값인 a가 x 또는 y의 작은 값에서 큰 값의 범위 내에 있으면 TRUE이다. NOT을 가진 경우는 반대가 된다.

그러면 실제로 해보도록 하자. 앞에서 작성한 customer 테이블에 대해 old 컬럼의 값이 15에서 25의 범위 내에 있는지 확인한다.

mydb=# select name, old,
mydb-#   old between 15 and 33 as "15 이상 33 이하"
mydb-# from customer;
  name   | old | 15 이상 33 이하
---------+-----+-----------------
 kimkc   |  41 | f
 devkuma |  20 | t
 yunho   |  32 | t
 yongtea |  34 | f
 jiwoo   |  12 | f
(5개 행)

지정한 범위 내에 있는 경우는 TRUE, 그렇지 같은 경우는 FALSE가 되었다.

다음 between 조건의 범위를 나타내는 숫자를 바꿔보자.

mydb=# select name, old,
mydb-#   old between 33 and 15 as "33 이상 15 이하"
mydb-# from customer;
  name   | old | 33 이상 15 이하
---------+-----+-----------------
 kimkc   |  41 | f
 devkuma |  20 | f
 yunho   |  32 | f
 yongtea |  34 | f
 jiwoo   |  12 | f
(5개 행)

33 이상 15 이하의 범위는 존재하지 않기 때문에 모두 FALSE가 되었다.

여기에 SYMMETRIC을 지정하면 범위를 지정한 두 값을 정렬 범위 지정이 제대로 된 결과가 나온다.

mydb=# select name, old,
mydb-#   old between symmetric 33 and 15 as "15 이상 33 이하"
mydb-# from customer;
  name   | old | 15 이상 33 이하
---------+-----+-----------------
 kimkc   |  41 | f
 devkuma |  20 | t
 yunho   |  32 | t
 yongtea |  34 | f
 jiwoo   |  12 | f
(5개 행)

15과 33의 작은 값에서 큰 값까지의 범위 내에 있으면 TRUE, 그렇지 않으면 FALSE가 된다.

값이 NULL인지 비교 (IS NULL, IS NOT NULL)

IS NULL는 비교 대상의 값이 NULL인지 비교하고 결과로 boolean 형의 값을 돌려준다.

expression IS NULL       expression이 NULL이다.
expression IS NOT NULL   expression이 NULL이 아니다.

비교 대상의 값이 NULL이면 TRUE이다. NOT이 있다면 NULL이면 TRUE이다. 값을 NULL인지 확인 때 비교 연산자 = 로 비교하면 맞는 결과가 나오지 않는다.

또한 비표준 구문으로 다음과 같은 결과가 되는 다음의 조건도 준비되어 있다.

expression ISNULL       expression이 NULL이다.
expression NOTNULL      expression이 NULL이 아니다.

그럼 실제로 해보도록 하자. 다음과 같은 테스트 테이블을 만든다.

mydb=# create table friends(name varchar(10), old integer);
CREATE TABLE
mydb=#

다음과 같은 데이터를 테이블에 추가했습니다.

mydb=# insert into friends values ('kimkc', 39), ('devkuma', NULL), (NULL, 19);
INSERT 0 3
mydb=# \pset null 'NULL'
Null 값은 "NULL" 문자로 보여짐.
mydb=# select * from friends;
  name   | old
---------+------
 kimkc   |   39
 devkuma | NULL
 NULL    |   19
(3개 행)

※ 기본적으로 NULL을 검색하여 표시하면 아무것도 표시되지 않기 때문에, NULL 인 경우 ‘NULL’로 표시되도록 설정되어 있다.

그러면 SELECT 명령을 사용하여 name 컬럼 및 old 컬럼에 저장되는 값이 NULL인지 판별해 본다.

mydb=# select name, name is null as "NULL?", old, old is null as "NULL?" from friends;
  name   | NULL? | old  | NULL?
---------+-------+------+-------
 kimkc   | f     |   39 | f
 devkuma | f     | NULL | t
 NULL    | t     |   19 | f
(3개 행)

컬럼에 저장되는 값이 NULL이면 TRUE, 그렇지 않으면 FALSE가 되었다.

여기까지 PostgreSQL에서 제공되는 비교 연산자의 사용법에 대해 설명하였다.

1.4.4.6 - PostgreSQL | PostgreSQL 기본 구성 | 산술 연산자 사용

산술 연산자는 사칙 연산 등의 계산 외, 제곱근과 계승 등을 위해 이용한다. 여기에서는 산술 연산자의 사용법에 대해 설명한다.

산술 연산자의 종류와 사용법

PostgreSQL로 이용할 수 있는 산술 연산자는 다음과 같다다.

+   더하기   2 + 3    ->  5
-   빼기    2 - 3    ->  -1
*   곱하기   2 * 3    ->  6
/   나누기   4 / 2    ->  2  ※ 정수의 나눗셈에서는 나머지를 버린다.
%   나머지   5 % 4    ->  1
^   제곱     2.0^3.0  ->  8  ※ 左から右に適用
|/  제곱근   |/25.0   ->  5
||/ 세제곱근  ||/27.0  ->  3
!   계승    5!       ->  120
!!  계승    !!5      ->  120  ※ 전치 연산자
@   절대치   @-5.0    ->  5

위의 산술 연산자는 모든 수치 데이터 형으로 사용 할 수 있다.

그럼 실제로 해보도록 하자. 다음과 같은 테스트 테이블을 만든다.

mydb=# create table test( num1 real,  num2 real);
CREATE TABLE
mydb=#

다음과 같은 데이터를 테이블에 추가한다.

mydb=# insert into test values (15.4, 7.25), (-5.9, 9.0), (18.225, -7.3);
INSERT 0 3
mydb=# select * from test;
  num1  | num2
--------+------
   15.4 | 7.25
   -5.9 |    9
 18.225 | -7.3
(3개 행)

그러고 SELECT 명령을 사용하여 num1 컬럼과 num2 컬럼에 대해 연산을 수행한 결과를 가져본다.

mydb=# select num1, num2,
mydb-#   num1 + num2 as "num1 + num2",
mydb-#   num1 * num2 as "num1 * num2",
mydb-#   num1 / num2 as "num1 / num2"
mydb-# from test;
  num1  | num2 | num1 + num2 | num1 * num2 | num1 / num2
--------+------+-------------+-------------+-------------
   15.4 | 7.25 |       22.65 |  111.649994 |   2.1241379
   -5.9 |    9 |         3.1 |  -53.100002 | -0.65555555
 18.225 | -7.3 |      10.925 |  -133.04251 |  -2.4965754
(3개 행)

산술 연산자를 사용하여 컬럼에 저장되는 값에 대한 연산한 결과를 얻을 수 있다.

비트 연산자 사용

산술 연산자는 다른 비트 연산을 할 연산자도 준비되어 있다.

&   비트 AND          91 & 15  ->  11
|   비트 OR            32 | 3  ->  35
#   비트 XOR           17 # 5  ->  20
~   비트 NOT           ~1      ->  -2
<<  비트 왼쪽 시프트    1 << 4  ->  16
>>  비트 오른쪽 시프트  8 >> 2  ->  2

비트 연산자는 정수 데이터 형식 또는 비트 문자열로 사용할 수 있다.

비트 AND 연산자의 왼쪽과 오른쪽의 같은 위치에 있는 비트를 비교하여 두 비트가 모두 1인 경우에만 1로 한다.

85 & 15

0000000001010101 = 85
0000000000001111 = 15
------------------------
0000000000000101 = 5

비트 OR 연산자의 왼쪽과 오른쪽의 같은 위치에 있는 비트를 비교하여 어느 하나라도 비트 1의 경우에 1로 한다.

85 | 15

0000000001010101 = 85
0000000000001111 = 15
------------------------
0000000001011111 = 95

비트 XOR 연산자의 왼쪽과 오른쪽의 같은 위치에 있는 비트를 비교하여 어느 하나가 비트 1의 경우에 1한다. 모두 1이거나 모두 0이면 0으로한다.

85 # 15

0000000001010101 = 85
0000000000001111 = 15
------------------------
0000000001011010 = 90

비트 NOT 연산자 오른쪽의 값의 각 비트를 반전 (0이라면 1, 1이면 0으로 한다)시킨다.

~ 85

0000000001010101 = 85
------------------------
1111111110101010 = -86

비트 왼쪽 시프트는 대상의 값을 지정한 수 만큼 왼쪽으로 이동한다.

85 << 2

0000000001010101 = 85
------------------------
0000000101010100 = 340

비트 오른쪽 시프트는 대상의 값을 지정된 수 만큼 오른쪽으로 이동한다.

85 >> 2

0000000001010101 = 85
------------------------
0000000000010101 = 21

SELECT 명령을 사용하여 간단히 해본다.

mydb=# select 85 & 15 as "85 & 15",
mydb-#   85 | 15 as "85 | 15",
mydb-#   85 # 15 as "85 # 15",
mydb-#   ~ 85 as "~ 85",
mydb-#   85 << 2 as "85 << 2",
mydb-#   85 >> 2 as "85 >> 2";
 85 & 15 | 85 | 15 | 85 # 15 | ~ 85 | 85 << 2 | 85 >> 2
---------+---------+---------+------+---------+---------
       5 |      95 |      90 |  -86 |     340 |      21
(1개 행)

비트 연산자를 사용하여 연산한 결과를 얻을 수 있다.

여기까지 PostgreSQL에서 제공되는 산술 연산자의 사용법에 대해 설명하였다.

1.4.5 - PostgreSQL | 데이터 형식(Data type)

PostgreSQL로 이용할 수있는 데이터 형식 및 사용 방법에 대해 설명한다.

1.4.5.1 - PostgreSQL | 데이터 형식(Data type) | 숫자 형식(integer, decimal, double precision 등)

PosgtreSQL에서 사용할 수있는 데이터 타입에서 숫자 형식의 사용법에 대해 설명하도록 하겠다. 숫자는 정수 데이터 타입 (smallint, integer, bigint), 정밀한 숫자 (numeric, decimal), 부동 소수점 데이터 형 (real, double precision)이 포함되어 있다. 자동 증가 타입은 다음 페이지에서 설명한다.

정수 형식 (smallint, integer, bigint)

먼저 정수 데이터 형식이다. 취급 숫자의 범위가 다른 3 가지 데이터 타입이 있다.

형식 크기 범위 별칭
smallint 2 바이트 -32768에서 +32767 int2
integer 4 바이트 -2147483648에서 +2147483647 int, int4
bigint 8 바이트 -9223372036854775808에서 +9223372036854775807 int8

정수이므로 소수점이 있는 숫자는 처리 할 수 없다. 소수점이 있는 숫자를 저장하게 되면 정수로 변환되어 저장된다. 또한 각 데이터 타입에 저장할 수 있는 값의 범위가 정해져 있어 범위를 초과 한 값을 저장하려고 하면 오류가 발생한다.

예를 들어, 다음과 같은 테이블을 만들었다.

mydb=# create table numtest1 (num1 smallint, num2 integer, num3 bigint);
CREATE TABLE
mydb=#

각각의 컬럼에 설정되어 있는 데이터 형의 범위 내의 값이면 저장할 수 있다. 예를 들어, 다음과 같은 데이터를 저장한다.

mydb=# insert into numtest1 values (8000, 350000, 4000000000);
INSERT 0 1
mydb=#

범위를 초과하는 값을 저장하려고하면 오류가 발생한다. 예를 들어 num1 컬럼은 smallint 형이므로 저장할 수있는 값의 범위는 -32768에서 +32767까지이다. 이 값을 초과하여 50000을 값으로 저장하려고 한다.

mydb=# insert into numtest1 values (50000, 50000, 50000);
오류:  smallint1의 범위를 벗어났습니다.
mydb=#

num1 컬럼에 저장할 수있는 값의 범위를 초과 한 값을 저장하려고 하였기에 “오류: smallint의 범위를 벗어났습니다.“라는 오류가 발생하였다.

정밀한 숫자 (numeric, decimal)

다음은 정밀한 숫자이다.

타입 크기 특징 범위 별칭
numeric 가변 사용자 지정, 정확 소수점 위 131072 자리까지 소수점 아래는 16,383 자리까지 decimal

numeric 및 decimal 타입은 매우 큰 숫자를 저장할 수 있으며 연산을 해도 오차가 발생하고 정확하게 할 수 있는 데이터 타입이다. 그러나 처리는 정수와 부동 소수점 데이터 형과 비교해 매우 늦어 지므로 주의가 필요하다.

numeric 형을 지정하려면 다음 유형으로 한다.

NUMERIC(precision, scale) NUMERIC(precision) NUMERIC

precision는 숫자 전체의 최대 정확도, scale은 소수점 이하 자릿수를 지정한다. 예를 들어 numeric (5, 2)하면 소수점 이하 2자리, 전체 5자리 정확도의 값을 저장할 수 있기 때문에 저장할 수 있는 값의 범위는 -999.99에서 999.99가 된다.

scale을 생략하면 0이 지정된 것으로 간주된다. 또한 precision과 scale이 모두 생략된 경우는 가능한 최대의 정확도 및 소수점 이하의 자리수가 설정된다.

예를 들어, 다음과 같은 테이블을 만들었다.

mydb=# create table numtest2 (num numeric(5, 2));
CREATE TABLE
mydb=#

컬럼에 설정되어 있는 데이터 형의 범위 내의 값이면 저장할 수 있다. 예를 들어 다음과 같은 3개의 데이터를 저장한다.

mydb=# insert into numtest2 values (45), (34.25), (-752.4);
INSERT 0 3
mydb=#

정수 부분의 범위를 넘는 값을 저장하려고하면 오류가 발생한다. 예를 들어 num 컬럼은 numeric(5, 2) 형이므로 저장할 수 있는 값의 범위는 -999.99에서 +999.99까지이다. 이 값을 초과하는 1500.2 을 저장하려고 한다.

mydb=# insert into numtest2 values (1500.2);
오류:  수치 필드 오버플로우
상세정보:  전체 자릿수 5, 소수 자릿수 2의 필드는 10^3보다 작은 절대 값으로 반올림해야 합니다.
mydb=#

num 컬럼에 저장할 수있는 값의 범위를 초과 한 값을 저장하였기에 “오류: 수치 필드 오버플로우” 오류가 발생한다.

소수점 이하의 자리수가 지정한 범위를 초과하면 오류가 되지 않고 지정된 자릿수로 변환되어 저장된다. 예를 들어 다음과 같은 값을 2개 포함 해 보자.

mydb=# insert into numtest2 values (32.245),(-8.5224);
INSERT 0 2
mydb=# select * from numtest2;
   num
---------
   45.00
   34.25
 -752.40
   32.25
   -8.52
(5개 행)


mydb=#

32.245는 32.5로, -8.5224는 -8.52으로 저장되었다.

부동 소수점 데이터 타입 (real, double precision)

마지막으로 부동 소수점 데이터 형식이다. 취급 숫자의 범위가 서로 다른 두 가지 데이터 타입이 있다.

형식 크기 특징 범위 별칭
real 4 바이트 가변 정밀도, 부정확 최소 6 자리의 정밀도 (적어도 1E-37에서 1E + 37) float4
double precision 8 바이트 가변 정밀도, 부정확 최소 15 자리의 정밀도 (약 1E-307에서 1E + 308) float8

부동 소수점에서 저장하고 검색 할 때 오차가 발생할 수 있으므로 정확한 연산 등에는 적합하지 않다.

예를 들어, 다음과 같은 테이블을 만들었다.

mydb=# create table numtest3 (num1 real, num2 double precision);
CREATE TABLE
mydb=# 

컬럼에 설정되어 있는 데이터 형의 범위 내의 값이면 저장할 수 있다. 예를 들어, 다음과 같은 데이터를 저장한다.

mydb=# insert into numtest3 values (15.775, 812.5532245);
INSERT 0 1
mydb=# select * from numtest3;
   num1   |        num2
----------+--------------------
   15.775 |        812.5532245
(1개 행)


mydb=# 

각각의 데이터 형 이상의 자릿수의 값을 저장하려고하면 반올림 저장 될 수 있습니다. 예를 들어 다음과 같은 데이터를 저장한다.

mydb=# insert into numtest3 values (9.4475658, 52.75120024568652456);
INSERT 0 1
mydb=# select * from numtest3;
   num1   |        num2
----------+--------------------
   15.775 |        812.5532245
 9.447566 | 52.751200245686526
(2개 행)


mydb=# 

9.4475658는 9.44757으로 52.75120024568652456는 52.7512002456865으로 반올림되어 저장되었다.

범위를 초과하는 값을 저장하려고 하면 오류가 발생한다. 예를 들어 다음과 같은 값을 저장하려고 한다.

mydb=# insert into numtest3 values (4.8e50, 4.8e50);
오류:  "480000000000000000000000000000000000000000000000000"는 real 자료형의 범위를 벗어납니다.
mydb=#

저장할 수 있는 값의 범위를 초과 한 값을 저장하려고했기에 범위에 벗어났다는 오류가 발생되었다.

PosgtreSQL에서 사용할 수있는 데이터 형식에서 숫자 형식의 사용법에 대해 설명하였다.

1.4.5.2 - PostgreSQL | 데이터 형식(Data type) | 자동 증가 형식 (serial 등)

PosgtreSQL에서 사용할 수 있는 데이터 형에서 자동 증가 타입의 사용법에 대해 설명하겠다. 자동 증가 타입으로 설정한 컬럼은 자동으로 연속 값이 저장된다. 자동 증가 타입은 smallserial, serial, bigserial의 3 가지 유형의 데이터가 존재한다.

자동 증가 형식

연번 형은 취급 숫자의 범위가 다른 3 가지 데이터 유형이 있다.

형식 크기 범위 별칭
smallserial 2 바이트 1~32767 serial2
serial 4 바이트 1~2147483647 serial4
bigserial 8 바이트 1~9223372036854775807 serial8

자동 증가 타입이 설정된 컬럼이 포함된 테이블에 데이터를 추가를 하면, 자동 증가 타입의 컬럼에 직접 값을 지정하는 것이 아니라 기본값이 포함되도록 한다. 그러면 자동으로 지금까지 등록된 값보다 큰 값(일반적으로 1 큰 값)이 자동으로 저장된다. (MySQL에서 말하는 컬럼에 AUTO_INCREMENT를 설정 한 것과 비슷하다)

※ 자동 증가 형식은 내부적으로 시퀀스를 이용하여 구현되어 있다.

실습으로 다음과 같은 테이블을 생성한다.

mydb=# create table myfriends (id serial, name varchar(10), address varchar(10));
CREATE TABLE
mydb=#

그러고, 테이블에 데이터를 추가한다. id 컬럼의 데이터 유형은 자동 증가 타입 serial이므로 id에 값을 지정하지 않고 데이터를 추가한다.

mydb=# insert into myfriends (name, address) values ('Yunho', 'Goyang');
INSERT 0 1
mydb=#

그러면 myfriends 테이블의 데이터를 검색하여 확인본다.

mydb=# select * from myfriends;
 id | name  | address
----+-------+---------
  1 | Yunho | Goyang
(1개 행)


mydb=#

id 컬럼은 첫 번째 값인 1이 포함되어 있다. 그럼 또 3개의 데이터를 추가보도록 한다.

mydb=# insert into myfriends (name, address) values ('Seonah', 'Bucheon'), ('Yongtae', 'Seoul'), ('Dongeog', 'Gangnam');
INSERT 0 3
mydb=#

다시 myfriends 테이블의 데이터를 검색하여 확인본다.

mydb=# select * from myfriends;
 id |  name   | address
----+---------+---------
  1 | Yunho   | Goyang
  2 | Seonah  | Bucheon
  3 | Yongtae | Seoul
  4 | Dongeog | Gangnam
(4개 행)


mydb=#

id 컬럼은 자동으로 연속된 값이 포함되어 있으며, 2, 3, 4의 값이 포함되어 있다. 이와 같이 자동 증가 데이터 형식을 컬럼으로 설정하면, 값이 지정되지 않으면 디폴트 값으로 연속적인 값이 자동으로 저장된다. (좀 더 덧붙이자면, 어디까지나 마지막 저장된 보다 큰 값이 저장되는 것이지 반드시 연속적인 값으로 되는 것은 아니다)

자동 증가 타입이 설정된 컬럼에 값을 지정하여 데이터를 추가

자동 증가 타입이 설정된 컬럼에 따로 지정하지 않으면 디폴트 값으로 자동으로 연속적인 값이 저장되지만, 임의의 값을 지정하여 데이터를 추가 할 수도 있다.

현재 4개의 데이터를 추가한 상태에 다음 데이터를 추가하게 되면, 자동 증가 타입이 설정된 id 컬럼에 다음 디폴트 값인 5가 저장된다.

여기서 id 컬럼에 값을 지정하여 데이터를 추가 할 수 있다.

mydb=# insert into myfriends values (7, 'Yunjo', 'Paris');
INSERT 0 1
mydb=# select * from myfriends;
 id |  name   | address
----+---------+---------
  1 | Yunho   | Goyang
  2 | Seonah  | Bucheon
  3 | Yongtae | Seoul
  4 | Dongeog | Gangnam
  7 | Younjo  | Paris
(5개 행)


mydb=#

데이터를 추가한 후 테이블에서 데이터를 검색해 보면 지정한 값이 그대로 저장되어 있다. 이와 같이 자동 증가 타입이 설정된 컬럼에도 값을 지정하여 데이터를 추가 할 수 있다.

여기에서 다시 id 컬럼에 지정하지 않고 디폴트값으로 데이터를 추가하면, id 컬럼에 무슨 값이 들어가는지 확인하려고 한다.

mydb=# insert into myfriends (name, address) values ('Sueun', 'Yongin');
INSERT 0 1
mydb=# select * from myfriends;
 id |  name   | address
----+---------+---------
  1 | Yunho   | Goyang
  2 | Seonah  | Bucheon
  3 | Yongtae | Seoul
  4 | Dongeog | Gangnam
  7 | Younjo  | Paris
  5 | Sueun   | Yongin
(6개 행)


mydb=#

데이터를 추가한 후에 테이블에서 데이터를 검색해 보면, id 컬럼은 원래 다음으로 들어가려던 5가 저장된다.

그럼, id 컬럼에 디폴트 값이 포함되도록 2개의 데이터를 더 추가해 보자.

mydb=# insert into myfriends (name, address) values ('Hansol', 'Seocho'), ('Yujin', 'Unknown');
INSERT 0 2
mydb=# select * from myfriends;
 id |  name   | address
----+---------+---------
  1 | Yunho   | Goyang
  2 | Seonah  | Bucheon
  3 | Yongtae | Seoul
  4 | Dongeog | Gangnam
  7 | Younjo  | Paris
  5 | Sueun   | Yongin
  6 | Hansol  | Seocho
  7 | Yujin   | Unknown
(8개 행)


mydb=#

id 컬럼에는 이전 저장된 값 5 다음으로 6과 7이 저장되어 있다. 이미 id 컬럼에 7이라는 값이 저장된 데이터를 수동으로 추가되었지만, 중복된 값이 있는지와는 상관없이 연속적인 값이 저장이 되었다.

이와 같이 자동 증가 타입이 설정된 컬럼에 값을 지정하여 데이터를 추가 할 수도 있지만, 그 데이터는 자동으로 저장되는 값으로 반영되지 않는다는 점을 주의가 필요하다.

PosgtreSQL에서 사용할 수있는 데이터 형에서 자동 증가 타입의 사용법에 대해 설명하였다.

1.4.5.3 - PostgreSQL | 데이터 형식(Data type) | 문자열 형식(varchar, char, text)

PosgtreSQL에서 사용할 수 있는 데이터 형식 중 문자 사용에 대해 설명한다. 문자는 가변 길이 문자형(character varying, varchar), 고정 길이 문자형(character, char) 제한없이 가변 문자형(text)이 있다.

문자열 형식

문자는 다음과 같은 데이터 형이 준비되어 있습니다.

형식 크기 별칭
character varying(n) 가변 길이 문자열 varchar(n)
character(n) 고정 길이 문자열 char(n)
text 제한없이 가변 길이

character varying(n)은 가변 길이의 문자형이다. 최대 길이(n)까지의 문자를 저장할 수 있다. character(n)은 고정 길이 문자형이다. 저장할 문자가 n개가 되지 않는 경우에는 나머지는 공백으로 채워진다.

두 문자형 모두 n개의 문자보다 긴 문자열을 저장하려고 하면 에러가 발생하게 되지만, n개의 문자보다 짧으면 n개의 문자만 저장된다.

길이를 지정하지 않고 character varying라고만 지정하면 문자 제한이 없는 것으로 간주된다. 이에 반해 길이를 지정하지 않고 character라고만지정하면 character(1)로 간주된다.

text 형식은 길이에 제한이 없는 가변 길이 문자형이다. 매우 긴 문자열을 포함하는 경우에 사용한다.

예를 들어, 다음과 같은 테이블을 생성한다

devkuma=# create table strtest (str1 varchar(10), str2 char(10));
CREATE TABLE
devkuma=#

생성한 테이블에 다음과 같은 데이터를 추가한다.

devkuma=# insert into strtest values('kuma', 'kuma');
INSERT 0 1
devkuma=# insert into strtest values('  kuma  ', '  kuma  ');
INSERT 0 1
devkuma=#

첫 번째 데이터는 ‘kuma’라는 문자열을 각각 포함되어 있고, 두 번째 데이터는 문자열 앞뒤에 공백이 각각 두 개의 ’ kuma ‘라는 문자열을 포함되어 있다. 그러면 테이블에서 데이터를 조회해 보자. 값을 얻을 때 char_length 함수도 사용하여 문자 수도 함께 표시해 보도록 한다.

devkuma=# select str1, char_length(str1), str2, char_length(str2) from strtest;
   str1   | char_length |    str2    | char_length
----------+-------------+------------+-------------
 kuma     |           4 | kuma       |           4
   kuma   |           8 |   kuma     |           6
(2개 행)


devkuma=#

가변 길이 문자열(str1)은 SELECT으로 조회하면 저장된 문자열 앞뒤의 공백을 포함하여 그대로 조회가 되고 있다. 그에 비해 고정 길이 문자열(str2)은 SELECT으로 조회된 문자열의 앞에 공백은 그대로이지만 문자열 뒤에 있는 공백은 모두 제거된 것을 확인할 수 있다.

– PosgtreSQL에서 사용할 수 있는 데이터 형식 중 문자 사용에 대해 설명하였다.

1.4.5.4 - PostgreSQL | 데이터 형식(Data type) | 날짜/시간 형식(timestamp, interval, data 등)

여기에서는 날짜 / 시간 데이터 형식에 대해 설명한다.

날짜/시간 형식

사용 가능한 형식은 다음과 같다.

형식 크기 설명 별칭
timestamp [(p)] [without time zone] 8 바이트 날짜와 시간 모두
timestamp [(p)] [with time zone] 8 바이트 날짜와 시간 모두 시간대 포함 timestamptz
interval [(p)] 12 바이트 시간 간격
date 4 바이트 날짜
time [(p)] [without time zone] 8 바이트 시간
time [(p)] with time zone 12 바이트 시간, 시간대 첨부 timetz

time 형이나 timestamp 데이터 형식의 선택적 인수(p)는 초의 소수점 이하의 정밀도를 지정이다. 허용되는 p범위는 타임 스탬프 및 간격 유형에 대해 0에서 6까지이다.

날짜와 시간 입력은 다양한 포멧으로 가능하지만, 판단할지 애매한 경우는 DateStyle 따라 달라진다. DateStyle는 예를 들어 날짜가 “년 월 일” 순서으로 작성되어 있는지 “월 일 년” 순서로 기술되어 있는지 등을 지정하는 것이다. 아래에서는 어떤 DataStyle도 제대로 해석되는 포맷만을 일단 보도록 한다.

날짜 입력 방법은 다음과 같다.

January 8, 1999
1999-01-08
1999-Jan-08
Jan-08-1999
08-Jan-1999
19990108
990108
1999.008      ※ 연도와 그 일까지의 합계

시간 입력 방법은 다음과 같다.

04:05:06.789
04:05:06
04:05
040506
04:05 AM     ※ = 04:05
04:05 PM     ※ = 16:05

시간도 포함한 시간 입력 방법은 다음과 같다.

04:05:06 PST
04:05:06+09:00
04:05:06+0900
04:05:06+09

※ 한국 표준 시간의 경우 “+09:00"이다.

날짜 입력시 월명으로 사용할 수있는 값은 다음과 같다.

January     Jan         ※ 1월
February    Feb         ※ 2월
March       Mar         ※ 3월
April       Apr         ※ 4월
May                     ※ 5월
June        Jun         ※ 6월
July        Jul         ※ 7월
August      Aug         ※ 8월
September   Sep、Sept   ※ 9월
October     Oct         ※10월
November    Nov         ※11월
December    Dec         ※12월

날짜와 시간은 작은 따옴표로 묶어 작성한다. 각 유형의 경우 입력 예는 아래와 같다.

timestamp                     '2004-10-19 10:23:54'
timestamp with time zone      '2004-10-19 10:23:54+09'
date                          '2004-10-19'
time                          '10:23:54'
time with time zone           '10:23:54+09'

시간 간격 형 interval 형은 시간 지정한 시간만큼을 연산을 할 경우에 사용한다.

예를 들면, 현재 시간보다 30분 전 시간을 구하려면 아래와 같이 구하면 된다.

devkuma=# select now(), now() - interval'30 minute';
              now              |           ?column?
-------------------------------+-------------------------------
 2020-10-24 00:51:55.780337+09 | 2020-10-24 00:21:55.780337+09
(1개 행)


devkuma=#

현재 시간보다 하루 전 날짜 구하는 방법은 아래와 같다.

devkuma=# select now(), now()::date - '1 day'::interval;
              now              |      ?column?
-------------------------------+---------------------
 2020-10-24 00:52:48.932335+09 | 2020-10-23 00:00:00
(1개 행)


devkuma=#

그밖에 사용법

현재 시간 조회하는 방법은 아래와 같다.

devkuma=# select now();
              now
-------------------------------
 2020-10-24 01:46:31.014282+09
(1 )

현재 타임존 조회하는 방법은 아래와 같다.

devkuma=# show timezone;
  TimeZone
------------
 Asia/Seoul
(1 )

타임존 변경 방법하는 방법은 아래와 같다.

devkuma=# SET TIME ZONE 'Asia/Seoul';
SET
devkuma=#

시스템 일자를 조회하는 방법은 아래와 같다.

devkuma=# select current_date, current_time, timeofday();
 current_date |    current_time    |              timeofday
--------------+--------------------+-------------------------------------
 2020-10-24   | 01:45:50.757997+09 | Sat Oct 24 01:45:50.765987 2020 KST
(1 )


devkuma=# select now(), current_timestamp, timestamp 'now';
              now              |       current_timestamp       |         timestamp
-------------------------------+-------------------------------+----------------------------
 2020-10-24 01:46:02.990667+09 | 2020-10-24 01:46:02.990667+09 | 2020-10-24 01:46:02.990667
(1 )

날짜에서 년도를 추출하는 방법은 아래와 같다.

devkuma=# select date_part('year', timestamp '2020-07-30 20:38:40');
 date_part
-----------
      2020
(1 )


devkuma=# select date_part('year', current_timestamp);
 date_part
-----------
      2020
(1 )


devkuma=# select extract('isoyear' from date '2006-01-01');
 date_part
-----------
      2005
(1 )


devkuma=# select extract('isoyear' from current_timestamp);
 date_part
-----------
      2020
(1 )


devkuma=# select date_trunc('year', timestamp '2020-07-30 20:38:40');
     date_trunc
---------------------
 2020-01-01 00:00:00
(1 )


devkuma=# select date_trunc('year', current_timestamp);
       date_trunc
------------------------
 2020-01-01 00:00:00+09
(1 )

월을 추출하는 방법은 아래와 같다.

devkuma=# select date_part('month', timestamp '2020-07-30 20:38:40');
 date_part
-----------
         7
(1 )


devkuma=# select date_part('month', current_timestamp);
 date_part
-----------
        10
(1 )


devkuma=# select extract('month' from timestamp '2020-07-30 20:38:40');
 date_part
-----------
         7
(1 )


devkuma=# select extract('month' from interval '2 years 3 months');
 date_part
-----------
         3
(1 )


devkuma=# select extract('month' from interval '2 years 13 months');
 date_part
-----------
         1
(1 )


devkuma=# select date_trunc('month', timestamp '2020-07-30 20:38:40');
     date_trunc
---------------------
 2020-07-01 00:00:00
(1 )

날짜에서 일을 추출하는 방법은 아래와 같다.

devkuma=# select date_part('day', timestamp '2020-07-30 20:38:40');
 date_part
-----------
        30
(1 )


devkuma=# select date_trunc('day', timestamp '2020-07-30 20:38:40');
     date_trunc
---------------------
 2020-07-30 00:00:00
(1 )

시간에서 시를 추출하는 방법은 아래와 같다.

devkuma=# select date_part('hour', timestamp '2013-07-30 20:38:40');
 date_part
-----------
        20
(1 )


devkuma=# select date_part('hour', interval '4 hours 3 minutes');
 date_part
-----------
         4
(1 )


devkuma=# select date_trunc('hour', timestamp '2020-07-30 20:38:40');
     date_trunc
---------------------
 2020-07-30 20:00:00
(1 )

시간에서 분을 추출하는 방법은 아래와 같다.

devkuma=# select date_part('minute', timestamp '2020-07-30 20:38:40');
 date_part
-----------
        38
(1 )


devkuma=# select date_trunc('minute', timestamp '2020-07-30 20:38:40');
     date_trunc
---------------------
 2013-07-30 20:38:00
(1 )

시간에서 초를 추출하는 방법은 아래와 같다.

devkuma=# select date_part('second', timestamp '2013-07-30 20:38:40');
 date_part
-----------
        40
(1 )


devkuma=# select extract('second' from time '17:12:28.5');
 date_part
-----------
      28.5
(1 )


devkuma=# select date_trunc('second', timestamp '2013-07-30 20:38:40');
     date_trunc
---------------------
 2013-07-30 20:38:40
(1 )

세기(century) 추출를 추출하는 방법은 아래와 같다.

devkuma=# select extract('century' from timestamp '2000-12-16 12:21:13');
 date_part
-----------
        20
(1 )


devkuma=# select extract('century' from timestamp '2020-07-30 20:38:40');
 date_part
-----------
        21
(1 )


devkuma=# select date_trunc('century', timestamp '2020-07-30 20:38:40');
     date_trunc
---------------------
 2001-01-01 00:00:00
(1 )

요일/일차 추출를 추출하는 방법은 아래와 같다.

devkuma=# select extract('isodow' from timestamp '2013-07-30 20:38:40');   -- 일요일(0), 토요일(6)
 date_part
-----------
         2
(1 )


devkuma=# select extract('doy' from timestamp '2013-07-30 20:38:40'); -- 월요일(1), 일요일(7)
 date_part
-----------
       211
(1 )


devkuma=# select extract('doy' from timestamp '2020-07-30 20:38:40');
 date_part
-----------
       212
(1 )

1.4.5.5 - PostgreSQL | 데이터 형식(Data type) | 논리 값 데이터 형식(boolean)

PosgtreSQL에서 사용할 수 있는 데이터 형에서 논리 값 데이터 형의 사용법에 대해 설명한다. 논리 데이터 형식는 boolean 형만 존재한다.

논리 데이터 형식 사용 논리 데이터 형식으로 준비되어있는 데이터 형은 하나입니다.

형식 크기 범위 별칭
boolean 1 바이트 참 또는 거짓 상태 bool

boolean 형은 참 또는 거짓을 나타내는 값 중 하나를 포함한다. PostgreSQL에서는 참 또는 거짓을 나타내는 값으로 다음 값을 사용할 수 있다.

참을 나타내는 값 :

TRUE
't'
'true'
'y'
'yes'
'on'
'1'

거짓를 나타내는 값 :

FALSE
'f'
'false'
'n'
'no'
'off'
'0'

boolean 형의 컬럼에 값을 저장하는 데 어떤 값을 사용하여도 상관 없지만, 될 수 있으면 알아보기 쉽게 TRUE와 FALSE를 사용하면 좋을거 같다.

실습으로 다음과 같은 테이블을 생성해 본다.

devkuma=# create table booltest (flag boolean);
CREATE TABLE
devkuma=#

그러고 테이블에 데이터를 추가한다. 참과 거짓을 나타내는 다양한 값을 넣어 보도록 한다.

devkuma=# insert into booltest values (TRUE), ('no'), ('0'), ('yes'), (FALSE);
INSERT 0 5
devkuma=#

booltest 테이블의 데이터를 검색하여 확인해 본다.

devkuma=# select * from booltest;
 flag
------
 t
 f
 f
 t
 f
(5개 행)


devkuma=#

boolean 형의 값을 조회해 보면 기본적으로 t 또는 f가 표시되는 것을 확인할 수 있다.

PosgtreSQL에서 사용할 수 있는 데이터 형에서 논리 값 데이터 형의 사용법에 대해 알아보았다.

1.4.5.6 - PostgreSQL | 데이터 형식(Data type) | 네트워크 주소 형식(cidr, inet, macaddr)

여기에서는 네트워크 주소 형식에 대해 알아보자. 사용 가능한 형식은 다음과 같다.

형식 크기 설명
cidr 12 혹은 24 바이트 IPv4 및 IPv6 네트워크
inet 12 혹은 24 바이트 IPv4 또는 IPv6 호스트 및 네트워크
macaddr 6 바이트 MAC 주소

inet 형, cidr 형 둘 다 인터넷 주소의 표기 방법이다. 예를 들어 “192.168.128.0/24"와 같이 “주소 / 넷 마스크의 비트 수"의 형식으로 작성한다. IPv4에서도 IPv6를 모두 작성 가능하다.

macaddr 형은 하드웨어 고유의 MAC 주소를 저장하는 형식이다. 예로는 다음과 같다.

'08002b:010203'
'08002b-010203'
'0800.2b01.0203'
'08-00-2b-01-02-03'
'08:00:2b:01:02:03'

1.4.6 - PostgreSQL | 데이터베이스(Database)

PostgreSQL에서는 목적에 따라 여러 데이터베이스를 만들고 관리 할 수 있다. 여기에서는 데이터베이스를 만들거나 삭제하며 만든 데이터베이스에 연결하는 방법에 대해 설명한다.

1.4.6.1 - PostgreSQL | 데이터베이스(Database) | 데이터베이스 생성(CREATE DATABASE)

CREATE DATABASE 명령을 사용하여 PostgreSQL 데이터베이스를 새로 만드는 방법을 설명한다.

새 데이터베이스 생성하기

데이터베이스를 작성하려면 CREATE DATABASE 명령을 사용합니다. 형식은 다음과 같습니다.

CREATE DATABASE name
    [ [ WITH ] [ OWNER [=] user_name ]
           [ TEMPLATE [=] template ]
           [ ENCODING [=] encoding ]
           [ LC_COLLATE [=] lc_collate ]
           [ LC_CTYPE [=] lc_ctype ]
           [ TABLESPACE [=] tablespace_name ]
           [ ALLOW_CONNECTIONS [=] allowconn ]
           [ CONNECTION LIMIT [=] connlimit ]
           [ IS_TEMPLATE [=] istemplate ] ]

옵션이 여러가기가 준비되고, 기본이 되는 구문은 다음과 같다.

CREATE DATABASE name

데이터베이스 이름 (name)을 지정하여 새 데이터베이스를 생성한다. 명시적으로 지정하지 않으면 작성된 템플릿 데이터베이스 template1를 복사하여 데이터베이스가 만들어 진다.

데이터베이스를 만들려면 명령을 수행하는 역할이 수퍼 유저이거나 CREATEDB 권한을 가지고 있어야 한다.

그러면 실제로 해보도록 하자. psql에서 다음과 같이 실행한다.

C:\Users\kimkc>psql -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=# create database devkuma;
CREATE DATABASE
postgres=#

데이터베이스가 생성 되었다.

그러면 생성된 데이터베이스 조회해 보자.

postgres=# \l
                                      데이터베이스 목록
   이름    |  소유주  | 인코딩 |     Collate      |      Ctype       |      액세스 권한
-----------+----------+--------+------------------+------------------+-----------------------
 devkuma   | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |                        <<<< 신규 생성됨
 postgres  | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 sample    | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 template0 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
 template1 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
(5개 행)


postgres=#

문자 셋 및 데이터 정렬을 지정하여 데이터베이스를 만들기

데이터베이스를 만들 때 데이터베이스에서 사용하는 문자 셋 (ENCODING) 및 문자열 정렬 순서(LC_COLLATE)와 문자 분류(LC_CTYPE)는 명시적으로 지정하지 않은 경우에는 기본값이 사용되지만, 이 설정들은 한 번 데이터베이스를 만들면 변경할 수 없으므로 주의해야 한다.

만약 명시적으로 문자 셋을 지정하여 데이터베이스를 만들려면 다음 형식을 사용한다.

CREATE DATABASE name
  ENCODING encoding
  LC_COLLATE lc_collate
  LC_CTYPE lc_ctype

또한 로케일 설정 (LC_COLLATE와 LC_CTYPE)이 기본적으로 사용되는 템플릿 template1의 로케일 설정과 다른 경우에는 명시적으로 템플릿으로 template0를 사용해야 한다.

CREATE DATABASE name
  TEMPLATE template0
  ENCODING encoding
  LC_COLLATE lc_collate
  LC_CTYPE lc_ctype

그러면 실제로 해보도록 하자. 이번에는 문자 셋으로 UTF8 하고 문자열 정렬 순서과 문자 분류로 ‘Korean_Korea.949’을 설정하여 데이터베이스를 만든다.

postgres=# create database devkuma2
postgres-#   template template0
postgres-#   encoding UTF8
postgres-#   lc_collate 'Korean_Korea.949'
postgres-#   lc_ctype 'Korean_Korea.949';
CREATE DATABASE
postgres=#	

문자 셋, 문자열 정렬 순서, 그리고 문자 분류를 지정하여 새 데이터베이스를 생성하였다.

그러면, 생성 된 데이터베이스 나열 해 보자.

postgres=# \l
                                      데이터베이스 목록
   이름    |  소유주  | 인코딩 |     Collate      |      Ctype       |      액세스 권한
-----------+----------+--------+------------------+------------------+-----------------------
 devkuma   | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 devkuma2  | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |                        <<< 신규 생성
 postgres  | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 sample    | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 template0 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
 template1 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
(6개 행)


postgres=#

방금 만든 devkuma2 데이터베이스가 존재하고, 문자 셋와 문자열 정렬 순서, 문자 분류가 지정된 것으로 되어있는 것을 확인할 수 있다.

여기까지 CREATE DATABASE 명령을 사용하여 데이터베이스를 만드는 방법에 대해 설명하였다.

1.4.6.2 - PostgreSQL | 데이터베이스(Database) | 생성된 데이터베이스 목록 조회

PostgreSQL로 생성된 데이터베이스 목록을 조회하는 방법에 대해 설명한다.

\l 명령을 사용하기

먼저 psql의 메타 명령을 사용하는 방법이다. 생성된 데이터베이스 목록을 검색하려면 다음과 같이 수행한다.

postgres=# \l
                                      데이터베이스 목록
   이름    |  소유주  | 인코딩 |     Collate      |      Ctype       |      액세스 권한
-----------+----------+--------+------------------+------------------+-----------------------
 devkuma   | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 devkuma2  | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 postgres  | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 sample    | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 template0 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
 template1 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
(6개 행)


postgres=#

생성된 데이터베이스 목록을 조회할 수 있다.

시스템 카탈로그 pg_database에서 조회하기

이어서 PostgreSQL 시스템 카탈로그의 하나인 pg_database에서 얻을 수 있는 방법에 대해서 알아보자. 시스템 카탈로그는 PostgreSQL의 관리 시스템이 사용하는 테이블에서 데이터베이스 및 테이블 등의 정보를 관리하는데 사용하는 pg_database에는 다음과 같은 열이 있다.

이름 설명
oid oid 식별자 (명시적으로 지정하지 않으면 얻을 수 없다)
datname name 데이터베이스 이름
datdba oid 데이터베이스 소유자
encoding int4 데이터베이스 문자 집합의 식별 번호
datcollate name 데이터베이스 문자열 정렬 순서
datctype name 데이터베이스 문자 유형
datistemplate bool 모든 사용자가 복제할 수 있는지 여부 (false의 경우는 수퍼 유저 또는 데이터베이스 소유자 만이 복제 가능)
datallowconn bool 데이터베이스에 연결할 수 있는지 여부 (template0의 데이터 변경을 방지하기 위해 사용)
datconnlimit int4 동시 연결의 최대 수 (-1은 무제한)
datlastsysoid oid 데이터베이스 최종 시스템 OID
datfrozenxid xid 이 데이터베이스에서 이 값보다 이전 트랜잭션 ID는 영속적인(동결 된) 트랜잭션 ID를 갖도록 변경되어 있다. 이는 이 데이터베이스에 대해 트랜잭션 ID 순환(wraparound) 문제를 방지하고, pg_xact을 축소시키는 것을 목적으로 한진공 작업을 수행할 것인지 여부를 추적하기 위해 사용된다.
datminmxid xid 이 데이터베이스 내에 트랜잭션 ID로 대체되기 전에 모든 멀티 트랜잭션 ID이다. 이는 트랜잭션 ID 순환(wraparound) 문제를 방지하거나 pg_multixact을 축소시키기 위해 데이터베이스를 진공해야하는지 여부를 추적하는 데 사용된다.
dattablespace oid 데이터베이스의 기본 테이블 공간
datacl aclitem [] 액세스 권한 목록

이번에는 pg_database에서 다음 컬럼에 대한 데이터를 조회해보자.

postgres=# select datname, datdba, encoding, datcollate, datctype from pg_database;
  datname  | datdba | encoding |    datcollate    |     datctype
-----------+--------+----------+------------------+------------------
 postgres  |     10 |        6 | Korean_Korea.949 | Korean_Korea.949
 template1 |     10 |        6 | Korean_Korea.949 | Korean_Korea.949
 template0 |     10 |        6 | Korean_Korea.949 | Korean_Korea.949
 sample    |     10 |        6 | Korean_Korea.949 | Korean_Korea.949
 devkuma   |     10 |        6 | Korean_Korea.949 | Korean_Korea.949
 devkuma2  |     10 |        6 | Korean_Korea.949 | Korean_Korea.949
(6개 행)


postgres=#

생성 된 데이터베이스 목록이 조회해 된 것을 볼수 있다.

여기서 데이터베이스의 소유자를 나타내는 datdba은 역할에 대한 데이터를 관리하는 시스템 카탈로그 pg_authid의 oid 값이 표시되어 있기 때문에 실제 역할 이름을 얻으려면 pg_authid의 rolname를 참조해야 한다. 그리고 문자 셋을 나타내는 encoding은 pg_encoding_to_char 함수를 사용하면 실제 문자 셋 이름을 얻을 수 있다.

예를 들어, 다음과 같이 실행한다.

postgres=# select datname,
postgres-#     pg_authid.rolname as dbrollname,
postgres-#     pg_encoding_to_char(encoding) as dbencoding,
postgres-#     datcollate,
postgres-#     datctype
postgres-#   from pg_database
postgres-#   join pg_authid on pg_authid.oid = pg_database.datdba;
  datname  | dbrollname | dbencoding |    datcollate    |     datctype
-----------+------------+------------+------------------+------------------
 devkuma2  | postgres   | UTF8       | Korean_Korea.949 | Korean_Korea.949
 devkuma   | postgres   | UTF8       | Korean_Korea.949 | Korean_Korea.949
 sample    | postgres   | UTF8       | Korean_Korea.949 | Korean_Korea.949
 template0 | postgres   | UTF8       | Korean_Korea.949 | Korean_Korea.949
 template1 | postgres   | UTF8       | Korean_Korea.949 | Korean_Korea.949
 postgres  | postgres   | UTF8       | Korean_Korea.949 | Korean_Korea.949
(6개 행)


postgres=#

데이터베이스 소유자 또는 문자 셋을 사용하여 데이터베이스의 목록을 죄회 할 수 있다.

생성 된 데이터베이스 목록을 가져 오는 방법에 대해 설명했습니다.

참고

1.4.6.3 - PostgreSQL | 데이터베이스(Database) | 지정된 데이터베이스에 연결

PostgreSQL에 연결할 때 모든 데이터베이스에 연결하는 방법과 psql에서 현재 연결된 데이터베이스와 다른 데이터베이스에 연결하는 방법에 대해 설명한다.

psql 옵션을 사용하여 연결하는 데이터베이스 지정

psql을 사용하여 PostgreSQL에 연결할 때 연결하는 데이터베이스를 지정하지 않으면 연결하는 역할과 동일한 이름의 데이터베이스에 연결한다.

예를 들어, 다음과 같이 역할로 postgres를 사용하여 연결하는 경우 역할 이름과 같은 postgres 데이터베이스에 연결한다.

C:\Users\kimkc>psql -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=#   >>>>>>>>>>>>> 프롬프트에 현재 접속중인 데이터베이스명이 표시된다.

현재 연결된 데이터베이스 이름은 프롬프트에 표시된다.

\c 명령을 사용하여 데이터베이스에 연결

psql을 사용하여 PostgreSQL에 접속한 뒤 현재 연결된 데이터베이스와 다른 데이터베이스에 연결하려면 psql의 메타 명령 중 하나인 \c를 사용한다.

그럼 devkum 데이터베이스에 연결을 시도한다. 다음과 같이 실행한다.

C:\Users\kimkc>psql -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=# \c devkuma
접속정보: 데이터베이스="devkuma", 사용자="postgres".
devkuma=#

devkuma 데이터베이스에 연결되었다. 프롬프트에 표시되는 데이터베이스 이름도 devkuma로 변경 되었다. (\c 대신 \connect를 사용할 수도 있다)

현재 연결된 데이터베이스 이름 조회

PostgreSQL에서 제공되는 current_database 함수를 사용하면 현재 연결된 데이터베이스 이름을 조회할 수 있다.

그러면 다음과 같이 실행한다.

devkuma=# select current_database();
 current_database
------------------
 devkuma
(1개 행)


devkuma=#

현재 연결된 데이터베이스인 devkuma가 조회되었다.

여기까지 지정된 데이터베이스에 연결하는 방법에 대해 알아보았다.

1.4.6.4 - PostgreSQL | 데이터베이스(Database) | 데이터베이스 변경 (ALTER DATABASE)

ALTER DATABASE 명령을 사용하여 생성된 데이터베이스를 변경하는 방법에 대해 설명한다.

데이터베이스 설정 변경

먼저 데이터베이스마다 설정을 변경하는 방법에 대해 알아보자. 형식은 다음과 같다.

ALTER DATABASE name [ [ WITH ] option [ ... ] ]

option:
    ALLOW_CONNECTIONS allowconn
    CONNECTION LIMIT connlimit
    IS_TEMPLATE istemplate

데이터베이스(name)의 설정을 변경한다. 변경할 수 있는 설정은 데이터베이스를 만들 때 설정 할 수있는 ALLOW_CONNECTIONS, CONNECTION LIMIT, IS_TEMPLATE 3가지 이다.

데이터베이스의 설정을 변경하려면, 명령을 수행하는 역할이 수퍼 유저이거나 데이터베이스 소유자이어야 한다.

그러면 실제로 해보도록 하자. 먼저 수퍼 유저의 역할 hamster 데이터베이스를 만든다. 이때 동시 연결 수를 설정하는 CONNECTION LIMIT를 5로 설정한다.

devkuma=# create database hamster connection limit 5;
CREATE DATABASE
devkuma=#

데이터베이스가 작성되었다. 확인을 위해 시스템 카탈로그의 하나 인 pg_database에서 데이터베이스 목록과 동시 연결 수를 조회해 본다.

devkuma=# select datname, datconnlimit from pg_database;
  datname  | datconnlimit
-----------+--------------
 postgres  |           -1
 template1 |           -1
 template0 |           -1
 sample    |           -1
 devkuma   |           -1
 devkuma2  |           -1
 hamster   |            5
(7개 행)


devkuma=#

hamster 데이터베이스가 작성되고 있고, 동시 연결 수가 5로 되어있는 것을 확인할 수 있다.

그러면 ALTER DATABASE 명령을 사용하여 hamster 데이터베이스의 동시 연결 수를 3으로 변경하려고 한다. 다음과 같이 실행한다.

devkuma=# alter database hamster connection limit 3;
ALTER DATABASE
devkuma=#

동시 연결 수가 3으로 변경되었다. 확인을 위해 다시 pg_database에서 데이터베이스 목록과 동시 연결 수를 조회한다.

devkuma=# select datname, datconnlimit from pg_database;
  datname  | datconnlimit
-----------+--------------
 postgres  |           -1
 template1 |           -1
 template0 |           -1
 sample    |           -1
 devkuma   |           -1
 devkuma2  |           -1
 hamster   |            3
(7개 행)


devkuma=#

hamster 데이터베이스의 동시 연결 수가 3으로 되어있는 것을 확인할 수 있다.

데이터베이스 이름 변경

다음 데이터베이스의 이름을 변경하는 방법에 대해 알아보자. 형식은 다음과 같다.

ALTER DATABASE name RENAME TO new_name

데이터베이스(name) 이름을 다른 이름(new_name)으로 변경한다.

데이터베이스의 이름을 변경하려면, 명령을 수행하는 역할이 수퍼 유저 또는 데이터베이스 소유자이면서 CREATEDB 권한을 가지고 있어야 한다. 연결된 데이터베이스의 이름을 변경할 수 없다.

그러면 실제로 해보도록 하자. 방금 만든 hamster 데이터베이스의 이름을 rabbit로 변경한다. 다음과 같이 실행한다.

devkuma=# alter database hamster rename to rabbit;
ALTER DATABASE
devkuma=#

데이터베이스의 이름을 변경 할 수 있었다. 확인을 위해 pg_database에서 데이터베이스 목록을 조회해 본다.

devkuma=# select datname, pg_authid.rolname as dbrollname
devkuma-#   from pg_database
devkuma-#   join pg_authid on pg_authid.oid = pg_database.datdba;
  datname  | dbrollname
-----------+------------
 rabbit    | postgres   <<<<< hamster에서 rabbit으로 변경
 devkuma2  | postgres
 devkuma   | postgres
 sample    | postgres
 template0 | postgres
 template1 | postgres
 postgres  | postgres
(7개 행)


devkuma=#

hamster 데이터베이스의 이름이 rabbit으로 바뀌어있는 것을 확인할 수 있다.

데이터베이스 소유자를 변경

다음 데이터베이스의 소유자를 변경하는 방법이다. 다음 형식을 사용한다.

ALTER DATABASE name OWNER TO {new_owner | CURRENT_USER | SESSION_USER}

데이터베이스(name)의 소유자를 다른 역할(new_owner)로 변경한다.

데이터베이스의 이름을 변경하려면, 명령을 수행하는 역할이 수퍼 유저이거나 데이터베이스 소유자이면서 새로운 소유자의 직접 또는 간접적으로 멤버이며, CREATEDB 권한을 가지고 있어야 한다.

그러면 실제로 해보도록 한다. 생성된 rabbit 데이터베이스의 소유자는 현재 postgres 역할이다.

devkuma=# \l rabbit
                               데이터베이스 목록
  이름  |  소유주  | 인코딩 |     Collate      |      Ctype       | 액세스 권한
--------+----------+--------+------------------+------------------+-------------
 rabbit | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
(1개 행)


devkuma=#

rabbit 데이터베이스의 소유자를 orange 역할로 변경하려고 한다. 먼저, orange 역할을 만들고, 다음과 같이 실행한다.

devkuma=# create role orange with login password 'apple';
CREATE ROLE
devkuma=#

데이터베이스의 소유자가 변경되었다. 확인을 위해 다시 \l 명령을 실행해 본다.

devkuma=# alter database rabbit owner to orange;
ALTER DATABASE
devkuma=# \l rabbit
                              데이터베이스 목록
  이름  | 소유주 | 인코딩 |     Collate      |      Ctype       | 액세스 권한
--------+--------+--------+------------------+------------------+-------------
 rabbit | orange | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
(1개 행)


devkuma=#

rabbit 데이터베이스의 소유자가 orange 역할로 변경되는 것을 확인할 수 있다.

그 밖에

ALTER DATABASE 명령에는 그 밖에 다음과 같은 형식이 준비되어 있다.

ALTER DATABASE name SET TABLESPACE new_tablespace

ALTER DATABASE name SET configuration_parameter { TO | = } { value | DEFAULT }
ALTER DATABASE name SET configuration_parameter FROM CURRENT
ALTER DATABASE name RESET configuration_parameter
ALTER DATABASE name RESET ALL

여기까지 ALTER DATABASE 명령을 사용하여 데이터베이스를 변경하는 방법에 대해 설명하였다.

1.4.6.5 - PostgreSQL | 데이터베이스(Database) | 데이터베이스 삭제 (DROP DATABASE)

DROP DATABASE 명령을 사용하여 생성 된 데이터베이스를 삭제하는 방법에 대해 설명합니다.

데이터베이스 삭제

생성된 데이터베이스를 삭제하려면 DROP DATABASE 명령을 사용한다. 형식은 다음과 같다.

DROP DATABASE [ IF EXISTS ] name

삭제할 데이터베이스 이름(name)을 지정하여 데이터베이스를 삭제한다.

데이터베이스를 삭제하려면 명령을 수행하는 역할이 수퍼 유저이거나 데이터베이스 소유자여야 합니다. 또한 명령을 실행하려는 역할이나 다른 역할이 삭제하려는 데이터베이스에 연결되어 있는 동안은 제거할 수 없다. 삭제하려는 데이터베이스와 다른 데이터베이스에 연결하여 제거해야 한다. (다른 데이터베이스에 연결하는 방법은 “지정된 데이터베이스에 연결“를 참조하도록 한다)

그럼 실제로 해보도록 하자. 생성된 devkuma2 데이터베이스를 제거한다. 다음과 같이 실행한다.

postgres=# drop database devkuma2;
DROP DATABASE
postgres=#

데이터베이스가 삭제되었다.

그러고 데이터베이스 목록을 표시하여 확인해 본다.

postgres=# \l
                                      데이터베이스 목록
   이름    |  소유주  | 인코딩 |     Collate      |      Ctype       |      액세스 권한
-----------+----------+--------+------------------+------------------+-----------------------
 devkuma   | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 postgres  | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 rabbit    | orange   | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 sample    | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
 template0 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
 template1 | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =c/postgres          +
           |          |        |                  |                  | postgres=CTc/postgres
(6개 행)


postgres=#

방금 삭제한 devkuma2는 데이터베이스 목록에 표시되지 않는 것으로 확인할 수 있다.

연결된 데이터베이스를 삭제하려고 하면 어떻게되는지 확인해 보도록 하다. devkuma 데이터베이스에 연결한다.

postgres=# \c devkuma
접속정보: 데이터베이스="devkuma", 사용자="postgres".
devkuma=#

mydb 데이터베이스에 연결되어 있는 상태에서 devkuma 데이터베이스를 삭제해 본다.

devkuma=# drop database devkuma;
오류:  현재 열려 있는 데이터베이스는 삭제할 수 없습니다
devkuma=#

다음과 같이 “오류: 현재 열려 있는 데이터베이스는 삭제할 수 없습니다“라고 오류가 표시되고 데이터베이스 삭제에 실패한다.

이처럼 현재 연결되어있는 데이터베이스를 삭제할 수 없다.

여기까지 DROP DATABASE 명령을 사용하여 데이터베이스를 삭제하는 방법에 대해 알아보았다.

1.4.7 - PostgreSQL | 스키마(Schema)

PostgreSQL에서는 동일한 데이터베이스에 여러 스키마라는 것을 만들 수 있다. 테이블이나 함수는 스키마에 작성된 동일한 데이터베이스라도 다른 스키마하면 같은 이름의 테이블을 만들 수 있다. 여기에서는 스키마 생성 및 삭제 등 스키마의 사용 방법에 대해 설명한다.

스키마 생성

CREATE SCHEMA schema_name AUTHORIZATION username;

스키마 이름 변경

ALTER SCHEMA schema_name RENAME TO new_schema_name;

스키마 소유자 변경

ALTER SCHEMA username OWNER TO new_username;

스키마 삭제

DROP SCHEMA schema_name CASCADE;

https://www.postgresql.org/docs/10/static/sql-createschema.html

1.4.7.1 - PostgreSQL | 스키마(Schema) | 데이터베이스, 스키마, 테이블의 관계

PostgreSQL에서는 데이터베이스와 테이블과는 다른 스키마라는 것이 존재한다. 여기에서는 PostgreSQL의 데이터베이스 스키마 및 테이블의 관계에 대해 간략하게 설명한다.

PostgreSQL의 데이터베이스 스키마, 테이블의 관계

PostgreSQL의 데이터베이스 스키마 및 테이블의 관계에 대해 간략하게 설명한다.

데이터베이스

PostgreSQL에서는 여러 데이터베이스를 관리할 수 있다. 설치한 직후는 템플릿 데이터베이스인 template0와 template1, 그리고 postgres라는 데이터베이스가 만들어진다. (이러한 데이터베이스가 모와서 데이터베이스 클러스터라고 부른다).

데이터베이스는 작성 권한이 있으면 신규로 추가 할 수 있다.

스키마

PostgreSQL에서 실제 데이터는 테이블에 저장된다. 테이블은 목적에 따라 여러 개 만들 수 있고 그것을 정리된 것이 데이터베이스이며, 거기에 PostgreSQL에서는 데이터베이스에 스키마라는 것이 있다.

스키마는 데이터베이스에 작성되는 테이블이나 함수 등의 개체를 그룹화하는 것이다. 스키마가 다르면 동일한 데이터베이스에도 동일한 테이블 이름으로 테이블을 만들 수 있다. 데이터베이스를 작성하면 자동으로 public라는 특별한 스키마가 작성된다.

public 스키마는 기본적으로 모든 역할에 권한과 CREATE 권한이 부여되며, public 스키마에 어떤 역할도 테이블을 만들 수 있다.

public 스키마와는 별도로 스키마를 데이터베이스에 만들 수 있다. 다른 데이터베이스 시스템에서는 사용자 이름과 동일한 이름의 스키마 이름을 가진 스키마만 작성할 수 없는 것도 있지만, PostgreSQL에서는 모든 이름의 스키마를 만들 수 있다.

테이블

테이블은 스키마에 작성한다. 스키마가 다르면 같은 테이블 이름의 테이블도 만들 수 있다. 또한 스키마마다 테이블 등의 오브젝트를 작성할 수있는 권한을 설정할 수 있다.

CREATE TABLE 명령으로 테이블을 만들 경우 테이블 이름에 스키마를 생략하면 기본적으로 public 스키마에 테이블이 만들어 진다. (역할 이름과 같은 스키마가 생성 된 경우는 제외)

여기까지 PostgreSQL의 데이터베이스와 테이블 및 스키마와의 관계에 대해 알아보았다.

1.4.7.2 - PostgreSQL | 스키마(Schema) | 스키마 생성(CREATE SCHEMA)

CREATE SCHEMA 명령을 사용하여 PostgreSQL로 스키마를 새로 만드는 방법을 설명한다.

새로운 스키마 생성

스키마를 만들려면 CREATE SCHEMA 명령을 사용한다. 형식은 다음과 같다.

CREATE SCHEMA schema_name

스키마 이름(schema_name)를 지정하여 현재 연결된 데이터베이스에 새 스키마를 만든다. 스키마 이름은 임의의 이름을 지정할 수 있지만, pg_로 시작하는 이름은 특별한 의미를 가지므로 사용할 수 없다.

스키마를 생성하려면 명령을 실행하는 역할이 수퍼 유저이거나 스키마를 만들 데이터베이스에서 CREATE 권한을 가지고 있어야한다.

그러면 실제로 해보도록 하자. 스키마를 생성할 데이터베이스에 연결한다. 이번에는 devkuma 데이터베이스에 스키마를 만든다.

postgres=# \c devkuma
접속정보: 데이터베이스="devkuma", 사용자="postgres".
devkuma=#

devkuma 데이터베이스에 현재 생성된 스키마를 확인하기 위해 psql 메타 명령 \dn 명령을 실행한다.

devkuma=# \dn
스키마(schema) 목록
  이름  |  소유주
--------+----------
 public | postgres
(1개 행)


devkuma=#

기본적으로 생성되는 public 스키마가 생성되어있는 것을 확인할 수 있다.

그러면 새로운 스키마를 만든다. 다음과 같이 실행한다.

devkuma=# create schema myschema;
CREATE SCHEMA
devkuma=#

새로운 스키마가 작성되었다. 확인을 위해 다시 \dn 명령을 실행한다.

devkuma=# \dn
 스키마(schema) 목록
   이름   |  소유주
----------+----------
 myschema | postgres
 public   | postgres
(2개 행)


devkuma=#

myschema 스키마가 생성되어있는 것을 확인할 수 있다.

생성할 스키마의 소유자 지정

스키마를 작성한 경우 작성된 스키마의 소유자는 스키마를 만든 역할되지만 소유자로 다른 역할을 지정하여 스키마를 만들 수 있다. 형식은 다음과 같다.

CREATE SCHEMA schema_name AUTHORIZATION role_specification

스키마 이름 (schema_name)과 스키마의 소유자(role_specification)를 지정해 새로운 스키마를 만든다. 이 형식으로 스키마를 만들려면 명령을 수행하는 역할이 소유하는 롤의 직접 또는 간접적인 구성원이거나 수퍼 유저이여야 한다.

그러면 실제로 해보록 하자. 스키마를 만들 데이터베이스에 연결한 후 다음과 같이 실행한다.

devkuma=# create schema orange_schema authorization orange;
CREATE SCHEMA
devkuma=# 

moorangemo 역할을 소유자로 orange_schema 스키마를 작성하였다. 확인을 위해 \dn 명령을 실행한다.

devkuma=# \dn
   스키마(schema) 목록
     이름      |  소유주
---------------+----------
 myschema      | postgres
 orange_schema | orange
 public        | postgres
(3개 행)


devkuma=#

이미 생성된 스키마 이외에 orange 역할이 소유자인 orange_schema가 새로 생성되어 있는 것을 확인할 수 있다.

새로운 스키마를 생성할때 스키마에 테이블과 같은 개체를 생성하기

새로운 스키마를 만들 때, 스키마에 테이블이나 인덱스와 같은 개체를 동시에 만들 수 있다. 형식은 다음과 같다.

CREATE SCHEMA schema_name
  [ AUTHORIZATION role_specification ] 
  schema_element [ ... ]

schema_element에 동시에 작성하는 SQL 명령을 작성한다. 작성할 수 있는 명령은 CREATE TABLE, CREATE VIEW, CREATE INDEX, CREATE SEQUENCE, CREATE TRIGGER, GRANT 중 하나이다. 여러 명령을 하는 경우에는 SQL 명령의 마지막에 세미콜론 (;)을 하지 말아야 한다.

그러면 실제로 해보록 하자. 스키마를 생성할 때 데이터베이스에 연결한 후 다음과 같이 실행한다.

devkuma=# create schema testschema create table testtbl (id integer);
CREATE SCHEMA
devkuma=#

testschema 스키마를 생성하고 생성된 스키마에 testtbl 테이블을 만들었다. 확인을 위해 \dt 명령을 실행한다.

devkuma=# \dt testschema.*
         릴레이션(relation) 목록
   스키마   |  이름   |  종류  |  소유주
------------+---------+--------+----------
 testschema | testtbl | 테이블 | postgres
(1개 행)


devkuma=#

만든 testschema에 testtbl 테이블이 생성되어 있는 것을 확인할 수 있다.

여기까지 CREATE SCHEMA 명령을 사용하여 새 스키마를 만드는 방법에 대해 알아보았다.

1.4.7.3 - PostgreSQL | 스키마(Schema) | 생성된 스키마 목록 조회

PostgreSQL에서 이미 생성된 스키마 목록을 조회하는 방법에 대해 설명한다.

\dn 명령을 사용하여

먼저 psql의 메타 명령을 사용하는 방법에 대해 알아보자. 이미 생성된 스키마 목록을 조회하려면 대상 데이터베이스에 연결하여 다음과 같이 실행한다.

devkuma=# \dn
   스키마(schema) 목록
     이름      |  소유주
---------------+----------
 myschema      | postgres
 orange_schema | orange
 public        | postgres
 testschema    | postgres
(4개 행)


devkuma=#

현재 연결되어 있는 devkuma 데이터베이스에는 기본적으로 생성되는 public 스키마 외에 postgres 역할 소유자의 myschema 스키마와 orange 역할 소유자 orange_schema 스키마가 생성되어 있다.

\dn 명령에 +를 추가하여실행하면 스키마의 기본 정보에 추가된 액세스 권한을 포함하여 조회할 수 있다.

devkuma=# \dn+
                           스키마(schema) 목록
     이름      |  소유주  |     액세스 권한      |          설명
---------------+----------+----------------------+------------------------
 myschema      | postgres |                      |
 orange_schema | orange   |                      |
 public        | postgres | postgres=UC/postgres+| standard public schema
               |          | =UC/postgres         |
 testschema    | postgres |                      |
(4개 행)


devkuma=#

다른 데이터베이스에 연결하여 \dn 명령을 실행하게 되면, 새로 연결된 데이터베이스에서 생성된 스키마 목록을 조회된다. 아래와 같이 postgres 데이터베이스에 연결하여 \dn 명령을 실행해 보자.

devkuma=# \c postgres
접속정보: 데이터베이스="postgres", 사용자="postgres".
postgres=# \dn
스키마(schema) 목록
  이름  |  소유주
--------+----------
 public | postgres
(1개 행)


postgres=#

현재 연결된 ostgres 데이터베이스는 기본적으로 생성되는 public 스키마만 생성되어 있었습니다.

시스템 카탈로그 pg_namespace 조회

이어서 PostgreSQL 시스템 카탈로그의 하나인 pg_namespace에서 얻을 수있는 방법에 대해 알아보자. 시스템 카탈로그는 PostgreSQL의 관리 시스템이 사용하는 테이블에서 데이터베이스 및 테이블 등의 정보를 관리하는 데 사용한다. pg_namespace에는 다음과 같은 열이 있다.

이름 데이터 형식 설명
oid oid 식별자 (명시적으로 지정하지 않으면 얻을 수 없다)
nspname name 네임스페이스의 이름
nspowner oid 이름 공간의 소유자
nspacl aclitem[] 액세스 권한 목록

그러면 대상 데이터베이스에 연결하여 이번에는 pg_namespace에서 다음 컬럼에 대한 데이터를 조회해 보자.

devkuma=# select nspname, nspowner, nspacl from pg_namespace;
      nspname       | nspowner |               nspacl
--------------------+----------+-------------------------------------
 pg_toast           |       10 |
 pg_temp_1          |       10 |
 pg_toast_temp_1    |       10 |
 pg_catalog         |       10 | {postgres=UC/postgres,=U/postgres}
 public             |       10 | {postgres=UC/postgres,=UC/postgres}
 information_schema |       10 | {postgres=UC/postgres,=U/postgres}
 myschema           |       10 |
 orange_schema      |    24635 |
 testschema         |       10 |
(9개 행)


devkuma=#

현재 연결된 데이터베이스에 포함된 스키마 목록을 조회하였다. (pg_으로 시작되는 스키마는 PostgreSQL 시스템에서 사용한다. 또 information_schema도 시스템에서 사용하는 것이다.)

또한 스키마의 소유자를 나타내는 nspowner은 역할에 대한 데이터를 관리하는 시스템 카탈로그 pg_authid의 oid 값이 표시되어 있기 때문에 실제 역할 이름을 얻으려면 pg_authid의 rolname를 참조한다. 예를 들어, 다음과 같이 실행한다.

devkuma=# select nspname, pg_authid.rolname as schemaowner, nspacl
devkuma-#   from pg_namespace
devkuma-#   join pg_authid on pg_authid.oid = pg_namespace.nspowner;
      nspname       | schemaowner |               nspacl
--------------------+-------------+-------------------------------------
 pg_toast           | postgres    |
 pg_temp_1          | postgres    |
 pg_toast_temp_1    | postgres    |
 pg_catalog         | postgres    | {postgres=UC/postgres,=U/postgres}
 public             | postgres    | {postgres=UC/postgres,=UC/postgres}
 information_schema | postgres    | {postgres=UC/postgres,=U/postgres}
 myschema           | postgres    |
 orange_schema      | orange      |
 testschema         | postgres    |
(9개 행)


devkuma=#

스키마의 소유자 역할 이름을 포함하여 스키마의 목록을 조회가 되었다.

여기까지 생성된 스키마 목록을 검색하는 방법에 대해 설명하였다.

1.4.7.4 - PostgreSQL | 스키마(Schema) | 스키마 검색 경로를 설정하기

스키마 이름을 생략하여 테이블 등의 객체를 지정하는 경우에 실제로는 어떤 스키마 안의 객체인지 찾기 위해서 사용되는 스키마 검색 경로 설정 방법과 사용법에 대해 설명한다.

스키마 검색 경로란?

테이블 등의 객체는 스키마에 생성된 스키마마다 같은 이름의 테이블을 만들 수 있기에, 어떤 테이블을 명확히 하려면 “스키마명.테이블명"과 같이지정해야 한다.

예를 들어, schemaA 스키마에 staff 테이블이 있고, schemaB 스키마에 같은 이름의 staff 테이블이 있는 경우에 단순히 staff 테이블을 지정하면 두 스키마 중 staff 테이블인지 모르게 된다.

select * from staff;

테이블을 명확히 지정하려면 schemaA.staff과 schemaB.staff 등과 같이 “스키마명.테이블명"으로 지정해야 한다.

select * from schemaA.staff;

매번 스키마명을 넣어야 한다면 귀찮아 지기에 스키마명을 생략하기 위해 어떤 스키마인지를 설정할 수 있다. 이 설정이 스키마 검색 경로이다. 스키마 검색 경로에는 여러 스키마를 설정해 둘 수 있다.

스키마명1, 스키마명2 ...

예를 들어, 스키마가 생략하고 테이블명만 작성되면, 스키마 검색 경로의 시작부터 스키마에 테이블명이 존재하는지 여부를 확인하고 있다. 일치하는 테이블을 찾게 되면 그 스키마에 테이블명으로 판단하게 된다.

그럼 구체적으로 스키마 검색 경로의 사용법과 설정 방법에 대해 알아 보자.

스키마 검색 경로의 현재 설정 값을 확인하기

스키마 검색 경로에 설정되어 있는 값을 확인하려면 다음과 같이 수행한다.

devkuma=# show search_path;
   search_path
-----------------
 "$user", public
(1개 행)


devkuma=#

다음과 같이 조회하였다.

 "$user", public

스키마 검색 경로는 스키마를 쉼표(,)로 구분하고 있다. 앞에 언급되어 있는 "$user"는 현재의 역할과 동일한 이름의 스키마를 나타낸다. 현재는 postgres 역할로 연결되어 있어서 다음과 같이 기술되어 있던 것과 동일하다.

postgres, public

스키마 검색 경로가 사용되는 방법

스키마 검색 경로는 SELECT 등의 기존의 테이블을 찾는 경우와 CREATE 등의 새로운 테이블을 생성하는 경우에 사용되는 방법이 조금 다르다.

먼저 테이블을 찾는 경우이다. 테이블에서 데이터를 검색하는 경우에 스키마명을 생략하고 테이블명만을 작성하면 스키마 검색 경로에 나열된 스키마를 처음부터 순서대로 테이블이 존재하는지 여부를 확인하게 된다.

예를 들어, 스키마 검색 경로는 다음과 같이 되어 있다고 하자.

schemaA, schemaB, schemaC

여기에서 예를 들어 다음과 같이 스키마를 생략하고 테이블 이름을 지정하고 데이터를 검색하게 되면

select * from mytbl;

먼저 schemaA 스키마에 mytbl이 있는지 여부를 확인한다. 발견되면 schemaA.mytbl로 데이터를 가져온다. 발견되지 않으면 schemaB 스키마에 mytbl이 있는지 여부를 확인하게 된다. 이 과정을 이 스키마 검색 경로에 설정되어 있는 스키마의 수 만큼 반복하게 된다.

마지막까지 발견되지 않았던 경우는 에러가 발생하게 된다. 스키마 검색 경로에 기재되지 않은 스키마가 따로있더라도 체크되지 않는다.

다음은 테이블을 생성할 경우이다. 스키마명을 생략하고 테이블명을 작성하게 되면 스키마 검색 경로에 나열된 스키마가 존재하는지 여부를 확인할 것이다. 실재하는 스키마가 발견된 경우 그 스키마에서 테이블을 만든다.

예를 들어, 스키마 검색 경로는 다음과 같이 되어 있다고 하자.

schemaA, schemaB, schemaC

먼저 schemaA부터 확인하고 있고, 실제로 존재하는 스키마가 발견되면 경우에는 그 스키마가 현재 스키마되 된다.

여기에서 다음과 같이 스키마명을 생략하여 테이블을 작성하게 되면, 현재의 스키마에 테이블이 만들어 진다.

create table mytbl (...);

그러면 테이블을 만들어 보도록 하자. 현재 수퍼 유저 postgres 역할로 devkuma 데이터베이스에 연결되어 있다. 스키마 검색 경로는 기본 상태이므로 다음과 같이되어 있습니다.

"$user", public

devkuma 데이터베이스에는 역할 이름과 같은 postgres 스키마가 생성되지 않으므로 스키마 검색 경로에서 최초로 실재하는 스키마는 public이다. 따라서 스키마를 생략하여 테이블을 만들게 되면 public 스키마에 테이블이 만들어 진다.

는 다음과 같이 실행하여 테이블을 작성하십시오.

devkuma=# create table memo (id integer, memo text);
CREATE TABLE
devkuma=#

테이블이 생성되었다. 확인을 위해 psql 메타 명령인 \dt를 실행해 본다.

devkuma=# \dt
      릴레이션(relation) 목록
 스키마 | 이름 |  종류  |  소유주
--------+------+--------+----------
 public | memo | 테이블 | postgres
(1개 행)


devkuma=#

public 스키마에 방금 만든 memo 테이블이 생성되어있는 것을 확인할 수 있다.

스키마 검색 경로를 변경하기

스키마 검색 경로를 현재 세션 동안만 일시적으로 변경하려면 SET 명령을 사용하여 변경한다.

SET search_path TO 스키마명1, 스키마명2, ...;

예를 들어 스키마 검색 경로를 myschema, public으로 설정하려면 다음과 같이 실행한다.

devkuma=# set search_path to myschema, public;
SET
devkuma=#

스키마 검색 경로가 변경되었다. 확인을 위해 show search_path을 실행해 보자.

devkuma=# show search_path;
   search_path
------------------
 myschema, public
(1개 행)


devkuma=#

스키마 검색 경로를 지정한 값으로 변경되는 것을 확인할 수 있다.

다음은 스키마 검색 경로를 영구적으로 변경하려는 경우에는 ALTER ROLE 명령을 사용하여 변경한다.

ALTER ROLE user SET search_path = 스키마명1, 스키마명2 ...;

예를 들어, 스키마 검색 경로를 myschema, public으로 설정하려면 다음과 같이 실행한다.

devkuma=# alter role postgres set search_path to myschema, public;
ALTER ROLE
devkuma=#

스키마 검색 경로를 변경하였다. 확인을 위해 한번 PostgreSQL과의 연결을 종료하고 다시 접속을 실시 show search_path을 실행해 보자.

devkuma=# alter role postgres set search_path to myschema, public;
ALTER ROLE
devkuma=# \q

C:\Users\kimkc>psql -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=# show search_path;
   search_path
------------------
 myschema, public
(1개 행)


postgres=#

스키마 검색 경로를 지정한 값으로 변경되는 것을 확인할 수 있다.

기본 값으로 되돌리려면 다음과 같이 실행한다.

alter role postgres set search_path to default;

모든 사용자의 기본 스키마 검색 경로를 변경하려면, PostgreSQL 설정 파일인 postgresql.conf 파일을 수정한다. (postgresql.conf 파일의 위치 등에 대해서는 postgresql.conf 파일 설정 방법“을 참조하도록 하자)

postgresql.conf 파일을 텍스트 편집기에서 연 후 다음과 같은 위치를 찾는다.


#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS
#------------------------------------------------------------------------------

# - Statement Behavior -

#client_min_messages = notice		# values in order of decreasing detail:
					#   debug5
					#   debug4
					#   debug3
					#   debug2
					#   debug1
					#   log
					#   notice
					#   warning
					#   error
#search_path = '"$user", public'	# schema names
#row_security = on
#default_tablespace = ''		# a tablespace name, '' uses the default
#search_path = '"$user", public'	# schema names

현재 search_path에 대한 설정은 주석으로 되어 있기에 첫 번째 #을 제거한다. 그러고 기본 스키마 검색 경로를 설정하고자 하는 값을 설정한다. 여기서는 다음과 같이 설정한다.

search_path = 'myschema, clientschema'    # schema names

변경했으면 postgresql.conf 파일을 저장한다. 그러고 PostgreSQL을 다시 시작한다.

그런 다음 PostgreSQL에 연결하여 show search_path을 실행해 본다.

C:\Users\kimkc>psql -U postgres
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=# show search_path;
   search_path
------------------
 myschema, clientschema
(1개 행)


postgres=#

스키마 검색 경로의 기본 값이 변경되는 것을 확인할 수 있다.

여기까지 스키마 검색 경로의 설정과 사용법에 대해 알아보았다.

1.4.7.5 - PostgreSQL | 스키마(Schema) | 스키마 변경(ALTER SCHEMA)

ALTER SCHEMA 명령을 사용하여 생성된 스키마를 변경하는 방법에 대해 설명한다. 변경이 가능한 것은 스키마의 이름과 소유자의 변경이다.

스키마명 변경

먼저 ALTER SCHEMA 명령을 사용하여 스키마명을 변경하는 방법이다. 형식은 다음과 같다.

ALTER SCHEMA name RENAME TO new_name

현재 스키마의 이름(name)을 새 스키마 이름(new_name)으로 변경한다.

스키마의 이름을 변경하려면 명령을 수행하는 역할이 수퍼 유저 또는 스키마의 소유자이고, 스키마가 작성되는 데이터베이스에서 CREATE 권한을 가지고 있어야 한다.

그러면 실제로 해보도록 하자. psql 메타 명령인 \dn을 실행하여 확인해 보면 현재 devkuma 데이터베이스는 기본적으로 생성되는 public 이외에 2 개의 스키마가 작성되어 있다.

devkuma=# \dn
   스키마(schema) 목록
     이름      |  소유주
---------------+----------
 myschema      | postgres
 orange_schema | orange
 public        | postgres
(3개 행)


devkuma=#

이 중 orange_schema 스키마의 이름을 fruit로 변경한다. 다음과 같이 실행한다.

devkuma=# alter schema orange_schema rename to fruit;
ALTER SCHEMA
devkuma=#

스키마의 이름이 변경되었다. 확인을 위해 다시\dn 명령을 실행해 본다.

devkuma=# \dn
 스키마(schema) 목록
   이름   |  소유주
----------+----------
 fruit    | orange
 myschema | postgres
 public   | postgres
(3개 행)


devkuma=#

orange_schema 스키마의 이름이 fruit 변경되는 것을 확인할 수 있다.

스키마의 소유자 변경

다음으로 ALTER SCHEMA 명령을 사용하여 스키마의 소유자를 변경하는 방법이다. 방법은 다음과 같다.

ALTER SCHEMA name OWNER TO new_owner

스키마(name)을 새 스키마의 소유자(new_name)로 변경한다.

스키마의 소유자를 변경하려면, 명령을 수행하는 역할이 수퍼 유저 또는 스키마의 소유자이면서 새로운 소유자의 직접 또는 간접적으로 구성원이고 스키마가 작성되는 데이터베이스에서 CREATE 권한을 가지고 있어야 한다.

그러면 실제로 해보도록 하자. psql 메타 명령 \dn을 실행하여 확인해 보면, 현재 devkuma 데이터베이스는 기본적으로 생성되는 public 이외에 2개의 스키마가 작성되어 있다.

devkuma=# \dn
 스키마(schema) 목록
   이름   |  소유주
----------+----------
 fruit    | orange
 myschema | postgres
 public   | postgres
(3개 행)


devkuma=#

이 중에 fruit 스키마의 소유자를 kimkc 역할로 변경한다. 다음과 같이 실행한다.

devkuma=# alter schema fruit owner to kimkc;
오류:  "kimkc" 롤(role) 없음
devkuma=#

위와 같이 역할(role)이 없다면, 에러가 발생하기에 아래와 같이 역할을 하나 생성한다.

devkuma=# create role kimkc with superuser login password '1234';
CREATE ROLE
devkuma=#

그럼 다음과 같이 다시 실행해 보자.

devkuma=# alter schema fruit owner to kimkc;
ALTER SCHEMA
devkuma=#

스키마의 소유자가 변경되었다. 확인을 위해 다시 \dn 명령을 실행해 본다.

devkuma=# \dn
 스키마(schema) 목록
   이름   |  소유주
----------+----------
 fruit    | kimkc
 myschema | postgres
 public   | postgres
(3개 행)


devkuma=#

fruit 스키마의 소유자가 kimkc 역할로 변경되는 것을 확인할 수 있다.

여기까지 ALTER SCHEMA 명령을 사용하여 생성된 스키마를 변경하는 방법에 대해 설명하였다.

1.4.7.6 - PostgreSQL | 스키마(Schema) | 스키마 삭제(DROP SCHEMA)

DROP SCHEMA 명령을 사용하여 생성된 스키마를 삭제하는 방법에 대해 알아보자.

스키마 삭제

DROP SCHEMA 명령을 사용하여 스키마를 삭제하는 방법에 대해 알아보자. 형식은 다음과 같다.

DROP SCHEMA [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]

지정된 스키마 (name)을 제거한다. 스키마에 테이블과 같은 객체가 포함된 경우 CASCADE를 지정하면 객체를 포함하여 삭제한다. RESTRICT를 지정하면 삭제를 취소하게 된다. 기본값은 RESTRICT 이다.

스키마를 삭제하려면 명령을 수행하는 역할이 수퍼 유저 또는 스키마의 소유자여야 한다. 그리고 CASCADE를 지정하게 되면 한번에 삭제되는 객체의 소유자가 따로 있어도 객체는 삭제된다.

그러면 실제로 해보도록 하자. 일반 사용자 kimkc 역할 PostgreSQL의 devkuma 데이터베이스에 연결한다. psql 메타 명령 \dn을 실행하여 확인 해 보면 현재이 데이터베이스에는 kimkc 역할 소유자인 fruit 스키마가 작성되어 있다.

devkuma=# \dn
 스키마(schema) 목록
   이름   |  소유주
----------+----------
 fruit    | kimkc
 myschema | postgres
 public   | postgres
(3개 행)


devkuma=#

fruit 스키마를 삭제한다. 다음과 같이 실행한다.

devkuma=# drop schema fruit;
DROP SCHEMA
devkuma=#

스키마가 삭제되었다. 확인을 위해 다시 \dn 명령을 실행해 본다.

devkuma=# \dn
 스키마(schema) 목록
   이름   |  소유주
----------+----------
 myschema | postgres
 public   | postgres
(2개 행)


devkuma=#

fruit 스키마가 삭제된 것을 확인할 수 있다.

그럼 다시 kimkc 롤을 사용하여 apple 스키마를 만든다.

C:\Users\kimkc>psql -U kimkc -d devkuma
kimkc 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

devkuma=# create schema apple;
CREATE SCHEMA
devkuma=#

kimkc 역할과 다른 수퍼 유저의 롤을 사용하여 apple 스키마에 테이블을 만든다.

C:\Users\kimkc>psql -U postgres -d devkuma
postgres 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

devkuma=# create table apple.blog (id integer);
CREATE TABLE
devkuma=#

이제 apple 스키마에는 kimkc 역할과 다른 역할이 소유자의 테이블이 생성하였다.

devkuma=# \dt apple.*;
      릴레이션(relation) 목록
 스키마 | 이름 |  종류  |  소유주
--------+------+--------+----------
 apple  | blog | 테이블 | postgres
(1개 행)


devkuma=#

그러면 kimkc 역할을 사용하여 fruit 스키마를 제거하기 위해 다음과 같이 실행한다.

C:\Users\kimkc>psql -U kimkc -d devkuma
kimkc 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

devkuma=# drop schema apple;
오류:  기타 다른 개체들이 이 개체에 의존하고 있어, apple 스키마 삭제할 수 없음
상세정보:  apple.blog 테이블 의존대상: apple 스키마
힌트:  이 개체와 관계된 모든 개체들을 함께 삭제하려면 DROP ... CASCADE 명령을 사용하십시오
devkuma=#

그러자 “오류: 기타 다른 개체들이 이 개체에 의존하고 있어, apple 스키마 삭제할 수 없음“라는 오류 메시지가 표시되고 스키마를 삭제 할 수 없다.

명시적으로 CASCADE를 지정하지 않으면 RESTRICT를 지정된 것과 같기에 스키마에서의 객체의 존재하기에 삭제가 되지 않는다.

이번에는 CASCADE를 지정하여 apple 스키마를 삭제한다. 다음과 같이 실행한다.

devkuma=# drop schema apple cascade;
알림:  apple.blog 테이블 개체가 덩달아 삭제됨
DROP SCHEMA
devkuma=#

apple 스키마가 삭제되었다. apple 스키마에 다른 소유자가 만든 테이블이 포함되어 있었지만 동시에 삭제되었다.

여기까지 DROP SCHEMA 명령을 사용하여 생성된 스키마를 제거하는 방법을 알아 보았다.

1.4.8 - PostgreSQL | 역할(사용자) ROLE 생성

PostgreSQL의 역할은 일반적으로 사용자와 그룹을 분류된다. 여기에서 역할 생성 및 삭제, 또한 역할에 대한 권한을 부여 절차 등에 대해 설명한다.

1.4.8.1 - PostgreSQL | 역할(사용자) ROLE 생성 | 역할 생성(CREATE ROLE)

CREATE ROLE 명령을 사용하여 PostgreSQL에서 역할을 새로 만드는 방법에 대해 설명한다.

새 역할 만들기

새 역할을 만들려면 CREATE ROLE 명령을 사용한다. 형식은 다음과 같다.

CREATE ROLE name [ [ WITH ] option [ ... ] ]

option:
      SUPERUSER | NOSUPERUSER
    | CREATEDB | NOCREATEDB
    | CREATEROLE | NOCREATEROLE
    | INHERIT | NOINHERIT
    | LOGIN | NOLOGIN
    | REPLICATION | NOREPLICATION
    | BYPASSRLS | NOBYPASSRLS
    | CONNECTION LIMIT connlimit
    | [ ENCRYPTED ] PASSWORD 'password'
    | VALID UNTIL 'timestamp'
    | IN ROLE role_name [, ...]
    | IN GROUP role_name [, ...]
    | ROLE role_name [, ...]
    | ADMIN role_name [, ...]
    | USER role_name [, ...]
    | SYSID uid

옵션이 많지만, 역할을 사용하여 사용자 인증을 위한 목적이라면기본 형식은 다음과 같다.

CREATE ROLE name [ WITH ] LOGIN PASSWORD 'password'

역할 이름(name)을 지정하고 클라이언트에서 PostgreSQL에 연결할 때 사용자 인증의 대상으로 사용 가능한 역할을 암호 인증 할 때 사용하는 비밀번호를 ('password')으로 만든다.

역할을 만들려면 명령을 수행하는 역할이 수퍼 유저 또는 CREATEROLE 권한을 가지고 있어야 한다.

그러면 실제로 해보도록 하자. psql에서 다음과 같이 실행한다.

postgres=# create role kuma with login password 'bear';
CREATE ROLE
postgres=#

이름이 kuma 이고, 인증 비밀번호가 bear의 새로운 역할이 생성되었다.

그러면 생성된 역할을 조회해 보자.

postgres=# \du
                                롤 목록
 롤 이름  |                      속성                      | 소속 그룹:
----------+------------------------------------------------+------------
 kimkc    | 슈퍼유저                                       | {}
 kuma     |                                                | {}
 orange   |                                                | {}
 postgres | 슈퍼유저, 롤 만들기, DB 만들기, 복제, RLS 통과 | {}


postgres=#

PostgreSQL 설치시 자동으로 생성된 postgres 이외에 방금 만든 kuma가 추가되어 있는 것을 확인할 수 있다.

생성한 역할에 PostgreSQL에 연결하기

방금 만든 역할에서 PostgreSQL에 연결를 해보자. 명령 프롬프트에서 다음과 같이 실행한다.

psql -U kuma -d devkuma

psql을 사용하는 경우 연결하는 데이터베이스를 생략하면 역할 이름과 동일한 데이터베이스에 연결하기 때문에, kuma라는 데이터베이스를 만들지 않기도 해서 여기에서는 생성된 데이터베이스 이름을 명시적으로 지정하였다.

다음과 같이 kuma 암호 입력 대기하므로 롤 만들 때 지정한 암호를 입력한다.

C:\Users\kimkc>psql -U kuma -d devkuma
kuma 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

devkuma=>

saru 역할 PostgreSQL에 연결할 수있었습니다.

CREATE ROLE 명령 권한 옵션

CREATE ROLE 명령에서 지정 가능한 옵션에서 권한과 관련된 옵션에 대해 설명한다. 기타 옵션에 대해서는 필요할 때마다 설명하겠다.

SUPERUSER or NOSUPERUSER

SUPERUSER를 지정하게 되면 작성하는 역할은 수퍼 유저가 된다. 수퍼 유저는 대부분의 권한을 가지게 되므로 만들 때주의가 필요하다. 최초에 PostgreSQL 설치할 때 자동으로 생성되는 postgres 역할은 슈퍼 유저이다. NOSUPERUSER을 지정하게 되면 수퍼 유저가 아닌 역할이 만들어진다.

SUPERUSER를 지정하려면 다음과 같다. 이 옵션을 지정하지 않으면 기본적으로 NOSUPERUSER가 지정된 것으로 간주된다.

CREATE ROLE name WITH SUPERUSER

CREATEDB or NOCREATEDB

CREATEDB를 지정하게 되면 역할은 데이터베이스를 만들 수있는 권한을 가진다. NOCREATEDB을 지정한 경우 데이터베이스를 만들 수 없다.

CREATEDB를 지정하려면 다음과 같다. 이 옵션을 지정하지 않으면 기본적으로 NOCREATEDB가 지정된 것으로 간주된다.

CREATE ROLE name WITH CREATEDB

CREATEROLE or NOCREATEROLE

CREATEROLE을 지정하게 되면 역할을 만들 권한을 갖는다. NOCREATEROLE를 지정하면 롤을 만들 수 없다.

※ CREATEROLE 권한을 부여할 경우 주의가 필요하다. CREATEROLE 권한이 있는 역할은 자신이 가지고 있지 않은 권한을 가진 같은 역할을 새로 만들 수 있다. 따라서 CREATEROLE를 지정하게 되면 결과적으로 거의 모든 권한을 주는 것과 같은 의미를 가진다.

CREATEROLE을 지정하려면 다음과 같다. 이 옵션을 지정하지 않으면 기본적으로 NOCREATEROLE가 지정된 것으로 간주된다.

CREATE ROLE name WITH CREATEROLE

그러면 실제로 해보도록 하자. CREATEDB 및 CREATEROLE을 지정된 새 역할 superkuma 을 만들려고 한다.

devkuma=# create role superkuma with createdb createrole login password 'bear';
CREATE ROLE
devkuma=#

이름이 superkuma의 새로운 역할이 만들어 졌다.

그러면 생성 된 역할을 조회해 본다.

devkuma=# \du
                                 롤 목록
  롤 이름  |                      속성                      | 소속 그룹:
-----------+------------------------------------------------+------------
 kimkc     | 슈퍼유저                                       | {}
 kuma      |                                                | {}
 orange    |                                                | {}
 postgres  | 슈퍼유저, 롤 만들기, DB 만들기, 복제, RLS 통과 | {}
 superkuma | 롤 만들기, DB 만들기                           | {}


devkuma=#

새로 만든 역할의 속성 부분을 보면 “롤 만들기, DB 만들기"라고 표시되어 있으며, CREATE ROLE 명령을 실행했을 때 지정한 CREATEDB 및 CREATEROLE 설정이 반영되는 것을 확인할 수 있다.

여기까지 CREATE ROLE 명령을 사용하여 새 역할을 만드는 방법에 대해 알아보았다.

1.4.8.2 - PostgreSQL | 역할(사용자) ROLE 생성 | 생성된 역할 조회

PostgreSQL로 생성된 역할 목록을 검색하는 방법에 대해 설명한다.

\du 명령 사용

먼저 psql의 메타 명령을 사용하여 역할 목록을 조회하는 방법을 설명하겠다. 다음과 같이 실행한다.

devkuma=# \du
                                 롤 목록
  롤 이름  |                      속성                      | 소속 그룹:
-----------+------------------------------------------------+------------
 kimkc     | 슈퍼유저                                       | {}
 kuma      |                                                | {}
 orange    |                                                | {}
 postgres  | 슈퍼유저, 롤 만들기, DB 만들기, 복제, RLS 통과 | {}
 superkuma | 롤 만들기, DB 만들기                           | {}


devkuma=#

생성된 역할 목록이 조회 되었다. 역할마다 “롤 이름, 속성, 소속 그룹"의 정보가 표시된다.

시스템 카탈로그 pg_roles으로 조회하기

이어 PostgreSQL 시스템 카탈로그의 하나인 pg_roles에서 역할 목록을 얻는 방법이다. pg_roles에는 다음과 같은 열이 있다.

이름 설명
rolname name 역할 이름
rolsuper bool 슈퍼 유저 권한의 유무
rolinherit bool 멤버 인 롤의 권한을 상속할지 여부
rolcreaterole bool 역할 만들기 권한의 유무
rolcreatedb bool 데이터베이스 생성 권한이 있는지
rolcanlogin bool 로그인을 할 수있는 역할 여부
rolreplication bool 복제에 대한 역할 여부
rolconnlimit int4 최대 동시 연결 수 (-1은 무제한)
rolpassword text 비밀번호 (다만, ********와 같이 표시)
rolvaliduntil timestamptz 암호 만료 (만료되지 않으면 NULL)
rolbypassrls bool 모든 행 단위 보안 정책을 무시할지 여부
rolconfig text[] 런타임 구성 변수에 대한 역할 별 기본
oid oid 롤의 ID

컬럼의 수가 많기에, 예를 들면 pg_roles에서 rolname과 rolsuper과 rolcanlogin 값을 조회해 본다. 다음과 같이 실행한다.

devkuma=# select rolname, rolsuper, rolcanlogin from pg_roles;
          rolname          | rolsuper | rolcanlogin
---------------------------+----------+-------------
 pg_monitor                | f        | f
 pg_read_all_settings      | f        | f
 pg_read_all_stats         | f        | f
 pg_stat_scan_tables       | f        | f
 pg_read_server_files      | f        | f
 pg_write_server_files     | f        | f
 pg_execute_server_program | f        | f
 pg_signal_backend         | f        | f
 postgres                  | t        | t
 kuma                      | f        | t
 superkuma                 | f        | t
(11개 행)


devkuma=#

역할 목록이 표시되었다. 이번에는 PostgreSQL 설치시 생성되는 postgres 역할과 나중에 만든 kuma 및 superkuma 역할 외에 pg_가 붙는 역할이 많이 표시되었다. 이 역할들은 기본 역할들로써 스스로 만든 역할에 일시적으로 권한을 부여하는 경우 등에 이용된다.

기본 역할 이외의 역할 목록을 표시할 경우에는 예를 들어 다음과 같이 실행한다.

devkuma=# select rolname, rolsuper, rolcanlogin
devkuma-#   from pg_roles
devkuma-#   where rolname not like 'pg_%';
  rolname  | rolsuper | rolcanlogin
-----------+----------+-------------
 postgres  | t        | t
 kuma      | f        | t
 superkuma | f        | t
(3 )


devkuma=#

여기까지 PostgreSQL로 생성된 역할 목록을 검색하는 방법에 대해 알아보았다.

1.4.8.3 - PostgreSQL | 역할(사용자) ROLE 생성 | 역할 속성, 이름 변경(ALTER ROLE)

ALTER ROLE 명령을 사용하여 생성된 역할에 속성을 변경할 수 있다. 역할의 속성에는 역할을 만들 때 설정한 수퍼 유저 여부와 데이터베이스 및 역할 생성 권한이 있는지 등이 있다. 여기에서 역할의 속성을 변경하는 방법에 대해 설명한다.

역할의 속성 변경하기

역할의 속성을 변경하려면 ALTER ROLE 명령을 사용한다. 형식은 다음과 같다.

ALTER ROLE role_name [ WITH ] option [ ... ]

option:
      SUPERUSER | NOSUPERUSER
    | CREATEDB | NOCREATEDB
    | CREATEROLE | NOCREATEROLE
    | INHERIT | NOINHERIT
    | LOGIN | NOLOGIN
    | REPLICATION | NOREPLICATION
    | BYPASSRLS | NOBYPASSRLS
    | CONNECTION LIMIT connlimit
    | [ ENCRYPTED ] PASSWORD 'password'
    | VALID UNTIL 'timestamp'

변경 대상의 역할(role_name)의 속성을 변경한다. 변경하려는 속성에 대해 값을 설정하면 된다. 설정되지 않은 속성은 그대로 유지된다. 역할 이름을 지정하는 대신 현재 사용자를 나타내는 CURRENT_USER와 현재 세션 사용자를 나타내는 SESSION_USER를 지정할 수도 있다.

역할을 만들 때 사용한 CREATE ROLE 명령에서 설정 가능한 항목은 ALTER ROLE 명령으로 변경할 수 있다. 그러나 역할을 그룹으로 사용하는 경우의 설정 항목에 대해서는 별도의 명령을 사용해야 한다.

수퍼 유저는 모든 롤의 속성을 변경할 수 있다. CREATEROLE 권한을 가진 역할은 슈퍼 사용자 및 복제 이외의 역할의 속성을 변경할 수 있다. 다른 역할은 자신의 비밀번호를 변경할 수 있다.

그러면 실제로 해보도록 하자. 수퍼 유저 postgres에서 PostgreSQL에 접속하여 생성된 역할 superkuma의 속성을 변경한다. superkuma 속성은 현재 다음과 같이 되어 있다.

devkuma=# \du superkuma
                    롤 목록
  롤 이름  |         속성         | 소속 그룹:
-----------+----------------------+------------
 superkuma | 롤 만들기, DB 만들기 | {}


devkuma=#

CREATEDB 및 CREATEROLE 권한이 설정되어 있다.

그러면 CREATEROLE 권한을 제거하고 CONNECTION LIMIT에 3을 설정하려고 한다. 다음과 같이 실행하자.

devkuma=# alter role superkuma with nocreaterole connection limit 3;
ALTER ROLE
devkuma=#

superkuma 역할의 속성이 변경되었다. 그럼 다시 superkuma의 속성을 확인하려고 한다.

devkuma=# \du superkuma
              롤 목록
  롤 이름  |   속성    | 소속 그룹:
-----------+-----------+------------
 superkuma | DB 만들기+| {}
           | 3개 연결  |


devkuma=#

CREATEROLE 권한이 삭제되고, 최대 동시 연결 수가 3으로 설정되어 있는 것을 확인할 수 있다.

일반 역할이 자신의 연결 암호를 변경하기

다음 수퍼 유저 또는 CREATEROLE 권한이 없는 일반 롤이 자신의 암호를 변경하려고 한다.

암호를 변경하는 역할은 kuma으로 하려고 한다. 명령 프롬프트에서 kuma로 데이터베이스에 연결한다. (연결 데이터베이스는 postgres로 하였다.)

C:\Users\kimkc>psql -U kuma -d postgres
kuma 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=>

ALTER ROLE 명령을 사용하여 암호를 변경하려고 한다. 이번에는 역할 이름을 지정하는 대신에 현재의 연결을 역할을 나타내는 SESSION_USER를 대신 사용해 보겠다. 다음과 같이 실행하도록 하자.

postgres=> alter role session_user with password 'mybear';
ALTER ROLE
postgres=>

현재 연결을 시작했을 때의 역할인 kuma 연결 암호를 새로운 값으로 변경하였다.

변경 권한이 없이 속성을 변경하려는 경우

암호 변경 이외의 속성의 변경은 수퍼 유저 또는 CREATEROLE 권한을 가진 역할 밖에 할 수 없다. 권한이 없이 속성을 변경하면 어떻게 되는지를 확인하려고 한다.

그러면 일반 역할 kuma로 데이터베이스에 연결한다. 연결 데이터베이스는 postgres로 하였다.

C:\Users\kimkc>psql -U kuma -d postgres
kuma 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=>

kuma으로 연결한 상태에서 kuma 역할의 속성을 변경하려고 한다. 다음과 같이 실행해 보자.

postgres=> alter role kuma with createrole;
오류:  권한 없음
postgres=>

시도해 보면 “오류: 권한 없음” 표시되었다. 이처럼 역할의 속성을 변경하려면 필요한 권한이 갖은 역할 밖에 할 수 없다.

여기까지 ALTER ROLE 명령을 사용하여 생성 된 역할의 속성을 변경하는 방법에 대해 알아보았다.

1.4.8.4 - PostgreSQL | 역할(사용자) ROLE 생성 | 역할 이름 변경(ALTER ROLE)

ALTER ROLE 명령을 사용하여 생성 된 역할의 이름을 변경 할 수 있다. 여기에서 역할의 이름을 변경하는 방법에 대해 설명한다.

역할의 이름 변경하기

역할의 이름을 변경하려면 ALTER ROLE 명령을 사용한다. 형식은 다음과 같다.

ALTER ROLE name RENAME TO new_name

변경 대상의 역할(role_name)의 이름을 새 이름(new_name)으로 변경한다. 현재 세션 사용자(연결할 때 사용된 역할)의 이름은 변경할 수는 없다. 즉, 자신의 이름을 변경할 수 없다는 것을 뜻한다.

수퍼 유저는 모든 역할의 이름을 변경할 수 있다. CREATEROLE 권한을 가진 역할은 슈퍼 유저가 아닌 역할의 이름을 변경할 수 있다.

그리고 클라이언트의 인증 방법으로 md5를 사용하는 경우에는 역할 이름을 변경하면 접속 암호가 지워지게 되므로 새로 설정해야 한다.

그러면 실제로 해보도록 하자. 수퍼 유저 postgres에서 PostgreSQL에 접속하여 생성된 역할 kuma의 이름을 변경한다.

먼저 현재 생성된 역할 목록을 확인해 보자.

postgres=> \du
                                 롤 목록
  롤 이름  |                      속성                      | 소속 그룹:
-----------+------------------------------------------------+------------
 kuma      |                                                | {}
 postgres  | 슈퍼유저, 롤 만들기, DB 만들기, 복제, RLS 통과 | {}
 superkuma | DB 만들기                                     +| {}
           | 3개 연결                                       |


postgres=>

현재 3개의 역할이 생성되어 있다.

그러면 kuma 역할의 이름을 mykuma로 변경한다. 다음과 같이 실행한다.

devkuma=# alter role kuma rename to mykuma;
알림:  롤 이름이 변경 되어 MD5 암호를 지웠습니다
ALTER ROLE
devkuma=#

mykuma 역할 이름이 변경되었다. 그리고 이 역할은 인증 방법으로 md5를 사용하고 있었기 때문에, “알림: 롤 이름이 변경 되어 MD5 암호를 지웠습니다” 라고 표시되었듯이 암호가 지워졌다.

그래서 ALTER ROLE 명령을 사용하여 접속 암호를 다시 설정한다.

devkuma=# alter role mykuma with password 'mybear';
ALTER ROLE
devkuma=#

mykuma 역할 접속 암호가 설정되었다.

그러면 확인을 위해 다시 생성된 역할 목록을 확인해 보자.

devkuma=# \du
                                 롤 목록
  롤 이름  |                      속성                      | 소속 그룹:
-----------+------------------------------------------------+------------
 mykuma    |                                                | {}
 postgres  | 슈퍼유저, 롤 만들기, DB 만들기, 복제, RLS 통과 | {}
 superkuma | DB 만들기                                     +| {}
           | 3개 연결                                       |


devkuma=#

kuma 역할이 mykuma 역할로 이름이 변경되는 것을 확인할 수있다. 여기서 역할에 설정되어 속성은 그대로 되어 있다.

여기까지 ALTER ROLE 명령을 사용하여 생성 된 역할의 이름을 변경하는 방법에 대해 알아보았다.

1.4.8.5 - PostgreSQL | 역할(사용자) ROLE 생성 | 역할 암호 만료 설정(ALTER ROLE)

PortgreSQL에 연결된 역할의 인증에 사용할 비밀번호에 대해서 역할을 만들 때와 역할을 생성한 후에도 속성을 변경하여 비밀번호 만료를 설정할 수 있다. 여기에서는 역할의 비밀번호 만료를 설정하는 방법과 비밀번호가 비활성화 된 역할을 다시 암호 인증을 할 수 있도록 설정하는 방법에 대해 설명한다.

비밀번호 만료 설정

역할 만들 때 비밀번호 만료를 설정하려면 다음과 같이 실행한다.

CREATE ROLE name
  WITH LOGIN PASSWORD 'password' VALID UNTIL 'timestamp'

LOGIN 및 PASSWORD ‘password’속성을 설정하여 만드는 역할은 PostgreSQL의 연결 역할로 사용할 수 있지만, VALID UNTIL 'timestamp'를 설정하여 사용하는 암호 만료를 ’timestamp’까지 설정할 수 있다.

또한 생성된 역할에 대해 비밀번호 만료 특성만을 설정한 경우 다음과 같이 실행한다.

ALTER ROLE name WITH VALID UNTIL 'timestamp'

현재 설정되어 있는 비밀번호 만료를 ’timestamp’까지 설정한다.

유효 기간은 ‘2020-10-31 20:33:10’과 같이 지정한다. 지정된 날짜와 시간이 되었을 때부터 비밀번호가 비활성화된다. 만약 ‘2020-10-31’처럼 날짜만 지정하면 ‘2020-10-31 00:00:00’와 같다.

그러면 실제로 해보도록 하자. 현재 생성된 mykuma 롤의 유효 기간을 ‘2020-10-10 06:00:00’로 설정한다.

devkuma=# alter role mykuma with valid until '2020-10-10 06:00:00';
ALTER ROLE
devkuma=#

mykuma 역할의 비밀번호 만료가 설정되었다.

비밀번호 만료를 설정한 후에 psql 메타 명령 \du를 사용하여 mykuma 역할의 속성을 표시하여 보면, 암호 만료가 설정되어있는 것을 확인할 수 있다.

devkuma=# \du mykuma
                             롤 목록
 롤 이름 |                   속성                    | 소속 그룹:
---------+-------------------------------------------+------------
 mykuma  | 비밀번호 만료기한: 2020-10-10 06:00:00+09 | {}


devkuma=#

암호 만료가 설정 역할은 비밀번호가 만료되기 전에 PostgreSQL에 연결되어 있지 않으면 비밀번호가 만료되는 날짜와 시간이 되어도 연결은 그대로 사용할 수 있다. 그러나 연결을 끊기면 비밀번호 인증 실패한다.

암호가 비활성화 된 상태에서 연결을 시도하면 “psql: 오류: 서버 접속 실패: 치명적오류: 사용자 “mykuma"의 password 인증을 실패했습니다“고 표시되어 연결에 실패한다.

C:\Users\kimkc>psql -U mykuma -d postgres
mykuma 사용자의 암호:
psql: 오류: 서버 접속 실패: 치명적오류:  사용자 "mykuma"의 password 인증을 실패했습니다

C:\Users\kimkc>

암호 만료 설정을 취소하기

비밀번호가 만료되어 비활성화 된 경우에는 새로운 비밀번호를 설정해도 인증할 수 없다. 다시 해당 역할이 비밀번호 인증을 할 수 있게하려면 비밀번호 만료 설정을 ALTER ROLE 명령을 사용하여 취소해야 한다.

그러면 실제로 시도합니다. 암호가 현재 사용할 수있는 momo 역할에 대해 암호 만료 설정을 취소하려면 만료로 현재보다 먼저 날짜와 시간을 다시 지정하거나 무기한을 의미하는 ‘infinity’를 설정하십시오 .

postgres=# alter role mykuma with valid until 'infinity';
ALTER ROLE
postgres=#

mykuma 역할의 암호 만료 설정이 취소되었다. 정확하게는 비밀번호 만료 무기한 설정되었다.

다시 psql 메타 명령 \du를 사용하여 momo 역할의 속성을 표시해 보면, 암호 만료가 ‘infinity’로 설정되어 있는 것을 확인할 수 있다.

postgres=# \du mykuma
                      롤 목록
 롤 이름 |            속성             | 소속 그룹:
---------+-----------------------------+------------
 mykuma  | 비밀번호 만료기한: infinity | {}


postgres=#

유효 기간 설정 변경 후에 해당 역할로 PostgreSQL에 접속을 보면 문제없이 암호 인증이 통해서 연결을 할 수 있다.

C:\Users\kimkc>psql -U mykuma -d postgres
mykuma 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

postgres=>

여기까지 역할의 암호 만료를 설정하는 방법과 암호가 비활성화 된 역할을 다시 암호 인증을 실시 할 수 있도록 설정하는 방법에 대해 알아보았다.

1.4.8.6 - PostgreSQL | 역할(사용자) ROLE 생성 | 역할 테이블, 뷰 등에 대한 권한을 추가(GRANT)

GRANT 명령을 사용하여 역할에 권한을 추가할 수 있다. 권한은 테이블이나 스키마 작성 권한이나 테이블과 컬럼에서 데이터를 검색하거나 데이터를 추가할 수 있는 권한 등이다. 여기에서는 GRANT 명령을 사용하여 권한을 추가하는 방법에 대해 설명한다.

GRANT 명령을 사용하여 권한 추가하기

GRANT 명령을 사용하여 역할에 권한을 추가할 수 있다. 목적에 따라 여러 형식이 준비되어 있다.

GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
    [, ...] | ALL [ PRIVILEGES ] }
    ON { [ TABLE ] table_name [, ...]
         | ALL TABLES IN SCHEMA schema_name [, ...] }
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( column_name [, ...] )
    [, ...] | ALL [ PRIVILEGES ] ( column_name [, ...] ) }
    ON [ TABLE ] table_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { { USAGE | SELECT | UPDATE }
    [, ...] | ALL [ PRIVILEGES ] }
    ON { SEQUENCE sequence_name [, ...]
         | ALL SEQUENCES IN SCHEMA schema_name [, ...] }
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] }
    ON DATABASE database_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON DOMAIN domain_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON FOREIGN DATA WRAPPER fdw_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON FOREIGN SERVER server_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { EXECUTE | ALL [ PRIVILEGES ] }
    ON { { FUNCTION | PROCEDURE | ROUTINE }
       routine_name [ ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) ] [, ...]
       | ALL { FUNCTIONS | PROCEDURES | ROUTINES } IN SCHEMA schema_name [, ...] }
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON LANGUAGE lang_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] }
    ON LARGE OBJECT loid [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
    ON SCHEMA schema_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { CREATE | ALL [ PRIVILEGES ] }
    ON TABLESPACE tablespace_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON TYPE type_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

여러 형식이 있지만, 기본은 아래와 같다.

GRANT 권한 ON 대상 TO 누구

권한“은 대상에 따라 다른데 정리하자면 다음과 같다.

권한 설명
SELECT 테이블이나 뷰에 데이터를 조회할 수 있다. UPDATE와 DELETE를하기 위해서도 필요하다.
INSERT 테이블에 새로운 데이터를 추가할 수 있다.
UPDATE 테이블의 데이터를 업데이트 할 수 있다.
DELETE 테이블에서 데이터를 삭제할 수 있다.
TRUNCATE 테이블의 데이터를 비울 수 있다.
REFERENCES 테이블과 컬럼에 외래 키 제약 조건을 만들 수 있다.
TRIGGER 테이블에 트리거를 만들 수 있다.
CREATE 대상이 데이터베이스의 경우 스키마를 만들 수 있다. 대상 스키마의 경우, 테이블 등의 개체를 만들 수 있다.
CONNECT 지정된 데이터베이스에 연결할 수 있다.
TEMPORARY 데이터베이스에 임시 테이블을 만들 수 있다.
EXECUTE 함수 또는 프로 시저 연산자의 사용을 허용한다.
USAGE 대상이 스키마의 경우는 객체에 대한 액세스를 허용한다. 대상이 스키가 아니면, 대상에 따라 권한이 부여 된다.
ALL PRIVILEGES 사용할 수 있는 권한을 정리해 허용한다.

대상“은 테이블, 테이블의 컬럼 데이터베이스 스키마 등이다.

누구“는 역할 이름을 지정한다. 현재 사용자를 나타내는 CURRENT_USER와 현재 세션 사용자를 나타내는 SESSION_USER도 지정할 수 있다. 또한 PUBLIC으로 지정한 경우에는 모든 역할(현재 그리고 향후 추가되는 역할의 전부)에 대한 권한이 추가된다.

권한을 다른 역할에 추가는 대상의 객체의 소유자 및 수퍼 유저가 할수 있다. 또한 GRANT 명령을 실행할 때에는 WITH GRANT OPTION을 지정하면 권한이 부여된 역할은 동일한 권한을 다른 역할에 추가할 수 있게 한다. (단, PUBLIC을 지정한 경우는 넣을 수 없다).

그러면 보다 구체적으로 살펴 보도록 하겠다.

테이블에 대한 권한 추가

먼저 테이블에 대한 권한을 추가하는 방법에 대해서 설명하겠다.

GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
    [, ...] | ALL [ PRIVILEGES ] }
    ON { [ TABLE ] table_name [, ...]
         | ALL TABLES IN SCHEMA schema_name [, ...] }
    TO role_specification [, ...] [ WITH GRANT OPTION ]

예를 들어, role_name 역할에 테이블 table_name을 대상으로 SELECT와 INSERT 권한을 추가하려면 다음과 같이 실행한다.

GRANT SELECT, INSERT ON table_name To role_name;

또한, role_name 역할에 테이블 table_name을 대상으로 모든 권한을 추가하려면 다음과 같이 실행한다. (모든 권한이라는 것은 이 형식으로 지정할 수 있는 SELECT, INSERT, UPDATE, DELETE TRUNCATE, REFERENCES, TRIGGER이다).

GRANT ALL PRIVILEGES ON table_name To role_name;

role_name 역할에 지정된 스키마 schema_name에 포함된 모든 테이블을 대상으로 SELECT 권한을 추가하려면 다음과 같이 실행한다.

GRANT SELECT ON ALL TABLES IN SCHEMA schema_name To role_name;

role_name 역할에 테이블 table_name을 대상으로 SELECT와 INSERT 권한을 추가하고 또한 다른 사용자에게 동일한 권한을 부여할 수 있도록하려면 다음과 같이 실행한다.

GRANT SELECT, INSERT ON table_name To role_name WITH GRANT OPTION;

그러면 실제로 해보도록 하자. 현재 devkuma 데이터베이스에는 public 스키마에 memo 테이블과 myschema 스키마 book 테이블이 작성되어 있다. 테이블과 컬럼에 대한 권한 정보를 얻으려면 psql 메타 명령 \dp를 사용한다. (\dp는 테이블, 뷰, 시퀀스 목록을 액세스 권한과 함께 표시하는 명령이다.)

devkuma=# \dp
                         액세스 권한
 스키마 | 이름 |  종류  | 액세스 권한 | 칼럼 접근권한 | 정책
--------+------+--------+-------------+---------------+------
 public | memo | 테이블 |             |               |
(1개 행)


devkuma=# \dp myschema.*
                          액세스 권한
  스키마  | 이름 |  종류  | 액세스 권한 | 칼럼 접근권한 | 정책
----------+------+--------+-------------+---------------+------
 myschema | book | 테이블 |             |               |
(1개 행)


devkuma=#

여기서 수퍼 유저가 아닌 mykuma 역할에 SELECT 권한을 추가 할 수 있다. 권한을 추가하기 전에 데이터를 취득하면 어떻게되는지를 확인하기 위해 mykuma에서 PostgreSQL에 접속 후 memo 테이블에서 데이터를 조회해 본다.

devkuma=> select * from memo;
오류:  memo 테이블에 대한 접근 권한 없음
devkuma=>

memo 테이블에 대한 SELECT 권한이 없기에 “오류: memo 테이블에 대한 접근 권한 없음“라고 표시되어 데이터를 가져 오는데 실패한다.

그러면 mykuma 역할에 memo 테이블 및 myshema.book 테이블에 SELECT 권한을 추가한다.. 수퍼 유저로 접속하여 다음과 같이 실행한다.

devkuma=# grant select on memo to mykuma;
GRANT
devkuma=# grant select on myschema.book to mykuma;
GRANT
devkuma=#

권한이 추가되었다. 그러면 확인을 위해 다시 \dp를 실행해 보자.

devkuma=# \dp
                                액세스 권한
 스키마 | 이름 |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
--------+------+--------+---------------------------+---------------+------
 public | memo | 테이블 | postgres=arwdDxt/postgres+|               |
        |      |        | mykuma=r/postgres         |               |
(1개 행)


devkuma=# \dp myschema.*
                                 액세스 권한
  스키마  | 이름 |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
----------+------+--------+---------------------------+---------------+------
 myschema | book | 테이블 | postgres=arwdDxt/postgres+|               |
          |      |        | mykuma=r/postgres         |               |
(1개 행)


devkuma=#

각 테이블의 액세스 권한 곳에 다음과 같은 값이 설정되어 있다.

postgres=arwdDxt/postgres+
 mykuma=r/postgres

값은 다음과 같다. mykuma 역할에 대해서는 postgres 역할이 r(SELECT)의 권한이 추가되어 있다는 의미이다.

rolename=xxxx -- 역할에 부여된 권한
=xxxx -- PUBLIC에 부여 된 권한

            r -- SELECT (읽기 (read))
            w -- UPDATE (쓰기 (write))
            a -- INSERT (추가 (append))
            d -- DELETE
            D -- TRUNCATE
            x -- REFERENCES
            t -- TRIGGER
            X -- EXECUTE
            U -- USAGE
            C -- CREATE
            c -- CONNECT
            T -- TEMPORARY
      arwdDxt -- 모든 권한 (테이블용. 다른 객체는 다르다.)
            * -- 직전의 권한에 관한 그랜트 옵션

        /yyyy -- 이 권한을 부여한 역할

그럼 다시 일반 역할인 mykuma에서 PostgreSQL에 접속 후 memo 테이블에서 데이터를 조회해 본다.

devkuma=> select * from memo;
 id | memo
----+------
(0개 행)


devkuma=>

memo 테이블에서 데이터를 검색할 수 있게 되었다.

마찬가지로 mysheme.book 테이블에 데이터를 조회해 본다.

devkuma=> select * from myschema.book;
오류:  myschema 스키마(schema) 접근 권한 없음
줄 1: select * from myschema.book;
                    ^
devkuma=>

이번에는”오류: myschema 스키마(schema) 접근 권한 없음“라고 표시되어 데이터를 조회할 수 없다. 이것은 public 스키마이면 기본적으로 스키마의 테이블 등의 객체에 대한 액세스가 허용되지만만, public 이외의 스키마의 경우 명시적으로 USAGE 권한을 추가하지 않으면 스키마의 객체에 액세스 할 수 없다.

스키마 객체에 대한 권한을 추가하는 방법에 대한 자세한 내용은 나중에 설명하기로 하고, 우선 수퍼 유저로 접속한 뒤 다음과 같이 실행한다.

devkuma=# grant usage on schema myschema to mykuma;
GRANT
devkuma=#

myshema 스키마의 개체에 대한 액세스가 허용되었다.

그럼 다시 mysheme.book 테이블에서 데이터를 조회해 보겠다.

devkuma=> select * from myschema.book;
 id
----
(0개 행)


devkuma=>

myschema.book 테이블에서 데이터를 조회할 수 있게 되었다.

테이블의 컬럼에 대한 권한 추가

다음 테이블의 컬럼에 대한 권한을 추가하는 방법에 대해서 설명하겠다.

GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( column_name [, ...] )
    [, ...] | ALL [ PRIVILEGES ] ( column_name [, ...] ) }
    ON [ TABLE ] table_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

예를 들어 role_name 역할에 테이블 table_name의 column_name 컬럼을 대상으로 SELECT 권한을 추가하려면 다음과 같이 실행한다.

GRANT SELECT (column_name) ON table_name To role_name;

그러면 실제로 해보도록 하자. psql 메타 명령 \dp를 사용하여 테이블과 컬럼에 대한 권한을 확인 해 보면, mykuma 아무것도 권한이 추가되지 않은 상태이다.

devkuma=# \dp
                                액세스 권한
 스키마 | 이름 |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
--------+------+--------+---------------------------+---------------+------
 public | memo | 테이블 | postgres=arwdDxt/postgres |               |
(1개 행)


devkuma=#

그러면 mykuma 역할에 memo 테이블의 memo 컬럼에 대한 SELECT 권한을 추가한다. 수퍼 유저로 접속하여 다음과 같이 실행한다.

devkuma=# grant select (memo) on memo to mykuma;
GRANT
devkuma=#

권한이 추가되었다. 그러면 확인을 위해 재차 \dp를 실행해 본다.

devkuma=# \dp
                                   액세스 권한
 스키마 | 이름 |  종류  |        액세스 권한        |    칼럼 접근권한    | 정책
--------+------+--------+---------------------------+---------------------+------
 public | memo | 테이블 | postgres=arwdDxt/postgres | memo:              +|
        |      |        |                           |   mykuma=r/postgres |
(1개 행)


devkuma=#

열 권한 곳에 다음과 같은 값이 설정되어 있다.

memo:              +
 mykuma=r/postgres 

설정되어 있는 값은 접근 권한의 경우와 동일한다. mykuma 역할에 대해 postgres 역할에 의해 r(SELECT)의 권한이 추가되었다는 의미이다.

그럼 다시 일반 역할인 momo에서 PostgreSQL에 접속하여 memo 테이블에서 데이터를 조회한다.

devkuma=> select * from memo;
오류:  memo 테이블에 대한 접근 권한 없음
devkuma=>

오류: memo 테이블에 대한 접근 권한 없음“라고 표시되면서 데이터 조회에 실패한다. 이는 memo 테이블에는 id 열과 memo 컬럼의 두 컬럼이 있고 mykuma 역할은 name 컬럼의 SELECT 권한 한 추가되지 않기 때문이다.

그러면 memo 테이블의 memo 컬럼 만 데이터를 조회해 본다.

devkuma=> select memo from memo;
 memo
------
(0개 행)


devkuma=>

이번에는 데이터의 조회에 성공하였다.

또한 public 스키마 이외의 스키마에 작성되는 테이블의 컬럼에서 데이터를 검색하거나하려면 대상 스키마의 USAGE 권한이 필요하다.

스키마를 만들 수 있는 권한 추가하기

다음 지정된 데이터베이스에 스키마를 만들 수있는 권한을 추가하는 방법에 대해서 설명하겠다.

GRANT {{CREATE | CONNECT | TEMPORARY | TEMP} [...] | ALL [PRIVILEGES]}
    ON DATABASE database_name [...]
    TO role_specification [...] [WITH GRANT OPTION]

예를 들어 role_name 역할에 데이터베이스 database_name에서 스키마를 만들 수있는 권한을 추가하려면 다음과 같이 실행한다.

GRANT CREATE ON DATABASE database_name To role_name;

그러면 실제로 해보도록 하자. psql 메타 명령 \l을 사용하여 devkuma 데이터베이스에 대한 권한을 확인해 보면, memo는 devkuma 데이터베이스에 대해 아무런 권한이 없다.

devkuma=# \l devkuma
                                데이터베이스 목록
  이름   |  소유주  | 인코딩 |     Collate      |      Ctype       | 액세스 권한
---------+----------+--------+------------------+------------------+-------------
 devkuma | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 |
(1개 행)


devkuma=#

그러면 momo 역할에 devkuma 데이터베이스에 스키마를 만들 수 있는 권한을 추가한다. 수퍼 유저로 접속하여 다음과 같이 실행한다.

devkuma=# grant create on database devkuma to mykuma;
GRANT
devkuma=#

권한이 추가되었다. 그러면 확인을 위해 다시 \l을 실행해 본다.

devkuma=# \l devkuma
                                     데이터베이스 목록
  이름   |  소유주  | 인코딩 |     Collate      |      Ctype       |      액세스 권한
---------+----------+--------+------------------+------------------+-----------------------
 devkuma | postgres | UTF8   | Korean_Korea.949 | Korean_Korea.949 | =Tc/postgres         +
         |          |        |                  |                  | postgres=CTc/postgres+
         |          |        |                  |                  | mykuma=C/postgres
(1개 행)


devkuma=#

액세스 권한 있는 곳에 다음과 같은 값이 설정되어 있다.

=Tc/postgres         +
postgres=CTc/postgres+
mykuma=C/postgres

설정되어 있는 값은 접근 권한의 경우와 동일한다. mykuma 역할에 대해서는 postgres 역할에 의해 C(CREATE)의 권한이 추가되었다라는 의미이다.

그러면 일반 역할인 mykuma에서 PostgreSQL에 접속 후 devkuma 데이터베이스에 mykuma 스키마를 만들려고 한다.

devkuma=> create schema mykuma;
CREATE SCHEMA
devkuma=>

mydb 데이터베이스에 devkuma 스키마를 만들 수 있게 되었다. 확인을 위해 psql 메타 명령 \dn을 실행해 본다. `` devkuma=> \dn 스키마(schema) 목록 이름 | 소유주 ———-+———- mykuma | mykuma myschema | postgres public | postgres (3개 행)

devkuma=> ``

mykuma 스키마가 생성되어있는 것을 확인할 수 있다.

스키마에 테이블 같은 객체를 만들 수 있는 권한 추가하기

마지막으로 지정된 스키마에 테이블 같은 개체를 만들 수 있는 권한을 추가하는 방법에 대해서 설명하겠다.

GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
    ON SCHEMA schema_name [, ...]
    TO role_specification [, ...] [ WITH GRANT OPTION ]

예를 들어 role_name 역할에 schema_name 스키마에서 테이블 등을 만들 수있는 권한을 추가하려면 다음과 같이 실행한다.

GRANT CREATE ON SCHEMA schema_name To role_name;

또한 role_name 역할에 schema_name 스키마에서 테이블 등에 대한 액세스 권한을 추가하려면 다음과 같이 실행한다.

GRANT USAGE ON SCHEMA schema_name To role_name;

그러면 실제로 해보록 하자. psql 메타 명령 \dn+를 사용하여 myschema 스키마에 대한 권한을 확인 해 보면, momo는 myschema 스키마에 대한 권한이 아무것도 없습니다.

devkuma=# \dn+
                         스키마(schema) 목록
   이름   |  소유주  |     액세스 권한      |          설명
----------+----------+----------------------+------------------------
 mykuma   | mykuma   |                      |
 myschema | postgres | postgres=UC/postgres+|
          |          | mykuma=UC/postgres   |
 public   | postgres | postgres=UC/postgres+| standard public schema
          |          | =UC/postgres         |
(3개 행)


devkuma=#

그러면 mykuma 역할에 myschema 스키마에 개체를 만들 수 있는 권한을 추가한다. 수퍼 유저로 접속하여 다음과 같이 실행한다.

devkuma=# grant create on schema myschema to mykuma;
GRANT
devkuma=#

권한이 추가되었다. 그러면 확인을 위해 다시 \dn+를 실행해 본다.

devkuma=# \dn+
                         스키마(schema) 목록
   이름   |  소유주  |     액세스 권한      |          설명
----------+----------+----------------------+------------------------
 mykuma   | mykuma   |                      |
 myschema | postgres | postgres=UC/postgres+|
          |          | mykuma=UC/postgres   |
 public   | postgres | postgres=UC/postgres+| standard public schema
          |          | =UC/postgres         |
(3개 행)


devkuma=#

액세스 권한 곳에 다음과 같은 값이 설정되어 있습니다.

postgres=UC/postgres+
mykuma=C/postgres

설정되어 있는 값은 접근 권한의 경우와 동일한다. mykuma 역할에 대해서는 postgres 역할에 의해 C(CREATE)의 권한이 추가되었다는 의미이다.

그러면 일반 역할인 mykuma으로 PostgreSQL에 접속하여, devkuma 데이터베이스의 myschema 스키마에 product 테이블을 만들려고 한다.

devkuma=> create table myschema.product (id integer, name varchar (10));
CREATE TABLE
devkuma=>

myschema 스키마에 product 테이블을 만들었다. 확인을 위해 psql 메타 명령 \dp를 실행해 본다.

devkuma=> \dp myschema.*
                                  액세스 권한
  스키마  |  이름   |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
----------+---------+--------+---------------------------+---------------+------
 myschema | book    | 테이블 | postgres=arwdDxt/postgres+|               |
          |         |        | mykuma=r/postgres         |               |
 myschema | product | 테이블 |                           |               |
(2개 행)


devkuma=>

myschema.product 테이블이 생성되어 있는 것을 확인할 수 있다.

여기까지 GRANT 명령을 사용하여 권한을 추가하는 방법에 대해 설명하였다.

1.4.8.7 - PostgreSQL | 역할(사용자) ROLE 생성 | 역할 권한 삭제 (REVOKE)

GRANT 명령을 사용하여 역할에 추가된 권한은 REVOKE 명령으로 제거 할 수 있다. 여기에서는 REVOKE 명령을 사용하여 권한을 삭제하는 방법에 대해 설명하겠다.

REVOKE 명령을 사용하여 권한 제거

REVOKE 명령을 사용하여 역할에 추가된 권한을 제거 할 수 있다. 목적에 따라 여러 형식이 준비되어 있다.

REVOKE [ GRANT OPTION FOR ]
    { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
    [, ...] | ALL [ PRIVILEGES ] }
    ON { [ TABLE ] table_name [, ...]
         | ALL TABLES IN SCHEMA schema_name [, ...] }
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { SELECT | INSERT | UPDATE | REFERENCES } ( column_name [, ...] )
    [, ...] | ALL [ PRIVILEGES ] ( column_name [, ...] ) }
    ON [ TABLE ] table_name [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { USAGE | SELECT | UPDATE }
    [, ...] | ALL [ PRIVILEGES ] }
    ON { SEQUENCE sequence_name [, ...]
         | ALL SEQUENCES IN SCHEMA schema_name [, ...] }
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] }
    ON DATABASE database_name [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON DOMAIN domain_name [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON FOREIGN DATA WRAPPER fdw_name [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON FOREIGN SERVER server_name [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { EXECUTE | ALL [ PRIVILEGES ] }
    ON { { FUNCTION | PROCEDURE | ROUTINE }
       function_name [ ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) ] [, ...]
         | ALL { FUNCTIONS | PROCEDURES | ROUTINES } IN SCHEMA schema_name [, ...] }
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON LANGUAGE lang_name [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] }
    ON LARGE OBJECT loid [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
    ON SCHEMA schema_name [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { CREATE | ALL [ PRIVILEGES ] }
    ON TABLESPACE tablespace_name [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON TYPE type_name [, ...]
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

여러 형식이 있지만, 기본은 아래와 같다.

REVOKE 권한 ON 대상 FROM 누구

여기서 “권한, 대상, 누구” GRANT 명령과 동일하다. 자세한 내용은 “역할 테이블, 뷰 등에 대한 권한을 추가(GRANT)“를 참조하도록 하자.

GRANT OPTION FOR를 넣어 REVOKE 명령을 실행하게 되면 , 권한을 다른 역할에 부여 소유권만 삭제된다. (GRANT 명령에서 WITH GRANT OPTION에 의해 추가 된 권한). 지정하지 않으면 권한 자체와 권한을 다른 역할에 부여한 내용도 모두 삭제된다.

REVOKE 명령은 마지막에 CASCADE 또는 RESTRICT를 지정할 수 있다. 자신에 추가된 것과 동일한 권한을 다른 역할에 추가한 경우에 기본 설정(RESTRICT)으로 자신에 추가된 권한의 삭제를 시도하면 실패하게 된다. 이에 반해 CASCADE를 지정하게 되면 자신의 권한과 동시에 자신이 다른 역할에 추가한 동일한 권한도 함께 삭제된다.

권한을 다른 역할에서 제거는 개체의 소유자 및 수퍼 유저만 가능하다. 그 외에 역할의 경우는 GRANT 명령의 WITH GRANT OPTION으로 다른 역할에 추가한 권한만 삭제할 수 있다.

그러면보다 구체적으로 방법을 살펴 보도록 하겠다.

테이블에 대한 권한을 제거

테이블에 대한 권한을 삭제하는 경우를 예로 REVOKE 명령의 사용법을 확인하려고 한다. 사용하는 형식은 다음과 같다.

REVOKE [ GRANT OPTION FOR ]
    { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
    [, ...] | ALL [ PRIVILEGES ] }
    ON { [ TABLE ] table_name [, ...]
         | ALL TABLES IN SCHEMA schema_name [, ...] }
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

예제로 role_name 역할에 테이블 table_name을 대상으로 SELECT와 INSERT 권한을 제거하려면 다음과 같이 실행한다.

REVOKE SELECT, INSERT ON table_name FROM role_name;

role_name 역할에 테이블 table_name을 대상으로 SELECT 권한을 다른 역할에 추가할 권한만 제거하려면 다음과 같이 실행한다.

REVOKE GRANT OPTION FOR SELECT ON table_name FROM role_name;

role_name 역할에 테이블 table_name을 대상으로 SELECT 권한을 삭제하고 role_name 역할이 다른 역할에 추가 한 SELECT 권한도 동시에 제거하려면 다음과 같이 실행한다.

REVOKE SELECT ON table_name FROM role_name CASCADE;

role_name 역할에 테이블 table_name을 대상으로 모든 권한을 제거하려면 다음과 같이 실행한다.

REVOKE ALL PRIVILEGES ON table_name FROM role_name;

그러면 실제로 해보도록 하자. 현재 public 스키마에 staff 테이블이 생성되고, momo 역할에 대해 SELECT, INSERT, UPDATE 권한이 추가되어 있다. psql 메타 명령 \dp를 사용하여 확인을 해보자.

devkuma=# \dp
                                액세스 권한
 스키마 | 이름 |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
--------+------+--------+---------------------------+---------------+------
 public | memo | 테이블 | postgres=arwdDxt/postgres+|               |
        |      |        | mykuma=arw/postgres       |               |
(1개 행)


devkuma=#

※ 위 액세스 권한은 GRANT SELECT,INSERT,UPDATE ON memo TO mykuma;으로 추가된 권한이다.

memo 테이블의 액세스 권한에 다음과 같은 값이 설정되어 있다.

postgres=arwdDxt/postgres
mykuma=arw/postgres

값의 의미는 다음과 같다. mykuma 역할에 대해서는 postgres 역할이 a(INSERT), r(SELECT), w(UPDATE)의 권한이 추가되어 있다 것을 의미한다.

rolename=xxxx -- 롤에 부여 된 권한
=xxxx -- PUBLIC에 부여된 권한

            r -- SELECT (읽기 (read))
            w -- UPDATE (쓰기 (write))
            a -- INSERT (추가 (append))
            d -- DELETE
            D -- TRUNCATE
            x -- REFERENCES
            t -- TRIGGER
            X -- EXECUTE
            U -- USAGE
            C -- CREATE
            c -- CONNECT
            T -- TEMPORARY
      arwdDxt -- 모든 권한 (테이블 용 다른 객체는 다르다.)
            * -- 직전의 권한에 관한 그랜트 옵션

        / yyyy -이 권한을 부여한 역할

그러면, mykuma 역할에서 memo 테이블에 대한 SELECT 권한을 제거한다. 수퍼 유저로 접속하여 다음과 같이 실행한다.

devkuma=# revoke select on memo from mykuma;
REVOKE
devkuma=#

SELECT 권한이 삭제되었다. 확인을 위해 다시 \dp 명령을 실행해 보자.

devkuma=# \dp
                                액세스 권한
 스키마 | 이름 |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
--------+------+--------+---------------------------+---------------+------
 public | memo | 테이블 | postgres=arwdDxt/postgres+|               |
        |      |        | mykuma=aw/postgres        |               |
(1개 행)


devkuma=#

액세스 권한의 값이 mykuma=arw/postgres에서 mykuma=aw/postgres되어 r(SELECT)가 제거된 것을 확인할 수 있다.

다음은 mykuma 역할에서 memo 테이블에 대한 모든 권한을 제거해 보자. 수퍼 유저로 접속한 뒤 다음과 같이 실행한다.

devkuma=# revoke all privileges on memo from mykuma;
REVOKE
devkuma=#

남아 있던 INSERT 및 UPDATE 권한도 삭제되었다. 확인을 위해 다시 \dp 명령을 실행해 보도록 하자.

devkuma=# \dp
                                액세스 권한
 스키마 | 이름 |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
--------+------+--------+---------------------------+---------------+------
 public | memo | 테이블 | postgres=arwdDxt/postgres |               |
(1개 행)


devkuma=#

액세스 권한에 mykuma 역할 자체가 모두 삭제되었다.

CASCADE를 지정하고 권한을 모두 삭제하기

역할 A가 자신에게 추가된 권한을 다른 역할 B에 추가한 경우에 역할 A의 권한을 삭제하려고 하면 실패한다. 이러한 경우 REVOKE 명령을 실행할 때 CASCADE를 지정한다. CASCADE를 지정하면 역할 A의 권한을 제거하는 동시에 역할 A가 역할 B에 추가한 권한도 연쇄적으로 삭제된다.

그러면 실제로 해보도록 하자. 먼저 수퍼 유저로 PostgreSQL에 접속하여 mykuma 역할에 public 스키마에 memo 테이블을 대상으로 SELECT 권한을 추가한다. 이때 WITH GRANT OPTION을 추가하여 다른 역할에 권한을 추가할 수 있도록 한다.

devkuma=# grant select on memo to mykuma with grant option;
GRANT
devkuma=#

다음에 mykuma 역할로 PostgreSQL에 접속하여, superkuma 역할에 public 스키마에 memo 테이블을 대상으로 SELECT 권한을 추가한다.

C:\Users\kimkc>psql -U mykuma -d devkuma
mykuma 사용자의 암호:
psql (12.2)
도움말을 보려면 "help"를 입력하십시오.

devkuma=> grant select on memo to superkuma;
GRANT
devkuma=>

psql 메타 명령 \dp를 사용하여 테이블에 추가되는 권한을 확인하려고 한다.

devkuma=> \dp
                                액세스 권한
 스키마 | 이름 |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
--------+------+--------+---------------------------+---------------+------
 public | memo | 테이블 | postgres=arwdDxt/postgres+|               |
        |      |        | mykuma=r*/postgres       +|               |
        |      |        | superkuma=r/mykuma        |               |
(1개 행)


devkuma=>

staff 테이블의 액세스 권한 곳에 다음과 같은 값이 설정되어 있다.

postgres=arwdDxt/postgres+
mykuma=r*/postgres       +
superkuma=r/mykuma

mykuma 역할에 대해서는 postgres 역할이 r(SELECT) *(그랜트 옵션)의 권한이 추가되었으며, superkuma 역할에 대해서는 momo 역할은 r(SELECT)의 권한이 추가되었다.

이 상태에서 mykuma 역할에서 memo 테이블에 대한 SELECT 권한을 제거하려고 한다. 수퍼 유저로 접속하여 다음과 같이 실행한다.

devkuma=# revoke select on memo from mykuma;
오류:  ???의존(적인) 권한이 존재합니다
힌트:  그것들을 취소하려면 "CASCADE"를 사용하세요.
devkuma=#

그러자 “오류: ???의존(적인) 권한이 존재합니다“라는 오류가 발생하고 권한의 삭제에 실패하였다. mykuma 역할에서 제거하려고 권한 대해 mykuma 역할이 다른 역할에 권한을 추가하였기 때문이다.

이런 때는 REVOKE 명령에 CASCADE를 지정하여 실행한다. 수퍼 유저로 접속하여 다음과 같이 실행한다.

devkuma=# revoke select on memo from mykuma cascade;
REVOKE
devkuma=#

이번에는 권한의 삭제에 성공하였다. 확인을 위해 다시 \dp 명령을 실행해 본다.

devkuma=# \dp
                                액세스 권한
 스키마 | 이름 |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
--------+------+--------+---------------------------+---------------+------
 public | memo | 테이블 | postgres=arwdDxt/postgres |               |
(1개 행)


devkuma=#

액세스 권한의 값에 mykuma에 추가한 권한이 삭제되면서 superkuma에 추가한 권한도 삭제되었다. 이렇게 REVOKE 명령을 실행할 때 CASCADE를 지정하면, 제거하려는 권한이 다른 역할에 추가된 경우에도 연쇄 적으로 제거할 수 있다.

여기까지 REVOKE 명령을 사용하여 권한을 삭제하는 방법에 대해 알아 보았다.

1.4.8.8 - PostgreSQL | 역할(사용자) ROLE 생성 | 지정한 역할이 소유하는 객체의 소유권을 다른 역할로 변경 (REASSIGN OWNED)

REASSIGN OWNED 명령을 사용하여 지정한 역할이 소유하는 데이터베이스 객체의 소유권을 다른 역할로 변경하는 방법에 대해 설명한다. 역할을 삭제하기 전에 다른 역할에 소유권을 변경하는 경우 등에 사용된다.

객체의 소유권을 다른 역할로 변경

REASSIGN OWNED 명령을 사용하면, 지정한 역할이 데이터베이스에 소유하고 있는 모든 데이터베이스 객체를 다른 역할에 소유권을 변경할수 있다. 형식을 다음과 같다.

REASSIGN OWNED BY old_role [, ...] TO new_role

지정한 역할(old_role)이 소유하는 개체의 소유권을 다른 열할(new_role)로 변경한다.

직접 해보도록 하자. 현재 devkuma 데이터베이스에는 mykuma 역할 소유자 mykuma 스키마가 작성되어 있다.

devkuma=# \dn
 스키마(schema) 목록
   이름   |  소유주
----------+----------
 mykuma   | mykuma
 myschema | postgres
 public   | postgres
(3개 행)


devkuma=#

그리고 public 스키마에는 book 테이블이 생성되어 있다.

devkuma=# \dt
      릴레이션(relation) 목록
 스키마 | 이름 |  종류  |  소유주
--------+------+--------+----------
 public | blog | 테이블 | postgres
 public | book | 테이블 | mykuma
 public | memo | 테이블 | postgres
(3개 행)


devkuma=#

public 스키마 중의 blog 테이블에 mykuma 역할에 SELECT 권한이 추가되어 있다.

devkuma=# \dp
                                액세스 권한
 스키마 | 이름 |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
--------+------+--------+---------------------------+---------------+------
 public | blog | 테이블 | postgres=arwdDxt/postgres+|               |
        |      |        | mykuma=r/postgres         |               |
 public | book | 테이블 |                           |               |
 public | memo | 테이블 | postgres=arwdDxt/postgres |               |
(3개 행)


devkuma=#

그럼 devkuma 데이터베이스에서 mykuma 역할이 소유하는 객체의 소유권을 superkuma 역할로 변경하려고 한다. 다음과 같이 실행해 보자.

devkuma=# reassign owned by mykuma to superkuma;
REASSIGN OWNED
devkuma=#

개체의 소유권이 superkuma 역할로 변경되었습니다.

그러면 mykuma 스키마의 소유권을 확인하여 보자.

devkuma=# \dn
 스키마(schema) 목록
   이름   |  소유주
----------+-----------
 mykuma   | superkuma
 myschema | postgres
 public   | postgres
(3개 행)


devkuma=#

mykuma 스키마의 소유권이 superkuma 역할로 변경되는 것을 확인할 수 있다.

다음은 public 스키마에 생성된 book 테이블의 소유권을 확인해합니다.

devkuma=# \dt
      릴레이션(relation) 목록
 스키마 | 이름 |  종류  |  소유주
--------+------+--------+-----------
 public | blog | 테이블 | postgres
 public | book | 테이블 | superkuma
 public | memo | 테이블 | postgres
(3개 행)


devkuma=#

book 테이블의 소유권이 superkuma 역할로 변경되는 것을 확인할 수 있다.

마지막으로 public 스키마 중의 blog 테이블에 mykuma 역할에 추가된 권한을 확인하려고 한다.

devkuma=# \dp
                                액세스 권한
 스키마 | 이름 |  종류  |        액세스 권한        | 칼럼 접근권한 | 정책
--------+------+--------+---------------------------+---------------+------
 public | blog | 테이블 | postgres=arwdDxt/postgres+|               |
        |      |        | mykuma=r/postgres         |               |
 public | book | 테이블 |                           |               |
 public | memo | 테이블 | postgres=arwdDxt/postgres |               |
(3개 행)


devkuma=#

mykuma 롤에 부여된 권한은 그대로 이다. REASSIGN OWNED 명령은 권한까지 변경되지 않는다.

REASSIGN OWNED 명령을 사용하여 지정된 역할이 소유하고있는 데이터베이스 개체의 소유권을 다른 역할로 변경하는 방법에 대해 알아보았다.

1.4.9 - PostgreSQL | 터미널 명령어 모음

DB 초기화

initdb --encoding=UTF-8 --no-locale --username={user name} --pgdata={database directory}

DB 생성

createdb --encoding=UTF-8 --username=root --owner=root --port=5432 {database name}

DB 시작

pg_ctl start --pgdata={database directory} --log={log file}

pg_ctl이 루트로 실행되지 않도록합니다. 구성 파일을 사용하기 때문에 postgres가 소유하고 루트 권한으로 “pg_ctl start"를 수행하면 특권이 부여 될 수 있다.
iDEFENSE가 지적한 에스컬레이션 공격. 물론 포스트 마스터는 실패하지만 익숙하지 않은 시스템 관리자를 보호하기 위해 좀 더 빨리 실패해야 한다.
Postgres와 함께. 선택한 수정은 모든 경우에 pg_ctl의 루트 사용을 사용하지 않도록 설정하는 것이다.

DB 종료

pg_ctl stop --pgdata={database directory} -m fast

DB 접속

psql --host=127.0.0.1 --port=5432 --username={user name} --dbname={database name} --password

테이블 목록 확인

\dt

나가기

\q

1.4.9.1 - PostgreSQL | 터미널 명령어 모음 | 초기화(initdb)

도움말 표시

$ initdb --help
initdb PostgreSQL 데이터베이스 클러스터를 초기화 하는 프로그램.

사용법:
  initdb [옵션]... [DATADIR]

옵션들:
  -A, --auth=METHOD         로컬 연결의 기본 인증 방법
      --auth-host=METHOD    local TCP/IP 연결에 대한 기본 인증 방법
      --auth-local=METHOD   local-socket 연결에 대한 기본 인증 방법
 [-D, --pgdata=]DATADIR     새 데이터베이스 클러스터를 만들 디렉터리
  -E, --encoding=ENCODING   새 데이터베이스의 기본 인코딩
      --locale=LOCALE       새 데이터베이스의 기본 로캘 설정
      --lc-collate=, --lc-ctype=, --lc-messages=LOCALE
      --lc-monetary=, --lc-numeric=, --lc-time=LOCALE
                            새 데이터베이스의 각 범주에 기본 로캘 설정
                            (환경에서 가져온 기본 값)
      --no-locale           -locale=C와 같음
      --pwfile=FILE         파일에서 새 superuser의 암호 읽기
  -T, --text-search-config=CFG
                            기본 텍스트 검색 구성
  -U, --username=NAME       데이터베이스 superuser 이름
  -W, --pwprompt            새 superuser 암호를 입력 받음
  -X, --waldir=WALDIR       트랜잭션 로그 디렉터리 위치

덜 일반적으로 사용되는 옵션들:
  -d, --debug               디버깅에 필요한 정보들도 함께 출력함
  -k, --data-checksums      자료 페이지 체크섬 사용
  -L DIRECTORY              입력파일들이 있는 디렉터리
  -n, --no-clean            오류가 발생되었을 경우 그대로 둠
  -N, --no-sync             작업 완료 뒤 디스크 동기화 작업을 하지 않음
  -s, --show                내부 설정값들을 보여줌
  -S, --sync-only           데이터 디렉터리만 동기화

기타 옵션:
  -V, --version             버전 정보를 보여주고 마침
  -?, --help                이 도움말을 보여주고 마침

데이터 디렉터리를 지정하지 않으면, PGDATA 환경 변수값을 사용합니다.

오류보고: <pgsql-bugs@postgresql.org>.

명령어 예시

$ initdb --encoding=UTF-8 --no-locale --pgdata=c:/db/database --pwfile=c:/db/pw.txt
이 데이터베이스 시스템에서 만들어지는 파일들은 그 소유주가 "devkuma" id로
지정될 것입니다. 또한 이 사용자는 서버 프로세스의 소유주가 됩니다.

데이터베이스 클러스터는 "C" 로케일으로 초기화될 것입니다.
기본 텍스트 검색 구성이 "english"(으)로 설정됩니다.

자료 페이지 체크섬 기능 사용 하지 않음

c:/db/database 디렉터리 만드는 중 ...완료
하위 디렉터리 만드는 중 ...완료
max_connections 초기값을 선택하는 중 ...100
기본 shared_buffers를 선택하는 중... 128MB
사용할 동적 공유 메모리 관리방식을 선택하는 중 ... windows
환경설정 파일을 만드는 중 ...완료
부트스트랩 스크립트 실행 중 ... 완료
부트스트랩 다음 초기화 작업 중 ... 완료
자료를 디스크에 동기화 하는 중 ... 완료

경고: 로컬 연결의 인증 방법으로 "trust" 방식을 지정했습니다.
이 값을 바꾸려면, pg_hba.conf 파일을 수정하든지,
다음번 initdb 명령을 사용할 때, -A 옵션 또는 --auth-local,
--auth-host 옵션을 사용해서 인증 방법을 지정할 수 있습니다.

작업완료. 이제 다음 명령을 이용해서 서버를 가동 할 수 있습니다:

    pg_ctl -D c:/db/database -l 로그파일 start

1.4.9.2 - PostgreSQL | 터미널 명령어 모음 | 백업(pg_dump)

도움말 표시

$ pg_dump --help
pg_dump 프로그램은 데이터베이스를 텍스트 파일 또는 기타
다른 형태의 파일로 덤프합니다.

사용법:
  pg_dump [옵션]... [DB이름]

일반 옵션들:
  -f, --file=FILENAME         출력 파일 이름
  -F, --format=c|t|p          출력 파일 형식(사용자 지정, tar, 일반 텍스트)
  -v, --verbose               세부 정보 표시 모드
  -Z, --compress=0-9          압축되는 형식의 압축 수준
  --lock-wait-timeout=TIMEOUT 테이블 잠금에 대한 TIMEOUT을 기다린 후 실패

  --help                      이 도움말을 표시하고 종료
  --version                   버전 정보를 출력하고 종료

출력 내용을 다루는 옵션들:
  -a, --data-only             스키마 빼고 자료만 덤프
  -b, --blobs                 Large Object들도 함께 덤프함
  -c, --clean                 다시 만들기 전에 데이터베이스 개체 지우기(삭제)
  -C, --create                데이터베이스 만드는 명령구문도 포함시킴
  -E, --encoding=인코딩       지정한 인코딩으로 자료를 덤프 함
  -n, --schema=SCHEMA         지정한 SCHEMA들 자료만 덤프
  -N, --exclude-schema=SCHEMA 지정한 SCHEMA들만 빼고 모두 덤프
  -o, --oids                  OID 포함해서 덤프
  -O, --no-owner              일반 텍스트 형식에서
                              개체 소유권 복원 건너뛰기
  -s, --schema-only           자료구조(스키마)만 덤프
  -S, --superuser=NAME        일반 텍스트 형식에서 사용할 superuser 사용자 이름
  -t, --table=TABLE           지정한 이름의 테이블들만 덤프
  -T, --exclude-table=TABLE   지정한 테이블들만 빼고 덤프
  -x, --no-privileges         액세스 권한 (grant/revoke) 정보는 덤프 안 함
  --binary-upgrade            업그레이드 유틸리티 전용
  --inserts                   COPY가 아니라 INSERT 명령으로 데이터 덤프
  --column-inserts            열 이름과 함께 INSERT 명령으로 데이터 덤프
  --disable-dollar-quoting    $ 인용 구문 사용안함 , SQL 표준 따옴표 사용
  --disable-triggers          자료만 복원할 때 트리거 사용을 안함
  --no-tablespaces            테이블스페이스 할당을 덤프하지 않음
  --role=ROLENAME             덤프 전에 SET ROLE 수행
  --use-set-session-authorization
                              SET SESSION AUTHORIZATION 명령을 ALTER OWNER 명령
                              대신 사용하여 소유권 설정

연결 옵션들:
  -h, --host=HOSTNAME      접속할 데이터베이스 서버 또는 소켓 디렉터리
  -p, --port=PORT          데이터베이스 서버의 포트 번호
  -U, --username=NAME      연결할 데이터베이스 사용자
  -w, --no-password        암호 프롬프트 표시 안 함
  -W, --password           암호 입력 프롬프트 보임(자동으로 처리함)

데이터베이스 이름을 지정하지 않았다면, PGDATABASE 환경변수값을
사용합니다.

백업 덤프 예제

./pg_dump --port=5432 --username=root --schema=public --file=devkuma.dump --no-owner devkuma

복원 예제

./psql --port=5432 --username=admin --dbname=devkuma --no-password -f devkuma.dump

참조

https://www.postgresql.org/docs/10/static/app-pgdump.html

1.4.10 - PostgreSQL | ALTER 제약조건 및 테이블 이름 변경

테이블명 변경

문법

ALTER TABLE {테이블명} RENAME TO {새 테이블명};

예제

ALTER TABLE test RENAME TO test_bak;

제약조건 변경

문법

ALTER INDEX {제약조건명} RENAME TO {새 제약조건명};

예제

ALTER INDEX pk_test RENAME TO pk_test_bak;

컬럼 추가

ALTER TABLE books ADD publication date;

컬럼명 변경

ALTER TABLE books RENAME COLUMN in_stock TO is_in_stock;

컬럼 삭제

ALTER TABLE books DROP COLUMN IF EXISTS publication;

컬럼 default값 추가

ALTER TABLE books ALTER COLUMN id SET DEFAULT nextval('books_idx');

컬럼 default값 제거

ALTER TABLE books ALTER id DROP DEFAULT;

컬럼 NOT NULL 세팅

ALTER TABLE books ALTER COLUMN id SET NOT NULL;

컬럼 NOT NULL 제거

ALTER TABLE books ALTER COLUMN id DROP NOT NULL;

컬럼 데이터 타입 변경

ALTER TABLE books ALTER COLUMN publication TYPE text;

constraint 추가

ALTER TABLE editions ADD CONSTRAINT foreign_book FOREIGN KEY (book_id) REFERENCES books (id);
ALTER TABLE editions ADD CONSTRAINT hard_or_paper_back CHECK (type='p' OR type='h');

constraint 변경 (변경은 없고 DROP -> ADD 해야 함)

ALTER TABLE editions DROP CONSTRAINT editions_type_check;
ALTER TABLE editions ADD CONSTRAINT editions_type_check CHECK (type=ANY);

테이블 소유자 변경

ALTER TABLE employees OWNER TO corwin;

테이블 재구축

테이블 재구축

방법1)

CREATE TABLE new_books (id, title, author_id, subject_id) AS SELECT id, title, author_id, subject_id FROM books;  
ALTER TABLE books RENAME TO old_books;  
ALTER TABLE books RENAME TO books;  
DROP TABLE old_books;

방법2)

CREATE TABLE new_books (id integer UNIQUE, title text NOT NULL, author_id integer, subject_id integer, CONSTRAINT books_id_pkey PRIMARY KEY);  
INSERT INTO new_books SELECT id, title, author_id, subject_id FROM books;  
ALTER TABLE books RENAME TO old_books;  
ALTER TABLE new_books RENAME TO books;  
DROP TABLE old_books;  

1.4.11 - PostgreSQL | Sequence 활용하기

시퀀스 생성

CREATE SEQUENCE [시퀀스명];

시퀀스명 변경

ALTER SEQUENCE [기존 시퀀스명] rename to [새 시퀀스명];

시퀀스 목록

SELECT c.relname FROM pg_class c WHERE c.relkind = 'S';

시퀀스 삭제

--  seq_foo를 삭제한다.
DROP SEQUENCE sequence seq_foo;

시퀀스 초기화

drop table tbl_ttt_seq;
drop sequence seq_ttt;

시퀀스 현재값 변경

SELECT setval('시퀀스명', 원하는값);

시퀀스 확인

SELECT * FROM pg_class where relkind = 'S';

테이블 생성

CREATE TABLE tbl_ttt_seq 
(
    seq int not null default nextval('seq_ttt')
,   a varchar(10)
);

– 테이블 삭제시 자동 시퀀스 삭제 처리 – 이거 안하면 테이블 삭제 후에도 존재

ALTER SEQUENCE seq_ttt OWNED BY tbl_ttt_seq.seq; 

데이터 입력

insert into tbl_ttt_seq (a) values ('aaa');
insert into tbl_ttt_seq (a) values ('bbb');
insert into tbl_ttt_seq (a) values ('ccc');
insert into tbl_ttt_seq (a) values ('ddd');
insert into tbl_ttt_seq (a) values ('eee');

select * from tbl_ttt_seq;

시퀀스 현재값 확인

select currval('seq_ttt'), nextval('seq_ttt');

시퀀스 변경

select setval('seq_ttt', 55);

시퀀스 의존성 체크

SELECT p.relname, a.adsrc FROM pg_class p JOIN pg_attrdef a ON (p.relfilenode = a.adrelid) WHERE a.adsrc ~ ‘seq_ttt’;

사용중인 시퀀스 삭제

alter table tbl_ttt_seq alter column seq set default null;

drop sequence seq_ttt;

출처: http://dbrang.tistory.com/784 [dBRang]

1.4.12 - PostgreSQL | ROWNUM 사용하기 row_number()

오라클(Oracle)에서 사용하는 ROWNUM을 postgreSQL에서 사용하는 방법이다

SELECT (row_numver() over()) AS rownum
     , id
     , title
  FROM board
rownum id title
1 10 title11
2 21 title22
3 20 title44
4 30 title55
5 13 title66

1.4.13 - PostgreSQL | 트리거(Trigger) 사용 예제

1. 더미 테이블 생성

CREATE TABLE tb_account(uid INT, id TEXT, pw TEXT, register date);

2. UpdateRegister 함수 선언

CREATE FUNCTION UpdateRegister() RETURNS OPAQUE AS
$$
DECLARE
BEGIN
    UPDATE tb_account SET register = now() WHERE uid = new.uid;
    RETURN NULL;
END
$$ LANGUAGE 'plpgsql';

3. UpdateRegisterTrigger 트리거 선언

CREATE TRIGGER UpdateRegisterTrigger
AFTER INSERT on tb_account
FOR EACH ROW EXECUTE PROCEDURE UpdateRegister();

4. 트리거(Trigger) 사용

INSERT INTO tb_account(uid, id, pw) VALUES (1, 'james', '007');
INSERT INTO tb_account(uid, id, pw) VALUES (2, 'tomas', '008');
INSERT INTO tb_account(uid, id, pw) VALUES (3, 'suzan', '008');

5. 결과확인

SELECT * FROM tb_account;

1.4.14 - PostgreSQL | 함수(Function)

함수 목록 조회

SELECT p.proname
  FROM pg_catalog.pg_namespace n
       JOIN pg_catalog.pg_proc p ON p.pronamespace = n.oid
 WHERE n.nspname = 'public'

함수 삭제

DROP FUNCTION {함수명}

참조

1.4.15 - PostgreSQL | 외래키(Forein key) 테이블 및 컬럼 목록 조회

조회하고자 하는 테이블이 외래키로 참조하고 있는 테이블 및 컬럼 목록 조회

SELECT CCU.table_name      AS search_table_name
     , CCU.column_name     AS search_column_name
     , KCU.table_name      AS foreign_table_name
     , KCU.column_name     AS foreign_column_name
     , KCU.constraint_name AS foreign_constraint_name
 FROM information_schema.table_constraints TC
      JOIN information_schema.key_column_usage KCU ON TC.constraint_name = KCU.constraint_name
      JOIN information_schema.constraint_column_usage CCU ON CCU.constraint_name = TC.constraint_name
WHERE TC.constraint_type = 'FOREIGN KEY'
  AND CCU.table_name = #{조회하고자 하는 테이블명};

조회하고자 하는 테이블을 외래키로 참조하고 있는 테이블 및 컬럼 목록 조회

SELECT TC.table_name   AS search_table_name
     , KCU.column_name AS search_column_name
     , CCU.table_name  AS foreign_table_name
     , CCU.column_name AS foreign_column_name
     , CCU.constraint_name AS foreign_constraint_name
  FROM information_schema.table_constraints TC
       JOIN information_schema.key_column_usage KCU ON KCU.constraint_name = TC.constraint_name
       JOIN information_schema.constraint_column_usage CCU ON CCU.constraint_name = TC.constraint_name
 WHERE TC.constraint_type = 'FOREIGN KEY'
   AND TC.table_name = #{조회하고자 하는 테이블명};

1.5 - SQLite

SQLite

데이터베이스 SQLite 사용법에 관해 설명한다. SQLite 서버로서가 아닌 하나의 응용 프로그램에서 주로 작동한다. 설치도 간단하고 매우 컴팩트(compact)하기에 응용 프로그램과 함께 배포되는 경우가 많다. 여기에서는 SQLite를 사용하여 데이터베이스와 테이블을 만드는 방법, 그리고 데이터를 추가하거나 조회하는 방법을 하나하나 설명한다.

1.5.1 - SQLite | SQLite 설치

SQLite는 데이터베이스 서버를 사용하지 않고 데이터베이스마다 하나의 파일을 사용하여 관리한다. 데이터베이스 서버를 사용하지 않기에 서버로 상시 가동시키는 어플리케이션이 따로 없다. 명령어로 사용하기 위해서는 커멘드 라인(CLI, Command line interface) 프로그램을 따로 사용하고, PHP와 Ruby on Rails 등에서 사용하려면 DLL이 있어야 한다. 여기에서는 SQLite 커멘드 라인 프로그램과 DLL을 다운로드하는 방법 및 설치 방법에 대해 설명한다.

1.5.1.1 - SQLite | SQLite 설치 | SQLite 커맨드 라인 도구 다운로드 및 설치

SQLite는 명령어로 사용하기 위해서는 커맨드 라인(Command line) 프로그램 다운로드 및 설치 방법을 설명한다. 2019년 10월 현재 최신 버전은 SQLite 3.30.1(2019-10-11) 이다.

SQLite 다운로드

브라우저에서 SQLite의 공식 페이지에 접근한다.

sqlite.org

화면 상단의 메뉴에서 “Download"라고 쓰여진 메뉴를 클릭한다.

Sqlite download

해당 OS에 맞게 도구 바이너리(sqlite-tools-xxx와 같은 파일명 형식의 파일)을 클릭하여 다운로드하여 임의의 장소에 저장한다.

SQLite 커맨드 라인 프로그램 설치

다운로드한 파일은 압축 파일로 되어 있다. 임의의 디렉토리에 압축을 푼 것만으로 설치가 되었다.

$ ls -al
total 5016
drwxr-xr-x@  5 kimkc  staff      170 10 11 18:32 .
drwxr-xr-x  41 kimkc  staff     1394 10 14 23:41 ..
-rwxr-xr-x@  1 kimkc  staff   691768 10 11 18:31 sqldiff
-rwxr-xr-x@  1 kimkc  staff  1152260 10 11 18:32 sqlite3
-rwxr-xr-x@  1 kimkc  staff   719796 10 11 18:31 sqlite3_analyzer

커멘드 라인 도구인 sqlite3, 두개의 데이터베이스의 차이를 알려주는 sqldiff, 데이터베이스 분석 보고서를 볼 수 있는 sqlite3_analyzer 이렇게 3개의 파일이 포함되어 있다.

1.5.1.2 - SQLite | SQLite 설치 | SQLite 커맨드 라인 도구 테스트

다운로드 한 SQLite 커맨드 라인 도구를 실제로 사용해보고 동작 여부를 확인해 본다.

커맨드 라인 도구로 데이터베이스와 테이블 만들기

먼저 명령 프롬프트를 시작한다. PATH를 따로 설정하지 않았기에 sqlite3 실행 파일이 있는 디렉토리로 이동한다.

데이터베이스를 만들어 보도록 하자. 커맨드 라인 도구를 사용하여 데이터베이스를 만들려면 다음 형식을 사용한다.

sqlite3 데이터베이스명

데이터베이스명을 지정하고 sqlite3 프로그램을 실행하면 이미 데이터베이스가 존재하는 경우는 지정된 이름의 데이터베이스에 연결하고 지정된 이름의 데이터베이스가 존재하지 않는 경우에는 새 데이터베이스를 생성한 다음에 연결한다.

데이터베이스명은 뭐든 상관 없지만 지정된 데이터베이스명으로 파일이 생성된다. 예를 들어 sampledb.sqlite3 라든지 sampledb.db 라든지입니다. 확장자 없이 sampledb라도 괜찮다. 여기에서는 sample.sqlite3로 하겠다.

sqlite3 sample.sqlite3
$ sqlite3 sample.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> 

sample.sqlite3 라는 데이터베이스가 새로 작성되고 데이터베이스에 연결이 되었다. 그러나 실제로 파일이 만들어지는 것은 데이터베이스에 테이블와 같은 것을 작성했을 때이다. SQLite에 연결되어 있는 동안은 sqlite> 프롬프트가 표시되어 있다.

다음은 데이터베이스에서 테이블을 하나 만들려 보자. 다음과 같이 실행하도록 한다.

create table user(id, name);
$ sqlite3 sample.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> create table user(id, name);
sqlite> 

테이블이 생성되었다. 이번에는 데이터베이스를 만들고 데이터베이스에 테이블을 만들어 보았다.

데이터베이스와의 연결을 종료하려면 “.exit"를 입력한다.

.exit
$ sqlite3 sample.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> create table user(id, name);
sqlite> .exit

데이터베이스와의 연결이 끊겼다.

데이터베이스가 저장된 파일

데이터베이스를 생성 한 후 테이블와 같은 것을 생성 했을 때에 데이터베이스 파일에 저장된다. 만들 위치를 따로 지정하지 않았기 때문에 sqlite3 실행 파일이 있는 디렉토리에 데이터베이스 파일이 생성되어 있다.

$ ls
sample.sqlite3   sqldiff          sqlite3          sqlite3_analyzer

sample.sqlite3라는 파일이 새로 생성있는 것을 볼 수 있다. 이 파일은 방금 만든 데이터베이스 sample.sqlite3에 대한 데이터가 저장되어있는 파일이다. 이렇게 SQLite는 데이터베이스마다 파일을 하나 (경우에 따라서는 여러 파일을 사용하는 경우도 있다) 생성하고 관리한다.

1.5.1.3 - SQLite | SQLite 설치 | sqlite3.dll 다운로드

SQLite는 커멘드 라인에서 사용 할 수도 있지만, PHP와 Rails와 같은 다른 응용 프로그램에서 사용되는 경우가 더 많다. 응용 프로그램에서 사용하려면 sqlite3.dll을 이용해야 한다. 여기에서는 sqlite3.dll 파일을 다운로드하는 방법에 대해 설명한다.

sqlite3.dll 다운로드

sqlite3.dll를 다운로드 받기 위해서는 아래 URL로 접근한다.

브라우저에서 SQLite의 공식 페이지에 접근한다.

Sqlite download

Windows 64bit 환경이라면 “Precompiled Binaries for Windows"블록에 sqlite-dll-win64-x64-3300100.zip 라고 적힌 링크를 클릭하여 다운로드하여 임의의 장소에 저장한다.

sqlite3.dll을 PATH가 설정된 폴더에 복사

다운로드하여 저장한 sqlite-dll-win64-x64-3300100.zip 압축 파일을 풀면 sqlite3.dll과 sqlite3.def 두 파일이 나오는데, sqlite3.dll 파일 경로가 설정된 폴더에 저장한다. (예 : C:\Windows\System32에 넣어도 된다)

sqlite3.def sqlite3.dll

사실은 PHP를 설치하면 SQLite를 사용하는데 필요한 DLL도 함께 설치되어 있다. 그러기에 반드시 별도로 다운로드를 해야 하는 것은 아니다.

1.5.1.4 - SQLite | SQLite 설치 | SQLite 문서 참조

SQLite 문서를 참조하는 방법에 대해 설명한다. 인터넷에서 볼 수 있으며 다운로드하여 볼 수도 있다.

온라인 문서를 참조하기

온라인 문서를 참조하기 위해서는 Sqlite 공식 페이지 상단의 메뉴에서 “Documentation"를 클릭하거나 아래 링크로 이동한다.

https://www.sqlite.org/docs.html

sqlite documentation

예를 들면, “Programming Interfaces” 펼쳐서 “SQL Syntax” 메뉴를 클릭한다.

sqlite documentation

SQLite에서 이용 가능한 SQL에 대한 설명을 볼 수 있다.

이 외에도 SQLite에 관한 다양한 문서를 참조할 수 있다.

문서 다운로드하기

문서를 로컬에 다운로드하고 싶은 경우에는 화면 상단의 메뉴에서 “Download"라고 쓰여져 메뉴를 클릭하거나 아래 링크로 이동한다.

https://www.sqlite.org/download.html

sqlite documentation

“Documentation” 블록에 있는 “sqlite-doc-3300100.zip"링크를 클릭하면 문서를 다운로드 받을 수 있다.

다운로드가 완료되면 파일은 압축이 되어 있기에 풀면 아래와 같이 html 파일들이 많이 나올 것이다.

sqlite documentation

여기서 index.html 파일을 찾아서 열어 보자.

sqlite documentation

인터넷에 공식 사이트를 본 페이지와 같은 화면이 표시된다. 화면 상단의 “Documentation"메뉴를 클릭하면 오프라인으로도 문서를 참조할 수 있게 되었다.

1.5.2 - SQLite | SQLite 기초 지식

SQLite를 이용하는데 있어서 알아 두어야 할 기초 지식에 대해 설명한다.

1.5.2.1 - SQLite | SQLite 기초 지식 | 커맨드 라인 도구에서 SQL 및 명령의 입력 방법

SQLite에서는 커맨드 라인 도구를 사용하여 데이터베이스를 작성하거나 데이터 검색 등이 가능하다. 여기에서는 SQLite 커맨드 라인 도구를 사용할 때 어떻게 입력하면 좋을지에 대해 설명한다.

명령어 실행하기

SQLite 커맨드 라인 도구를 실행하려면 명령 프롬프트를 실행 한 후에 sqlite3 를 설치한 디렉토리로 이동하여 다음과 같이 실행한다. 도구가 기동하고 인수에 지정된 데이터베이스에 연결한다.

sqlite3 데이터베이스명

이번에는 이전에 만들었던 sample.sqlite3 데이터베이스에 연결하기 위해 다음과 같이 실행한다. (이전에 만든 파일이 지웠거나 없어도 연결은 된다.)

$ sqlite3 sample.sqlite3 
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> 

커맨드 라인 도구에는 커맨드 라인 도구에 대한 설정을 할 수 있는 SQLite 명령어 등을 실행하거나 임의의 SQL 문을 실행할 수 있다.

SQLite 명령어 .show을 실행해 보자. 명령은 명령어 이외에 필요에 따라 인수를 입력하고 [Enter] 키를 누르면 실행된다.

$ sqlite3 sample.sqlite3 
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .show
        echo: off
         eqp: off
     explain: auto
     headers: off
        mode: list
   nullvalue: ""
      output: stdout
colseparator: "|"
rowseparator: "\n"
       stats: off
       width: 
    filename: sample.sqlite3
sqlite>

SQL 문 실행하기

이번에는 간단한 SQL 문을 실행해 보자. SQL 문이면 마지막에 세미콜론(;)을 입력 할 때까지가 하나의 SQL 문으로 처리되므로 [;]까지 입력 한 후에 [Enter] 키를 누르면 일에서 실행된다.

sqlite> create table user(id, name);
sqlite> insert into user(id, name) values (1, 'devkuma');
sqlite> insert into user(id, name) values (2, 'araikuma');
sqlite> select * from user;
1|devkuma
2|araikuma
sqlite> 

이전에 예전에 만든 테이블 user이 있다면 첫줄은 실행하지 않아도 된다.

장문의 SQL 문을 분할하여 입력하기

SQL 문에서는 하나의 SQL 문이 길어질 수 있다. 모든 문장을 한 번에 입력하려고 하면 입력 어려웠거나 어디까지 입력했는지 이해하기 어려운 경우가 있다.

sqlite> select id as user_id, name as user_name from user where id = 1 and name = 'devkuma' order by name desc;
1|devkuma
sqlite> 

이렇게 긴 경우에는 커맨드 라인 도구는 분할하여 입력 할 수도 있다. 입력 도중에 [Enter] 키를 눌르면 누른다.

sqlite> select id as user_id, name as user_name 
   ...> 

SQL 문은 마지막으로 [;]가 나타날 때까지가 하나의 문장으로 판단된다. [;]가 입력되지 않은 시점에서 [Enter] 키를 눌렀을 경우 아직 입력 도중이라고 판단되어 계속 입력 할 수 있다.

그럼 나머지를 모두 입력해 보자. 방금 처럼 적당한 타이밍에 [Enter] 키를 누른다.

sqlite> select id as user_id, name as user_name 
   ...> from user
   ...> where id = 1 and name = 'devkuma'
   ...> order by name desc
   ...> 

[Enter] 키를 누를 때 공백을 하나두고 입력 할 필요가 없다. 명령어 도중에 [Enter] 키를 누르면 자동으로 이전과 이후의 문장은 다른 단어로 처리된다.

SQL 문의 경우는 [;]가 포함 된 문장을 입력하고 [Enter] 키를 눌렀을 때 문장의 입력이 완료되었다고 판단되어 SQL 문이 실행된다.

sqlite> select id as user_id, name as user_name 
   ...> from user
   ...> where id = 1 and name = 'devkuma'
   ...> order by name desc
   ...> ;
1|devkuma
sqlite>

문장을 한줄에 입력했을 경우와 문장을 분할하여 입력하여 경우 모두 동일한 결과이므로 상황에 따라 사용을 하면 된다.

지금까지 SQLite 커맨드 라인 도구에서 명령이나 SQL 문을 어떻게 입력하는지에 대해서 설명하였다.

1.5.2.2 - SQLite | SQLite 기초 지식 | SQLite에서 정의된 키워드에 대한 주의점

SQLite에는 많은 키워드가 정의되어 있다. 이러한 키워드는 테이블 이름과 컬럼 이름 등에 사용할 수 없지만, 적절한 작성 방법을 하여 사용할 수도 있다. 여기에서는 SQLite에서 정의된 키워드 주의점 및 키워드 목록에 관해 설명한다.

키워드를 식별자로 사용하는 방법

SQLite에는 많은 키워드가 정의되어 있다. 나중에 목록을 나열하겠지만 TABLE 또는 SELECT와 같은 단어은 키워드이다. 키워드는 예약어라고도 한다.

테이블명이나 데이터베이스명은 식별자라고 한다. 식별자는 알파벳이나 숫자 등을 조합하여 지정할 수 있다. 예를 들어 booktable이나 name 가능하다. 단, SQLite의 키워드는 그대로 식별자로 사용할 수 없다. 예를 들면 select를 테이블명으로 사용하려고하면 오류가 발생한다.

create table select(id, name);
sqlite> create table select(id, name);
Error: near "select": syntax error
sqlite>

키워드를 테이블명이나 데이터베이스명 등에는 사용하는 것은 가급적 피하는 것이 좋겠지만, 어떤 이유로 키워드를 식별자로 사용해야 하는 경우에는 다음의 4가지 방법 중에 하나를 사용한다.

'keyword'
"keyword"
[keyword]
`keyword`

따움표(’)로 키워드를 둘러싸면 문자열 값으로 처리된다. 식별자를 작성해야 곳에 따움표으로 묶인 문자열 값을 작성하게 되면 식별자로 처리되므로 아래와 같이 하면 지정할 수 있다.

create table 'select'(id, name);
sqlite> create table 'select'(id, name);
sqlite> 

쌍따움표("), 대괄호 ([]), 억음 부호(`)로 키워드를 둘러싼 경우 식별자로 처리된다. 또한 문자열을 작성해야 곳에 쌍따움표로 둘러싸서 식별자를 작성하면 문자열로 처리된다.

create table "select"(id, name);
sqlite> create table "select"(id, name);
Error: table "select" already exists
sqlite> drop table 'select';
sqlite> create table "select"(id, name);
sqlite> 

여기서 대괄호는 Access 또는 SQL Server에서 사용되는 방식이고 엄음 부호는 MySQL에서 사용되는 방식디다. 이 두 가지 방식은 각각의 데이터베이스와의 호환성을 위해 준비되어 있는 것이므로, 일반적으로 따움표 또는 쌍따움표로 사용하도록 한다.

키워드 목록

SQLite의 키워드로 정의되는 것을 아래에 나열한다. 이것은 다음의 공식 사이트에 게재되어 있다.

SQLite Keywords

ABORT ACTION ADD AFTER ALL
ALTER ANALYZE AND AS ASC
ATTACH AUTOINCREMENT BEFORE BEGIN BETWEEN
BY CASCADE CASE CAST CHECK
COLLATE COLUMN COMMIT CONFLICT CONSTRAINT
CREATE CROSS CURRENT CURRENT_DATE CURRENT_TIME
CURRENT_TIMESTAMP DATABASE DEFAULT DEFERRABLE DEFERRED
DELETE DESC DETACH DISTINCT DO
DROP EACH ELSE END ESCAPE
EXCEPT EXCLUSIVE EXISTS EXPLAIN FAIL
FILTER FOLLOWING FOR FOREIGN FROM
FULL GLOB GROUP HAVING IF
IGNORE IMMEDIATE IN INDEX INDEXED
INITIALLY INNER INSERT INSTEAD INTERSECT
INTO IS ISNULL JOIN KEY
LEFT LIKE LIMIT MATCH NATURAL
NO NOT NOTHING NOTNULL NULL
OF OFFSET ON OR ORDER
OUTER OVER PARTITION PLAN PRAGMA
PRECEDING PRIMARY QUERY RAISE RANGE RECURSIVE
REFERENCES REGEXP REINDEX RELEASE RENAME
REPLACE RESTRICT RIGHT ROLLBACK ROW
ROWS SAVEPOINT SELECT SET TABLE
TEMP TEMPORARY THEN TO TRANSACTION
TRIGGER UNBOUNDED UNION UNIQUE UPDATE
USING VACUUM VALUES VIEW VIRTUAL
WHEN WHERE WINDOW WITH WITHOUT

이 중에 일부 키워드는 특히 따움표나 쌍따움표 등으로 둘러싸지 않아도 식별자로 사용할 수 있는 것도 있다. 단지, 키워드를 그대로 식별자로 사용하면 알아 보기 어렵기 때문에 그다지 바람직한 방법은 아니다.

테이블 등을 생성 때에 알 수 없는 에러가 발생하면 테이블명과 컬럼명이 키워드되어 있는지 확인하여 보길 바란다.

1.5.2.3 - SQLite | SQLite 기초 지식 | SQL 문에 주석 작성

SQL 문에 주석을 구성하는 형식에 관해 설명한다.

주석 작성

SQL 문에서 어떤 의견이나 설명을 남기고 싶은 경우 등 SQLite는 SQL 문에 주석을 추가하려는 경우에는 다음의 두 가지 방법이 있다.

-- 주석

/* 주석 */

주석이 작성되어도 SQL 문을 실행할 때 무시되므로 실행 결과에 영향을 주지 않는다.

-- 주석 형식으로 주석을 작성한 경우 -- 부터 행 마지막까지 작성된 문자열은 주석이 된다. 예를 들어 다음과 같이 작성한다.

select * from user; -- 목록
sqlite> select * from user; -- 목록
1|devkuma
2|araikuma
sqlite>

/* 주석 */와 같은 형식으로 주석을 작성하며 /*에서 */까지 작성된 문자열은 주석이 된다.

select * from /* 주석 */ user;
sqlite> select * from /* 주석 */ user;
1|devkuma
2|araikuma
sqlite>

명령 도구에서 주석을 작성하는 일은 그다지 많이 없겠지만, 여러 SQL 문을 외부 파일에 작성해 두었다가 가져와 SQLite에서 실행하는 경우에 주석를 작성해 둔다면 좋을 것이다.

1.5.3 - SQLite | 데이터베이스 (Database)

SQLite3를 사용하여 데이터베이스를 만드는 방법과 만든 데이터베이스를 삭제하는 방법에 대해 설명한다.

1.5.3.1 - SQLite | 데이터베이스 (Database) | 데이터베이스 생성 및 연결

SQLite 데이터베이스를 새로 만드는 방법, 그리고 생성 된 데이터베이스에 연결하는 방법에 대해 설명한다.

데이터베이스 생성

데이터베이스를 새로 만들려면 명령 프롬프트에서 다음과 같이 입력한다.

sqlite3 데이터베이스명

예를 들면 myfriend.sqlite3라는 데이터베이스를 만들려고 한다. 명령 프롬프트를 시작하고 sqlite 파일이 있는 디렉토리로 이동한 후에 다음과 같이 실행한다.

$ sqlite3 myfriend.sqlite3
$ sqlite3 myfriend.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite>

myfriend.sqlite3 데이터베이스가 존재하지 않으면 새로운 데이터베이스 myfriend.sqlite3가 생성되고 생성된 데이터베이스에 바로 연결된다.

데이터베이스에 연결된 상태되기에 테이블 등을 데이터베이스에서 만들 수 있다. 뭐라도 만들지 않으면 데이터베이스는 생성되지 않기에 여기서는 간단한 테이블을 생성해 보자. 테이블을 만드는 자세한 방법은 다른 페이지에서 하도록 하겠다.

$ sqlite3 myfriend.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> create table customer (id, name);
$

명령 커맨드 도구를 종료하려면 .exit라고 입력한다. 그러면 연결된 데이터베이스에서 연결이 끊인다.

.exit

그럼 실제로 확인해 보자. 먼저 myfriend.sqlite3 데이터베이스에 연결하여 .exit을 입력해 본다.

$ sqlite3 myfriend.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite>.exit
$

그럼 sqlite3가 생성한 데이터베이스 파일 확인해 보자.

$ ls
myfriend.sqlite3 sample.sqlite3   sqldiff          sqlite3          sqlite3_analyzer

데이터베이스명으로 지정한 이름과 동일한 이름의 파일이 생성되어 있다. SQLite에는 이렇게 데이터베이스를 생성하면 같은 이름의 파일이 작성되어 데이터베이스에 포함된 모든 데이터가 이 파일에 저장된다.

또한 sqlite3 파일이 있는 디렉토리로 이외에 다른 곳에 데이터베이스 파일을 설치하고자 한다면 다음과 같이 경로와 함께 지정한다.

sqlite3 경로/데이터베이스명

예를 들어, 절대 경로 /Users/devkuma/sqlite/test 디렉토리에 sample.sqlite3 데이터베이스를 만들려면 다음과 같이 실행한다.

$ sqlite3 /Users/devkuma/sqlite/testsample.sqlite3

데이터베이스명에 대해

데이터베이스명은 원하는 이름을 지정할 수 있다. 앞에서는 myfriend.sqlite3로 지정했지만 어떤 형식이라도 상관 없다.

myfriend
myfriend.db
myfriend.sqlite
myfriend.sqlite3

데이터베이스를 생성하면 SQLite에서는 데이터베이스명과 동일한 이름의 파일을 생성하여 데이터를 저장한다. 예를 들어, 데이터베이스명이 myfriend인 경우, 생성되는 파일명도 myfriend로 확장자가 없는 파일이 만들어지게 되어 나중에 파일을 보게 되면 이것이 무슨 파일인지 알아 보기가 힘들어 진다.

따라서 데이터베이스명을 정할 때 데이터베이스가 저장되는 파일명도 고려하여 지정하는 것이 좋다. 일반적으로 어떤 확장자가 사용되고 있는지는 모르겠지만 여기에서는 .sqlite3 확장자로 통일하겠다.

데이터베이스에 연결

데이터베이스에 대해 다양한 작업을 수행하려면 먼저 데이터베이스에 연결해야 한다. 신규 데이터베이스를 작성한 경우는 생성과 동시에 접속이 이루어 지지만, 이미 생성된 데이터베이스에 연결하려면 명령 프롬프트에서 다음과 같이 입력한다.

sqlite3 데이터베이스명

보면 알 수 있듯이 신규 데이터베이스를 만들 때와 같은 형식이다. 인수에 이미 존재하는 데이터베이스명을 지정하면, 새로운 데이터베이스가 생성되는 것이 아니라 인수에 지정된 데이터베이스에 연결이 된다.

그럼 앞에서 생성한 myfriend.sqlite3 데이터베이스에 연결을 시도해 보자. 명령 프롬프트를 시작하고 sqlite3가 있는 디렉토리로 이동 한 후에 다음과 같이 실행한다.

$ sqlite3 myfriend.sqlite3
$ sqlite3 myfriend.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite>

새로운 데이터베이스가 생성된 것이 아니라 기존의 데이터베이스에 연결되어 있는지 확인하기 위해 기존의 데이터베이스에 만든 테이블을 확인하여 보기 위해 .tables 명령어를 실행한다.

.tables

그럼 실제로 확인해 보자. 먼저 myfriend.sqlite3 데이터베이스에 연결하여 .tables을 입력해 본다.

$ sqlite3 myfriend.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
customer
sqlite>

customer 테이블이 생성되어 있는 것을 확인할 수 있다. 이처럼 새 데이터베이스를 만들 경우와 기존 데이터베이스에 연결하는 경우에 동일한 명령어와 동일한 형식으로되어 있으므로 주의하길 바란다.

1.5.3.2 - SQLite | 데이터베이스 (Database) | 연결된 데이터베이스 확인

SQLite에서 현재 연결된 데이터베이스 및 데이터베이스가 저장되는 파일명에 대한 정보를 조회하는 방법에 대해 설명한다.

연결된 데이터베이스에 대한 정보

데이터베이스에 연결되어 있을 때에 현재 연결된 데이터베이스명과 데이터베이스가 저장되는 파일명을 조회하려면 SQLite 명령 .databases을 사용한다.

.databases

그럼 실제로 확인해 보자. 먼저 myfriend.sqlite3 데이터베이스에 연결하여 .databases을 입력해본다.

$ sqlite3 myfriend.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .databases
main: /Users/devkuma/sqlite/myfriend.sqlite3
sqlite> 

다음과 같이 화면에 표시되었다.

main이 데이터베이스명이다. 그리고 /Users/devkuma/sqlite/myfriend.sqlite3이 데이터베이스가 저장되는 파일명이다.

데이터베이스명이라고 하면 헤갈리겠지만, main은 테이블명과 컬럼명과 같이 SQL 문에서 데이터베이스명을 지정해야 하는 경우에 사용되는 이름이다. “sqlite3 데이터베이스명"으로 연결된 데이터베이스에 대해 자동으로 main이라는 데이터베이스명이 지정된다. 반대로 데이터베이스명이 main이 연결되어 있는 데이터베이스이다.

이 “main"라는 이름은 지금 신경 쓰이겠지만 일단 지금은 무시해도 된다. 상세한 설명은 여기하지는 않지만, 연결된 데이터베이스가 있는 경우 .databases 명령을 실행시 목록에 동일하게 표시된다. 표시되는 데이터베이스명은 연결할 때 지정된 이름이 된다.

$ sqlite3 myfriend.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> 
sqlite> attach database 'myaddress.sqlite3' as address;
sqlite> 
main: /Users/devkuma/sqlite/myfriend.sqlite3
address: /Users/devkuma/sqlite/myaddress.sqlite3
sqlite> 

이렇게 .databases 명령을 사용하여 현재 연결된 데이터베이스에 대한 정보를 확인할 수 있다.

1.5.3.3 - SQLite | 데이터베이스 (Database) | 데이터베이스 백업 및 삭제

SQLite에서 만든 데이터베이스 파일을 복사하는 방법으로 백업하는 방법, 그리고 데이터베이스를 삭제하는 방법에 대해 설명한다.

데이터베이스 백업

SQLite에서 데이터베이스마다 하나의 파일을 만들고 모든 정보는 파일에 저장된다. 그러므로 특정 데이터베이스를 백업하려면 그 데이터베이스에서 사용하는 파일을 단순히 복사만 할 수 있다.

그럼 실제로 확인해 보자. 현재 myfriend.sqlite3 데이터베이스가 생성되어 있고, 이 데이터베이스에 연결하도록 한다.

$ sqlite3 myfriend.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite>

.tables 명령을 실행하여 데이터베이스에 작성되는 테이블을 확인하면 customer이라는 테이블이 작성되어 있다.

$ sqlite3 myfriend.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
customer
sqlite>

그러면 데이터베이스와의 연결을 끊어보자. 그리고 myfriend.sqlite3 파일을 복사하여 다른 이름 myfriendbackup.sqlite3로 저장한다. 파일 이름은 뭐든지 상관 없다.

$ cp myfriend.sqlite3 myfriendbackup.sqlite3 

이것으로 기존 데이터베이스와 동일한 데이터베이스가 생성되었다. 그럼 백업으로 만들어진 myfriendbackup.sqlite3 데이터베이스에 연결하여 .tables 명령을 실행하여 데이터베이스에 작성되는 테이블 목록을 표시해 보자.

$ sqlite3 myfriendbackup.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
customer
sqlite>

기존 데이터베이스와 같은 테이블이 생성되어 있는 것을 확인할 수 있다. 간단히 확인 했을뿐지만, 데이터베이스가 복제되는 것을 알 수 있을 것이다. 이렇게 SQLite에서 데이터베이스의 데이터가 포함된 파일을 복사하는 것만으로 백업 할 수 있다.

이 방법뿐 아니라 백업을 수행하는 방법으로는 .backup 명령과 .restore 명령을 사용하는 “백업 및 복구"도 준비되어 있다.

데이터베이스 삭제

SQLite에서 데이터베이스마다 하나의 파일에서 독립적으로 관리하고 있기 때문에 데이터베이스를 삭제하려면 데이터베이스를 저장하고 있는 파일을 삭제한다. SQLite에 뭔가 따로 할 것은 없다.

1.5.3.4 - SQLite | 데이터베이스 (Database) | 빈공간의 정리 (VACUUM)

SQLite 데이터베이스에서 파일의 추가 및 삭제를 반복하다 보면 저장되는 데이터의 양에 비해 파일의 크기가 커질 수 있다. 여기에서는 VACUUM 문을 사용하여 사용하지 않는 공간을 확보하는 방법에 대해 설명한다.

VACUUM 사용

SQLite 데이터베이스를 생성하면 파일이 1개가 생성되고 테이블이나 저장된 데이터가 그 파일에 저장된다. 테이블에 데이터를 추가하다 보면 점차 데이터베이스 파일의 크기도 커지지만, 테이블에서 데이터를 삭제해도 바로 데이터베이스 파일의 크기가 줄어들지 않는다. 파일에서 이용되고 있던 영역은 즉시 제거하는 것이 아니라 다음 데이터가 추가될 때 재사용하려고 하기 때문이다.

평상시에는 신경 쓸 필요는 없지만, 파일 사이즈가 걱정이 된다면 VACUUM 문을 실행하여 사용하지 않는 공간을 확보하고 파일 크기를 작게 할 수 있다.

VACUUM 문은 다음과 같이 실행한다.

VACUUM;

VACUUM 문을 실행하면 데이터베이스의 내용을 임시 데이터베이스에 한번 옮겼다가 다시 되돌리는 처리를 하게 된다. 그렇게 하므로써 빈공간을 없애는 동시에 데이터를 순차적으로 저장하여 바로 잡는 작업이 이루어진다.

주의할 점은 VACUUM의 대상이 되는 것은 main 데이터베이스만 해당된다. 연결된 데이터베이스는 VACUUM의 대상이 되지 않는다. 그리고 INTEGER PRIMARY KEY가 설정된 컬럼이 없는 테이블의 경우 저장되어 있는 데이터에 할당된 ROWID는 변경 될 수 있다.

실습

그러면 실습해 보겠다. 다음과 같이 데이터베이스의 파일 크기가 16,384 바이트인 sample.sqlite3 파일이 있다.

$ ls -al
total 5048
drwxr-xr-x@ 7 kimkc  staff      238 10 19 23:20 .
drwxr-xr-x  9 kimkc  staff      306 10 17 23:49 ..
drwxr-xr-x  6 kimkc  staff      204 10 19 23:20 bak
-rw-r--r--  1 kimkc  staff    16384 10 19 23:20 sample.sqlite3
-rwxr-xr-x@ 1 kimkc  staff   691768 10 11 18:31 sqldiff
-rwxr-xr-x@ 1 kimkc  staff  1152260 10 11 18:32 sqlite3
-rwxr-xr-x@ 1 kimkc  staff   719796 10 11 18:31 sqlite3_analyzer

데이터베이스에 연결하여, 데이터베이스에 있는 테이블을 하나 삭제한다.

$ sqlite3 sample.sqlite3 
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .table
select    user      username
sqlite> drop table user;
sqlite> 

테이블을 삭제하고 데이터베이스의 파일 크기를 확인해 보면, 삭제 이전과 변함없이 16,384 바이트로 되어 있다.

$ ls -al
total 5048
drwxr-xr-x@ 7 kimkc  staff      238 10 19 23:24 .
drwxr-xr-x  9 kimkc  staff      306 10 17 23:49 ..
drwxr-xr-x  6 kimkc  staff      204 10 19 23:20 bak
-rw-r--r--  1 kimkc  staff    16384 10 19 23:24 sample.sqlite3
-rwxr-xr-x@ 1 kimkc  staff   691768 10 11 18:31 sqldiff
-rwxr-xr-x@ 1 kimkc  staff  1152260 10 11 18:32 sqlite3
-rwxr-xr-x@ 1 kimkc  staff   719796 10 11 18:31 sqlite3_analyzer

이렇게 테이블이나 데이터를 삭제해도 즉시 데이터베이스의 파일 크기가 작아지는 것은 아니다. 그러면 데이터베이스에 다시 연결하여 VACUUM 문을 실행해 보자.

3 sample.sqlite3 
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> vacuum;
sqlite> 

VACUUM 문을 실행한 후에 데이터베이스의 파일 크기를 확인해 보면 12,288 바이트로 파일 크기가 작아진 것을 확인할 수 있다.

$ ls -al
total 5040
drwxr-xr-x@ 7 kimkc  staff      238 10 19 23:29 .
drwxr-xr-x  9 kimkc  staff      306 10 17 23:49 ..
drwxr-xr-x  6 kimkc  staff      204 10 19 23:20 bak
-rw-r--r--  1 kimkc  staff    12288 10 19 23:29 sample.sqlite3
-rwxr-xr-x@ 1 kimkc  staff   691768 10 11 18:31 sqldiff
-rwxr-xr-x@ 1 kimkc  staff  1152260 10 11 18:32 sqlite3
-rwxr-xr-x@ 1 kimkc  staff   719796 10 11 18:31 sqlite3_analyzer
kimkcui-MacBook-Pro:sqlite-tools-osx-x86-3300100 kimkc$ 

이렇게 VACUUM을 실행하면 사용되지 않는 공간을 확보하고 파일 크기를 줄일 수 있다. 파일 크기가 걱정된다면 VACUUM을 실행하도록 하자.

1.5.4 - SQLite | 데이터 타입 (Data Type)

SQLite3 데이터 타입에 대해 설명한다. SQLite3 데이터 타입은 저장된 값 자체의 데이터 타입과 값을 포함한 컬럼의 데이터 타입은 별개로 생각해야 한다.

1.5.4.1 - SQLite | 데이터 타입 (Data Type) | SQLite에서 사용할 수 있는 데이터 타입

테이블에 값을 저장하기 위한 컬럼을 정의하는데 있어서 어떤 컬럼에 어떤 값을 저장할지에 따라 데이터 타입을 지정할 수 있다. 여기에서 SQLite의 컬럼에 지정 가능한 데이터 타입에 대해 설명한다.

저장되는 값의 데이터 타입

여러 데이터베이스에서는 컬럼별로 데이터 타입을 지정한다. 그로 인해 컬럼마다 저장할 수 있는 값이 정해진다. SQLite3는 테이블을 정의할 때 컬럼마다 데이터 타입을 지정할 필요는 없다. 데이터 타입을 지정하지 않은 컬럼은 다양한 형태의 값이 저장 될 수 있다.

단지 데이터 유형을 지정하지 않은 경우에도 컬럼에 저장된 값이 어떤 데이터 타입인지를 구분하고 NULL, INTEGER, REAL, TEXT, BLOB 이렇게 5가지 데이터 타입으로 분류 된다.

데이터 타입 설명
NULL NULL 값
INTEGER 부호있는 정수. 1, 2, 3, 4, 6, or 8 바이트로 저장
REAL 부동 소수점 숫자. 8 바이트로 저장
TEXT 텍스트. UTF-8, UTF-16BE or UTF-16-LE 중 하나에 저장
BLOB Binary Large OBject. 입력 데이터를 그대로 저장

SQL 문에 작성 된 값의 작성 방법에 따라 그 값의 데이터 유형을 결정된다. 예를 들어 작은 따움표(’)로 둘러싸여 있으면 TEXT 타입으로, 소수점도 지수도 없으면 INTEGER 타입으로, 소수점 또는 지수가 있으면 REAL 타입으로 결정된다.

컬럼 데이터 타입

테이블을 만들 때에 컬럼에 데이터 타입을 지정하는 수 있는데 필수는 아니다.

다음 예에서 컬럼 id를 INTEGER 타입으로 컬럼 name을 TEXT 타입으로 지정하여 테이블을 작성한다.

create table customer (id integer, name text);

SQLite3에서 컬럼에 지정된 유형은 TEXT, NUMERIC, INTEGER, REAL, NONE 이렇게 5가지이다.

TEXT
NUMERIC
INTEGER
REAL
NONE

컬럼 데이터 타입을 지정된 경우에도 다른 데이터 타입의 값을 저장할 수 있는데, 컬럼의 데이터 타입과 값의 데이터 타입의 조합에 따라 값의 데이터 타입이 변환하여 저장한다. 변환는 다음과 같이 된다.

  1. TEXT 타입의 컬럼에 INTEGER 또는 REAL 데이터 타입의 값이 포함 된 경우, TEXT 타입으로 변환 되어 저장된다.

  2. NUMERIC 타입의 컬럼에 TEXT 타입의 값이 포함 된 경우, INTEGER 타입 또는 REAL 타입으로 변환을 해보고 성공하면 데이터 타입으로 저장되지만, 실패하면 TEXT 타입 그대로 저장된다.

  3. INTEGER 타입의 컬럼에 정수로 나타낼 수 있는 REAL 타입의 값(예 : 34.0 등), 또는 같은 타입의 TEXT 타입의 값이 포함된 경우 INTEGER 형으로 변환되어 저장된다.

  4. REAL 타입의 컬럼에 INTEGER 값이 포함 된 경우 REAL 타입으로 변환되어 저장한다.

  5. NONE 타입의 컬럼의 경우는 변환이 처리가 없다.

컬럼에 데이터 타입을 지정하는 방법

SQLite3에서 컬럼에 지정된 타입은 TEXT, NUMERIC, INTEGER, REAL, NONE 이렇게 5 가지 있지만, integer 대신에 int로 지정할 수도 있다. 이는 다른 데이터베이스와의 호환성을 고려한 것이라고 생각된다. 어떤 지정 방법이 가능한지에 대해 알아보자.

데이터 타입이 문자열 INT를 포함한 경우 INTEGER 타입이 된다.

create table customer (id int, name text); -- integer 대신에 int로 작성 

데이터 타입이 문자열 CHAR, CLOB, TEXT 중 하나를 포함하는 경우 TEXT 타입이 된다.

create table customer (id integer, name varchar); -- text 대신에 varchar로 작성

데이터 타입이 문자열 BLOB를 포함한 경우, 또한 데이터 타입이 지정되지 않은 경우는 NONE 타입이 된다.

데이터 타입이 문자열 REAL, FLOA, DOUB 중 하나를 포함하는 경우 REAL 타입이 된다.

위의 어디에도 포함되지 않은 경우는 NUMERIC 타입이 된다.

1.5.4.2 - SQLite | 데이터 타입 (Data Type) | 컬럼에 저장된 값의 데이터 타입 확인

컬럼에 데이터 타입을 지정한 경우와 지정하지 않은 경우로 각각 테이블을 만들고, 거기에 다양한 데이터를 저장하면 어떤 데이터 타입으로 저장되는지에 대해 알아 보겠다. 역시 저장된 값은 결국에 NULL, INTEGER, REAL, TEXT, BLOB 이렇게 5가지 데이터 타입으로 분류된다.

컬럼에 데이터 타입을 지정하지 않은 경우

예로서 데이터베이스를 하나 만들고 다음과 같이 테이블을 데이터베이스에 작성한다. 테이블에는 컬럼이 두 가지이고, 컬럼에 데이터 타입을 지정하지 않는다. 이전에 언급했듯이, 컬럼에 데이터 타입을 지정하지 않은 경우는 NONE 타입이 되고, 컬럼에 저장된 값은 자동 변환되지 않고 그대로 저장된다.

create table test (val1, val2);
$ sqlite3 test.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> 
sqlite> create table test (val1, val2);
sqlite> 

테이블에 데이터 3개를 저장하도록 하겠다.

insert into test values(null, 51);
insert into test values(3.14, 'devkuma');
insert into test values('Good Morning', 1.3e-2);
sqlite> insert into test values(null, 51);
sqlite> insert into test values(3.14, 'devkuma');
sqlite> insert into test values('Good Morning', 1.3e-2);
sqlite> 

저장 한 데이터를 조회해 본다.

select * from test;

보기 쉽게 모드(.mode)와 헤더(.header)의 설정을 변경하고 있다.

sqlite> select * from test;
|51
3.14|devkuma
Good Morning|0.013
sqlite> 
sqlite> .mode column
sqlite> .header on
sqlite> 
sqlite> select * from test;
val1        val2      
----------  ----------
            51        
3.14        devkuma   
Good Morni  0.013     
sqlite> 

이번에는 값의 데이터 타입을 확인해 보려고 한다. 값의 데이터 타입은 typeof 함수를 사용해 확인 할 수 있기에 다음과 같이 값과 그 값의 데이터 타입을 이어서 표시하도록 데이터를 조회해 보자.

select val1, typeof(val1), val2, typeof(val2) from test;
sqlite> select val1, typeof(val1), val2, typeof(val2) from test;
val1        typeof(val1)  val2        typeof(val2)
----------  ------------  ----------  ------------
            null          51          integer     
3.14        real          devkuma     text        
Good Morni  text          0.013       real        
sqlite> 

null은 NULL, 정수는 INTEGER 타입, 문자열은 TEXT 타입, 부동 소수점은 REAL 타입으로 저장되어 있다. 이와 같이 추가된 값에 따라 저장된 값의 데이터 타입이 설정되어 있는 것을 확인할 수 있다.

컬럼에 데이터 타입을 지정하는 경우

앞에서는 컬럼 데이터 타입을 지정하지 않고 테이블을 작성하였는데, 이번에는 다양한 데이터 타입을 컬럼으로 설정했을 때에 값이 어떻게 저장되는지를 확인하려고 한다.

예로서 다음과 같이 테이블을 만든다. 테이블에는 열이 두 가지가 있고 첫 번째 컬럼은 TEXT 타입, 두 번째 컬럼에는 NUMERIC 타입을 설정하고 있다.

create table test2 (val1 text, val2 numeric);
sqlite> create table test2 (val1 text, val2 numeric);
sqlite> 

테이블에 데이터 3개를 저장하도록 하겠다.

insert into test2 values ('Summer', 'Summer');
insert into test2 values (48, 48);
insert into test2 values ('72', '72');
insert into test2 values (39.24, 39.24);
insert into test2 values ('0.17', '0.17');
insert into test2 values (null, null);
sqlite> insert into test2 values('Summer', 'Summer');
sqlite> insert into test2 values(48, 48);
sqlite> insert into test2 values('72', '72');
sqlite> insert into test2 values(39.24, 39.24);
sqlite> insert into test2 values('0.17', '0.17');
sqlite> insert into test2 values(null, null);
sqlite> 

저장된 데이터와 그 데이터 타입을 조회해 보겠다.

select val1, typeof(val1), val2, typeof(val2) from test2;
sqlite> select val1, typeof(val1), val2, typeof(val2) from test2;
val1        typeof(val1)  val2        typeof(val2)
----------  ------------  ----------  ------------
Summer      text          Summer      text        
48          text          48          integer     
72          text          72          integer     
39.24       text          39.24       real        
0.17        text          0.17        real        
            null                      null        
sqlite> 

TEXT 타입으로 설정한 컬럼는 TEXT 타입의 값은 그대로 저장되었지만, INTEGER 타입이나 REAL 타입의 값은 TEXT 타입으로 변환되어 저장되었다. NULL 타입의 값은 변환되지 않고 그대로 저장되었다.

NUMERIC 타입을 설정한 컬럼에 TEXT 형태의 값을 저장하면 INTEGER 타입 또는 REAL 타입으로 변환을 시도하여 성공하면 변환 된 값으로 저장되고, 실패하면 TEXT 타입 그대로 저장된다. NULL 타입의 값은 변환되지 않고 그대로 저장된다.

이번에는 TEXT 타입와 NUMERIC 타입만을 시도해 보았다. 컬럼에 데이터 타입을 설정한 경우에는 경우에 따라서는 값의 데이터 타입이 변환되어 저장되어 있는 것을 확인할 수 있었다.

1.5.4.3 - SQLite | 데이터 타입 (Data Type) | 문자열 이스케이프(escape) 처리

SQLite에서 테이블에 문자열 값을 저장할 때는 작은 따옴표(’)로 묶어서 작성되지만, 문자열에 작은 따옴표가 포함되어 있는 경우에는 이스케이프(escape) 처리가 필요하다. 여기에서는 문자열을 이스케이프 처리하는 방법에 대해 설명한다.

문자열에 이스케이프 처리

SQLite에서 문자열 값을 작성하려면 다음과 같이 값을 작은 따옴표(’)로 묶어 작성한다.

'문자열'

큰 따옴표(")로 묶으면 식별자가 된다. 문자열을 작성해야 하는 곳에 식별자를 작성하게 되면 문자열처럼 취급되기 때문에 큰 따옴표로 묶으면 문자열로 처리되는 것처럼 보이지만, 문자열은 작은 따옴표로 묶어야 한다.

작은 따옴표가 구분 문자로 사용되기 때문에, 문자열에 작은 따움표가 포함 된 경우는 이스케이프 처리가 필요하다.

테스트를 위해 다음과 같이 테이블을 만든다.

create table test (val text);

이전에 같은 이름의 테스트 테이블이 있다면 먼저 삭제하고 생성한다.

sqlite> drop table test;
sqlite> create table test (val text);
sqlite>

다음과 작은 따움표가 포함된 문자열을 저장한다고 해보자.

I'm a student.

이 문자열을 테이블에 저장하기 위해 다음과 같이 실행하면 에러가 발생하지 않지만 SQL 문이 완결되지 않아 입력을 기다리고 있는 상태가 된다. 이는 어디서부터 어디까지가 문자열인지 알 수 없기 때문이다.

insert into test values('I'm a student.');
sqlite> insert into test values('I'm a student.');
   ...> 
   ...> ';
Error: near "m": syntax error
sqlite> 

작은 따움표가 포함된 문자열의 경우는 문자열에 작은 따움표(’) 앞에 작은 따움표(’)를 한번 더 작성하여 이스케이프를 처리한다. 다음과 같이 작성하면 된다.

insert into test values('I''m a student.');
sqlite> 
sqlite> insert into test values('I''m a student.');
sqlite> 

이번에는 무사히 값을 저장되었다. 확인을 위해 테이블 값을 조회해 보자.

select * from test;
sqlite> select * from test;
I'm a student.

저장되는 값이 이스케이프 처리를 하지 않으면 제대로 저장이 되지 않은 것을 확인 하였다. 이렇게 문자열에 작은 따옴표가 포함된 경우는 꼭 이스케이프 처리를 해야 한다는 것을 잊지 말도록 하자.

1.5.5 - SQLite | 테이블 (Table)

SQLite3에서의 테이블에 대한 다양한 조작 방법을 설명한다. 테이블의 생성 및 삭제, 테이블에 대한 다양한 제약 조건을 설정하는 방법을 알아 보겠다.

1.5.5.1 - SQLite | 테이블 (Table) | 테이블 생성

데이터베이스에서 실제 값이 저장되는 곳이 테이블이다. 하나의 데이터베이스에 여러 테이블을 만들 수 있다. 여기에서는 테이블을 만드는 방법을 설명한다.

CREATE 문으로 테이블 생성

SQLite에서 테이블을 만드는 기본 구문은 다음과 같다.

CREATE TABLE 테이블명 (컬럼명1, 컬럼명2, ...);

테이블명은 임의의 이름을 지정할 수 있다. (단, sqlite_으로 시작하는 테이블명은 SQLite 자체가 사용하기에 지정할 수 없다)

테이블에는 적어도 하나의 컬럼이 필요하다. 여러 컬럼이 포함된 테이블은 쉼표(,)로 구분하여 작성한다.

그럼 실제로 테이블을 만들어 보자. 다음은 테이블명이 customer으로, 이 테이블에는 id와 name이라는 두 개의 컬럼이 포함된다.

create table customer (id, name);
sqlite> 
sqlite> create table customer (id, name);
sqlite> 

위와 같이 표시되면 테이블은 생성되었다.

컬럼에 데이터 타입을 지정하여 테이블 생성

SQLite에서 테이블을 만들 때, 컬럼에 데이터 타입을 지정하지 않아도 만들 수 있지만 지정할 수도 있다. (자세한 내용은 SQLite 데이터 타입을 참조한다) 컬럼에 데이터 타입을 지정하여 테이블을 생성하는 구문은 다음과 같다.

CREATE TABLE 테이블명 (컬럼명1 데이터타입, 컬럼명2 데이터타입, ...)

컬럼명 뒤에 공백을 사이에 두고 그 컬럼에 설정하는 데이터 타입을 지정한다.

그럼 실제로 테이블을 만들어 보자. 다음은 테이블명이 customer2이고, 이 테이블에는 id와 name이라는 두 개의 열이 포함되어 id 컬럼의 데이터는 INTERGER 타입, name 컬럼의 데이터 타입은 TEXT 타입이다.

create table customer2 (id integer, name text);
sqlite> 
sqlite> create table customer2 (id integer, name text);
sqlite> 

위와 같이 표시되면 테이블은 생성되었다.

데이터베이스에 작성되는 테이블 목록 확인

데이터베이스에 작성되는 테이블 목록을 확인하려면 SQLite 명령 .tables을 사용한다.

.tables
.tables? TABLE?

인수를 생략하면 생성된 모든 테이블 목록을 표시한다. 인수에 테이블명을 지정하면 지정한 값과 일치하는 테이블 목록을 표시한다.

그럼 목록을 확인해 보자.

.tables
sqlite> .tables
customer   customer2  test       test2    
sqlite> 

이 페이지에서 만든 customer customer2 두 테이블이 표시되었다.

인수를 넣어 테이블명 목록을 조회해 보자.

.tables
.table customer%
sqlite> .tables customer
customer
sqlite> 
sqlite> .table customer%
customer   customer2
sqlite> 

첫 번째는 정확한 테이블명을 인수로 넣었고, 두 번째는 퍼센트(%)를 사용하여 단어를 포함된 테이블을 조회하였다.

1.5.5.2 - SQLite | 테이블 (Table) | 테이블 스키마(구조) 확인

데이터베이스에 생성되는 테이블이 어떤 CREATE 문을 사용되어 만들었는지에 대해 확인하는 방법에 대해 설명한다. sqlite_master 테이블을 이용하는 방법과 .schema 명령을 사용하는 방법의 두 가지가 있다.

sqlite_master 테이블에서 조회

우선 sqlite_master 테이블을 이용하는 방법이다. 다음 같은 SQL 문을 실행한다. (쉽게 확인할 수 있도록 먼저 “.mode” 명령으로 mode를 line으로 변경한다)

select * from sqlite_master;
sqlite> select * from sqlite_master;
table|customer|customer|2|CREATE TABLE customer (id, name)
table|customer2|customer2|3|CREATE TABLE customer2 (id integer, name text)
sqlite> 
sqlite> .mode line
sqlite> 
sqlite> select * from sqlite_master;
    type = table
    name = customer
tbl_name = customer
rootpage = 2
     sql = CREATE TABLE customer (id, name)

    type = table
    name = customer2
tbl_name = customer2
rootpage = 3
     sql = CREATE TABLE customer2 (id integer, name text)
sqlite> 

위와 같이 데이터를 조회할 수 있다.

현재 2개의 테이블이 생성되어 있기에 sqlite_master 테이블에는 2개의 행(row)이 표시가 되고 있다. 각각 행에 name 컬럼은 테이블명, sql 컬럼에는 테이블이 작성되었을 때의 CREATE TABLE 문이 표시되고 있다. 이렇게 sqlite_master 테이블을 조회하여 테이블이 어떻게 생성이 되었는지를 확인 할 수 있다.

특정 테이블의 CREATE TABLE 문을 확인하고 싶은 경우에는 WHERE 절을 사용하여 다음과 같은 SQL 문을 실행한다.

select * from sqlite_master where type='table' and name='customer';
sqlite> select * from sqlite_master where type='table' and name='customer';
    type = table
    name = customer
tbl_name = customer
rootpage = 2
     sql = CREATE TABLE customer (id, name)

지정된 테이블명에 대한 데이터만 조회되었다.

.schema 명령을 사용하여 조회

테이블의 스키마에 대한 정보정보만 조회하고자 한다면 SQLite 명령 .schema으로도 확인할 수 있다.

.schema
.schema? TABLE?

인수를 생략하면 모든 테이블과 인덱스의 스키마 정보를 표시한다. 또한 인수에 테이블명을 지정하면 테이블명과 일치하는 테이블과 지정된 테이블명과 관련된 스키마 정보를 표시 할 수 있다.

그럼 .schema 명령을 사용하여 조회해 보도록 하자.

.schema
sqlite> .schema
CREATE TABLE customer (id, name);
CREATE TABLE customer2 (id integer, name text);
sqlite> 

현재 생데이터베이스에 생성되어 있는 2개의 테이블에 대한 CREATE 문이 표시되는 것을 확인할 수 있다. 이렇게 .schema 명령을 사용하여도 테이블이 어떻게 만들어 졌는지를 확인하였다.

1.5.5.3 - SQLite | 테이블 (Table) | 테이블명 변경과 컬럼 추가 및 삭제

이미 생성된 테이블명을 변경하는 방법, 그리고 테이블에 컬럼을 추가하는 방법 대해 설명한다.

테이블명 변경하기

이미 생성된 테이블명을 변경하려면 ALTER TABLE 문을 사용한다. 형식은 다음과 같다.

ALTER TABLE 테이블명 RENAME TO 새 테이블명;

예를 들어, table_old 테이블명을 table_new으로 변경하려면 다음과 같이 실행한다.

alter table table_old rename to table_new;

그럼 실제로 테이블명을 변경해 보자. SQLite 명령인 .tables로 현재 생성되어 있는 테이블 목록을 확인해 보면 customer와 customer2가 생성되는 것을 볼 수 있다.

sqlite> .table
customer   customer2
sqlite> 

customer2 테이블의 이름을 user에 변경하려고 한다.

alter table customer2 rename to user;
sqlite> 
sqlite> alter table customer2 rename to user;
sqlite> 

다시 .tables 명령을 실행해 보면 생성 된 테이블 목록이 customer와 user되어 있는 것을 확인할 수 있다.

sqlite> .table
customer  user    
sqlite> 

컬럼 추가하기

이미 생성한 테이블에 컬럼을 추가하려면 ALTER TABLE 문을 사용한다. 형식은 다음과 같다.

ALTER TABLE 테이블명 ADD COLUMN 컬럼명 [데이터 타입];

현재 버전에서는 컬럼을 추가 할 수는 있지만 삭제할 수는 없다.

추가된 컬럼은 테이블의 마지막에 추가된다. 또한 컬럼을 추가하려면 다음 조건을 충족해야 한다.

  1. PRIMARY KEY 또는 UNIQUE 제약 조건을 설정할 수 없다.
  2. DEFAULT 제약 조건을 설정할 때는 CURRENT_TIME / CURRENT_DATE / CURRENT_TIMESTAMP는 지정할 수 없다.
  3. NOT NULL 제약 조건을 설정할 때는 NULL이 아닌 기본값 설정이 필요하다.

예를 들어 mytable 테이블에 new_column 컴럼을 추가하려면 다음과 같이 실행한다.

alter table mytable add column new_column;

그럼 실제로 컴럼을 추가해 보자. 기존 user 테이블에 address 컬럼을 추가한다. 데이터 타입은 TEXT 형태이다. 먼저 .schema 명령을 사용하여 현재 테이블의 스키마를 확인한다.

.schema user
sqlite> .schema user
CREATE TABLE IF NOT EXISTS "user" (id integer, name text);
sqlite>

user 테이블에 address 컬럼을 추가한다.

alter table user add column address text;
sqlite> 
sqlite> alter table user add column address text;
sqlite> 

다시 .schema 명령을 실행해 보면 address 컬럼이 추가 된 것을 확인할 수 있다.

.schema user
sqlite> .schema user
CREATE TABLE IF NOT EXISTS "user" (id integer, name text, address text);
sqlite> 

컬럼 삭제하기

이미 생성한 테이블에 컬럼을 삭제을 즉 DROP COLUMN 문을 SQLite에서는 지원하지 않는다. 대신에 공식 사이트에서는 DROP TABLE, ALTER TABLE RENAME 문을 이용하라고 가이드 하고 있다.

아예 테이블 자체를 새로 만들어서 데이터를 옮겨 넣으라는 뜻이다. 귀찮은 작업이긴 하지만 지금으로서는 따로 방법이 없다.

그럼 실제로 해보도록 하겠다. 먼저 .schema 명령을 사용하여 현재 user 테이블의 스키마를 확인한다.

sqlite> .schema user
CREATE TABLE IF NOT EXISTS "user" (id integer, name text, address text);
sqlite> 

새로 user2 테이블을 생성한다. 기존에 user 테이블의 데이터를 새로운 user2 테이블에 넣는다.

sqlite> create table user2 (id integer, name text);
sqlite> 
sqlite> insert into user2 (id, name) select id, name from user;

기존 user 테이블을 삭제하고, 새로운 user2 테이블의 테이블명을 기존 user 테이블명으로 변경한다.

sqlite> drop table user;
sqlite> 
sqlite> alter table user2 rename to user;

다시 .schema 명령을 실행해 보면 address 컬럼이 없는 것을 확인할 수 있다.

sqlite> .schema user
CREATE TABLE IF NOT EXISTS "user" (id integer, name text);

1.5.5.4 - SQLite | 테이블 (Table) | 테이블 삭제

생성된 테이블을 삭제하는 방법에 대해 설명한다.   

생성된 테이블 삭제

생성된 테이블을 삭제하려면 DROP TABLE 문을 사용한다. 형식은 다음과 같다.

DROP TABLE 테이블명;

테이블을 삭제하면 테이블에 연결되어있는 인덱스 및 트리거도 함께 삭제된다.

예를 들어 mytable 테이블을 삭제하려면 다음과 같이 실행한다.

drop table mytable;

테이블의 삭제로 인해 데이터베이스에 생긴 불필요한 공간을 제거하려면 VACUUM 문을 실행해야 한다. VACUUM 문장에 대해서는 빈공간의 정리 (VACUUM)을 참조하도록 한다.

그럼 실제로 테이블을 삭제해 보자. SQLite 명령 .tables에서 현재 생성 된 테이블 목록을 확인해 보면 customer와 user가 생성되어 있는 것을 볼 수 있다.

.tables
sqlite> .table
customer  user    
sqlite>

user 테이블을 삭제한다.

drop table user;
sqlite> drop table user;
sqlite>

user 테이블이 삭제되었는지 .tables 명령을 실행하여 생성된 인덱스를 확인한다.

sqlite> .table
customer    
sqlite> 

user 테이블이 삭제된 것을 확인할 수 있다.

1.5.5.5 - SQLite | 테이블 (Table) | PRIMARY KEY 제약 조건

PRIMARY KEY 제약 조건의 사용법에 대해 설명한다. 컬럼에 PRIMARY KEY 제약 조건을 설정하게 되면 그 컬럼이 기본 키가 된다.

PRIMARY KEY 제약 조건이란?

컬럼에 PRIMARY KEY 제약 조건을 설정하면 그 컬럼이 기본 키(주 키 또는 프라이머리 키)가 된다. 기본 키는 하나 이상의 컬럼의 조합으로 설정되고, 테이블에 하나만 존재한다. 기본 키가 설정된 컬럼에서는 다른 데이터와 중복 된 값을 포함할 수 없다.

컬럼에 PRIMARY KEY 제약 조건을 설정하려면 다음과 같이 작성한다.

CREATE TABLE 테이블명 (컬럼명 PRIMARY KEY, ...);

여러 컬럼의 조합에 대해 PRIMARY KEY 제약 조건을 설정하려면 다음과 같이 작성한다.

CREATE TABLE 테이블명 (컬럼명 1, 컬럼명 2, ...,
  PRIMARY KEY (컬럼명 1, 컬럼명 2, ...));

PRIMARY KEY 제약 조건이 설정된 컬럼에는 중복 된 값을 포함 할 수 없다. 단 SQLite에서 null는 여러 컬럼에 저장이 가능하다. 여러 컬럼을 조합되어 PRIMARY KEY 제약 조건이 설정되어 있는 경우는 여러 컬럼에 저장된 값과 같은 조합의 값을 저장할 수 없다.


그러면 실제로 제약 조건을 설정해 보자. 다음과 같이 테이블을 만든다. id 컬럼에 PRIMARY KEY 제약 조건을 설정한다.

create table user (id int primary key, name text);
sqlite> create table user (id int primary key, name text);
sqlite> 

PRIMARY KEY 제약 조건을 설정하고 id 컬럼에 중복 된 값을 포함 할 수 없다. 이미 다른 데이터에 저장되어 있는 값과 동일한 값의 데이터를 저장하려고 하면 Error : UNIQUE constraint failed : user.id 에러 메세지가 표시된다.

insert into user values (1, 'devkuma');
insert into user values (2, 'araikuma');
insert into user values (1, 'kimkc');
sqlite> insert into user values (1, 'devkuma');
sqlite> insert into user values (2, 'araikuma');
sqlite> insert into user values (1, 'kimkc');
Error: UNIQUE constraint failed: user.id
sqlite>

위와 같이 PRIMARY KEY 제약 조건이 설정된 컬럼에 이미 존재하는 데이터와 동일한 값을 가진 데이터를 추가 할 수 없다.

INTEGER 타입의 컬럼에 PRIMARY KEY 제약 조건을 설정하는 경우

데이터 타입이 INTEGER 컬럼에 PRIMARY KEY 제약 조건을 설정 한 경우, 새로운 데이터를 추가 할 때 해당 컬럼의 값을 생략하면 AUTOINCREMENT 제약을 설정한 경우와 동일하게 자동으로 값이 저장된다.

CREATE TABLE 테이블명 (컬럼명 INTEGER PRIMARY KEY, ...);

컬럼에 저장되는 값은 대상의 컬럼에 현재 저장되어있는 최대 값에 1을 더한 값이다.

주의 사항

컬럼의 데이터 타입은 INT가 포함 된 경우 모든 INTEGER 타입이 되지만, 일련 번호가 자동적으로 부여되는 것은 컬럼에 INTEGER PRIMARY KEY로 작성한 경우일 뿐이다. INT PRIMARY KEY와 같이 작성하면 이런 동작하지 않는다.

실제로 제약 조건을 설정해 보자. 다음과 같이 테이블을 만든다. INTEGER 타입의 id 컬럼에 PRIMARY KEY 제약 조건을 설정한다.

 ``` create table user (id integer primary key, name text);

sqlite> drop table user; sqlite> create table user (id integer primary key, name text); sqlite>


다음은 id 컬럼에 값을 지정하지 않고 데이터를 여러개를 저장해 보도록 한다.

insert into user (name) values (‘devkuma’); insert into user(name) values (‘araikuma’); insert into user(name) values (‘kimkc’);

sqlite> insert into user (name) values (‘devkuma’); sqlite> insert into user(name) values (‘araikuma’); sqlite> insert into user(name) values (‘kimkc’); sqlite> sqlite> select * from user; 1|devkuma 2|araikuma 3|kimkc


값을 생략하지 않고 PRIMARY KEY 제약 조건이 설정된 컬럼에 임의의 숫자를 저장할 수도 있다. 단, PRIMARY KEY 제약 조건이 설정되어 있기 때문에 중복되는 값은 저장할 수 없다.

insert into user values (8, ‘happykuma’);

sqlite> insert into user values (8, ‘happykuma’); sqlite> sqlite> select * from user; 1|devkuma 2|araikuma 3|kimkc 8|happykuma


다음은 PRIMARY KEY 제약 조건이 설정된 컬럼에 값을 지정하지 않고 다시 데이터를 추가하면 PRIMARY KEY 제약 조건이 설정된 컬럼에 저장되는 최대 값이 8이므로 추가되는 값은 9가 된다.

insert into user (name) values (‘mykuma’);

sqlite> insert into user (name) values (‘mykuma’); sqlite> select * from user; 1|devkuma 2|araikuma 3|kimkc 8|happykuma 9|mykuma


---

이 처럼 PRIMARY KEY 제약 조건이 설정된 컬럼에 같은 값을 중복하여 저장할 수 없지만, 데이터를 삭제하면 그 데이터의 컬럼에 저장되어 있던 값은 다른 데이터를 추가 할 때 지정할 수 있게 된다.

예를 들면, 현재 PRIMARY KEY 제약 조건이 설정된 컬럼에 저장되는 값이 9 데이터를 삭제 한 후 새로운 데이터로 PRIMARY KEY 제약 조건이 설정된 컬럼에 9를 사용하여 데이터를 추가 할 수 있다 .

delete from user where id = 9; insert into user values (9, ‘yourkuma’);


위와 같이 PRIMARY KEY 제약 조건이 설정된 컬럼에 중복 값을 포함 할 수 없지만, 일단 삭제 해 버리면 다른 데이터를 추가 할 때 삭제 한 것과 같은 값을 컬럼에 저장할 수 있다.

---

SQLite에서는 테이블 생성시 컬럼에 데이터 타입을 지정된 경우에 다른 데이터 타입으로 저장하더라도 오류가 발생하지 않다. 예를 들어 INTEGER 타입을 설정한 컬럼에 TEXT 타입의 값을 저장 할 수도 있다.

create table numtest1 (id integer);

sqlite> create table numtest1 (id integer); sqlite> sqlite> insert into numtest1 values (‘hello’); sqlite>


이에 반해 컬럼에 데이터 타입으로 INTEGER를 지정하고 PRIMARY KEY 제약 조건을 설정한 경우에는  정수외에 값을 저장할 수 없게 된다.

create table numtest2 (id integer primary key);

insert into numtest2 values (10); insert into numtest2 values ( ‘7’); insert into numtest2 values ( ‘Hello’);

sqlite> create table numtest2 (id integer primary key); sqlite> sqlite> insert into numtest2 values (10); sqlite> insert into numtest2 values ( ‘7’); sqlite> insert into numtest2 values ( ‘Hello’); Error: datatype mismatch sqlite>


INTEGER PRIMARY KEY가 지정되어 있는 컬럼에 숫자 10와 '7' 같이 TEXT 타입인 숫자 값이라면 자동으로 INTEGER 타입으로 자동으로 변환되는 데이터는 추가 할 수 있지만, 'Hello' 같이 숫자로 변형이 되지 않는 TEXT 타입의 값을 저장하려고 하면 "Error : datatype mismatch" 오류가 발생한다.

데이터 타입이 설정된 컬럼에 다양한 데이터 타입의 값을 저장했을 때 어떻게 변환되어 저장되는지에 대해서는 "[SQLite에서 사용할 수 있는 데이터 타입](/books/pages/1271)"을 참조하길 바란다.

1.5.5.6 - SQLite | 테이블 (Table) | ROWID 참조 및 INTEGER PRIMARY KEY와의 관계

SQLite에서 데이터를 추가하게 되면 데이터마다 ROWID 값이 자동으로 할당되어 다른 컬럼의 값과 동일하게 데이터로 저장된다. 여기에서 ROWID의 이용 방법과 INTEGER PRIMARY KEY와의 관계에 대해 설명한다.

ROWID 조회하기

ROWID는 테이블에 존재하는 숨겨진 컬럼이다. 테이블에 데이터를 추가 할 때마다 자동으로 ROWID에 값이 설정된다.

테스트를 위해 다음과 같이 테이블을 만들고 데이터를 2개 추가한다.

create table user (id integer, name text);
insert into user values (3, 'devkuma');
insert into user values (8, 'araikuma');
sqlite> create table user (id integer, name text);
sqlite> 
sqlite> insert into user values (3, 'devkuma');
sqlite> insert into user values (8, 'araikuma');
sqlite> 

테이블 데이터를 select * from user;와 같이 조회하게 되면 ROWID의 값은 표시되지 않지만 ROWID를 명시적으로 지정하여 데이터를 조회하면 일반 컬럼과 같은 데이터를 조회를 할 수 있다.

select *, rowid from user;
sqlite> .mode column
sqlite> .header on
sqlite> 
sqlite> select * from user;
id          name      
----------  ----------
3           devkuma   
8           araikuma  
sqlite> 
sqlite> select *, rowid from user;
id          name        rowid     
----------  ----------  ----------
3           devkuma     1         
8           araikuma    2         
sqlite> 

위와 같이 ROWID의 값이 데이터별로 저장되어있는 것을 확인할 수 있다.

데이터를 새로 추가했을 때, 이미 저장되어 있는 데이터 중 ROWID의 값 중에 가장 큰 값을 찾고, 거기에 1을 더한 값이 새로 추가되는 데이터의 ROWID의 값으로 저장된다.

현재 저장되어 있는 데이터 중에 ROWID 값이 가장 큰 값은 2이므로 다음 데이터를 추가하면 데이터의 ROWID의 값은 3이다.

insert into user values (5, 'kimkc');
sqlite> insert into user values (5, 'kimkc');
sqlite> 
sqlite> select *, rowid from user;
id          name        rowid     
----------  ----------  ----------
3           devkuma     1         
8           araikuma    2         
5           kimkc       3 

ROWID는 WHERE 절을 조건식으로 사용할 수도 있다.

select *, rowid from user where rowid = 2;
sqlite> select *, rowid from user where rowid = 2;
id          name        rowid     
----------  ----------  ----------
8           araikuma    2         
sqlite> 

또한 ROWID는 다른 데이터베이스와의 호환성을 유지하기 위해 별칭으로 OID_ROWID_도 지원한다. 어느 것을 사용하여도 ROWID의 값을 반환된다. 예를 들면 ROWID 대신 _ROWID_를 사용하여도 된다.

select *, rowid, oid, _rowid_ from user;
sqlite> select *, rowid, oid, _rowid_ from user;
id          name        rowid       rowid       rowid     
----------  ----------  ----------  ----------  ----------
3           devkuma     1           1           1         
8           araikuma    2           2           2         
5           kimkc       3           3           3         
sqlite> 

ROWID 값 설정하기

ROWID의 값은 데이터를 추가 할 때 자동으로 설정되기 때문에 일반적으로 신경쓰지 않아도 되지만, 임의의 값을 지정할 수도 있다. 앞에서 만든 테이블에 다음과 같이 새로운 데이터를 추가 할 수도 있다.

insert into user (id, name, ROWID) values (10, 'happykuma', 8);
sqlite> insert into user (id, name, ROWID) values (10, 'happykuma', 8);
sqlite> 

데이터를 조회해보면, 지정된 값이 ROWID에 저장되어있는 것을 확인할 수 있다.

select *, rowid from user;
sqlite> select *, rowid from user;
id          name        rowid     
----------  ----------  ----------
3           devkuma     1         
8           araikuma    2         
5           kimkc       3         
10          happykuma   8         
sqlite> 

주의할 점은 ROWID는 중복 된 값을 가질 수 없다. 이미 다른 데이터에 설정되어있는 값으로 ROWID로 설정하려고 하면 “Error : UNIQUE constraint failed : user.rowid"라는 오류 메시지가 표시된다.

insert into user (id, name, ROWID) values (9, 'mykuma', 3);
sqlite> insert into user (id, name, ROWID) values (9, 'mykuma', 3);
Error: UNIQUE constraint failed: user.rowid
sqlite> 

ROWID 참조 및 INTEGER PRIMARY KEY와의 관계

여기까지의 ROWID의 특징에 대해 알아보았는데, ROWID은 데이터 타입을 INTEGER에 PRIMARY KEY 제약 조건이 설정한 컬럼과 동일한 동작하는 것으로 보인다.

실은 컬럼에 INTEGER PRIMARY KEY를 설정하게 되면 그 컬럼은 ROWID와 같은 값을 참조한다. INTEGER PRIMARY KEY가 설정된 컬럼에 값을 지정하면 ROWID도 같은 값이 되어, ROWID 값을 지정하면 INTEGER PRIMARY KEY가 설정된 컬럼도 같은 값이 된다. 다른 값을 지정하여 데이터를 추가한 경우와 나중에 지정한 값이 모두 포함된다.

테스트를 위해 다음과 같이 테이블을 만들고 데이터를 3개 추가해 보자.

create table user (id integer primary key, name text);
insert into user values (1, 'devkuma');
insert into user values (6, 'kimkc');
insert into user values (3, 'ariakuma');
sqlite> drop table user;
sqlite> 
sqlite> create table user (id integer primary key, name text);
sqlite> 
sqlite> insert into user values (1, 'devkuma');
sqlite> insert into user values (6, 'kimkc');
sqlite> insert into user values (3, 'ariakuma');
sqlite> 

테이블에서 ROWID를 포함하여 데이터를 조회한다.

select *, rowid from user;
sqlite> select *, rowid from user;
id          name        id        
----------  ----------  ----------
1           devkuma     1         
3           ariakuma    3         
6           kimkc       6         
sqlite> 

조회한 ROWID를 보면 INTEGER PRIMARY KEY가 설정된 컬럼명 데이터가 표시되어 있다. 정확히 알 수 없기 때문에 추측되어 버립니다 만, INTEGER PRIMARY KEY가 설정된 열이있는 경우에는 ROWID로 그 컬럼이 사용되는지도 모든다.

분명 ROWID를 조회하였지만, 조회한 데이터를 보면 INTEGER PRIMARY KEY가 설정된 컬럼명으로 데이터가 표시되어 있다. 정확히 알 수 없지만 추측해 본다면, INTEGER PRIMARY KEY가 설정된 컬럼이 있는 경우에는 ROWID가 그 컬럼으로 사용되는거 같다.

1.5.5.7 - SQLite | 테이블 (Table) | AUTOINCREMENT을 설정했을 때 값의 할당 규칙

컬럼에 INTEGER PRIMARY KEY와 AUTOINCREMENT를 같이 설정되면 어떻게 자동으로 값이 설정되는 되는지에 대해 설명한다. 그리고 현재까지 할당된 최대 값을 확인하는 방법을 함께 알아 본다.

AUTOINCREMENT을 설정했을 때 값의 할당 규칙

컬럼에 INTEGER PRIMARY KEY를 설정되면 데이터를 추가될 때마다 컬럼 값을 따로 지정하지 않으면 자동으로 값이 할당되어 저장된다. 자동으로 저장되는 값은 대상의 컬럼에 저장된 최대 값에 1을 더한 값이다. 이 값이 이전에 할당된 적이 있었는지와는 관계 없이 데이터의 추가와 삭제를 반복되면 이전에 저장된 적이 있는 값이 다시 컬럼에 저장되기도 한다.

그 반해 컬럼에 INTEGER PRIMARY KEY와 AUTOINCREMENT를 같이 설정하면 자동으로 설정되는 값의 규칙이 달라진다. 대상의 컬럼에 현재 저장되어 있는 최대 값에 1이 추가되는 것이 아니라 해당 컬럼에 현재까지 할당된 최대 값에 1이 더해진 값이 저장되게 된다.

INTEGER PRIMARY KEY에 AUTOINCREMENT를 같이 설정하려면 다음과 같이 작성한다.

CREATE TABLE 테이블명 (컬럼명 INTEGER PRIMARY KEY AUTOINCREMENT ...);

데이터가 남아 있는지 여부는 관계가 없기 때문에, 자동으로 설정되는 값은 항상 현재까지의 할당된 값의 최대 값이다. 현재까지 저장되었던 적이 있는 값이 자동으로 저장되는 것은 아니다.

그럼 직접 해보도록 하겠다. 우선 AUTOINCREMENT을 지정하지 않으면 어떻게 되는지 확인해 본다. 다음과 같이 테이블을 만든다.

create table user (id integer primary key, name text);
sqlite> 
sqlite> create table user (id integer primary key, name text);
sqlite> 

3개의 데이터를 추가한다. INTEGER PRIMARY KEY를 설정한 컬럼에는 값을 지정하지 않았기에 값이 자동으로 저장된다.

insert into user (name) values ('devkuma');
insert into user (name) values ('kimkc');
insert into user (name) values ('araikuma');
sqlite> 
sqlite> insert into user (name) values ('devkuma');
sqlite> insert into user (name) values ('kimkc');
sqlite> insert into user (name) values ('araikuma');
sqlite> 
sqlite> .mode column
sqlite> .header on
sqlite> 
sqlite> select * from user;
id          name      
----------  ----------
1           devkuma   
2           kimkc     
3           araikuma  
sqlite> 

ID 컬럼에서 가장 큰 값이 들어있는 데이터(ID 컬럼의 값이 3 데이터)를 삭제한다.

delete from user where id = 3;
sqlite> 
sqlite> delete from user where id = 3;
sqlite> 
sqlite> select * from user;
id          name      
----------  ----------
1           devkuma   
2           kimkc     
sqlite> 

그러고 다시 데이터를 추가해 본다. 이때 INTEGER PRIMARY KEY를 설정한 컬럼에 값을 지정하지 않으면 대상 컬럼에 저장되는 최대 값(현재는 2)에 1을 더한 값이 자동으로 설정되어 컬럼에 자동으로 설정되는 값은 3이다.

insert into user (name) values ('happykuma');
sqlite> insert into user (name) values ('happykuma');
sqlite> 
sqlite> select * from user;
id          name      
----------  ----------
1           devkuma   
2           kimkc     
3           happykuma 
sqlite> 

위와 같이 이전에 한 번 다른 데이터에 할당된 값이어도 그와 관계없이 최대 값에 1을 더한 값을 컬럼에 설정된다.


그럼 다음은 AUTOINCREMENT을 같이 지정보자. 다음과 같이 테이블을 만든다.

create table user2 (id integer primary key autoincrement, name text);
sqlite> 
sqlite> create table user2 (id integer primary key autoincrement, name text);
sqlite> 

앞에서 살펴본 바와 같이 3개의 데이터를 추가한다. INTEGER PRIMARY KEY AUTOINCREMENT를 설정한 컬럼에 값을 지정하지 않으므로 값이 자동으로 저장된다.

insert into user2 (name) values ('devkuma');
insert into user2 (name) values ('kimkc');
insert into user2 (name) values ('araikuma');
sqlite> insert into user2 (name) values ('devkuma');
sqlite> insert into user2 (name) values ('kimkc');
sqlite> insert into user2 (name) values ('araikuma');
sqlite> 
sqlite> select * from user2;
id          name      
----------  ----------
1           devkuma   
2           kimkc     
3           araikuma  
sqlite> 

ID 컬럼에서 가장 큰 값이 들어있는 데이터(ID 컬럼의 값이 3 데이터)를 삭제한다.

delete from user1 where id = 3;
sqlite> 
sqlite> delete from user2 where id = 3;
sqlite> 
sqlite> select * from user2;
id          name      
----------  ----------
1           devkuma   
2           kimkc     
sqlite> 

그러고 다시 데이터를 추가해 본다. 이때 INTEGER PRIMARY KEY를 설정한 컬럼에 값을 지정하지 않으면 대상 컬럼에 저장되는 최대 값(현재는 2)에 1을 더한 값이 자동으로 설정되어 컬럼에 자동으로 설정되는 값은 3이다.

그러고 다시 데이터를 추가해 본다. 이때 INTEGER PRIMARY KEY AUTOINCREMENT를 설정한 컬럼에 값을 지정하지 않으면 대상 컬럼에 지금까지 저장된 수있는 최대 값(현재 3)에 1을 더한 값이 자동으로 설정되기 때문에 4가 저장된다.

insert into user2 (name) values ('happykuma');
sqlite> insert into user2 (name) values ('happykuma');
sqlite> 
sqlite> select * from user2;
id          name      
----------  ----------
1           devkuma   
2           kimkc     
4           happykuma 

이렇게 AUTOINCREMENT을 설정하면 이전에 설정된 적이 있는 값이 다시 사용되는 것을 방지 할 수 있다.

컬럼에 현재까지 할당된 최대 값 확인

AUTOINCREMENT이 컬럼에 설정되어 있는 경우, 삭제된 데이터를 포함하여 과거에 대상의 컬럼에 현재까지 할당된 최대 값을 참조하게 되는데, 이 값은 SQLite가 자동으로 생성하는 특별한 테이블인 sqlite_sequence 테이블에 저장되어 있다.

sqlite_sequence 테이블의 스키마를 확인해 보면 name과 seq 두 컬럼이 포함되어 있는 것을 알 수 있다.

.schema sqlite_sequence
sqlite> .schema sqlite_sequence
CREATE TABLE sqlite_sequence(name,seq);
sqlite>

name에는 테이블명, seq는 INTEGER PRIMARY KEY AUTOINCREMENT가 설정된 컬럼에 과거에 할당된 최대 값이 저장된다. 그럼 방금 사용했던 user2 테이블의 최대 값을 확인하여 보자.

select * from sqlite_sequence where name = 'user2';
sqlite> select * from sqlite_sequence where name = 'user2';
name        seq       
----------  ----------
user2       4         
sqlite> 

현재 user2 테이블에 INTEGER PRIMARY KEY AUTOINCREMENT가 설정된 컬럼에 지금까지 할당된 최대 값이 4 인 것을 확인할 수 있다.

1.5.5.8 - SQLite | 테이블 (Table) | NOT NULL 제약 조건

컬럼에 저장하는 값으로 NULL을 금지하고 싶은 경우, 컬럼에 NOT NULL 제약 조건을 설정한다. 여기에 NOT NULL 제약 조건의 사용법에 대해 설명한다.

NOT NULL 제약 조건이란?

컬럼에 NOT NULL 제약 조건을 설정하면 해당 열에 NULL을 저장할 수 없다. NOT NULL 제약 조건을 컬럼에 설정하는 형식은 다음과 같다.

CREATE TABLE 테이블명 (컬럼명 NOT NULL, ...);

그러면 실제로 제약 조건을 설정해 보자. 다음과 같이 테이블을 만든다. name 컬럼에는 NOT NULL 제약 조건이 설정한다.

create table user (name text not null, address text);
sqlite> create table user (name text not null, address text);
sqlite> 

그러면 데이터를 추가해 보자. address 컬럼에는 NULL을 저장할 수 있다.

insert into user values ('dekuma', 'Korea');
insert into user values ('kimkc', null);
sqlite> insert into user values ('dekuma', 'Seoul');
sqlite> insert into user values ('kimkc', null);
sqlite> 

name 컬럼에 NOT NULL 제약 조건이 설정되어 있기에, NULL을 저장하려고 하면 “Error: NOT NULL constraint failed: user.name"라는 오류 메시지가 표시된다.

insert into user values (null, 'Busan');
sqlite> insert into user values (null, 'Busan');
Error: NOT NULL constraint failed: user.name
sqlite>

NOT NULL 제약 조건이 설정된 컬럼에 값을 지정하지 않고 데이터를 추가하는 경우도 자동으로 NULL로 저장이 되기에 같은 오류가 발생한다.

insert into user (address) values ('Daejun');
sqlite> insert into user (address) values ('Daejun');
Error: NOT NULL constraint failed: user.name
sqlite> 

데이터를 추가 할 때에 반드시 값이 설정될 필요가 있는 컬럼에는 NOT NULL 제약 조건을 설정하도록 하자.

1.5.5.9 - SQLite | 테이블 (Table) | UNIQUE 제약 조건

컬럼에 저장하는 값으로 이미 저장되어 있는 데이터의 값과 중복 값을 입력되지 않도록 하고 싶은 경우, 컬럼에 UNIQUE 제약 조건을 설정한다. 여기에서는 UNIQUE 제약 조건 사용에 대해 설명한다.

UNIQUE 제약 조건이란?

컬럼에 UNIQUE 제약 조건을 설정하면 대상의 컬럼에 중복 된 값이 저장될 수 없다. UNIQUE 제약 조건을 컬럼에 설정하는 형식은 다음과 같다.

CREATE TABLE 테이블명 (컬럼명 UNIQUE, ...);

그러면 실제로 제약 조건을 설정해 보자. 먼저 다음과 같이 테이블을 만든다. id 컬럼에 UNIQUE 제약 조건이 설정되어 있다.

create table user (id integer unique, name text);
sqlite> create table user (id integer unique, name text);
sqlite> 

그러면 데이터를 추가해 보자.

insert into user values (1, 'devkuma');
insert into user values (2, 'kimkc');
insert into user values (4, 'araikuma');
sqlite> insert into user values (1, 'devkuma');
sqlite> insert into user values (2, 'kimkc');
sqlite> insert into user values (4, 'araikuma');
sqlite> 

다음은 UNIQUE 제약 조건이 설정되어 있는 id 컬럼의 값으로 이미 저장된 데이터와 같은 값을 설정한 데이터를 추가해 본다. 그러면 “Error: UNIQUE constraint failed: user.id"라는 오류 메시지가 표시된다.

insert into user values (2, 'happykuma');
sqlite> insert into user values (2, 'happykuma');
Error: UNIQUE constraint failed: user.id
sqlite>

이렇게 UNIQUE 제약 조건을 컬럼에 설정하는 것으로, 중복된 값이 컬럼에 저장될 수 없게 된다.

다만 UNIQUE 제약 조건이 설정되어 있어도 NULL은 중복하여 저장할 수 있다.

insert into user values (null, 'mykuma');
insert into user values (null, 'yourkuma');
sqlite> insert into user values (null, 'mykuma');
sqlite> insert into user values (null, 'yourkuma');
sqlite> 

컬럼에 NULL을 포함하는 것을 허용하지 않으려면 NOT NULL 제약 조건을 설정하면 된다.

여러 컬럼의 조합에 UNIQUE 제약 조건을 설정

UNIQUE 제약 조건을 단일 컬럼뿐 아니라, 여러 컬럼의 조합에 UNIQUE 제약 조건을 설정할 수 있다. 형식은 다음과 같다.

CREATE TABLE 테이블명 (컬럼명1, 컬럼명2, ... , UNIQUE (컬럼명1, 컬럼명2, ...));

예를 들어 2개의 컬럼을 대상으로 UNIQUE 제약 조건을 설정하면 각각의 컬럼의 값은 중복도 상관 없지만 2개의 컬럼 값의 조합과 같은 조합의 값을 저장하려고 하면 오류가 발생한다.

그러면 실제로 제약 조건을 설정해 보자. 먼저 다음과 같이 테이블을 만든다. no 컬럼과 unit 컬럼 쌍으로 UNIQUE 제약 조건이 설정한다.

create table employee (no integer, unit text, name text, unique (no, unit));
sqlite> create table employee (no integer, unit text, name text, unique (no, unit));
sqlite> 

우선 몇개의 데이터를 추가한다.

insert into employee values (1, 'Sales', 'devkuma');
insert into employee values (4, 'Dev', 'kimkc');
insert into employee values (2, 'Office', 'araikuma');
sqlite> insert into employee values (1, 'Sales', 'devkuma');
sqlite> insert into employee values (4, 'Dev', 'kimkc');
sqlite> insert into employee values (2, 'Office', 'araikuma');
sqlite> 

다음은 no 컬럼의 값만 기존의 데이터와 동일한 데이터와 unit 컬럼의 값만 기존의 데이터와 동일한 데이터를 추가해 본다. 이번에는 단일 컬럼의 값이 기존 데이터와 동일하지만 에러는 없다.

insert into employee values (4, 'Design', 'mykuma');
insert into employee values (7, 'Sales', 'yourkuma');
sqlite> insert into employee values (4, 'Design', 'mykuma');
sqlite> insert into employee values (7, 'Sales', 'yourkuma');
sqlite> 

다음은 no 컬럼과 unit 컬럼 값의 조합이 기존의 데이터와 동일한 데이터를 추가해 본다. 그러면 UNIQUE 제약 조건이 설정되어 있기 때문에 “Error: UNIQUE constraint failed: employee.no, employee.unit"라는 오류 메시지가 표시된다.

insert into employee values (2, 'Office', 'blackkuma');
sqlite> insert into employee values (2, 'Office', 'blackkuma');
Error: UNIQUE constraint failed: employee.no, employee.unit
sqlite> 

이와 같이 하나의 컬럼에 대해서뿐만 아니라 여러 컬럼의 조합에 대해서도 UNIQUE 제약 조건을 설정할 수 있다.

1.5.5.10 - SQLite | 테이블 (Table) | DEFAULT 제약 조건

테이블에 데이터를 추가할 때, 값을 생략한 컬럼은 보통 NULL이 저장되지만 NULL 대신에 기본으로 저장되는 값을 설정할 경우에는 DEFAULT 제약 조건으로 설정한다. 여기에서는 DEFAULT 제약 조건의 사용법에 대해 설명한다.

DEFAULT 제약 조건이란?

컬럼에 DEFAULT 제약 조건을 설정하면, 데이터를 추가할 때 값을 생략할 시에 기본값을 설정할 수 있다. DEFAULT 제약 조건을 컬럼에 설정하는 경우 형식은 다음과 같다.

CREATE TABLE 테이블명 (컬럼명 DEFAULT 값, ...);

기본값은 NULL, 숫자, 문자열을 지정할 수 있다.

그러면 실제로 제약 조건을 설정해 보자. 다음과 같이 테이블을 만든다. price 컬럼와 name 컬럼에는 각각 DEFAULT 제약 조건이 설정되어 있다.

create table product (id integer, name text default 'no name', price integer default 0);
sqlite> create table product (id integer, name text default 'no name', price integer default 0);
sqlite> 

우선 몇개의 데이터를 추가해 보자. DEFAULT 제약 조건이 설정되어 있는 컬럼에 값을 지정한 경우에는 지정한 값이 컬럼에 저장된다.

insert into product values (1, 'PC', 75000);
insert into product values (4, 'Desk', 18000);
sqlite> insert into product values (1, 'PC', 75000);
sqlite> insert into product values (4, 'Desk', 18000);
sqlite> 

다음은 DEFAULT 제약 조건이 설정되어 있는 name 컬럼의 값을 지정하지 않고, 데이터를 추가하면 컴럼에는 기본값인 ’no name’이 저장된다.

insert into product (id, price) values (6, 18000);
sqlite> insert into product (id, price) values (6, 18000);
sqlite> 
sqlite> .mode column
sqlite> .header on
sqlite> 
sqlite> select * from product;
id          name        price     
----------  ----------  ----------
4           Desk        18000     
1           PC          75000     
6           no name     18000     
sqlite> 

동일하게 DEFAULT 제약 조건이 설정되어있는 price 컬럼의 값을 지정하지 않고, 데이터를 추가하면 컬럼에는 기본값 인 0이 저장된다.

insert into product (id, name) values (8, 'Chair');
sqlite> insert into product (id, name) values (8, 'Chair');
sqlite> 
sqlite> select * from product;
id          name        price     
----------  ----------  ----------
4           Desk        18000     
1           PC          75000     
6           no name     18000     
8           Chair       0         
sqlite> 

이렇게 DEFAULT 제약 조건을 컬럼에 설정하면, 값이 지정되지 않고 데이터가 추가된 경우는 NULL이 아닌 지정한 값이 기본 값으로 저장되도록 할 수 있다.

데이터를 추가한 날짜를 기본값으로 설정

DEFAULT 제약 조건에 지정된 기본 값으로 다음 값을 지정하면 데이터를 추가했을 때 그 당시의 날짜와 시간을 가져 디폴트 값으로 컬럼 가능 할 수 있다.

지정값 형식
CURRENT_TIME HH : MM : SS
CURRENT_DATE YYYY-MM-DD
CURRENT_TIMESTAMP YYYY-MM-DD HH : MM : SS
시간대는 UTC이다.

그러면 실제로 제약 조건을 설정해 보자. 다음과 같이 테이블을 만든다. date_time 컬럼에 DEFAULT 제약 조건을 설정하고 기본값으로 CURRENT_TIMESTAMP를 지정한다.

create table user (id integer, name text, date_time default CURRENT_TIMESTAMP);

sqlite> create table user (id integer, name text, date_time default CURRENT_TIMESTAMP);
sqlite> 

그러면 DEFAULT 제약 조건이 설정되어 date_time 컬럼의 값은 생략하고 데이터를 일부만 추가한다.

insert into user (id, name) values (1, 'devkuma');
insert into user (id, name) values (3, 'kimkc');
sqlite> insert into user (id, name) values (1, 'devkuma');
sqlite> insert into user (id, name) values (3, 'kimkc');
sqlite> 
sqlite> select * from user;
id          name        date_time          
----------  ----------  -------------------
1           devkuma     2019-10-22 14:43:07
3           kimkc       2019-10-22 14:43:08
sqlite> 

DEFAULT 제약 조건을 설정한 date_time 컬럼에 데이터를 추가했을 때의 날짜와 시간이 저장되어 있는 것을 확인할 수 있다.

1.5.5.11 - SQLite | 테이블 (Table) | CHECK 제약 조건

컬럼에 저장되는 값에 조건에 일치하는지 여부의 판정을 할 경우, 컬럼에 CHECK 제약 조건을 설정한다. 여기에서 CHECK 제약 조건 사용에 대해 설명한다.

CHECK 제약 조건이란?

컴럼에 CHECK 제약 조건을 설정하면 데이터를 추가 할 때 값이 지정된 조건을 충족하는지 여부를 확인 할 수 있다. CHECK 제약 조건을 컬럼에 설정하는 경우 형식은 다음과 같다.

CREATE TABLE 테이블명 (컬럼명 CHECK (조건식), ...);

조건식은 컬럼에 저장되는 값의 조건을 설명한다. 예를 들면 저장할 수 있는 값을 0 이상의 값으로 제한하는 등 조건식을 지정할 수 있다.

또한 여러 컬럼을 조합한 조건식을 설정하려면 형식으로 다음과 같은 형식도 사용할 수 있다. 자세한 내용은 나중에 설명하도록 하겠다.

CREATE TABLE 테이블명 (컬럼명1, 컬럼명2, ..., CHECK (조건식));

그러면 실제로 제약 조건을 설정해 보자. 다음과 같이 테이블을 만든다. old 컬럼에는 CHECK 제약 조건이 설정되어 있으며, 18이상의 정수만 저장할 수 없습니다.

create table user (id integer, name text, old integer check (old> 18));
sqlite> create table user (id integer, name text, old integer check (old> 18));
sqlite> 

old 컬럼의 값이 조건에 일치하는 경우는 문제없이 데이터가 추가 된다.

insert into user values (1, 'devkuma', 19);
insert into user values (4, 'kimkc', 21);
qlite> insert into user values (1, 'devkuma', 19);
sqlite> insert into user values (4, 'kimkc', 21);
sqlite> 
sqlite> select * from user;
id          name        old       
----------  ----------  ----------
1           devkuma     19        
4           kimkc       21        
sqlite> 

다음은 old 컬럼이 조건에 일치하지 않는 데이터를 추가해 본다. 그러면 “Error: CHECK constraint failed: user"라는 오류 메시지가 표시된다.

insert into user values (7, 'araikuma', 16);
sqlite> insert into user values (7, 'araikuma', 16);
Error: CHECK constraint failed: user
sqlite> 

이와 같이 CHECK 제약 조건을 사용하여 컬럼에 저장할 수있는 값을 제한할 수 있다.

AND / OR를 사용하여 복잡한 조건식을 설정한다.

CHECK 제약 조건으로 작성하는 조건식을 AND 또는 OR를 사용하여 더 복잡한 조건식을 작성할 수 있다. 예를 들어 old 컬럼의 값을 18보다 크고 30 이하로 제한하는 경우는 다음과 같이 작성할 수 있다.

create table user (id integer, name text,
  old integer check (old > 18 and old < 30));

다른 예로 gender 컬럼의 값을 man 또는 woman로 제한하려면 다음과 같이 작성할 수 있다.

create table user (id integer, name text,
  gender text check (gender = 'man' or gender = 'woman'));

또한 여러 컬럼을 사용한 조건식을 작성하는 경우, 두 번째 형식을 사용하여 다음과 같이 작성할 수도 있다. 아래에서는 성별이 여성이거나 나이가 20보다 큰 경우에 데이터를 추가할 수 있다.

create table user (name text, old integer, gender text,
  check (gender = 'woman' or old> 20));

그러면 실제로 제약 조건을 설정해 보자. 마지막에 작성된 테이블을 실제로 만들어 보자.

sqlite> create table user (name text, old integer, gender text, check (gender = 'woman' or old> 20));
sqlite> 

데이터를 추가한다. 성별이 여자이거나 나이가 20보다 큰 경우는 성공적으로 추가가 된다.

insert into user values ( 'dekuma', 16, 'woman');
insert into user values ( 'kimkc', 31, 'man');
insert into user values ( 'araikuma', 24, 'woman');
sqlite> insert into user values ( 'dekuma', 16, 'woman');
sqlite> insert into user values ( 'kimkc', 31, 'man');
sqlite> insert into user values ( 'araikuma', 24, 'woman');
sqlite> 

다음은 CHECK 제약 조건의 조건식에 맞지 않는 데이터를 추가할 수 있다. 그러자 “Error: CHECK constraint failed: user"라는 오류 메시지가 표시된다.

insert into user values ('happykuma', 15, 'man');
sqlite> insert into user values ('happykuma', 15, 'man');
Error: CHECK constraint failed: user
sqlite>

이렇게 AND 또는 OR를 사용하여 더 복잡한 조건식을 CHECK 제약 조건으로 설정하는 것도 가능하다.

1.5.6 - SQLite | 뷰(View)

뷰(View)는 SELECT 문 등으로 테이블의 일부 컬럼의 데이터를 열람하고 싶은 경우에 재사용하기 쉽도록 이름을 붙인 것이다. 여기에서는 SQLite3의 뷰에 대한 다양한 조작 방법을 설명한다.

1.5.6.1 - SQLite | 뷰(View) | 뷰(View) 생성

SQLite에는 뷰(View)라는 기능이 포함되어 있다. 뷰는 생성된 테이블의 컬럼과 데이터 검색 조건을 지정하여 만든 가상적인 테이블이다. 여기에서는 뷰 생성 및 이용 방법에 대해 설명한다.

뷰 생성

뷰를 만들려면 다음 형식을 사용한다.

CREATE VIEW 뷰명 AS SELECT ;

AS를 작성하고 SELECT 문을 사용하여 기존의 테이블에 있는 컬럼과 조건 등을 지정한다. 예를 들면 다음과 같다.

create view myview as select name, price from product where price> 3000;

이렇게 뷰는 테이블에서 일부만을 추출하여 마치 다른 테이블인거처럼 사용 할 수 있도록 해준다. 나중에 설명하지만, 뷰에서 테이블처럼 데이터를 검색 할 수 있지만 뷰를 사용하여 데이터를 업데이트하거나 추가 할 수 없다.

그럼 실제로 뷰를 만들어 보자. 우선 원본이 되는 테이블을 다음과 같이 작성한다.

create table user (id integer, name text, address text, old integer);
sqlite> create table user (id integer, name text, address text, old integer);
sqlite> 

테이블에 데이터를 추가한다.

insert into user values (1, 'devkuma', 'Seoul', 23);
insert into user values (2, 'kimkc', 'Busan', 19);
insert into user values (3, 'araikuma', 'Seoul', 38);
insert into user values (4, 'happykuma', 'Seoul', 24);
insert into user values (5, 'mykuma', 'Daejeon', 18);
sqlite> insert into user values (1, 'devkuma', 'Seoul', 23);
sqlite> insert into user values (2, 'kimkc', 'Busan', 19);
sqlite> insert into user values (3, 'araikuma', 'Seoul', 38);
sqlite> insert into user values (4, 'happykuma', 'Seoul', 24);
sqlite> insert into user values (5, 'mykuma', 'Daejeon', 18);
sqlite> 

그럼 이 테이블을 바탕으로 뷰를 생성해 보자. 여기서는 seouluser라는 뷰를 생성한다.

create view seouluser as select id, name from user where address = 'Seoul';
sqlite> create view seouluser as select id, name from user where address = 'Seoul';
sqlite>

생성한 뷰는 user 테이블의 id와 name 컬럼을 가지고 있고, 데이터로 user 테이블의 address 컬럼의 값이 ‘Seoul’과 일치하는 데이터만 가지고 있다. (나중에 설명하겠지만 실제로는 데이터를 가지고 있는 것은 아니다.)

그러면 작성한 뷰에서 데이터를 검색해 보자.

select * from tokyouser;
sqlite> select * from seouluser;
1|devkuma
3|araikuma
4|happykuma

테이블과 동일하게 뷰에서 데이터를 조회되는 것을 볼 수 있다. 즉, 뷰에서 조회하는 데이터와 뷰의 원본이 되고 있는 테이블에서 다음과 같이 데이터를 조회하는 경우와 동일하다.

select id, name from user where address = 'Tokyo';
sqlite> select id, name from user where address = 'Seoul';
1|devkuma
3|araikuma
4|happykuma
sqlite> 

뷰를 만들면하여 테이블에서 필요한 데이터를 쉽게 간단히 조회할 수 있다.

뷰에서 얻을 수 있는 데이터에 대해

뷰는 어떤 데이터를 원래의 테이블에서 검색하는지에 대한 정의를 하고 있을 뿐 데이터 자체는 가지고 있지 않다. 뷰에서 데이터를 검색할 때마다 현재 테이블의 데이터에서 조건에 맞는 데이터를 가져 온다. 그러므로 뷰에 대해 동일한 SQL 문을 실행해도 원래 테이블의 데이터가 바뀐 경우 다른 결과가 될 가능성이 있다.

실제로 해보도록 하자. 현재 원본이 되는 테이블에 다음과 같은 데이터가 포함되어 있다.

select * from user;
sqlite> select * from user;
1|devkuma|Seoul|23
2|kimkc|Busan|19
3|araikuma|Seoul|38
4|happykuma|Seoul|24
5|mykuma|Daejeon|18

생성된 뷰에서 데이터를 가져온다.

select * from seouluser;
sqlite> select * from seouluser;
1|devkuma
3|araikuma
4|happykuma

그러면 뷰의 원본이 되는 테이블에 데이터를 추가한 후에 다시 뷰에 데이터를 조회하려고 한다. 그러면 뷰에서 조회되는 데이터가 같이 추가되는 것을 볼 수 있다.

insert into user values (6, 'yourkuma', ' Seoul', 17);
sqlite> insert into user values (6, 'yourkuma', ' Seoul', 17);
sqlite> 
sqlite> select * from seouluser;
1|devkuma
3|araikuma
4|happykuma
6|yourkuma
sqlite> 

이렇게 뷰는 뷰 자체에서 데이터를 유지하고 있는 것은 아니라, 원본이 되는 테이블을 매번 가져온다는 점을 유의하도록 하자.

뷰에서 데이터 추가 및 삭제

뷰에서 데이터를 조회할 수는 있지만, 뷰에 데이터를 추가하거나 삭제, 데이터 업데이트 등의 작업을 수행할 수 없다.

실제로 해보면 다음과 같이 “Error: cannot modify seouluser because it is a view” 오류가 발생한다.

insert into seouluser values (8, 'kuma');
sqlite> insert into seouluser values (8, 'kuma');
Error: cannot modify seouluser because it is a view
sqlite> 

생성된 뷰 목록 조회

생성된 뷰의 목록만 조회하는 방법은 없지만, SQLite 명령 .tables를 실행하면 생성된 테이블뿐만 아니라 뷰도 함께 표시된다.

.tables
sqlite> .table
seouluser  user     
sqlite> 

그리고 SQLite의 특별한 테이블인 sqlite_master 테이블에서 다음과 같이 생성된 뷰의 이름과 생성되었을 때의 SQL 문을 얻을 수 있다.

select name, sql from sqlite_master where type = 'view';
sqlite> select name, sql from sqlite_master where type = 'view';
seouluser|CREATE VIEW seouluser as select id, name from user where address = 'Seoul'
sqlite> 

1.5.6.2 - SQLite | 뷰(View) | 뷰(View) 삭제

생성된 뷰를 삭제하는 방법에 대해 설명한다.

뷰 삭제

생성된 뷰를 삭제하려면 다음과 같은 형식을 사용한다.

DROP VIEW 뷰명;

뷰를 삭제해도 원본이 있는 테이블이나 테이블에 저장되어 있는 데이터에 영향을 주지 않는다.

뷰를 삭제하기 전에 SQLite 명령 .tables를 실행하여 생성된 인덱스를 확인한다.

.tables
sqlite> .table
seouluser  user     
sqlite> 

seouluser 뷰를 삭제한다.

drop view seouluser;
sqlite> drop view seouluser;
sqlite> 

seouluser 뷰가 삭제되었는지 .tables 명령을 실행하여 생성된 뷰를 확인한다.

.tables
sqlite> .table
user
sqlite> 

seouluser 뷰가 삭제된 것을 확인할 수 있다.

1.5.7 - SQLite | 인덱스(Index)

인덱스는 테이블에 포함된 데이터의 색인과 같은 역할을 한다. 인덱스를 생성하면 데이터 검색을 빠르게 할 수 있는 경우가 있다. 여기에서는 SQLite에서 인덱스를 사용하는 방법을 설명한다.

1.5.7.1 - SQLite | 인덱스(Index) | 인덱스 의미와 장단점

테이블에 많은 열이 포함되어 있거나 대량의 데이터가 저장되어 있는 경우, 테이블에서 특정 데이터를 검색하려고 하면 매우 시간이 걸릴 수 있다. 이런 경우에 적절한 컬럼에 인덱스를 생성하면 검색이 빨라질 수 있다. 여기에서는 인덱스에 대한 간단한 설명과 인덱스를 사용하는 경우의 장점과 단점에 대해 설명한다.

인덱스란?

인덱스는 테이블의 정보를 검색했을 때 검색의 대상으로 자주 사용하는 컬럼의 값만 꺼내 쉽게 찾을 수 있도록 해 놓은 것이다. 예를 들어 다음과 같은 4개의 컬럼이 있는 테이블이 있다.

id name address old
1 devkuma Seoul 23
2 kimkc Busan 19
3 araikuma Seoul 38
4 mykuma Daejeon 18
5 yourkuma Seoul 17
6 happykuma Seoul 24

name 컬럼의 값을 검색하려고 할 때, 테이블에 저장되는 데이터는 차례로 나열되어 있는 것이 아니므로 원하는 데이터가 있는지 차례로 검색해 나갈 것이다. 데이터가 이 정도의 양이면 문제가 없겠지만, 수백만의 데이터가 저장되어 있는 경우에 위에서 부터 찾아가는 것은 매우 비효율적이다.

이러한 경우 인덱스를 생성하면 검색 속도를 향상시킬 수 있다. 인덱스를 간단히 대상 컬럼의 데이터를 검색하여, 빠르게 검색할 수 있도록 가공하여 저장해 둔 것이다. 예를 들어 name 컬럼의 값을 대상으로 한 인덱스를 만들면 다음과 같은 것이다.

id name
3 araikuma
1 devkuma
2 kimkc
6 happykuma
4 mykuma
5 yourkuma

이 인덱스는 id 컬럼와 name 컬럼의 값만을 유지하고 name 컬럼의 값을 오름차순으로 정렬하여 데이터를 저장하고 있다. name 컬럼의 값을 검색할 때 원본의 테이블을 검색하는 것보다 데이터 량이 적고 또한 정렬도 이루어지고 있기 때문에 빠르게 검색할 수 있다.

그럼 어떻게 빠르게 검색 할 수 있는지에 대해서는 B-Tree 방식이나 함수 방식 등 다양한 방법이 있을 것이다. SQLite에서는 어떤 방식을 하고 있는지는 알 수 없다.

인덱스를 생성하면 테이블과는 별도로 검색에 최적화 된 상태로 필요한 데이터만 테이블과는 별도로 저장된다는 것을 기억하도록 하자.

인덱스의 장점과 단점

인덱스를 생성 해두면 유용하기도 하지만, 무조건 장점만 있는 것이 아니다. 테이블과는 별도로 데이터를 독자적으로 보유하고 있기 때문에 테이블에 데이터를 추가하면 인덱스으로도 데이터가 추가된다. 또한 데이터를 추가할 때마다 정렬도 다시 이루어진다. 결과적으로 데이터를 추가 할 때 처리 속도가 느려진다.

저장되어 있는 데이터가 적은 테이블에서 인덱스를 만들거나 저장되는 데이터의 종류가 적은 컬럼에 인덱스를 생성해도 효과는 그다지 기대할 수 없다. 데이터의 검색이 그다지 많지 않는 컬럼에 인덱스를 생성하는 것은 의미가 없다.

인덱스는 유용하지만 데이터 추가시 처리가 무거워지는 단점도 있으므로 인덱스가 필요한지에 대해 잘 검토한 후에 생성되도록 해야 한다.

1.5.7.2 - SQLite | 인덱스(Index) | 인덱스 생성

SQLite에서 인덱스를 생성하는 방법과 생성된 색인 목록을 표시하는 방법에 대해 설명한다.

CREATE INDEX 문을 사용하여 인덱스 생성

SQLite에서 인덱스를 만들 때는 다음 형식을 사용한다.

CREATE INDEX 인덱스명 ON 테이블명 (컬럼명 1, 컬럼명 2, ...);

인덱스는 테이블에 있는 컬럼을 대상으로 생성한다. 단일 컬럼에 인덱스를 만들 수 있고, 여러 컬럼을 조합하여 인덱스를 만들 수도 있다.

그러면 실제로 인덱스를 생성해 보자. 먼저 다음과 같이 테이블을 만든다.

create table user (name text, old integer, address text);
sqlite> create table user (name text, old integer, address text);
sqlite> 

테이블에 데이터를 추가한다.

insert into user values ('devkuma', 28, 'Seoul');
insert into user values ('kimkc', 22, 'Busan');
insert into user values ('araikuma', 32, 'Seoul');
insert into user values ('happykuma', 23, 'Seoul');
insert into user values ('mykuma', 23, 'Daejeon');
sqlite> insert into user values ('devkuma', 28, 'Seoul');
sqlite> insert into user values ('kimkc', 22, 'Busan');
sqlite> insert into user values ('araikuma', 32, 'Seoul');
sqlite> insert into user values ('happykuma', 23, 'Seoul');
sqlite> insert into user values ('devkuma', 23, 'Daejeon');
sqlite> 

그러면 생성한 테이블에 name 컬럼을 대상으로 한 인덱스를 만들어 보자. 여기서는 인덱스명을 nameindex으로 해서 다음과 같이 실행한다.

create index nameindex on user (name);
sqlite> create index nameindex on user (name);
sqlite> 

이것으로 인덱스가 생성이 완료되었다.

인덱스를 이용한 데이터 검색

인덱스를 생성해두면 데이터를 검색할 때 빨라지는 경우도 있지만, 인덱스 생성된 경우와 생성되지 않은 경우에 데이터의 검색 방법에는 차이가 없다. 테이블에 대해 검색을 수행할 때, 인덱스가 생성되면 자동으로 인덱스를 이용하여 검색이 이루어진다.

예를 들어, 인덱스 생성된 테이블에 name 컬럼을 대상으로 한 조건을 설정하고 데이터를 검색하려면 다음과 같다.

select * from user where name = 'devkuma';
sqlite> select * from user where name = 'devkuma';
devkuma|28|Seoul

이렇게 인덱스가 생성되었는지 아닌지를 의식할 필요가 없다.

데이터베이스에서 생성된 파일 목록

데이터베이스에서 생성되는 인덱스 목록을 확인하려면 SQLite 명령 .indices을 사용한다.

.indices
.indices? TABLE?

인수를 생략하는 경우에는 생성된 인덱스 목록을 표시한다. 인수에 테이블 이름을 지정한 경우에는 지정한 값과 일치하는 테이블을 대상으로 한 인덱스의 목록을 표시한다.

그럼 실제로 목록을 표시해 보자.

.indices
sqlite> .indices
nameindex
sqlite>

이 페이지에서 만든 인덱스가 표시되었다.

sqlite> .indices user
nameindex
sqlite>

위에 명령어으로는 user테이블을 대상으로 한 nameindex가 표시되었다.

1.5.7.3 - SQLite | 인덱스(Index) | UNIQUE 인덱스 생성

인덱스의 대상이 되는 테이블의 컬럼에 저장되는 값은 중복된 값이 포함되어 있어도 상관 없지만, 중복 값을 허용하지 않도록 설정할 수도 있다. 이러한 인덱스를 유니크(Unique) 인덱스라고 한다. 여기에서는 유니크 인덱스 사용에 대해 설명한다.

UNIQUE 인덱스 생성

UNIQUE 인덱스를 작성하는 형식은 다음과 같다.

CREATE UNIQUE INDEX 인덱스명 ON 테이블명 (컬럼명1, 컬럼명2, ...);

대상이 되는 컬럼에 중복된 값이 포함되어 있으면 유니크 인덱스는 만들 수 없다. 또한 유니크 인덱스를 생성한 후에 유니크 인덱스의 대상인 컬럼에 이미 저장되어 있는 값과 동일한 데이터는 테이블에 추가할 수 없다.

여러 컬럼의 조합하여 인덱스를 작성하는 경우에는 각각의 컬럼에서 중복된 값이 포함되어 있어도 지정된 모든 컬럼 값의 조합이 중복되지 않으면 유니크 인덱스를 만들 수 있다.

그러면 실제로 유니크 인덱스를 생성해 보자. 먼저 다음과 같이 테이블을 만든다.

create table user (name text, old integer, address text);
sqlite> create table user (name text, old integer, address text);
sqlite> 

INSERT 문을 사용하여 다음과 같이 데이터를 추가한다.

insert into user values ('devkuma', 28, 'Seoul');
insert into user values ('kimkc', 22, 'Busan');
insert into user values ('araikuma', 32, 'Seoul');
insert into user values ('happykuma', 23, 'Seoul');
sqlite> insert into user values ('devkuma', 28, 'Seoul');
sqlite> insert into user values ('kimkc', 22, 'Busan');
sqlite> insert into user values ('araikuma', 32, 'Seoul');
sqlite> insert into user values ('happykuma', 23, 'Seoul');
sqlite> 

그러면 생성한 테이블에 name 컬럼을 대상으로 유니크 인덱스를 만들어 보자. name 컬럼에는 현재 중복된 값이 포함되어 있지 않으므로 name 컬럼을 대상으로 한 유니크 인덱스를 만들 수 있다.

create unique index nameindex on user (name);
sqlite> create unique index nameindex on user (name);
sqlite> 

유니크 인덱스가 생성되었다.

생성된 유니크 인덱스의 대상이 되는 컬럼에는 이미 저장되어 있는 것과 같은 값을 가지는 데이터를 추가할 수 없다. 예를 들어, 다음과 같은 데이터를 추가하려고하면 “Error: UNIQUE constraint failed: user.name” 오류가 발생한다.

insert into user values ('devkuma', 18, 'Busan');
sqlite> insert into user values ('devkuma', 18, 'Busan');
Error: UNIQUE constraint failed: user.name
sqlite> 

다만, NULL는 유니크 인덱스가 생성되는 컬럼에도 여러 데이터 중복 저장할 수 있다.

또한 address 컬럼에 중복된 값이 포함되어 있다. 이러한 중복 값을 가지는 컬럼을 대상으로 유니크 인덱스를 만들려고하면 Error:UNIQUE constraint failed: user.address 오류가 발생한다.

create unique index nameindex2 on user (address);
sqlite> create unique index nameindex2 on user (address);
Error: UNIQUE constraint failed: user.address
sqlite> 

UNIQUE 인덱스와 테이블의 컬럼에 대한 PRIMARY KEY 제약 조건 / UNIQUE 제약 조건과의 관계

테이블의 컬럼에 PRIMARY KEY 제약 조건UNIQUE 제약 조건을 설정할 수 있다.

컬럼에 PRIMARY KEY 제약 조건 및 UNIQUE 제약 조건을 설정한 경우의 동작은 대상의 컬럼에 UNIQUE 인덱스를 작성하는 경우와 매우 비슷하며 SQLite는 공식 사이트에 다음과 같은 내용이 있다.

In most cases, UNIQUE and PRIMARY KEY constraints are implemented by creating a unique index in the database. (The exceptions are INTEGER PRIMARY KEY and PRIMARY KEYs on WITHOUT ROWID tables.) Hence, the following schemas are logically equivalent:

  1. CREATE TABLE t1(a, b UNIQUE);

  2. CREATE TABLE t1(a, b PRIMARY KEY);

  3. CREATE TABLE t1(a, b);
    CREATE UNIQUE INDEX t1b ON t1(b);

PRIMARY KEY 제약 조건은 테이블에 하나 밖에 설정할 수 없는 반면, UNIQUE 제약 조건과 UNIQUE 인덱스는 같은 테이블에 여러 설정하거나 생성할 수 있다. 다만, 동작이 유사하더라도 설정 목표는 다르기 때문에 적절한 설정을 할 수 있도록 한다.

1.5.7.4 - SQLite | 인덱스(Index) | 인덱스 스키마(구조) 확인

생성된 인덱스가 어떤 CREATE INDEX 문을 사용하여 생성되었는지에 대해 확인하는 방법에 대해 설명한다. sqlite_master 테이블을 이용하는 방법과 .schema 명령을 사용하는 방법의 두 가지 방법이 있다.

sqlite_master 테이블에서 조회

먼저 sqlite_master 테이블을 이용하는 방법이다. 다음 같은 SQL 문을 실행한다. (쉽게 확인할 수 있도록 먼저 “.mode” 명령으로 mode를 line으로 변경한다.)

select * from sqlite_master;
sqlite> select * from sqlite_master;
table|user|user|2|CREATE TABLE user (name text, old integer, address text, colunm name)
index|nameindex|user|3|CREATE UNIQUE INDEX nameindex on user (name)
sqlite> 
sqlite> .mode line
sqlite> 
sqlite> select * from sqlite_master;
    type = table
    name = user
tbl_name = user
rootpage = 2
     sql = CREATE TABLE user (name text, old integer, address text)

    type = index
    name = nameindex
tbl_name = user
rootpage = 3
     sql = CREATE UNIQUE INDEX nameindex on user (name)
sqlite> 

생성된 테이블과 인덱스에 대한 데이터를 조회할 수 있다. 여기서 조회한 데이터에서 인덱스에 대한 데이터는 다음과 같다.

    type = index
    name = nameindex
tbl_name = user
rootpage = 3
     sql = CREATE UNIQUE INDEX nameindex on user (name)

type 컬럼은 인덱스의 경우는 index로 표시된다. (테이블의 경우 table이다.) name 컬럼에는 인덱스명, tbl_name 컬럼에는 인덱스가 지정된 테이블명, sql 컬럼에는 인덱스를 생성했던 SQL 문이 표시된다.

인덱스에 대한 데이터만 표시하려면 WHERE 절을 사용하여 다음과 같이 실행한다.

select * from sqlite_master where type = 'index';
sqlite> select * from sqlite_master where type = 'index';
    type = index
    name = nameindex
tbl_name = user
rootpage = 3
     sql = CREATE UNIQUE INDEX nameindex on user (name)

.schema 명령을 사용하여 조회

인덱스의 스키마에 대한 정보만 조회하고자 한다면 SQLite 명령 .schema으로도 확인할 수 있다.

.schema
.schema? TABLE?

인수를 생략하면 모든 테이블과 인덱스의 스키마 정보를 표시한다. 또한 인수에 테이블명을 지정하면 인덱스명과 일치하는 테이블과 지정된 테이블명과 관련된 스키마 정보를 표시할 수 있다.

그럼 .schema 명령을 사용하여 조회해 보도록 하자.

.schema
sqlite> .schema
CREATE TABLE user (name text, old integer, address text, colunm name);
CREATE UNIQUE INDEX nameindex on user (name);
sqlite>

현재 데이터베이스에 생성되어 있는 테이블 및 인덱스에 대한 CREATE 문이 표시되는 것을 확인할 수 있다.

1.5.7.5 - SQLite | 인덱스(Index) | 인덱스 삭제

생성된 인덱스를 삭제하는 방법에 대해 설명한다.

생성된 인덱스 삭제

생성된 인덱스를 삭제하려면 DROP INDEX 문을 사용한다. 형식은 다음과 같다.

DROP INDEX 인덱스명;

그럼 실제로 인덱스를 삭제해 보자. 삭제하기 전에 SQLite 명령 .indices를 사용하여 생성된 인덱스를 확인한다.

.indices
sqlite> .indices
nameindex
sqlite> 

nameindex 인덱스를 삭제하려면 다음과 같이 실행한다.

drop index nameindex;
sqlite> drop index nameindex;
sqlite> 

nameindex 인덱스가 삭제되었는지 .indices 명령을 실행하여 생성된 인덱스를 확인한다.

.indices
sqlite> .indices
sqlite> 

nameindex 인덱스가 삭제된 것을 확인할 수 있다.

1.5.8 - SQLite | 트리거(Trigger)

트리거를 생성하면 테이블에 데이터 추가 및 수정 등을 하면 자동으로 다른 SQL 문을 실행할 수 있다. 예를 들어, 테이블의 컬럼 값을 수정하면 자동으로 다른 테이블의 컬럼 값을 같은 값으로 수정하는 등의 처리가 가능하다. 여기에서는 트리거 생성 방법 등에 대해 설명한다.

1.5.8.1 - SQLite | 트리거(Trigger) | 트리거 생성

특정 테이블의 데이터가 수정되면 동시에 다른 테이블의 데이터를 수정하고 싶은 경우가 있다. 이러한 경우에 사용되는 것이 트리거이다. 여기에서는 트리거를 만드는 방법에 대해 설명한다.

트리거 생성

트리거를 생성하여 지정된 SQL 문이 실행될 때 다른 SQL 문을 자동으로 수행할 수 있다. 트리거를 생성하는 형식은 다음과 같다.

CREATE TRIGGER 트리거명 [ BEFORE | AFTER | INSTEAD OF]
 { DELETE | UPDATE [OF 컬럼명, ...] | INSERT } ON 테이블명
 [ FOR EACH ROW | FOR EACH STATEMENT ]
 [ WHERE 조건식 ]
 BEGIN
  SQL문1;
  SQL문2;
  ...
 END;

복잡하게 보이지만, 어떤한 수행이 있어 트리거가 실행 하는지를 지정하는 방법 SQL 문을 실행하는 방법을 작성한다. 수행이 되는 SQL 문은 UPDATE 문, INSERT 문, 그리고 DELETE 문이 있다.

테이블에 대해 UPDATE가 발생했을 때 트리거를 설정하려면 다음과 같이 작성할 수 있다.

CREATE TRIGGER 트리거명 UPDATE ON 테이블명
 BEGIN
  SQL문1;
  SQL문2;
  ...
 END;

또한 특정 컬럼에 UPDATE가 수행되었을 때 트리거를 설정할 수 있다.

CREATE TRIGGER 트리거명 UPDATE OF 컬럼명 ON 테이블명
 BEGIN
  SQL문1;
  SQL문2;
  ...
 END;

마찬가지로 테이블에 DELETE 문이 실행되었을 때와 INSERT 문이 실행되었을 때 트리거를 설정하는 경우는 각각 다음과 같다.

CREATE TRIGGER 트리거명 DELETE OF 컬럼명 ON 테이블명
 BEGIN
  SQL문1;
  SQL문2;
  ...
 END;
CREATE TRIGGER 트리거명 INSERT OF 컬럼명 ON 테이블명
 BEGIN
  SQL문1;
  SQL문2;
  ...
 END;

예를 들어 데이터가 수정되었을 때 실행하는 트리거를 설정해두고, 수정 이전 혹은 이후의 데이터를 받아와서 트리거에서 실행하는 SQL문에서 사용할 수 있다. 이에 대해서는 다음 페이지에서 설명한다.


그러면 실제로 트리거를 만들어 보자. 다음과 같이 두 테이블을 만든다.

create table product(id integer, name text, price integer);
create table log(id integer primary key, act text);
sqlite> create table product(id integer, name text, price integer);
sqlite> create table log(id integer primary key, act text);
sqlite> 

다음은 product 테이블을 대상으로 3개의 트리거를 생성한다. product 테이블에 데이터를 추가/삭제/ 수정 되었을 때 각 트리거가 실행되어 log 테이블 act 컬럼에 로그를 기록한다.

create trigger itrigger insert on product
begin
insert into log(act) values('INSERT Action');
end;
create trigger dtrigger delete on product
begin
insert into log(act) values('DELETE Action');
end;
create trigger utrigger update on product
begin
insert into log(act) values('UPDATE Action');
end;
sqlite> create trigger itrigger insert on product
   ...> begin
   ...> insert into log(act) values('INSERT Action');
   ...> end;
sqlite> 
sqlite> create trigger dtrigger delete on product
   ...> begin
   ...> insert into log(act) values('DELETE Action');
   ...> end;
sqlite> 
sqlite> create trigger utrigger update on product
   ...> begin
   ...> insert into log(act) values('UPDATE Action');
   ...> end;
sqlite> 

그러면 product 테이블에 데이터를 추가하거나 데이터를 수정하거나 삭제했을 때, 트리거가 실행 되었는지 확인한다. 먼저 product 테이블에 데이터를 추가한다. 그러고 log 테이블을 확인한다.

insert into product values (1, 'Book', 15000);

product 테이블에 데이터를 추가할 때 트리거가 실행되어 log 테이블에 데이터를 추가된다.

sqlite> insert into product values (1, 'Book', 15000);
sqlite> 
sqlite> select * from log;
1|INSERT Action
sqlite> 

다음은 product 테이블에 추가한 데이터를 일부 수정한다. 그러고 log 테이블을 확인한다.

update product set price = 25000 where id = 1;

product 테이블의 데이터가 수정 될 때마다 트리거가 실행되어 log 테이블에 데이터를 추가하고 있다.

sqlite> update product set price = 25000 where id = 1;
sqlite> 
sqlite> select * from log;
1|INSERT Action
2|UPDATE Action
sqlite> 

마지막으로 product 테이블에 추가한 데이터를 삭제한다. 그러고 log 테이블을 확인한다.

delete from product where id = 1;

product 테이블의 데이터가 삭제될 때마다 트리거가 실행되어 log 테이블에 데이터를 추가하고 있다.

sqlite> delete from product where id = 1;
sqlite> 
sqlite> select * from log;
1|INSERT Action
2|UPDATE Action
3|DELETE Action
sqlite> 

이렇게 생성된 트리거에 설정한 동작을 했을 때, 각각 트리거에 설정된 SQL 문이 실행되는 것을 확인할 수 있다.

BEFORE 트리거와 AFTER 트리거

예를 들어 데이터가 추가되었을 때에 실행되는 트리거를 생성하는 경우에는 먼저 데이터가 추가되고 나서 트리거에 설정된 SQL 문이 실행되지만, BEFORE 키워드를 지정하면 데이터가 추가 되기 전에 먼저 트리거에 작성된 SQL 문이 실행 된 후에 데이터의 추가가 된다.

형식은 다음과 같다. 트리거명 뒤에 BEFORE를 작성한다. 다음 형식은 INSERT를 작성하는 경우를 예로 들었지만, DELETE와 UPDATE에도 동일하다.

CREATE TRIGGER 트리거명 BEFORE INSERT ON 테이블명
 BEGIN
  SQL문1;
  SQL문2;
  ...
 END;

동일하게 AFTER를 작성할 수도 있지만, AFTER가 기본 동작이기에 명시적으로 작성할 필요는 없다. (그러나 정말 그런지는 확인이 되지 않았다.)

CREATE TRIGGER 트리거명 AFTER INSERT ON 테이블명
 BEGIN
  SQL문1;
  SQL문2;
  ...
 END;

구체적인 예를 들 수 없지만, BEFORE 트리거를 사용할 때는 주의가 필요한다. 아래 공식 사이트에 기재되어 있는 내용을 그대로 게시하도록 하겠다. 가능하다면 AFTER 트리거를 사용하는 것이 좋다는 내용이다.

SQLite Query Language: CREATE TRIGGER

Cautions On The Use Of BEFORE triggers

If a BEFORE UPDATE or BEFORE DELETE trigger modifies or deletes a row that was to have been updated or deleted, then the result of the subsequent update or delete operation is undefined. Furthermore, if a BEFORE trigger modifies or deletes a row, then it is undefined whether or not AFTER triggers that would have otherwise run on those rows will in fact run.

The value of NEW.rowid is undefined in a BEFORE INSERT trigger in which the rowid is not explicitly set to an integer.

Because of the behaviors described above, programmers are encouraged to prefer AFTER triggers over BEFORE triggers.

FOR EACH ROW 및 FOR EACH STATEMENT

트리거를 만들 때 FOR EACH ROW를 지정하면 대상 테이블에 포함된 데이터에 대해 UPDATE 또는 DELETE가 1행이 실행될 때마다 트리거로 설정된 SQL 문이 실행된다. 또한 FOR EACH STATEMENT를 지정하면 몇 행의 데이터에 대한 작업을 수행 되어도 트리거로 설정된 SQL 문은 한 번만 실행되지 않는다.

CREATE TRIGGER 트리거명 INSERT ON 테이블명 FOR EACH ROW
 BEGIN
  SQL문1;
  SQL문2;
  ...
 END;

다만, SQLite에서는 현재 FOR EACH ROW 밖에 대응되지 않는다. 그러기에 FOR EACH ROW를 생략해도 FOR EACH ROW이 설정된 것으로 간주된다.

1.5.8.2 - SQLite | 트리거(Trigger) | 수정 이전과 이후의 데이터 값을 트리거에서 참조

트리거는 데이터가 추가 되었을 때와 수정 되었을 때 지정된 SQL 문을 실행되는데, 새로 추가된 데이터 값이나 수정된 데이터의 수정 이전 및 이후의 값을 트리거 중에 실행되는 SQL문에서 참조 할 수 있다. 여기에서는 수정 이전 및 이후의 데이터 값을 트리거에서 참조하는 방법에 대해 설명한다.

NEW. 컬럼명과 OLD. 컬럼명

새로 추가된 데이터 값이나 수정 되었을 때 데이터의 수정 이전 및 이후의 값은 테이블에 포함 된 데이터의 값을 수정 할 때 다른 테이블에 포함 된 같은 값도 동시에 수정하려는 경우 등에 사용할 수 있다.

컬럼의 값을 참조하려면 트리거에서 실행되는 SQL문 안에 “NEW.컬럼명"과 “OLD.컬럼명"을 사용한다.

NEW.컬럼명
OLD.컬럼명

“NEW.컬럼명"은 INSERT문 또는 UPDATE문에서 트리거가 실행 한 경우에 사용할 수 있다. “OLD.컬럼명"은 DELETE 문 또는 UPDATE 문에서 트리거가 실행 한 경우에 사용할 수 있다. 컬럼명은 INSERT 문이나 DELETE 문에 설정되는 테이블의 컬럼이어야 한다.

간단한 예로 설명보겠다. user 테이블에 새로운 데이터 추가될 때에 실행되는 트리거를 생성한다. 트리거가 실행되면 새로 추가된 데이터의 name 컬럼의 값을 참조 다른 log 테이블에 추가하는 SQL 문을 실행한다. 추가된 데이터의 name 컬럼의 값은 new.name으로 작성하여 참조 할 수 있다.

create trigger mytrigger insert on user
begin
insert into log values (new.name);
end;

이렇게 트리거로 실행되는 SQL 문에서 트리거가 설정된 테이블에 데이터가 추가되거나 삭제된 컬럼의 값을 참조할 수 있다.

DELETE 문의 트리거 예제

DELETE에 대한 트리거를 생성하여 실행해 보겠다. 다음과 같이 두 테이블을 만든다. user 테이블은 고객 정보의 관리 테이블이다. history 테이블은 구매 내역 테이블이다.

create table user (id integer, name text);
create table history (userid integer, goods text, sales integer);
sqlite> create table user (id integer, name text);
sqlite> create table history (userid integer, goods text, sales integer);
sqlite> 

각각의 테이블에 다음 데이터를 저장한다.

insert into user values (1, 'devkuma');
insert into user values (2, 'kimkc');
insert into user values (3, 'araikuma');
insert into history values (1, 'PC', 550000);
insert into history values (2, 'Mouse', 34000);
insert into history values (1, 'Watch', 85000);
insert into history values (3, 'Light', 24000);
insert into history values (2, 'Mobile', 720000);
sqlite> insert into user values (1, 'devkuma');
sqlite> insert into user values (2, 'kimkc');
sqlite> insert into user values (3, 'araikuma');
sqlite> 
sqlite> insert into history values (1, 'PC', 550000);
sqlite> insert into history values (2, 'Mouse', 34000);
sqlite> insert into history values (1, 'Watch', 85000);
sqlite> insert into history values (3, 'Light', 24000);
sqlite> insert into history values (2, 'Mobile', 720000);
sqlite> 

이어서 트리거를 생성한다. user 테이블에서 데이터가 삭제되면 그 고객에 대한 데이터를 history 테이블에서 삭제하는 트리거를 생성한다.

create trigger deleteuser delete on user
begin
delete from history where userid = old.id;
end;
sqlite> create trigger deleteuser delete on user
   ...> begin
   ...> delete from history where userid = old.id;
   ...> end;
sqlite> 

이제 준비는 완료되었다. 그러면 user 테이블에서 고객을 한명을 삭제해 보자. 그러고 history 테이블을 확인해 보면, 그 고객에 대한 기록이 삭제되는 것을 확인할 수 있다.

delete from user where id = 2;
sqlite> delete from user where id = 2;
sqlite> 
sqlite> select * from user;
1|devkuma
3|araikuma
sqlite> 
sqlite> select * from history;
1|PC|550000
1|Watch|85000
3|Light|24000
sqlite> 

UPDATE 문의 트리거 예제

다음은 UPDATE문에 대한 트리거를 생성하여 실행해 보겠다. 다음과 같이 두 테이블을 만든다. product 테이블은 제품 관리 테이블이다. history 테이블은 구매 내역 테이블이다.

create table product (id integer, name text);
create table history (user text, name text, sales integer);
sqlite> create table product (id integer, name text);
sqlite> create table history (user text, name text, sales integer);
sqlite> 

각각의 테이블에 다음 데이터를 저장한다.

insert into product values (1, 'Android');
insert into product values (2, 'iPhone');
insert into product values (3, 'iPad');

insert into history values ('devkuma', 'iPhone', 1390000);
insert into history values ('kimkc', 'iPad', 999000);
insert into history values ('araikuma', 'Android', 1092000);
insert into history values ('happykuma', 'iPhone', 965800);
sqlite> insert into product values (1, 'Android');
sqlite> insert into product values (2, 'iPhone');
sqlite> insert into product values (3, 'iPad');
sqlite> 
sqlite> insert into history values ('devkuma', 'iPhone', 1390000);
sqlite> insert into history values ('kimkc', 'iPad', 999000);
sqlite> insert into history values ('araikuma', 'Android', 1092000);
sqlite> insert into history values ('happykuma', 'iPhone', 965800);
sqlite> 

이어서 트리거를 생성한다. product 테이블의 상품명이 수정되면 그 고객에 대한 데이터를 history 테이블의 같은 상품명도 함께 수정되는 트리거를 생성한다.

create trigger updateproduct update of name on product
begin
update history set name = new.name where name = old.name;
end;
sqlite> create trigger updateproduct update of name on product
   ...> begin
   ...> update history set name = new.name where name = old.name;
   ...> end;
sqlite> 

이제 준비는 완료되었다. 그러면 user 테이블에서 고객을 한명을 삭제해 보자. 그러고 history 테이블을 확인해 보면, 그 고객에 대한 기록이 삭제되는 것을 확인할 수 있다.

이제 준비는 완료되었다. 그러면 product 테이블의 상품명을 하나를 수정해 보자. 그러고 history 테이블을 확인해 보면, 수정된 상품명이 포함된 이력도 새 이름으로 수정되는 것을 확인할 수 있다.

update product set name = 'iPhone 11' where name = 'iPhone';
sqlite> update product set name = 'iPhone 11' where name = 'iPhone';
sqlite> 
sqlite> select * from product;
1|Android
2|iPhone 11
3|iPad
sqlite> 
sqlite> select * from history;
devkuma|iPhone 11|1390000
kimkc|iPad|999000
araikuma|Android|1092000
happykuma|iPhone 11|965800
sqlite> 

이번에는 DELETE 및 UPDATE의 경우만 대한 트리거를 생성하여 실행해봤지만, INSERT의 경우도 마찬가지로 트리거에서 실행하는 SQL 문에서 트리거의 대상이 되는 테이블에 있는 컬럼의 값을 참조할 수 있다.

1.5.8.3 - SQLite | 트리거(Trigger) | 트리거 스키마(구조) 확인

트리거가 어떤 CREATE 문을 사용하여 생성되었는지에 대해 확인하는 방법에 대해 설명한다. sqlite_master 테이블을 이용하는 방법과 .schema 명령을 사용하는 방법의 두 가지가 있다.

sqlite_master 테이블에서 조회

먼저 sqlite_master 테이블을 이용하는 방법이다. 다음과 같이 SQL 문을 실행한다. (쉽게 확인할 수 있도록 먼저 “.mode” 명령으로 mode를 line으로 변경한다.)

select * from sqlite_master;
sqlite> select * from sqlite_master;
table|product|product|3|CREATE TABLE product (id integer, name text)
table|history|history|2|CREATE TABLE history (user text, name text, sales integer)
trigger|updateproduct|product|0|CREATE TRIGGER updateproduct update of name on product
begin
update history set name = new.name where name = old.name;
end
sqlite> 
sqlite> .mode line
sqlite> 
sqlite> select * from sqlite_master;
    type = table
    name = product
tbl_name = product
rootpage = 3
     sql = CREATE TABLE product (id integer, name text)

    type = table
    name = history
tbl_name = history
rootpage = 2
     sql = CREATE TABLE history (user text, name text, sales integer)

    type = trigger
    name = updateproduct
tbl_name = product
rootpage = 0
     sql = CREATE TRIGGER updateproduct update of name on product
begin
update history set name = new.name where name = old.name;
end
sqlite> 

생성된 테이블과 트리거에 대한 데이터를 조회할 수 있다. 여기서 조회한 데이터에서 트리거와 관련된 데이터는 다음과 같다.

    type = trigger
    name = updateproduct
tbl_name = product
rootpage = 0
     sql = CREATE TRIGGER updateproduct update of name on product
begin
update history set name = new.name where name = old.name;
end

type 컬럼은 트리거의 경우 trigger로 표시된다. (테이블의 경우 table이다.) name 컬럼에는 트리거명, tbl_name 컬럼에는 트리거가 지정된 테이블명, sql 컬럼에는 트리거를 생성했던 SQL 문이 표시된다.

트리거에 대한 데이터만 표시하려면 WHERE 절을 사용 다음과 같이 실행한다.

select * from sqlite_master where type = 'trigger';
sqlite> select * from sqlite_master where type = 'trigger';
    type = trigger
    name = updateproduct
tbl_name = product
rootpage = 0
     sql = CREATE TRIGGER updateproduct update of name on product
begin
update history set name = new.name where name = old.name;
end
sqlite> 

.schema 명령을 사용하여 조회

트리거의 스키마에 대한 정보만 조회하고자 한다면 SQLite 명령 .schema에서도 확인할 수 있다.

.schema
.schema? TABLE?

인수를 생략하면 모든 테이블과 트리거의 스키마 정보를 표시한다. 또한 인수에 테이블명을 지정하면 테이블명과 일치하는 테이블과 지정된 테이블명과 관련된 트리거 정보를 표시할 수 있다.

그럼 .schema 명령을 사용하여 조회해 보도록 하자.

.schema
sqlite> .schema
CREATE TABLE product (id integer, name text);
CREATE TABLE history (user text, name text, sales integer);
CREATE TRIGGER updateproduct update of name on product
begin
update history set name = new.name where name = old.name;
end;
sqlite> 

현재 데이터베이스에 생성되어 있는 2개의 테이블과 1개의 트리거에 대한 CREATE 문이 표시되는 것을 확인할 수 있다.

1.5.8.4 - SQLite | 트리거(Trigger) | 트리거 삭제

트리거를 삭제하는 방법을 설명한다. 트리거는 직접 삭제하는 경우뿐만 아니라, 트리거가 설정되어 있던 테이블이 삭제된 경우에도 자동으로 삭제된다.

생성된 트리거 삭제

생성된 트리거를 삭제하려면 DROP TRIGGER 문을 사용한다. 형식은 다음과 같다.

DROP TRIGGER 트리거명;

그럼 실제로 트리거를 삭제해 보자. 삭제하기 전에 SQLite 명령 .schema를 사용하여 생성된 트리거를 확인한다.

.schema
sqlite> .schema
CREATE TABLE product (id integer, name text);
CREATE TABLE history (user text, name text, sales integer);
CREATE TRIGGER updateproduct update of name on product
begin
update history set name = new.name where name = old.name;
end;
sqlite> 

updateproduct 트리거를 삭제한다.

drop trigger updateproduct;
sqlite> drop trigger updateproduct;
sqlite> 

updateproduct 트리거를 삭제되었는지 .schema 명령을 실행하여 생성된 트리거를 확인한다.

.schema
sqlite> .schema
CREATE TABLE product (id integer, name text);
CREATE TABLE history (user text, name text, sales integer);
sqlite> 

updateproduct 트리거가 삭제된 것을 확인할 수 있다.

트리거가 설정된 테이블을 삭제한 경우

트리거가 설정된 테이블이 삭제되면 트리거도 자동으로 삭제된다.

실제로 확인을 해보도록 하겠다. “.schema” 명령으로 현재 생성되어 있는 테이블과 트리거를 표시한다.

.schema
.schema
CREATE TABLE product (id integer, name text);
CREATE TABLE history (user text, name text, sales integer);
CREATE TRIGGER updateproduct update of name on product
begin
update history set name = new.name where name = old.name;
end;
sqlite> 

혹시 위에서 트리거가 없다면 조회된 스키마를 이용해서 새로 만드길 바란다.

updateproduct 트리거는 product 테이블에 설정된 트리거이다. 그러면 product 테이블을 삭제하고 다시 .schema 명령을 실행해 본다.

drop table product;
sqlite> drop table product;
sqlite> 
sqlite> .schema
CREATE TABLE history (user text, name text, sales integer);
sqlite> 

product 테이블을 삭제하면 product 테이블에 설정된 updateproduct 트리거도 자동으로 삭제되는 것을 확인할 수 있다.

1.5.9 - SQLite | 데이터의 추가와 수정, 삭제

SQLite에서 테이블에 데이터를 추가하거나 제거하는 방법에 대해 설명한다.

1.5.9.1 - SQLite | 데이터의 추가와 수정, 삭제 | 데이터 추가 (INSERT)

INSERT 문을 사용하여 테이블에 데이터를 추가하는 방법에 대해 설명한다. 모든 컬럼에 값을 지정하여 데이터를 추가하는 방법 외에 컬럼을 지정하여 데이터를 추가할 수도 있다.

모든 컬럼에 값을 지정하여 데이터 추가하기

테이블에 데이터를 추가하려면 INSERT 문을 사용한다. 몇가지 형식이 있는데, 테이블의 모든 컬럼에 값을 지정하여 데이터를 추가하는 경우의 형식은 다음과 같다.

INSERT INTO 테이블명 VALUES (1, 2, ...);

추가하려는 테이블에 포함된 컬럼 만큼의 값을 지정한다. 값은 작성된 순서대로 컬럼에 저장되기에, 값을 저장할 컬럼의 순서와 동일한 순서로 값을 지정해야 한다.

그러면 실제로 데이터를 추가해 보도록 하겠다. 먼저 다음과 같이 테이블을 만든다.

create table user (id integer, name text, old integer, address text);
sqlite> create table user (id integer, name text, old integer, address text);
sqlite> 

이 테이블에는 4개의 컬럼 id, name, old, address가 있다. 이 테이블에 데이터를 추가하려면 4개의 값을 컬럼의 순서에 따라 작성하여 추가한다. 예를 들어 “1, ‘devakuma’, 25, ‘Seoul’“와 같이 작성한다. 그럼 실제로 데이터를 추가해 보도록 한다.

insert into user values (1, 'devakuma', 25, 'Seoul');
sqlite> insert into user values (1, 'devakuma', 25, 'Seoul');
sqlite> 

위와 같이 에러가 표시되지 않으면 데이터의 추가는 성공이다. 동일하게 데이터를 추가한다.

insert into user values (3, 'kimkc', 32, 'Daejeon');
insert into user values (5, 'ariakuma', 27, 'Busan');
insert into user values (6, 'happykuma', 20, 'Gwangju');
sqlite> insert into user values (3, 'kimkc', 32, 'Daejeon');
sqlite> insert into user values (5, 'ariakuma', 27, 'Busan');
sqlite> insert into user values (6, 'happykuma', 20, 'Gwangju');
sqlite> 

이것으로 테이블에 4개의 데이터가 추가되었다. 그러면 SELECT 문을 사용하여 테이블에서 데이터를 조회해 보자.

select * from user;
sqlite> select * from user;
1|devakuma|25|Seoul
3|kimkc|32|Daejeon
5|ariakuma|27|Busan
6|happykuma|20|Gwangju
sqlite> 

4개의 데이터가 저장되어 있는데, 값을 입력한 순서대로 컬럼에 저장되어 있는 것을 확인할 수 있다.


이에 반해, 컬럼의 수와 추가 데이터의 수가 일치하지 않는 경우에는 에러가 발생한다. 방금 생성한 테이블은 4개의 컬럼이 있는데, 3개의 값만을 지정하여 데이터를 추가하면 “Error: table user has 4 columns but 3 values were supplied"와 같은 에러가 발생한다.

insert into user values (8, 'mykuma', 35);
sqlite> insert into user values (8, 'mykuma', 35);
Error: table user has 4 columns but 3 values were supplied
sqlite>

특정 컬럼에만 값을 지정하고, 다른 컬럼에는 기본값을 저장하려면 아래 설명하는 컬럼을 지정하는 방법을 참고하도록 한다.

특정 컬럼에만 값을 지정하여 데이터 추가하기

앞에서는 모든 컬럼에 값을 지정하는 방법을 데이터를 추가 하였지만, 특정 컬럼에만 값을 지정하고 데이터를 추가할 수도 있다. 이 경우에는 값이 지정되지 않은 컬럼은 기본 값이 저장된다.

컬럼을 지정하는 형식은 다음과 같다.

INSERT INTO 테이블  (컬럼1, 컬럼2, ...) VALUES (1, 2, ...);

값을 저장할 컬럼을 테이블명 뒤에 괄호로 둘려싸서 지정한다. 여러 컬럼이 있는 경우에는 쉼표(,)로 구분하여 작성한다. 그리고 지정된 컬럼의 수 만큼의 값을 지정한다.

데이터가 추가되었을 때, 지정된 컬럼에 값이 저장되지만, 지정되지 않은 컬럼에 DEFAULT 제약 조건이 설정되어 있으면 기본 값이 저장되고 DEFAULT 제약 조건이 설정되어 있지 않으면 NULL이 저장된다. (DEFAULT 제약 조건에 대해서는 “DEFAULT 제약 조건“을 참조한다.)

그러면 실제로 데이터를 추가해 보도록 하겠다. 먼저 다음과 같이 테이블을 만든다.

create table user (id integer, name text, address text default 'no value');
sqlite> create table user (id integer, name text, address text default 'no value');
sqlite>

테이블에는 3개의 컬럼이 있고 address 컬럼에 디폴트 제약이 설정되어 있다. 그러면 데이터를 추가해 보자. 우선 3개의 컬럼에 모두 값을 지정하여 데이터를 추가한다.

insert into user (id, name, address) values (1, 'devkuma', 'Seoul');
sqlite> insert into user (id, name, address) values (1, 'devkuma', 'Seoul');
sqlite> 

데이터 추가가 완료되었다. 모든 컬럼에 값을 지정하여 추가할 경우는 이 페이지의 앞부분에서 설명한 형식으로 다음과 같이 실행했을 경우와 동일하다.

insert into user values (1, 'devkuma', 'Seoul');

다음은 2개의 컬럼에만 값을 지정하여 데이터를 추가해 보자. 첫 번째는 id 컬럼와 name 컬럼만 두 번째는 id 컬럼과 address 컬럼에만 값을 지정하여 데이터를 추가한다.

insert into user (id, name) values (2, 'Busan');
insert into user (id, address) values (3, 'araikuma');
sqlite> insert into user (id, name) values (2, 'Busan');
sqlite> insert into user (id, address) values (3, 'araikuma');
sqlite> 

이것으로 테이블에는 지금 2건과 합해서 3건의 데이터가 추가되었다. 그러면 SELECT 문을 사용하여 테이블에서 데이터를 조회해 보자.

select * from user;
sqlite> .mode column
sqlite> .header on
sqlite> 
sqlite> select * from user;
id          name        address   
----------  ----------  ----------
1           devkuma     Seoul     
2           Busan       no value  
3                       araikuma  
sqlite> 

2번째 행의 데이터를 추가했을 때에는 address 컬럼의 값을 지정하지 않았지만, address 컬럼에 디폴트 제약이 설정되어 있었기 때문에 기본값인 ’no value’가 저장되어 있다. 3번째 행의 데이터를 추가했을 때에는 name 컬럼의 값을 지정하지 않았기 때문에 기본 값 NULL이 저장되어 있다. (NULL은 화면에 아무것도 표시되지 않는다.)

이렇게 테이블의 컬럼 중에 일부 컬럼 만에 값을 지정하여 데이터를 추가 할 수 있다.


또한 데이터를 추가할 때에 작성하는 컬럼의 순서는 테이블에서 정의된 컬럼의 순서대로 할 필요는 없다. 다음의 2건은 동일한 결과이며 에러도 발생하지 않는다.

insert into user (id, name) values (2, 'mykuma');
insert into user (name, id) values ('mykuma', 2);

  다만, 보기 힘들 수 있게 될수 있으므로 특별한 이유가 없다면 테이블에서 정의된 컬럼의 순서대로 작성하길 바란다.

값을 지정하지 않고 데이터 추가하기

값을 1개도 지정하지 않고 데이터를 추가할 수 있다. 이 경우 모든 컬럼에 디폴트 값이 저장된다.

값을 지정하지 않고 데이터를 추가하는 경우의 형식은 다음과 같다.

INSERT INTO 테이블명 DEFAULT VALUES;

그러면 실제로 해보도록 하겠다. 테이블은 이전 전과 동일한 것을 그대로 사용한다.

create table user (id integer, name text, address text default 'no value');

값을 지정하지 않고 아래와 같이 데이터를 추가한다.

insert into user default values;
sqlite> insert into user default values;
sqlite> 

그러면 추가된 데이터를 SELCT 문으로 확인해 본다.

select * from user;
sqlite> select * from user;
id          name        address   
----------  ----------  ----------
1           devkuma     Seoul     
2           Busan       no value  
3                       araikuma  
                        no value  
sqlite> 

추가된 데이터는 값이 지정되어 있지 않으므로 디폴트 제한이 설정되어 있지 않은 id 컬럼 name 컬럼에는 디폴트 값인 NULL이 저장되고, 디폴트 제약이 설정되어 있는 address 컬럼에 설정되어 있는 기본값이 저장이 되었다.

이와 같이 값을 1개나 지정하지 않고 데이터를 테이블에 추가 할 수 있다.

여러 데이터를 하나의 SQL문으로 한번에 추가하기

여러 데이터를 하나의 SQL문으로 한번에 추가할 수 있다. 여러 개의 SQL문을 실행하여 여러 건의 추가할 수도 있지만 한개의 SQL문만으로도 추가할 수도 있다.

여러 데이터를 하나의 SQL문으로 추가하는 경우의 형식은 다음과 같다.

INSERT INTO 테이블명 VALUES (1-1, 2-1, ...), (2-1, 2-2, ...), ...;
INSERT INTO 테이블명 (컬럼1, 컬럼2, ...) VALUES (1-1, 2-1, ...), (2-1, 2-2, ...), ...;

테이블 컬럼의 수 만큼의 값을 지정한 묶음을 쉼표(,)로 구분하여 여러 데이터를 작성한다. 그러고 값을 저장할 컬럼을 지정한 경우라면 지정된 만큼의 값을 지정한 묶음으로 지정한다.

그러면 실제로 데이터를 추가해 보도록 하겠다. 먼저 다음과 같이 테이블을 만든다.

create table user (id integer, name text, old integer);
sqlite> create table user (id integer, name text, old integer);
sqlite> 

이 테이블에는 3개의 컬럼 id, name, old가 있다. 이 테이블에 데이터를 추가하려면 3개의 값을 컬럼의 순서에 따라 작성한 묶음으로 작성한다. 예를 들어 “(1, ‘devakuma’, 25), (3, ‘kimkc’, 32), (5, ‘ariakuma’, 27)“와 같이 작성한다. 그럼 실제로 데이터를 추가해 보도록 한다.

insert into user values
(1, 'devakuma', 25), (3, 'kimkc', 32), (5, 'ariakuma', 27);
sqlite> insert into user values
   ...> (1, 'devakuma', 25), (3, 'kimkc', 32), (5, 'ariakuma', 27);
sqlite> 

이것으로 테이블에 3개의 데이터가 추가되었다. 그러면 SELECT 문을 사용하여 테이블에서 데이터를 조회해 보자.

select * from user;
sqlite> select * from user;
id          name        old       
----------  ----------  ----------
1           devakuma    25        
3           kimkc       32        
5           ariakuma    27        
sqlite> 

4개의 데이터가 저장되어 있는 것을 확인할 수 있다.

이 방법은 프로그램에서 여러 데이터를 매번 DB에 연결하여 INSERT문을 실행하여 발생하는 성능 부하 개선에 활용될 수도 있을거라 생각된다.

1.5.9.2 - SQLite | 데이터의 추가와 수정, 삭제 | 테이블에서 가져온 데이터를 다른 테이블에 추가

테이블에 데이터를 추가할 때에 INSERT 문으로 데이터를 지정하고 추가하는 방법 외에 다른 테이블에 저장되어 있는 데이터를 SELECT 문에서 조회하여 추가 할 수도 있다. 여기에서는 테이블에서 가져온 데이터를 다른 테이블에 추가하는 방법에 대해 설명한다.

다른 테이블에서 가져온 데이터를 테이블에 추가하기

다른 테이블에서 가져온 데이터를 테이블에 추가하는 경우의 형식은 다음과 같다.

INSERT INTO 테이블명 SQL문;

데이터를 추가할 테이블의 컬럼 수와 SQL문으로 조회한 컬럼의 수는 동일해야 한다. 컬럼명은 달라도 상관 없다. 예를 들면 다음과 같다.

INSERT INTO 테이블1 SELECT C1, C2, C3 FROM 테이블2 WHERE 조건식;

위에서는 테이블1에서 정의된 컬럼의 수는 3개여야 한다.

데이터를 추가할 컬럼을 지정할 수도 있다. 형식은 다음과 같다.

INSERT INTO 테이블명 (컬럼명1, 컬럼명2, ...) SQL문;

이 경우에도 데이터를 추가할 컬럼의 수와 SQL문으로 조회한 컬럼의 수는 동일해야 한다. 예를 들면 다음과 같다.

INSERT INTO 테이블1 (N1, N2, N3) SELECT C1, C2, C3 FROM 테이블2 WHERE 조건식;

그럼 실제로 해보도록 하자. 원본으로 사용하는 테이블은 다음과 같이 만들고, 데이터를 추가한다.

create table user (id integer, name text, old integer);
insert into user values (1, 'devkuma', 24);
insert into user values (2, 'kimkc', 31);
insert into user values (3, 'araikuma', 18);
insert into user values (4, 'happykuma', 25);
insert into user values (5, 'mykuma', 19);
sqlite> create table user (id integer, name text, old integer);
sqlite> 
sqlite> insert into user values (1, 'devkuma', 24);
sqlite> insert into user values (2, 'kimkc', 31);
sqlite> insert into user values (3, 'araikuma', 18);
sqlite> insert into user values (4, 'happykuma', 25);
sqlite> insert into user values (5, 'mykuma', 19);
sqlite> 

다음은 데이터를 추가할 테이블을 다음과 같이 만든다.

create table olduser (id integer, name text, address text);
sqlite> create table olduser (id integer, name text, address text);
sqlite> 

user 테이블에서 old 컬럼의 값이 20보다 큰 데이터를 조회하고, id 컬럼와 name 컬럼의 값을 olduser 테이블에 추가한다.

insert into olduser (id, name) select id, name from user where old> 20;
sqlite> insert into olduser (id, name) select id, name from user where old> 20;
sqlite> 

데이터가 추가되었는지 olduser 테이블에서 데이터를 조회해 본다. 아래 실행에서는 비교할 수 있도록 먼저 user 테이블의 데이터를 조회하고 있다.

select * from olduser;
sqlite> select * from user;
id          name        old       
----------  ----------  ----------
1           devkuma     24        
2           kimkc       31        
3           araikuma    18        
4           happykuma   25        
5           mykuma      19        
sqlite> 
sqlite> select * from olduser;
id          name        address   
----------  ----------  ----------
1           devkuma               
2           kimkc                 
4           happykuma      
sqlite> 

이와 같이 원본의 테이블에서 old 컬럼의 값이 20보다 큰 데이터만 조회하여 다른 테이블에 추가할 수 있었다. 또한 데이터를 추가할 때 olduser 테이블의 address 컬럼에 값을 지정하지 않으므로 디폴트 값 NULL이 저장되었다.

1.5.9.3 - SQLite | 데이터의 추가와 수정, 삭제 | 데이터 수정 (UPDATE)

UPDATE 문을 사용하여 테이블에 저장되어 있는 데이터를 새로운 값으로 수정하는 방법에 대해 설명한다.

테이블의 데이터 수정하기

테이블에 저장되어 있는 데이터를 새로운 값으로 수정하려면 UPDATE 문을 사용한다. 형식은 다음과 같다.

UPDATE 테이블명 SET 컬럼명 1 = 값1, 컬럼명2 = 값2, ... WHERE 조건식;

먼저 수정하려는 데이터를 WHERE 절 조건식을 사용하여 지정한다. 조건식에 일치하는 데이터가 여러 건이라면 여러 데이터가 한꺼번에 수정된다. WHERE를 생략하면 테이블의 모든 데이터가 된다.

다음은 어떤 값을 수정할 것인지를 컬럼명과 값으로 지정한다. 여러 컬럼의 값을 한 번에 수정할 수 있다.

그러면 실제로 해보도록 하자. 다음과 같이 테이블을 만든다. 그러고 테이블에 데이터를 추가한다.

create table employee (id integer, name text, unit text, flag text);
insert into employee values (1, 'devkuma', 'Sales', 'Yes');
insert into employee values (2, 'kimkc', 'Office', 'No');
insert into employee values (3, 'araikuma', 'Office', 'Yes');
insert into employee values (4, 'happykuma', 'Tech', 'Yes');
insert into employee values (5, 'mykuma', 'Sales', 'No');
sqlite> create table employee (id integer, name text, unit text, flag text);
sqlite> 
sqlite> insert into employee values (1, 'devkuma', 'Sales', 'Yes');
sqlite> insert into employee values (2, 'kimkc', 'Office', 'No');
sqlite> insert into employee values (3, 'araikuma', 'Office', 'Yes');
sqlite> insert into employee values (4, 'happykuma', 'Tech', 'Yes');
sqlite> insert into employee values (5, 'mykuma', 'Sales', 'No');
sqlite> 

먼저 id 컬럼의 값이 3인 데이터의 name 컬럼의 값을 새로운 값으로 수정하고, 데이터를 조회하여 값이 수정 되었는지 확인한다.

update employee set name = 'raccoon' where id = 3;
select * from employee where id = 3;
sqlite> update employee set name = 'raccoon' where id = 3;
sqlite> 
sqlite> .mode column
sqlite> .header on
sqlite> 
sqlite> select * from employee where id = 3;
id          name        unit        flag      
----------  ----------  ----------  ----------
3           raccoon     Office      Yes       
sqlite> 

다음은 여러 데이터를 한번에 수정해 보자. unit 컬럼의 값이 ‘Office’의 데이터에 대해 unit 컬럼의 값을 ‘Desk’로 변경하고, 데이터를 조회하고 테이블의 값이 수정 되었는지 확인한다.

update employee set unit = 'Desk' where unit = 'Office';
select * from employee;
sqlite> update employee set unit = 'Desk' where unit = 'Office';
sqlite> 
sqlite> select * from employee;
id          name        unit        flag      
----------  ----------  ----------  ----------
1           devkuma     Sales       Yes       
2           kimkc       Desk        No        
3           raccoon     Desk        Yes       
4           happykuma   Tech        Yes       
5           mykuma      Sales       No        
sqlite> 

마지막으로 모든 데이터를 한번에 수정해 보자. flag 컬럼의 값을 ‘unknown’으로 변경하고, 데이터를 조회하고 테이블의 값이 변경되었는지 여부를 확인한다.

update employee set flag = 'unknown';
select * from employee;
sqlite> update employee set flag = 'unknown';
sqlite> 
sqlite> select * from employee;
id          name        unit        flag      
----------  ----------  ----------  ----------
1           devkuma     Sales       unknown   
2           kimkc       Desk        unknown   
3           raccoon     Desk        unknown   
4           happykuma   Tech        unknown   
5           mykuma      Sales       unknown   
sqlite> 

1.5.9.4 - SQLite | 데이터의 추가와 수정, 삭제 | 데이터 삭제 (DELETE)

DELETE 문을 사용하여 테이블에 저장되어 있는 데이터를 삭제하는 방법에 대해 설명한다.

 

테이블의 데이터를 삭제하기

테이블에서 데이터를 삭제하려면 DELETE 문을 사용한다. 형식은 다음과 같다.

DELETE FROM 테이블명 WHERE 조건식;

먼저 삭제하려는 데이터를 WHERE 절 조건식으로 지정한다. 조건식에 일치하는 데이터가 여러 개인 경우에는 여러 데이터를 한꺼번에 삭제된다. WHERE 절을 생략하면 테이블의 모든 데이터가 삭제된다.

그러면 실제로 해보도록 하겠다. 다음과 같이 테이블을 만든다. 그러고 테이블에 데이터를 추가한다.

create table user (id integer, name text, old integer);
insert into user values (1, 'devkuma', 32);
insert into user values (2, 'kimkc', 15);
insert into user values (3, 'araikuma', 18);
insert into user values (4, 'happykuma', 24);
insert into user values (5, 'mykuma', 19);
sqlite> create table user (id integer, name text, old integer);
sqlite> 
sqlite> insert into user values (1, 'devkuma', 32);
sqlite> insert into user values (2, 'kimkc', 15);
sqlite> insert into user values (3, 'araikuma', 18);
sqlite> insert into user values (4, 'happykuma', 24);
sqlite> insert into user values (5, 'mykuma', 19);
sqlite> 

먼저 old 컬럼의 값이 20보다 작은 데이터를 삭제하고, 데이터를 조회하여 데이터가 삭제 되었는지 확인한다.

delete from user where old < 20;
select * from user;
sqlite> delete from user where old < 20;
sqlite> 
sqlite> select * from user;
id          name        old       
----------  ----------  ----------
1           devkuma     32        
4           happykuma   24        
sqlite> 

다음 테이블에 들어있는 데이터를 모두 삭제해 보자. 아래와 같이 실행하면 모든 데이터가 삭제되는 것을 확인할 수 있다.

delete from user;
select * from user;
sqlite> delete from user;
sqlite> 
sqlite> select * from user;
sqlite> 

1.5.10 - SQLite | 데이터 조회

테이블에 저장된 데이터를 조회하는 방법에 대해 설명한다. 조회할 데이터의 조건식을 설정하고 정렬하여 받아오기 등이 있다.

1.5.10.1 - SQLite | 데이터 조회 | 데이터 조회 (SELECT문)

테이블에 저장된 데이터를 조회하려면 SELECT 문을 사용한다. 여기에서는 SELECT 문을 사용하여 데이터를 받아 오는 방법에 대해 설명한다.

SELECT 문을 사용하여 데이터 조회

테이블에 저장된 데이터를 조회하려면 SELECT 문을 사용한다. 기본 형식은 다음과 같다.

SELECT 컬럼명1, 컬럼명2, ... FROM 테이블명;

조회할 테이블명을 FROM 다음에 작성한다. 그리고 테이블에서 어떤 컬럼의 값을 받아 올 것인가를 SELECT 다음에 작성한다. 여러 컬럼의 값을 받아오는 경우는 쉼표(,)로 구분하여 컬럼을 지정한다.

또한 조건식을 지정하고 없으면 테이블에 포함된 모든 데이터를 가져온다. 특정 조건을 만족하는 데이터 만 조회하는 방법은 “조회할 데이터의 조건을 설정 (WHERE 절)“에서 설명한다.

실제로 조회를 해보도록 하자. 먼저 다음과 같이 테이블을 만든다.

create table user (id integer, name text, address text);
sqlite> create table user (id integer, name text, address text);
sqlite> 

INSERT 문을 사용하여 테이블에 데이터를 추가한다.

insert into user values (1, 'devkuma', 'Seoul');
insert into user values (2, 'kimkc', 'Busan');
insert into user values (3, 'araikuma', 'Seoul');
insert into user values (4, 'happykuma', 'Seoul');
insert into user values (5, 'mykuma', 'Daejeon');
sqlite> insert into user values (1, 'devkuma', 'Seoul');
sqlite> insert into user values (2, 'kimkc', 'Busan');
sqlite> insert into user values (3, 'araikuma', 'Seoul');
sqlite> insert into user values (4, 'happykuma', 'Seoul');
sqlite> insert into user values (5, 'mykuma', 'Daejeon');
sqlite> 

그러면 SELECT 문을 사용하여 테이블에서 데이터를 조회해 보도록 하자. user 테이블의 id 컬럼와 name 컬럼의 값을 받아오려면 다음과 같이 실행한다.

select id, name from user;

지정된 테이블의 지정된 컬럼의 값을 받아올 수 있다.

sqlite> select id, name from user;
1|devkuma
2|kimkc
3|araikuma
4|happykuma
5|mykuma

여기서 조회된 데이터의 표시 방법은 SQLite 명령으로 변경할 수 있다. 자세한 내용은 “SQLite 명령 사용"을 참조한다. 이제부터는 다음과 같이 표시 방법을 변경하여 표시한다.

.header on
.mode column