DB 테이블을 생성할 때 데이터의 타입을 정의해 주는 것은 아주 중요하다.

연산이 필요한 데이터가 있을 수도 있고, BLOB같이 많은 공간을 사용하는 데이터 타입은 비효율적일 수도 있다.

결국 DB의 저장과 효율을 높이기 위해서는 알맞는 데이터 타입을 사용할 필요가 있으며 테이블의 사이즈를 줄이고 데이터의 처리 속도를 향상 시킬 수 있다.

DEC(6,2) : 소수점 앞의 숫자는 최대 6개, 소수점 뒤의 숫자는 최대 2개를 의미


*VARCHAR와 CHAR의 차이

- VARCHAR는 길이가 변할 수 있으므로(최대 길이는 유효) 길이가 변할 수 있는 데이터의 타입으로 사용하면 유용하다.(공간 절약)

- CHAR 만약 길이가 변하지 않고, 항상 정해진 길이라면 CHAR를 사용할 수 도 있다.


*참고

작은 따옴표를 사용하는 데이터 타입

-char / varchar / date / datetime / time / timestamp / blob

작은 따옴표를 사용하지 않는 데이터 타입

- dec / int

https://cocodo.tistory.com/11?category=739808



자바 데이터베이스 관련 더 찾은 블로그

https://m.blog.naver.com/PostView.nhn?blogId=printf7&logNo=10170263611&proxyReferer=https%3A%2F%2Fwww.google.com%2F



자바에서 데이터베이스로 쿼리문을 전송할 때 사용할 수 있는 인터페이스가 2가지 있다. Statement와 PreparedStatement이다. 둘다 쿼리 전송기능을 가지고 있지만 차이점이 있다. 어떤 점이 차이가 나고, 어느 인터페이스를 사용하는 것이 좋은지 자세하게 알아보자.

공통점


  1. 두 인터페이스 모두 SQL 질의문을 전달하는 역할을 한다.

  2. 사용시 반드시 try~catch문 또는 throws 처리를 해야한다.


Statement (인터페이스)


  1. Statement 객체는 Statement 인터페이스를 구현한 객체를 Connection 클래스의 createStatement( ) 메소드를 호출함으로써 얻어진다.

  2. Statement 객체가 생성되면 executeQuery( ) 메소드를 호출하여 SQL문을 실행시킬 수 있다. 메소드의 인수로 SQL문을 담은 String객체를 전달한다.

  3. Statement는 정적인 쿼리문을 처리할 수 있다. 즉 쿼리문에 값이 미리 입력되어 있어야 한다.



<Connection 인터페이스 API>





PreparedStatement (인터페이스)


  1. PreparedStatement 객체는 Connection 객체의 preparedStatement( ) 메소드를 사용해서 생성한다. 이 메소드는 인수로 SQL문을 담은 String객체가 필요하다.

  2. SQL문장이 미리 컴파일되고, 실행 시간동안 인수값을 위한 공간을 확보할 수 있다는 점에서 Statement 객체와 다르다. 

  3. Statement 객체의 SQL은 실행될 때 매번 서버에서 분석해야 하는 반면, PreparedStatement 객체는 한 번 분석되면 재사용이 용이하다.

  4. 각각의 인수에 대해 위치홀더(placeholder)를 사용하여 SQL문장을 정의할 수 있게 해준다. 위치홀더는 ? 로 표현된다.

  5. 동일한 SQL문을 특정 값만 바꾸어서 여러 번 실행해야 할 때, 인수가 많아서 SQL문을 정리해야 될 필요가 있을 때 사용하면 유용하다.



<Connection 인터페이스 API>





예제



  • 테이블


1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE TEST 
(
  ID VARCHAR2(20NOT NULL 
, PW VARCHAR2(20NOT NULL 
, NAME VARCHAR2(20NOT NULL 
CONSTRAINT TABLE1_PK PRIMARY KEY 
  (
    ID 
  )
  ENABLE 
);
cs



  • DBConnection.java (오라클 연결을 위한 클래스)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class DBConnection 
{
    public static Connection dbConn;
    
        public static Connection getConnection()
        {
            Connection conn = null;
            try {
                String user = "scott"
                String pw = "tiger";
                String url = "jdbc:oracle:thin:@localhost:1521:orcl";
                
                Class.forName("oracle.jdbc.driver.OracleDriver");        
                conn = DriverManager.getConnection(url, user, pw);
                
                System.out.println("Database에 연결되었습니다.\n");
                
            } catch (ClassNotFoundException cnfe) {
                System.out.println("DB 드라이버 로딩 실패 :"+cnfe.toString());
            } catch (SQLException sqle) {
                System.out.println("DB 접속실패 : "+sqle.toString());
            } catch (Exception e) {
                System.out.println("Unkonwn error");
                e.printStackTrace();
            }
            return conn;     
        }
}
cs



  • StatementTest.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.sql.Statement;
import java.sql.Connection;
import java.sql.SQLException;
 
public class StatementTest 
{
    public static void main(String args[])
    {
        Connection conn = null// DB연결된 상태(세션)을 담은 객체
        Statement stm = null;  // SQL 문을 나타내는 객체
        
        try {
            conn = DBConnection.getConnection();
            stm = conn.createStatement();
            
            String quary = "INSERT INTO TEST VALUES('id1', 'pw1', 'name1')";
            int success = stm.executeUpdate(quary);
            
            if(success > 0)
                System.out.println("데이터 입력 성공");
            else
                System.out.println("데이터 입력 실패");
 
        } catch (SQLException sqle) {
            sqle.printStackTrace();
        }
    }
}
cs



  • PreparedStatementTest.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.PreparedStatement;
 
public class PreparedStatementTest 
{
    public static void main(String args[])
    {
        Connection conn = null// DB연결된 상태(세션)을 담은 객체
        PreparedStatement pstm = null;  // SQL 문을 나타내는 객체
        
        try {
            
            String quary = "INSERT INTO TEST VALUES(?, ?, ?)";
            
            conn = DBConnection.getConnection();
            pstm = conn.prepareStatement(quary);
            
            // 쿼리에 값을 세팅한다.
            // 여기서 1, 2, 3은 첫번째, 두번째, 세번째 위치홀더 라는 뜻
            pstm.setString(1"id2");
            pstm.setString(2"pw2");
            pstm.setString(3"name2");
            
            int success = pstm.executeUpdate();
            
            if(success > 0)
                System.out.println("데이터 입력 성공");
            else
                System.out.println("데이터 입력 실패");
 
            
        } catch (SQLException sqle) {
            sqle.printStackTrace();
        }
    }
}
cs



  • StatementTest.java 실행결과




  • PreparedStatementTest.java 실행결과





어떤 것을 사용하는 것이 좋은가?


PreparedStatement를 사용하는 것이 좋다. PreparedStatement는 동적인 쿼리문을 처리할 수 있으므로 같은 SQL문에서 값만 변경하여 사용한다던가 인수가 많은 경우에 사용하기 좋다. 또한 미리 컴파일되기 때문에 수행 속도가 Statement보다 빠른 장점이 있다.


Statement 객체는 쿼리 실행시 값에 작은따옴표( ' )가 포함되어 있으면 작은따옴표를 두 개( ' ' ) 표시해야 한다. 예를 들어 입력할 값이 I ' am 이라고 하자. 그러면 쿼리문 작성시에 값을 I ' ' am 이렇게 입력해야 한다. 그러나 PreparedStatement 객체는 작은따옴표 문제를 쿼리 실행시 자동으로 처리하므로 신경쓸 필요가 없다는 장점이 있다.



출처: https://all-record.tistory.com/79 [세상의 모든 기록]

http://rapapa.net/?p=311


Mysql Join 해부(Left, Right, Outer, Inner Join)

Mysql DB를 다룰 때 초보 수준에서 약간 중급으로 넘어가면서 흔히들 많이 어려워 하는 것이 Join 구문입니다.

먼저, 아래와 같은 테이블 두개가 있다고 합시다.

+————+————–+——+
| name | phone | pid |
+————+————–+——+
| Mr Brown | 01225 708225 | 1 |
| Miss Smith | 01225 899360 | 2 |
| Mr Pullen | 01380 724040 | 3 |
+————+————–+——+

+——+——+———————-+
| pid | spid | selling |
+——+——+———————-+
| 1 | 1 | Old House Farm |
| 3 | 2 | The Willows |
| 3 | 3 | Tall Trees |
| 3 | 4 | The Melksham Florist |
| 4 | 5 | Dun Roamin |
+——+——+———————-+

두 테이블은 pid 칼럼으로 엮여 있습니다.

먼저 두 테이블을 pid가 같은 것을 조건으로 일반적인 Join을 걸면 결과는 아래와 같습니다.

 

+———–+————–+———————-+
| name | phone | selling |
+———–+————–+———————-+
| Mr Brown | 01225 708225 | Old House Farm |
| Mr Pullen | 01380 724040 | The Willows |
| Mr Pullen | 01380 724040 | Tall Trees |
| Mr Pullen | 01380 724040 | The Melksham Florist |
+———–+————–+———————-+

pid가 같은 조건이 있는 줄은 두 테이블 모두에서 모조리 나오게 됩니다.
어느테이블이 기준이랄 것이 없이 양쪽에서 줄들이 추가되는 형국이죠. 이 때 서로 같은 것이 존재하지 않는 줄은 아예 출력이 되지 않습니다.

반면, Left 혹은 right join은 기준 테이블, 즉 반드시 출력되는 테이블을 잡아 줍니다.
위의 SQL 구문에서 Left Join을 걸어 보면 아래와 같은 결과가 나옵니다.

+————+————–+———————-+
| name | phone | selling |
+————+————–+———————-+
| Mr Brown | 01225 708225 | Old House Farm |
| Miss Smith | 01225 899360 | NULL |
| Mr Pullen | 01380 724040 | The Willows |
| Mr Pullen | 01380 724040 | Tall Trees |
| Mr Pullen | 01380 724040 | The Melksham Florist |
+————+————–+———————-+

이 때 Left Join이기 때문에 왼쪽 테이블이 기준이 됩니다.
따라서, 왼쪽 테이블의 모든 Row가 결과값에 반드시 한줄 이상 나오는 보장을 받게 됩니다. 왼쪽 테이블(demo_people)에 해당하는 오른쪽 테이블의 pid가 여러개일경우 위와 같이 여러줄이 나옵니다.

반면, right join은 left join과 반대로 기준이 오른쪽 테이블입니다.
오른쪽 테이블은 반드시 한줄 이상 나오는 보장을 받게 되는 것이죠.
결과 값을 한번 보시죠.

 

+———–+————–+———————-+
| name | phone | selling |
+———–+————–+———————-+
| Mr Brown | 01225 708225 | Old House Farm |
| Mr Pullen | 01380 724040 | The Willows |
| Mr Pullen | 01380 724040 | Tall Trees |
| Mr Pullen | 01380 724040 | The Melksham Florist |
| NULL | NULL | Dun Roamin |
+———–+————–+———————-+

왼쪽 테이블(demo_people)에 해당 pid 값이 같은 줄이 없어도,
오른쪽 테이블(demo_property)이 모두 나와야 하기 때문에, null, null 값이 출력되게 된 것입니다.

INNER JOIN은 JOIN과 같고,
LEFT OUTER JOIN은 LEFT JOIN과 같고,
RIGHT OUTER JOIN은 RIGHT JOIN과 같습니다.


데이터베이스 설계 순서는 요구분석 - 개념적설계 - 논리적설계 - 물리적설계 - 구현 순으로 이뤄집니다.




1. 요구조건 분석 / 명세

데이터베이스의 사용자, 사용목적, 사용범위, 제약조건 등에 대한 내용을 정리하고 명세서를 작성합니다.



2. 개념적 설계 (E-R모델)

정보를 구조화 하기 위해 추상적 개념으로 표현하는 과정으로 개념 스키마 모델리오가 트랜잭션 모델링을 병행하고, 요구조건 분석을 통해 DBMS 독립적인 E-R 다이어그램을 작성합니다.



3. 논리적 설계 (데이터 모델링)

자료를 컴퓨터가 이해할 수 있도록 특정 DBMS의 논리적 자료 구조로 변환하는 과정입니다. 관계형 데이터베이스인 경우 이 단계에서 테이블을 설계하고, 정규화 과정을 거치게 됩니다.



2. 물리적 설계 (데이터 구조화)

논리적 구조로 표현된 데이터를 물리적 구조의 데이터로 변환하는 과정입니다. 데이터베이스 파일의 저장 구조 및 액세스 경로, 인덱스의 구조와 저장 레코드의 크기, 순서, 접근 경로 등을 결정하며, 반응시간, 공간활용도, 트랜잭션 처리량을 고려하여 설계를 하여야 합니다.



2. 데이터베이스 구현

앞선 설계 단계에서 도출된 데이터베이스 스키마를 실제 파일로 생성하는 단계입니다. 특정 DBMS에서 데이터베이스 스키마를 생성한 후 데이터를 입력하며, 응용 프로그램에서 사용하기 위한 트랜잭션을 생성합니다. 


LIMIT 과 OFFSET은 보통 쿼리의 pagination을 개발할 때 주로 사용됩니다.


1. 처음 10개의 ROW를 반환

SELECT * FROM test LIMIT 10;

2. 위 SQL과 아래의 SQL이 같은 결과

SELECT * FROM test LIMIT 10 OFFSET 0;

3. 11번째부터 10개의 ROW를 반환

SELECT * FROM test LIMIT 10 OFFSET 10;

출처는 권남님 블로그 입니다.  http://kwonnam.pe.kr/wiki/database/mysql/basic

2019년 5월 13일 14:44분에 수정된 글


MySQL 기본 명령어 정리

MySQL의 기본 명령어와 SQL문 정리. 내가 RDBMS에 완전 문외한이기 때문에 기본 SQL쿼리까지…

Redhat Linux 7.3, MySQL 3.23.58 에서 테스트함.

설치는 Redhat 기본 제공 RPM으로 했다.

데이터베이스 접속

mysql -u 사용자명 -p dbname

설치 직후에는 root 사용자에 비밀번호가 없으므로 다음과 같이 접속하여 MySQL을 관리할 수 있다.

mysql -u root mysql

외부 서버에서 접속이 불가하면 MySQL 설정에서 bind-address 항목을 살펴본다.

원격 서버 접속

# hostname:3306
mysql --protocol=tcp -h 'hostname' -P 3306 -u [username]  -p [database-name]

비밀번호 변경

MySQL을 설치한 직후에는 root 계정에 암호가 지정되어 있지 않다. 다음 세가지 방법으로 비밀번호를 변경 할 수 있다.

# mysqladmin 이용
mysqladmin -u root password 새비밀번호
# update문 이용
mysql -u root mysql
----
mysql> UPDATE user SET password=password('새비밀번호') WHERE user='root';
mysql> FLUSH PRIVILEGES;
# Set Password 이용
mysql> SET PASSWORD FOR root=password('새비밀번호');

일단 root 비밀번호가 설정된 상태에서는 mysql이나 mysqladmin 명령을 실행할 때 -p 옵션을 붙여주고 기존 비밀번호를 입력해야만 한다.

사용자 확인

-- 현재 사용자 목록
use mysql;
select User,Host from user;

-- 권한 확인 'username'@'hostname'
SHOW GRANTS FOR 'username'@'%' 

사용자 추가 / 권한

-- 생성 후 필수 권한만 부여. - 실서비스 계정은 가급적 이렇게 필요 권한만 부여할 것.
CREATE USER username@localhost IDENTIFIED BY 'userpassword'
GRANT SELECT,INSERT,UPDATE,DELETE ON dbname.* TO 'username'@'localhost';
 
-- 생성과 전체 권한 일괄 부여
GRANT ALL PRIVILEGES ON dbname.* TO username@localhost IDENTIFIED BY 'password';

username 이라는 사용자를 password라는 비밀번호를 갖도록 하여 추가한다. username은 dbname이라는 데이타베이스에 대해 모든 권한을 가지고 있다. username 사용자는 로칼 호스트에서만 접속할 수 있다. 다른 호스트에서 접속하려면

GRANT ALL PRIVILEGES ON dbname.* TO username@'%' IDENTIFIED BY 'password';

위를 또한 번 실행한다. '%'에서 홑따옴표를 주의한다.

특정 권한만 주려면, 아래와 같은 형태로 권한을 나열한다.

GRANT INSERT, UPDATE, SELECT, DELETE,SHOW DATABASES, SHOW VIEW ON dbname.* TO username@'localhost' IDENTIFIED BY 'pwd';
-- 권한 뺐기
REVOKE CREATE,DROP,ALTER,CREATE VIEW,ALTER ROUTINE,CREATE ROUTINE
  ON dbname.* 
  FROM 'user'@'%';

MySQL error 1045(28000): Access denied for user ...에 따르면 '%'란 localhost 를 제외한 모든 호스트를 뜻한다고 한다. 즉, localhost에 대해서는 명시적으로 따로 권한을 지정해야 한다.

CREATE USER를 통한 사용자 생성이 안될 경우에는 mysql - ERROR 1396 (HY000): Operation CREATE USER failed for 'jack'@'localhost' 참조하여, 먼저 생성하려던 사용자를 drop 하고 재시도 해본다. 이 현상이 나타나는 이유는, CREATE USER/GRANT 명령으로 사용자와 권한을 추가/관리해야 하는데 mysql.dbmysql.user 테이블을 직접 조작하다가 일관성이 깨졌기 때문이다. 가급적 mysql의 계정/권한 테이블에 대한 직접 조작은 하지 말아야 한다.

drop user admin@localhost;
flush privileges;
create user admin@localhost identified by 'admins_password'

불필요한 사용자 삭제는

mysql> DLETE FROM USER WHERE USER='username';
mysql> FLUSH PRIVILEGES;

데이터베이스 생성/보기

# 데이터베이스를 생성하고,
mysql> CREATE DATABASE dbname;
 
mysql> CREATE DATABASE dbname CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
 
# 현재 존재하는 데이터베이스 목록을 보여준다.
mysql> SHOW DATABASES;
 
# 특정 데이타베이스를 사용하겠다고 선언한다.
mysql> USE dbname;
 
# 쓸모 없으면 과감히 삭제한다.
mysql> DROP DATABASE [IF EXISTS] dbname;

IF EXISTS 옵션은 비록 데이타베이스가 없더라도 오류를 발생시키지 말라는 의미이다.

테이블 생성/보기

테이블을 생성하고,

mysql> CREATE TABLE tablename (
  column_name1 INT PRIMARY KEY AUTO_INCREMENT,
  column_name2 VARCHAR(15) NOT NULL,
  column_name3 INT
) ENGINE=INNODB;

현재 데이타베이스의 테이블 목록을 보고

mysql> SHOW TABLES;

테이블 구조를 살펴본다.

mysql> EXPLAIN tablesname;
혹은
mysql> DESCRIBE tablename;

이름을 잘못 지정했으면 이름을 변경할 수도 있다.

mysql> RENAME TABLE tablename1 TO tablename2[, tablename3 TO tablename4];

필요 없으면 삭제한다.

mysql> DROP TABLE [IF EXISTS] tablename;

현재 상태 보기

mysql> STATUS
 
--------------
mysql Ver 11.18 Distrib 3.23.58, FOR pc-linux (i686)
 
Connection id: 26
CURRENT DATABASE: study
CURRENT USER: study@localhost
CURRENT pager: stdout
USING OUTFILE: ''
Server version: 3.23.58
Protocol version: 10
Connection: Localhost via UNIX socket
Client characterset: latin1
Server characterset: euc_kr
UNIX socket: /var/lib/mysql/mysql.sock
Uptime: 2 hours 9 MIN 59 sec
 
Threads: 1 Questions: 160 Slow queries: 0 Opens: 28 FLUSH TABLES: 1
OPEN TABLES: 1 Queries per SECOND avg: 0.021
--------------

호스트별 커넥션 갯수

SELECT SUBSTRING_INDEX(HOST,':',1) AS host, COUNT(*) AS cnt
FROM information_schema.processlist
GROUP BY SUBSTRING_INDEX(HOST,':',1);

INSERT

mysql> INSERT INTO tablename VALUES(1,2, ...);
혹은
mysql> INSERT INTO tablename (col1, col2, ...) VALUES(1,2, ...);

SELECT

mysql> SELECT col1, col2, ... FROM tablename;

컬럼명을 *로 하면 모든 컬럼 의미.

mysql> SELECT col1 AS '성명', col2 AS '국어점수' FROM grade;

컬럼의 이름을 바꿔서 출력.

mysql> SELECT * FROM tablename ORDER BY col1 DESC;
mysql> SELECT col1, korean + math english AS '총점' FROM tablename ORDER BY '총점' ASC;

DESC는 내림차순 ASC는 오름차순.

mysql> SELECT * FROM grade WHERE korean < 90;

결과중 처음부터 10개만 가져오기

mysql> SELECT * FROM grade LIMIT 10;

결과중 100번째부터 10개만 가져오기. 첫번째 레코드는 0번 부터 시작한다.

mysql> SELECT * FROM grade LIMIT 100, 10;

UPDATE

mysql> UPDATE tablename SET col1=새값 WEHER 조건

DELETE

mysql> DELETE FROM tablename WEHRE 조건

DELETE with JOIN

-- DELETE 바로 뒤에 삭제할 테이블을 적는다.
-- INNER JOIN
DELETE T1, T2
FROM T1
INNER JOIN T2 ON T1.key = T2.key
WHERE condition;
 
-- LEFT JOIN
DELETE T1
FROM T1 
LEFT JOIN T2 ON T1.key = T2.key 
WHERE T2.key IS NULL;

mysql에서 쿼리 결과 세로로 보기

-E 옵션을 줘서 실행한다.

mysql -E -u root -p mysql

mysql에서 발생한 오류나 경고 다시 보기

mysql> SHOW errors;
mysql> SHOW warnings;

show processlist

SHOW FULL processlist;
SHOW FULL processlist\G;

MySQL 버전 알아보기

SHOW VARIABLES LIKE "%version%";

CREATE TABLE 구문 보기

SHOW CREATE TABLE [테이블이름]\G

테이블/컬럼 정보보기

-- 전체 테이블 정보를 주석까지 포함해 보여준다.
SHOW TABLE STATUS;

SHOW TABLE STATUS like 'member%'; -- 테이블 이름 매칭 조건

-- 테이블의 컬럼 정보를 주석까지 포함해 보여준다.
SHOW FULL COLUMNS FROM [테이블이름];

테이블/컬럼의 주석(Comment) 지정

-- 테이블의 주석 변경
ALTER TABLE [테이블이름] COMMENT = '테이블설명';

-- 컬럼의 주석 변경
ALTER TABLE [테이블이름] CHANGE COLUMN [컬럼이름] [새컬럼이름] ...여러 속성... COMMENT '새 주석';

테이블과 컬럼의 전체 정보

information_schema 의 TABLESCOLUMNS를 쿼리하여 테이블, 컬럼 정보를 살펴볼 수 있다.

select t.TABLE_SCHEMA, t.TABLE_NAME, c.COLUMN_NAME, c.COLUMN_TYPE, c.COLUMN_COMMENT from `TABLES` t
	inner join `COLUMNS` c on t.TABLE_NAME = c.TABLE_NAME
order by t.TABLE_SCHEMA, t.TABLE_NAME

Group By 의 비 Grouping 컬럼 정보 concat

SELECT keyword, GROUP_CONCAT( syn SEPARATOR  '-' )  -- syn 컬럼들이 '-' 로 묶인 문자열로 나온다.
FROM syn_common
GROUP BY keyword


+ Recent posts