'MySQL'에 해당되는 글 9건

  1. 2009.01.07 Mysql Account 생성
  2. 2009.01.07 root 사용자 password 바꾸기
  3. 2009.01.07 mysql 동시접속자수
  4. 2009.01.07 mysql
  5. 2009.01.07 Mysql Data Type 1
  6. 2009.01.07 mysql query
  7. 2009.01.07 mysql password 잊어버렸을때
  8. 2009.01.07 mysql 백업
  9. 2008.07.10 tomcat + mysql 한글깨짐

Mysql Account 생성

sql 2009. 1. 7. 12:25
GRANT 명령으로 DB 사용자 계정만들기...

예전 버젼이 낮을때 까정.. insert 문으로 사용자 계정을 만들었지요..
이젠.. grant 명령으로 간단히 만드세요..^^

mysql> create database 디비명;

mysql> GRANT ALL PRIVILEGES ON 디비명.* TO 아이디@localhost
     - > IDENTIFIED BY '패스워드';

디비명.* 는 해당 디비에 대한 모든 권한을 준다는 것입니다.
디비명을 * 로 하면.. 그 사용자는 모든 권한을 갖게되겠지요..

ALL PRIVILEGES 키워드를 일부 쿼리 유형으로 바꿔주면.. 해당 쿼리만 사용할수 있읍니다.
예로
mysql> GRANT SELECT,INSERT,UPDATE ON 디비명.* TO .............
하면.. 사용자는 SELECT,INSERT,UPDATE 쿼리만 사용할수
있다는 의미겠지요.. ^^ 

'sql' 카테고리의 다른 글

JOIN  (0) 2009.01.08
DB Design시 고려해야 할 제약성(Constraint) 정의  (0) 2009.01.07
root 사용자 password 바꾸기  (0) 2009.01.07
mysql 한글지원설정  (0) 2009.01.07
mysql 동시접속자수  (0) 2009.01.07
Posted by 으랏차
,
=======================================================

  MySQL 의 root 사용자 암호 바꾸기

=======================================================

MySQL 초기 설치시 관리자 암호는 설정 되어져 있지 않다.실질적으로
서비스 할경우엔 반드시 관리자(root) 암호를 설정해야 한다.

root 암호 설정하는 방법에는 3가지가 있다.

1. UPDATE 문 이용하기
2. SET PASSWORD 이용하기
3. mysqladmin 이용하기

▶ UPDATE 문 이용하기

$ mysql -u root mysql

mysql> update user set password=password('new-passwd') where user='root';
mysql> flush privileges;

update 문 이용하여 암호를 변경할땐 꼭 flush privileges; 를 실행
하여 변경된 내용을 적용해야 한다. MYSQL 에서 사용자 권한에 관한
내용은 MYSQL 실행시 메모리에 불러놓고 이용되는데 이에 관해 변경
된 내용이 있을땐 반드시 서버에 변경된 내용을 갱신하라는 명령을
전달해야 한다.


▶ SET PASSWORD 이용하기

mysql> set password for root=password('new-passwd');

이 방법은 flush privileges 가 필요 없다.

▶ mysqladmin 이용하기

root 암호 초기 설정 시 :

$ mysqladmin -u root password new-passwd

root 암호 변경 시 :

$ mysqladmin -u root -p password new-passwd
Enter password:

'sql' 카테고리의 다른 글

DB Design시 고려해야 할 제약성(Constraint) 정의  (0) 2009.01.07
Mysql Account 생성  (0) 2009.01.07
mysql 한글지원설정  (0) 2009.01.07
mysql 동시접속자수  (0) 2009.01.07
mysql tunning  (2) 2009.01.07
Posted by 으랏차
,

mysql 동시접속자수

sql 2009. 1. 7. 12:23
리눅스 Q&A자료실에 올라왔던 내용이기도 합니다.

인터넷 사용자들이 많아지면서 인기있는 웹 사이트에서는

Max connections 에러가 발생되는 것을 볼 수있을겁니다.

우선, MySQL은 동시에 연결될 수 있는 클라이언트의 수가

100입니다.

이런 에러가 발생한다면 먼저 접속되어있는 클라이언트의 수를
확인하셔야겠죠. 확인방법은 아래와 같습니다.
$ mysqladmin -u root -p variables | grep max_connection

| max_connections                 | 100

이제는, 클라이언트의 동시 접속자를 늘리는 명령입니다.
먼저 mysqld - 이 데몬을 kill 하셔야 겠죠...
$ safe_mysqld -O max_connections=200 &
참고로, 리눅스나 솔라리스 계열에서는 클라이언트의 동시 접속자수가 500 ~ 1,000 까지 가능하다고 합니다.

이렇게 하신 후 다시 확인합니다.
$ mysqladmin -u root -p variables | grep max_connection

그럼 제한되어 있지만 원하시는 만큼의 동시접속자 수를

늘리시게 된겁니다.

'sql' 카테고리의 다른 글

root 사용자 password 바꾸기  (0) 2009.01.07
mysql 한글지원설정  (0) 2009.01.07
mysql tunning  (2) 2009.01.07
mysql  (0) 2009.01.07
Mysql Data Type  (1) 2009.01.07
Posted by 으랏차
,

mysql

sql 2009. 1. 7. 11:52
## 원본 : mysql 3.21 Reference Manual PostScript 매뉴얼
(현재 최신 매뉴얼은 3.22 레퍼런스 매뉴얼이며 3.22.14b-gamma입니다.)

5, 6, 9, 10, 11, 12, 13, 15, 17, 18장 번역 : 문태준(taejun@hitel.net)
18장 중 C-API 부분 번역 : 권용찬 (golmong@cre.co.kr)


이메일 : taejun@hitel.net, taejun@taejun.pe.kr
웹 : http://taejun.pe.kr (Mysql 관련 자료가 모아져 있습니다)

자료를 공유하기 편하도록 홈페이지를 열었습니다.
많이 애용해주시면 감사~하겠습니다.


개인적인 질문은 각 통신망의 리눅스 동호회나 인터넷을 참고하세요.

**로 묶인 것은 제가 주를 단 것입니다.

설치와 관련된 부분은 kldp.org의 database 항목에서 관련된 정보가 있으므로 참고하시면 됩니다.



0. 목차
1. MySQL 일반 정보
2. MySQL 메일링 리스트 및 질문 요청 방법, 에러(버그) 알리기
3. 라이센싱 / mysql에 지불해야 할 경우
4. mysql 설치
5. mysql의 표준 호환성
6. mysql 접근 권한 시스템
7. mysql 언어 레퍼런스
8. SQL 질의 예제
9. mysql 서버 기능
10. mysql의 최대 성능 향상 방법
11. mysql 벤치마크
12. mysql 유틸리티
13. 테이블 유지보수 및 파손 복구에 isamchk 사용하기
14. mysql에 새로운 기능 추가
15. mysql ODBC 지원
16. 일반적인 문제 및 에러
17. 일반적인 문제 해결 방법
18. mysql 클라이언트 툴과 API
19. 다른 데이터베이스와 비교

부록 A ; MYSQL 사용자
부록 B ; 개발 프로그램
부록 C ;MYSQL 개발자
부록 D ; MYSQL 버전업 역사
부록 E ; MYSQL의 알려진 에러 및 설계에서 부족한 부분
부록 F ; MYSQL에 장래 추가할내용(The TODO)
부록 G ; 다른 시스템에 포팅하기
부록 H ; MYSQL 정규표현식 설명
부록 I ; Unireg 설명
부록 J ; 비MS 운영체제에서 MYSQL 서버 라이센스
부록 K ; MS 운영체제에서 MYSQL 라이센스
SQL 명령, 타입, 함수 인덱스
개념 인덱스

1. MySQL 일반 정보
1.1 MYSQL은 무엇인가?
1.2 매뉴얼에 관하여
        1.2.1 매뉴얼에서 쓰인 규정
1.3 MYSQL 역사
1.4 주요특징
1.5 안정성
1.6 2000년 문제
1.7 일반적인 SQL 정보 및 참고자료
1.8 관련 링크

2. MySQL 메일링 리스트 및 질문 요청 방법, 에러(버그) 알리기
2.1 mysql 메일링 리스트
2.2 질문하기, 버그 알리기
2.3 버그나 문제를 알리는 방법
        2.3.1 mysql이 손상되었을때
2.4 메일링리스트에서 질문 답변에 대한 안내

3. 라이센싱 / mysql에 지불해야 할 경우
3.1 mysql 비용
3.2 상업적인 지원 방는 방법
        3.2.1 기본 이메일 지원
        3.2.2 확장 이메일 지원
        3.2.3 login 지원
        3.2.4 확장 login 지원

3.3 라이센싱이나 지원에 지불하는 방법
3.4 라이센싱/지원 관련 정보 담당자
3.5 사용자 저작권 관련
3.6 mysql에 요금을 내지 않고 상업적으로 판매하는 경우
3.7 mysql을 사용해야 설정할 수 있는 제품을 판매하는 경우
3.8 mysql을 사용하여 상업적인 웹 서버를 운영하는 경우
3.9 상업용 Perl/Tcl/PHP/기타 애플리케이션을 판매하는 경우
3.10 앞으로의 라이센싱 변화

4. mysql 설치
4.1 mysql 구하기
4.2 mysql에서 지원하는 운영체제
4.3 사용할 수 있는 mysql 버전
4.4 업데이트 방법 및 시기
4.5 설치 개괄
4.6 바이너리로 설치하기
        4.6.1 클라이언트 프로그램 만들기
        4.6.2 시스템 관련 참고사항
                4.6.2.1 리눅스
                4.6.2.2 HP-UX
4.7 소스로 설치하기
        4.7.1 빠른 설치
        4.7.2 패치 적용하기
        4.7.3 일반 설정 옵션
4.8 컴파일에서 문제점이 생길때
4.9 MIT-pthreads 주의사항
4.10 펄 설치
        4.10.1 펄 DBI/DBD 인터페이스 사용시 문제점
4.11 시스템 관련 참고사항
        -솔라리스, 솔라리스 x86, 선OS4, 알파-DEC-유닉스, 알파-DEC-OSF1,
         SGI-IRIX, FreeBSD, BSD/OS 2, SCO, SCO Unixware 7.0, IBM-AIX, HP-UX
        -리눅스 : 리눅스-x86, 레드햇 5.0/5.1, 리눅스-스팍,리눅스-알파,MkLinux
         리눅스 RPM
4.12 TcX 바이너리
4.13 Win32 관련사항
4.14 설치후 셋업 및 테스트
        4.14.1 mysql_install_db 실행시 문제점
        4.14.2 mysql 서버 실행시 문제점
        4.14.3 자동 시작/정지 시키기
        4.14.5 옵션 파일
4.15 업그레이드/다운그레이드할때의 주의사항
        4.15.1 3.21에서 3.22로 업그레이드
        4.15.2 3.20에서 3.21로 업그레이드
        4.15.3 다른 아키텍쳐로 업그레이드

5. mysql의 표준 호환성
5.1 mysql의 ANSI SQL92 확장 부분
5.2 mysql에서 빠진 기능
        5.2.1 sub-selects
        5.2.2 SELECT INTO TABLE
        5.2.3 트랜잭션
        5.2.4 저장 프로시져/트리거
        5.2.5 외래키
                5.2.5.1 외래키 사용하지 않는 이유
        5.2.6 뷰
        5.2.7 '--' 로 주석문 시작
5.3 mysql에서 따르고 있는 표준
5.4 BLOB/TEXT 타입의 제한사항
5.5 COMMIT-ROLLBACK 없이 사용하는 방법

6. mysql 접근 권한 시스템
6.1 접근 권한 시스템이란 무엇인가
6.2 mysql 서버에 연결하기
        6.2.1 비밀번호를 안전하게 유지하기
6.3 mysql에서 제공하는 권한
6.4 권한 시스템 작동 방식
6.5 접근 제어 1 : 연걸 인증
6.6 접근 제어 2 : 요청 인증
6.7 권한변경시 적용 방법
6.8 초기 mysql 권한 설정
6.9mysql에 새로운 사용자 권한 추가
6.10 비밀번호 설정 방법
6.11 접근 금지 에러가 나는 이유
6.12 크랙커에 대비하여 mysql을 안전하게 만드는 방법

7. mysql 언어 레퍼런스
7.1 문자 : 문자열과 숫자 기록하기
        7.1.1 문자열
        7.1.2 숫자
        7.1.3 NULL 값
        7.1.4 데이터베이스, 테이블, 인덱스, 컬럼, 알리아스 이름
                7.1.4.1 이름에서 대소문자 구별
7.2 컬럼 타입
        7.2.1 컬럼 타입 저장 요구량
        7.2.2 숫자 타입
        7.2.3 날짜/시간 타입
                7.2.3.1 DATETIME, DATE, TIMESTAMP 타입
                7.2.3.2 TIME 타입
                7.2.3.3 YEAR 타입
        7.2.4 문자열 타입
                7.2.4.1 CHAR, VARCHAR 타입
                7.2.4.2 BLOB, TEXT 타입
                7.2.4.3 ENUM 타입
                7.2.4.4 SET 타입
        7.2.5 컬럼에 적절한 타입 고르기
        7.2.6 컬럼 인덱스
        7.2.7 다중 컬럼 인덱스
        7.2.8 다른 데이터베이스 엔진의 컬럼 타입 사용
7.3 SELECT 와 WHERE 문에서의 함수(functions)
        7.3.1 그룹(Group)으로 묶는 기능
        7.3.2 산술연산자
        7.3.3 비트(Bit) 함수
        7.3.4 논리(logical) 연산자
        7.3.5 비교 연산자
        7.3.6 문자열 비교 함수
        7.3.7 흐름 제어(control flow) 함수
        7.3.8 산술 함수
        7.3.9 문자열 함수
        7.3.10 날짜/시간 함수
        7.3.11 기타 함수
        7.3.12 GROUP BY 문에서 사용하는 함수
7.4 CREATE DATABASE
7.5 DROP DATABASE
7.6 CREATE DATABASE
        7.6.1 자동 컬럼 변환
7.7 ALTER TABLE
7.8 OPTIMIZE TABLE
7.9 DROP TABLE
7.10 DELETE
7.11 SELECT
7.12 JOIN
7.13 INSERT
7.14 REPLACE
7.15 LOAD DATA INFILE
7.16 UPDATE
7.17 USE
7.18 FLUSH(캐쉬 삭제)
7.19 KILL
7.20 SHOW(테이블, 컬럼 정보 얻기)
7.21 EXPLAIN(SELECT 관련 정보 얻기)
7.22 DESCRIBE(컬럼 정보 얻기)
7.23 LOCAK TABLES/UNLOCK TABLES
7.24 SET OPTION
7.25 GRANT / REVOKE
7.26 CREATE INDEX(호환성 기능)
7.27 DROP INDEX(호환성 기능)
7.28 주석
7.29 CREATE FUNCTION/DROP FUNCION
7.30 예약된 문자 사용하는 경우

8. SQL 질의 예제
        8.1 twin project에서의 질의
                8.1.1 Find all non-distributed twins
                8.1.2 Show a table on twin pair status

9. mysql 서버 기능
        9.1 mysql에서 지원하는 언어
                9.1.1 데이터와 정열에 사용하는 문자 셋
                9.1.2 새로운 문자셋 추가
                9.1.3 멀티바이트 문자 지원
        9.2 업데이트 로그
        9.3 mysql 테이블 최대 크기

10. mysql의 최대 성능 향상 방법
10.1 버퍼 크기 조정
10.2 메모리 상용 방법
10.3 속도 향상에 영향을 미치는 컴파일/링크 방법
10.4 인덱스 사용방법
10.5 WHERE 문에서 최적화하기
10.6 테이블 열고 닫는 방법
        10.6.1 데이터베이스에서 많은 수의 테이블을 만들때의 단점
10.7 많은 테이블을 여는 이유
10.8 데이터베이스와 테이블에서 심볼링 링크 사용
10.9 테이블에 락 거는 방법
10.10 테이블을 빠르고 작게 배열하는 방법
10.11 INSERT 문에서 속도에 영향을 미치는 부분
10.12 DELETE 문에서 속도에 영향을 미치는 부분
10.13 mysql에서 최대 속도를 얻는 방법
10.14 로우 포맷과 다른 점은 무엇인가? 언제 VARCHAR/CHAR을 사용해야 하는가?

11. mysql 벤치마크

12. mysql 유틸리티
        12.1 서로 다른 mysql 프로그램 개괄
        12.2 텍스트 파일에서 데이터 가져오기
        12.3 mysql 압축 읽기 전용 테이블 생성기

13. 테이블 유지보수 및 파손 복구에 isamchk 사용하기
        13.1 isamchk 명령어 사용법
        13.2 isamchk 메모리 사용법
        13.3 테이블 유지보수 설정
        13.4 테이블에서 정보 얻기
        13.5 파손 복구에 isamchk 사용하기
                13.5.1 에러가 났을때 테이블 점검 방법
                13.5.2 테이블 복구 방법
                13.5.3 테이블 최적화하기

14. mysql에 새로운 기능 추가
        14.1 새로운 사용자 정의 기능 추가
                14.1.1 UDF calling sequences
                14.1.2 Argument processing
                14.1.3 Return values and error handling
                14.1.4 사용자 정의 기능 컴파일 및 설치하기

        14.2 새로운 native 기능 추가

15. mysql ODBC 지원
        15.1 MyODBC가 지원하는 운영체제
        15.2 MyODBC에 문제가 있을때
        15.3 MyODBC와 작동하는 프로그램
        15.4 ODBC 관리자 프로그램 설정 방법
        15.5 ODBC에서 AUTO_INCREMENT 컬럼의 값 가져오기

16. 일반적인 문제 및 에러
        16.1 mysql 사용시 일반적인 에러
                16.1.1 mysql 서버가 맛이 간 경우
                16.1.2 local mysql 서버에 접속이 안되는 경우
                16.1.3 Host '...' is blocked 에러
                16.1.4 Out of Memory 에러
                16.1.5 Packet too large 에러
                16.1.6 The table is full 에러
                16.1.7 Commands out of sync 에러(클라이언트)
                16.1.8 Ignoring user 에러
                16.1.9 Table 'xxx' doesn't exist 에러
        16.2 mysql에서 디스크가 꽉 찼을때
        16.3 텍스트 파일에서 SQL 문장 실행하기
        16.4 mysql에서 임시 파일을 저장하는 곳
        16.5 /tmp/mysql.sock을 지워지지 않도록 보호하는 방법
        16.6 Access denied 에러
        16.7 일반 사용자로 mysql 실행하기
        16.8 파일 퍼미션 문제
        16.9 File not found
        16.10 DATE 컬럼 사용할때 문제
        16.11 검색에서 대소문자 구별
        16.12 NULL 값에서 문제
        16.13 alias에서 문제
        16.14 관련된 테이블에서 레코드를 지웠을때
        16.15 해당하는 레코드가 없을때 문제 해결
        16.16 ALTER TABLE 에서 문제

17. 일반적인 문제 해결 방법
        17.1 데이터베이스 복사
        17.2 데이터베이스 백업
        17.3 같은 머신에서 여러개의 mysql 서버 실행하기

18. mysql 클라이언트 툴과 API
        18.1 mysql C API
        18.2 C API 테이타타입
        18.3 C API 함수 개괄
        18.4 C API 함수 설명
                **생략**
                18.4.47 mysql_query()가 성공했는데도 mysql_store_result()
                        가 NULL을 반환하는 경우
                18.4.48 질의에서 결과 얻는 방법
                18.4.49 마지막으로 입력된 unique ID 얻는 방법
                18.4.50 C API와 링크할때 문제점
                18.4.51 thread-safe 클라이언트 만들기
        18.5 mysql perl API
                18.5.1 DBI with DBD :: mysql
                        18.5.1.1 dbi 인터페이스
                        18.5.1.2 DBI/DBD 추가 정보
        18.6MYSQL 자바 연결(JDBC)
        18.7 mysql PHP API
        18.8 MYSQL C++ API
        18.9 mysql Python API
        18.10 Mysql TCL API

19. 다른 데이터베이스와 비교
        19.1 mSQL과의 비교
                19.1.1 mSQL 툴을 mysql로 옮기기
                19.1.2 msql과 mysql 클라이언트/서버 통신 프로토콜의 차이점
                19.1.3 msql 2.0과 mysql의 SQL 문 차이점
        19.2 PostgreSQL과의 비교


부록 A ; MYSQL 사용자
부록 B ; 개발 프로그램
부록 C ;MYSQL 개발자
부록 D ; MYSQL 버전업 역사
부록 E ; MYSQL의 알려진 에러 및 설계에서 부족한 부분
부록 F ; MYSQL에 장래 추가할내용(The TODO)
부록 G ; 다른 시스템에 포팅하기
부록 H ; MYSQL 정규표현식 설명
부록 I ; Unireg 설명
부록 J ; 비MS 운영체제에서 MYSQL 서버 라이센스
부록 K ; MS 운영체제에서 MYSQL 라이센스
SQL 명령, 타입, 함수 인덱스
개념 인덱스
5. mysql의 표준 호환성

5.1 mysql의 ANSI SQL92 확장부분
mysql에는 다른 sql 데이터베이스에서 찾을 수 없는 확장된 부분이 있다. 이런 부분을 사용
하는 경우 주의해야 한다. 왜냐면  mysql에서 사용한 코드가 다른  SQL 서버에 포팅할 수
없을 수도 있기 때문이다. 어떤 경우에는 /*! ... */ 형식의 주석문을 사용한 MYSQL 확장을
이용해 포팅가능한 코드를 만들 수 있다. 예를 들어보자:

SELECT /*! STRAIGHT_JOIN */ col_name from table1,table2 WHERE ...

MYSQL의 확장 부분은 다음과 같다:
- 필드타입 MEDIUMINT, SET, ENUM , 그리고 다른 BLOB 와 TEXT 타입.
- 필드속성 AUTO_INCREMENT, BINARY, UNSIGNED and ZEROFILL.
- 모든 문자열 비교는 기본적으로  대소문자를 구별하지 않으며 현재의  문자셋(기본적으로
ISO-8859-1 Latin1)에 의해 정렬 순서가 결정된다. 이것을 원하지 않으면  컬럼을 BINARY
속성으로 정의해야 하며 이런 경우에는 mysql 서버 호스트가 사용하는 ASCII 순서에 따라
문자열을 비교한다.
- MYSQL은 데이터베이스를 디렉토리로 만들고 테이블은 파일이름으로 만든다. 이것은 두
가지를 함축하고 있다:
    ㅇ 파일이름의 대소문자를 구별하는 (대부분의  유닉스 시스템. 리눅스도 마찬가지겠지
     용~) 운영  시스템에서는 MYSQL의 데이터베이스 이름과  테이블 이름은 대소문자를
     구별한다. 테이블 이름을 기억하는데 문제가 있다면 모든 것을 소문자로 만들자.
    ㅇ 테이블의 백업, 이름바꾸기, 옮기기, 삭제,  복사를 위해 표준 시스템 명령을  사용할
     수 있다. 예를 들어 테이블의 이름을 바꾸려면  해당하는 테이블의 `.ISD', `.ISM' and
     `.frm' 파일의 이름을 바꾸면 된다.
- SQL문에서 db_name.tbl_name 문을 이용하여  다른 데이터베이스의 테이블에 접근할  수
있다. 일부 SQL 서버는 같은 기능을 지원하지만 이것을 User space라고 부른다. MYSQL은
다음과  같은  TABLESPACES를  지원하지  않는다  :  create  table  ralph.my_table...IN
my_tablespace.
- 수치(숫자형) 컬럼에서 LIKE를 사용할 수 있다.
- SELECT문에서   INTO OUTFILE  과  STRAIGHT_JOIN  을 사용할   수 있다.  7.11
[SELECT] 참고.
-EXPLIAN SELECT는 테이블에서 어떻게 조인이 되었는지에 대한 정보를 보여준다.
- Use of index names, indexes on a subpart of a field, and use of INDEX or KEY in a
CREATE TABLE statement. 7.6 [CREATE TABLE] 참고.
- ALTER TABLE에서 CHANGE col_name, DROP col_name 또는 DROP INDEX 를 사용
한다. 7.7 [ALTER TABLE] 참고.
- ALTER TABLE 문에서 IGNORE 사용.
- ALTER TABLE 문에서 다중 ADD, ALTER, DROP or CHANGE 사용
- IF EXISTS 키워드를 이용한 DROP TABLE 사용.
- 한 테이블 이상에서 DROP TABLE 사용.
- LOAD DATA INFILE 사용. 대부분의 경우 이 문장은  오라클의 LOAD DATA INFILE
과 호환된다. 7.15 [LOAD DATA INFILE] 참고.
(** 많은 양의 데이터를 한꺼번에 입력할 때 일일이 INSERT 문을 하는  것보다 속도가 빠
르다. **)
- OPTIMIZE TABLE 문 사용.
- 문자열은 ''' 만이 아니라 '"' 또는 ''' 로 닫을 수 있다.
- escape `\' 문자 사용.
- SET OPTION 문. 7.24 [SET OPTION] 참고.
- GROUP BY 부분에서 모든 컬럼을 사용할 필요가 없다. 이러한  기능은 일반적인 질의가
아닌 특정한 질의에서 성능을 향상시킨다. 7.3.12 [GROUP BY Functions] 참고.
(** ANSI SQL에서는 여러 테입믈을 이용하여 GROUP  BY를 사용할 때 사용하고자 하는
모든 컬럼에 GROUP BY를 지정해 주어야 한다. 이렇게 되는 경우 불필요한 연산이 수행될
수 있는데 MYSQL에서는 이러한 것을 없앤 것이다. 7.3.12를 참고한다 **)
- 다른 SQL 환경을 사용했던 사용자를 위해 MYSQL은 많은  기능에서 알리아스를 지원한
다. 예를 들어 모든 문자 펑션은 ANSI SQL 과 ODBC 구문을 지원한다.
- MYSQL은 C 프로그래밍 언어와 같은 논리적인 OR 과 AND를 의미하는 || 와 && 를 인
식한다. MYSQL에서는 || 와 OR 는 같은 말이며 && 와 AND 도 마찬가지이다. 이러한 미
묘한 구문때문에 MYSQL은 string  concatenation(문자열 연관, 연결?)을 위한  ANSI SQL
오퍼레이터인 || 을 지원하지 않는다. 대신 CONCAT()를 사용한다. CONCAT() 는 많은 인
자가 있어서 MYSQL 에서 || 오퍼레이터의 사용을 변환하기 쉽다.

MySQL understands the || and && operators to mean logical OR and AND, as in the C
programming language. In  MySQL, ||  and OR are  synonyms, as  are &&  and AND.
Because of this  nice syntax,  MySQL doesn't support  the ANSI  SQL operator  || for
string concatenation; use  CONCAT() instead.  Since CONCAT() takes  any number  of
arguments, it's easy to convert use of the || operator to MySQL.

- 포팅이 가능하도록 SQL CODE에서  STRAIGHT_JOIN같은 MYSQL만의 키워드 사용을
지원하기 위해 이런 키워드를 /* */ 주석안에 내장할 수 있다. 주석문안의 내용은 '!' 로 시
작한다. 이런 경우  MYSQL에서는 주석문을 다른  MYSQL 구문과 같이  해석하지만 다른
SQL 서버에서는 이러한 확장기능을 사용하지 않고 건너띌 수 있다. 예를 보자:

SELECT /*! STRAIGHT_JOIN */ * from table1,table2 WHERE ...

- 다음의 기능이나 명령 사용:
    ㅇ CREATE DATABASE or DROP DATABASE. 7.4 [CREATE DATABASE] 참고.
    ㅇ MOD() 대신에 % 사용. %는 C 프로그래머를 위해 지원하며 또한 PostgresSQL과의
     호환성을 위해 지원한다.
    ㅇ 컬럼 문에서 =, <>, <= ,<, >=,>, <<, >>, AND, OR, LIKE 사용.
    ㅇ LAST_INSERT_ID(). 18.4.49 [mysql_insert_id()] 참고.
    ㅇ REGEXP or NOT REGEXP.
    ㅇ 하나나 하나 이상의 인자를 사용한 CONCAT() 나 CHAR(). MYSQL에서 이러한 펑
     션은 여러개의 인자를 가질 수 있다.
    ㅇ  BIT_COUNT(),   ELT(),  FROM_DAYS(),   FORMAT(), IF(),   PASSWORD(),
     ENCRYPT(), PERIOD_ADD(), PERIOD_DIFF(), TO_DAYS(), or WEEKDAY().
    ㅇ 서브스트링을 없애는 데 TRIM()  사용.(Use of TRIM() to trim  substrings) ANSI
     SQL 에서는 단일 문자 제거만 지원한다.
(** 꺼어억~ 트림이 술먹고 하는 트림은 아니랍니다... **)
ㅇ 그룹 펑션에서 STD(), BIT_OR() and BIT_AND()
ㅇ DELET + INSERT 대신 REPLACE 사용. 7.14 [REPLACE] 참고.
ㅇ FLUSH flush_option 명령.


5.2 MYSQL에서 빠진 기능
다음의 기능들은 현재 버전의 MYSQL에  빠져있다. 다음 버전에서의 우선권을 확인하라면
MYSQL TODO 목록을  참고하자(http://www.mysql.com/Manual_split/manual_Todo.html).
이것이 가장 최신 버전의 TODO 목록이다. 부록 F [TODO] 참고.

5.2.1 Sub-selects
다음은 Mysql에서 작동하지 않는다:

SELECT * FROM table1 WHERE id IN (SELECT id FROM table2);

Mysql에서는 오직 INSERT ... SELECT ... and  REPLACE ... SELECT ... 만을 지원한다.
독립적인 서브-select 문은 3.23.0에서 아마도 사용할 수 있을 것이다.  그대신 현재 IN() 펑
션을 사용할 수 있다.

5.2.2 SELECT INTO TABLE
Mysql은 아직  SELECT ...  INTO TABLE  ....을 지원하지  않는다. 현재,  Mysql은 오직
SELECT ... INTO OUTFILE ..., 만을 지원하며 기본적으로는 동일하다.

5.2.3 트랜잭션(Transactions)
트랜잭션은 지원되지  않는다. Mysql은  곧 atomic(원자성?)  오퍼레이션을 지원할  것이며
atomic 오퍼레이션은 rollback이 없는 트랜잭션과 비슷하다.  atomic 오퍼레이션을 사용하며
insert/select/모든 명령의 그룹을 실행할 수 있으며 어떤 스레드도 충돌하지 않을 수 있도록
보장해준다. 이 문맥에서 일반적으로 롤백(rollback)은  필요없다. 현재 LOCK TABLES  와
UNLOCK TABLES 명령을  이용하여 다른  스레드가 충돌하는 것을  막을 수  있다. 7.23
[Lock Tables] 참고.

5.2.4 저장 프로시저와 트리거
저장 프로시저는 서버에서 컴파일되고 저장될 수 있는 SQL 명령 세트이다. 이런 기능이 수
행되면 클라이언트는 전체 질의를 다시 할 필요가 없고 또한 저장 프로시저를 참조할 수 있
다. 이런 기능이 있으면 질의는  한번만 해석되고 서버와 클라이언트간의  주고받아야 하는
데이터가 줄어들므로 속도가 향상된다. 또한 서버의  펑션 라이브러리를 가짐으로서 개념적
인 단계를 향상시킬  수 있다. (???  You can also  raise the conceptual  level by  having
libraries of functions in the server.)

트리거는 특별한 이벤트가 발생했을 때 생기는 저장 프로시져이다.  예를 들어 트랜잭션 테
이블에서 레코드가 삭제되고 모든 트랜잭션이 지워질 때 상응하는 테이블을 삭제할 수 있는
저장 프로시저를 설치할 수 있다.

앞으로는 저장 프로시저를 지원할 예정이지만 트리거는 아니다. 트리거는 필요하지 않은 경
우에도 사용될 수 있어서 일반적으로 속도가 느려진다.

언제 저장 프로시저를 사용하게  될지는 앞으로 Mysql에 추가할  목록인 부록 F를 참고하
자.(The TODO)

(** 전반적으로 트랜잭션 처리와 트리거 등은 데이터베이스의 속도를 저하시킵니다. Mysql
은 이렇게 속도에 영향을 미칠 수 있는 부분을 제거하여  빠른 속도를 내는 것이지요. 이러
한 부분이 자기가 사용하는 데이터베이스에서 얼마나 중요한가 판단을 해 보아야 할 것입니
다. 보통 소형 DBMS에서는 회복과 병행수행을 지원하지 않는 경우가 많다. 즉 병행수행은
발생하지 않으며, 회복은 사용자의 문제로 생각한다. 그러므로 사용자가 데이터베이스의  예
비 사본을 준비하며, 고장이 발생하면 작업을 다시 해야 한다. 트리거같은 경우는 자료의 무
결성을 보장하기 위해 필요한 것이다. **)

5.2.5 외래키(Foreign Keys)
SQL 문에서 외래키는 테이블을 조인할 때  사용하지 않지만 대부분 참조 무결성을 확인할
때 사용한다. SELECT 문에서 다중 테이블에서 자료를 가져오길 원하면 테이블을 조인해서
처리할 수 있다.

SELECT * from table1,table2 where table1.id = table2.id

7.12 [JOIN] 참고.

Mysql에서 외래키(FOREIGN KEY) 문은 다른 SQL 제품의 CREATE TABLE 명령과의 호
환성 때문에   존재한다: 외래키는   아무것도 하지  않는다.  ON  DELETE  ...  가  없는
FOREIGN KEY 구문은 대부분 문서적인  목적으로 사용한다. 일부 ODBC  애플리케이션은
이것을 자동적인 WHERE 문을 만들 때 사용할 것이다. 그렇지만 이것은 대부분 생략(무시)
하고 넘어가기 쉽다. 외래키는 때로는 제약조건 체크(constraint check)로 사용을 하지만 이
러한 체크는 데이터가 테이블에 정확한 순서로 들어갈때는 불필요하다. Mysql은 일부 애플
리케이션에서 외래키가 존재하는 것을 필요로 하기 때문에(제대로 작동하든  안하든 상관없
이) 지원하는 것일 뿐이다.

Mysql에서 외래키를 가진 테이블의 레코드를  삭제할 때 애플리케이션에 적절한 DELETE
문을 추가하여 ON DELETE ... 가 수행되는 것을  막음으로써 문제를 해결할 수있다. 경험
상 이렇게 하는 것이 외래키를 사용하는 것과 같이 빠르며(어떤  경우에는 더 빠름) 포팅하
기가 더 좋다.

가까운 시일안에 우리는 외래키 기능을 확장할 것이다. 그래서 최소한 mysqldump와 ODBC
에서 정보가 저장되고 검색할 수 있도록 할 것이다.


5.2.5.1 외래키를 사용하지 않는 이유

외래키를 사용할 때 어디에서 출발해야 할지 모르는 많은 문제가 있다:

- Foreign key들은 상황을 매우 복잡하게 만든다. 왜냐하면, foreing key의 정의가 database에
담겨야 하고, foreign key를 구현하는 것은 "자연스런" File 사용법(data file들을 옮기고,
복사하고, 삭제하는 등...)을 제한한다.
(** 문태준님 역주: 번역이 이상한데 외래키가 있으면 참조 무결성 규칙을 위해 여러 가지
보상  연산을 하게 된다. 이것을 뜻하고 있는 듯하다. **)

- INSERT 와 UPDATE 문은 속도에 많은 영향을  끼친다. 그리고 이런 경우 보통 올바른
순서로 올바른 테이블에 레코드를 삽입하기 때문에 대부분 모든 외래키 체크는 사용할 필요
없다.

- 한쪽의 영향이 전체 데이터베이스에 연쇄 작용을 하기 때문에 테이블에서 업데이트를 할
때 매우 많은 테이블에서 락을 사용해야 한다. 한 테이블에서 먼저 레코드를 삭제하고 그후
에 다른 테이블에서 레코드를 삭제하는 것이 훨씬 빠르다.

- Table를 완전히 지우고, (backup이나 새로운 source로부터) 모든 record들을 다시 복구하는
방법으로 Table을 복구할 수 없다.

- Foreign key를 사용한다면, table을 dump(backup)하고 (그 dump한 자료를) restore하는 데
있어 그 일련의 순서를 적절하게 지켜야 한다.

- 각각의 table의 정의가 쓸모있고 적절하더라도, (각 Table들이 상호참조하게 된다면) 단순한
create문으로는 재생성이 불가능한 circular definition(순환정의)가 쉽게 발생한다.
(역자주: A라는 Table이 B의 자료를 참조하는 foreign key를 담고 있고,
         B는 C에 대한 foreign key를, C는 A에 대한 foreign key를 담도록 table이 구성된다면
         한번에 A,B,C table을 생성할 수 없다. A,B,C를 만든다음 각각의 foreign key를 지정해
         주는 방법을 쓰게 된다.)

외래키의 좋은 점은 단지 다음와 같다. ODBC와  특정한 다른 클라이언트 프로그램에서 어
떻게 테이블이 연결되어 있는지를 볼 수 있고 연결 다이어그램을 보는데 사용하며 애플리케
이션을 만드는데 돕는 점이다.
(역자주: 이 글은 foreign key에 대해서 매우 비판적이다. 하지만, 이것은 foreign key의
         일부 기능일뿐이다. 무엇보다도 client가 각 Table내 DATA의 연관관계에 대해서
         빼먹었는지에 대해서 일일이 신경쓰지 않게 하고, 항상 자료의 무결성(정합성[?])을
         보장한다는 것은 매우 중요하다.

         특히, Table이 1~20개가 아닌 100단위가 넘어간다면, client에서 일일이 신경쓰며
         programing하는 것도 힘들지만, debugging도 예상보다 힘들어진다.
         각 table간의 연결관계를 잘 문서화한다면 programmer들이 foreign key로 고통받기
         보다는 관련 Table을 check해야 되는 수고를 덜게된다.

         foreign key가 기피되는 주된 이유는 table내의 Data를 수정하는 것이 쉽지 않기
         때문이다. 현장 실무자의 논리에 어긋나는 요청을 처리하는 데 있어서
         foreign key만큼 거추장스런 놈도 없으리라. )

Mysql에서는 곧 외래키 정의를 저장할 수 있도록 해서 클라이언트가 어떻게 원래의 연결이
만들어졌는지에 대해서 질문하고 답을 받을 수 있도록 할 것이다. 현재의  '.frm' 파일 포맷
은 아직 이것을 지원하지 못하고 있다.


5.2.6 뷰

Mysql은 뷰를 지원하지 않는다. 그렇지만 TODO(이후 개선 목록)에 있다.
MySQL doesn't support views, but this is on the TODO.

5.2.7 `--'을 사용한 주석

일부 다른 SQL 데이터베이스는 '--'  로 주석을 시작한다. mysql 명령  라인 도구가 '--'로
시작하는 모든 줄을 제거할 지라도 Mysql은  '#'을 주석 문의 시작으로 사용한다.  사용자는
또한 C 명령  스타일인 /*  this is  a comment  */ 를  mysql에서 사용할  수 있다.  7.28
[Comment] 참고.

Mysql은 '--'를 지원하지  않을 것이다;  '--'은 퇴보한  주석문 형태로 자동으로  생성되는
SQL 질의에서 많은 문제를 발생시킨다. 다음의 예제를 보자. 우리는 자동적으로 payment를
!payment! 의 값으로 입력하도록 하고 있다 :

UPDATE tbl_name SET credit=credit-!payment!

payment의 값이 음수일 때 어떤 일이 생길 것이라 생각하는가?

1--1은 합당한 SQL문이기 때문에 '--'가 주석문의 시작을 의미하는 것을 꺼리는 것이다.

'--' 주석을 포함하는 텍스트 파일의 SQL 프로그램을 가졌다면 다음과 같이 사용해야 한다:

shell> replace " --" " #" < text-file-with-funny-comments.sql \
         | mysql database

instead of the normal(정상적인 경우 대신???):

shell> mysql database < text-file-with-funny-comments.sql

명령 파일로 '--' 주석을 '#' 주석으로 바꿀 수 있다:

shell> replace " --" " #" -- text-file-with-funny-comments.sql

다음의 명령으로 원래대로 돌려놓자:

shell> replace " #" " --" -- text-file-with-funny-comments.sql

(** 일부 SQL에서 사용하는 --  주석문에서 문제가 생길 수 있으므로  MYSQL에서는 #을
주석문으로 사용한다는 말이다 **)

5.3 Mysql이 따르고 있는 표준은 무엇인가?

Entry level SQL92. ODBC level 0-2.


5.4 BLOB 와 TEXT 타입의 제한
BLOB 나 TEXT 필드에서 GROUP BY 나 ORDER BY를 사용하길 원하면 그 필드를 고정
길이 객체로 만들어야 한다. 이렇게  하는 표준적인 방법은 SUBSTRING  펑션을 사용하는
것이다. 예를 보자:

mysql> select comment from tbl_name order by SUBSTRING(comment,20);

이렇게 하지 않으면 정렬할 때  오직 첫 번째 max_sort_lengths  (기본값=1024)만을 고려된
다.

BLOB 와 TEXT 는 기본값을 가질 수 없으며 또한 언제나 NULL 컬럼일 것이다.

BLOB and  TEXT cannot   have DEFAULT values   and will also  always  be NULL
columns.

5.5 COMMIT-ROLLBACK 없이 어떻게 대치할 수 있을까?
Mysql은 COMMIT-ROLLBACK 을 지원하지 않는다. 문제는 COMMIT-ROLLBACK을 효
과적으로 다루기 위해서는 Mysql에서 현재 사용하는 것과 완전히 다른 테이블 설계가 필요
하다는 것이다. Mysql은 또한 테이블을 자동  클린업하는 추가적인 스레드와 더 많은 디시
크를 사용할 수 있는 기능이 필요하다. 이러한  기능은 현재보다 mysql을 2-4배 느리게 만
든다. Mysql은 대부분의  다른 SQL 데이터베이스보다  훨씬 더 빠르다.  (전형적으로 최소
2-3대 빠름) 이러한 이유는 Mysql에 COMMIT-ROLLBACK이 없기 때문이다.

당분간은 우리는   SQL 서버  언어의  성능을  향상시키는데 더   주력할 것이다.  대부분
COMMIT-ROLLBACK 기능이 정말로 필요한 경우는 드물다. 또한 이렇게 하는 것이 더 좋
은 성능을 낼 수 있다.

일반적으로 트랜잭션이 필요한 루트는 LOCK TABLES를 사용해 코드를 짤  수 있다. 또한
레코드를 업데이트할 때 커서를 사용할 필요가 없다.

우리는 트랜잭션과 커서를 TODO에 넣었지만  우선권이 높은 것은 아니다.  이러한 기능을
수행한다면 CREATE TABLE 의 옵션으로 될 것이다. 이것은 옵션으로 지정한 테이블에서
만 작동하며 그 테이블은 느리게 될 것이라는 것을 의미한다.

우리는   100%   보편적인   데이터보다는   정말로   빠른    데이터베이스가   필요하다.
COMMIT-ROLLBACK 기능을 수행하더라도 속도에 손상이 없다면  우리는 그것을 지원할
것이다. 당분간은 더 중요하게 해야할 일들이  많이 있다. 우리가 어떤 것에 우선권을  두고
있는지는 TODO를 참고하자. 상위 단계의 지원을 받는 고객은 이것을 바꿀 수 있으며 우선
권이 변경될 수도 있다.

현재의 문제는 실제로 ROLLBACK 이다. 롤백없이 LOCK TABLES을 이용하여 여러 종류
의 COMMIT를 사용할 수 있다. 롤백을 지원하기  위해 Mysql은 업데이트가 된 모든 예전
레코드를 저장하고 롤백이 일이났을 때 시작 시점으로 돌아갈 수 있도록 바꾸어야 한다. 예
를 들어 이러한 것은 전혀 어렵지 않다.(현재의 isamlog 는 이런 경우를 위해 사용할 수 있
다) 그러나 ALTER/DROP/CREATE TABLE에서 롤백을 수행하는 것은 무척 어렵다.

롤백 사용을 피하기 위해 다음의 전략을 사용할 수 있다:

1. 접근하기 원하는 모든 테이블에 락을 사용. LOCK TABLES ... 
2. 조건 테스트(Test conditions)
3. 모든 것이 제대로 된다면 업데이트를 한다.
4. UNLOCK TABLES

일반적으로 가능한 롤백을 이용해 트랜잭션을 사용하는 것보다는 이러한 방법이 훨씬 더 빠
르다. 그렇지만 항상 사용가능한 것은 아니다. 이러한 방법으로 해결할 수 없는 유일한 상황
은 업데이트중 누군가가 스레드를 죽였을 때이다. 이런 경우 모든 락은 해제가 된다. 그렇지
만 업데이트의 일부는 실행되지 않을 것이다.

물론 단일 오퍼레이션에서 레코드를 업데이트하는 펑션을 사용할 수 있다. 다음의 테크닉을
사용하며 매우 효율적인 애플리케이션을 만들 수 있다:

- 현재 값과 관련되어 있는 필드를 수정
- 실제로 변화가 생겼을때만 필드를 업데이트

예를 들어, 어떤 고객 정보를 업데이트 할 때 오직 바뀐 데이터만 업데이트를 한다. 그리고
For example, when we are doing updates on some customer information, we update only
the customer data that have  changed and test only  that none of the changed  data, or
data that depend on the changed data, have changed compared to the original row.
변화된 데이터의 테스트는 UPDATE 문에서  WHRE 절을 사용하여 할 수  있다. 레코드가
업데이트되지 않았다면 클라이언트에 다음과 같은 메시지를 준다:  "당신이 바꾼 데이터 일
부가 다른 사용자에 의해  바뀌었습니다". 그러고나서 우리는  윈도우에서 예전의 레코드와
현재의 레코드를 비교하여 보여준다. 그러면  사용자는 어떤 고객 정보  레코드를 사용할지
결정할 수 있다.

이렇게 하면 "컬럼 라킹"과 비슷하다. 그렇지만 실제로는 더 빠르다.  왜냐하면 현재의 값과
관련되어 있는 값의 컬럼만 업데이트하기 때문이다.  이렇나 전형적인 업데이트문은 다음과
비슷할 것이다:

UPDATE tablename SET pay_back=pay_back+'relative change';

UPDATE customer
  SET
    customer_date='current_date',
    address='new address',
    phone='new phone',
    money_he_owes_us=money_he_owes_us+'new_money'
  WHERE
    customer_id=id AND address='old address' AND phone='old phone';

지금 보듯이   이렇게 하면  매우  효율적이며  설사 다른   클라이언트가 pay_back   이나
money_he_owes_us 컬럼의 값을 바꾸었을 때라도 제대로 작동한다.

대부분의 경우, 사용자는 테이블에서 유일한 값(identifiers)을 관리하기 위해 롤백과  테이블
락을 사용하고 싶어한다. 이것은  AUTO_INCREMENT 컬럼과 SQL  LAST_INSERT_ID() 
펑션, 또는 mysql_insert_id() 의 C API 펑션을 사용하여 더욱  효율적으로 사용할 수 있다. 
18.4.49 [mysql_insert_id()] 참고.

TcX에서는 언제나 이런 문제를 해결할  수 있기 때문에 결고 low-level  락을 필요로 하지
않는다. 어떤 경우에는 정말로 로우-락이 필요하다. 그렇지만 이런 경우는 극소수이다. 로우
-레벨 락을 원하면 테이블에서 플래그 컬럼을 사용할 수 있다. 다음과 같다:

UPDATE tbl_name SET row_flag=1 WHERE id=ID;

만약 row가 발견되고 row_flag가 원래의  row에서 이미 1이 아니라면  영향을 받은 row의
숫자로서 1일 반환한다.

MySQL returns 1 fro the number of  affected rows if the row was found  and row_flag
wasn't already 1 in the original row.

6. Mysql 접근 권한 시스템

mysql 은 진보적이지만 비표준적인 보안/권한 시스템을 가지고 있다. 이번 장에서는 이것이
어떻게 작동하는지를 설명하고 있다.

6.1 권한 시스템이란 무엇인가?
Mysql 권한 시스템의 주요 기능은 데이터베이스에서 select, insert, update, delete 권한
을 호스트의 사용자 이름과 관련짓는 것이다.

추가적인 기능에는 익명 사용자  기능과 LOAD DATA INFILE 과 관리자 오퍼레이션과 같은 my
sql만의  특수한 권한을 허용하는 부분이 포함되어 있다.


Mysql에서 인증을 목적으로 사용하는 사용자 이름은 유닉스 사용자 이름(로그인 이름)이나
위도우 사용자 이름고는 전혀 관계가 없다는 것!을 기억하자. 대부분 mysql 클라이언트는 m
ysql 사용자 이름으로 현재의 유닉스 사용자 이름을 사용하여 접속하려 할 것이다. 그렇지
만 이건 오직 편의를 위해서이다. 클라이언트 프로그램은 -u 나 --user 옵션으로 지정한 다
른 이름을 허용한다. 이것은 mysql 사용자 이름에 비밀번호를 설정하지 않으면 데이터베이
스의 보안에 문제가 생길 수 있다는 것을 의미한다. 어떤 이름을 사용하여 서버에 접속하려
고 하는 사람은 각 이름에 비밀번호가 설정되어 있지 않다면 접속에 성공할 것이다.

유닉스 사용자 이름이 일반적으로 8글자로 제한되어 있는 것과 다르게 mysql 사용자 이름은
16글자까지 사용할 수 있다.

mysql 비밀번호는 유닉스의 비밀번호와 아무 관련이 없다. 유닉스 머신에 로그인할 때 사용
하는 비밀번호와 데이터베이스에 접속할 때 사용하는 비밀번호는 전혀 관련이 없다. 또한 m
ysql은 유닉스 로그인 프로세스에서 사용하는 것과 다른 알고리즘으로 비밀번호를 암호화한
다.


6.2 mysql 서버에 접속하기

mysql 클라이언트 프로그램은 일반적으로 연결 패러미터(매개 변수)가 필요하다.: 연결할
호스트, 사용자 이름, 비밀번호. 예를 들어 mysql 클라이언트는 다음과 같이 시작할 수 있
다. (선택 인자는 [ ] 로 닫는다)

shell>; mysql [-h host_name] [-u user_name] [-pyour_pass]


-p와 뒤에 붙은 비밀번호 사이에는 공간이 없다는 것을 기억하자.

-h, -u, -p를 대체할 수 있는 형식으로는 --host=host_name, --user=user_name and --passw
ord=your_pass  이 있다.

mysql은 커맨드 라인에서 연결 매개변수가 빠져있을 때는 기본 값을 사용한다. 기본 호스트
이름은 localhost 이고 기본 사용자 이름은 유닉스 로그인 이름이다.(-p 가 빠져있으면 비
밀번호는 사용하지 않는다) 그래서 만약 유닉스 사용자 이름이 joe 라면 다음의 명령은 동
일하다.:

shell> mysql -h localhost -u joe
shell> mysql -h localhost
shell> mysql -u joe
shell> mysql


다른 mysql 클라이언트도 비슷하게 작동한다.

유닉스 시스템에서 연결을 할 때 사용할 수 있는 기본 값이 있어서 클라이언트 프로그램을
사용할 때마다 명령행에서 옵션을 사용하지 않아도 된다:

ㅇ 홈 디렉토리의 '.my.cnf' 설정 파일의 [client]  섹션에서 연결 변수를 설정할 수 있다.
파일에서 이와 연관된 섹션은 다음과 같다:
[client]
host=host_name
user=user_name
password=your_pass

4.14.4 [option files] 참고.

ㅇ 환경 변수를 사용하여 연결 변수를 지정할 수 있다. 호스트는 MYSQL_HOST 로 지정할 수
있다. Mysql  사용자 이름은 USER, LOGNAME, 또는 LOGIN을 사용할 수 있다. (이러한 값들은
이미 유닉스 로그인 이름으로 설정되어 있을 것이다. 그러므로 바꾸지 않는게 좋다) 비밀번
호는 MYSQL_PWD로 지정할 수 있다.(그렇지만 이것은 안전하지 않다; 다음 섹션을 참고하자)

연결 변수가 여러 가지 방법을 지정되었다면 명령행에서 지정한 값이 설정 파일과 환경 변
수로 설정한 것보다 우선권을 가진다. 또한 설정 파일의 값이 환경 변수보다 우선권을 가진
다.

6. 2. 1 비밀번호의 보안 유지

다른 사용자가 발견할 수 있게 비밀번호를 지정하는 방법은 권하지 않는다. 클라이언트 프
로그램을 실행할 때 비밀번호를 지정하는 방법은 아래와 같으며 각 방법마다 위험도를 같이
설명하였다:

ㅇ 명령행에서 -pyour_pass 또는 --password=your_pass 옵션 사용.  이 방법은 편리하지만
위험한 방법이다. 비밀번호를 시스템 상황 프로그램(ps 등)을 통해 볼 수 있기 때문에 다른
사용자가 명랭행으로 볼 수 있다.(mysql 클라이언트는 일반적으로 초기화되는 동안 명령행
인자를 0으로 덮어씌운다. 그렇지만 값을 볼 수 있는 짧은 틈이 여전히 있다)

ㅇ -p 또는 --password 옵션 사용(비밀번호 값을 지정하지는 않음). 이런 경우 클라이언트
프로그램은 터미널에서 비밀번호를 물어본다:
shell> mysql -u user_name -p
Enter password: ********

클라이언트는 비밀번호를 칠 때 터미널에서 '*'  문자를 보여준다. 그러므로 다른 사용자가
비밀번호를 볼 수 없다. 다른 사용자가 볼 수 없으므로 명령행에서 비밀번호를 입력하는 것
보다 훨씬 더 안전하다. 그렇지만 이 방법은 비대화식의 스크립트로 클라이언트 프로그램을
사용하면 적절하지 않다.

ㅇ 설정 파일에 비밀번호 저장. 예를 들어 홈 디렉토리의 '.my.cnf' 파일에서 [client] 섹
션에 비밀번호를 지정할 수 있다.
[client]
password=your_pass

비밀번호를 '.my.cnf' 파일에 저장한다면 그 파일은 그룹이나 다른 사용자가 읽기/쓰기를
할 수 없도록 해야 한다. 파일의 퍼미션이 400 이나 600 인지 확인하자.

4.14.4 [옵션 파일] 참고.

ㅇ 비밀번호를 MYSQL_PWD 환경 변수에 저장할 수 있다. 그렇지만 이 방법은 정말로 위험하
며 사용해서는 안된다. 일부 ps 프로그램은 실행 프로세스의 환경변수를 보여주는 옵션이
있다; MYSQL_PWD에 설정을 하면 다른 사람들이 쉽게 비밀번호를 볼 수 있다. 이런 기능의 p
s가 없는 시스템일지라도 프로세스 환경변수를 검색할 수 있는 방법이 없다고 생각하는 것
은 현명하지 못하다.

이중에서 가장 안전한 방법은 클라이언트 프로그램이 비밀번호를 요구하거나 적절하게 보안
이 된 '.my.cnf' 파일에 비밀번호를 지정하는 것이다.

6.3 mysql에서 제공하는 권한

권한과 관련된 정보는 mysql 데이터베이스의(데이터베이스 이름이 mysql 임) user, db, hos
t, table_priv, columns_priv  테이블에 저장된다. mysql 서버는 시작할 때, 그리고 환경을
지정할 때(6.7 [권한 변경] 참고) 이 테이블의 내용을 읽어들인다.

mysql에서 제공하는 권한을 설정할 때 사용하는 이름은 아래와 같다.테이블의 컬럼 이름은
grant tables의 각 권한 및 권한이 적용되는 context와 연관되어 있다.


Privilege              Column         Context
(권한)          (컬럼)         (환경)
select         Select_priv     tables
insert         Insert_priv     tables
update         Update_priv     tables
delete         Delete_priv     tables
index          Index_priv      tables
alter          Alter_priv      tables
create         Create_priv     databases, tables or indexes
drop            Drop_priv       databases or tables
grant          Grant_priv      databases or tables
reload         Reload_priv     server administration
shutdown       Shutdown_priv   server administration
process        Process_priv    server administration
file            File_priv               file access on server


select, insert, update, delete 권한은 데이터베이스의 테이블에서 레코드에 대한 오퍼레
이션을 할 수 있도록 허용한다.

SELECT 문은 오직 실제로 테이블에서 줄(레코드)를 가져올 때만 select 권한이 필요하다.
서버의 데이터베이스에 접근 권한이 없는 경우라고 하더라도 특정한 SELECT  문은 사용할
수 있다. 예를 들면 간단한 계산을 위해 mysql 클라이언트를 사용할 수 있다:

mysql> SELECT 1+1;
mysql> SELECT PI()*2;

index(인덱스) 권한은 인덱스를 생성하거나 제거할 수 있다.

alter 권한은 ALTER TABLE 을 사용할 수 있도록 한다.

create  와 drop 권한은 새로운 데이터베이스와 테이블을 생성하거나 존재하는 데이터베이
스와 테이블을 제거할 수 있도록 허용한다.

사용자에게 mysql 데이터베이스의 drop 권한을 허용하면, 그 사용자는 mysql 접근권한 정보
가 저장된 데이터베이스를 없앨 수 있다는것!을 명심하자.


grant 권한은 사용자가 가지고 있는 권한을 다른 사용자가 가질 수 있도록 허용한다.

file 권한은 LOAD DATA INFILE and SELECT ... INTO OUTFILE  문을 이용하여 서버에 파일을
저장하고 읽을 수 있는 권한을 허용한다. 이러한 권한을 가진 사용자는 mysql 서버가 읽고
쓸 수 있는 파일을 읽고 쓸 수 있는 권한이 허용된다.

나머지 권한들은 관리자 오퍼레이션에 사용되며 mysqladmin 프로그램의 기능을 수행한다.
아래의 테이블은 각 관리자 권한에 따라 사용할 수 있는 mysqladmin 명령을 보여준다:

Privilege              Commands permitted to privilege holders
(권한)          (권한에 따라 허용되는 명령)
reload         reload, refresh, flush-privileges, flush-hosts, flush-logs, flush-tab
les
shutdown       shutdown
process        processlist, kill


reload 명령은 서버가 grant 테이블을 다시 읽어 들인다. refresh 명령은 모든 열린 테이블
을 닫으며 로그 파일을 열고 닫는다. flush-privileges 는 reload 명령과 동의어이다. 다른
flush-* 명령은 refresh 와 비슷한 기능을 수행한다. 그러나 범위에 제한이 있으며 어떤 경
우에는 더 선택할 만하다. 예를 들어 로그 파일만 닫고 다시 열고자 한다면 flush-logs 가
refresh보다 더 나은 선택이다.
(** flush의 옵션으로는 호스트, 로그 파일, 권한 설정, 테이블, status variables 설정 변
수가 있다. SQL 문에서 또는 mysqladmin 유틸리티를 사용하면 된다. **)

shutdown 명령은 서버를 셧다운한다. (** 이거 번역 맞아~~?? **)

processlist 명령은 서버에서 실행되고 있는 스레드에 대한 정보를 보여준다. kill 명령은
서버 스레드를 죽인다. 언제나 자신의 스레드는 보거나 죽일 수 있지만 다른 사용자에 의해
시작된 스레드는 프로세스 권한이 있어야 보거나 죽일 수 있다.

몇가지 권한은 조심스럽게 허용해야 한다:y:

ㅇ grant(허용) 권한은 사용자가 다른 사용자의 권한을 설정할 수 있도록 허용한다. 다른
권한과 grant 권한을 가진 두 사용자는 권한을 결합할 수 있다.
ㅇ file 권한은 서버에서 모든 사람이 읽기 가능한 파일을 읽는데 남용 될 수 있.... SELEC
T  문을 이용해 접근할 수 있는 내용...
The file privilege can be abused to read any world-readable file on the server into a
database table, the contents of which can then be accessed using SELECT.
(** 굳이 권한을 주지 않아도 이용할 수 있는 것은 권한을 주지 않는게 낫다는 말이겠지요
**)
ㅇ shutdown 권한은 다른 사용자에가 완전히 서비스를 사용하지 못하도록 남용될 수 있다.
ㅇ  process 권한은 비밀번호를 설정하고 바꾸는 질의를 포함해 현재 수행하고 있는 질의를
보는데 사용될 수 있다.
ㅇ mysql 데이터베이스에 대한 권한은 비밀번호와 다른 접근 권한 정보를 바꾸는 데 사용될
수 있다. (비밀번호가 암호화되어 저장되었다고 하더라도, 충분한 권한을 가진 악의있는 사
용자는 다른 비밀번호롤 바꿀 수 있다)

mysql 권한 시스템으로 다룰 수  없는 몇가지가 있다:
ㅇ 접근을 거부할 사용자를 명백하게 지정할 수 없다. 왜냐하면 사용자와 연결을 거부하는
것을 완전하게 연관시킬 수 없기 때문이다.
ㅇ 사용자가 테이터베이스에서 테이블을 만들고 지울 수 있는 권한을 가질 수 있지만 데이
터베이스 자체를 만들고 삭제할 수는 없도록 지정할 수 없다.
(** 그러니까 create 와 drop 권한을 주면 데이터베이스 자체에 대해 제어할 수 있지요. 그
안의 테이블만 만들고 지울 수 있도록 하지는 못한다는 말 **)

{{}}
6.4 권한 시스템 작동 방법

mysql 권한 시스템은 모든 사용자가 허용된 것만큼만 할 수 있도록 보증한다. mysql 서버에
연결할 때, 사용자 확인은 연결한 호스트와 사용자가 지정한 사용자 이름에 의해 결정된다.
시스템은 사용자 확인과 지정한 권한에 따라 권한을 허용한다.


mysql은 사용자를 확인하는데 호스트이름과 사용자 이름 둘다 사용한다. 왜냐면 인터넷에서
이름이 같다고 같은 사용자라고 생각할 수는 없기 때문이다. 예를 들어 whitehouse.gov에서
접속하는 사용자 bill 은 microsoft.com에서 접속하는 사용자 bill 과 같은 사람일 필요는
없다. mysql은 때론 같은 이름을 가지고 있더라도 호스트를 이용해 사용자를 구별한다 : wh
itehouse.gov에서 접속하는 bill에게 특정한 권한을 허용할 수 있고 microsoft.com에서 접
속하는 bill에게 다른 권한을 허용할 수 있다.

mysql의 접근 제어는 두가지 단계가 있다:

단계 1: 서버에서 사용자가 연결할 수 있는지 없는지 판단

단계 2 (서버에 사용자가 연결이 허용되었을 경우) : 사용자가 수행하려는 명령에 대해 충
분한 권한이 있는지 각 요청마다 서버에서 판단.예를 들면, 데이터베이스의 테이블에서 sel
ect rows를 할때, 또는 데이터베이스에서 테이블을 제거할 때 서버에서 테이블에 대한 sele
ct 권한이 있는지 데이터베이스에 대한 제거 권한이 있는지 확인을 한다.



서버는 접근 제어의 각 두 단계에서 mysql 데이터베이스의 user, db, host 테이블을 이용한
다.grant 테이블의 필드는 아래와 같다:

Table name     user            db              host
Scope fields   Host            Host            Host
(필드 범위)     User            Db              Db
                Password                User
Privilege fields      Select_priv     Select_priv     Select_priv
(권한 필드)     Insert_priv     Insert_priv     Insert_priv
                Update_priv     Update_priv     Update_priv
                Delete_priv     Delete_priv     Delete_priv
                Index_priv      Index_priv      Index_priv
                Alter_priv      Alter_priv      Alter_priv
                Create_priv     Create_priv     Create_priv
                Drop_priv       Drop_priv       Drop_priv
                Grant_priv      Grant_priv      Grant_priv
                Reload_priv            
                Shutdown_priv          
                Process_priv           
                File_priv              


접권 제어의 두번째 단계를 위해(요청 인증), 요청이 테이블에 관계된 것이라면 추가적으로
tables_priv 와 columns_priv 테이블을 참고한다. 이 테이블의 필드는 다음과 같다:

Table name     tables_priv     columns_priv
Scope fields   Host            Host
                Db              Db
                User            User
                Table_name      Table_name
                                Column_name
Privilege fields      Table_priv      Type
                Column_priv    
Other fields   Timestamp       Timestamp
                Grantor




각 승인(grant) 테이블은 필드 범위와 권한 필드로 구성되어 있다.

필드 범위는 테이블에서 각 엔트리의 범위를 결정한다. 다시 말하면 엔트리가 적용되는 con
text(환경, 배경)이다. 예를 들면, Host 와 User 값이 'thomas.loc.gov' 와 'bob' 인 user
테이블 엔트리는 thomas.loc.gov 호스트에서 bob이 연결을 할때 서버에서 인증을 하는데 사
용된다.비슷하게 Host, User, db 필드값이  'thomas.loc.gov', 'bob', 'reports' 인 db 테
이블 엔트리는 thomas.loc.gov 호스트에서 bob 이 reports 데이터베이스에 접근할 때 사용
된다. tables_priv 와 columns_priv 테이블은 테이블이나 각 엔트리가 적용될 수 있는 테이
블/컬럼 조합을 가리키는 범위 필드를 포함하고 있다.

접권 체크를 하기 위해, HOst 값 비교는 대소문자를 구별하지 않는다. User, Password, Db,
Table_name 값은 대소문자를 구별한다. mysql 3.22.12 와  이후 버전에서 Column_name 값은
대소문자를 구별하지 않는다. (3.22.11에서는 대소문자 구별함)

권한 필드는 테이블 엔트리에 승인되는 권한을 가리키며 이는 수행할 수 있는 오페레이션이
다. 서버는 사용자의 권한을 완벽하게 설정하기 위해 다양한 승인(grant) 테이블의 정보를
조합한다. 여기에 사용하는 규칙은 6.6 [Request access]를 참고하자.

범위 필드는 문자열이며 다음과 같이 정의되었다; 기본 값은 빈 문자열이다:

Field name     Type   
Host            CHAR(60)       
User            CHAR(16)       
Password                CHAR(16)       
Db              CHAR(64)      (CHAR(60) for the tables_priv and columns_priv tables)



user, db, host 테이블에서 모든 권한 필드는 ENUM('N','Y')로 정의되어 있다. -- 각각은
'N' 나 'Y'의 값을 가지며 기본값은 'N' 이다.
(** ENUM 타입은 목록 값중 오직 하나의 값만 가진다.필드 타입 참조 **)

tables_priv 와 columns_priv 테이블에서 권한 필드는 SET 필드로 정의된다:
(** SET 타입은 목록 값중에 0이나 1개 이상의 값을 가진다 **)

Table name     Field name     Possible set elements
tables_priv     Table_priv      'Select', 'Insert', 'Update',
                                'Delete', 'Create', 'Drop', 'Grant',
                                'References', 'Index', 'Alter'
tables_priv     Column_priv     'Select', 'Insert', 'Update',
                                'References'
columns_priv    Type            'Select', 'Insert', 'Update',
                                'References'

간단하게 말해서 서버는 승인(grant) 테이블을 다음과 같이 사용한다:

ㅇ user 테이블의 scope(범위) 필드는 들어오는 연결에 대해 허용할 것인지 거부할 것인지
를 결정한다. 허용된 연결에 대하여, 권한 필드는 사용자의 전체적인 (superuser) 권한을
가리킨다.

ㅇ db 와 host 테이블은 함께 사용된다:
        - db 테이블의 범위 필드는 어떤 호스트에서 어떤 데이터베이스에 대해 어떤 사용
자가 접근할 수 있는지 결정한다. 권한 필드는 어떤 오퍼레이션이 허용되었는지를 결정한
다.
        - host 테이블은 db 테이블의 엔트리를 여러개의 호스트에 적용하려고 할 때 db 테
이블의 확장을 위해 사용한다.예를 들어, 사용자가 현재 네트웍의 여러 호스트에서 데이터
베이스를 사용할 수 있도록 하려면,사용자의 "db" 테이블 엔트리에 Host 값을 비워두고, "h
ost" 테이블에 각 호스트의 엔트리를 넣으면 된다.이러한 절차는 6,6 [Request access]에
자세하게 나와 있다.

ㅇ tables_priv 와 columns_priv 테이블은 db 테이블과 비슷하다. 그렇지만 더 세부적으로
지정할 수 있다: 이 테이블들은 데이터베이스 단계에서 더 나아가 테이블과 컬럼 단계에 적
용할 수 있다.

관리 권한(reload, shutdown,기타..)은 오직 user 테이블에서만 지정을 할 수 있다는 것을
기억하자! 왜냐면 관리자 오퍼레이션은 서버 자체에 대한 오퍼레이션이며 특정한 데이터베
이스를 지정하는 것이 아니다. 그러므로 이러한 권한은 다른 승인(grant) 테이블에 있을 필
요가 없다.실제로, 오직 user 테이블만이 관리자 오퍼레이션을 수행할 수 있는지 없는지를
결정할 때 참고가 된다.

파일(file) 권한도 마찬가지로 user 테이블에서만 지정한다.위와 같은 관리 권한은 아니다.
그렇지만 서버 호스트에서 파일을 읽거나 쓸 수 있는 권한은 접근하고 있는 데이터베이스와
무관한 것이다.

mysqld 서버는 시작할 때 승인(grant) 테이블을 한번 읽는다. 승인 테이블을 변경하고 효과
를 발휘하려면 6.7 [Privilege changes]를 참고하자.

승인 테이블의 내용을 수정했을 때 원하는대로 권한이 설정되었는지 확인하는 것은 좋은 생
각이다. 유용한 진단 프로그램은 mysqlaccess 스크립트로서 Yves CArlier 가 mysql distrib
ution 으로 제공하고 있다. 어떻게 작동하고 있는지 확인하기 위해 mysqlaccess 에 --help
옵션을 주어 실행해보자. 물론 6.11 [Access denied] 와 6.12 [Security]를 참고하자.


mysqlaccess는 오직 user, db, host 테이블만 점검한다. 테이블이나 컬럼 단계의 권한까지
는 점검하지 않는다는 것을 기억하자.

6.5 접근 제어, 단계 1 : 연결 확인(인증)

mysql 서버에 접속하려고 할 때 서버는 사용자 확인과 비밀번호를 통해 접속을 허용하거나
거부한다. 사용자 확인이 안되면 서버는 접속을 완전히 거부한다. 사용자 확인이 되면 서버
는 연결을 받아들이고 2번째 단계로 들어가며 요청을 기다린다.

사용자 확인은 두가지 정보에 기반하고 있다:

ㅇ 접속하는 호스트
ㅇ mysql 사용자 이름

사용자 확인은 user 테이블의 세가지 범위 필드(Host, User, Password)를 사용하여 수행된
다. 서버는 user 테이블 엔트리의 호스트이름과 사용자 이름이 맞으며, 비밀번호가 정확할
때만 접속을 받아들인다.

아래와 같이 user 테이블의 범위 필드값을 지정할 수 있다:

ㅇ Host 값은 호스트 이름이나 IP 숫자 또는 로컬 호스트를 가리키는 'localhost' 가 될 것
이다.
ㅇ Host 필드에서 '%' 와 '_' 의 와일드카드 문자를 사용할 수 있다.
ㅇ '%'의 Host 값은 모든 호스트 이름을 나타낸다. 공백의 호스트 값은 '%'와 같다. 특정한
호스트에 대한 이러한 값은 당신의 서버에 연결할 수 있다는 것을 참고하자.
ㅇ 와일드카드 문자는 User 필드에는 허용되지 않는다. 그렇지만 모든 유저에 해당하는 공
백으로 둘 수 있다. 연결을 하려는 목록에 공백 사용자 이름이 있다면 클라이언트에서 실제
로 지정한 이름 대신에 그 사용자는 익명 사용자, 이름이 없는 사용자로서 간주된다.
ㅇ Password 필드는 공백으로 될 수 있다.이것은 아무런 비밀번호나 사용할 수 있다는 것을
의미하는 것은 아니며 사용자는 비밀번호를 지정하지 않고 연결을 해야 한다는 의미이다.

아래의 테이블은 연결 요청에 적용하는 "user" 테이블 목록의 Host, User 값이 어떻게 조합
되는지를 보여주는 예제이다:

호스트값/사용자 값 : 목록에 해당하는 연결
'thomas.loc.gov'/'fred' : thomas.loc.gov 에서 연결하는 fred
'thomas.loc.gov'/'' : thomas.loc.gov 에서 연결하는 모든 사용자
'%'/'fred' : 모든 호스트에서 연결하는 fred
'%'/'' : 모든 호스트에서 연결하는 모든 사용자
'%.loc.gov'/'fred' : loc.gov 도메인의 모든 호스트에서 연결하는 fred
'x.y.%'/'fred' : x.y.net, x.y.com, x.y.edu 등에서 접속하는 fred (이것은 아마도 유용하
지 않을 것이다)
'144.155.166.177'/'fred' : 144.155.166.177의 IP 주소에서 접속하는 fred
'144.155.166.%'/'fred' : 144.155.166 클래스 C 서브넷의 모든 호스트에서 접속하는 fred


Host 필드에서 IP에 와일드 카드를 사용할 수 있기 때문에(예를 들어 '144.155.166.%' 는
서브넷의 모든 호스트에 적용된다) 144.155.166.somewhere 와 같은 호스트 이름을 이용하여
부당하게 이용할 가능성이 생길 수 있다. 이러한 것을 막기 위해 mysql은 숫자와 도트(.)으
로 시작하는 호스트이름은 허용하지 않는다. 1.2.foo.com 과 같은 호스트라면 이러한 호스
트이름은 승인(grant) 테이블의 Host 컬럼과 매치되지 않는다. IP 숫자만이 IP 와일드 카드
값과 매치시킬 수 있다.

만약 한개 이상의 user table 목록이 있다면 서버는 어떻게 user table을 선택할까? 이런
경우에는 user table의 정렬 순서에 따라 해결을 하며 , 정열은 서버가 시작할때 수행이 된
다. user table이 다음과 같다고 가정해보자:


+-----------+----------+-
| Host      | User     | ...
+-----------+----------+-
| %         | root     | ...
| %         | jeffrey  | ...
| localhost | root     | ...
| localhost |          | ...
+-----------+----------+-



서버가 테이블을 읽을 때, 먼저 특정하게 지정된 값이 있는 호스트부터 목록을 정열한다.
(Host 컬럼에서 '%'는 "모든 호스트"를 의미하여 최소한도로 지정하는 것이다) 목록에서 호
스트값이 같으면 먼저 특정하게 지정된 사용자가 있는 것부터 정열한다.(공백으로 되어 있
는 User 값은 "모든 사용자"를 의미하여 최소한도로 지정하는 것이다.) 이렇게 하면 정열된
user 테이블은 다음과 같다:



+-----------+----------+-
| Host      | User     | ...
+-----------+----------+-
| localhost | root     | ...
| localhost |          | ...
| %         | jeffrey  | ...
| %         | root     | ...
+-----------+----------+-


정열된 순서에 따라 매칭 알고리즘이 적용되며 먼저 매칭되는 것을 사용한다. localhost에
서 jeffrey가 연결을 하려할때, Host 컬럼에서 'localhost' 목록이 먼저 매칭된다. 물론 사
용자 이름이 공백인 목록은 연결하는 호스트네임과 사용자 이름에 매칭된다. ('%'/'jeffrey
' 목록 또한 매칭이 된다. 그러나 테이블에서 처음으로 매칭되는 것은 아니다.)


다른 예제가 있다. user 테이블이 다음과 같다고 가정해보자:

+----------------+----------+-
| Host           | User     | ...
+----------------+----------+-
| %              | jeffrey  | ...
| thomas.loc.gov |          | ...
+----------------+----------+-


정열된 테이블은 다음과 같다:

+----------------+----------+-
| Host           | User     | ...
+----------------+----------+-
| thomas.loc.gov |          | ...
| %              | jeffrey  | ...
+----------------+----------+-

첫번째로 thomas.loc.gov에서 jeffrey가 연결하는 것이 매칭되며, whitehouse.gov 에서 jef
frey가 연결하는 거은 두번째로 매칭이 된다.

서버에 연결할 때 문제가 생기면, user 테이블을 출력하여 어떤 것이 먼저 매칭되는지 직접
정열을 하면 된다.


6.6 접근 제어, 2단계 : 요청 인증

{{}}연결되었다면 서버는 2단계로 들어간다. 연결이 성사되었을 때 각 요구에 대해 사용자가 수
행하려는 연산의 유형에 기반하여 서버는 사용자가 충분한 권한을 가지고 있는지 점검한다.
여기서 승인 테이블의 권한 필드가 작동한다. 권한은 user, db, host, table_priv, columns
_priv 테이블의 정보를 사용한다. GRANT 와 REVOKE 명령을 이용하여 권한 테이블을 다룰 수
있다. 7.25 [GRANT] 참고. (이전에 보았던 각 권한 테이블의 필드 목록을 참고하는 것이 도
움이 될 것이다; 6.4[Privilege] 참고.)

user 테이블의 승인 권한은 사용자에게 전체적인 기반을 제공하며 현재의 데이터가 어떤 것
인지와는 상관이 없다. 예를 들어, user 테이블에서 사용자에게 delete 권한을 승인했다면
서버 호스트에서 어떤 데이터베이스의 레코드라도 삭제할 수 있다! 다르게 말해서 user 테
이블 권한은 슈퍼유저 권한이며, 슈퍼유저(서버나 데이터베이스 관리자 등)에게만 user 테
이블에 대한 권한을 승인하는 것이 좋다. 다른 사용자에게는 user 테이블에서 권한을 'N'로
설정하고, db와 host 테이블을 사용하여 특정 데이터베이스에 기반한 권한승인을 하는게 좋
다.

db 와 host 테이블은 특정 데이터베이스의 권한을 승인한다. 각 테이블의 Host 와 Db 필드
에서 와일드카드 문자 '%' 와 '_' 를 사용할 수 있으며 값이 공백이면 필드 범위(scope fie
lds)에서 모든 값을 허용한다. '%' Host 값은 "모든 호스트"를 의미한다. db 테이블에서 Ho
st 값이 공백이면 "host 테이블에서 더 자세한 정보를 문의하라"는 의미이다. A '%' or bla
nk Db value in the host table means or "any database." (** or의 뜻이 무엇인지 모르겠
네요. host 테이블에서 Db 의 값이 '%' 또는 공백이면 "모든 데이터베이스"를 의미한다는
말 같은데요 **) User 값이 공백이면 익명 사용자로 간주된다.

서버가 시작할 때 db 와 host 테이블을 읽고 정열을 한다.(동시에 user 테이블을 읽는다.)
db 테이블은 Host, Db, User 순으로 필드 범위를 정열하며 host 테이블은 Host, Db 순으로
필드 범위를 정열한다. user 테이블과 같이 특정하게 지정되어 있는 값이 먼저 정열되고 최
소한도로 지정된 값이 나중에 정열된다. 서버에서 매칭되는 목록을 찾을때, 가장 먼저 발견
한 것을 사용한다.

tables_priv 와 columns_priv 테이블은 특정한 테이블과 컬럼에 관련된 권한을 승인한다.d
b와 host 테이블의 Host 필드와 같이 와일드카드를 Host 필드에서 사용할 수 있다. 그렇지
만 Db, Table_name, Column_name 필드에서는 와일드카드나 공백값을 사용할 수 없다.

Host 테이블에서만 와일드카드를 사용할 수 있지만 tables_priv 와 columns_priv 테이블은
db 테이블과 비슷하게 정열이 되며 정열은 간단하다.

요청 인증 과정은 아래에서 설명한다. 접근-점검 소스 코드에 친숙하다면, 여기서 설명하는
것은 코드에서 사용된 알고리즘과는 약간 다르다는 것을 알 수 있다.여기서의 설명은 코드
가 실제로 작동하는 방식과 동일하다. 단지 설명을 간단하게 하는데서 차이가 있는 것이다.

관리자 요청에 대해서(shutdown, reload 등) 서버는 단지 user 테이블만 체크를 한다. 왜냐
면 user 테이블에서만 관리자 권한을 지정하기 때문이다. 목록에서 요청된 연산을 허용하면
접근이 허용되며 아닌 경우에는 접근이 거부된다.예를 들어, mysqladmin shutdown을 실행하
고자 하는데 user 테이블 목록에서는 사용자에게 shutdown 권한을 승인하지 않으면, db나 h
ost 테이블을 체크하지 않더라도 접근이 거부된다. (이러한 테이블에는 Shutdown_priv 컬럼
이 없기 때문에 이렇게 할 필요도 없다)

데이터베이스와 관련된 요청에 대해(insert, update 등) 서버는 먼저 user 테이블 목록에서
사용자의 전체(슈퍼유저) 권한을 점검한다. 목록에서 요청한 연산을 허용하면 접근이 승인
된다.

user 테이블에서 전체적인 권한이 불충분하면, 서버는 db 와 host 테이블을 점검하여 데이
터베이스에 관련된 권한을 결정한다:

1. 서버는 db 테이블에서 매칭되는 Host, Db, User 필드를 찾는다. 연결하려는 사용자의 호
스트 이름과 Mysql 사용자 이름이 Host 와 User에 매칭되다. 사용자가 접근하기 원하는 데
이터베이스는 Db 필드에 매칭된다. 적합한 Host 와 User 목록이 없으면 접근은 거부된다.

2. 매칭되는 db 테이블 목록이 있고 Host 필드가 공백이 아니면, 목록은 사용자의 데이터베
이스 관련 권한을 정의한다.


3. 매칭되는 db 테이블 목록의 Host 필드가 공백이면, host 테이블에서 어떤 호스트가 데이
터베이스에 접근할 수 있는지 판단한다는 것을 의미한다. 이런 경우, 더 자세한 정보를 위
해 host 테이블에서 매칭되는 Host 와 Db 필드를 찾는다. host 테이블에 매칭되는 목록이
없으면 접근은 거부된다. 매칭되는 목록이 있으면 사용자의 데이터베이스 관련 권한은 db
와 host 테이블 목록에서 권한을 intersection 하여 결정된다.(** insertection은 교집합을
생각하면 되지요. and 조건 **) 다시 말해서, db와 host 테이블 둘 다 'Y'로 되어있을 때
권한이 설정된다.이러한 방법으로 db 테이블에서 일반적인 권한을 승인할 수 있으며, 그러
고나서 host 테이블 목록을 사용해 host를 기반으로 하여 선택적으로 권한을 제한할 수 있
다.)

db 와 host 테이블 목록을 이용해 데이터베이스와 관련된 권한 승인을 결정한 후, 서버는
이러한 정보를 user 테이블에서 승인한 전체적인 권한에 추가한다. 그 결과가 요청한 연산
을 허용하면 접근이 허용된다. 다른 방법으로, 서버는 tables_priv 와 columns_priv 테이블
에서 사용자의 테이블과 컬럼 권한을 점검하고 사용자의 권한에 추가한다. 그 결과에 따라
접근이 허용되거나 거부된다.

왜 서버에서 전체적인 사용자 엔트리 권한에 데이터베이스, 테이블, 컬럼에 관련된 권한을
추가하는지가 명확하지 않다.... 이런 경우 사용자 권한은 초기에 요청된 연산에 대하여 불
충분하다... (It may not be apparent why the server adds the database-, table- and col
umn-specific privileges to the global user entry privileges for those cases in which
the user privileges are initially found to be insufficient for the requested operatio
n.) 요청은 한가지 유형 이상의 권한이 필요하기 때문이다. 예를 들어, INSERT ... SELECT
문을 수행할 때 insert 와 select 권한 둘 다 필요하다. 사용자의 권한은 user 테이블에서
한가지 권한을 승인하고 db 테이블 엔트리에서 다른 권한을 승인할 것이다. 이런 경우, 사
용자는 이러한 요청을 수행하기 위해 필요한 권한을 가지고 있다. 그렇지만 서버는 자체적
으로 다른 테이블에 대해서는 .....(In this case, you have the necessary privileges to
perform the request, but the server cannot tell that from either table by itself;) ;
두 엔트리에 의해 승인된 권한이 조합되어야 한다.

host 테이블은 "안전한" 서버 목록을 유지하는데 사용할 수 있다. TcX에서는, host 테이블
에는 지역 네트웍의 모든 시스템이 포함되어 있다. 여기서는 모든 권한이 허용된다.

안전하지 않는 호스트를 가리키기 위해 host 테이블을 사용할 수 있다. 안전하다고 생각되
지 않는 공개 지역에 위치한 public.your.domain 시스템이 있다고 가정해보자. 사용자는 사
용자 네트웍의 모든 호스트에 접근할 수 있으며, host 테이블 엔트리가 다음과 같은 시스템
만 제외한다 :

+--------------------+----+-
| Host               | Db | ...
+--------------------+----+-
| public.your.domain | %  | ... (all privileges set to 'N')
| %.your.domain      | %  | ... (all privileges set to 'Y')
+--------------------+----+-

당연히 접근 권한이 원하는대로 되어 있는지 언제나 승인 테이블에서 목록을 테스팅해야 한
다. (예를 들어 mysqlaccess 를 사용)



6.7 권한 변경시 적용 방법


mysqld 가 시작할 때, 모든 승인 테이블 내용이 메모리로 올라가고 이때부터 유효하게 된
다.

GRANT, REVOKE, SET PASSWORD 를 이용해 승인 테이블에 변경을 하면 바로 서버에서 인식을
한다.

권한 테이블을 직접 변경했다면(INSERT, UPDATE 등을 사용하여), 서버에서 승인 테이블을
재가동하도록 하기 위해 FLUSH PRIVIEGES 문이나 mysqladmin flush-privileges 를 실행해야
한다.그렇게 하지 않으면 서버를 다시 시작하기 전까지 변경된 권한이 적용되지 않는다.

서버에서 권한 테이블이 변경되었다는 것을 감지했을 때, 이미 존재하던 클라이언트 연결은
다음과 같이 영향을 받는다:

1. 테이블과 컬럼 권한 변경은 클라이언트의 다음 요청부터 적용된다.
2. 데이터베이스 권한 변경은 다음의 USE db_name 명령부터 적용된다.
3. 전체적인 권한과 비밀번호 변경은 클라이언트가 다음에 연결할 때부터 적용된다.
{{}}

6.8 초기 mysql 권한설정

mysql을 설치하고 나서, mysql_install_db 스크립트를 실행해서 초기 접근 권한을 설정해야
한다. 4.7.1 [Quick install] 참고. mysql_install_db 스크립트는 mysqld 서버를 시작하고,
다음과 같이 승인 테이블의 권한을 초기화한다:

- mysql root 사용자는 슈퍼유저이며 모든 것을 할 수 있다. 로컬 호스트에서만 연결할 수
있다.
주의 : 처음에 root 비밀번호는 비어있다. 그래서 누구나 비밀번호없이 root로 연결할 수있
고 모든 권한을 승인받는다.

- 익명 사용자는 'test' 나 'test_' 로 시작하는 데이터베이스에 대한 모든 권한을 승인받
는다. 모든 사용자가 로컬 호스트에서 연결할 수 있으며 익명 사용자로 간주된다.

- 다른 권한은 거부된다. 예를 들어 일반 사용자는 mysqladmin shutdown 이나 mysqladmin p
rocesslist 를 사용할 수 없다.


설치했을 때 초기 권한이 폭넓게  설정되어 있기 때문에 가장 먼저 mysql root 사용자의 비
밀번호를 설정해야 한다. 다음과 같이 설정하면 된다. (PASSWORD() 함수를 이용해 비밀번호
를 설정해야 한다!):

shell> mysql -u root mysql
mysql> UPDATE user SET Password=PASSWORD('new_password')
           WHERE user='root';
mysql> FLUSH PRIVILEGES;

or

shell> mysqladmin -u root password new_password
(** PASSWORD() 라는 함수를 사용하지 않아도 되므로 편리함. 또한 SQL문에서 grant 명령을
이용해 설정할 수도 있지요 **)

첫번째 방법을 사용하면 직접 user 테이블의 비밀번호를 업데이트한다. 이경우 서버가 다시
승인 테이블을 읽도록 해야 한다.(FLUSH PRIVILEGES 사용). 왜냐하면 다른 방법으로는 변경
사항을 알릴 수 없기 때문이다.

(** 승인 테이블을 다시 읽지 않아서 이전에 설정했던 비밀번호가 제대로 안되는 경우가 있
을 것입니다. 꼭 기억하고 있어야해요 **)

root 비밀번호가 설정되었으면 서버에 root로 접속할때마다 비밀번호를 명시해야 한다.

추가로 셋업을 하거나 테스트할 때는 비밀번호를 설정할 필요가 없기 때문에 root 비밀번호
를 빈값으로 남겨두고 싶을 것이다. 그렇지만 실제 작업을 하기 전에는 반드시 비밀번호를
설정했는지 확인해야 한다.

기본권한을 어떻게 설정하는지 mysql_install_db 스크립트를 살펴보자. 다른 사용자에게 권
한을 어떻게 설정할지 이것을 기본으로 사용할 수 있다.

위에서 설명한 것과 다르게 초기 권한을 설정하기 원하면, mysql_install_db 스크립트를 실
행하기 전에 수정하면 된다.

완전하게 승인 테이블을 다시 만들기 위해 mysql 데이터베이스를 포함하는 디렉토리의 '*IS
M' 과 '*.ISD' 파일을 제거해야 한다. (이 디렉토리는 database 디렉토리에서 'mysq''이라
는 이름이 붙어있다. mysqld --help 해서 database 디렉토리의 목록을 볼 수 있다.) 원하는
대로 권한을 수정한 후 mysql_install_db 스크립트를 실행하자.


6.9 mysql에 새로운 사용자 권한 추가하기

두가지 방법으로 사용자를 추가할 수 있다 : GRANT 문 사용 또는 mysql 승인 테이블 직접
조작. GRANT 문을 사용하는 것이 더 선호되는 방법이다.

아래의 예제는 새로운 사용자를 설정하기 위해 어떻게 mysql 클라이언트를 사용하는지 보여
준다. 이 예제는 이전에 설명했던것과 같이 기본값에 따라 권한을 설정하는 것으로 가정한
다. 이것은 설정을 바꾸기 위해 mysqld가 실행되고 있는 같은 시스템에 있어야 한다는 것을
말한다. (**초기값은 localhost에서만 접속 가능하므로**) 또한 mysql root 사용자로 접속
해야 하고 root 사용자는 mysql 데이터베이스에 대한 insert 권한과 reload 관리자 권한이
있어야 한다. root 사용자의 비밀번호를 바꾸었으면, 아래와 같이 mysql 명령행 상태에서
비밀번호를 명시해야 한다.

GRANT 문을 이용해 새로운 사용자를 추가할 수 있다:

shell> mysql --user=root mysql
mysql> GRANT ALL PRIVILEGES ON *.* TO monty@localhost
           IDENTIFIED BY 'something' WITH GRANT OPTION;
mysql> GRANT ALL PRIVILEGES ON *.* TO monty@"%"
           IDENTIFIED BY 'something' WITH GRANT OPTION;
mysql> GRANT RELOAD,PROCESS ON *.* TO admin@localhost;
mysql> GRANT USAGE ON *.* TO dummy@localhost;

위 GRANT 문에서는 세 명의 사용자를 설정한다:

monty : 어느 곳에서든 서버에 연결할 수 있는 완전한 슈퍼유저이지만 비밀번호를 사용해야
한다. 우리는 monty@localhost 와 monty@"%"를 사용한 GRANT 문에 대해서 반드시 논의를 해
야 한다. localhost 목록을 추가하지 않으면, mysql_install_db 에 의해 생성된 localhost
의 익명 사용자 목록(등록?)이 로컬 호스트에서 접속할때 우선권을 갖는다. 왜냐하면 지정
된 Host 필드 값이 있으며 정열 순서에서 먼저 오기 때문이다. (** 승인 테이블의 정열 순
서가 특정한 Host를 지정한 것부터 시작하는 것을 기억하자.

admin : 비밀번호 없이 localhost에서 접속할 수 있으며 reload와 process 관리자 권한을
승인받은 사용자. 이경우 사용자가 mysqladmin processlist 뿐만 아니라 mysqladmin reloa
d, mysqladmin refresh, mysqladmin flush-* 명령을 실행할 수 있다.데이터베이스와 관련된
권한은 승인되지 않았다. 이것은 추가적인 GRANT 문을 사용해 나중에 승인할 수 있다.

dummy : 비밀번호없이 연결할 수 있지만 오직 localhost에서만 연결 가능한 사용자. 권한
유형(privilege type)이 USAGE 이기 때문에 전체적인 권한이 'N'로 설정되어 있다. USAGE
는 아무런 권한도 설정하지 않는다. 나중에 데이터베이스와 관련된 권한을 승인할 수 있다.

또한 동일한 사용자 접근 정보를 INSERT 문을 통해 직접 추가할 수 있으며 이경우에는 서버
가 승인 테이블을 다시 읽도록 알려주어야 한다.(**FLUSH PRIVILEGES 사용**)

shell> mysql --user=root mysql
mysql> INSERT INTO user VALUES('localhost','monty',PASSWORD('something'),
                'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y')
mysql> INSERT INTO user VALUES('%','monty',PASSWORD('something'),
                'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y')
mysql> INSERT INTO user SET Host='localhost',User='admin',
                 Reload_priv='Y', Process_priv='Y';
mysql> INSERT INTO user (Host,User,Password)
                        VALUES('localhost','dummy',");
mysql> FLUSH PRIVILEGES;

mysql 버전에 따라 위에서 'Y' 값이 다를 수 있다는 것을 기억하자. 3.22.11 버전 이후에서
사용할 수 있는 확장된 INSERT 문은 여기서 admin 사용자에게 사용되었다.

슈퍼유저를 설정하기 위해 권한필드를 'Y'로 한 user 테이블 목록만 만들면 된다는 것을 기
억하자. db 나 host 테이블 목록은 필요없다. (** 관리자 권한은 db나 host 테이블과는 전
혀 관련이 없다. db는 접속할 수 있는 데이터베이스에 대해 상세하게 설정하고 host 테이블
은 db테이블을 좀 더 정교하게 설정하기 위해 필요한 것이다. 관리자 권한은 오직 user 테
이블만 관련되어있다 **)

마지막 INSERT 문(dummy 사용자)에서는 user 테이블의 권한 컬럼이 명확하게 설정되지 않았
다. 왜냐면 이 컬럼의 기본값은 'N'로 되어 있기 때문이다.

다음의 예제에서는 custom 이라는 사용자를 추가한다. custom은 localhost, server.domain,
whitehouse.gov에서 접속할 수 있다. localhost에서는 bankaccount 데이터베이스에만 접속
할 수 있으며 whitehouse.gov에서는 expenses 데이터베이스에, 모든 세 호스트상에서는 cus
tomer 데이터베이스에 접속하길 원한다. 모든 세 호스트상에서 stupid라는 비밀번호를 사용
하길 원한다.

GRANT 문을 이용 이러한 사용자 권한을 설정하기 위해 다음의 명령을 실행하자:

shell> mysql --user=root mysql
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
           ON bankaccount.*
           TO custom@localhost
           IDENTIFIED BY 'stupid';
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
           ON expenses.*
           TO custom@whitehouse.gov
           IDENTIFIED BY 'stupid';
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
           ON customer.*
           TO custom@'%'
           IDENTIFIED BY 'stupid';

승인 테이블을 직접 수정해 사용자 권한을 설정하려면 다음의 명령을 사용하자. (마지막에
FLUSH PRIVILEGES 를 사용해야 한다는 것을 기억하자):

shell> mysql --user=root mysql
mysql> INSERT INTO user (Host,User,Password)
       VALUES('localhost','custom',PASSWORD('stupid'));
mysql> INSERT INTO user (Host,User,Password)
       VALUES('server.domain','custom',PASSWORD('stupid'));
mysql> INSERT INTO user (Host,User,Password)
       VALUES('whitehouse.gov','custom',PASSWORD('stupid'));
mysql> INSERT INTO db
       (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,
        Create_priv,Drop_priv)
       VALUES
       ('localhost','bankaccount','custom','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO db
       (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,
        Create_priv,Drop_priv)
       VALUES
       ('whitehouse.gov','expenses','custom','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO db
       (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,
        Create_priv,Drop_priv)
       VALUES('%','customer','custom','Y','Y','Y','Y','Y','Y');
mysql> FLUSH PRIVILEGES;
~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~

처음의 세가지 INSERT 문은 custom 사용자가 비밀번호를 사용하여 다양한 호스트에서 접속
할 수 있도록 user 테이블 목록을 추가한다. 그렇지만 그에게 어떠한 퍼미션도 승인하지 않
는다. (모든 권한은 기본값으로 'N' 이다) 다음의 세가지 INSERT 문은 적절한 호스트에서
접속을 할 때, custom 에게 bankaccount, expenses, customer 데이터베이스에 대한 권한을
승인하는 db 테이블 목록을 추가한다. 일반적으로 승인 테이블을 직접 수정하였으면, 변경
된 권한을 적용하기 위해 서버가 승인 테이블을 다시 읽도록 해 주어야 한다.

특정한 사용자가 특정한 도메인의 시스템에서 접속할 수 있도록 설정하고자 한다면, 다음과
같이 GRANT 문을 설정할 수 있다:

mysql> GRANT ...
           ON *.*
           TO myusername@"%.mydomainname.com"
           IDENTIFIED BY 'mypassword';

승인 테이블을 직접 수정하려면 다음과 같이 한다:

mysql> INSERT INTO user VALUES ('%.mydomainname.com', 'myusername',
           PASSWORD('mypassword'),...);
mysql> FLUSH PRIVILEGES;
~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~

승인 테이블을 다루기 위해 xmysqladmin, mysql_webadmin, xmysql 프로그램을 사용할 수 있
다. http://www.mysql.com/Contrib 에서 이러한 유틸리티를 찾을 수 있다.


6.10 비밀번호 설정 방법

앞의 예제는 중요한 원칙을 보여준다 : INSERT 나 UPDATE 문에서 공백이 아닌 비밀번호를
저장할 때 반드시 암호화하기 위해 PASSWORD() 함수를 사용해야 한다!! user 테이블은 비밀
번호를 플레인텍스트(**일반 텍스트 파일**)가 아니라 암호화된 형태로 저장하기 때문이다.
이러한 사실을 잊어버리면 다음과 같이 비밀번호를 설정하려고 할 것이다:

shell> mysql -u root mysql
mysql> INSERT INTO user (Host,User,Password)
       VALUES('%','jeffrey','bLa81m0');
mysql> FLUSH PRIVILEGES;

플레인텍스트 값 'bLa81m0' 은 user 테이블에 비밀번호로 저장이 된다.jeffrey라는 사용자
가 이 비밀번호를 사용해 서버에 연결하려고 할 때 mysql 클라이언트를 이 비밀번호를 암호
화해서 그 결과를 서버로 보낸다. 서버는 암호화된 비밀번호('bLa81m0'이 아니다)를 user
테이블의 비밀번호(플레인텍스트 'bLa81m0' 값이다)와 비교한다. 비교는 실패하고 서버는
연결을 거부한다:

shell> mysql -u jeffrey -pbLa81m0 test
Access denied

비밀번호는 user 테이블에 입력될 때 반드시 암호화되어야 하기 때문에, INSERT 문은 다음
과 같이 사용해야 한다:

mysql> INSERT INTO user (Host,User,Password)
       VALUES('%','jeffrey',PASSWORD('bLa81m0'));

또한 SET PASSWORD 문을 사용할 때도 PASSWORD() 함수를 사용해야 한다:

mysql> SET PASSWORD FOR jeffrey@"%" = PASSWORD('bLa81m0');

참고 : PASSWORD() 함수는 비밀번호 암호화를 수행한다. 그렇지만 유닉스에서 비밀번호를
암호화하는 방법과는 다르다. 유닉스 비밀번호와 mysql 비밀번호가 동일할 때 PASSWORD()
가 유닉스 비밀번호 파일(** /etc/passwd 파일 **)에 암호화되어 저장된 값과 같다고 생각
하면 안 된다.

GRANT ... IDENTIFIED BY 문이나 mysqladmin password 명령을 사용해 비밀번호를 설정하면
PASSWORD() 함수는 필요없다. 둘다 비밀번호를 암호화해서 저장한다:

mysql> GRANT USAGE ON *.* TO jeffrey@"%" IDENTIFIED BY 'bLa81m0';
shell> mysqladmin -u jeffrey password bLa81m0

(** 당근, GRANT 문이나 mysqladmin password 명령을 사용하는게 편하겠지요? mysql의 암호
화 알고리즘이 유닉스와 다르듯 유닉스 계정과 mysql의 사용자는 전혀 다르다는 것도 다시
한번 기억하고 있어야 합니다.**)



6.11 접근 거부 에러가 나는 이유

mysql 서버에 연결하려 할 때 접근 거부 에러가 나면, 아래에서 설명하는 것에 따라 해결
방법을 찾을 수 있다:

- 초기 승인 테이블 내용을 설정하기 위해 mysql을 설치한 후 mysql_install_db 스크립트를
실행하였는가? 실행하지 않았다면 스크립트를 실행하자. 6.8 [Default privileges] 참고.
다음 명령을 이용해 초기 권한을 시험해 볼 수 있다:

        shell> mysql -u root test

에러없이 서버에 접속할 수 있을 것이다. mysql 데이터베이스 디렉토리에 'user.ISD' 파일
이 있는지 확인해보아야 한다. (일반적으로 'mysql 설치 디렉토리/var/mysql/user.IDS' 이
다)

- 설치를 새로하고 난후 , 서버에 연결하고 사용자와 접근 권한을 설정해야 한다:

        shell> mysql -u root mysql

초기에 mysql root 사용자만 비밀번호가 없기 때문에 서버에 연결할 수 있다. 보안문제가
있기 때문에, 다른 mysql 사용자를 설정하기 전에 먼저 root의 비밀번호를 설정해야 한다.
root로 접속하려하는데 다음의 에러가 났다고 가정하자:

        Access denied for user: '@unknown' to database mysql

이것은 user 테이블에 User 컬럼 = root 라는 목록이 없고, mysqld가 사용자 클라이언트의
호스트이름을 해석할 수 없다는 것을 의미한다. 이런 경우 --skip-grant-tables 옵션을 이
용해 서버를 다시 시작해야 하고 '/etc/hosts' 를 편집하거나 '\windows\hosts' 파일에 사
용자 호스트 목록을 추가해야 한다.

- 3.22.11 이전 버전에서 3.22.11이나 이후 버전으로 업데이트했다면, mysql_fix_privilege
_tables 스크립트를 실행했는가? 하지 않았다면 실행하자. mysql 3.22.11에서 GRANT 문 기
능이 가능해지면서 승인 테이블 구조가 바뀌었다.

- (INSERT 나 UPDATE 문을 사용해) 승인 테이블을 직접 고쳤고 변화가 아직 반영되지 않은
것으로 보이면, FLUSH PRIVILEGES 문을 사용하거나 mysqladmin flush-privileges 명령을 사
용해 서버가 승인 테이블을 다시 읽도록 해야 한다는 것을 기억하자.그렇지 않으면 서버가
재시작하기 전까지는 변화된 것이 반영되지 않는다. root 비밀번호를 설정하고 나서 권한을
flush 하기까지는 비밀번호를 명시할 필요가 없다. 왜냐면 서버는 아직 비밀번호를 바꾸었
는지 모르기 때문이다.

- 세션 중간에 권한이 변경된 것으로 보이면 슈퍼유저가 바꾸었을 것이다. 승인 테이블을
재시작하는 것은 새로운 클라이언트 접속에 영향을 미치지만 이미 존재하고 있던 연결은 6.
7 [Privileges changes]에서 설명한대로 영향을 미친다.

- 시험하기 위해, mysqld 대몬에  --skip-grant-tables 옵션을 주어 시작하자. 그러고나서
mysql 승인 테이블을 변경할 수 있고 변경된것이 원하는대로 작동하는지를 체크하는 mysqla
ccess 스크립트를 사용할 수 있다. 원하는대로 수정이 되었으면 mysqld 서버가 새로운 승인
테이블로 시작할 있도록 mysq1admin flush-priveleges 를 실행한다.
주의 : 승인테이블을 재로딩하는 것은 --skip-grant-tables 옵션을 무효화한다. 이를 통해
서버를 다운시키고 다시 재시작하지 않고도 승인 테이블을 시작할 수 있다.

- 펄, Python, ODBC 프로그램에서 접근하는데 문제가 있다면, mysql -u user_name db_name 
또는 mysql -u user_name -pyour_pass db_name 으로 서버에 접속을 시도해보자. (-p 와 비
밀번호사이에는 공백이 없다는 것을 기억하자. 또한 --password=your_pass 형태로도 사용할
수 있다) mysql 클라이언트로 접속이 되면 프로그램에 문제가 있는 것이며 접근 권한에는
문제가 없다.

- 비밀번호가 제대로 작동하지 않으면, INSERT, UPDATE, SET PASSWORD 문에서 비밀번호를
설정하면서 PASSWORD() 함수를 반드시 사용해야 한다는 것을 기억하자. PASSWORD() 함수는
GRANT ... INDENTIFIED BY 문이나 mysqladmin password 명령을 사용했다면 불필요하다. 6.1
0 [Passwords] 참고.

- localhost 는 지역 호스트 이름과 같은 말이다. 또한 호스트를 명백하게 설정하지 않은
경우 클라이언트에서 연결하려는 호스트의 기본값이다. 그러나 MIT-pthreads를 사용하는 시
스템에서는 localhost로의 연결이 제대로 작동하지 않는다. (localhost 연결은 유닉스 소켓
을 통해 만들어진다. 그렇지만 MIT-pthreads에서는 유닉스 소켓을 지원하지 않는다.) 이와
같은 시스템에서 문제를 피하려면, 서버에 호스트 이름을 명확하게 말해주기 위해 --host
옵션을 사용해야 한다. 그러면 mysqld 서버에 TCP/IP 연결을 만든다. 이경우, 서버 호스트
의 user 테이블 목록에 실제 호스트이름이 있어야 한다. (서버와 동일한 호스트에서 클라이
언트 프로그램을 실행한다고 하더라도 마찬가지이다.)

- mysql -u user_name db_name 으로 데이터베이스에 접속하려 할 때 접근 거부 에러가 나
면, user 테이블에 문제가 있을 것이다. mysql -u root mysql 를 실행하여 점검하고 다음의
SQL 문을 사용하자:

        mysql> SELECT * FROM user;

여기서 사용자의 호스트이름과 mysql 사용자 이름과 맞는 Host 와 User 컬럼의 목록이 포함
되어 있어야 한다.

- Access denied 에러 메시지는 접속하려는 사용자와 호스트 이름, 그리고 비밀번호를 사용
했는지 여부를 보여줄 것이다. 일반적으로 user 테이블에 에러 메시지에서 보여준 호스트
이름과 사용자 이름과 정확하게 맞는 목록을 가지고 있어야 한다.


- 다른 시스템에서 mysql 서버에 접속할 때 다음의 에러 메시지가 나오면, user 테이블에
연결을 하려고 하는 호스트 이름이 없다는 것을 말한다:

        Host ... is not allowed to connect to this MySQL server

(서버 호스트에서!) 명령행 유틸리티인 mysql을 사용하여 user 테이블에 연결하고자 하는
사용자/호스트 이름을 추가하여 해결할 수 있다. mysql 3.22 를 사용하고 있지 않고 연결하
고자 하는 시스템의 IP 숫자나 호스트 이름을 모른다면, user 테이블에 Host 컬럼 값으로
'%' 목록을 입력하고 서버 시스템에서 --log 옵션을 사용해 mysqld 를 재시작하자. 클라이
언트 시스템에서 연결을 시도한 후 mysql 로그에는 어떻게 실제로 연결을 했는지에 대한 정
보가 들어있다. (그러고나서 '%'를 로그에 나온 실제 호스트 이름으로 바꾼다. 그렇지 앟으
면 보안에 문제가 생길 수 있다.)

- mysql -u root test 는 작동을 하는데 mysql -h your_hostname -u root test 에서 접근에
에러가 나면, user 테이블에 정확한 호스트 이름이 없을 것이다. 일반적인 문제는 user 테
이블의 Host 값에는 완전하지 않은 호스트 이름(** 도메인은 빼고 호스트 이름만 넣은 경우
**) 이 들어가 있는데 시스템의 네임 해석 루틴은 FQDN(fully-qualified domain name - **
완전한 도메인 이름과 호스트 이름을 사용**)으로 처리하는 경우이다(또는 거꾸로 해석).예
를 들어, user 테이블에 호스트 이름이 'taejun' 으로 되어있는데, DNS는 mysql에 호스트
이름이 'taejun.subnet.se'라고 알려줄 수 있으며 이경우는 제대로 작동하지 않을 것이다.
user 테이블에 Host 컬럼 값으로서 해당하는 IP 숫자나 호스트 이름을 추가하자. (대신, us
er 테이블에 Host 값으로 와일드카드 문자를 포함할 수 있다. 예를 들어, 'taejun.%'. 그러
나 호스트이름 끝에 '%'를 사용하는 것은 안전하지 않으며 권하지도 않는다!)

- mysql -u user_name test 는 작동하는데 mysql -u user_name other_db_name 은 작동하지
않는다면 db 테이블에 other_db_name 목록이 없는 경우이다.

- 서버 시스템에서 mysql -u user_name db_name 은 작동을 하는데, 다른 클라이언트 시스템
에서 mysql -u user_name db_name 은 작동을 하지 않는다면, user 테이블이나 db 테이블에
클라이언트 시스템의 목록이 없는 것이다.

- 왜 Access denied 가 나는지 해결하지 못하면, user 테이블에서 와일드카드('%' 또는 '
_')를 포함하고 있는 Host 값을 가진 목록을 모두 제거하자. 매우 일반적인 에러는 Host='%
' 그리고 User='some user'로 입력을 하고나서 , 이렇게 하면 같은 시스템에서 연결할 때 l
ocalhost를 지정할 수 있도록 허용한다고 생각하는 것이다. 이것이 제대로 작동하지 않는
이유는 기본 권한에 Host='localhost' 와 User='' 목록이 포함되어 있기 때문이다. Host 값
에 '%' 보다 더 분명한 'localhost' 목록이 있기 때문에, localhost에서 접속할 때 새로운
목록보다 먼저 선택이 된다. 정확한 절차는 두번째 항목으로 Host='localhost' 와 User='so
me_user' 를 입력하거나 Host='localhost' 와 User='' 를 제거하는 것이다.

- 다음의 에러가 나는 경우:

        Access to database denied

db 나 host 테이블에 문제가 있을 것이다. 선택한 db 테이블의 목록에 Host 컬럼이 비어있
다면, host 테이블에 db 테이블 목록에 적용되는 호스트 이름이 있는지를 확인해야 한다.
(** 일반적으로 db 테이블에 host 값을 비워두는 경우, host 테이블에서 접근하는 호스트를
제어할 수 있다 **)

- 다음의 에러가 나는 경우:


        Access to database denied

SELECT ... INTO OUTFILE 또는 LOAD DATA INFILE 의 SQL 문을 사용하는 경우, user 테이블
목록에서 file 권한이 설정되어 있지 않았을 것이다.

- 다른 모든 것이 실패하였을 경우, mysqld 대몬을 디버깅 옵션으로 시작하자. (예를 들어,
--debug=d,general,query) 그러면 각 명령에서 생기는 정보와 함께, 접속을 시도하는 호스
트와 사용자에 대한 정보를 출력할 것이다. G.1 [Debugging] 참고.

- mysql 승인 테이블에서 다른 문제에 부딪쳤고 이 문제를 메일링리스트에 알려야겠다고 느
끼면, mysql 승인 테이블을 덤프하여 제공해야 한다. mysqldump mysql 명령을 사용해 테이
블을 덤프할 수 있다. 언제나 mysqlbug 스트립트를 사용하여 문제를 올리자.

- 다음의 에러가 나는 경우, mysqld 대몬이 실행되지 않거나 잘못된 소켓이나 포트로 연결
하려고 시도하는 경우:

        Can't connect to local MySQL server
        Can't connect to MySQL server on some_hostname

먼저 mysqld 대몬이 실제로 작동하는지 ps를 이용해 확인한다. 소켓 파일이 있느지 확인하
고 점검을 해 보아야 한다.(일반적으로 `/tmp/mysql.sock' 임) 또는 telnet host_name 3306
으로 접속을 시도해보자. 더 자세한 정보를 위해 mysqladmin version 과 mysqladmin -h hos
t_name version 을 사용해 볼 수 있다. 물론 참고할 것이 있는지 mysql 데이타 디렉토리의
에러 로그 파일을 점검해보자.

{{}}{{}}{{}}- 클라이언트 프로그램은 설정 파일이나 환경 변수에서 지정한 연결 패러미터를 사용할 수
있다는 것을 기억하자. 명령행에서 지정하지 않았는데 클라이언트가 잘못된 기본 연결 패러
미터를 보내는 것으로 생각되면, 홈 디렉토리에서 환경변수나 '.my.cnf' 파일을 점검해보
자. 비록 여기서 지정한 연결 패러미터와는 관계가 멀지만 시스템의 전반적인 mysql 설정
파일을 점검해 볼 수 있다. 4.14.4 [Option files] 참고. 클라이언트에서 아무런 옵션도 주
지 않았는데 Access denied 에러 메시지가 나오는 경우, 옵션 파일 중에서 예전의 비밀번호
를 지정하지 않았는지 확인해 보자. 4.14.4 [Option files] 참고.




6.12 크랙커에 대비하여 mysql을 안전하게 하는 방법

mysql 서버에 연결할 때 일반적으로 비밀번호를 사용해야 한다. 비밀번호는 연결할 때 단순
한 텍스트로 전송되지 않는다.

서버/클라이언트 연결은 암호화되지 않는다; 모든 정보는 연결을 볼 수 있는 누구라도 읽을
수 있는 텍스트로 전송된다. 이 문제에 대해 걱정이 되면 문제를 어렵게 하기 위해 압축 프
로토콜(mysql 3.22 이상 버전)을 사용할 수 있다. 보안을 더 확실하게 하기 위해 ssh를 설
치할 수 있다. (http://www.cs.hut.fi/ssh 참고) 이것을 이용해 mysql 서버와 클라이언트
사이에 암호화된 TCP/IP 연결을 사용할 수 있다.

mysql 시스템의 보안을 유지하게 위해 다음의 제안을 신중하게 고려하자:

- 모든 mysql 사용자가 비밀번호를 사용. 어떤 사용자가 비밀번호가 없으면 'mysql - u 사
용자이름' 을 이용해 간단하게 그 사용자로 로그인할 수 있다는 것을 기억하자. 이것은 클
라이언트/서버 애플리케이션의 일반적인 작동방법이다. mysql_install_db 스크립트를 실행
하기 전에 이 스크립트를 수정하여 모든 사용자의 비밀번호를 바꿀 수 있다. 또는 mysql ro
ot 사용자의 비밀번호를 바꿀 때는 다음과 같이 하면 된다:

shell> mysql -u root mysql
mysql> UPDATE user SET Password=PASSWORD('new_password')
           WHERE user='root';
mysql> FLUSH PRIVILEGES;

- mysql 데몬을 유닉스의 root 사용자로 시작하지 말자. mysqld 는 다른 사용자가 실행할
수 있다.또한 보안을 더 엄격하게 하기 위해 mysql이라는 유닉스 사용자를 만들 수 있다.
다른 유닉스 사용자로 mysqld를 실행하면 user 테이블에서 root 사용자 이름을 바꿀 필요가
없다. mysqld를 다른 유닉스 사용자가 시작하기 위해 mysql.server 스크립트를 수정하면 된
다. 일반적으로 su 명령을 사용한다. 더 자세한 내용은 16.7 [Changing mysql user] 참고.

- mysqld를 실행할 수 있는 사용자만이 데이터베이스 디렉토리에 읽기/쓰기 권한을 가지고
있는지 확인.

- 모든 사용자에게 process 권한을 주지 말자. mysqladmin processlist 을 출력하면 현재
실행하는 쿼리의 내용을 볼 수 있다. 그러므로 이러한 명령을 실행할 권한이 있는 사용자는
다른 사용자의 UPDATE user SET password=PASSWORD(_'no_secure') 질의를 볼 수 있다.mysql
은 process 권한을 가진 사용자를 위해 추가적인(extra) 연결을 저장한다.그래서 mysql roo
t 사용자는 모든 일반적인 연결이 사용되었어도 로그인하고 점검을 할 수 있다.

- 모든 사용자에게 file 권한을 주지 말자. 이러한 권한이 있는 사용자는 mysqld 대몬의 권
한이 있는 파일 시스템의 어느 곳에라도 파일을 저장할 수 있다.좀 더 안전하게 하기 위해
SELECT ... INTO OUTFILE 로 생성되는 모든 파일은 모든 사용자가 읽기만 할 수 있으며 이
미 존재하는 파일을 덮어씌울 수 없다.
(** file 권한은 LOAD DATA INFILE , SELECT .. INTO OUTFILE 문을 이용하여 서버에 파일을
저장하고 읽을 수 있는 권한을 허용한다. 이러한 권한을 가진 사용자는 mysql 서버가 읽고
쓸 수 있는 파일을 읽고 쓸 수 있는 권한이 허용된다. 일반 사용자에게 이런 권한을 줄 필
요는 없다. 필요한 부분만 권한을 주는 것이 좋다. 권한을 남용말자. **)

- DNS 를 신뢰하지 못한다면 승인 테입르에서 호스트이름 대신 IP를 사용하자. 기본적으로
mysqld 의 --secure 옵션은 호스트이름을 안전하게 한다. 어떤 경우 와일드카드 문자가 포
함된 호스트이름 값을 사용할때는 매우 조심해야 한다.

- mysql.server 스크립트에서 유닉스 root 사용자의 비밀번호를 넣는다면, 이 스크립트는
오직 root만이 읽을 수 있도록 해야 한다.

다음의 mysqld 옵션은 보안과 관련되어 있다:

--secure : gethostbyname() 시스템 콜에 의해 리턴된 IP 숫자가 원래의 호스트이름을 reso
lve 한 것과 같은지를 점검한다. 이것은 어떤 사람이 다른 호스트 이름을 에뮬레이터해서
접근하는 것을 어렵게 만든다. 이 옵션은 또한 호스트이름이 온전한지에 대한 점검을 추가
한다. 해석하는데 때로는 시간이 많이 걸려서 mysql 3.21에서는 기본적으로 설정이 되어 있
지 않다. mysql 3.22에서는 호스트이름을 캐쉬하고 이 옵션이 기본적으로 설정되어 있다.
(** 함수 gethostbyname()은 호스트 이름을 인자로 받아 그에 해당하는 IP 주소 및 기타 정
보를 해당하는 구조체에 담아 그 구조체의 포인터를 리턴하는 함수입니다. 쉽게 말해서 호
스트 이름을 넣으면 해당 IP 주소를 찾아주지요.**)

--skip-grant-tables : 이 옵션을 사용하면 서버가 권한 시스템을 전혀 사용하지 않는다.
그러면 모든 사용자가 모든 데이터베이스에 접속할 수 있다! (mysqladmin reload 를 실행하
여 실행중인 서버가 승인 테이블을 사용하도록 할 수 있다.)

--skip-name-resolve : 호스트이름이 해석되지 않는다. 승인 테이블의 모든 Host 컬럼값은
반드시 IP 숫자이거나 로컬호스트이어야 한다.

--skip-networking : 네트웍을 통한 TCP/IP 연결을 허용안함. mysqld와 모든 연결은 유닉스
도메인 소켓을 통해 만들어진다. 이 옵션은 MIT-pthreads를 사용하는 시스템에서는 제대로
작동을 하지 않는다. 왜냐면 MIT-pthreads 패키지는 유닉스 소켓을 지원하지 않기 때문이
다. (** 리눅스를 사용하는 사람들에게는 상관없겠죠? 유닉스 도메인 소켓을 지원하니깐.
이와 비슷하게 postgres도 6.3버전 이후부터인가요? 기본적으로 유닉스 도메인 소켓으로 바
뀌었지요. **)
9. mysql 서버 기능(functions)

9.1 mysql에서 지원하는 언어
mysqld의 에러 메시지는 다음의 언어로 나타날 수 있습니다. : Czech, Dutch, English(기본
값), 기타 등등 (** 기타 언어는 매뉴얼 참고. 한글, 일본어 등 동양권은 여기서 빠져있습니
다. 9.1.1 절을 참고하세요. **)

mysqld를 특정한 언어로 시작하려면 --language=lang 또는 -L lang 옵션을 사용하면 됩니
다.

예> # mysqld--language=swedish

또는

# mysqld --language=/usr/local/share/swedish

모든 언어는 소문자로 표기합니다.

언어 파일은 (기본값으로) 'mysql_base_dir/share/LANGUAGE/'에 있습니다.

에러 메시지 파일을 업데이트하려면 'errmsg.txt' 파일을  편집하고 'errmsg.sys' 파일을 만
들기 위해 다음의 명령을 사용합니다.

# comp_err errmsg.txt errmsg.sys

만약 mysql의 새로운 버전으로 업그레이드하면 새로운 'errmsg.txt' 파일로 위와 같은 과정
을 거쳐야 합니다.


9.1.1 데이터와 정열에 사용하는 문자 셋
기본적으로 mysql은 ISO-8859-1 (Latin 1) 문자 셋을 사용합니다. 이 문자셋은 미국과 서유
럽에서 사용하는 문자 셋입니다.

문자셋은 이름에 어떤 문자셋을 사용할 수 있는지 그리고 SELECT 문에서 ORDER BY 와
GROUP BY 문을 사용할 때 어떻게 정렬되는지를 결정합니다.

컴파일할때 configure에서 --with-charset=charset 옵션을 사용하여 문자셋을 바꿀 수 있습
니다. 자세한 것은 4.7.1 을 참고하세요.
(** 한글을 사용하기 위해서 이 부분은 중요합니다. 우리는  컴파일할때 문자셋을 sjis로 바
꾸어 주면 됩니다. sjis는 일본어 2바이트 문자  셋입니다. 이렇게 해야 정렬이 제대로 되고
정규표현식을 사용할 수 있습니다. **) 


9.1.2 새로운 문자셋 추가
** 생략 **


9.1.3 멀티바이트 문자 지원
멀티 바이스 문자셋을 만들면 _MB 매크로를 사용할 수 있습니다.

** 생략 **


9.2 업데이트 로그
mysqld를 시작할 때 --log-update=file_name 옵션을  사용하면 mysqld는 데이타를 업데이
트한 모든  sql 문을  포함하는 로그  파일을 기록합니다.  파일은  data 디렉토리(**  보통
mysql 설치 디렉토리 밑의 data  디렉토리 **)에 기록되며 file_name.#  형태로 됩니다. #은
mysqladmin refresh 나 mysqladmin flush -logs, FLUSH LOGS 문, 또는 서버를 재시작할
때마다 증가됩니다.

--log 나 -l 옵션을 사용하면 파일 이름은 'hostname.log'가 되며, restart나 refreshes를  해
도 새로운 로그 파일이 만들어지지 않습니다. 기본적으로 mysql.server 스크립트는  -l 옵션
으로 mysql 서버를 시작합니다. production enviroment(?)를 사용하여 시작할때 더 나은 성
능이 필요하면 mysql.server에서 -l 옵션을 제거할 수 있습니다.

업데이트 로그는 지능적이서 실제로 데이타가 업데이트될 때만 로그 기록을 남깁니다. 그래
서 WHERE를 사용한 UPDATE나 DELETE에서 해당하는 레코드를 찾지 못하면 로그 파일
에 기록하지 않습니다. 또한 이미 존재하는 값을 사용할때도 Update 문은 무시됩니다.

업데이트 로그 파일에서 데이터베이스를  업데이트하려면 다음과 같이  하면 됩니다. (로그
파일이 'file_name.#'의 형태라고 가정)

# ls -1 -t -r file_name.[0-9]* | xargs cat | mysql

ls는 정확한 순서로 모든 로그 파일을 가져올 때 사용합니다.

이것은 데이터베이스에 손상이 생긴 뒤 백업 파일로 복구할 때 유용하며, 백업과 손상이 생
긴 시간 사이에 일어난 업데이트를 다시 할때(redo) 사용할 수 있습니다.

또한 다른 호스트에 미러링된 데이터베이스를  가지고 있으며 마스터 데이터베이스에  생긴
변화를 복사할때 업데이트 로그를 사용할 수 있습니다.

9.3 mysql 테이블 최대 크기
mysql 자체는 테이블 최대 크기가 4G이며 운영 시스템은 각자의 고유한 파일 크기  제한이
있습니다. 리눅스에서는 현재 2G 입니다. 솔라리스 2.5.1에서는 4G이며,  솔라리스 2.6에서는
1000G가 될 것입니다. 현재 테이블 크기 제한은 4G이거나, (MYSQL 제한) 운영 시스템 제
한입니다. 4G 이상으로 확대하기 위해 앞으로 mysql을  바꿀 것입니다. 부록 F를 참고하세
요.

거대 테이블을 읽기 전용으로 하면  많은 테이블을 하나로 모으고  압축하는 pack_isam 을
사용할 수 있습니다. pack_isam은 일반적으로 테이블을 최소 50%  압축하여 효과적으로 더
큰 테이블을 사용할 수 있습니다. 12.3 [pack_isam]을 참고하세요.

다른 솔루션은 MERGE 라이브러리에 포함되어 있으며  identical 테이블을 모아 하나로 관
리할 수 있습니다. (여기서  Identical은 모든 테이블이 identical  컬럼 정보로 만들어진다는
것을  의미합니다.)  현재  MERGE는  인덱스를  지원하지  않기  때문에  테이블의  모음
(collection)만을 검색하는데만 사용할 수 있습니다. 가까운  시일내에 여기에 인덱스를 추가
할 것입니다.

(** 참고로 읽기 전용 테이블은 mysql에 라이센스를 지불하고 구입했을 때만 만들 수 있습
니다. 단지 읽기만 하는 것은 가능하지만 압축이 된 읽기 전용 테이블은 만들 수  없습니다.
**)
10. mysql의 최대 성능 향상 방법

10.1 버퍼 크기 조정
mysqld 서버가 사용하는 기본 버퍼 크기는 다음의 명령으로 알 수 있다.

shell> mysqld --help

이 명령은 모든 mysqld 옵션의 목록과 설정 변수를 보여준다. 출력되는 내용은 기본값을
포함하고 있으며 다음과 비슷하다.

Possible variables for option --set-variable (-O) are:
back_log              current value: 5
connect_timeout       current value: 5
join_buffer           current value: 131072
key_buffer            current value: 1048540
long_query_time       current value: 10
max_allowed_packet    current value: 1048576
max_connections       current value: 90
max_connect_errors    current value: 10
max_join_size         current value: 4294967295
max_sort_length       current value: 1024
net_buffer_length     current value: 16384
record_buffer         current value: 131072
sort_buffer           current value: 2097116
table_cache           current value: 64
tmp_table_size        current value: 1048576
thread_stack          current value: 131072
wait_timeout          current value: 28800

mysqld 서버가 현재 가동중이면 다음의 명령을 통해 실제 변수값을 볼 수 있다.

shell> mysqladmin variables

각 옵션은 밑에서 설명한다. 버퍼  크기, 길이, 스택 크기는 바이트이다. 'K'(킬로바이트)
나 'M'(메가바이트)를 앞에 붙여 값을 지정할 수 있다. 예를 들면 16M는 16 메가바이트를
가리킨다. 대소문자는 구별하지 않는다. 16M 와 16m은 같다.

-back_log
mysql이 가질 수 있는 최대 연결 요청의 수. 이것은 main mysql 스레드가 매우 짧은 시간
동안 매우 많은 연결 요청을 받을 때 기능을 한다. 이때 메인 스레드가 연결을 체크하고 새
로운 스레드를 시작하는데는 약간의 시간이 걸린다.(그러나 아주 짧은 시간임) back_log 값
은 mysql이 순간적으로 새로운 요청에 답하는 것을 멈추기전에 이 짧은 시간동안 얼마나
많은 요청을 쌓아두고 있는지를 지정한다. 매우 짧은 시간동안 매우 많은 연결이 예상될때
만 이 값을 증가시켜야 한다.

다른 말로 이 값은 tcp/ip 연결을 받는 listen queue의 크기이다. 각 운영체제마다 이러한 큐
의 크기에 한계가 있다. Unix system call listen(2) 매뉴얼페이지에 자세한 정보가 있다. ba
ck_log값의 한계는 운영체제 문서를 확인해봐라. back_log를 최대값보다 더 높여도 효과가
없다.

-connect_timeout
Bad handshake에 반응하기 전에 연결 패킷을 mysql 서버에서 기다리는 시간.(초)

-join_buffer
(인덱스를 사용하지 않는 조인의) full-join에서 사용하는 버퍼의 크기. 버퍼는 두 테이블 사
이에서 각 full-join마다 한번 할당이 된다. 인덱싱을 추가하지 못할 때 조인 버퍼를 증가시
키면 full join의 속도를 향상시킬 수 있다. (일반적으로 빠르게 조인을 하는 가장 좋은 방법
은인덱스를 추가하는 것이다)

-key_buffer
인덱스 블락은 버퍼링되고 모든 스레드에서 공유한다. 키 버퍼는 인덱스 블락에서 사용하는
버퍼의 크기이다. 인덱스가 많은 테이블에서 delete나 insert 작업을 많이 하면 키 버퍼값을
증가시키는 것이 좋다. 더 빠른 속도를 내려면 LOCK TABLES를 사용하자. [Lock Tables]
참고.

-max_allowed_packet
한 패킷의 최대 크기. 메시지 버퍼는 net_buffer_length 바이트로 초기화되지만 필요하면 최
대 허용 패킷 바이트를 증가시킬 수 있다.기본값은 큰 패킷을 잡기에는 작다. 거대 BLOB
컬럼을 사용한다면 값을 증가시켜야 한다. 사용자가 원하는 최대 blob만큼 크게  해야 한다.

-max_connections
동시 클라이언트 숫자. mysqld가 필요로하는 파일 지시자(descriptor)의 숫자만큼 값을 늘려
야 한다. 밑에서 파일 디스크립터 제한에 대한 내용을 참고하자.

-max_connect_errors
호스트에서 최대 연결 에러이상의 interrupted 연결이 있으면 더 많은 연결을 위해 호스트는
block화된다. FLUSH HOSTS 명령으로 호스트의 block을 해제할 수 있다.

-max_join_size
최대 조인 크기이상으로 레크도를 읽는 조인을 하면 에러가 난다. 만약 사용자가 where 문
을 사용하지 않고 시간이 많이 걸리면서 몇백만개의 레코드를 읽는 조인을 수행하려 하면
이 값을 설정한다.

-max_sort_length
BLOB나 TEXT 값으로 정열할때 사용하는 바이트의 숫자. (각 값중 오직 첫번째 max_sort
_length 바이트만 사용된다. 나머지는 무시된다)

-net_buffer_length
질의에서 통신 버퍼가 초기화되는 크기. 일반적으로 바뀌지 않지만 매우 적은 메모리를 가
지고 있을 때 예상되는 질의에 맞게 세팅할 수 있다. (이것은 클라이언트에 가는 예상된 sql
문의 길이이다. 질의문이 이 크기를 넘으면 버퍼는 자동으로 max_allowed_packet 바이트까
지 증가한다)

-record_buffer
순차적인 검색을 하는 각 스레드에서 각 검색 테이블에 할당하는 버퍼 크기. 순차적인 검색
을 많이 하면 이 값을 증가시켜야 한다.

-sort_buffer
정렬이 필요한 각 스레드에서 할당하는 버퍼 크기. order by 나 group by 오퍼레이션을 빠
르게 하려면 이 값을 증가시킨다. 16.4 [임시 파일] 참고.

-table_cache
모든 스레드에서 열 수 있는 테이블의 숫자. mysqld가 필요로 하는 파일 디스크립터의 숫
자만큼 이 값을 증가시켜라. mysql은 각 유일한 오픈 테이블에서 두개의 파일 디스크립터가
필요하다. 파일 디스크립터 제한을  참고한다. 테이블 캐쉬가 어떻게 작동하는지는 10.6 [테
이블 캐쉬]를 참고한다.

-tmp_table_size
임시 테이블이 이 값을 넘으면 mysql은 "The Table tbl_name is full"이라는 에러 메시지를
낸다. 매우 많은 group by 질의를 사용하면 이 값을 증가시켜야 한다.

-thread_stack
각 스레드의 스택 사이즈. creash-me test(**역자주 : 데이터베이스의 벤치마킹을 하는 테스
트입니다. 말그대로 데이터베이스를 죽여주지요) 에서 잡히는 많은 제한은 이 값에 달려있
다. 기본값은 일반적으로 충분히 크다. 11장의 [벤치마크] 참조

-wait_timeout
연결을 끊기전에 연결 활동(activity)을 서버에서 기다리는 시간(초).

table_cache 와 max_connections는 서버가 열 수 있는 최대 파일 갯수에 영향을 미친다. 이
값을 증가시키면 운영시스템에서 오픈 파일 디스크립터의 per-process 숫자의 한계까지 올
릴 수 있다. (** ... imposed by your operating system on the per-process number of
open file descriptors. 번역이 이상하므로 영문 참고)
그러나 많은 시스템에서 이 한계를 증가시킬수 있다. 이렇게 하려면 각 시스템에서 이 한계
를 변화시키는 방법이 매우 다양하므로 운영체제 문서를 참고해야 한다.

table_cache 는 max_connections 와 관계가 있다. 예를 들면 200개의 연결이 있으면 최소 2
00 * n 의 테이블 캐쉬를 가져야 한다. 여기서 n은 조인에서 테이블의 최대 숫자이다.

mysql은 매우 유용한 알고리즘을 사용하기 때문에 일반적으로는 매우 적은 메모리로 사용
할 수 있으며 메모리가 많을 수록 성능이 더 많이 향상된다.

많은 메모리와 많은 테이블을 가졌고 중간정도 숫자의클라이언트에서 최대의 성능을 원한다
면 다음과 같이 사용한다.

shell> safe_mysqld -O key_buffer=16M -O table_cache=128 \
           -O sort_buffer=4M -O record_buffer=1M &

메모리가 적고 연결이 많으면 다음과 같이 사용한다.

shell> safe_mysqld -O key_buffer=512k -O sort_buffer=100k \
           -O record_buffer=100k &

또는:

shell> safe_mysqld -O key_buffer=512k -O sort_buffer=16k \
           -O table_cache=32 -O record_buffer=8k -O net_buffer=1K &

매우 많은 연결이 있을 때 mysqld가 각 연결마다 최소한의 메모리를 사용하도록 설정하지
않았다면 "swapping problems" 문제가 생길 것이다.

mysqld에서 옵션을 바꾸었으면 그것은 서버의 해당하는 인스턴스에만 영향을 미친다는 것
을 기억하자.

옵션을 바꾸었을때의 효과를 보기 위해 다음과 같이 해보자.

shell> mysqld -O key_buffer=32m --help

마지막에 --help 옵션이 들어간 것을 기억하자. 그렇지 않으면 커맨드 라인에서 사용한 옵
션의 효력은 출력에는 반영되지 않을 것이다.


10.2 메모리 사용 방법 <메모리 최적화>

아래에서 설명하는 목록은 mysqld 서버가 메모리를 사용하는 방법에 대해서 나타내고 있
다. 메모리 사용과 관련된 서버의 변수 이름이 주어진다.


- 키 버퍼(변수 key_buffer)는 모든 스레드에서 공유한다. 서버에서 사용하는 다른 버퍼는
필요한대로 할당이 된다.

- 각 연결은 각 스레드마다의 특정한 공간을 사용한다. 스택(64k, 변수 thread_stack) , 연결
버퍼(변수 net_buffer_length), result 버퍼 (변수 net_buffer_length) 등. 연결 버퍼와 result
버퍼는 필요할때 max_allowed_packet 까지 동적으로 증가된다. 질의가 수행될 때 현재의
질의문의 복사문이 또한 할당이 된다.
(** When a query is running a copy of the current query string is also alloced.)

- 모든 스레드는 같은 기본 메모리를 공유한다.
- 메모리 맵은 아직 지원이 안된다. (압축 테이블을 제외하고. 그러나 이것은 다른 이야기이
다) 왜냐하면 4GB의 32비트 메모리 공간은 대부분의 대형 테이블에서 충분히 크기가 않기
때문이다. 우리가 64비트 주소 공간을 가진 시스템을 가지게 될 때 우리는 메모리 맵핑을
위한 일반적인 지원을 추가할 것이다.

- 테이블에서 순차적인 검색을 하는 각 요청은 read 버퍼에 할당이 된다. (변수 record_buff
er)

- 모든 조인은 한번에 수행이 되며 대부분의 조인은 임시 테이블을 생성하지 않고 수행이
된다. 대부분의 테이블은 메모리 기반(HEAP) 테이블이다. 거대 길이의 레코드를 가졌거나
BLOB 컬럼을 포함한 임시 테이블은 디스크에 저장이 된다. 현재의 문제는 메모리 기반 테
이블이 tmp_table_size를 초과했을때 "The table tbl_name is full"이라는 에러가 생기는 것
이다. 가까운 시일안에 필요할때 자동적으로 메모리 기반(HEAP) 테이블을 디스크 기반(NI
SAM) 테이블로 바꾸도록 고칠 것이다.
이 문제를 해결하기 위해서 mysqld의 tmp_table_size 옵션을 설정하여 임시 테이블 크기를
늘이거나 클라이언트 프로그램에서 SQL_BIG_TABLES라는 sql 옵션을 설정하여야 한다. 7.
24 SET OPTION 을 참고하자.
mysql 3.20에서 임시 테이블의 최대 크기는 record_buffer*16이다. 3.20 버전을 사용하고 있
다면 record_buffer의 값을 증가시켜야 한다. 또한 mysqld를 시작할 때 --big-tables 옵션을
사용하여 항상 임시 테이블을 디스크에 저장할 수 있지만 질의 속도에 영향을 미친다.

- 정열을 하는 대부분의 요청은 정렬 버퍼와 하나나 두개의 임시 파일을 할당한다. 16.4의
[임시 파일]을 참고한다.

- 대부분의 파징(parsing)과 계산은 지역 메모리에서 이루어진다. 작은 아이템에는 메모리 o
verhead가 필요없고 일반적인 느린 메모리 할당(slow memory allocation)과 freeing(메모리
해제)는 무시된다. 메모리는 오직 예상지 못한 거대 문자열에서 할당이 된다.( mallloc() 과
free() 사용)

- 각 인덱스 파일은 한번에 열리며 각 병행수행되는 스레드에서 데이터 파일은 한번에 열
린다. 각 병행수행 스레드마다 테이블 구조, 각 컬럼의 컬럼 구조, 3 * n 의 버퍼 크기가 할
당된다. ( n은 최대 레코드 길이이며 BLOB 컬럼은 해당하지 않는다) BLOB는 BLOB 데이
터의 길이에 5에서 8 바이트를 더한 값을 사용한다.

- BLOB 컬럼을 가진 각 테이블에서 버퍼는 거대 BLOB 값을 읽을 수 있도록 동적으로 커
진다. 테이블을 검색하면 버퍼는 최대 BLOB의 값만큼 버퍼가 할당이 된다.

- 모든 사용중인 테이블의 테이블 핸들러는 캐쉬에 저장되며 FIFO로 관리가 된다. 일반적
으로 캐쉬는 64 엔트리를 갖는다. 동시에 두개의 실행 스레드에서 테이블을 사용하면 캐쉬
는 테이블의 두 엔트리를 포함한다. 10.6 [테이블 캐쉬]를 참고한다.

- mysqladmin flush-tables 명령은 사용하지 않는 모든 테이블을 닫고 현재 실행되는 스레
드가 끝날 때 모든 사용중인 테이블을 닫는다고 표시한다. 이것은 효과적으로 사용중인 메
모리를 해제한다.


ps 와 다른 시스템 상황 프로그램은 mysqld가 많은 메모리를 사용하고 있다고 보고할 것이
다. 이것은 다른 메모리 주소의 스레드-스택때문에 생긴다. 예를 들면 솔라리스의 ps 는 스
택사이의 사용하지 않는 메모리를 사용하는 메모리로 간주한다. 이것은 swap -s를 이용 사
용가능한 스왑을 체크하여 확인할수 있다. 우리는 mysqld를 상용 메모리 유출 측정 프로그
램으로 테스팅해서 mysqld에는 메모리 유출이 없다.


10.3 속도 향상에 영향을 미치는 컴파일/링크 방법 <컴파일시 최적화하기>

다음 테스트의 대부분은 리눅스와 mysql 벤치마크를 가지고 수행되었지만 다른 운영 시스
템에도 암시해주는 것이 있다.

static으로 링크를 할때 가장 빠른 실행 속도를 얻을 수 있다. 데이터베이스에 연결하기 위
해 TCP/IP보다는 유닉스 소켓을 사용하면 더 좋은 성능을 낼 수 있다.

리눅스에서 pgcc와 -O6을 사용하면 가장 빠르다. 'sql_yacc.cc'를 이 옵션으로 컴파일하려면
gcc/pgcc는 모든 성능을 내기 위해 많은 메모리가 필요하기 때문에 180M의 메모리가 필요
하다. 또한 mysql을 설정할때 libstdc++ 라이브러리를 포함하지 않기 위해 CXX=gcc라고 설
정해야 한다.

- pgcc를 사용하고 모두다 -O6 옵션으로 컴파일하면 mysqld 서버는 gcc로 컴파일한 것보
다 11% 빨라진다.

- 동적으로 링크하면 (-static을 사용하지 않고) 13% 느려진다.
    If you connect using TCP/IP rather than Unix sockets, the result is 7.5% slower.
- 유닉스 소켓을 사용하는 것보다 tcp/ip로 연결하는 것이 7.5% 느려진다.

- On a Sun sparcstation 10, gcc 2.7.3 is 13% faster than Sun Pro C++ 4.2.
- On Solaris 2.5.1, MIT-pthreads is 8-12% slower than Solaris native threads.
(** 번역을 안한 이후. 리눅스랑 상관없으니깐... **)

TcX에서 제공한 mysql 리눅스 배포판은 pgcc로 컴파일되었고 정적으로 링크되었다.


10.4 How MySQL uses indexes

prefix- and end-space compressed. See section 7.26 CREATE INDEX syntax (Compatibil
ity function).

모든 인덱스(PRIMARY, UNIQUE and INDEX()) 는 B-trees 에 저장된다. 문자열은 자동적
으로 앞 뒤의 공간(?)이 압축된다. 7.26 [인덱스 생성] 참고.

인덱스의 사용 :
- WHERE 문에서 해당하는 레코드 빨리  찾기
- 조인을 수행할때 다른 테이블에서 레코드 가져오기
- 특정 키에서 MAX() 나 MIN() 값 찾기
- 소팅이나 그룹화할때 인덱스 키를 사용하면 테이블을 정열하거나 그룹화한다. 키에  DES
C가 붙으면 역순으로 인덱스를 읽는다.
- 어떤 경우에는 데이터 파일에 묻지 않고 값을 가져온다. 어떤 테이블에서 사용하는 모든
컬럼이 숫자이고 특정 키로 형성되어있으면 빠른 속도로 인덱스 트리에서 값을 가져올 수
있다.

다음 예제를 보자.

mysql> SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;


다중 컬럼 인덱스가 col1 과 col2에 있으면 해당하는 레코드를 직접 가져올 수 있다.  분리
된 단일 컬럼 인덱스가 col1 과 col2 에 있으면 최적화기는 어떤 인덱스가 더 적은 레코드
를 가졌는지 확인하고 레코드를 가져오기 위해 그 인덱스를 사용하도록 결정한다.

테이블이 다중 컬럼 인덱스를 가졌다면 최적화기가 레코드를 찾는데 어떤 인덱스키를 사용
할 수 있다. 예를 들면 세가지 컬럼 인덱스(col1, col2, col3)를 가졌다면 (col1), (col1,col2)
(col1,col2,col3) 인덱스를 사용하여 검색을 할 수 있다. 

MySQL can't use a partial index if the columns don't form a leftmost prefix of the inde
x.
Suppose you have the SELECT statements shown below:
(** 해석이 잘 안되는데 예제를 보시면 무슨 말인지 알 수 있을 것임**)

mysql> SELECT * FROM tbl_name WHERE col1=val1;
mysql> SELECT * FROM tbl_name WHERE col2=val2;
mysql> SELECT * FROM tbl_name WHERE col2=val2 AND col3=val3;

If an index exists on (col1,col2,col3), only the first query shown above uses the index.
The second and third queries do involve indexed columns, but (col2) and (col2,col3) are
not leftmost prefixes of (col1,col2,col3).

인덱스가 (col1,col2,col3)로 있다면 위의 질의중 오직 첫번째 질의만 인덱스를 사용한다. 두
번째 및 세번째 질의은 인덱스된 컬럼이 포함되어 있지만 (col2) 와 (col2,col3)는 (col1,col2,c
ol3) 인덱스에 해당하지 않는다.

MySQL also uses indexes for LIKE comparisons if the argument to LIKE is a constant
string that doesn't start with a wildcard character. For example, the following SELECT
stat ements use indexes:

mysql은 또한 LIKE의 인수가 와일드카드 문자로 시작하지 않는 상수 문자열일이라면 LIK
E 비교문에서 인덱스를 사용한다. 예를 들어 다음의 SELECT 문은 인덱스를 사용한다.

mysql> select * from tbl_name where key_col LIKE "Patrick%";
mysql> select * from tbl_name where key_col LIKE "Pat%_ck%";

첫번째 문장에서는 "Patrick" <= key_col < "Patricl" 을 가진 레코드만 고려된다. 두번째 문
장에서는 "Pat" <= key_col < "Pau" 을 가진 레코드만 고려된다.


다음의 SELECT 문은 인덱스를 사용하지 않는다:

mysql> select * from tbl_name where key_col LIKE "%Patrick%";
mysql> select * from tbl_name where key_col LIKE other_col;

첫번째 문장에서 LIKE 값은 와일드카드 문자로 시작하고 있다. 두번째 문장에서는 LIKE
값이 상수가 아니다.



10.5 WHERE 문에서 최적화하기
(이번 절은 완전한 내용을 포함하고 있지는 않다. mysql은 많은 최적화방법이 있다.)

In general, when you want to make a slow SELECT ... WHERE faster, the first thing t
o check is whether or not you can add an index. All references between different tables
should usually be done with indexes. You can use the EXPLAIN command to determine
which indexes are used for a SELECT. See section 7.21 EXPLAIN syntax (Get informat
ion about a SELECT).
일반적으로 느린 SELECT ... WHERE 문을 빠르게 하려면 가장 먼저 확인해야 할 것이 인
덱스 추가 문제이다. 다른 테이블사이에서 모든 레퍼런스(references 참조)는 일반적으로 인
덱스에 의해 수행된다. SELECT 문에서 어떤 인덱스를 사용하는지 결정하기 위해 EXPLAI
N 명령을 사용할 수 있다. 7.21 [Explain]을 참고.

mysql에서 수행하는 최적화는 다음과 같다.


- 불필요한 삽입어 제거

       ((a AND b) AND c OR (((a AND b) AND (c AND d))))
    -> (a AND b ANDc) OR (a AND b AND c AND d)

-상수 폴딩(folding)

       (a<b AND b=c) AND a=5
    -> b>5 AND b=c AND a=5

- 상수 조건 제거(상수 폴딩때문에  필요)

       (B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6)
    -> B=5 OR B=6

- 인덱스에서 사용되는 상수 표현은 한번에 계산된다.
(Constant expressions used by indexes are evaluated only once.)

- WHERE 절이 없는 단일 테이블의 COUNT(*)는 테이블 정보에서 직접 값을 가져온다.
단일 테이블에서 사용된 NOT NULL 표현도 이와 같이 수행된다. 

- 유효하지 않은 상수 표현은 미리 제거된다. mysql은 불가능하고 해당하는 레코드가 없는
SELECT 문을 빠르게 감지한다.

- GROUP BY 나 그룹 펑션(COUNT(), MIN() ...)을 사용하지 않으면 HAVING은 WHERE
에 합쳐진다.
(** HAVING 절에서는 인덱스를 사용하지 못함. 그러므로 가능한 HAVING절을 사용하지
않는게 속도면에서 좋다 **)

- 각 서브 조인에서 빠르게 WHERE 문을 계산하고 가능한한 레코드를 제외하도록 간소하
게 WHERE 문이 만들어진다.

- mysql은 일반적으로 최소한의 레코드를 찾기 위해 인덱스를 사용한다. =, >, >=, <, <=,
BETWEEN 그리고  'something%' 처럼 앞이 와일드카드로 시작하지 않는 LIKE 문등을
사용하여 비교를 할 때 인덱스를 사용한다. (** 10.4 절에서 설명하였듯이 like 를 사용할때
와일드카드로 시작하는 like 문을 사용하면 인덱스를 사용하지 않는다. 일정한 단어로만 시
작하는 컬럼에서 자료를 찾을 때 유용할 것이다. **)

- Any index that doesn't span all AND levels in the WHERE clause is not used to opti
mize the query.

다음의 WHERE 문은 인덱스를 사용한다.:

... WHERE index_part1=1 AND index_part2=2
... WHERE index=1 OR A=10 AND index=2      /* index = 1 OR index = 2 */
... WHERE index_part1='hello' AND index_part_3=5
          /* optimized like "index_part1='hello'" */

다음의 WHERE 문은 인덱스를 사용하지 않는다.:

... WHERE index_part2=1 AND index_part3=2  /* index_part_1 is not used */
... WHERE index=1 OR A=10                  /* No index */
... WHERE index_part1=1 OR index_part2=10  /* No index spans all rows */

- 질의에서 다른 테이블보다 모든 상수 테이블을 먼저 읽는다. 상수 테이블은 다음과 같다.
        ㅇ빈 테이블이나 1개의 레코드만 있는 테이블
        ㅇWHERE 문에서 UNIQUE 인덱스나 PRIMARY KEY 를 사용하고 모든 인덱스
는 상수 표현으로된 테이블

        다음의 테이블은 상수 테이블로 사용된다.

    mysql> SELECT * FROM t WHERE primary_key=1;
    mysql> SELECT * FROM t1,t2
               WHERE t1.primary_key=1 AND t2.primary_key=t1.id;

- 모든 가능성을 시도하여 테이블을 조인하는데 가장 좋은 조인 조합을 찾는다. (ORDER B
Y나 GROUP BY의 모든 컬럼이 동일한 테이블에서 나오면 조인을 할때 이 테이블이 먼저
선택된다)

- ORDER BY 문과 다른 GROUP BY 문이 있을 때, 또는 ORDER BY 나 GROUP BY가
조인 큐의 첫번째 테이블이 아닌 다른 테이블의 컬럼을 포함하고 있으면 임사 테이블을 만
든다.

- 각 테이블 인덱스를 찾고 레코드의 30%미만을 사용하는 (best) 인덱스가 사용된다. 그런
인덱스가 없으면 빠른 테이블 검색이 사용된다.

- 어떤 경우에는 mysql은 데이터 파일을 조회하지 않고 인덱스에서 레코드를 읽을 수 있
다. 인덱스에서 사용한 모든 컬럼이 숫자라면 질의를 처리하는데 단지 인덱스 트리만을 사
용한다.

- 각 레코드가 출력되기 전에 HAVING 절에 맞지 않는 레코드는 건너뛴다.

다음은 매우 빠른 질의의 예이다:

mysql> SELECT COUNT(*) FROM tbl_name;
mysql> SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;
mysql> SELECT MAX(key_part2) FROM tbl_name
           WHERE key_part_1=constant;
mysql> SELECT ... FROM tbl_name
           ORDER BY key_part1,key_part2,... LIMIT 10;
mysql> SELECT ... FROM tbl_name
           ORDER BY key_part1 DESC,key_part2 DESC,... LIMIT 10;

다음의 커리는 인덱스 트리만을 사용하여 값을 구한다.(인덱스 컬럼은 숫자라고 가정):

mysql> SELECT key_part1,key_part2 FROM tbl_name WHERE key_part1=val;
mysql> SELECT COUNT(*) FROM tbl_name
           WHERE key_part1=val1 and key_part2=val2;
mysql> SELECT key_part2 FROM tbl_name GROUP BY key_part1;

다음의 질의는 개별적인 정열을 하지 않고 정열된 순서대로 열을 가져오는 데 인덱스를 사
용한다:

mysql> SELECT ... FROM tbl_name ORDER BY key_part1,key_part2,...
mysql> SELECT ... FROM tbl_name ORDER BY key_part1 DESC,key_part2 DESC,...


10.6 테이블 열고 닫는 방법

open 테이블의 캐쉬는 table_cache의 최대값까지 커질 수 있다. (기본값 64 ; 이 값은 mysql
d에서 -0 table_cache=# 으로 바꿀 수 있다) 캐쉬가 꽉 찼을때, 그리고 다른 스레드가 테이
블을 열려고 할 때, 또는 mysqladmin refresh 나 mysqladmin flush-tables를 사용할때를 제
외하고는 테이블은 결코 닫히지 않는다.

테이블 캐쉬가 꽉 차면 서버는 캐쉬 엔트리를 사용하도록 조절하기 위해 다음의 절차를 사
용한다.

- 가장 먼저 사용했던 순서대로 현재 사용하지 않는 테이블을 닫는다.
- 캐쉬가 꽉 찼고 어떤 테이블도 닫히지 않지만 새로운 테이블을 열어야 한다면 캐쉬가 필
요한 만큼 임시적으로 확장된다.
- 캐쉬가 임시적으로 확장된 상태이고 테이블을 사용할 수 없는 상황으로 가면 테이블을
닫고 캐쉬를 해제한다.

테이블은 각 동시병행적인 접근때마다 열린다. 동일한 테이블에 접근하는 두개의 스레드가
있거나 같은 질의에서 테이블에 두번 접근하면(with AS) 테이블을 두번 열여야 한다는 의
미이다. 테이블의 첫번째 개방은 두개의 파일 디스크립터를 가진다. ; 추가적인 테이블의 개
방은 하나의 파일 디스크립터를 가질 뿐이다. 처음에 개방에 사용하는 추가적은 파일 디스
크립터는 인덱스 파일에 사용된다. ; 이 디스크립터는 모든 스레드에서 공유된다.


10.6.1 데이터베이스에서 많은 수의 테이블을 만들때의 단점

디렉토리에 많은 파일이 있다면 open, close 그리고 create 오퍼레이션은 느려질 것이다. 서
로 다른 많은 테이블에서 SELECT 문을 수행하면 테이블 캐쉬가 꽉 찰 때 약간의 overhea
d가 있을 것이다. 왜냐면 개방된 테이블이 있다면 다른 테이블은 닫혀야 하기 때문이다. 테
이블 캐쉬를 크게 해서 이러한 오우버헤드를 줄일 수 있다.


10.7 많은 테이블을 여는 이유

mysqladmin status 를 실행할 때 다음과 같이 나올 것이다:

Uptime: 426 Running threads: 1 Questions: 11082 Reloads: 1 Open tables: 12

단지 6테이블을 사용했는데 이러한 결과는 당황스러울 것이다.

mysql은 멀티스레드를 사용한다. 그래서 동시에 같은 테이블에서 많은 질의를 할 수 있다.
같은 파일에 대하여 다른 상황을 가지는 두개의 스레드에 대한 문제를 줄이기 위해 테이블
은 각 동시병행적인 스레드마다 독립적으로 개방된다. 이것은 테이타 파일에서 약간의 메모
리와 하나의 추가적인 파일 디스크립터를 사용한다. 모든 스레드에서 인덱스 파일은 공유된
다.


10.8 데이터베이스와 테이블에서 심볼릭 링크 사용

데이터베이스 디렉토리에서 테이블과 데이터베이스를 다른 위치로 옮기고 새로운 위치로 심
볼릭 링크를 사용할 수 있다. 이렇게 하는 것을 원할 경우가 있다. 예를 들면 데이터베이스
를 더 여유공간이 많은 파일시스템으로 옮기는 경우 등.

mysql에서 테이블이 심볼링 링크되었다는 것을 감지하면 심볼링 링크가 가리키는 테이블을
대신 사용할 수 있다. realpath() call 을 지원하는 모든 시스템에서 작동한다. (최소한 리눅
스와 솔라리스는 realpath()를 지원한다) realpath()를 지원하지 않는 시스템에서 동시에 실
제 경로와 심볼릭 링크된 경로에 접근하면 안된다. 이런 경우에는 업데이트 된후에 테이블
이 모순될 수 있다.

mysql은 기본값으로 데이터베이스 링크를 지원하지 않는다. 데이터베이스간에 심볼릭 링크
를 사용하지 않는 작동을 잘 할 것이다. mysql 데이터 디렉토리에 db1 데이터베이스가 있
고 db1을 가리키는 db2 심볼릭 링크를 만들었다고 해보자:

shell> cd /path/to/datadir
shell> ln -s db1 db2

이제 db1에 tbl_a라는 테이블이 있다면 db2에도 tbl_a가 나타날 것이다. 한 스레드가 db1.tbl
_a를 업데이트하고 다른 스레드가 db2.tbl_a를 업데이트하면 문제가 생길 것이다.

정말로 이 기능이 필요하면 , `mysys/mf_format.c'에서 다음의 코드를 수정해야 한다.:

if (!lstat(to,&stat_buff))  /* Check if it's a symbolic link */
    if (S_ISLNK(stat_buff.st_mode) && realpath(to,buff))

위 코드를 다음과 같이 수정한다 :

if (realpath(to,buff))


10.9 테이블에 락 거는 방법

mysql의 모든 락은 deadlock-free 이다. 언제나 질의를 시작할때 한번에 모든 필요한 락을
요청하고 언제나 같은 순서대로 테이블에 락을 걸어 관리한다.

WRITE 락을 사용하는 방법은 다음과 같다:

- 테이블에 락이 없으면 그 테이블에 write 락을 건다.
- 이런 경우가 아니라면 write 락 큐에 락을 요청한다.

READ 락을 사용하는 방법은 다음과 같다:

- 테이블에 write 락이 없으면 그 테이블에 read 락을 건다.
- 이런 경우가 아니라면 read 락 큐에 락을 요청한다.

락이 해제되었을 때 락은 write 락 큐의 스레드에서 사용할 수 있으며 그러고 나서 read 락
큐의 스레드에서 사용한다.

테이블에서 업데이트를 많이 하면 SELECT 문은 더 이상 업데이트가 없을 때까지 기다린
다는 것을 의미한다.

이러한 문제를 해결하기 위해 테이블에서 INSERT 와 SELECT 오퍼레이션을 많이 사용하
는 경우에 다음과 같이 하면 된다. 임시 테이블에 레코드를 입력하고 한번에 임시 테이블에
서 실제 테이블로 레코드를 업데이트한다.

다음의 예를 보자:

mysql> LOCK TABLES real_table WRITE, insert_table WRITE;
mysql> insert into real_table select * from insert_table;
mysql> delete from insert_table;
mysql> UNLOCK TABLES;

만약 어떤 경우에 SELECT문에 우선권을 주고 싶다면 INSERT 옵션에서 LOW_PRIORITY
or HIGH_PRIORITY 옵션을 사용할 수 있다. 7.13 [Insert] 참고. (** LOW_PRIORITY를 지
정하면 클라이언트에서 테이블을 읽지 않을 때까지 INSERT 문 수행이 미루어진다. **)

단일 큐를 사용하기 위해 `mysys/thr_lock.c' 의 락킹 코드를 바꿀 수 있다. 이런 경우 writ
e 락과 read 락은 같은 우선권을 가지며 어떤 애플리케이션에서는 유용할 수 있다.

10.10 테이블을 빠르고 작게 배열하는 방법 <** 테이블 최적화 **>

다음은 테이블에서 최대의성능을 내는 방법과 저장 공간을 절약할 수 있는 테크닉이다:

- 가능한한 NOT NULL로 컬럼을 선언한다. 속도가 빨라지며 각 컬럼마다 1 비트를 절약할
수 있다.
- default 값을 가질 때 유리하다. 입력되는 값이 기본값과 다를 때만 확실하게 값이 입력된
다. INSERT 문에서 첫번째 TIMESTAMP 컬럼이나 AUTO-INCREAMENT 컬럼의 값을
입력할 필요가 없다. 18.4.49 [mysql_insert_id()] 참고.
- 가능한한 테이블을 작게 만드려면 더 작은 integer 타입을 사용하자. 예를 들면 MEDIUM
INT 가 보통 INT 보다 좋다.
- 가변 길이 컬럼이 없다면(VARCHAR, TEXT or BLOB columns), 고정 길이 레코드 포
맷이 사용된다. 이 경우 속도는 더 빠르지만 불행히도(흑흑~) 낭비되는 공간이 더 많다. 10.1
4 [Row format] 참고.
- mysql이 질의를 효과적으로 최적화하기 위해 많은 양의 데이터를 입력한후 isamchk --a
nalyze를 실행하자. 이렇게 하면 동일한 값을 가진 줄의 평균 숫자를 가리키는 각 인덱스의
값을 업데이트한다. (물론 unique 인덱스에서는 항상 1이다)
- 인덱스와 인덱스에 따른 데이타를 정열하려면
isamchk --sort-index --sort-records=1 을 사용하자.(if you want to sort on index 1).
인덱스에 따라 정렬된 모든 레코드를 읽기 위해 unique 인덱스를 가졌다면 이렇게 하는 것
이 속도를 빠르게 하는 가장 좋은 방법이다.
- INSERT 문에서 가능한 다중 값 목록을 사용하자. 개별적인 SELECT 문보다 훨씬 빠르
다. 데이타를 테이블에 입력할 때 LOAD DATA INFILE을 사용하자. 많은 INSERT 문을
사용하는 것보다 보통 20배 빠르다. 7.15 [Load] 참고.

많은 인덱스를 가진 테이블에 데이타를 입력할때 다음의 과정을 사용하면 속도를 향상시킬
수 있다.
1. mysql이나 Perl 에서 CREATE TABLE로 테이블을 만든다.
2. mysqladmin flush-tables 실행. (** 열린 테이블을 모두 닫음 **)
3. isamchk --keys-used=0 /path/to/db/tbl_name 사용. 테이블에서 모든 인덱스 사용을 제
거한다.
4. LOAD DATA INFILE 를 이용 테이블에 데이타를 입력.
5. pack_isam을 가지고 있고 테이블을 압축하기 원하면 pack_isam을 실행.
6. isamchk -r -q /path/to/db/tbl_name 를 이용 인덱스를 다시 생성.
7. mysqladmin flush-tables 실행.

- LODA DATA INFILE 과 INSERT 문에서 더 빠른 속도를 내려면 키 버퍼를 증가시킨
다. mysqld나 safe_mysqld에서 -O key_buffer=# 옵션을 사용하면 된다. 예를 들어 16M는
풍부한 램을 가졌다면 훌륭한 값이다.
- 다른 프로그램을 사용하여 데이타를 텍스트 파일로 덤프할때 SELECT ... INTO OUTFIL
E 을 사용하자. 7.15 [LOAD DATA INFILE] 참고.
- 연속으로 다량의 insert와 update를 할 때 LOCK TABLE을 사용하여 테이블에 락을 걸
면 속도를 향상시킬 수 있다. LOAD DATA INFILE 그리고 SELECT ...INTO OUTFILE
는 원자적이기 때문에 LOCK TABLE을 사용하면 안된다. 7.23 [LOCK TABLES/UNLOCK
TABLES] 참고.

테이블이 얼마나 단편화되었는지 점검하려면 '.ISM' 파일에서 isamchk -evi 를 실행한다. 1
3장 [Maintenance] 참고.



10.11 INSERT 문에서 속도에 영향을 미치는 부분 <** insert 최적화 **>

insert 하는 시간은 다음와 같이 구성된다:

    Connect: (3)
    Sending query to server: (2)
    Parsing query: (2)
    Inserting record: (1 x size of record)
    Inserting indexes: (1 x indexes)
    Close: (1)

(숫자)는 비례적인 시간이다. 이것은 테이블을 개방할때 초기의 overhead를 고려하고 있지
는 않다. (매 동시병행적으로 수행되는 질의마다 발생)

The size of the table slows down the insertion of indexes by N log N (B-trees).


테이블의 크기는 N log N(B-trees)에 따라 인덱스의 입력이 느려진다. (**말이 좀 이상. 테
이블이 커짐에 따라 인덱스 생성도 느려진다는 뜻이겠죵 **)

테이블에 락을 걸거나 insert 문에서 다중 값 목록을 사용하여 입력 속도를 빠르게 할 수
있다. 다중 값 목록을 사용하면 단일 insert 보다 5배 정도 속도가 빨라진다.

mysql> LOCK TABLES a WRITE;
mysql> INSERT INTO a VALUES (1,23),(2,34),(4,33);
mysql> INSERT INTO a VALUES (8,26),(6,29);
mysql> UNLOCK TABLES;

주요한 속도 차이는 모든 INSERT 문이 완료되고 난 후에 한번에 인덱스 버퍼가 쓰여기지
때문에 생긴다. 보통 서로 다른 여러 INSERT 문이 있으면 많은 인덱스 버퍼 플러쉬가 있
을 것이다. 모든 줄을 단일 문으로 입력하면 락은 필요없다.

락킹은 또한 다중 연결 테스트의 총 시간을 줄일 수는 있다. 그러나 어떤 스레드에서는 총
대기시간은 증가할 수 있다.(왜냐면 락을 기다리기 때문이다)
예를 들어보자:

thread 1 does 1000 inserts
thread 2, 3, and 4 does 1 insert
thread 5 does 1000 inserts

락을 사용하지 않으면 2, ,3 4는 1과 5 전에 끝마칠 것이다. 락을 사용하면 2,3,4는 아마도 1
이나 5 전에 끝나지 않을 것이다. 그러나 총 시간은 40% 빨라진다.

INSERT, UPDATE, DELETE 오퍼레이션은 mysql에서 매우 빠르다. 그렇기 때문에 줄에서
5개  이상의 insert나 update를 할 때 락을 추가하면 더 좋은 성능을 얻을 수 있다. 줄에 매
우 많은 자료를 입력한다면 다른 스레드에서 테이블에 접근하도록 하기 위해 때때로(각 100
0줄마다) UNLOCK TABLES를 사용하는 LOCK TABLES 실행하면 된다. 이렇게 하면 좋
은 성능을 낼 수 있다. (** 열심히 입력을 하고 중간에 락을 풀었다가 다시 락을 거는 것
반복함**)

물론 LOAD DATA INFILE 이 더 빠르다.



10.12 DELETE 문에서 속도에 영향을 미치는 부분 <** DELETE 문 최적화 **>

레코드를 삭제하는 시간은 정확히 인덱스 숫자에 비례한다. 레코드를 빠르게 지우기 위해
인덱스 캐쉬의 크기를 증가시킬 수 있다. 기본 인덱스 캐쉬는 1M 이다; 빠르게 삭제하기 위
해 증가되어야 한다.(충분한 메모리를 가지고 있다면 16M로 하자)


10.13 mysql에서 최대 속도를 얻는 방법

벤치마킹을 시작하자! mysql 벤치마크 스위트에서 어떤 프로그램을 사용할 수 있다. (일반
적으로 'sql-bench' 디렉토리에 있음) 그리고 입맞에 맞게 수정하자. 이렇게 하면 당신의 문
제를 해결할 수 있는 다른 해결책을 찾을 수 있으며 당신에게 가장 빠른 해결책을 테스트할
수 있다.

- mysqld를 적절한 옵션으로 시작하자. 메모리가 많을수록 속도가 빠르다.
  10.1 [MySQL parameters] 참고.
- SELECT 문의 속도를 빠르게 하기 위해 인덱스를 만들자.
10.4 [MySQL indexes] 참고.
- 가능한 효율적으로 컬럼 타입을 최적화하자. 예를 들면 가능한 NOT NULL로 컬럼을 정
의하자. 10.10 [Table efficiency] 참고.
- --skip-locking 옵션은SQL 요청에서 파일 락킹을 없앤다. 속도가 빨라지지만 다음의 과
정을 따라야 한다:
        ㅇ isamchk로 테이블을 체크하거나 수리하기 전에 mysqladmin flush-tables 로 모
든 테이블을 플러시해야 한다. (isamchk -d tbl_name은 언제나 허용된다. 왜냐하면 이건 단
순히 테이블의 정보를 보여주기 때문이다)
        ㅇ 동시에 뜬 두개의 mysql 서버가 동일한 테이블을 업데이트하려 한다면 동일한
데이터 파일에 두개의 mysql 서버를 띄우면 안된다.

        --skip-locking 옵션은 MIT-pthreads로 컴파일할때 기본값이다. 왜냐면 모든 플랫
폼의 MIT-pthreads에서 flock()가 완전하게 지원이 되지 않기 때문이다.

- 업데이트에 문제가 있다면 업데이트를 미루고 나중에 하자. 많은 업데이트를 하는 것이
한번에 하나를 업데이트하는 것보다 더 빠르다.
- FreeBSD 시스템에서 MIT-pthreads에 문제가 있으면 FreeBSD 3.0 이후 버전으로 업데
이트 하는것이 좋다. 이렇게 하면 유닉스 소켓을 사용하는 것이 가능하며(FreBSD에서 유닉
스 소켓이 MIT-pthreads에서 TCP/IP 연결을 사용하는 것보다 빠르다) 그리고 스레드 패키
지가 조정(intergrated?)되어야 한다.
- 테이블이나 컬럼 단계를 체크하는 GRANT는 성능을 떨어뜨린다.


10.14 로우 포맷과 다른 점은 무엇인가? 언제 VARCHAR/CHAR을 사용해야 하는가?

mysql은 실제의 SQL VARCHAR 타입이 없다. 그대신 mysql은 레코드를 저장하고 이것을
VARCHAR로 에뮬레이트하는데 세가지 방법이 있다.

테이블에 VARCHAR, BLOB, TEXT 컬럼이 없으면 고정 row size를 사용한다. 그외에는
동적 row size를 사용한다. CHAR 과 VARCHAR 컬럼은 애플리케이션의 관점에서 동일하
게 취급된다; 둘다 trailing space는 컬럼을 가져올때 제거된다.

isamchk -d 를 이용 테이블에서 사용하는 포맷을 체크할 수 있다.
(-d 는 "테이블 묘사"를 의미)

mysql은 세가지 다른 테이블 포맷을 가지고 있다; 고정길이, 다이나믹, 압축.


고정 길이 테이블
- 기본 포맷. 테이블에 VARCHAR, BLOB, TEXT 컬럼이 없을 때 사용.
- 모든 CHAR, NUMERIC, DECIMAL 컬럼은 컬럼 길이에 space-padded 이다. (** space-
padded를 무엇이라고 번역해야 할지 애매모호해서 **)
- 매우 빠름
- 캐쉬하기 쉽다
- 손상 후 복구가 쉽다. 왜냐면 고정된 위이에 레코드가 위치하기 때문이다.
- 많은 양의 레코드가 지워졌거나 운영 시스템에서 자유 공간을 늘리길 원치 않는다면 (isa
mchk를 이용) 재조직화할 필요없다.
- 보통 다이나믹 테이블보다 많은 디스크 공간을 필요로 한다.


다이나믹 테이블
- 테이블이 VARCHAR, BLOB, TEXT 컬럼을 포함하고 있을 때 사용.
- 모든 문자열 컬럼은 다이나믹하다.(4보다 작은 길이를 가진 문자열 제외)
- 컬럼이 문자열 컬럼에서 비었거나 ('') 숫자형 컬럼에서 0(NULL 값을 가진 컬럼과 동일
한 것이 아니다) 을 나타내는 비트맵이 모든 레코드 앞에 선행된다. 문자열 컬럼에서 trailin
g space를 제거한 후 zero의 길이를 가지거나 숫자형 컬럼이 zero의 값을 가지면 비트 맵으
로 표시되고 디스크에 저장되지 않는다. 비지 않은 문자는 문자내용에 길이 바이트만큼 추
가되어 저장된다.
- 보통 고정 길이 테이블보다 디스크 공간 절약.
- 줄의 길이를 확장하는 정보를 가지고 줄을 업데이트하면 줄은 단편화될 것이다. 이런 경
우 더 좋은 성능을 위해 때때로 isamchk -r 을 실행해야 한다. 통계적으로(?) isamchk -ei
tbl_name을 사용하자.
- 손상후 복구가 어렵다. 왜냐면 레코드가 많은 조각드로 단편화되고 링크(단편)가 없어지
기 때문이다.
- 다이나믹 사이즈 테이블의 예상되는 열 길이 :
    3
    + (number of columns + 7) / 8
    + (number of char columns)
    + packed size of numeric columns
    + length of strings
    + (number of NULL columns + 7) / 8

각 링크마다 6 바이트가 더 있다. 다이나믹 레코드는 업데이트로 레코드가 늘어날때마다 링
크된다. 각 새로운 링크는 최소 20바이트일 것이며, 그래서 다음의 확장은 아마도 동일한 링
크로 될 것이다. 그게 아니라면 다른 링크가 있을 것이다. isamchk -ed 로 얼마나 많은 링
크가 있는지 체크할 수 있다. 모든 링크는 isamchk -r 로 제거할 수 있다.(** ?? **)

There is a penalty of 6 bytes for each link. A dynamic record is linked whenever an up
date causes an enlargement of the record. Each new link will be at least 20 bytes, so th
e next enlargement will probably go in the same link. If not, there will be another link.
You may check how many links there are with isamchk -ed. All links may be removed
with isamchk -r.


압축 테이블

- 읽기 전용 테이블은 pack_isam 유틸리티로 만들 수 있다. 확장 mysql 이메일 지원을 구
입한 모든 고객은 내부적인 용도로 pack_isam을 사용할 권리가 주어진다.
- 압축해제 코드는 모든 mysql 배포판에 있으므로 pack_isam이 없는 고객도 pack_isam으
로 압축된 테이블을 읽을 수 있다. (테이블이 같은 플랫폼에서 압축되어 있는한)
- 매우 적은 디스크 용량을 사용.
- 각 레코드는 개별적으로 압축이 된다.( 매우 적은 액세스 overhead) 레코드의 헤더는 테
이블의 가장 큰 레코드에 따라 (1-3 바이트) 고정된다. 각 컬럼은 다르게 압축이 된다. 압축
타입은 다음과 같다:

        ㅇ 일반적으로 각 컬럼마다 다른 Huffman 테이블이다.
        ㅇ Suffic 공간 압축
        ㅇ Prefix 공간 압축
        ㅇ 0 값을 가진 숫자는 1비트로 저장.
        ㅇ integer 컬럼의 값이 작은 범위를 가졌다면, 컬럼은 최대한 작은 타입으로 저장
된다. 예를 들면 BIGINT 컬럼은 모든 값이 0부터 255라면 TINIINT 컬럼(1바이트)로 저장
된다.
        ㅇ 컬럼이 몇가지 가능한 값으로만 구성되어 있다면, 컬럼 타입은 ENUM으로 변환
된다.
        ㅇ 컬럼은 위 압축 방법을 조합하여 사용한다.
- 고정 길이나 다이나믹 길이의 테이블을 다룰 수 있다. 그러나 BLOB나 TEXT 컬럼은 다
룰 수 없다.
- isamchk로 압축을 해재할 수 있다.

mysql은 다른 인덱스 타입을 지원한다. 그러나 일반적인 타입은 NISAM이다. 이것은 B-tre
e 인덱스이며 모든 키의 갑을 합하여 (키 길이+4)*0.67로 인덱스 파일의 크기를 대강 계산
할 수 있다. (이것은 모든 키가 정렬된 순서로 입력된 가장 나쁜 경우이다)


String indexes are space compressed. If the first index part is a string, it will also be p
refix compressed. Space compression makes the index file smaller if the string column h
as a lot of trailing space or is a VARCHAR column that is not always used to the full
length. Prefix compression helps if there are many strings with an identical prefix.

문자열 인덱스는 공간이 압축된다. 첫번째 인덱스 부분이 문자열이라면, prefix가 압축된다.
문자열 컬럼이 다량의 trailing space를 가졌거나 언제나 완전한 길이를 사용하지 않는 VA
RCHAR 컬럼일 때 space 압축은 인덱스 파일을 더 작게 만든다. prefix 압축은 많은 문자
열에 동일한 prefix가 있을 때 유용하다.
{{<!-- This HTML file has been created by texi2html 1.52 (hacked by david@detron.se)
     from /dr1/my/masters/mysql/Docs/manual.texi on 2 Febuary 1999 -->
}}11. mysql 벤치마크 스위트

여기에는 mysql 벤치마크 스위트(그리고 crash-me)에 대한 기술적인 설명이 들어가야 한다. 그렇
지만 아직 작성이 되지 않았다. 현재로서는 배포판의 'bench' 디렉토리에서 코드와 결과를 살펴보
아야 한다.(또한 다음의 웹페이지에서 살펴볼 수 있다.
{{{{http://www.mysql.com/crash-me-choose.htmy)
}}
}}

이것은 사용자에게  주어진 SQL 수행이 제대로 수행되는지 아닌지를 알려주는 벤치마크이다.

crash-me 는 실제로 질의를 수행하여 데이터베이스에서 지원하는 기능과 능력, 제한사항 등을 측
정하는 프로그램이다. 예를 들어 다음의 사항을 측정한다:

        ㅇ 지원하는 컬럼 타입
        ㅇ 지원하는 인덱스 숫자
        ㅇ 지원하는 펑션
        ㅇ 질의의 최대 크기
        ㅇ VARCHAR 컬럼의 최대 크기
번역자 : 문태준(taejun@hitel.net)

12. mysql 유틸리티

12.1 다양한 mysql 프로그램 개요

mysql client 라이브러리를 사용하여 서버와 통신을 하는 모든 mysql 클라이언트는 다음의
환경 변수를 사용한다:

Name                    Description
MYSQL_UNIX_PORT         기본 소켓; 로컬호스트에서 접속할때 사용
MYSQL_TCP_PORT          기본 TCP/ip port
MYSQL_PWD               기본 패스워드
MYSQL_DEBUG             디버깅할때 Debug-trace 옵션
TMPDIR                  임시 테이블/파일이 생성되는 디렉토리

MYSQL_PWD 를 사용하는 것은 보안에 취약하다. 6.2 [Connecting] 참고.

'mysql' 클라이언트는 명령행 라인 히스토리에 환경 변수를 저장하기 위해 MYSQL_HISTF
ILE 이라는 파일을 사용한다.

모든 MYSQL 프로그램은 매우 다양한 옵션이 있다. 그러나 모든 MYSQL 프로그램에서 --
help 옵션을 제공한다. --help 옵션을 이용해 프로그램의 다양한 옵션에 대한 모든 정보를
볼 수 있다. 예를 들어, mysql --help 를 해보자.

아래의 목록은 mysql 프로그램에 대해서 설명하고 있다:

isamchk : mysql 테이블 정보 보기, 점검, 최적화, 복구 유틸리티. 많은 기능이 있기 때문에
별도의 장에서 자세히 설명하고 있다. 13장 참고.

make_binary_release : 컴파일된 mysql 바이너리 버전을 만든다. 다른 myql 사용자의 편의
를 위해 ftp.tcx.e의 '/pub/mysql/Incoming' 에 올리자.

msql2mysql : msql 프로그램을 mysql로 변환하는 쉘 스크립트. 모든 경우를 다룰 수는 없
지만 변환할때 유용할 것이다.

mysql : 간단한 SQL 쉘. (GNU readline 호환성있음) 상호대화식 및 비대화식으로 사용할
수 있다. 대화식으로 사용하는 경우, 질의 결과는 아스키-테이블 포맷으로 출력된다. 비대화
식으로 사용할 경우, 결과는 텝으로 분리된 포맷으로 출력된다. (출력 포맷은 명령행 라인
옵션을 이용해 바꿀 수 있다) 다음과 같이 스크립트를 사용할 수 있다:

shell> mysql database < script.sql > output.tab

클라이언트에서 메모리가 부족해서 문제가 생기면 --quick 옵션을 사용하자. 그러면 질의
결과를 가져오기 위해 mysql_store_result() 대신 mysql_use_result()를 사용한다.

mysqlaccess : host, user, database 조합의 접근 권한 점검 스크립트.

mysqladmin : 데이터베이스 생성 및 삭제, 승인 테이블 재로딩, 디스크에 테이블 플러싱, 로
그 파일 재오픈 등을 수행하는 관리자용 유틸리티. mysqladmin은 또한 서버에서 버전, 프로
세스, 상태(status) 정보를 확인할 수 있다.

mysqlbug : mysql 버그 레포트 스크립트. mysql의 버그를 알릴 때 사용하는 스크립트.

mysqld : SQL 대몬. 항상 실행되고 있어야 한다.

mysqldump : mysql 데이터베이스를 SQL문 형태의 파일이나 탭으로 구분된 텍스빚 파일로
덤프하는 유틸리티.

mysqlimport : LOAD DATA INFILE을 사용해 텍스트 파일의 자료를 테이블에 입력하는
유틸리티. 12.2 참고.

mysqlshow : 데이터베이스, 테이블, 컬럼과 인덱스에 대한 정보 출력

mysql_install_db : 기본 권한으로 MYSQL 승인 테이블 생성. 처음 설치했을때만 수행된다.

replace : msql2mysql에서 사용되는 유틸리티이다. 그렇지만 다양하게 적용할 수 있다. 파일
이나 표준 입력의 문자열을 교체할 수 있다. 먼저 긴 문자열을 매칭하기 위해 제한된 상황
의 시스템에서 사용하자(** ??) 문자열을 교체하는데 사용할 수 있다. 예를 들어 다음의 명
령어는 파일에서 a와 b를 교체한다:

shell> replace a b b a -- file1 file2 ...

safe_mysqld : mysqld 대몬을 시작하는 스크립트. 에러가 났을때 서버를 재시작하고 로그
파일에 실행 정보를 기록하는 등 몇가지 안정 대책이 있다.



12. 2 텍스트 파일에서 데이터 입력(수입?)하기

mysqlimport 는 명령행 인터페이스에서 LOAD DATA INFILE sql 문을 제공한다. 대부분
의 옵션은 LOAD DATA INFILE 과 동일하다. 7.15 [Load] 참고.
다음과 같이 사용한다:

shell> mysqlimport [options] filename ...

명령행에서 지정한 텍스트 파일에 대하여, mysqlimport는 파일이름에서 확장자를 제거한다.
그리고 파일의 내용을 어떤 테이블에 넣을 것인지 결정하는데 사용한다. 예를 들어 vkdlfdlf
madl 'patient.txt', 'patient.text', 'patient'는 모두 patient라는 테이블 이름으로 입력될 것이
다.

mysqlimport 는 다음의 옵션을 지원한다:

-C, --compress : 서버, 클라이언트에서 압축을 지원하면 서버/클라이언트 사이에서 모든
정보를 압축한다

-#, --debug[=option_string] : 프로그램 사용 추적(디버깅용)

-d, --delete : 텍스트 파일에서 입력하기 전에 테이블을 비움

--fields-terminated-by=...
--fields-enclosed-by=...
--fields-optionally-enclosed-by=...
--fields-escaped-by=...
--fields-terminated-by=...
        : LODA DATA INFILE 에서의 옵션과 동일한 기능을 갖는 옵션

-f, --force : 에러 생략.텍스트 파일을 위한 테이블이 없으면, 다른 남아있는 파일을 계속
처리(if a table for a text file doesn't exist, continue processing any remaining files) 이
옵션이 없는 경우, 테이블이 없으면 빠져나온다

--help : 도움말 출력

-h host_name, --host=host_name : 지정한 호스트의 mysql 서버에 데이타 입력. 기본값은
localhost

-i, --ignore : --replace 옵션의 정보 참고.

-l, --lock-tables : 텍스트 파일로 처리하기 전에 모든 테이블에 쓰기 락을 건다. 그러면 서
버에서 모든 테이블이 동기화될 수 있다.

-L, --local : 클라이언트에서 입력 파일 읽음. 기본적으로, localhost에서 접속하면 텍스트
파일은 서버에 있다고 가정된다.

-pyour_pass, --password[=your_pass] : 서버에 연결할 때 사용하는 비밀번호. '=your_pass
'를 지정하지 않으면 터미널에서 비밀번호를 물어봄

-P port_num, --port=port_num : 호스트에 연결할 때 사용하는 TCP/IP 숫자. (localhost가
아닌 호스트에서 접속할 때 사용. 예를 들어 유닉스 소켓을 사용하는 경우)

-r, --replace : --replace 와 --ignore 옵션은 unique key 값의 레코드가 중복되어 있을 경
우에 사용된다. --replcae 를 명시할 경우, 동일한 unique key 값을 가지고 있는 레코드를
대체한다. --ignore 를 명시할 경우, unique key 값이 동일한 레코드는 생략된다. 두 옵션
모두 명시하지 않는다면, 중복되는 키 갑이 발견되면 에러를 출력하고 텍스트 파일의 나머
지 부분은 생략이 된다.

-s, --silent : 침묵 모드. 에러가 발생했을 때만 출력.

-S /path/to/socket, --socket=/path/to/socket : 로컬호스트(기본 호스트값)에서 접속할 때
사용하는 소켓 파일.

-u user_name, --user=user_name : 서버에 연결할 때 사용하는 mysql 사용자 이름. 기본값
은 유닉스 로그인 이름.

-v, --verbose : Verbose 모드. 프로그램의 수행에 대한 상세한 정보 출력.

-V, --version : 버전 정보 출력.



12.3 mysql 압축 읽기 전용 테이블 생성기
** 이 부분은 번역 생략. 간단하게 소개만 합니다 **

pack_isam 은 10 라이센스 이상을 구입하거나 extended support 를 받을 때 사용할 수 있
는 추가 유틸리티. 바이너리로만 배포하므로 특정한 플랫폼에서만 사용 가능. 압축률은 40%
-70% 정도이다. 메모리맵을 사용하므로(mmap()) mmap()가 작동되지 않으면 문제가 생긴
다. 압축하고나서는 읽기 전용 테이블이 되며 BLOB 칼럼은 압축하지 못한다.

공개적으로 구할 수 있는 mysql에서 압축 읽기 전용 테이블을 생성하지는 못하지만 읽는
것은 가능하다. 기타 자세한 내용은 매뉴얼을 참고하자.
13. 테이블 유지보수 및 파손 복구에 isamchk 사용하기


데이터베이스 테이블의 정보를 얻을 때, 테이블 점검, 복구 및 최적화 할때 isamchk 유틸리티를 사
용할 수 있다. 다음의 섹션은 어떻게 isamchk를 사용하는지(옵션에 대한 상세한 설명 포함), 테이블
유지 계획을 어떻게 설정할 것인지, 어떻게 isamchk의 다양한 기능을 수행하기 위해 isamchk를 사
용하는지에 대해 설명하고 있다.

{{}}13.1 isamchk 명령어 사용법

isamchk 는 다음과 같이 사용한다:

shell> isamchk [options] tbl_name

옵션은 isamchk로 무엇을 할 것인지 지정한다. 아래에서 설명한다. (isamchk --help 명령으로 옵션
의 목록을 볼 수 있다) 옵션이 없을 때, isamchk는 단지 테이블을 점검한다. 더 많은 정보를 얻으려
하거나 특정한 작업이 필요하면 아래에서 설명하는데로 옵션을 지정한다.

tbl_name은 점검하기 원하는 데이터베이스 테이블이다. 데이터베이스 디렉토리가 아닌 다른 곳에
서 isamchk를 실행하면, 파일의 경로를 지정해야 한다. 왜냐하면 isamchk는 데이터베이스의 위치
에 대해서 알지 못하기 때문이다. 실제로, isamchk는 작업하려는 파일이 데이터베이스 디렉토리에
있는지 아닌지 신경을 쓰지 않는다; 데이터베이스 테이블에 해당하는 파일을 다른 곳으로 복사하
고 그곳에서 복구 작업을 할 수 있다.

원한다명 isamchk의 명령행에서 여러개의 테이블을 사용할 수 있다. 또한 이름을 인덱스 파일 이
름('.ISM' 가 붙음)으로 지정할 수 있으며 이런 경우 '*.ISM' 패턴을 사용하여 디렉토리의 모든 테이
블을 지정할 수 있다. 예를 들어 데이터베이스 디렉토리에 있다면 다음과 같이 디렉토리의 모든
테이블을 점검할 수 있다:

shell> isamchk *.ISM


If you are not in the database directory, you can check all the tables there by specifying the path to the dir
ectory:

데이터베이스 디렉토리에 있지 않으면, 디렉토리의 경로를 지정하여 모든 테이블을 점검할 수 있
다.

shell> isamchk /path/to/database_dir/*.ISM

또한 mysql data 디렉토리의 경로를 사용한 와일드 카드를 지정하여 모든 데이터베이스의 모든 테
이블을 점검할 수 있다:

shell> isamchk /path/to/datadir/*/*.ISM

isamchk는 다음의 옵션을 지원한다:

-a, --analyze
Analyze the distribution of keys. This will make some joins in MySQL faster.
키의 분포를 분석. mysql에서 특정한 조인을 빠르게 만든다.

-#, --debug=debug_options
Output debug log. The debug_options string often is 'd:t:o,filename'.
디버그 로그 출력. debug_options 문자는 흔지 'd:t:o,filename' 이다.

-d, --description
테이블의 정보 출력

-e, --extend-check
테이블을 매우 상세하게 점검. 아주 특정한 경우에만 필요하다. 일반적으로 isamchk는 이 옵션이
없어도 모든 에러를 찾을 수 있다.

-f, --force
Overwrite old temporary files. If you use -f when checking tables (running isamchk without -r), isamchk
will automatically restart with -r on any table for which an error occurs during checking.
이전의 오래된 임시 파일을 덮어씀. 테이블을 점검할때 -f를 사용하면(-r 없이 isamchk를 실행) isa
mchk는 점검하는 동안 에러가 발생하는 테이블에서 자동으로 -r 옵션을 시작한다.


--help
도움말 출력.

-i, --information
점검을 한 테이블의 통계 정보 출력.

-k #, --keys-used=#
Used with -r. Tell the NISAM table handler to update only the first # indexes. Higher-numbered indexes
are deactivated. This can be used to get faster inserts! Deactivated indexes can be reactivated by using isa
mchk -r.
-r 과 함께 사용. NISAM 테이블 핸들러에 첫 # 인덱스만 업데이트하라는 것을 알려준다. Higher-nu
mberd(?) 인덱스가 해제된다. 이것은 insert를 빠르게 할때 사용한다! 해제된 인덱스는 isamchk -r 을
사용하여 재활성화된다.

-l, --no-symlinks
복구할때 심볼릭 링크를 따르지 않는다. 일반적으로 isamchk는 심볼릭 링크가 가리키는 테이블을
복구한다.



-q, --quick
빠르게 복구하기 위해 -r 과 함께 사용. 일반적으로 원래의 데이타 파일은 건드리지 않는다; 두번째
-q를 지정하여 원래의 데이타 파일을 사용하도록 할 수 있다.

-r, --recover
복구 모드. 유일하지 않는 unique 키만 제외하고 거의 모든 것을 복구한다.

-o, --safe-recover
복구 모드. 구식 복구 방법을 사용; -r을 사용하여 복구하는 것보다 느리다. 그렇지만 -r이 다룰 수
없는 몇가지 경우에 사용할 수 있다.
(** -r이 다룰 수 없는 경우란 무엇인지 잘 모르겠네요... **)

-O var=option, --set-variable var=option
변수값 설정. 설정가능한 변수는 아래에서 설명.

-s, --silent
침묵 모드. 에러가 발생할 때만 출력을 한다. 두개의 -s(-ss)를 사용하면  isamchk에서 매우 조용하
게 작업을 할 수 있다.

-S, --sort-index
Sort index blocks. This speeds up "read-next" in applications.
인덱스 블락 정열. 애플리케이션에서 "read-next" 속도를 향상.(??)

-R index_num, --sort-records=index_num
인덱스에 따라 레코드를 정렬. 이 작업을 하면 데이타를 집중시킬 수 있고 이 인덱스를 사용한 SE
LECT 와 ORDER BY 작업의 속도를 증가시킬 수 있다. (처음에는 정렬하는 시간이 매우 느리다!)
테이블의 인덱스 번호를 찾기 위해 SHOW INDEX를 사용한다. SHOW INDEX는 isamchk에서 사용
하는 것과 같은 순서로 테이블의 인덱스를 보여준다. 인덱스는 1번부터 시작하여 번호가 매겨진
다.

-u, --unpack
Unpack a table that was packed with pack_isam.
pack_isam 으로 압축된 테이블의 압축 해제.

-v, --verbose
Verbose 모드. 정보를 출력. -d 와 -e 와 함께 사용할 수 있다. 여러개의 -v를 사용(-vv, -vvv)하여 더
자세하게 볼 수 있다.

-V, --version
isamchk 버전 출력.

-w, --wait
테이블에 락이 걸려 있으면 기다림.

--set-variable (-O) 옵션의 설정 가능한 변수는 다음과 같다:

keybuffer             default value: 520192
readbuffer            default value: 262136
writebuffer           default value: 262136
sortbuffer            default value: 2097144
sort_key_blocks       default value: 16
decode_bits           default value: 9
{{}}

13.2 isamchk 메모리 사용법

Memory allocation is important when you run isamchk. isamchk uses no more memory than you specify
with the -O options. If you are going to use isamchk on very large files, you should first decide how much
memory you want it to use. The default is to use only about 3M to fix things. By using larger values, you
can get isamchk to operate faster. For example, if you have more than 32M RAM, you could use options s
uch as these (in addition to any other options you might specify):

메모리 할당은 isamchk를 실행할 때 중요하다.isamchk는 -O 옵션에서 지정한 것 이상으로 메모리
를 사용하지 않는다. 매우 큰 파일에서 isamchk를 사용하려 하면, 얼마마 많은 메모리를 사용할 것
인지 먼저 결정해야 한다. 기본값은 문제를 고치는데 3M를 사용한다. 더 많은 값을 사용해 더 빠르
게 isamchk를 사용할 수 있다. 예를 들어 32M 이상 램을 가지고 있다면 다음과 같은 옵션을 사용할
수 있다. (사용자가 지정한 옵션에 추가하여):

shell> isamchk -O sortbuffer=16M -O keybuffer=16M \
           -O readbuffer=1M -O writebuffer=1M ...

-O sortbuffer=16M 를 사용하면 대부분의 경우에는 충분하다.

Be aware that isamchk uses temporary files in TMPDIR. If TMPDIR points to a memory file system, you
may easily get out of memory errors.
isamchk 는 TMPDIR의 임시 파일을 사용한다는 것에 주의하자. 만약 TMPDIR이 메모리 파일 시스
템을 가리킨다면 쉽게 메모리 에러에서 벗어날 수 있다.


13. 3 테이블 유지보수 설정
{{}}13.3 Setting up a table maintenance regime

문제가 생길때를 기다리는 것보다 정기적으로 테이블을 점검하는 게 좋다. 유지보수 계획를 위하
여 isamchk -s 를 사용해 테이블을 점검할 수 있다. -s 옵션을 사용하면 isamchk가 침묵 모드로 작동
을 하며 에러가 발생했을 때만 메시지를 출력한다.

서버를 시작할때 테이블을 점검하는 것도 좋은 생각이다. 예를 들어 업데이트 도중에 시스템이 리
부팅을 했을 때마다 일반적으로 영향을 받은 모든 테이블(이것을 "expected crashed table"이라고 한
다)을 점검해야 한다. 만약 오래된 '.pid' (프로세스 ID) 파일이 재부팅후에 남아 있다면 최근 24시간
동안 변경이 된 모든 테이블을 점검하기 위해 safe_mysqld에 isamchk를 실행하는 테스트를 추가해
야 한다.('.pid' 파일은 mysqld가 시작할때 만들어지며 일반적으로 mysqld가 종료될때 제거된다. 시
스템이 시작할때 '.pid' 파일이 있다는 것은 mysqld가 비정상적으로 종료되었다는 것을 나타낸다.)

더 좋은 테스트는 최근에 변경된 시간이 '.pid' 파일보다 최근인 테이블을 점검하는 것이다.

또한 일반적인 시스템 운영중에 정기적으로 테이블을 점검할 수 있다. TcX에서는 'crontab' 파일에
다음의 라인을 사용하여 일주일에 한번씩 우리의 중요한 테이블을 점검하도록 cron 작업을 돌린
다:

35 0 * * 0 /path/to/isamchk -s /path/to/datadir/*/*.ISM

이렇게 하면 손상된 테이블에 대한 정보를 출력하여 필요할때 테이블을 점검하고 복구할 수 있다.

몇년동안 우리는 예상하지 못하게 테이블이 손상(하드웨어 문제가 아닌 다른 이유로 문제가 생긴
테이블)된 경우가 없어서 우리에겐 일주일만으로도 충분하다. (이것은 정말로 진실이다)

우리만큼 mysql에 대해 신뢰를 할 때까지 최근 24시간동안 업데이트된 모든 테이블에 대해 매일
밤마다 isamchk -s 를 실행할 것을 추천한다.


13.4 테이블 정보 얻기

테이블에 대한 정보나 통계를 얻기 위해 아래의 명령을 사용하자. 뒤에서 자세하게 정보에 대해
설명할 것이다.

isamchk -d tbl_name
테이블에 대한 정보를 얻기 위해 "describe(설명) 모드"로 isamchk를 실행. mysql 서버를 --skip-locki
ng 옵션을  사용해 시작하면, isamchk는 서버가 실행되는 동안 업데이트된 테이블에서 에러가 난
것을 보고한다. 그러나 isamchk는 describe 모드에서 테이블을 변경하지 못하기 때문에 데이타를
읽을 위험이 없다.

isamchk -d -v tbl_name
isamchk가 수행하는 것에 대해 더 자세한 정보를 보기위해 -v 옵션을 추가하여 verbose 모드로 수
행할 수 있다.

isamchk -eis tbl_name
테이블에서 가장 중요한 정보만 보여준다. 전체 테이블을 다 읽어야 하기 때문에 속도가 느리다.

isamchk -eiv tbl_name
-eiv 와 비슷하지만 현재 무엇이 진행되고 있는지  보여준다.


isamchk -d 출력 예제:

ISAM file:     company.ISM
Data records:           1403698  Deleted blocks:         0
Recordlength:               226
Record format: Fixed length

table description:
Key Start Len Index   Type
1   2     8   unique  double
2   15    10  multip. text packed stripped
3   219   8   multip. double
4   63    10  multip. text packed stripped
5   167   2   multip. unsigned short
6   177   4   multip. unsigned long
7   155   4   multip. text
8   138   4   multip. unsigned long
9   177   4   multip. unsigned long
    193   1           text


isamchk -d -v 출력 예제:

ISAM file:     company.ISM
Isam-version:  2
Creation time: 1996-08-28 11:44:22
Recover time:  1997-01-12 18:35:29
Data records:           1403698  Deleted blocks:              0
Datafile: Parts:        1403698  Deleted data:                0
Datafilepointer (bytes):      3  Keyfile pointer (bytes):     3
Max datafile length: 3791650815  Max keyfile length: 4294967294
Recordlength:               226
Record format: Fixed length

table description:
Key Start Len Index   Type                      Root Blocksize Rec/key
1   2     8   unique  double                15845376      1024       1
2   15    10  multip. text packed stripped  25062400      1024       2
3   219   8   multip. double                40907776      1024      73
4   63    10  multip. text packed stripped  48097280      1024       5
5   167   2   multip. unsigned short        55200768      1024    4840
6   177   4   multip. unsigned long         65145856      1024    1346
7   155   4   multip. text                  75090944      1024    4995
8   138   4   multip. unsigned long         85036032      1024      87
9   177   4   multip. unsigned long         96481280      1024     178
    193   1           text


Example of isamchk -eis 출력 예제:

Checking ISAM file: company.ISM
Key:  1:  Keyblocks used:  97%  Packed:    0%  Max levels:  4
Key:  2:  Keyblocks used:  98%  Packed:   50%  Max levels:  4
Key:  3:  Keyblocks used:  97%  Packed:    0%  Max levels:  4
Key:  4:  Keyblocks used:  99%  Packed:   60%  Max levels:  3
Key:  5:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
Key:  6:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
Key:  7:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
Key:  8:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
Key:  9:  Keyblocks used:  98%  Packed:    0%  Max levels:  4
Total:    Keyblocks used:  98%  Packed:   17%

Records:          1403698    M.recordlength:     226   Packed:             0%
Recordspace used:     100%   Empty space:          0%  Blocks/Record:   1.00
Recordblocks:     1403698    Deleteblocks:         0
Recorddata:     317235748    Deleted data:         0
Lost space:             0    Linkdata:             0

User time 1626.51, System time 232.36
Maximum resident set size 0, Integral resident set size 0
Non physical pagefaults 0, Physical pagefaults 627, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 639, Involuntary context switches 28966


isamchk -eiv 출력 예제:

Checking ISAM file: company.ISM
Data records: 1403698   Deleted blocks:       0
- check file-size
- check delete-chain
index  1:
index  2:
index  3:
index  4:
index  5:
index  6:
index  7:
index  8:
index  9:
No recordlinks
- check index reference
- check data record references index: 1
Key:  1:  Keyblocks used:  97%  Packed:    0%  Max levels:  4
- check data record references index: 2
Key:  2:  Keyblocks used:  98%  Packed:   50%  Max levels:  4
- check data record references index: 3
Key:  3:  Keyblocks used:  97%  Packed:    0%  Max levels:  4
- check data record references index: 4
Key:  4:  Keyblocks used:  99%  Packed:   60%  Max levels:  3
- check data record references index: 5
Key:  5:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
- check data record references index: 6
Key:  6:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
- check data record references index: 7
Key:  7:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
- check data record references index: 8
Key:  8:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
- check data record references index: 9
Key:  9:  Keyblocks used:  98%  Packed:    0%  Max levels:  4
Total:    Keyblocks used:   9%  Packed:   17%

- check records and index references
[LOTS OF ROW NUMBERS DELETED]

Records:          1403698    M.recordlength:     226   Packed:             0%
Recordspace used:     100%   Empty space:          0%  Blocks/Record:   1.00
Recordblocks:     1403698    Deleteblocks:         0
Recorddata:     317235748    Deleted data:         0
Lost space:             0    Linkdata:             0

User time 1639.63, System time 251.61
Maximum resident set size 0, Integral resident set size 0
Non physical pagefaults 0, Physical pagefaults 10580, Swaps 0
Blocks in 4 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 10604, Involuntary context switches 122798



다음은 앞의 예제에서 사용한 테이블의 데이타와 인덱스 파일 크기이다:

-rw-rw-r--   1 monty    tcx     317235748 Jan 12 17:30 company.ISD
-rw-rw-r--   1 davida   tcx      96482304 Jan 12 18:35 company.ISM


isamchk가 출력하는 정보 타입에 대한 설명은 아래와 같다. "keyfile"은 인덱스 파일이다. "Record"
와 "row"는 같은 말이다.

ISAM file
        ISAM (index) 파일 이름.

Isam-version
        ISAM 포맷 버전. 현재는 항상 2.

Creation time
        데이타 파일 생성 시간.

Recover time
        인덱스/데이타 파일이 최근에 복구된 시간.

Data records
        테이블에 있는 레코드 수.

Deleted blocks
        지워진 블락이 차지하고 있는 공간. 이러한 공간을 줄이기 위해 테이블의 최적화를 할 수
있다. 13.5.3 [최적화] 참고.

Datafile: Parts
        동적인 레코드 포맷을 위해, 얼마나 많은 데이타 블락이 있는지를 알림. 단편화된 레코드
가 없는 최적화된 테이블에서는 Data records 와 같다.

Deleted data
        회수하지 않은 지원진 데이타의 바이트 수. 이렇나 공간을 없애기 위해 테이블을 최적화
할 수 있다. 13.5.3 [최적화] 참고.

Datafile pointer
        데이타 파일 포인터의 크기로 바이트수. 일반적으로 2,3,4,5 바이트이다. 대부분의 테이
블은 2바이트로 관리를 한다. 그렇지만 아직까지 mysql에서는 제어를 할 수 없다. 고정 테이블에서
이는 레코드 주소(address)이다. 동적 테이블에서 이는 바이트 주소이다.



Keyfile pointer
        인덱스 파일 포인터의 크기로 바이트. 일반적으로 1,2,3 바이트이다. 대부분의 테이블은
2바이트로 관리를 한다. 그렇지만 이 크기는 mysql에서 자동으로 계산이 된다. 항상 블락 주소이
다.

Max datafile length
        테이블의 데이터 파일(.ISD 파일)의 최대 크기. 바이트.

Max keyfile length
        테이블의 key file(.ISM 파일)의  최대 크기. 바이트.

Recordlength
        각 레코드가 차지하고 있는 공간. 바이트.

Record format
테이블의 레코드를 저장하는데 사용된 포맷. 위에서 보여준 예제는 고정 길이를 사용하고 있다.
다른 값은 Compressed 와 Packed 이다.

table description
        테이블의 모든 키의 목록. 각 키에 대한 자세한 설명은 다음과 같다:
Key
        키의 숫자.
Start
        Where in the record this index part starts.
        레코드에서 인덱스가 시작하는 위치.
Len
How long this index part is. For packed numbers, this should always be the full length of the column. For
strings, it may be shorter than the full length of the indexed column, because you can index a prefix of a st
ring column.
        인덱스 부분의 길이. For packed numbers(꽉 찬 숫자를 위해??? 번역이 이상..), 컬럼의 총
길이가 되어야 한다. 문자열에서는 인덱스된 컬럼의 총 길이보다 작아야 한다. 왜냐하면 문자열
컬럼 앞에 인덱....

Index
        이 인덱스에 같은 값이 여러개 존재할 수 있는지 없는지를 나타냄.
Type
        인덱스 부부의 데이터 타입. packed, stripped, empty 옵션을 가진 NISAM 데이타 타입이
다.
Root
        루트 인덱스 블락의 주소.
Blocksize
        각 인덱스 블락의 크기.기본값은 1024이다. 그렇지만 이 값은 컴파일 할때 변경할 수 있
다.
Rec/key
This is a statistical value used by the optimizer. It tells how many records there are per value for this key.
A unique key always has a value of 1. This may be updated after a table is loaded (or greatly changed) wit
h isamchk -a. If this is not updated at all, a default value of 30 is given.
        최적화기(optimizer)에서 사용하는 통계적인 값. It tells how many records there are per valu
e for this key. unique 키는 항상 1의 값을 가진다. 이 값은 isamchk -a로 테이블이 로딩된 후에(또는
매우 많이 변경되었을때) 업데이트된다. 전혀 업데이트되지 않으면 기본값으로 30이 주어진다.


위의 첫번째 예제에서, 9번째 키는 두부분을 가진 멀티-파트 키이다.


Keyblocks used
What percentage of the keyblocks are used. Since the table used in the examples had just been reorganize
d with isamchk, the values are very high (very near the theoretical maximum).
        키블락이 사용하고 있는 비율.(%) 예제의 테이블은 isamchk로 재조직화(reorganize)되었
기 때문에 값이 매우 높다.(이론적인 최대값에 매우 근접)

Packed
MySQL tries to pack keys with a common suffix. This can only be used for CHAR/VARCHAR/DECIM
AL keys. For long strings like names, this can significantly reduce the space used. In the third example ab
ove, the 4th key is 10 characters long and a 60% reduction in space is achieved.
....  이름과 같은 long 문자열에서 공간 사용을 상당히 줄인다. 위의 네번째 예제에서 4번째 키는 10
문자 long 이고 60%의 공간이 줄었다.
(** 번역이 잘 안되는데 접미사같은 것을 붙여서 문자열 등의 공간을 줄인다 머 그런 것이겠지요 *
*)

Max levels
        How deep the B-tree for this key is. Large tables with long keys get high values.

Records
        테이블의 레코드수.
M.recordlength
        평균 레코드 길이. 고정 길리 레코드의 테이블에서 이 값은 레코드 길이와 같다.

Packed
        MySQL strips spaces from the end of strings. The Packed value indicates the percentage saving
s achieved by doing this.


mysql은 문자끝의 공백을 제거한다.Packed value는 이렇게 해서 절약된 공간의 비율을 말한다.

Recordspace used
        데이타 파일이 사용하는 공간의 비율.

Empty space
        데이타 파일이 사용하지 않는 공간의 비율.

Blocks/Record
        레코드당 평균 블락수.(즉, 단편화된 레코드가 몇개의 링크로 구성되어 있는지) 고정-포
맷 테이블에서는 항상 1이다. 이 값은 가능한한 1에 가깝게 유지해야한다. 이 값이 너무 커지면 isa
mchk로 테이블을 최적화(reorganize)해야 한다. 13.5.3 [Optimizaiton] 참고.

Recordblocks
        사용하는 블락(링크)수. 고정 포맷에서 이값은 레코드수와 같다.

Deleteblocks
        삭제된 블락(링크)수.

Recorddata
        데이타 파일이 사용하는 바이트수.

Deleted data
        데이터 파일에서 삭제된(사용하지 않는) 바이트수.

Lost space
        레코드가 매우 짧은 길이로 업데이트되면 약간의 공간을 잃게 된다. 이 값은 이러한 공간
의 bytes 합계이다.

Linkdata
        동적 테이블 포맷을 사용할 때,레코드 조각은 포이터로 링크된다.Linkdata는 이런 포인터
에서 사용하는 저장 공간의 합이다.


pack_isam으로 테이블을 압축했으면, isamchk -d 는 각 테이블 컬럼에 대한 추가적인 정보를 출력
한다.이러한 정보와 그 정보가 무엇을 의미하는지에 대해서는 12.3 [pack_isam]을 참고.


{{}}13.5 파손 복구에 isamchk 사용하기.
mysql에서 데이타를 저장하는데 사용하는 파일 포맷은 광범위하게 테스트되었다. 그렇지만 데이
터베이스 테이블의 손상될 수 있는 외부적인 상황이 항상 있다:



ㅇ 쓰기 도중에 mysqld 프로세스가 죽었을때.
ㅇ 예상치 못하게 컴퓨터가 셧다운되었을때(예를 들어, 컴퓨터의 전원이 나가는 경우)
ㅇ 하드웨어 에러

이번 절에서는 mysql 데이터베이스에서 data의 손상을 체크하고 이에 대처하는 방법에 대해서 설
명한다.

손상 복구 작업을 할 때 테이터베이스의 각 테이블 tbl_name은 데이터베이스 디렉토리의 세 파일
에 조응한다는 것에 대해서 이해하고 있는 것이 중요하다:

파일                    용도
`tbl_name.frm'            테이블 정의(형식) 파일
`tbl_name.ISD'            데이타 파일
`tbl_name.ISM'           인덱스 파일


세가지 파일 타입은 다양한 방법으로 손상을 당한다. 그렇지만 대부분의 문제는 데이타 파일과 인
덱스 파일에서 생긴다.

isamchk는 '.ISD' (데이타) 파일의 복사복을 만들어 작업을 한다. 이전의 '.ISD' 파일을 제거하고 새
로운 파일을 이전의 파일 이름으로 바꾸면서 복구 작업을 마친다. --quick 옵션을 사용하면 isamchk
는 임시 '.ISD' 파일을 만들지 않는다. 대신 '.ISD' 파일이 정확하다고 가정하여 '.ISD' 파일은 손대지
않고 새로운 인덱스 파일만 생성한다. isamchk는 자동으로 'ISD' 파일이 손상되었는지 확인하고 손
상되었을 경우 복구 작업을 중지하므로 --quick 옵션을 사용하는 것은 안전하다. 두개의 -quick 옵
션을 사용할 수 있다. 이런 경우 특정한 에러(중복된 키 등)에서 취소를 하지는 않지만 '.ISD' 파일
을 수정하여 문제를 해결하려고 한다.일반적으로 두개의 --quick 옵션을 사용하는 것은 복구작업
을 수행하기 위한 디스크 공간이 거의 없을 경우에만 유용하다. 이런 경우 isamchk를 수생하기전
에 최소한 백업을 해 놓아야 한다.

{{}}
13.5.1 에러가 났을때 테이블 점검 방법

테이블을 점검하기 위해 다음의 명령을 사용한다:
:
isamchk tbl_name
        모든 에러의 99.99%를 발견할 수 있다. 이 경우 발견하지 못하는 것은 데이타 파일과 관
련된 손상이다.(일반적으로 거의 생기지 않는다) 테이블을 점검하고자 한다면 일반적으로는 아무
런 옵션을 주지 않거나 -s 나 --silent 옵션을 주어 isamchk를 수행하는 것이다.

isamchk -e tbl_name
        이 옵션은 모든 데이터를 완전하게 점검한다.( -e 는 "extended check" 를 의미한다) 모든
키가 정확한 레코드를 가리키고 있는지 점검한다.It does a check-read of every key for each row to ve
rify that they indeed point to the correct row. 많은 키를 가진 큰 테이블에서는 시간이 많이 걸린다. is
amchk는 일반적으로 첫번째 에러를 발견하면 실행을 멈춘다. 더 많은 정보를 얻고자 한다면, --ver
bose (-v) 옵션을 추가할 수 있다. 이 옵션을 추가하면 isamchk는  최대 20개의 에러가 있을 때까지
계속 실행을 한다. 일반적으로는 간단한 isamchk (테이블 이름 외에 아무런 인수도 없는)만으로 충
분하다.

isamchk -e -i tbl_name
        위의 명령과 같다. 그렇지만 -i 옵션을 붙이면 isamchk가 정보의 통계를 출력한다.


13.5.2 테이블 복구방법

손상된 테이블의 징후는 일반적으로 질의가 갑자기 중지되고 다음과 같은 에러를 낸다:
ㅇ`tbl_name.frm' is locked against change
ㅇCan't find file `tbl_name.ISM' (Errcode: ###)
ㅇGot error ### from table handler (Error 135 is an exception in this case)
ㅇUnexpected end of file
ㅇRecord file is crashed

이런 경우, 테이블을 고쳐야 한다. isamchk는 일반적으로 잘못된 것을 감지하고 대부분을 고친다.

복구 과정은 아래에서 설명하는대로 4단계가 있다.시작하기 전에 먼저 데이타베이스 디렉토리로
이동하고(cd 명령 이용) 테이블 파일의 퍼미션을 확인해야 한다. mysqld를 실행할 수 잇는 유닉스
사용자가 읽을 수 있는지 확인해야 한다. (또한 작업을 하려는 사용자. 왜냐하면 점검하려는 파일
에 접근해야 하기 때문이다) 파일을 수정해야 한다면 파일에 쓰기 권한이 있어야 ㅎ난다.

1단계 : 테이블 점검

isamchk *.ISM 또는 (충분한 시간이 있다면 isamchk -e *.ISM). 불필요한 정보를 보지 않으려면 -s (s
ilent) 옵션을 사용한다.

isamchk에서 에러가 있다고 알리는 테이블만 고쳐야 한다. 이런 테이블의 경우는 2단계로 넘어간
다.

점검하면서 에러를 만났을 때(out of memory 에러 등) 또는 isamchk가 기능을 멈추었을 때 3단계로
넘어간다.


2단계 : 쉽고 안전한 복구

먼저 isamchk -r -r tbl_name을 시도한다. (-r -q는 "빠른 복구 모드"를 의미) 이경우 데이타 파일은 손
대지 않고 인덱스 파일 복구를 시도한다. 데이타 파일이 제대로 되어 있고 삭제 링크가 데이타 파
일내의 정확한 위치를 가리키고 있다면, 원활하게 작동을 하고 테이블을 고칠 것이다.(If the data fi
le contains everything that it should and the delete links point at the correct locations within the data file, t
his should work and the table is fixed.) 다음 테이블을 고치자. 그게 아니라면 다음 과정을 사용한다:

        1. 진행하기 전에 데이타 파일의 백업본 만들기
        2. isamchk -r tbl_name 사용.(-r 는 "복구 모드" 의미) 그러면 데이타 파일에서 정확하지 않
은 레코드와 삭제된 레코드를 제거하고 인덱스 파일을 재구성한다.
        3. 앞의 과정이 실패하면, isamchk --safe_recover tbl_name을 사용. Safe recovery 모드는 구
식 복구 방법을 사용하며 일반적인 복구 모드로 할 수 없는 몇가지 경우에 사용할 수 있다.(그렇지
만 더 느리다)


점검하면서 에러를 만났을 때(out of memory 에러 등) 또는 isamchk가 기능을 멈추었을 때 3단계로
넘어간다.


3단계 : 어려운 복구

인덱스 파일의 첫 16k 블락이 파괴되거나 정확하지 않은 정보를 가지고 있을 때, 또는 인덱스 파일
이 없는 경우에만 이번 단계까지 온다. 이경우 새로운 인덱스 파일을 만들어야 한다. 다음과 같이
하자:

        1. 데이타 파일을 안전한 장소로 이동.
        2. 새로운(빈) 데이타와 인덱스 파일을 만들기 위해 table description 파일을 사용:
                shell> mysql db_name
                mysql> DELETE FROM tbl_name;
                mysql> quit
        3. 이전의 데이타 파일을 새롭게 만든 데이터 파일로 복사. (이전의 데이타 파일을 새로운
파일로 옮기지는 말자; 잘못되었을 경우 복사본을 유지하길 원할 것이다)

2단계로 가자. isamchk -r -q는 이제 제대로 작동을 할 것이다. (무한 루프가 되면 안된다. This shoul
dn't be an endless loop).



4단계 : 매우 어려운 복구

description 파일 또한 손상을 입었을 경우에만 이번 단계까지 온다. description 파일은 테이블을 만
든 이후에 변경이 되지 않기 때문에, 이러한 경우는 결코 생겨서는 안된다.

        1. 백업본에서 description 파일을 복구해 3단계로 넘어간다. 또한 인덱스 파일을 복구할
수 있고 2단계로 넘어간다. 뒤의 경우 isamchk -r로 시작을 해야 한다.
        2. 백업본이 없지만 정확히 어떻게 테이블을 만들었는지 알고 있다면, 다른 데이터베이
스에 테이블의 복사본을 만든다. 새로운 데이타 파일을 제거하고 다른 데이터베이스의 description
과 인덱스 파일을 손상된 데이터베이스로 옮긴다. 이렇게 하면 새로운 description 과 인덱스 파일
을 얻을 수 있지만 데이타 파일만 따로 남아있다. 2단계로 가서 인덱스 파일을 재구성한다.

13.5.3 테이블 최적화

레코드를 삭제하거나 업그레이드 하면서 생긴 단편화된 레코드를 모으고 불필요하고 낭비된 공간
을 제거하기 위해 복구 모드로 isamchk를 실행한다:

        shell> isamchk -r tbl_name

SQL OPTIMIZE TABLE 문을 이용하여 같은 방법으로 테이블을 최적화할 수 있다. OPTIMIZE TA
BLE 은 쉽지만 isamchk가 더 빠르다.

또한 isamchk는 테이블의 성능을 향상시킬 수 있는 몇가지 옵션을 사용할 수 있다:

-S, --sort-index
        high-low 순서로 인덱스 트리 블락을 정열. 검색을 최적화하고 키에 의한 테이블 검색을
빠르게 한다.
-R index_num, --sort-records=index_num
        인덱스에 따라 레코드를 정열. 데이타를 지역화하고 이 인덱스를 사용하는 SELECT 와 O
RDER BY 오퍼레이션의 속도를 향상시킨다. (처음에는 정열을 하는 시간이 엄청 느리다!) 테이블
의 인덱스 번호를 확인하려면 SHOW INDEX를 사용하면 되며, SHOW INDEX는 isamchk가 인덱스
를 검색하는 순서대로 테이블의 인덱스를 보여준다. 인덱스는 1번부터 번호가 매겨진다.
-a, --analyze
        테이블에서 키의 분포를 분석. 나중에 테이블에서 레코드를 가져올 때 조인의 성능을 향
상시킨다.
15. mysql ODBC 지원


mysql은 MyODBC 프로그램을 통해 ODBC에 대한 지원을 제공한다.

15.1 MyODBC 를 지원하는 운영체제

MyODBC 는 윈도우95와 NT에서 32비트 ODBC(2.50) 레벨 0 드라이버이다. 우리는 누군가가 윈
도우 3.x 에 이 프로그램을 포팅해주길 바란다.
(** 당근, 저는 능력안됨 **)


15.2 MyODBC에 문제가 있는 경우

ODBC는 액세스, Admndemo.exe, C++-빌더, Centura Team Developer (formerly Gupta SQL/Win
dows), 콜드퓨전(솔라리스용), 크리스탈 레포트, 델파이, 엑셀, iHTML, FileMaker Pro, 폭
스프로, 노츠 Notes 4.5/4.6, SBSS, perl DBD-ODBC, 파라독스, 파워빌더, VC++, 비주얼 베
이직에서 테스팅되었다.

MyODBC 에서 잘 작동하는 다른  애플리케이션이 있으면 myodbc@tcx.se 에 메일을  보내주세
요~ (** 이정도는 해 주어야 서로서로 좋겠지용**)

어려움에 부딪치면, ODBC 매니저에서의 로그 파일과 MyODBC 로그에 대해서 알고 싶다. 이런
파일이 있으면 문제를 조금이나마 줄이는데 도움이 될 것이다.

MyODBC log 파일을 얻으려면 MyODBC 연결/설정 화면의 'Trace MyODBC' 옵션에 체크를 한다.
로그는 `c:\myodbc.log' 에 기록될 것이다. 이 옵션이 제대로 작동하려면 MYSQL2.DLL 이 아
니라 MYSQL.DLL 을 사용해야 한다는 것을 기억하자!


15.3 MyODBC 와 잘 작동하는 프로그램

대부분의 프로그램은 myodbc 와 잘 작동한다. 그렇지만 아래의 각 목록에 있는 것은 우리가
직접 테스팅하였거나 다른 사람들이 제대로 작동한다고 확증해 준 것이다::

ㅇ액세스
액세스와는 잘 작동한다:
        - 테이블에서 프라이머리 키를 지정해야 한다.
        - 업데이트가 되길 원하는 모든 테이블에 timestamp를 가지고 있어야 한다.
        - double float 필드만을 사용해야 한다. single floats 와 비교를 하면 실패한다.
        - mysql에 연결할 때 'Return matching rows' 옵션 필드에 체크를 하자.
        - NT에서의 액세스는 BLOB 컬럼을 OLE OBJECTS 로 보고한다. 대신 MEMO 컬럼을 가
지길 원하면 ALTER TABLE 을 이용해 컬럼을 TEXT 로 바꾸자.

ㅇ 엑셀
잘 작동한다 몇가지 팁이 있다:

dates 에서 문제가 있으면 CONCAT() 함수를 사용하여 string 으로서 select 를 사용하자.
예를 들면:

        select CONCAT(rise_time), CONCAT(set_time) from sunrise_sunset;


문자열로서 값을 가져오며 엑셀97에서 time 값이 제대로 인식될 것이다.

이 예제에서 CONCAT()의 목적은 ODBC가 컬럼을 "string type"으로 생각하도록 속이는 것이
다. CONCAT() 가 없으면, ODBC는 컬럼을 time 타입으로 인식하며 엑셀은 이것을 이해하는데
실패한다.

이것은 엑셀의 버그라는 것을 기억하자. 엑셀은 자동적으로 문자열(string)을 time으로 변
환한다. 소스가 텍스트 파일이라면 잘 될 것이다. 그렇지만 소스가 각 컬럼의 정확한 타입
을 보고하는 ODBC 연결이라면 분명히 어리석은 짓이다.

ㅇ odbcadmin
ODBC 테스트 프로그램.


ㅇ 델파이
DBE 3.2 나 이후 버전을 사용해야 한다. mysql에 연결할 때 'Don't optimize column width'
옵션 필드에 체크를 한다.

물론, 여기에는 myodbc 를 위한 ODBC 목록과 BDE 목록을 세팅하기 위해 잠재적으로 유용한
델파이 코드가 있다. (BDE 목록은 델파이 슈퍼 페이지에서 공개로 받을 수 있는 BDE Alias
데이터를 필요로 한다) : (Thanks to Bryan Brunton bryan@flesherfab.com for this)

fReg:= TRegistry.Create;
          fReg.OpenKey('\Software\ODBC\ODBC.INI\DocumentsFab', True);
          fReg.WriteString('Database', 'Documents');
          fReg.WriteString('Description', ' ');
          fReg.WriteString('Driver', 'C:\WINNT\System32\myodbc.dll');
          fReg.WriteString('Flag', '1');
          fReg.WriteString('Password', ");
          fReg.WriteString('Port', ' ');
          fReg.WriteString('Server', 'xmark');
          fReg.WriteString('User', 'winuser');
          fReg.OpenKey('\Software\ODBC\ODBC.INI\ODBC Data Sources', True);
          fReg.WriteString('DocumentsFab', 'MySQL');
          fReg.CloseKey;
          fReg.Free;

          Memo1.Lines.Add('DATABASE NAME=');
          Memo1.Lines.Add('USER NAME=');
          Memo1.Lines.Add('ODBC DSN=DocumentsFab');
          Memo1.Lines.Add('OPEN MODE=READ/WRITE');
          Memo1.Lines.Add('BATCH COUNT=200');
          Memo1.Lines.Add('LANGDRIVER=');
          Memo1.Lines.Add('MAX ROWS=-1');
          Memo1.Lines.Add('SCHEMA CACHE DIR=');
          Memo1.Lines.Add('SCHEMA CACHE SIZE=8');
          Memo1.Lines.Add('SCHEMA CACHE TIME=-1');
          Memo1.Lines.Add('SQLPASSTHRU MODE=SHARED AUTOCOMMIT');
          Memo1.Lines.Add('SQLQRYMODE=');
          Memo1.Lines.Add('ENABLE SCHEMA CACHE=FALSE');
          Memo1.Lines.Add('ENABLE BCD=FALSE');
          Memo1.Lines.Add('ROWSET SIZE=20');
          Memo1.Lines.Add('BLOBS TO CACHE=64');
          Memo1.Lines.Add('BLOB SIZE=32');

          AliasEditor.Add('DocumentsFab','MySQL',Memo1.Lines);


ㅇ C++빌더
BDE 3.0과 테스팅을 했다. 유일하게 알려진 문제는 테이블 스키마를 변경할 대로 질의 필드
가 업데이트되지 않는다. 그런데 BDE는 문제는 아닌 것 같지만 index PRIMARY 에서만 프라
이머리 키를 인식하지 못하는 것으로 보인다.
Tested with BDE 3.0. The only known problem is that when the table schema changes, q
uery fields are not updated. BDE however does not seem to recognize primary keys, onl
y the index PRIMARY, though this has not been a problem.


ㅇ 비주얼 베이직
테이블을 업데이트 가능하게 하려면 테이블의 프라이머리 키를 정의해야 한다.
(** 이 부분은 제가 출력한 ps 파일에는 없는데 웹사이트에는 있네요... **)
{{}}


15.4 ODBC 관리자 프로그렘 설정 방법

윈도우 95에서 서버의 이름을 지정하는 것에는 세가지 방법이 있다:

- 서버의 IP 주소 사용.
-'lmhosts' 파일에 다음의 정보 추가:
        ip hostname
예를 들면
        194.216.84.21 my
- DNS를 사용하여 PC를 설정

"ODBC setup" 을 채우는 예제:

Windows DSN name:   taejun
Description:        This is my love database
MySql Database:     love
Server:             194.216.84.21
User:               taejun
Password:           my_password
Port:

윈도우즈 DSN 이름 필드의 값은 윈도우즈 ODBC 셋업에서 유일한 값이다.

ODBC 셋업 화면에서 서버, 유저, 패스워드, 포드 필드에 값을 지정할 필요는 없다. 그러나
지정을 해두면 그 값이 연결을 시도할 때 기본값으로 사용된다. 그때마다 값을 바꿀 수 있
다.

포트 번호가 주어지지 않으면, 기본 포트(3306)이 사용된다.


15.5 ODBC에서 AUTO_INCREMENT 컬럼의 값 가져오기

일반적인 문제는 INSERT 에서 어떻게 자동으로 생성되는 ID 값을 가져올 수 있느냐이다. OD
BC에서 다음과 같이 할 수 있다. (auto 가 AUTO_INCREMENT 필드라고 가정):

INSERT INTO foo (auto,text) VALUES(NULL,'text');
SELECT LAST_INSERT_ID();

또는, 다른 테이블에 ID를 입력한다면 다음과 같이 할 수 있다:

INSERT INTO foo (auto,text) VALUES(NULL,'text');
INSERT INTO foo2 (id,text) VALUES(LAST_INSERT_ID(),'text');

다른 ODBC 애플리케이션(최소한 델파이와 액세스)에서 유용하게 사용하기 위해 다음의 질의
를 새롭게 입력된 열을 찾는데 사용할 수 있다:

SELECT * FROM tbl_name WHERE auto IS NULL;
17. 일반적인 문제 해결 방법

17. 1. 데이터베이스 복사(복제?)

데이터베이스를 복사하는 가장 일반적인 방법은 업데이트 로그를 이용하는 것이다. 9.2 [Th
e update log] 참고. 이 경우 마스터로 작동하는 데이터베이스 하나(데이터가 변경된 곳)와
슬레이브로 작동하는 다른 하나의 데이터베이스가 필요하다. 슬레이브를 업데이트하려면 단
지 mysql < update_log을 하면 된다. 슬레이드 데이터베이스에 맞는 호스트, 유저, 패스워
드 옵션을 지정한다. 그리고 입력값으로 마스터 데이터베이스의 업데이트 로그를 사용한다.

테이블에서 삭제한 것이 없다면, 마지막으로 복사를 한 후 (마지막으로 복사한 시간을 비
교) 테이블에서 입력되거나 변경된 열을 찾아내기 위해 TIMESTAMP 컬럼을 사용할 수 있고
미러링되는 데이터베이스에 변경된 자료만 복사를 한다.

업데이트 로그(for deletes)와 timestamps(on both sides)를 같이 사용하여 두가지 방법으
로 업데이트하는 시스템을 만들 수 있다. 그러나 이런 경우 두가지 ends(?)에서 변경된 동
일한 데이터에서 충돌을 관리할 수 있어야 한다. 아마도 어떤 것이 업데이트되었는지 결정
하기 위해 예전 버전을 유지하고 싶을 것이다.

It is possible to make a two-way updating system using both the update log (for delet
es) and timestamps (on both sides). But in that case you must be able to handle confl
icts when the same data have been changed in both ends. You probably want to keep the
old version to help with deciding what has been updated.

이 경우 복사(복제)는 SQL문으로 이루어지기 때문에, 데이터베이스를 업데이트하는 문장에
서 다음의 함수를 사용해서는 안된다; 여기에서는 원본 데이터베이스와 동일한 값을 반환하
지 않을 수 있다:

     DATABASE()
     GET_LOCK() and RELEASE_LOCK()
     RAND()
     USER(), SYSTEM_USER() or SESSION_USER()
     VERSION()

timestamp는 필요한 경우에 미러되는 곳으로 보내기지 때문에 모든 time 함수는 안전하게
사용할 수 있다. LAST_INSERT_ID() 또한 안전하게 사용할 수 있다.

All time functions are safe to use, as the timestamp is sent to the mirror if needed.
LAST_INSERT_ID() is also safe to use.


17.2 데이터베이스 백업

mysql 테이블은 파일로 저장되기 때문에 백업하기가 쉽다.  일관된 백업 작업을 위해
관련된 테이블에 LOCK TABLES를 실행하자. 7.23 [LOCK TABLES/UNLOCK TABLES synta
x]를 참고. 단지 읽기 락만이 필요하다; 데이터베이스 디렉토리의 파일 복사본을 만드
는 동안에도 다른 스레드에서는 테이블에 질의를 계속 할 수 있다. SQL 레벨의 백업을
하고자 한다면 SELECT INTO OUTFILE을 사용할 수 있다.

데이터베이스를 백업하는 다른 방법은 mysqldump 프로그램을 사용하는 것이다:

데이터베이스에 대한 풀 백업 실행:
    
shell> mysqldump --tab=/path/to/some/dir --lock-tables --opt
    
     서버에서 업데이트를 하지 않는한 간단하게 모든 테이블 파일(`*.frm', `*.ISD' , `*.
     ISM' 파일)을 복사할 수 있다. mysqld가 실행되고 있으면 멈추어야 한다. 그러고나서
     --log-update 옵션으로 다시 시작하자. ''hostname.n'의 형식을 가진 로그 파일이 생
     성될 것이다. n은 mysqladmin refresh, mysqladmin flush-logs, the FLUSH LOGS 문,
     또는 서버를 재시작할때마다 증가하는 숫자이다. 이렇게 생긴 로그 파일을 이용해 mys
     qldump를 수행하고 나서 데이터베이스에 변화된 내용을 복사(복제)하는데 필요한 정보
     를 얻을 수 있다.
    
복원하고자 한다면, 먼저 isamchk -r을 사용해 테이블을 복구하자. 모든 경우 99.9%가 제대
로 수행된다. isamchk가 실패하면 다음의 과정대로 따르자:

       
     기존의 mysqldump 백업을 복원한다.
     업데이트 로그에서 업데이트를 다시 수행하기 위해 다음의 명령을 실행한다:
    
shell> ls -1 -t -r hostname.[0-9]* | xargs cat | mysql

ls는 정확한 순서대로 로그 파일을 가져오는데 사용된다.

또한 SELECT * INTO OUTFILE 'file_name' FROM tbl_name을 이용해 선택적인 백업을 할 수
있으며 레코드가 중복되는 것을 방지하기 위해  LOAD DATA INFILE 'file_name' REPLACE ... 
을 이용해 복원할 수 있다. 이경우에는 테이블에서 PRIMARY 키나 UNIQUE 키가 필요하다. RE
PLACE 키워드는 새로운 레코드에서 unique 키 값이 같은 이전의 레코드가 중복되는 경우 이
전의 레코드를 새로운 레코드로 교체한다.


17.3 같은 머신에서 여러개의 mysqld 서버 실행하기
같은 머신에서 다중의 서버를 사용하길 원할 수 있다. 예를 들어, 이전의 서버를 그대
로 두고 새로운 mysql 릴리즈를 테스팅하는 경우가 있을 수 있다. 또는 다른 고객들
에게 독립적인 mysql 설치를 제공하길 원하는 인터넷 서비스 제공자일 수 있다.

다중 서버를 사용하길 원하면, 가장 쉬운 방법은 다른 TCP/IP 포트와 소켓 파일로 서버를
컴파일해서 서버에서 동일한 TCP/IP 포트나 소켓 파일을 청취하지 않도록 할 수 있다.

이미 사용하고 있는 서버가 기본 포트와 소켓 파일로 구성되어 있다고 가정해보자. 그러면
다음과 같은 명령으로 새로운 서버를 설정하자:

shell> ./configure  --with-tcp-port=port_number \
             --with-unix-socket=file_name \
             --prefix=/usr/local/mysql-3.22.9

여기서 port_number와 file_name은 기본 포트 숫자 및 소켓 파일의 경로와는 달라야하며, -
-prefix 값은 현재 설치되어 있는 mysql과는 다른 디렉토리를 지정해야 한다.

Here port_number and file_name should be different than the default port number and s
ocket file pathname, and the --prefix value should specify an installation directory
different than the one under which the existing MySQL installation is located.

다음의 명령으로 현재 실행되고 있는 mysql 서버의 소켓과 파일을 확인할 수 있다:

shell>; mysqladmin -h hostname --port port_number variables

사용하고 있는 포트에서 실행되고 있는 mysql 서버가 있다면, 소켓 이름을 포함해 mysql의
가장 중요한 설정 변수의 목록을 알 수 있다. 다중 mysqld 서버를 시작하고 중지시키기 위
해 시스템의 초기화 스크립트(일반적으로 ''mysql.server')를 수정해야 한다.

다른 포트와 소켓으로 서버를 시작하기 위해 새로운 mysql 서버를 재컴파일할 필요는 없다.
safe_mysqld를 시작할 때 옵션으로 포트와 소켓을 지정할 수 있다:

shell>; /path/to/safe_mysqld --socket=file-name --port=file-name

동일한 데이터베이스 디렉토리에서 로그를 기록하도록 하면서 또 다른 서버를 실행하고자
한다면, safe_mysqld 에 --log 와 --log-update 를 이용해 로그 파일의 이름을 지정해 주어
야 한다. 그렇지 않으면 두 서버가 같은 로그 파일에 기록을 하려고 할 것이다.

주의 : 일반적으로 동일한 데이터베이스의 자료를 업데이트하는 두개의 서버를 사용해서는
안된다! 운영체제에서 fault-free 시스템 로킹(locking)을 지원하지 않는다면,  좋지 않은
결과를 보게 될 것이다. (** fault-free란 정확히 무엇인지 모르겠군요. 아마도 락을 거는
데 문제가 있다면 그 락을 해제하는 것으로 보입니다. **)

두번재 서버에서 다른 데이터베이스 디렉토리를 사용하기 원하면 safe_mysqld 에 --datadir
=path 옵션을 사용할 수 있다.

다른 포트에서 실행중인 mysql 서버에 접근하길 원한다면 다음의 방법을 사용할 수 있다:

        ㅇ 클라이언트에서 다음의 옵션을 가지고 시작. --host 'hostname'  --port=port-n
umer or [--host localhost] --socket=file-name.
        ㅇ C나 펄 프로그램에서 mysql 서버에 접속할 때 포트와 소켓 인자를 준다.
        ㅇ 클라이언트를 시작하기 전에 MYSQL_UNIX_PORT 와 MYSQL_TCP_PORT 환경 변수를
설정. 일반적으로 특정한 소켓이나 포트를 사용하면,  'login' 파일에 환경 변수를 설정하
자. 12.1 [Programs] 참고.
        ㅇ 홈 디렉토리에서 '.my.cnf' 파일에 기본 소켓과 TCP/IP 소켓을 지정.  4.15.4
[Option files] 참고.





18 MySQL client tools and APIs

18.1 MySQL C API

C API 코드는 MYSQL에 함께 배포되며, 이것은 mysqlclient 라이브러리에 포함되어져 C 프
로그램으로 하여금 데이터베이스에 접근할 수 있도록 해준다. MySQL 소스 배포판의 많은
클라이언트들이 C로 코딩되어 있으며, C API를 사용하는 방법에 대한 설명을 찾고자 할 때
이 클라이언트들의 코드를 참조할 수 있다.

18.2 C API datatypes
-----------------------------------------------------------------------------------------
|데이터 형식       | 설명 |
-----------------------------------------------------------------------------------------
|  MYSQL       | 데이터 베이스와의 하나의 연결을 다루는 구조체로서 거의 모든     |
|       | MySQL 함수들에서 사용된다. |
-----------------------------------------------------------------------------------------
|  MYSQL_RES       | 행을 리턴하는 질의들(SELECT, SHOW, DESCRIBE, EXPLAIN)의 결 |
|       | 과를 표현하는 구조체로서 어떤 질의의 결과로서 리턴되는 정보들   |
|       | 을 결과 셋(result set)이라고 부른다. |
-----------------------------------------------------------------------------------------
|  MYSQL_ROW       | 한 행의 데이터를 표현하는 데이터 형식이다. 현재 이것은 counted  |
|       | byte string들의 배열로 구현되어 있다. 필드값이 이진 데이터를 포 |
|       | 함할 수 있는 경우에는 값 내부에 null 바이트를 가지고 있을 수    |
|       | 있기 때문에 이 데이터 형식을 null-terminated string으로 다룰 수 |
|       | 없다. 행들은 mysql_fetch_row() 함수를 호출함으로써 얻어올 수 있 |
|       | 다. |
-----------------------------------------------------------------------------------------
|  MYSQL_FIELD       | 필드 명, 형식, 크기와 같은 필드에 대한 정보를 포함하는 구조체 |
|       | 이다. 이 구조체의 멤버들은 아래에서 상세히 기술된다. 각각의 필  |
|       | 드에 대하여 mysql_fetch_field() 함수를 호출함으로써 각 필드에 대|
|       | 한 MYSQL_FIELD 구조체의 값을 얻을 수 있다. 필드에 들어있는      | 
|       | 실제 값은 이 구조체의 멤버가 아니며, 실제 데이터는 |
|       | MYSQL_ROW 구조체에 들어가게 된다. |
-----------------------------------------------------------------------------------------
|  MYSQL_FIELD_OFFSET | MySQL 필드 리스트에 들어가는 변위(offset)을 표현하는 type-safe  |
|       | 형식이다(mysql_field_seek() 함수에서 사용된다). Offset은 행 내에|
|       | 서의 필드의 개수이며 0에서 시작한다. |
-----------------------------------------------------------------------------------------
|  my_ulonglong       | 행들의 개수, mysql_affected_rows(), mysql_num_rows(), |
|       | mysql_insert_id() 함수에서 사용되는 형식이다. 이 형식은 0에서   |
|       | 1.84e19까지의 값을 가질 수 있다. 어떤 시스템에서는 |
|       | my_ulonglong 형식의 값을 출력하는데 문제가 있을 수 있는데, 이   |
|       | 경우에 이 값을 출력하기 위해서는 이것을 unsigned long 형식으로  |
|             | 변환한 후 '%lu' 프린트 형식을 사용한다. 예를 들면 다음과 같다:  |
|       | |
|       | printf (Number of rows: %lu\n", (unsigned long) \ |
|       | mysql_num_rows(result)); |
-----------------------------------------------------------------------------------------

MYSQL_FIELD 구조체는 다음과 같은 멤버들을 가진다:

char * name
- 필드의 이름

char * table
- 이 필드가 계산된 필드(calculated field)가 아닌 경우, 이 필드를 가지고 있는
  테이블의 이름. 계산된 필드인 경우 NULL 포인터를 가진다

char * def
- 이 필드의 디폴트 값(mysql_list_fields() 함수를 사용하는 경우에만 설정된다)

enum enum_field_types type
- 필드의 형식(type). 필드 형식은 다음 테이블 값 중에 하나이다.

-------------------------------------------------------------------------
| Type value | Type meaning |
-------------------------------------------------------------------------
| FIELD_TYPE_TINY | TINYINT field |
-------------------------------------------------------------------------
| FIELD_TYPE_SHORT | SMALLINT field |
-------------------------------------------------------------------------
| FIELD_TYPE_LONG | INTEGER field |
-------------------------------------------------------------------------
| FIELD_TYPE_INT24 | MEDIUMINT field |
-------------------------------------------------------------------------
| FIELD_TYPE_LONGLONG | BIGINT field |
-------------------------------------------------------------------------
| FIELD_TYPE_DECIMAL | DECIMAL or NUMERIC field |
-------------------------------------------------------------------------
| FIELD_TYPE_FLOAT | FLOAT field |
-------------------------------------------------------------------------
| FIELD_TYPE_DOUBLE | DOUBLE or REAL field |
-------------------------------------------------------------------------
| FIELD_TYPE_TIMESTAMP | TIMESTAMP field |
-------------------------------------------------------------------------
| FIELD_TYPE_DATE | DATE field |
-------------------------------------------------------------------------
| FIELD_TYPE_TIME | TIME field |
-------------------------------------------------------------------------
| FIELD_TYPE_DATETIME | DATETIME field |
-------------------------------------------------------------------------
| FIELD_TYPE_YEAR | YEAR field |
-------------------------------------------------------------------------
| FIELD_TYPE_STRING | String (CHAR or VARCHAR) field |
-------------------------------------------------------------------------
| FIELD_TYPE_BLOB | BLOB or TEXT field (use max_length to |
| | determine the maximum length) |
-------------------------------------------------------------------------
| FIELD_TYPE_SET | SET field |
-------------------------------------------------------------------------
| FIELD_TYPE_ENUM | ENUM field |
-------------------------------------------------------------------------
| FIELD_TYPE_NULL | NULL-type field |
-------------------------------------------------------------------------
| FIELD_TYPE_CHAR | Deprecated: use FIELD_TYPE_TINY instead   |
-------------------------------------------------------------------------

IS_NUM() 매크로(macro)를 이용하여 필드가 정수형(numeric type)인가를 테스트할 수 있다.
IS_NUM()에 형식값(type value)를 인수로 넘겼을 때 필드가 정수형이면 'TRUE'를 리턴한다:

if (IS_NUM(field->type)) {
    printf("Field is numeric\n");
}

unsigned int length
- 필드의 길이(width)

unsigned int max_length
- 결과 셋에 대한 필드의 최대 길이(maximum width). mysql_list_fields() 함수를
  사용하는 경우 이 멤버에는 필드의 최대 길이가 저장된다.

unsigned int flags
- 필드에 대한 Different bit-flags. 플래그 값은 0 혹은 다음의 비트 셋(bit set) 중에
  하나이다.

-----------------------------------------------------------------
|  플래그 값  | 의미 |
-----------------------------------------------------------------
|  NOT_NULL_FLAG | Field can't be NULL |
-----------------------------------------------------------------
|  PRI_KEY_FLAG | Field is part of a primary key |
-----------------------------------------------------------------
|  UNIQUE_KEY_FLAG | Field is part of a unique key |
-----------------------------------------------------------------
|  MULTIPLE_KEY_FLAG | Field is part of a key |
-----------------------------------------------------------------
|  UNSIGNED_FLAG | Field has the UNSIGNED attribute |
-----------------------------------------------------------------
|  ZEROFILL_FLAG | Field has the ZEROFILL attribute |
-----------------------------------------------------------------
|  BINARY_FLAG | Field has the BINARY attribute |
-----------------------------------------------------------------
|  AUTO_INCREMENT_FLAG | Field has the AUTO_INCREMENT attribute|
-----------------------------------------------------------------
|  ENUM_FLAG | Field is an ENUM (deprecated) |
-----------------------------------------------------------------
|  BLOB_FLAG | Field is a BLOB or TEXT (deprecated) |
-----------------------------------------------------------------
|  TIMESTAMP_FLAG | Field is a TIMESTAMP (deprecated) |
-----------------------------------------------------------------

BLOB_FLAG, ENUM_FLAG, TIMESTAMP_FLAG 플래그들은 이들 플래그들이 자신의 형식
(type)의 속성을 표시하기 보다는 필드의 형식을 표시하기 때문에 이 플래그들을 사용하는
것은 좋지 않다. 그 대신 'field->type' 값을 FIELD_TYPE_BLOB, FIELD_TYPE_ENUM, 혹은
FIELD_TYPE_TIMESTAMP과 비교하는 것이 좋다. 아래의 예제는 플래그 값들의 전형적인
사용법에 대하여 설명한다:

if (field->flags & NOT_NULL_FLAG) {
    printf("Field can't be null\n");
}

플래그 값의 불리언 상태(boolean status)를 결정하는데 다음과 같은 매크로(macro)들을 사용
할 수 있다:

IS_NOT_NULL(flags) 이 필드가 'NOT NULL'로 정의되어 있으면 참
IS_PRI_KEY(flags) 이 필드가 primary key인 경우에 참
IS_BLOB(flags) 이 필드가 BLOB이나 TEXT인 경우에 참

unsigned int decimals
- 정수형 필드(numeric field)인 경우 자리수(number of decimal)


18.3 C API function overview

-------------------------------------------------------------------------------------------------
| Function | Description |
-------------------------------------------------------------------------------------------------
| mysql_affected_rows() | 가장 최근의 UPDATE, DELETE, INSERT 질의에 의한 결과 |
| | 행의 개수 를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_close() | 서버와의 연결을 종료한다. |
-------------------------------------------------------------------------------------------------
| mysql_connect() | 서버와 연결하며 이 함수를 사용하기보다는 |
-------------------------------------------------------------------------------------------------
| |  mysql_real_connect() 함수를 사용하는 것이 권장된다. |
| mysql_create_db() | DB를 생성한다. 이 함수를 사용하기보다는 SQL 명령어인 |
| | CREATE DATABASE 문을 사용하는 것이 좋다. |
-------------------------------------------------------------------------------------------------
| mysql_data_seek() | 질의 결과 셋에서 임의의 행을 찾는다. |
-------------------------------------------------------------------------------------------------
| mysql_debug() | 주어진 문자열로 DBUG_PUSH를 수행한다. |
-------------------------------------------------------------------------------------------------
| mysql_drop_db() | DB를 드롭한다. 이 함수를 사용하기보다는 SQL 명령어인 DROP |
| | DATABASE 문을 사용하는 것이 좋다. |
-------------------------------------------------------------------------------------------------
| mysql_dump_debug_info() | 서버로 하여금 디버깅 정보를 로그에 남기도록 한다. |
-------------------------------------------------------------------------------------------------
| mysql_eof() | 결과셋의 마지막 행이 읽혀졌는가를 표시한다. 이 함수를 |
| | 사용하기보다는 mysql_errno()나 mysql_error()함수가 사용된다. |
-------------------------------------------------------------------------------------------------
| mysql_errno() | 가장 최근에 수행된 MySQL 함수에 대한 에러넘버를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_error() | 가장 최근에 수행된 MySQL 함수에 대한 에러메세지를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_escape_string() | SQL 문 내부의 특수 문자를 처리한다. |
-------------------------------------------------------------------------------------------------
| mysql_fetch_field() | 테이블의 다음 필드의 형식(type)을 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_fetch_field_direct() | 주어진 필드 번호에 대한 필드 형식을 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_fetch_fields() | 모든 필드 구조에 대한 배열을 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_fetch_lengths() | 현재 행의 모든 컬럼들의 길이를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_fetch_row() | 결과셋으로부터 다음 행을 가져온다. |
-------------------------------------------------------------------------------------------------
| mysql_field_seek() | 컬럼커서를 특정 컬럼으로 놓는다. |
-------------------------------------------------------------------------------------------------
| mysql_free_result() | 결과셋에 의해 사용된 메모리를 해제한다. |
-------------------------------------------------------------------------------------------------
| mysql_get_client_info() | 클라이언트의 버전 정보를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_get_host_info() | 현재 연결에 대한 정보를 가진 문자열을 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_get_proto_info() | 연결에 사용된 프로토콜 버전을 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_get_server_info() | 서버의 버전 넘버를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_info() | 가장 최근에 수행된 질의에 대한 정보를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_init() | MYSQL 구조체를 생성하거나 초기화한다. |
-------------------------------------------------------------------------------------------------
| mysql_insert_id() | AUTO_INCREMENT 필드에 대하여 가장 최근에 생성된 ID를 리턴. |
-------------------------------------------------------------------------------------------------
| mysql_list_dbs() | 간단한 정규식에 의해 매칭되는 DB 이름들을 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_list_fields() | 간단한 정규식에 의해 매칭되는 필드 이름들을 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_list_processes() | 현재 서버의 쓰레드들의 리스트를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_list_tables() | 간단한 정규식에 의해 매칭되는 테이블 이름들을 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_num_fields() | 결과셋 내의 컬럼 개수를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_num_rows() | 결과셋 내의 행 개수를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_ping() | 서버와의 연결이 제대로 수행되고 있나를 체크하며, 필요하면 |
| | 서버와 재연결한다. |
-------------------------------------------------------------------------------------------------
| mysql_query() | Null terminated 문자열로 주어지는 SQL 질의를 수행한다. |
-------------------------------------------------------------------------------------------------
| mysql_real_connect() | MySQL 서버와 연결한다. |
-------------------------------------------------------------------------------------------------
| mysql_real_query() | Counted 문자열로 주어진 SQL 질의를 수행한다. |
-------------------------------------------------------------------------------------------------
| mysql_reload() | 서버로 하여금 grant 테이블을 다시 리로드하게 한다. |
-------------------------------------------------------------------------------------------------
| mysql_row_seek() | mysql_row_tell() 함수가 리턴한 값을 가지고 결과셋 내의 한 |
| | 행을 찾는다. |
-------------------------------------------------------------------------------------------------
| mysql_row_tell() | Row cursor 위치를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_select_db() | 특정 DB에 연결한다. |
-------------------------------------------------------------------------------------------------
| mysql_shutdown() | DB 서버를 셧다운시킨다. |
-------------------------------------------------------------------------------------------------
| mysql_stat() | 서버의 상태를 표시하는 문자열을 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_store_result() | 전체 결과셋을 클라이언트로 가져온다. |
-------------------------------------------------------------------------------------------------
| mysql_thread_id() | 현재 쓰레드의 ID를 리턴한다. |
-------------------------------------------------------------------------------------------------
| mysql_use_result() | 행단위로 데이터를 넘겨받기 위한 결과 셋을 초기화한다. |
-------------------------------------------------------------------------------------------------

서버에 연결할 때는 먼저 mysql_init() 함수를 호출함으로써 connection handler를 초기화한 후,
그 handler와 hostname, user name, password와 같은 다른 정보들을 가지고 mysql_real_connect()
함수를 호출한다. 모든 작업이 끝나면 mysql_close() 함수를 호출함으로써 연결을 종료한다.

연결이 이루어져 있는 동안 클라이언트는 mysql_query()나 mysql_real_query() 함수를 이용하
여 서버에 SQL 질의를 보낼 수 있다. mysql_query()는 질의가 null-terminated string이어야 하
며, mysql_real_query()는 counted string이어야 한다. 만약 string이 바이너리 데이터(중간에
NULL 바이트를 가질 수도 있는)인 경우에는 반드시 mysql_real_query()를 사용하여야 한다.

SELECT 질의가 아닌 질의(INSERT, UPDATE, DELETE)에 대해서는 mysql_affected_row() 함수
를 호출함으로써 얼마나 많은 행이 affect되어졌나를 알 수 있다.

SELECT 질의의 경우, select되어진 행들을 결과셋(result set)으로 얻게 된다(SHOW, DESCRIBE,
EXPLAIN과 같은 문은 행을 리턴하는 것에 있어서 SELECT와 비슷하며, 이들은 SELECT
문과 동일하게 처리되어야 한다)

클라이언트가 결과셋을 처리하는 방법에는 두가지가 있다. 하나는 mysql_store_result() 함수
를 호출함으로써 전체 결과 셋을 한번에 얻어오는 것으로써, 이 함수는 질의에 의해 리턴된
모든 행들을 서버로부터 얻어내어 클라이언트에 저장한다. 두번째는 mysql_use_result() 함수
를 호출함으로써 클라이언트가 행별로 결과셋을 얻어오는 것이다. 이 함수는 행을 얻어오기 위한
작업을 초기화하기만 하며 실제로 서버로부터 행을 가져오지는 않는다.

위 두가지 경우 모두, mysql_fetch_row() 함수를 호출함으로써 행들에 접근하게 된다.
mysql_store_result()의 경우 mysql_fetch_row()는 서버로부터 이미 fetch된 행들에 접근하며,
mysql_use_result()의 경우, mysql_fetch_row()는 실제로 서버로부터 행들을 얻어온다. 각 행의
데이터 값의 크기에 대한 정보는 mysql_fetch_lengths() 함수를 호출함으로써 얻을 수 있다.

결과 셋을 이용한 작업이 끝나면 mysql_free_result()를 호출함으로써 결과 셋을 위해 사용된
메모리를 해제한다.

이 두가지 retrieval 체계는 상호 보완적이다. 클라이언트 프로그램은 각각의 요구에 따라 적
절한 접근 방법을 선택하여야 한다. 실질적으로는 mysql_store_result()가 주로 사용된다.

mysql_store_result()의 이점은 행들이 클라이언트로 fetch된 이후에는 클라이언트가 행들에
순차적으로 접근할 수 있을 뿐만 아니라 mysql_data_seek()나 mysql_row_seek()를 이용하여 결
과 셋 안에서 현행 위치(current row position)를 변경함으로써 결과 셋 내에서 앞뒤로 움직일
수 있다는 것이다. 또한 mysql_num_rows()를 호출함으로써 얼마나 많은 행들이 있나를 알
수도 있다. 다른 한편으로, mysql_store_result()를 위해 요구되는 메모리가 많기 때문에 out-of-
memory 상태를 만날 가능성이 많다.

mysql_use_result()의 이점은 한번에 하나의 행만을 관리하기 때문에 결과 셋을 위한 메모리
가 적게 필요하다는 것이며 또한 더 적은 메모리 할당 부하로 인하여 더 속도가 빠를 수 있
다. 단점은 서버를 tying up하는 것을 피하기 위하여 각 행을 빠르게 처리해주어야 하며 결
과 셋 내에서 랜덤하게 행들에 접근할 수 없고 단지 순차적으로만 접근할 수 있다. 또한 모
든 행들을 받기 전에는 결과 셋 내에 얼마나 많은 행들이 있나를 알 수가 없다. 결과 셋 중
간에 찾고자 하는 정보를 찾았더라도 모든 행을 끝까지 받고 있어야 한다.

API를 이용함으로써 질의가 SELECT인가 아닌가를 모른 상태에서도 클라이언트가 질의에
적절히 대응할 수 있게 할 수 있는데, 이는 mysql_query() (혹은 mysql_real_query())를 호출한
후에 mysql_store_result()를 호출함으로써 가능하다. 만약 결과 셋이 성공(succeed)이면 질의가
SELECT이며 행들을 읽을 수 있다. 만약 실패이면 mysql_num_fields()를 호출하여 result가
정말로 기대되어지는가를 결정할 수 있으며, mysql_num_fields()가 0을 리턴하면 질의는 아무
데이터도 리턴하지 않는다(이것은 질의가 INSERT, UPDATE, DELETE 등임을 의미한다). 만약
mysql_num_fields()가 0이 아니면 질의는 반드시 행을 리턴하여야 하는데 리턴하지 않았음을
의미하며, 이는 질의가 SELECT였으며 수행에 실패하였음을 알려준다.

mysql_store_result()과 mysql_use_result()은 결과 셋을 구성하는 필드들에 대한 정보(필드의 개
수, 이름, 형식 등)를 얻을 수 있게 해준다. mysql_fetch_field()를 각각 호출하여 행 내에서의
필드 정보를 순차적으로 접근하거나, mysql_fetch_field_direct()를 호출함으로써 얻은 행 내의
필드 번호를 통하여 접근할 수 있다. 현쟁의 필드 커서 위치(current field cursor position)는
mysql_field_seek() 함수를 호출함으로써 변경될 수 있다. Setting the field cursor affects subsequent
calls to mysql_fetch_field(). 또한 mysql_fetch_fields()를 호출함으로써 한꺼번에 모든 필드 정보
를 얻을 수도 있다.

에러를 찾고 리포팅하기 위해서 MySQL은 mysql_errno()와 mysql_error() 함수를 통하여 에러
정보에 접근할 수 있게 해준다. 이 두 함수는 가장 최근에 invoke된 성공하거나 실패할 수
있는 함수에 대한 에러 코드 혹은 에러 메시지를 리턴하며, 이를 통하여 언제 에러가 발생
하고 그것이 무엇인가를 알 수 있다.


18.4 C API function descriptions

아래의 함수 설명에 있어서 NULL로 표시한 인수나 리턴값은 C 프로그래밍 언어에서의
NULL의 의미이며, MySQL에서의 NULL 값을 의미하는 것이 아니다.

어떤 값을 리턴하는 함수들은 보통 포인터나 정수를 리턴한다. 만약 별 다른 표시가 없다면
포인터를 리턴하는 함수들은 성공하였을 때 NULL이 아닌 값을, 에러를 표시하기 위해서는
NULL 값을 리턴한다. 정수를 리턴하는 함수의 경우에는 성공하였을 경우 0을, 에러인 경우
에는 0이 아닌 값을 리턴한다.

함수가 에러를 리턴한 경우에는 mysql_errno(), mysql_error() 함수를 이용하여 에러를 체크할
수 있다.

18.4.1 mysql_affected_rows()
my_ulonglong mysql_affected_rows(MYSQL *mysql)

18.4.1.1 Description
마지막으로 수행된 UPDATE, DELETE, INSERT 질의에 의한 행의 수를 리턴하며, 주로
UPDATE, DELETE, INSERT 문에 대한 mysql_query() 함수가 호출된 후 즉시 호출된다.
SELECT 문의 경우 mysql_affected_rows() 함수는 mysql_num_row() 함수처럼 동작한다.

mysql_affected_rows() 함수는 현재 매크로(macro)로 구현되어 있다.

18.4.1.2 Return values
0보다 큰 정수는 affected되거나 retrieved된 행의 수를 의미한다. 0은 WHERE 조건에 합당
하는 레코드가 없거나 아무 질의도 아직 수행되지 않았음을 의미한다. -1은 질의가 에러를
리턴하였거나 혹은 SELECT 질의인 경우 mysql_affected_rows() 함수가 mysql_store_result() 함
수의 호출 이전에 호출되었음을 의미한다.

18.4.1.3 Errors
없음.

18.4.1.4 Example
mysql_query(&mysql,"UPDATE products SET cost=cost*1.25 WHERE group=10");
printf("%d products updated",mysql_affected_rows(&mysql));


18.4.2 mysql_close()
void mysql_close(MYSQL *mysql)

18.4.2.1 Description
현재 열려있는 서버와의 연결을 끊는다. 만약 연결 핸들(connection handle)이 mysql_init()이나
mysql_real_connect()에 의해서 자동으로 할당되었다면 mysql_close()는 mysql에 의해 지정된
연결 핸들을 deallocation한다.

18.4.2.2 Return values
없음.

18.4.2.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.3 mysql_connect()
MYSQL *mysql_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd)

18.4.3.1 Description
이 함수를 사용하는 것은 권장되지 않으며, 대신 mysql_real_connect() 함수가 주로 사용된다.

mysql_connect() 함수는 호스트에서 돌아가고 있는 MySQL DB 엔진과의 연결을 시도한다.
Mysql_connect()는 mysql_get_client_info() 함수를 제외한 다른 모든 API 함수를 수행하기 이
전에 반드시 성공적으로 수행되어져야 한다.

함수의 인수들이 가지는 의미는 mysql_real_connect() 함수와 동일하다.

18.4.3.2 Return values
mysql_real_connect() 함수와 동일.

18.4.3.3 Errors
mysql_real_connect() 함수와 동일.


18.4.4 mysql_create_db()
int mysql_create_db(MYSQL *mysql, const char *db)

18.4.4.1 Description
'db' 인수로 주어진 이름의 데이터베이스를 생성한다.

이 함수를 사용하는 것은 권장되지 않으며, 대신 'CREATE DATABASE'와 같은 SQL 문을
mysql_query() 함수를 이용하여 수행하는 것이 좋다.

18.4.4.2 Return values
데이터베이스가 성공적으로 생성된 경우 0, 에러가 발생한 경우 0이 아닌 값을 리턴한다.

18.4.4.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.4.4 Example
if(mysql_create_db(&mysql, "my_database"))
{
   fprintf(stderr, "Failed to create new database.  Error: %s\n", mysql_error(&mysql));
}


18.4.5 mysql_data_seek()
void mysql_data_seek(MYSQL_RES *result, unsigned int offset)

18.4.5.1 Description
결과 셋 내에서 임의의 행을 찾는다. 이를 위해서는 결과 셋 구조체가 질의에 의한 전체 결
과를 포함하고 있어야 하며, 따라서 mysql_data_seek() 함수는 mysql_use_result() 함수와는 사
용할 수 없고 mysql_store_result() 함수와 사용한다.

Offset 인수는 0에서 mysql_num_rows(result)-1 사이의 값을 가져야 한다.

18.4.5.2 Return values
None.

18.4.5.3 Errors
None.


18.4.6 mysql_debug()
void mysql_debug(char *debug)

18.4.6.1 Description
debug 인수로 주어진 문자열을 가지고 DBUG_PUSH를 수행한다. mysql_debug() 함수는 Fred
Fish debug 라이브러리를 사용한다. 이 함수를 사용하기 위해서는 반드시 클라이언트 라이브
러리를 디버깅을 지원하도록 컴파일하여야 한다. 19.10 절에서 MySQL 서버의 디버깅에 대하
여 논의하며, 19.11절에서 MySQL 클라이언트의 디버깅에 대하여 논의한다.

18.4.6.2 Return values
None.

18.4.6.3 Errors
None.

18.4.6.4 Example
아래와 같은 호출은 클라이언트 라이브러리로 하여금 클라이언트의에 '/tmp/client.trace' 파일
에 trace file을 생성하게 해준다:

mysql_debug("d:t:O,/tmp/client.trace");


18.4.7 mysql_drop_db()
int mysql_drop_db(MYSQL *mysql, const char *db)

18.4.7.1 Description
db 인수로 주어진 이름의 데이터베이스를 드롭시킨다.

이 함수를 사용하는 것은 권장되지 않으며, 대신 'DROP DATABASE'와 같은 SQL 문을
mysql_query() 함수를 이용하여 수행하는 것이 좋다.

18.4.7.2 Return values
데이터베이스가 성공적으로 드롭된 경우 0을, 에러가 발생한 경우 0이 아닌 값을 리턴한다.

18.4.7.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.7.4 Example
if(mysql_drop_db(&mysql, "my_database"))
  fprintf(stderr, "Failed to drop the database: Error: %s\n", mysql_error(&mysql));


18.4.8 mysql_dump_debug_info()
int mysql_dump_debug_info(MYSQL *mysql)

18.4.8.1 Description
서버로 하여금 로그에 디버깅 정보를 기록하게 한다. 연결되어 있는 사용자(connected user)는
이 작업을 위한 권한을 가지고 있어야 한다.

18.4.8.2 Return values
명령이 성공하면 0, 에러가 발생하면 0이 아닌 값을 리턴한다.

18.4.8.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.9 mysql_eof()
my_bool mysql_eof(MYSQL_RES *result)

18.4.9.1 Description
이 함수를 사용하는 것은 권장되지 않으며, 대신 mysql_errno() 함수나 mysql_error() 함수를
사용한다.

mysql_eof() 함수는 결과 셋의 마지막 행이 읽혀졌나를 판단한다.

mysql_store_result() 함수로부터 성공적으로 결과 셋을 얻었다면 클라이언트는 한번의 작업으
로 전체 셋을 얻게된다. 이 경우 mysql_fetch_row() 함수로부터 리턴되는 NULL은 항상 결과
셋의 마지막에 도달하였음을 의미하게 되며 mysql_eof() 함수를 호출할 필요가 없다.

한편으로 mysql_use_result() 함수를 사용하여 결과 셋을 얻었다면 셋의 행들은
mysql_fetch_row() 함수를 호출할 때마다 서버로부터 하나씩 얻어진다. 이 과정 중에 연결 에
러가 발생할 수 있기 때문에 myql_fetch_row() 함수가 리턴하는 NULL 값은 반드시 결과 셋
의 마지막에 도달하였음을 의미한다고 볼 수 없다. 이 경우 mysql_eof() 함수를 사용하여 무
엇이 일어났나를 결정할 수 있다. 이때 mysql_eof() 함수는 결과 셋의 마지막에 도달한 경우
라면 0이 아닌 값을 리턴하며, 에러인 경우에는 0을 리턴하게 된다.

표준 mySQL 에러 함수인 mysql_errno()와 mysql_error() 함수가 더 많은 정보를 제공하기 때
문에 이들을 사용하는 것이 좋다…


18.4.9.2 Return values
에러이면 0을, 결과 셋의 마지막에 도달하였으면 0이 아닌 값을 리턴한다.

18.4.9.3 Errors
None.

18.4.9.4 Example
다음은 mysql_eof()의 사용예를 보여준다:

mysql_query(&mysql,"SELECT * FROM some_table");
result = mysql_use_result(&mysql);
while((row = mysql_fetch_row(result)))
{
    // do something with data
}
if(!mysql_eof(result))  // mysql_fetch_row() failed due to an error
{
    fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}

However, you can achieve the same effect with the standard MySQL error functions:

mysql_query(&mysql,"SELECT * FROM some_table");
result = mysql_use_result(&mysql);
while((row = mysql_fetch_row(result)))
{
    // do something with data
}
if(mysql_errno(&mysql))  // mysql_fetch_row() failed due to an error
{
    fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}


18.4.10 mysql_errno()
unsigned int mysql_errno(MYSQL *mysql)

18.4.10.1 Description
mysql 인수에 의해 지정된 연결에 대하여 mysql_errno()는 가장 최근에 수행된 API 함수에
대한 에러 코드를 리턴한다. 0을 리턴하면 아무 에러도 발생하지 않음을 의미한다. 클라이언
트 에러 메시지 넘버들은 MYSQL의 'errmsg.h' 헤더 파일에 나열되어 있으며, 서버 에러 메
시지 넘버들은 'mysqld_error.h' 파일에 있다.

18.4.10.2 Return values:
에러 코드 값을 리턴. 아무 에러도 없으면 0을 리턴한다.

18.4.10.3 Errors
None.


18.4.11 mysql_error()
char *mysql_error(MYSQL *mysql)

18.4.11.1 Description
mysql 인수에 의해 지정된 연결에 대하여 mysql_error()는 가장 최근에 수행된 API 함수에
대한 에러 메시지를 리턴한다. 비어 있는 문자열("")을 리턴하면 아무 에러도 발생하지 않음
을 의미한다. 이것은 다음의 두가지 테스트가 동일함을 의미한다:

if(mysql_errno(&mysql)) {
    // an error occurred
}

if(mysql_error(&mysql)[0] != '\0') {
    // an error occurred
}

클라이언트 에러 메시지의 언어는 MySQL 클라이언트 라이브러리를 재컴파일함으로써 변경
될 수 있다. 현재 몇가지 다른 언어로 된 에러 메시지를 선택할 수 있으며, 9.1 절에서는
MySQL에 의해 지원되는 언어들을 다룬다.

18.4.11.2 Return values
에러를 기술하는 문자열. 에러가 없는 경우에는 비어있는 문자열을 리턴한다.

18.4.11.3 Errors
None.


18.4.12 mysql_escape_string()
unsigned int mysql_escape_string(char *to, const char *from, unsigned int length)

18.4.12.1 Description
from 인수에 있는 문자열을 서버로 넘겨질 escaped SQL 문으로 인코딩하여 to 인수에 넣는다.
인코딩되어지는 문자들은 NUL(ASCII 0), `\n', `\r', `\', `'' 등이다(7.1절에서는 문자열과 숫자들을
사용하는 방법에 대하여 다룬다).

from 인수인 문자열은 length 인수에서 주어진 바이트 수만큼의 길이(terminating NULL을 제
외한)를 가져야 한다. to 인수는 반드시 적어도 length 인수에서 주어진 길이의 2배에 1을
더한 만큼의 바이트가 할당되어야 한다. mysql_escape_string() 함수가 리턴할 때 to 인수의 내
용은 null-terminated 문자열이 된다. 리턴값은 인코딩되어진 문자열의 길이이며, 이것은
terminating null 문자가 포함되지 않은 길이다.

to 인수에 들어가는 문자열은 null-terminated 문자열이지만 이 문자열을 strlen() 함수나
strcpy() 함수에서 사용하여서는 안된다. 만약 from 인수로 주어진 문자열이 null을 포함하고
있다면 mysql_escape_string() 함수는 이 문자를 '\'을 앞에 붙여서 to 인수에 넣게 되며, 이것
을 위의 함수들은 여전히 terminating null로 인식하게 된다.

또한 이러한 내부적인 null 바이트는 mysql_query()에 의해서 terminating null로 여겨지기 때
문에 질의를 적절하게 수행할 수 없게 된다. 따라서 mysql_escape_query() 함수를 이용하여
질의를 만든 경우 mysql_query() 함수를 사용하기 보다는 mysql_real_query() 함수를 사용하는
것이 좋다.

18.4.12.2 Example
char query[1000],*end;

end = strmov(query, "INSERT INTO test_table values(");
*end++ = '\";
end += mysql_escape_string(query,"What's this",11);
*end++ = '\";
*end++ = ',';
*end++ = '\";
end += mysql_escape_string(query,"binary data: \0\r\n",16);
*end++ = '\";
*end++ = ')';

if (mysql_real_query(&mysql,query,(int) (end - query))) {
   fprintf(stderr, "Failed to insert row, Error: %s\n",
           mysql_error(&mysql));
}

예제에서 사용된 strmov() 함수는 mysqlclient 라이브러리에 포함되어 있으며, strcpy() 함수와
비슷하게 동작을 하지만 첫번째 인수의 terminating null에 대한 포인터를 리턴하는 것이 다
르다.

18.4.12.3 Return values
terminating null 문자를 제외한 to 인수에 들어간 값의 길이

18.4.12.4 Errors
None.


18.4.13 mysql_fetch_field()
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result)

18.4.13.1 Description
결과 셋의 한 컬럼의 정의(definition)를 MYSQL_FIELD 구조체에 담아서 리턴한다. 결과 셋
의 모든 컬럼들에 대한 정보를 얻고자 하는 경우에는 이 함수를 반복하여 호출한다. 더 이
상의 필드가 남아있지 않으면 mysql_fetch_field() 함수는 NULL을 리턴한다.

새로운 SELECT 질의가 수행될 때마다 mysql_fetch_field()는 새로운 첫번째 필드에 대한 정
보를 리턴하기 위해 초기화된다. mysql_fetch_field() 함수에 의해 리턴되는 필드는 또한
mysql_field_seek() 함수의 호출에 의해 영향을 받는다.

SELECT 문을 수행하기 위해 msyql_query()를 호출하였지만 mysql_store_result()를 호출하지
않은 경우, mysql_fetch_field()를 호출하여 BLOB 필드의 길이를 요청하게 되면 MySQL은 디
폴트 blob 길이인 8K 바이트를 리턴한다. 일단 한번 결과를 얻어오게 되면 field->max_length
는 주어진 질의에 있는 컬럼의 가장 큰 값의 길이를 가지게 된다.

18.4.13.2 Return values
현재 컬럼에 대한 MYSQL_FIELD 구조체를 리턴하며, 더 이상 남은 필드가 없으면 NULL을
리턴한다.

18.4.13.3 Errors
None.

18.4.13.4 Example
MYSQL_FIELD *field;

while((field = mysql_fetch_field(result)))
{
    printf("field name %s\n", field->name);
}


18.4.14 mysql_fetch_fields()
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *result)

18.4.14.1 Description
결과 셋에 대한 모든 MYSQL_FIELD 구조체의 배열을 리턴한다. 각각의 구조체는 결과 셋
내의 각 컬럼들에 대한 필드 정의에 대한 정보를 가지고 있다.

18.4.14.2 Return values
결과 셋의 모든 컬럼들에 대한 MYSQL_FIELD 구조체의 배열

18.4.14.3 Errors
None.

18.4.14.4 Example
unsigned int num_fields;
unsigned int i;
MYSQL_FIELD *fields;

num_fields = mysql_num_fields(result);
fields = mysql_fetch_fields(result);
for(i = 0; i < num_fields; i++) {
   printf("Field %u is %s\n", i, fields[i].name);
}


18.4.15 mysql_fetch_field_direct()
MYSQL_FIELD *mysql_fetch_field_direct(MYSQL_RES *result, unsigned int fieldnr)

18.4.15.1 Description
결과 셋 내에서 fieldnr 인수로 주어진 필드 번호의 필드에 대한 필드 정의를 MYSQL_FIELD
구조체로 리턴한다. 이 함수를 이용하여 임의의 컬럼에 대한 정의를 얻어올 수 있다. fieldnr
값은 0에서 mysql_num_fields(result)-1 사이의 값을 가져야 한다.

18.4.15.2 Return values
지정된 필드에 대한 MYSQL_FIELD 구조체

18.4.15.3 Errors
None.

18.4.15.4 Example
unsigned int num_fields;
unsigned int i;
MYSQL_FIELD *field;

num_fields = mysql_num_fields(result);
for(i = 0; i < num_fields; i++) {
    field = mysql_fetch_field_direct(result, i);
    printf("Field %u is %s\n", i, field->name);
}


18.4.16 mysql_fetch_lengths()
unsigned long *mysql_fetch_lengths(MYSQL_RES *result)

18.4.16.1 Description
결과 셋 내의 현재 행의 컬럼들의 길이를 리턴한다. 만약 필드 값들을 복사하고자 하는 경
우 이 길이 정보는 최적화(optimization)을 위해 유용하게 사용할 수 있다. strlen() 함수를 호
출할 필요가 없기 때문이다. 특히 결과 셋이 이진 데이터를 포함하고 있는 경우에는 strlen()
함수가 NULL 문자를 포함하고 있는 필드에 대하여 잘못된 결과를 리턴할 수 있기 때문에
데이터의 크기를 결정하는데 이 함수를 사용하여야 한다.

빈 컬럼이나 NULL 값을 가지는 컬럼에 대한 길이는 0이 된다. 이 두가지 경우를 구분하는
방법은 mysql_fetch_row() 함수에 대한 설명을 참조한다.

18.4.16.2 Return values
각 컬럼(null termination character를 제외한)의 크기를 가지고 있는 unsigned long 정수형의 배
열. 에러가 발생한 경우에는 NULL을 리턴한다.

18.4.16.3 Errors
mysql_fetch_lengths()는 결과 셋의 현재 행에 대해서만 유효하다. mysql_fetch_row() 함수를 호
출하기 전에 이 함수를 호출하거나 결과 셋 내의 모든 행들을 얻어온 후에 호출하는 경우에
NULL을 리턴한다.

18.4.16.4 Example
MYSQL_ROW row;
unsigned long *lengths;
unsigned int num_fields;
unsigned int i;

row = mysql_fetch_row(result);
if (row) {
    num_fields = mysql_num_fields(result);
    lengths = mysql_fetch_lengths(result);
    for(i = 0; i < num_fields; i++) {
         printf("Column %u is %lu bytes in length.\n", i, lengths[i]);
    }
}


18.4.17 mysql_fetch_row()
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)

18.4.17.1 Description
결과 셋의 다음 행을 얻어온다. mysql_store_result() 함수를 호출한 후에 사용한 경우, 더 이
상 얻어올 행들이 없으면 mysql_fetch_row()는 NULL을 리턴한다. mysql_use_result() 함수를
호출한 후에 사용한 경우, 더 이상 얻어올 행들이 없거나 에러가 발생하였을 때 NULL을
리턴한다.

행 내부의 값들의 개수는 mysql_num_fields(result)에 의해 주어진다. mysql_fetch_row()의 호출
에 의하여 행이 얻어졌을 때 행 내부의 값들은 row[0]부터 row[mysql_num_fields(result)-1]까
지의 값들로 참조될 수 있다. 행 내부에 NULL 값이 있는 경우에는 NULL 포인터로 표시된다.

행 내부의 필드 값들의 길이들은 mysql_fetch_lengths() 함수를 호출함으로써 얻어질 수 잇다.
비어있는 필드나 NULL을 포함하는 필드들은 모두 0의 길이를 가지며, 필드 값에 대한 포
인터를 체크함으로써 이 두가지 경우를 구분할 수 있다. 만약 포인터가 NULL이면 해당 필
드가 NULL인 경우이며, 아니면 비어있는 필드임을 의미한다.

18.4.17.2 Return values
다음 행에 대한 MYSQL_ROW 구조체. 더 이상 얻어올 행이 없거나 에러가 발생한 경우
NULL을 리턴한다.

18.4.17.3 Errors
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.17.4 Example
MYSQL_ROW row;
unsigned int num_fields;
unsigned int i;

num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result))) {
   unsigned long *lengths;
   lengths = mysql_fetch_lengths(result);
   for(i = 0; i < num_fields; i++) {
       printf("[%.*s] ", (int) lengths[i], row[i] ? row[i] : "NULL");
   }
   printf("\n");
}


18.4.18 mysql_field_seek()
MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET offset)

18.4.18.1 Description
필드 커서를 offset 인수로 주어진 위치로 설정한다. 다음 mysql_fetch_field() 함수의 호출은
해당 offset로 이동한 만큼에 해당되는 컬럼에 대한 필드 정보를 얻어오게 된다.

행의 첫부분으로 보내기 위해서는 offset을 0으로 주고 함수를 호출하면 된다.

18.4.18.2 Return values
필드 커서의 이전 값(previous value)

18.4.18.3 Errors
None.


18.4.19 mysql_field_tell()
MYSQL_FIELD_OFFSET mysql_field_tell(MYSQL_RES *result)

18.4.19.1 Description
마지막 mysql_fetch_field() 함수에 의해 사용된 필드 커서의 위치를 알려준다. 이 리턴값을
mysql_field_seek() 함수의 offset 인수로 사용할 수 있다.

18.4.19.2 Return values
필드 커서의 현재 변위(current offset)

18.4.19.3 Errors
None.


18.4.20 mysql_free_result()
void mysql_free_result(MYSQL_RES *result)

18.4.20.1 Description
mysql_store_result(), mysql_use_result(), mysql_list_dbs() 등에 의한 결과셋을 위해 할당되었던
메모리를 해제한다. 결과 셋을 가지고 작업을 끝내고 나면 반드시 mysql_free_result() 함수를
호출하여 메모리를 해제해 주어야 한다.

18.4.20.2 Return values
None.

18.4.20.3 Errors
None.


18.4.21 mysql_get_client_info()
char *mysql_get_client_info(void)

18.4.21.1 Description
클라이언트 라이브러리 버전(client library version)에 대한 정보를 표시하는 문자열을 리턴한다.

18.4.21.2 Return values
MySQL client library version을 표시하는 문자열

18.4.21.3 Errors
None.


18.4.22 mysql_get_host_info()
char *mysql_get_host_info(MYSQL *mysql)

18.4.22.1 Description
서버 호스트 네임을 포함하는 현재 사용되고 있는 연결(connection)의 형식(type)을 기술하는
문자열을 리턴한다.

18.4.22.2 Return values
서버 호스트 네임(server host name)과 연결 형식(connection type)을 나타내는 문자열

18.4.22.3 Errors
None.


18.4.23 mysql_get_proto_info()
unsigned int mysql_get_proto_info(MYSQL *mysql)

18.4.23.1 Description
현재 연결에 사용되고 있는 프로토콜의 버전을 리턴한다.

18.4.23.2 Return values
현재 연결에 사용되고 있는 프로토콜 버전을 나타내는 unsigned integer

18.4.23.3 Errors
None.


18.4.24 mysql_get_server_info()
char *mysql_get_server_info(MYSQL *mysql)

18.4.24.1 Description
서버 버전 넘버(server version number)를 표시하는 문자열을 리턴한다.

18.4.24.2 Return values
서버 버전 넘버를 표시하는 문자열

18.4.24.3 Errors
None.


18.4.25 mysql_info()
char *mysql_info(MYSQL *mysql)

18.4.25.1 Description
가장 최근에 수행된 질읭 peogks 정보를 제공하는 문자열을 리턴한다. 문자열의 형식(format)
은 질의의 타입에 따라 달라지며, 아래와 같다. 문자열은 질의에 대한 적절한 값들을 포함하
게 된다.

INSERT INTO ... SELECT ...
String format: Records: 100 Duplicates: 0 Warnings: 0
INSERT INTO ... VALUES (...),(...),(...)...
String format: Records: 3 Duplicates: 0 Warnings: 0
LOAD DATA INFILE ...
String format: Records: 1 Deleted: 0 Skipped: 0 Warnings: 0
ALTER TABLE
String format: Records: 3 Duplicates: 0 Warnings: 0

18.4.25.2 Return values
가장 최근에 수행된 질의에 대한 부가적인 정보를 표현하는 문장열. 해당 질의에 대하여 아
무런 정보도 얻을 수 없는 경우 NULL을 리턴한다.

18.4.25.3 Errors
None.


18.4.26 mysql_init()
MYSQL *mysql_init(MYSQL *mysql)

18.4.26.1 Description
mysql_real_connect() 함수를 위한 MYSQL 객체를 할당(allocate)하거나 초기화(initialize)한다.
만약 mysql 인수가 NULL 포인터이면 mysql_init() 함수는 새로운 객체를 할당, 초기화하여
그 객체를 리턴한다. NULL 포인터가 아니면 객체는 초기화된 후 객체의 주소가 리턴된다.
만약 mysql_init() 함수가 새로운 객체를 할당하게 되면 그 객체는 연결을 닫기 위해
mysql_close() 함수가 호출될 때 해제되게 된다.

18.4.26.2 Return values
초기화된 MYSQL * handle. 새로운 객체를 할당하기 위한 메모리가 부족한 경우 NULL을 리
턴한다.

18.4.26.3 Errors
메모리가 부족한 경우 NULL이 리턴된다.


18.4.27 mysql_insert_id()
my_ulonglong mysql_insert_id(MYSQL *mysql)

18.4.27.1 Description
AUTO_INCREMENT 필드에 대하여 가장 최근에 생성된 ID를 리턴한다. 이 함수는
AUTO_INCREMENT 필드를 포함하는 테이블에 INSERT 질의를 수행한 이후에 사용된다.

18.4.27.2 Return values
가장 최근에 갱신된 AUTO_INCREMENT 필드의 값

18.4.27.3 Errors
None.


18.4.28 mysql_kill()
int mysql_kill(MYSQL *mysql, unsigned long pid)

18.4.28.1 Description
서버로 하여금 pid 인수로 주어진 쓰레드(thread)를 죽이도록(kill) 요청한다.

18.4.28.2 Return values
성공인 경우 0을, 에러가 발생한 경우 0이 아닌 값을 리턴한다.

18.4.28.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.29 mysql_list_dbs()
MYSQL_RES *mysql_list_dbs(MYSQL *mysql, const char *wild)

18.4.29.1 Description
wild 인수로 지정된 간단한 정규식(simple regular expression)에 매치되는 서버 상의 데이터베
이스 이름들을 포함하고 있는 결과 셋을 리턴한다. wild 인수는 '%', '_'와 같은 와일드카드 문
자를 포함할 수 있으며 모든 데이터베이스 이름을 얻고자 하는 경우 NULL을 지정할 수 있
다. mysql_list_dbs() 함수를 호출하는 것은 'SHOW databases [LIKE wild]'와 같은 질의를 수행하
는 것과 비슷하다.

반드시 mysql_free_result() 함수를 이용하여 결과 셋을 해제시켜 주어야 한다.

18.4.29.2 Return values
성공한 경우 MYSQL_RES 결과 셋을 리턴하며, 에러인 경우 NULL을 리턴한다.

18.4.29.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_OUT_OF_MEMORY
Out of memory.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.30 mysql_list_fields()
MYSQL_RES *mysql_list_fields(MYSQL *mysql, const char *table, const char *wild)

18.4.30.1 Description
wild 인수로 지정된 간단한 정규식(simple regular expression)에 매치되는 테이블 상의 필드명
들을 포함하고 있는 결과 셋을 리턴한다. wild 인수는 '%', '_'와 같은 와일드카드 문자를 포함
할 수 있으며 모든 필드명을 얻고자 하는 경우 NULL을 지정할 수 있다. mysql_list_fields()
함수를 호출하는 것은 'SHOW fields FROM table [LIKE wild]'와 같은 질의를 수행하는 것과 비
슷하다.

반드시 mysql_free_result() 함수를 이용하여 결과 셋을 해제시켜 주어야 한다.

18.4.30.2 Return values
성공한 경우 MYSQL_RES 결과 셋을 리턴하며, 에러인 경우 NULL을 리턴한다.

18.4.30.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.31 mysql_list_processes()
MYSQL_RES *mysql_list_processes(MYSQL *mysql)

18.4.31.1 Description
현재의 서버 쓰레드들을 기술하는 결과 셋을 리턴한다. 이것은 'mysqladmin processlist'에 의
한 결과와 동일한 정보를 보여준다.
반드시 mysql_free_result() 함수를 이용하여 결과 셋을 해제시켜 주어야 한다.

18.4.31.2 Return values
성공한 경우 MYSQL_RES 결과 셋을 리턴하며, 에러인 경우 NULL을 리턴한다.

18.4.31.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.32 mysql_list_tables()
MYSQL_RES *mysql_list_tables(MYSQL *mysql, const char *wild)

18.4.32.1 Description
wild 인수로 지정된 간단한 정규식(simple regular expression)에 매치되는 현재 데이터베이스
상의 테이블명들을 포함하고 있는 결과 셋을 리턴한다. wild 인수는 '%', '_'와 같은 와일드카
드 문자를 포함할 수 있으며 모든 테이블명을 얻고자 하는 경우 NULL을 지정할 수 있다.
mysql_list_tables() 함수를 호출하는 것은 'SHOW tables [LIKE wild]'와 같은 질의를 수행하는
것과 비슷하다.

반드시 mysql_free_result() 함수를 이용하여 결과 셋을 해제시켜 주어야 한다.

18.4.32.2 Return values
성공한 경우 MYSQL_RES 결과 셋을 리턴하며, 에러인 경우 NULL을 리턴한다.

18.4.32.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.33 mysql_num_fields()
unsigned int mysql_num_fields(MYSQL_RES *result)
혹은,
unsigned int mysql_num_fields(MYSQL *mysql)

18.4.33.1 Description
결과 셋 내의 컬럼의 개수를 리턴한다.

mysql_num_fields()를 호출할 때 결과 셋에 대한 포인터 혹은 연결 핸들을 가지고 호출할 수
있다. 만약 mysql_store_result() 함수가 NULL을 리턴한 경우(즉 결과 셋 포인터를 얻어올 수
없을 때) 연결 핸들을 사용하게 되며, 이 경우 mysql_num_fields() 함수를 호출함으로써
mysql_store_result() 함수가 비어있지 않은 결과(non-empty result)를 생성하였나의 여부를 결정
할 수 있다. 이것은 클라이언트 프로그램으로 하여금 질의가 SELECT (혹은 SELECT-like) 문
인가 아닌가를 알지 못하는 상태에서 적절한 행동을 취할 수 있도록 해준다. 아래에 예가
있다.

18.4.33.2 Return values
결과 셋 내의 필드 개수를 표시하는 unsigned integer.

18.4.33.3 Errors
None.

18.4.33.4 Example
MYSQL_RES *result;
unsigned int num_fields;
unsigned int num_rows;

if (mysql_query(&mysql,query_string)) {
    // error
}
else {   // query succeeded, process any data returned by it
    result = mysql_store_result(&mysql);
    if (result) {   // there are rows
          num_fields = mysql_num_fields(result);
        // retrieve rows, then call mysql_free_result(result)
    }
    else {   // mysql_store_result() returned nothing; should it have?
        if(mysql_num_fields(&mysql) == 0) {
            // query does not return data
            // (it was not a SELECT)
            num_rows = mysql_affected_rows(&mysql);
        }
        else {   // mysql_store_result() should have returned data
            fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
        }
    }
}

또 다른 대안은 mysql_num_fields(&mysql) 대신에 mysql_errno(&mysql)을 호출하는 것이다.
이 경우, 질의가 SELECT 문인가를 msyql_num_fields()의 리턴값으로 결정하기보다는
mysql_store_result()로부터의 에러를 직접 체크하게 된다.


18.4.34 mysql_num_rows()
my_ulonglong mysql_num_rows(MYSQL_RES *result)

18.4.34.1 Description
결과 셋 내의 행의 개수를 리턴한다.

mysql_num_rows() 함수의 사용 여부는 결과 셋을 얻기 위해서 mysql_store_result() 함수를 사
용하는가  mysql_use_result() 함수를 사용하는가에 달려있다. 만약 mysql_store_result() 함수를
사용한다면 mysql_num_rows() 함수는 즉시 호출될 수 있다. 만약 mysql_use_result() 함수를
사용한다면 mysql_num_rows() 함수는 결과 셋 내의 모든 행들이 retrieve되기 이전에는 올바
른 값을 리턴하지 못한다.

18.4.34.2 Return values
결과 셋 내의 행의 개수

18.4.34.3 Errors
None.


18.4.35 mysql_ping()
int mysql_ping(MYSQL *mysql)

18.4.35.1 Description
서버에 대한 연결이 제대로 동작하고 있나를 확인하며, 만약 연결이 끊어진 경우에는 자동
으로 재연결을 시동한다.

이 함수는 클라이언트가 너무 오랫동안 쉬고 있을 때 서버가 연결을 끊었나를 체크하고 필
요하다면 다시 연결하기 위해 사용될 수 있다.

18.4.35.2 Return values
서버가 살아있으면 0, 에러인 경우 0이 아닌 값을 리턴한다.

18.4.35.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.36 mysql_query()
int mysql_query(MYSQL *mysql, const char *query)

18.4.36.1 Description
null-terminated 문자열인 query 인수에 지정된 SQL 질의를 수행한다. 질의는 반드시 하나의
SQL 문으로 이루어져야 하며, 질의문에 세미콜론(;)이나 '\g'를 붙여서는 안된다.

mysql_query()는 이진 데이터를 포함하는 질의에는 사용할 수 없다. 이진 데이터는 중간에
NULL 문자를 포함할 수 있으며 이경우 mysql_query() 함수는 거기서 질의 문자열이 끝난 것
으로 인식하기 때문에 이경우에는 mysql_real_query() 함수를 사용하여야 한다.

18.4.36.2 Return values
성공이면 0, 에러이면 0이 아닌 값을 리턴.

18.4.36.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.37 mysql_real_connect()
MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd,
const char *db, uint port, const char *unix_socket, uint client_flag)

18.4.37.1 Description
mysql_real_connect() 함수는 호스트에서 돌아가고 있는 MySQL 데이터베이스 엔진에 연결을
시도한다. mysql_real_connect()는 mysql_get_client_info() 함수를 제외한 다른 모든 API 함수를
수행하기 이전에 반드시 성공적으로 수행되어져야 한다.

사용하는 인수들은 다음과 같다:

mysql 인수는 MYSQL connection 구조체이거나 NULL이 될 수 있다. 만약 mysql 인수가
NULL이면 C API는 자동으로 연결 구조체를 위한 메모리를 할당하고 mysql_close()가 호출
될 때 이를 해제한다. 이경우 단점은 연결이 실패한 경우 에러 메시지를 얻어올 수 없다는
것이다(mysql_errno()나 mysql_error() 함수로부터 에러 메시지를 얻기 위해서는 유효한
MYSQL 포인터를 제공하여야 한다). mysql 인수가 NULL이 아니면 이미 존재하는 MYSQL
구조체의 주소가 되어야 한다. 이 경우, mysql_real_connect() 함수를 호출하기 전에 반드시
mysql_init() 함수를 호출하여 MYSQL 구조체를 초기화해 주어야 한다. 다음의 예를 보라.
host 인수는 호스트명이 되거나 IP 어드레스가 될 수 있다. 만약 host 인수가 NULL이거나
"localhost"인 경우 로컬 호스트로의 접속으로 간주된다. 만약 OS가 소켓(socket-Unix)이나 파
이프(pipe-Win32)를 지원한다면 서버와의 연결을 위하여 TCP/IP 대신 이들을 사용할 수 있다.
user 인수에는 사용자의 MySQL 로그인 아이디를 넣어준다. User 인수가 NULL이면 현재 사
용자로 간주된다. Unix 환경에서는 현재 시스템 로그인명이 사용되며, Windows ODBC에서는
현재 사용자명이 명확히 지정되어야 한다. 15.4절에서는 ODBC 관리 프로그램에서 각 필드
들을 어떻게 채울 것인가에 대하여 논의한다.
passwd 인수에는 사용자에 대한 패스워드를 넣어준다. 만약 passwd가 NULL이면 user 테이
블에서 패스워드 필드가 비어있는 사용자들만이 매치를 위해 체크되어진다. 이것은 데이터
베이스 관리자로 하여금 데이터베이스 사용자가 자신의 패스워드를 명시하였나에 따라서 다
른 권한을 가지도록 하는 MySQL 권한 시스템을 설정할 수 있도록 해준다. 주의할 것은
mysql_real_connect() 함수를 호출하기 전에 패스워드를 암호화하려고 하지 말아야 한다는 것
이며, 패스워드의 암호화는 클라이언트 API에 의하여 자동으로 수행된다.
db 인수는 사용할 데이터베이스의 이름을 넣어준다. 만약 db 인수가 NULL이면 디폴트 데
이터베이스에 연결을 시도하게 된다.
port 인수가 0이 아니면 port 인수에 명시된 포트 번호를 TCP/IP 연결을 위한 포트 번호로
사용하게 된다. host 인수가 연결의 형식을 결정한다는 것에 주의한다.
unix_socket 인수가 NULL이 아니면 이 인수 문자열이 사용될 socket이나 named pipe가 된다.
client_flag 값은 보통 0이지만 매우 특별한 경우에 다음의 플래그들의 조합으로 설정할 수
있다.

-----------------------------------------------------------------------------------------
| Flag name     | Flag meaning |
-----------------------------------------------------------------------------------------
| CLIENT_FOUND_ROWS | affected row의 갯수가 아닌 found row의 갯수를 리턴한다. |
-----------------------------------------------------------------------------------------
| CLIENT_NO_SCHEMA  | db_name.tbl_name.col_name 형식의 문법(syntax)을 허용하지 않는다. |
|     | 이것은 ODBC를 위한 것으로서, 만약 이와 같은 문법을 사용하면 파서가|
|     | 에러를 발생시키게 된다. 이것은 ODBC 프로그램에서 버그를 찾아낼 때 |
|     | 유용하게 사용할 수 있다. |
-----------------------------------------------------------------------------------------
| CLIENT_COMPRESS   | 압축 프로토콜(compression protocol)을 사용한다. |
-----------------------------------------------------------------------------------------
| CLIENT_ODBC     | 클라이언트가 ODBC 클라이언트임을 알려주며, 이것은 mysqld가 좀더 |
|     | ODBC에 적합하도록 해준다. |
-----------------------------------------------------------------------------------------

18.4.37.2 Return values
연결이 성공하면 MYSQL * 연결 핸들(connection handle)을 리턴하며, 연결에 실패하면 NULL
을 리턴한다. 연결이 성공한 경우 리턴 값은 첫번째 인수에 NULL을 지정하지 않았다면 그
인수와 동일하다.

18.4.37.3 Errors
CR_CONN_HOST_ERROR
MySQL 서버로의 연결에 실패
CR_CONNECTION_ERROR
로컬 MySQL 서버로의 연결에 실패
CR_IPSOCK_ERROR
IP 소켓의 생성에 실패
CR_OUT_OF_MEMORY
메모리가 없음(Out of memory).
CR_SOCKET_CREATE_ERROR
Unix 소켓의 생성에 실패
CR_UNKNOWN_HOST
해당 호스트명에 대한 IP 주소를 찾을 수 없음.
CR_VERSION_ERROR
일치하지 않는 프로토콜 버전을 사용하는 클라이언트 라이브러리를 가지고 서버에 접속
하려 시도함으로 인하여 프로토콜 불일치가 발생함. 이것은 매우 오래된 클라이언트
라이브러리를 사용하여 새로운 버전의 서버에 접속하고자 하는 경우에 발생한다. 이런
경우에는 서버를 시동할 때 '--old-protocol' 옵션을 넣어서 시동하면 해결할 수 있다.
CR_NAMEDPIPEOPEN_ERROR;
Win32 환경에서 named pipe를 생성하는데 실패함.
CR_NAMEDPIPEWAIT_ERROR;
Win32 환경에서 named pipe를 기다리는데 실패함.
CR_NAMEDPIPESETSTATE_ERROR;
Win32 환경에서 pipe 핸들러를 얻는데 실패함.

18.4.37.4 Example
MYSQL mysql;

mysql_init(&mysql);
if (!mysql_real_connect(&mysql,"host","user","passwd","database",0,NULL,0)) {
    fprintf(stderr, "Failed to connect to database: Error: %s\n", mysql_error(&mysql));
}


18.4.38 mysql_real_query()
int mysql_real_query(MYSQL *mysql, const char *query, unsigned int length)

18.4.38.1 Description
query 인수에 지정된 SQL 질의를 수행하며, query 인수는 length 인수에 지정된 길이를 가져
야 한다. 질의는 하나의 SQL 문으로 이루어져야 하며, 세미콜론이나 '\g'를 SQL문에 붙여서
는 안된다.

이진 데이터를 포함하는 질의 데이터인 경우 mysql_query() 함수가 아닌 mysql_real_query()
함수를 사용하여야 한다. 또한 mysql_real_query()함수는 질의문의 길이를 얻기 위한 strlen()
함수를 수행하지 않기 때문에 mysql_query() 함수에 비하여 수행 속도가 빠르다.

18.4.38.2 Return values
질의에 성공하면 0, 에러가 발생하면 0이 아닌 값을 리턴한다.

18.4.38.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.39 mysql_reload()
int mysql_reload(MYSQL *mysql)

18.4.39.1 Description
MySQL 서버로 하여금 grant 테이블을 다시 읽도록 요청한다. 현재 연결되어 있는 사용자는
반드시 권한 설정을 다시 읽어야 한다.

이 함수를 사용하는 것은 권장되지 않으며, 대신 'FLUSH PRIVILEGES' SQL 문을
mysql_query() 함수를 이용하여 수행하는 것이 좋다.

18.4.39.2 Return values
성공하면 0, 에러면 0이 아닌 값을 리턴한다.

18.4.39.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.40 mysql_row_seek()
MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET offset)

18.4.40.1 Description
결과 셋 내에 행 커서(row cursor)를 임의의 위치로 설정한다. 이를 위해서는 결과 셋 구조체
가 전체 질의 결과를 가지고 있어야 하며, 따라서 mysql_row_seek() 함수는 mysql_use_result()
함수와는 사용할 수 없고 mysql_store_result() 함수와 사용할 수 있다.

offset 인수는 mysql_row_tell() 함수나 mysql_row_seek() 함수를 호출한 리턴값이 되어야 하며,
이 값은 단순한 행 번호를 의미하는 것이 아니다. 만약 행 번호를 이용하여 결과 셋 내에서
행을 찾고자 하는 경우에는 mysql_data_seek() 함수를 사용하여야 한다.

18.4.40.2 Return values
행 커서(row cursor)의 이전값(previous value). 이 값은 mysql_row_seek() 함수의 인수로 넘겨진
다.

18.4.40.3 Errors
None.


18.4.41 mysql_row_tell()
MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *result)

18.4.41.1 Description
마지막 mysql_fetch_row() 함수 호출에 의한 행 커서의 현재 위치를 리턴한다. 이값은
mysql_row_seek() 함수의 인수로 넘겨진다.

mysql_row_tell() 함수는 반드시 mysql_store_result() 함수 뒤에 사용되어야 하며,
mysql_use_result() 함수 뒤에는 사용할 수 없다.

18.4.41.2 Return values
행 커서의 현재 변위(The current offset of the row cursor).

18.4.41.3 Errors
None.


18.4.42 mysql_select_db()
int mysql_select_db(MYSQL *mysql, const char *db)

18.4.42.1 Description
db 인수에 지정한 데이터베이스를 mysql 인수가 가진 연결에 대한 현재 작업 데이터베이스
가 되도록 한다. 이후의 질의에 대해서 여기서 지정한 데이터베이스가 데이터베이스를 명시
하지 않은 테이블 참조에 대한 디폴트 데이터베이스로 사용된다.

연결되어 있는 사용자가 db 인수에 지정된 데이터베이스를 사용할 권한을 가지고 있다는 것
이 인증되지 않으면 mysql_select_db() 함수는 실패한다.

18.4.42.2 Return values
성공이면 0, 에러이면 0이 아닌 값

18.4.42.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.43 mysql_shutdown()
int mysql_shutdown(MYSQL *mysql)

18.4.43.1 Description
데이터베이스 서버로 하여금 셧다운되도록 요청하며, 현재 연결된 사용자가 셧다운 권한을
가지고 있어야 한다.

18.4.43.2 Return values
성공이면 0, 에러이면 0이 아닌 값을 리턴한다.

18.4.43.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.44 mysql_stat()
char *mysql_stat(MYSQL *mysql)

18.4.44.1 Description
'mysqladmin status' 명령어에 의해 제공되는 정보와 비슷한 정보를 포함하고 있는 문자열을
리턴한다. 이 문자열에는 구동시간(uptime), 실행되고 있는 쓰레드 수, 질문(question), 재시동
(reload), 열려있는 테이블 등에 대한 정보가 포함되어 있다.

18.4.44.2 Return values
서버 상태를 기술하는 문자열. 에러이면 NULL을 리턴한다.

18.4.44.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.45 mysql_store_result()
MYSQL_RES *mysql_store_result(MYSQL *mysql)

18.4.45.1 Description
데이터를 얻어오는 질의(SELECT, SHOW, DESCRIBE, EXPLAIN)의 수행에 대하여
mysql_store_result() 혹은 mysql_use_result() 함수를 호출한다.

mysql_store_result() 함수는 전체 질의 결과를 클라이언트로 읽어오며, MYSQL_RES 구조체를
할당하고 결과를 이 구조체에 넣어준다. 아무 행도 리턴되지 않으면 빈 결과 셋(empty result
set)이 리턴된다(빈 결과 셋은 NULL 리턴 값과는 다르다).

mysql_store_result() 함수를 호출하면 mysql_num_rows() 함수를 호출하여 결과 셋 내에 얼마
나 많은 행들이 있는가를 알아 볼 수 있다.
mysql_fetch_row() 함수를 호출하여 결과 셋으로부터 행들을 얻어올 수 있으며, 혹은
mysql_row_seek()와 mysql_row_tell() 함수를 통하여 결과 셋 내에서의 현행 위치를 얻거나 위
치를 설정해줄 수 있다.

결과셋을 통한 작업이 끝나면 반드시 mysql_free_result() 함수를 호출해준다.

18.4.45.2 Return values
결과가 저장된 MYSQL_RES 결과 구조체(result structure). 에러이면 NULL을 리턴.

18.4.45.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_OUT_OF_MEMORY
Out of memory.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.

18.4.46 mysql_thread_id()
unsigned long mysql_thread_id(MYSQL *mysql)

18.4.46.1 Description
현재 연결에 대한 쓰레드 ID를 리턴한다. 이 값은 mysql_kill() 함수의 인수로 사용되어 쓰래
드를 kill 하는데 사용될 수 있다.

18.4.46.2 Return values
현재 연결에 대한 쓰레드 ID.

18.4.46.3 Errors
None.

18.4.47 mysql_use_result()
MYSQL_RES *mysql_use_result(MYSQL *mysql)

18.4.47.1 Description
데이터를 얻어오는 질의(SELECT, SHOW, DESCRIBE, EXPLAIN)의 수행에 대하여
mysql_store_result() 혹은 mysql_use_result() 함수를 호출한다.

mysql_use_result() 함수는 결과 셋을 얻어오는 작업을 초기화하지만 mysql_store_result() 함수
처럼 실제로 결과 셋을 읽어서 클라이언트에게 넘겨주지는 않는다. 대신 mysql_fetch_row()
함수를 각각 호출함으로써 각 행이 하나씩 얻어진다. 이것은 질의의 결과를 임시 테이블이
나 지역 버퍼에 저장하지 않고 서버로부터 직접 읽어오게 되며, 이는 mysql_store_result() 함
수에 비하여 좀더 빠르고 훨씬 적은 양의 메모리를 차지한다. 클라이언트는 현재 행에 대한
메모리와 max_allowed_packet 바이트까지 증가할 수 있는 통신 버퍼(communication buffer)만
을 할당하게 된다.

하지만 클라이언트 측에서 각 행별로 많은 작업을 수행하고 있거나 사용자가 ^S(stop scroll)
을 입력할 수도 있는 스크린으로 결과를 내보내는 경우에는 mysql_use_result() 함수를 사용
해서는 안된다. 이것은 서버를 멈추게 하고 다른 쓰레드들로 하여금 데이터가 얻어지고 있
는 테이블을 갱신하는 것을 막게 된다.

mysql_use_result() 함수를 사용할 때는 NULL 값이 리턴될 때까지 mysql_fetch_row() 함수를
수행해야 한다. 그렇지 않으면 아직 얻어오지 않은 행들이 다음 질의에 대한 결과 셋으로
넘어가서 리턴되게 된다. C API는 'Commands out of sync' 에러를 주게 된다.

mysql_use_result() 함수로부터 리턴되는 결과에 대해서 mysql_data_seek(), mysql_row_seek(),
mysql_row_tell(), mysql_num_rows(), mysql_affected_rows()과 같은 함수들을 사용할 수 없으며,
혹은 mysql_use_result() 함수가 종료할 때까지 다른 질의를 수행할 수 없다(그러나 모든 행들
을 fetch한 후에는 mysql_num_rows() 함수가 실제로 fetch되어진 행의 개수를 리턴한다).

결과 셋에 대한 작업이 끝나면 반드시 mysql_free_result()를 호출하여야 한다.

18.4.47.2 Return values
MYSQL_RES 결과 구조체(result structure). 에러이면 NULL을 리턴한다.

18.4.47.3 Errors
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_OUT_OF_MEMORY
Out of memory.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.


18.4.48 mysql_query() 함수가 성공하였는데 mysql_store_result() 함수가 가끔 NULL을 리턴하
는 이유는 무엇인가?

이런 경우가 있을 수 있으며 이것은 다음 중 하나의 상황이 발생하였음을 의미한다:

malloc()이 실패한 경우(예를 들어 결과 셋이 너무 큰 경우).
데이터를 읽을 수 없는 경우(연결에 에러가 생기는 등).
질의가 아무 데이터도 리턴하지 않는 경우(INSERT, UPDATE, DELETE 문 등의 경우).

질의문이 비어있지 않은(non-empty) 결과를 생성하였나는 mysql_num_fields() 함수를 호출함
으로써 체크해볼 수 있다. 만약 mysql_num_fields() 함수가 0을 리턴하면 결과셋이 빈 것이
고 가장 마지막 질의가 INSERT, DELETE와 같이 값을 리턴하지 않는 질의문임을 의미한다.
mysql_num_fields() 함수가 0이 아닌 값을 리턴하는 경우는 질의문이 비어있지 않는 결과를
생성해야만 함을 의미한다. 자세한 내용은 mysql_num_fields() 함수의 설명을 참조한다.

mysql_errno() 함수나 mysql_error() 함수를 호출함으로써 에러를 테스트해볼 수 있다.

18.4.49 What results can I get from a query?

질의의 결과로 리턴되는 결과 셋에 덧붙여 다음과 같은 정보들을 또한 얻을 수 있다:
- mysql_affected_rows()는 INSERT, UPDATE, DELETE 문을 수행할 때 가장 최근에 수행된
질의에 의해 affected된 행들의 개수를 리턴한다. 예외는 DELETE가 WHERE 구문없이
사용되어 테이블이 truncate되는 경우이다. 이 경우 mysql_affected_rows() 함수는 affected
된 레코드의 숫자로서 0을 리턴한다.
- mysql_num_rows()는 결과 셋 내의 행의 개수를 리턴한다. mysql_store_result() 함수와 함께
사용되는 경우에는 mysql_store_result()가 리턴되자마자 최대한 빨리 mysql_num_rows()가
호출되어진다. mysql_use_result()와 함께 사용되는 경우에는 mysql_fetch_row()가 모든 행
에 대해서 수행된 후에만 mysql_num_rows()가 호출될 수 있다.
- mysql_insert_id()는 AUTO_INCREMENT 인덱스를 가진 테이블에 행을 추가한 경우 가장
최근에 새로 생성된 ID를 리턴한다.
- 몇몇 질의들(LOAD DATA INFILE ..., INSERT INTO ... SELECT ..., UPDATE)은 부가적인 정
보를 리턴한다. 이 결과들은 mysql_info()에 의하여 리턴된다. mysql_info()는 리턴할 부가
적인 정보가 없는 경우 NULL을 리턴한다.

18.4.50 How can I get the unique ID for the last inserted row?
AUTO_INCREMENT 속성을 가지는 컬럼을 포함하고 있는 테이블에 새로운 레코드를 삽입
하는 경우, mysql_insert_id() 함수를 호출함으로써 가장 최근에 생성된 ID를 얻어올 수 있다.

다음의 코드를 수행함으로써 AUTO_INCREMENT 인덱스가 사용되었나를 체크해 볼 수 있다.
또한 질의가 AUTO_INCREMENT 인덱스를 가진 INSERT 문인가를 체크할 수 있다:

if (mysql_error(&mysql)[0] == 0 && mysql_num_fields(result) == 0 && mysql_insert_id(&mysql) != 0)
{
    used_id = mysql_insert_id(&mysql);
}

하나의 테이블에서 생성된 ID를 다른 테이블에 삽입하는데 사용하고자 하는 경우 다음과
같은 SQL 문을 사용한다:

INSERT INTO foo (auto, text) VALUES(NULL, 'text');             # generate ID by inserting NULL
INSERT INTO foo2 (id, text) VALUES(LAST_INSERT_ID(), 'text');  # use ID in second table


18.4.51 Problems linking with the C API
어떤 시스템에서는 C API를 linking하여 컴파일할 때 다음과 같은 에러가 발생할 수 있다:

gcc -g -o client test.o -L/usr/local/lib/mysql -lmysqlclient -lsocket -lnsl

Undefined       first referenced
symbol          in file
floor            /usr/local/lib/mysql/libmysqlclient.a(password.o)
ld: fatal: Symbol referencing errors. No output written to client

위와 같은 에러가 발생하는 경우에는 컴파일 시에 -lm을 추가함으로써 math 라이브러리를
함께 포함해 준다.


18.4.52 How to make a thread-safe client
클라이언트는 거의 thread-safe하다. 가장 큰 문제점은 'net.c'에서 소켓을 읽는 서브루틴들이
interrupt-safe하지 못하다는 것이다. 이것은 서버로부터 읽는 작업이 오래 걸릴 때 클라이언
트가 작업을 중단할 수 있는 기능을 원할 것이라는 생각에서 그렇게 작성되어졌다.

표준 클라이언트 라이브러리는 쓰레드 옵션없이 컴파일되어진다.

Thread-safe 클라이언트를 만들기 위해서는 -lmysys, -lstring, -ldbug 라이브러리와 서버가 사용
하는 net_serv.o를 함께 사용한다.

Threaded client를 사용하면 'thr_alarm.c' 파일에 있는 루틴들을 활용할 수 있다. mysys 라이브
러리의 루틴들을 사용할 때 기억해야할 단 한가지는 my_init() 함수를 가장 먼저 호출해야
한다는 것이다!

mysql_real_connect()를 제외한 모든 함수들은 현재 thread-safe하다. 다음의 내용에서는 thread-
safe 클라이언트 라이브러리를 컴파일하는 방법과 그것을 thread-safe 방식으로 사용하는 방
법에 대하여 기술한다(여기서는 mysql_real_connect() 함수에 대한 예를 설명하며,
mysql_connect()의 경우에도 동일하다).

mysql_real_connect() 함수를 thread-safe하게 만들기 위해서는 다음의 명령으로 클라이언트를
재컴파일하여야 한다:

shell> CPPFLAGS=-DTHREAD_SAFE_CLIENT ./configure ...

pthread 라이브러리가 디폴트로 포함되어지지 않기 때문에 표준 클라이언트(standard client)를
링킹할 때 정의되지 않은 심볼(undefined symbols)로 인한 에러가 발생할 수 있다.

결과로 나오는 'libmysqld.a' 라이브러리는 이젠 thread-safe하다. 이것은 두개의 thread가
mysql_real_connect()에 의해 리턴된 동일한 연결 핸들(connection handle)을 동시에 질의하지
않는 한 클라이언트 코드가 thread-safe하다는 것을 의미한다; 클라이언트/서버 프로토콜은
주어진 연결에 대하여 동시에 하나의 요청만을 허용한다. 동일한 연결에 대하여 복수개의
thread를 사용하길 원하는 경우에는 mysql_query()와 mysql_store_result()의 호출 묶음(call
combination)에 대하여 mutex lock을 해야 한다.

일단 mysql_store_result()가 준비되면 lock은 해제될 수 있으며 다른 thread들은 동일한 연결
에 대하여 query를 할 수 있다(다시 말해서, 서로 다른 thread들이 적절한 locking 프로토콜
을 사용하는 한 mysql_store_result()과 함께 생성된 서로 다른 MYSQL_RES 포인터들을 사용
할 수 있다). POSIX thread로 프로그램한다면 mutex lock을 생성하고 해제하는데
pthread_mutex_lock()와 pthread_mutex_unlock() 함수를 사용할 수 있다.

mysql_store_result()가 아닌 mysql_use_result()를 사용하는 경우에는 lock이 mysql_use_result()
와 mysql_fetch_row()에 대한 호출을 surround할 필요가 있다. 그러나 threaded client는
mysql_use_result()를 사용하지 않는 것이 가장 좋다.



19. 타 DB와 비교
19.1 Mysql/mSQL 비교

이번장은 Mysql 개발자가 쓴 글이므로  이점을 염두에 두고 보아야  한다.
그렇지만 개발자들이 알고 있는대로 솔직하게 작성되었다.

지원되는 함수, 타입, 제한사항등은 crash-me 웹 페이지를 참고하면 된다.

<성능>
속도 비교와 관련해서는 Mysql 벤치마크를 참고한다. (11장)  mSQL은 스레
드 생성 때문에 발생하는 과부하가 없고, 파져(parser)가  작으며, 기능이
적고 보안이 간단하다. 그래서 다음과 같은 경우에는 mSQL의  속도가 빠르
다.
- 연결/해제를 반복하면서 각 연결때마다 간단한 질의를 하는 테스트
-몇개의 컬럼과 키만 있는 아주 간단한 테이블에  자료을 입력(INSER)하는
경우
- CREATE TABLE, DROP TABLE
- 인덱스 없이 SELECT 하는 경우(테이블 검색이 매우 간단)

이런 작업들은 매우 간단해서, 시작시  높은 부하가 걸리는 경우에  좋다.
연결이 되고나서는 Mysql이 더 높은 성능을 보여준다.

그 외에는 다음과 같은 면에서 Mysql이 mSQL( 및 다른 SQL 프로그램) 보다
빠르다.
- 복합적인 SELECT 연산
- 대량의 자료를 검색 (Mysql은 성능이 뛰어나고 빠르며  안전한 프로토콜
을 사용한다)
-  가변길이  문자열을  가진  테이블.  Mysql은  효율적으로  되어있으며
VARCHAR 컬럼에 인덱스를 사용할 수 있다.
- 많은 컬럼을 가진 테이블을 다루는 경우
- 레코드 길이가 큰 테이블을 다루는 경우
- 많은 표현식(expression)을 가진 SELECT 문을 사용하는 경우
- 거대한 테이블에서 SELECT문을 사용하는 경우
- 동시에 다중으로 연결을  처리해야할 때. Mysql은 완전한  멀티스레드를
지원한다. 각 연결은 자신의 스레드를 가지며 이는 스레드에서  다른 스레
드를 기다릴 필요가 없다는 것을 의미한다. (현재의 스레드가 다른 스레드
에서 접근하기 원하는 테이블을  수정하고 있는 경우는 제외)  mSQL에서는
하나의 연결이 성립되면 그 연결에서 질의를 수행하든 하지 않든 상관없이
첫 번째 연결이 끝날때까지 다른 것들은 기다려야한다. 첫 번째 연결이 해
제되면 다음 연결이 성립되고 또 다른 것들은 다시 기다려야한다.
- 조인. mSQL에서는 SELECT에서 테이블의 정렬 순서를  변경하면 엄청나게
느려진다. 벤치마킹 프로그램을 사용하였을 때, 그 시간은 Mysql보다 최대
15000배 느렸다. mSQL에서는 최적의 순서에 따른 테이블을  정열하기 위한
조인 최적화기가 없기 때문이다. 그렇지만, 테이블을 mSQL2의 적절한 정열
순서에 따라 정확하게 테이블을 구성하고 WHERE 가 간단하고  인덱스 컬럼
을 사용하면 조인은 상대적으로 빨라진다. 11장 Mysql 벤치마킹 참고.
-  ORDER BY and GROUP BY.
- DISTINCT
- TEXT 나  BLOB 컬럼 사용

<SQL 특징>
-  GROUP BY, HAVING. mSQL은 GROUP BY를 지원하지 않는다. Mysql은  완전
하게 GROUP BY 와 HAVING을  지원하며 다음의 함수를 지원한다.  COUNT(),
AVG(), MIN(), MAX(),  SUM() and STD().  COUNT(*)는 하나의  테이블에서
SELECT를 사용하고, 컬럼은 검색하지 않으며  WHERE 문이 없을 경우  매우
빠르게 최적화되어있다. MIN() 과 MAX()는 문자열 인자를 가진다.
- INSERT 와 UPDATE에서 계산가능.
        예)Mysql> UPDATE SET x=x*10+y WHERE x<20;
- 알리아싱. Mysql에는 컬럼 알리아싱이 있다.
- 컬럼 이름 한정. Mysql에서 컬름 이름이 질의에 사용된 테이블에서 유일
하다면, 완전한 한정자를 쓸 필요가 없다. (** table_name.column_name 이
런 식으로 하지 않아도 된다는 말입니다**)
- SELECT문에서 다양한 함수 지원. 7.3 함수 참조.

<디스크 공간 효율성>
테이블을 얼마나 작게 만들 수  있는가를 말한다. Mysql에는 매우  정밀한
타입이 많으므로 매우 적은 공간만을 차지하도록 테이블을 만들  수 있다.
예를 들어 유용한 데이터타입중 하나는 MEDIUMINT로 3 바이트 long이다. 1
억건의 레코드가 있는 경우, 각 레코드당 1 바이트를 절약하는  것은 매우
중요하다. mSQL2는 지원하는 데이터 타입이 제한되어있어서 테이블을 작게
만들기가 어렵다.
<안정성>
안정성을 객관적으로 판단하는 것은 쉬운 일이 아니다.  Mysql의 안정성은
1.5장 참고. 우리는(Mysql 개발팀)은 mSQL의 안정성에 대한 경험이 없으므
로 이에 대해서는 말할 것이 없다.
<가격>
주요한 주제중의 하나가 라이센스이다. Mysql은 mSQL보다 유연한 라이센스
정책을 가지고 있으며 가격도 저렴하다. 어떤 제품을 선택하든  최소한 라
이센스나 이메일 지원에 대한 비용을 고려해야한다. (물론  판매하는 제품
에 Mysql이 포함되어있으면 라이센스가 반드시 필요하다)
<펄 인터페이스>
Mysql은 몇가지 추가된 기능과 함께 mSQL과 동일한 펄  인터페이스를 가지
고 있다.
<JDBC(Java)>
Mysql은 현재 4가지 JDBC 드라이버가 있다.
- The gwe driver: A Java interface by GWE technologies (not supported
anymore).
- The jms driver: An improved gwe driver by Xiaokun Kelvin ZHU
- The twz driver:  A type 4  JDBC driver by  Terrence W. Zellers  and
educational use.
- The mm driver: A type 4 JDBC driver by Mark Matthews{{</LI>
}}
추천하는 드라이버는 twz나 mm 드라이버이다. 둘다 훌륭하게  작동하는 것
으로 보고되었다.
mSQL에 JDBC 드라이버가 있는 것은 알고 있지만 그에 대한 비교 경험은 없
다.

<개발속도>
Mysql은 매우 작은 규모의 개발팀을 가지고 있지만, 모두 C와  C++ 코딩을
매우 빠르게 하는데  익숙해있다. 스레드, 함수, GROUP BY 등등  mSQL에는
아직 구현되지   않은 것들이  많아서 따라오려면   멀었다. mSQL의  최근
'HISTORY' 파일과 Mysql 레퍼런스 매뉴얼의 뉴스 섹션을 비교해보자. 어떤
것이 빠르게 개발되고 있는지 명확할 것이다.

<유틸리티 프로그램>
mSQL과 Mysql에는 매우 다양한 서드-파티 툴이 있다. mSQL에서 Mysql로 포
팅하는 것이 매우 쉽기 때문에, mSQL에서 사용가능한 대부분의 애플리케이
션은 Mysql에서도 사용할 수 있다.
Mysql에는 간단한 mSQL2Mysql 프로그램이 들어있는데 mSQL과 Mysql 사이의
대부분의 C-API 함수의 스펠링을 바꿀 수 있다. mSQL에서 Mysql 로 클라이
언트 프로그램을 포팅하는 것은 일반적으로 그다지 시간이 걸리지 않는다.

19.1.1 mSQL 툴을 Mysql로 바꾸기
경험상 보았을 때, mSQL C-API를 사용하는 mSQL-tcl 과  mSQLjava를 Mysql
C-API  에서 작동할 수 있도록 변환하는 것은 몇시간이면 충분하다.

변환과정은 다음과 같다:

1. 소스에서 mSQL2Mysql 스크립트를 실행한다. 여기에는 replace 프로그램
이 필요한데, Mysql과 함께 배포된다.
2. 컴파일
3. 모든 컴파일 에러 수정

mSQL C API와 Mysql C API 의 차이점은 다음과 같다.
- Mysql uses  a Mysql structure  as a connection  type (mSQL uses  an
int).
-  Mysql_connect()   takes a  pointer  to a   Mysql structure  as  a
parameter. It is easy  to define one globally  or to use malloc()  to
get one. Mysql_connect() also takes  2 parameters for specifying  the
user and password. You may set these to NULL, NULL for default use.
- Mysql_error() takes the  Mysql structure as  a parameter. Just  add
the parameter to your  old mSQL_error() code  if you are porting  old
code.
-  Mysql returns an  error number and  a text error  message for all
errors. mSQL returns only a text error message.
-  Some  incompatibilities exist  as  a result  of Mysql   supporting
multiple connections to the server from the same process.{{</LI>
}}
(
M
  ysql은 동일한 프로세스에서 서버에 다중 연결을 지원하므로 호환되지 않는 부분이  존재한다.)

19.1.2 mSQL 과 Mysql  클라이언트/서버 통신 프로토콜 차이점
mSQL과 Mysql을 둘다 지원하지 못하거나 힘든 몇가지 차이점이 있다.

Mysql 프로토콜이  mSQL 프로토콜과 다른 몇가지 주요 차이점은 다음과 같
다.
- 메시지 버퍼에 많은 검색 행을 가지고 있다.
- 메시지 버퍼는 질의나 결과가 현재의 버퍼보다 크면, 서버와 클라이언트
에서 설정한 제한선까지 동적으로 확장이 될 수 있다.
- 중복되거나 분실된 패킷을 잡아 모든 패킷의 숫자를 셀 수 있다.
- 모든 컬럼 값은 아스키로 전송된다. 컬럼과 행의 길이는  바이너리 코딩
(1, 2, 3 바이트)으로 꾸려져 전송된다.
- Mysql can read  in the result  unbuffered (without having to  store
the full set in the client).
- 단일 쓰기/읽기 락이 30초이상 걸리면, 서버에서 연결을 해제한다.
- 연결이 8시간 이상 쉬고 있으면, 서버에서 연결을 해제한다.

19.1.3  How mSQL 2.0 SQL syntax differs from Mysql
(** 굳이 번역하지 않습니다. 심심하면 한번 읽어보세요. mSQL  써본지 워
낙 오래되었고 그다지 쓸 일이 없어서요**)

Column types
Mysql
Has the following  additional types  (among others;  see see  section 
7.6)
ENUM type for one of a set of strings.
SET type for many of a set of strings.
BIGINT type for 64-bit integers.{{</LI>
}}

Mysql also supports the following additional type attributes:
UNSIGNED option for integer columns.
ZEROFILL option for integer columns.
AUTO_INCREMENT option for integer columns that are a PRIMARY KEY.
DEFAULT value for all columns.{{</LI>
}}

mSQL2
mSQL column types correspond to the Mysql types shown below:{{<TBODY>
}}

mSQLtype        Corresponding Mysql type
CHAR(len)       CHAR(len)
TEXT(len)       TEXT(len). len is the maximal length. And LIKE works.
INT             INT. With many more options!
REAL            REAL. Or   FLOAT. Both  4- and   8-byte versions  are
available.
UINT            INT UNSIGNED
DATE            DATE. Uses ANSI SQL format rather than mSQL's own.
TIME            TIME
MONEY   DECIMAL(12,2). A fixed-point value with two decimals.{{</TBODY>
}}

{{</DD>
}}Index creation
Mysql
Indexes may be specified at table creation time with the CREATE TABLE
statement.
mSQL
Indexes must  be  created after   the table has   been created,  with
separate CREATE INDEX statements.{{</DD>
}}

To insert a unique identifier into a table
Mysql
Use AUTO_INCREMENT as a column type specifier.
mSQL
Create a SEQUENCE on a table and select the _seq column.{{</DD>
}}
To obtain a unique identifier for a row
Mysql
Add a PRIMARY KEY or UNIQUE key to the table.
mSQL
Use the  _rowid column.  Observe  that _rowid  may change   over time
depending on many factors.{{</DD>
}}

To get the time a column was last modified
Mysql
Add a TIMESTAMP column to the table. This column is automatically set
to the current date and time  for INSERT or UPDATE statements if  you
don't give the column a value or if you give it a NULL value.
mSQL
Use the _timestamp column.{{</DD>
}}
NULL value comparisons
Mysql
Mysql follows ANSI SQL and a comparison with NULL is always NULL.
mSQL
In mSQL, NULL = NULL  is TRUE. You must  change =NULL to IS NULL  and
<>NULL to IS NOT NULL when porting old code from mSQL to Mysql.{{</DD>
}}

String comparisons
Mysql
Normally,  string  comparisons   are performed   in  case-independent
fashion with the sort order  determined by the current character  set
(ISO-8859-1 Latin1 by default). If you don't like this,  declare your
columns with the  BINARY attribute,  which causes  comparisons to  be
done according to the ASCII order used on the Mysql server host.
mSQL
All string comparisons are  performed in case-sensitive fashion  with
sorting in ASCII order.{{</DD>
}}

Case-insensitive searching
Mysql
LIKE is a case-insensitive  or case-sensitive operator, depending  on
the columns involved.  If possible,  Mysql uses indexes  if the  LIKE
argument doesn't start with a wildcard character.
mSQL
Use CLIKE.{{</DD>
}}
Handling of trailing spaces
Mysql
Strips all spaces at the end of CHAR and VARCHAR columns. Use  a TEXT
column if this behavior is not desired.
mSQL
Retains trailing space.{{</DD>
}}
WHERE clauses
Mysql
Mysql correctly prioritizes everything (AND is evaluated  before OR).
To get mSQL behavior in Mysql, use parentheses (as shown below).
mSQL
Evaluates everything from left to right. This means that some logical
calculations with more  than three arguments  cannot be expressed  in
any way. It also means you must change some queries when  you upgrade
to Mysql. You do this easily by adding parentheses. Suppose  you have
the following mSQL query:
Mysql&gt; SELECT * FROM table WHERE a=1 AND b=2 OR a=3 AND b=4;
To make Mysql  evaluate this the  way that mSQL  would, you must  add
parentheses:
Mysql&gt; SELECT  *  FROM table   WHERE (a=1  AND (b=2   OR (a=3  AND
(b=4))));
{{</DD>
}}Access control
Mysql
Has tables to  store grant  (permission) options per  user, host  and
database.
mSQL
Has a file `mSQL.acl'  in which you  can grant read/write  privileges
for users.


19.2 PostgreSQL과 Mysql 비교
PostgreSQL은 사용자-정의 타입, 트리거, 룰, 트랜잭션 지원 등 몇가지 향
상된 기능을 가지고 있다. 그렇지만 ANSI SQL 과 ODBC의 표준 타입이나 함
수 지원이   미약하다. 지원되거나  지원되지 않는   제한, 타입,  함수는
crash-me               웹               페이지를               참고하
자.(http://www.Mysql.com/crash-me-choose.htmy)

일반적으로 PostgreSQL은 Mysql보다 느리다. 11장 [벤치마크] 참고.  이것
은 대부분 트랜잭션 시스템 때문이다. 정말로 트랜잭션이 필요하고 속도를
희생할 생각이 있다면 PostgreSQL을 고려해볼 수 있다.

**참고
http://www.postgresql.org/comp-comparison.html
여기에 가면 간략하게나마 PostgreSQL과 타 DB와 비교자료가 있습니다.
http://database.sarang.net/database/database.phtml
여기에는 위 자료 번역본이 있습니다.

이 포스트를..

덧글 쓰기 엮인글 쓰기

[펌] mysql 명령어 정리2 MYSQL

2005/12/24 18:43

복사 http://blog.naver.com/meetjava/100020529369

출처 해바라기 | 해바라기
원문 http://blog.naver.com/roadily/60010593609
◈ 테이블 컬럼 타입


# 날짜 및 시간 관련 컬럼 타입

- DATE
   날짜를 표현하는 유형 [YYYY-MM-DD], 1000-01-01 ∼ 9999-12-31까지 나타낼 수 있

- DATETIME
   날짜와 시간을 표현하는 유형 [YYYY-MM-DD HH:MM:SS],  1000-01-01 00:00:00 ∼ 9999-12-31 23:59:59

- TIMESTAMP
   자동변경 컬럼 타입(4 Byte ),  1970-01-01 00:00:00부터 2037년 까지 표현

- TIME
   시간을 표현하는 유형 [HH:MM:SS],   839:59:59 ∼ 833:59:59 까지 표현

- YEAR
   년도를 표현하는 유형[기본적으로 4자리로 사용],  1901년 ∼ 2155년


# 문자 컬럼 타입

- CHAR(M)
   고정길이 문자열 컬럼,   M의 범위는 0 에서 255까지.

- VARCHAR(M)
   가변길이 문자열 컬럼,   M의 범위는 0 에서 255까지.

- TINYBLOB 또는 TINYTEXT
   최대길이 255개의 문자를 저장

- BLOB 또는 TEXT
   최대 길이가 63535인 문자를 저장

- MEDIUMBLOB 또는 MEDIUMTEXT
   최대 길이가 16777215인 문자를 저장

- LONGBLOB 또는 LONGTEXT
   최대 길이가 4294967295(4G)인 문자를 저장


* 참고

  - BLOB(Binary Large Object의 약자
  - BLOB타입은 대소문자를 구분하고 TEXT타입은 대소문자를 구분하지 않는점이 틀림
  - MySQL 3.23.2이번 버전에서는 BLOB와 TEXT컬럼에는 인덱스를 만들수 없다
  - BLOB와 TEXT컬럼의 저장시에 문자열 됫부분의 공백이 제거되지 않는다.
  - BLOB와 TEXT컬럼은 DEFAULT를 지정할 수 없다.


# 숫자 컬럼 타입

- TINYINT
    -128부터 127 까지의 정수형 타입,   부호가 없는 정수 0∼255까지 지원

- SMALLINT
    -32768부터 32767 까지의 정수형 타입,  부호가 없는 정수 0∼65535까지 지원

- MEDIUMINT
    -8388608부터 8388607 까지의 정수형 타입,   부호가 없는 정수 0∼16777215까지 지원

- INT 또는 INTEGER
   -2147483648부터 2147483647까지의 정수형 타입,  부호 없는 정수 0∼4294967295까지 지원

- BIGINT
   -9223372036854775808 부터 9223372036854775807 까지의 정수형 타입
   부호 없는 정수 0∼18446744073709551615까지 지원

- FLOAT(M,D)
   단정도 부동 소수점 실수,   -3.402823466E+38 ∼ -1.175494351E-38
   그리고 1.175494351E-38 ~ 3.402823466E+38까지  M은 숫자 전체의 길이, D는 소수점 자리수를 의미

- DOUBLE(M,D)
   2 배 정밀도를 가진 부동 소수점 실수,   -1.79769313486231517E+308 ∼ 2.22507385850720E+308



◈ 제약조건


# AUTO_INCREMENT(Oracle : Sequence)

-- 생성예제
mysql>CREATE TABLE sal(
           sal_id INT NOT NULL AUTO_INCREMENT,
           name VARCHAR(30) NOT NULL);

- AUTO_INCREMENT로 지정된 컬럼 타입은 숫자형이어야 한다 .
- AUTO_INCREMENT는 하나의 테이블에 하나의 컬럼만 지정할 수 있음
- AUTO_INCREMENT로 지정된 컬럼은 반드시 키 또는 인덱스로 정의되어야 한다.


# NOT NULL : NULL값을 허락하지 않음

# PRIMARY KEY : 중복된 데이터를 허락하지 않음, NOT NULL 조건도 추가

# UNIQUE : 중복돈 데이터를 허락하지 않음, NULL값을 허락한다.

# DEFAULT value : 디폴트 값을 지정함    


-- 예제..
mysql>CREATE TABLE emp2(
            id INT(3) NOT NULL,
            name VARCHAR(30) NOT NULL
            sal INT(5) DEFAULT 0,
            loc VARCHAR(50),
            PRIMARY KEY(id, name));



◈ SELECT 문을 이용하여 테이블 생성하기

-- Syntax
mysql>CREATE TABLE new_table SELECT column_list FROM old_table WHERE condition;

-- 예제
mysql>CREATE TABLE emp3 SELECT * FROM emp WHERE deptno = 10;



◈ 테이블 변경하기(ALTER TABLE)


# 컬럼 추가
mysql>ALTER TABLE table_name ADD COLUMN column_name data_type [FIRST|AFTER column_name];


# 컬럼 삭제
mysql>ALTER TABLE table_name DROP COLUMN column_name;


# 컬럼 변경
mysql>ALTER TABLE table_name CHANGE COLUMN  old_column  new_column  new_column_data_type;


# 테이블명 변경
mysql>ALTER TABLE old_table_name RENAME AS new_table_name;


# Primary Key 변경
mysql>ALTER TABLE table_name ADD PRIMARY KEY (column_list);

mysql>ALTER TABLE table_name DROP PRIMARY KEY;



◈ Database 백업


# mysqldump
mysqldump -uscott -ptiger scott > test.sql


# BACKUP TABLE : 테이블을 데이터 파일로 백업함
mysql>BACKUP TABLE table_name[,tbl_name] TO '/path/directory'


# RESTORE TABLE : BACKUP TABLE로 백업한 데이터를 복구한다.  
mysql>RESTORE TABLE table_name[,tbl_name] FROM '/path/directory'



◈ 참고 (Oracle => MySQL 비교)


# NVL => IFNULL


# SELECT SYSDATE FROM DUAL => SELECT NOW();


# TO_CHAR => SELECT CAST(NOW() AS CHAR)

mysql은 CAST(expression AS data_type)또는 CONVERT(expression,type)로 형변환

oracle : SELECT TO_CHAR(SYSDATE,'RRRR-MM-DD') credate FROM DUAL
mysql : SELECT CAST(DATE_FORMAT(now(),'%Y-%m-%d') AS CHAR) credate;


# DECODE => CASE 예제..
SELECT CASE week WHEN 1 THEN '일요일' WHEN 2 THEN '월요일'
                  WHEN 3 THEN '화요일' WHEN 4 THEN '수요일'
                  WHEN 5 THEN '목요일' WHEN 6 THEN '금요일'
                  WHEN 7 THEN '토요일' END week
FROM STORM_COUNTER
WHERE year = 2002

이 포스트를..

덧글 쓰기 엮인글 쓰기

[펌] MySQL 메뉴얼 MYSQL

2005/12/20 00:47

복사 http://blog.naver.com/meetjava/100020441564

출처 카페 > 성공하는 프로그래머 | 울트라맨
원문 http://cafe.naver.com/sprogrammer/101

       ## 원본 : mysql 3.21 Reference Manual PostScript 매뉴얼  ###

5, 6, 9, 10, 11, 12, 13, 15, 17, 18장 번역 : 문태준(taejun@hitel.net)
18장 중 C-API 부분 번역 : 권용찬 (golmong@cre.co.kr)


이메일 : taejun@hitel.net, taejun@taejun.pe.kr
웹 : http://taejun.pe.kr (Mysql 관련 자료가 모아져 있습니다)


                            - 차         례 -


5. mysql의 표준 호환성                                       
     5.1 mysql의 ANSI SQL92 확장부분                      
     5.2 MYSQL에서 빠진 기능                              
          5.2.1 Sub-selects                                   
          5.2.2 SELECT INTO TABLE                       
          5.2.3 트랜잭션(Transactions)                        
          5.2.4 저장 프로시저와 트리거                        
          5.2.5 외래키(Foreign Keys)                         
               5.2.5.1 외래키를 사용하지 않는 이유            
          5.2.6 뷰                                            
          5.2.7 `--'을 사용한 주석                            
     5.3 Mysql이 따르고 있는 표준은 무엇인가?               
     5.4 BLOB 와 TEXT 타입의 제한                        
     5.5 COMMIT-ROLLBACK없이 어떻게 대치할 수 있을까? 


6. Mysql 접근 권한 시스템                                   
     6.1 권한 시스템이란 무엇인가?                           
     6.2 mysql 서버에 접속하기                              
          6.2.1 비밀번호의 보안 유지                         
     6.3 mysql에서 제공하는 권한                            
     6.4 권한 시스템 작동 방법                               
     6.5 접근 제어,                                          
     6.6 접근 제어, 2단계 : 요청 인증                         
     6.7 권한 변경시 적용 방법                               
     6.8 초기 mysql 권한설정                                
     6.9 mysql에 새로운 사용자 권한 추가하기                
     6.10 비밀번호 설정 방법                                 
     6.11 접근 거부 에러가 나는 이유                         
     6.12 크랙커에 대비하여 mysql을 안전하게 하는 방법      


7. MySQL language reference.                               
     7.1 Literals: how to write strings and numbers          
          7.1.1 Strings                                      
          7.1.2 Numbers                                     
          7.1.3 Hexadecimal values                           
          7.1.4 NULL values                                 
          7.1.5 Database, table, index, column and alias names
               7.1.5.1 Case sensitivity in names              
     7.2 Column types                                       
          7.2.1 Column type storage requirements             
          7.2.2 Numeric types                               
          7.2.3 Date and time types                          
          7.2.4 String types                                  
          7.2.5 Numeric types                               
          7.2.6 Date and time types                          
               7.2.6.1 Y2K issues and date types             
               7.2.6.2 The DATETIME, DATE
                                    and TIMESTAMP types 
               7.2.6.3 The TIME type                        
               7.2.6.4 The YEAR type                       
          7.2.7 String types                                 
               7.2.7.1 The CHAR and VARCHAR types      
               7.2.7.2 The BLOB and TEXT types           
               7.2.7.3 The ENUM type                      
               7.2.7.4 The SET type                        
          7.2.8 Choosing the right type for a column         
          7.2.9 Column indexes                              
          7.2.10 Multiple-column indexes                      
          7.2.11 Using column types from other database engines
     7.3 Functions for use in SELECT and WHERE clauses    
          7.3.1 Grouping functions                             
          7.3.2 Normal arithmetic operations                    
          7.3.3 Bit functions                                   
          7.3.4 Logical operations                              
          7.3.5 Comparison operators                           
          7.3.6 String comparison functions                     
          7.3.7 Cast operators                                 
          7.3.8 Control flow functions                          
          7.3.9 Mathematical functions                         
          7.3.10 String functions                               
          7.3.11 Date and time functions                       
          7.3.12 Miscellaneous functions                        
          7.3.13 Functions for use with GROUP BY clauses     
     7.4 CREATE DATABASE syntax                         
     7.5 DROP DATABASE syntax                            
     7.6 CREATE TABLE syntax                             
          7.6.1 Silent column specification changes              
     7.7 ALTER TABLE syntax                               
     7.8 OPTIMIZE TABLE syntax                            
     7.9 DROP TABLE syntax                                
     7.10 DELETE syntax                                     
     7.11 SELECT syntax                                     
     7.12 JOIN syntax                                         
     7.13 INSERT syntax                                     
     7.14 REPLACE syntax                                    
     7.15 LOAD DATA INFILE syntax                        
     7.16 UPDATE syntax                                    
     7.17 USE syntax                                        
     7.18 FLUSH syntax (clearing caches)                     
     7.19 KILL syntax                                       
     7.20 SHOW syntax (Get information about tables, columns,...)
     7.21 EXPLAIN syntax (Get information about a SELECT)     
     7.22 DESCRIBE syntax (Get information about columns)      
     7.23 LOCK TABLES/UNLOCK TABLES syntax             
     7.24 SET OPTION syntax                                 
     7.25 GRANT and REVOKE syntax                        
     7.26 CREATE INDEX syntax                              
     7.27 DROP INDEX syntax                                
     7.28 Comment syntax                                     
     7.29 CREATE FUNCTION/DROP FUNCTION syntax       
     7.30 Is MySQL picky about reserved words?               


8 MySQL Tutorial                                             
     1. 서버에 연결하기/연결끊기                               
     2. 질문 하기(Entering Queries)                             
     3. 데이터 베이스 만들고 사용하기                          
          3.1 데이터베이스 만들고 선택하기                     
          3.2 테이블 만들기                                    
          3.3 테이블에 자료를 넣어 보자.                       
          3.4 테이블로부터 정보를 검색해 보자.                 
               3.4.1 모든 데이터를 검색하자.                    
               3.4.2 주어진 조건에 맞는 특정 행만을 검색해 보자.
               3.4.3 특정한 열 선택하기                        
               3.4.4 행 정열하기                               
               3.4.5 날짜 계산MySQL은 날짜를 다루는
                                     몇가지 함수를 제공해 준다.
               3.4.6 null 값에 대해 NULL값은 특별한 값이다.    
               3.4.7 패턴 일치                                 
               3.4.8 행수 세기                                 
          3.5 테이블 여러개 사용하기                           
          3.6 배치 모드(일괄 처리 모드)로 사용하기             


9. mysql 서버 기능(functions)                                  
     9.1 mysql에서 지원하는 언어                             
          9.1.1 데이터와 정열에 사용하는 문자 셋              
          9.1.2 새로운 문자셋 추가                            
          9.1.3 멀티바이트 문자 지원                          
     9.2 업데이트 로그                                        
     9.3 mysql 테이블 최대 크기                              

10. mysql의 최대 성능 향상 방법                              
     10.1 버퍼 크기 조정                                      
     10.2 메모리 사용 방법 <메모리 최적화>                   
     10.3 속도 향상에 영향을 미치는 컴파일/링크 방법          
     10.4 How MySQL uses indexes                           
     10.5 WHERE 문에서 최적화하기                          
     10.6 테이블 열고 닫는 방법                              
          10.6.1 데이터베이스에서 많은 수의 테이블을 만들때의 단점
     10.7 많은 테이블을 여는 이유                               
     10.8 데이터베이스와 테이블에서 심볼릭 링크 사용            
     10.9 테이블에 락 거는 방법                                 
     10.10 테이블을 빠르고 작게 배열하는 방법                   
     10.11 INSERT 문에서 속도에 영향을 미치는 부분           
     10.12 DELETE 문에서 속도에 영향을 미치는 부분          
     10.13 mysql에서 최대 속도를 얻는 방법                   
     10.14 로우 포맷과 다른 점은 무엇인가?                    


11. mysql 벤치마크 스위트                                     


12. mysql 유틸리티                                            
     12.1 다양한 mysql 프로그램 개요                        
     12.2 텍스트 파일에서 데이터 입력(수입?)하기            
     12.3 mysql 압축 읽기 전용 테이블 생성기                


13. 테이블 유지보수 및 파손 복구에 isamchk 사용하기         
     13.1 isamchk 명령어 사용법                             
     13.2 isamchk 메모리 사용법                             
     13.3 테이블 유지보수 설정                              
     13.4 테이블 정보 얻기                                  
     13.5 파손 복구에 isamchk 사용하기.                       
          13.5.1 에러가 났을때 테이블 점검 방법                
          13.5.2 테이블 복구방법                               
          13.5.3 테이블 최적화                                 


14 Adding new functions                                      
     14.1 Adding a new user-definable function                
          14.1.1 UDF calling sequences                        
          14.1.2 Argument processing                          
          14.1.3 Return values and error handling              
          14.1.4 Compiling and installing user-definable functions
     14.2 Adding a new native function                       


15. mysql ODBC 지원                                          
     15.1 MyODBC 를 지원하는 운영체제                       
     15.2 MyODBC에 문제가 있는 경우                         
     15.3 MyODBC 와 잘 작동하는 프로그램                    
     15.4 ODBC 관리자 프로그렘 설정 방법                     
     15.5 ODBC에서 AUTO_INCREMENT 컬럼의 값 가져오기   


17. 일반적인 문제 해결 방법                                    
     17.1. 데이터베이스 복사(복제?)                             
     17.2 데이터베이스 백업                                    
     17.3 같은 머신에서 여러개의 mysqld 서버 실행하기         


18 MySQL client tools                                         
     18.1 MySQL C API                                       
     18.2 C API datatypes                                     
     18.3 C API function overview                             
     18.4 C API function descriptions                           
          18.4.1 mysql_affected_rows()                          
          18.4.2 mysql_close()                                  
          18.4.3 mysql_connect()                               
          18.4.4 mysql_create_db()                              
          18.4.5 mysql_data_seek()                             
          18.4.6 mysql_debug()                                 
          18.4.7 mysql_drop_db()                               
          18.4.8 mysql_dump_debug_info()                      
          18.4.9 mysql_eof()                                    
          18.4.10 mysql_errno()                                 
          18.4.11 mysql_error()                                 
          18.4.12 mysql_escape_string()                         
          18.4.13 mysql_fetch_field()                            
          18.4.14 mysql_fetch_fields()                           
          18.4.15 mysql_fetch_field_direct()                     
          18.4.16 mysql_fetch_lengths()                         
          18.4.17 mysql_fetch_row()                            
          18.4.18 mysql_field_seek()                            
          18.4.19 mysql_field_tell()                            
          18.4.20 mysql_free_result()                            
          18.4.21 mysql_get_client_info()                        
          18.4.22 mysql_get_host_info()                         
          18.4.23 mysql_get_proto_info()                        
          18.4.24 mysql_get_server_info()                       
          18.4.25 mysql_info()                                  
          18.4.26 mysql_init()                                  
          18.4.27 mysql_insert_id()                             
          18.4.28 mysql_kill()                                  
          18.4.29 mysql_list_dbs()                              
          18.4.30 mysql_list_fields()                           
          18.4.31 mysql_list_processes()                         
          18.4.32 mysql_list_tables()                            
          18.4.33 mysql_num_fields()                           
          18.4.34 mysql_num_rows()                            
          18.4.35 mysql_ping()                                 
          18.4.36 mysql_query()                                
          18.4.37 mysql_real_connect()                          
          18.4.38 mysql_real_query()                            
          18.4.39 mysql_reload()                                
          18.4.40 mysql_row_seek()                             
          18.4.41 mysql_row_tell()                              
          18.4.42 mysql_select_db()                             
          18.4.43 mysql_shutdown()                            
          18.4.44 mysql_stat()                                  
          18.4.45 mysql_store_result()                           
          18.4.46 mysql_thread_id()                             
          18.4.47 mysql_use_result()                            
          18.4.49 What results can I get from a query?         
          18.4.50 How can I get the unique ID
                               for the last inserted row?       
          18.4.51 Problems linking with the C API              
          18.4.52 How to make a thread-safe client             


19. 타 DB와 비교                                              
     19.1 Mysql/mSQL 비교                                   
          19.1.1 mSQL 툴을 Mysql로 바꾸기                    
          19.1.2 mSQL 과 Mysql  클라이언트/서버
                                    통신 프로토콜 차이점      
          19.1.3  How mSQL 2.0 SQL syntax differs from Mysql
     19.2 PostgreSQL과 Mysql 비교                          



=======================================================================


                            - 내         용 -


5. mysql의 표준 호환성

5.1 mysql의 ANSI SQL92 확장부분

 mysql에는 다른 sql 데이터베이스에서 찾을 수 없는 확장된 부분이 있다.
이런 부분을 사용하는 경우 주의해야 한다. 왜냐면  mysql에서 사용한  코
드가 다른  SQL 서버에 포팅할 수 없을 수도 있기 때문이다. 어떤  경우에
는 /*! ... */ 형식의  주석문을 사용한 MYSQL 확장을 이용해  포팅가능한
코드를 만들 수 있다. 예를 들어보자:

SELECT /*! STRAIGHT_JOIN */ col_name from table1,table2 WHERE ...

MYSQL의 확장 부분은 다음과 같다:
- 필드타입 MEDIUMINT, SET, ENUM , 그리고 다른 BLOB 와 TEXT 타입.
- 필드속성 AUTO_INCREMENT, BINARY, UNSIGNED and ZEROFILL.
- 모든 문자열 비교는 기본적으로 대소문자를 구별하지 않으며  현재의 문
자셋(기본적으로 ISO-8859-1 Latin1)에 의해 정렬 순서가 결정된다.  이것
을 원하지 않으면 컬럼을 BINARY  속성으로 정의해야 하며 이런  경우에는
mysql 서버 호스트가 사용하는 ASCII 순서에 따라 문자열을 비교한다.

- MYSQL은 데이터베이스를 디렉토리로 만들고 테이블은 파일이름으로 만든
다. 이것은 두가지를 함축하고 있다:

        ㅇ 파일이름의 대소문자를 구별하는  (대부분의  유닉스 시스템.
리눅스도        마찬가지겠지용~) 운영시스템에서는 MYSQL의  데이터베이
스 이름과 테    이블 이름은 대소문자를 구별한다. 테이블 이름을 기억하
는데 문제가 있  다면 모든 것을 소문자로 만들자.

        ㅇ 테이블의 백업, 이름바꾸기, 옮기기, 삭제, 복사를  위해 표준
시스템  명령을 사용할 수 있다. 예를 들어 테이블의 이름을  바꾸려면 해
당하는  테이블의 `.ISD', `.ISM' and `.frm' 파일의 이름을 바꾸면 된다.

- SQL문에서 db_name.tbl_name 문을 이용하여 다른 데이터베이스의 테이블
에 접근할 수 있다. 일부 SQL 서버는 같은 기능을 지원하지만 이것을 User
space라고 부른다. MYSQL은 다음과 같은 TABLESPACES를 지원하지 않는다 :

create  table  ralph.my_table...IN my_tablespace.
- 수치(숫자형) 컬럼에서 LIKE를 사용할 수 있다.
- SELECT문에서 INTO OUTFILE 과 STRAIGHT_JOIN 을 사용할 수 있다.
7.11 [SELECT] 참고.
-EXPLIAN SELECT는 테이블에서 어떻게 조인이 되었는지에 대한  정보를 보
여준다.
- Use of index  names, indexes on  a subpart of a  field, and use  of
INDEX or KEY in a CREATE TABLE statement. 7.6 [CREATE TABLE] 참고.
- ALTER TABLE에서 CHANGE col_name,  DROP col_name 또는 DROP INDEX  를
사용한다. 7.7 [ALTER TABLE] 참고.
- ALTER TABLE 문에서 IGNORE 사용.
- ALTER TABLE 문에서 다중 ADD, ALTER, DROP or CHANGE 사용
- IF EXISTS 키워드를 이용한 DROP TABLE 사용.
- 한 테이블 이상에서 DROP TABLE 사용.
- LOAD DATA INFILE 사용. 대부분의 경우 이 문장은  오라클의 LOAD  DATA
INFILE과 호환된다. 7.15 [LOAD DATA INFILE] 참고.
(** 많은 양의 데이터를 한꺼번에 입력할 때 일일이 INSERT 문을 하는  것
보다 속도가 빠르다. **)
- OPTIMIZE TABLE 문 사용.
- 문자열은 ''' 만이 아니라 '"' 또는 ''' 로 닫을 수 있다.
- escape `\' 문자 사용.
- SET OPTION 문. 7.24 [SET OPTION] 참고.

- GROUP BY 부분에서 모든 컬럼을 사용할 필요가 없다. 이러한  기능은 일
반적인 질의가 아닌 특정한  질의에서 성능을 향상시킨다. 7.3.12  [GROUP
BY Functions] 참고.
(** ANSI SQL에서는 여러 테입믈을 이용하여 GROUP  BY를 사용할 때  사용
하고자 하는 모든 컬럼에 GROUP BY를 지정해 주어야 한다. 이렇게 되는 경
우 불필요한 연산이 수행될 수 있는데 MYSQL에서는 이러한 것을 없앤 것이
다. 7.3.12를 참고한다 **)

- 다른 SQL 환경을 사용했던 사용자를 위해 MYSQL은 많은  기능에서  알리
아스를 지원한다. 예를 들어 모든 문자  펑션은 ANSI SQL 과 ODBC  구문을
지원한다.

- MYSQL은 C 프로그래밍 언어와 같은  논리적인 OR 과 AND를 의미하는  ||
와 && 를 인식한다. MYSQL에서는 || 와  OR 는 같은 말이며 && 와 AND  도
마찬가지이다. 이러한 미묘한 구문때문에 MYSQL은 string  concatenation
(문자열 연관, 연결?)을 위한 ANSI SQL 오퍼레이터인 || 을 지원하지 않는
다. 대신 CONCAT()를 사용한다. CONCAT() 는 많은 인자가 있어서 MYSQL 에
서 || 오퍼레이터의 사용을 변환하기 쉽다.

MySQL understands the || and && operators to mean logical OR and AND,
as in the C programming language. In MySQL, || and OR are   synonyms,
as are && and AND. Because of this nice syntax, MySQL doesn't support
the ANSI  SQL operator  || for  string concatenation;  use  CONCAT()
instead. Since CONCAT() takes any  number of arguments, it's easy  to
convert use of the || operator to MySQL.

- 포팅이 가능하도록 SQL  CODE에서 STRAIGHT_JOIN같은 MYSQL만의  키워드
사용을 지원하기 위해 이런 키워드를 /* */ 주석안에 내장할 수  있다. 주
석문안의 내용은 '!' 로 시작한다. 이런 경우  MYSQL에서는 주석문을 다른 
MYSQL 구문과 같이  해석하지만 다른  SQL 서버에서는 이러한 확장기능을
사용하지 않고 건너띌 수 있다. 예를 보자:

SELECT /*! STRAIGHT_JOIN */ * from table1,table2 WHERE ...

- 다음의 기능이나 명령 사용:
        ㅇ   CREATE   DATABASE   or   DROP  DATABASE.   7.4   [CREATE   
              DATABASE] 참고.
        ㅇ MOD() 대신에 % 사용. %는 C 프로그래머를 위해 지원하며 또한
        PostgresSQL과의 호환성을 위해 지원한다.
        ㅇ 컬럼 문에서 =, <>, <= ,<,  >=,>, <<, >>, AND, OR, LIKE  사
        용.
        ㅇ LAST_INSERT_ID(). 18.4.49 [mysql_insert_id()] 참고.
        ㅇ REGEXP or NOT REGEXP.
        ㅇ 하나나 하나 이상의 인자를 사용한 CONCAT() 나 CHAR(). MYSQL
        에서 이러한 펑션은 여러개의 인자를 가질 수 있다.
        ㅇ   BIT_COUNT(),   ELT(),   FROM_DAYS(),   FORMAT(),   IF(),   
           PASSWORD(),    ENCRYPT(),     PERIOD_ADD(),    PERIOD_DIFF(),     
           TO_DAYS(), or WEE-DEC-OSF1, KDAY().
        ㅇ 서브스트링을 없애는 데 TRIM()  사용.(Use of TRIM() to trim
        substrings) ANSI SQL 에서는 단일 문자 제거만 지원한다.
       
        ㅇ 그룹 펑션에서 STD(), BIT_OR() and BIT_AND()
        ㅇ DELET + INSERT 대신 REPLACE 사용. 7.14 [REPLACE] 참고.
        ㅇ FLUSH flush_option 명령.
5.2 MYSQL에서 빠진 기능
 다음의 기능들은 현재 버전의 MYSQL에 빠져있다. 다음 버전에서의 우선권
을 확인하라면 MYSQL TODO 목록을 참고하자.
(http://www.mysql.com/Manual_split/manual_Todo.html).
이것이 가장 최신 버전의 TODO 목록이다. 부록 F [TODO] 참고.

5.2.1 Sub-selects
다음은 Mysql에서 작동하지 않는다:

SELECT * FROM table1 WHERE id IN (SELECT id FROM table2);

Mysql에서는 오직 INSERT ... SELECT ... and REPLACE ... SELECT  ... 만
을 지원한다. 독립적인 서브-select 문은 3.23.0에서 아마도 사용할 수 있
을 것이다.  그대신 현재 IN() 펑션을 사용할 수 있다.

5.2.2 SELECT INTO TABLE
Mysql은 아직  SELECT ...  INTO TABLE  ....을 지원하지   않는다. 현재,
Mysql은 오직 SELECT ... INTO OUTFILE ..., 만을  지원하며 기본적으로는
동일하다.

5.2.3 트랜잭션(Transactions)
트랜잭션은 지원되지 않는다.  Mysql은 곧 atomic(원자성?)  오퍼레이션을
지원할  것이며 atomic 오퍼레이션은 rollback이 없는 트랜잭션과  비슷하
다. atomic 오퍼레이션을 사용하며 insert/select/모든 명령의 그룹을  실
행할 수 있으며 어떤 스레드도  충돌하지 않을 수 있도록 보장해준다.  이
문맥에서 일반적으로 롤백(rollback)은  필요없다. 현재  LOCK TABLES  와
UNLOCK TABLES 명령을  이용하여 다른  스레드가  충돌하는 것을 막을 수
있다. 7.23 [Lock Tables] 참고.

5.2.4 저장 프로시저와 트리거
저장 프로시저는 서버에서 컴파일되고 저장될 수 있는 SQL 명령 세트이다.
이런 기능이 수행되면 클라이언트는 전체 질의를 다시 할 필요가  없고 또
한 저장 프로시저를 참조할 수  있다. 이런 기능이 있으면 질의는  한번만
해석되고 서버와 클라이언트간의 주고받아야 하는 데이터가 줄어들므로 속
도가 향상된다. 또한 서버의  펑션 라이브러리를 가짐으로서 개념적인  단
계를 향상시킬 수 있다. (??? You  can also raise the conceptual  level
by having libraries of functions in the server.)


트리거는 특별한 이벤트가 발생했을  때 생기는 저장 프로시져이다.  예를
들어 트랜잭션 테이블에서 레코드가 삭제되고 모든 트랜잭션이  지워질 때
상응하는 테이블을 삭제할 수 있는 저장 프로시저를 설치할 수 있다.

앞으로는 저장 프로시저를 지원할 예정이지만 트리거는 아니다.  트리거는
필요하지 않은 경우에도 사용될 수 있어서 일반적으로 속도가 느려진다.

언제 저장 프로시저를 사용하게  될지는  앞으로 Mysql에 추가할  목록인
부록 F를 참고하자.(The TODO)

(** 전반적으로 트랜잭션 처리와 트리거 등은 데이터베이스의 속도를 저하
시킵니다. Mysql은 이렇게 속도에 영향을  미칠 수 있는 부분을  제거하여
빠른 속도를 내는 것이지요. 이러한 부분이 자기가 사용하는 데이터베이스
에서 얼마나 중요한가 판단을 해 보아야 할 것입니다. 보통 소형 DBMS에서
는 회복과 병행수행을 지원하지 않는 경우가 많다. 즉  병행수행은 발생하
지 않으며, 회복은 사용자의 문제로 생각한다. 그러므로  사용자가 데이터
베이스의  예비 사본을 준비하며, 고장이  발생하면 작업을 다시 해야 한
다. 트리거같은 경우는  자료의 무결성을 보장하기  위해 필요한  것이다.
**)

5.2.5 외래키(Foreign Keys)
SQL 문에서 외래키는 테이블을 조인할 때 사용하지 않지만 대부분 참조 무
결성을 확인할 때 사용한다. SELECT 문에서 다중 테이블에서  자료를 가져
오길 원하면 테이블을 조인해서 처리할 수 있다.

SELECT * from table1,table2 where table1.id = table2.id

7.12 [JOIN] 참고.

Mysql에서 외래키(FOREIGN KEY) 문은 다른 SQL 제품의 CREATE  TABLE 명령
과의 호환성 때문에 존재한다: 외래키는 아무것도 하지 않는다. ON DELETE
...  가 없는 FOREIGN KEY 구문은 대부분 문서적인 목적으로 사용한다. 일
부 ODBC  애플리케이션은 이것을 자동적인 WHERE 문을 만들 때 사용할  것
이다. 그렇지만 이것은 대부분  생략(무시)하고 넘어가기 쉽다.  외래키는
때로는 제약조건 체크(constraint check)로 사용을 하지만 이러한  체크는
데이터가 테이블에 정확한 순서로  들어갈때는 불필요하다. Mysql은  일부
애플리케이션에서 외래키가 존재하는 것을 필요로 하기 때문에(제대로  작
동하든 안하든 상관없이) 지원하는 것일 뿐이다.
Mysql에서 외래키를 가진 테이블의 레코드를 삭제할 때 애플리케이션에 적
절한 DELETE 문을 추가하여 ON  DELETE ... 가 수행되는 것을  막음으로써
문제를 해결할 수있다. 경험상 이렇게 하는 것이 외래키를  사용하는 것과
같이 빠르며(어떤  경우에는 더 빠름) 포팅하기가 더 좋다.

가까운 시일안에  우리는 외래키  기능을 확장할  것이다. 그래서  최소한
mysqldump와 ODBC에서 정보가 저장되고 검색할 수 있도록 할 것이다.

5.2.5.1 외래키를 사용하지 않는 이유

외래키를 사용할 때 어디에서 출발해야 할지 모르는 많은 문제가 있다:

- Foreign key들은 상황을 매우 복잡하게 만든다. 왜냐하면,  foreing key
의 정의가 database에담겨야 하고, foreign key를 구현하는  것은 "자연스
런" File 사용법(data file들을 옮기고, 복사하고, 삭제하는  등...)을 제
한한다.
(** 문태준님 역주: 번역이 이상한데 외래키가 있으면 참조 무결성 규칙을
위해 여러 가지 보상  연산을 하게 된다. 이것을 뜻하고 있는 듯하다. **)

- INSERT 와 UPDATE 문은 속도에 많은 영향을  끼친다. 그리고 이런  경우
보통 올바른 순서로 올바른 테이블에 레코드를 삽입하기 때문에 대부분 모
든 외래키 체크는 사용할 필요 없다.

- 한쪽의 영향이 전체 데이터베이스에 연쇄 작용을 하기  때문에 테이블에
서 업데이트를 할 때 매우 많은 테이블에서 락을 사용해야 한다.  한 테이
블에서 먼저 레코드를 삭제하고 그후에 다른 테이블에서  레코드를 삭제하
는 것이 훨씬 빠르다.
- Table를 완전히 지우고, (backup이나 새로운 source로부터) 모든 record
들을 다시 복구하는 방법으로 Table을 복구할 수 없다.

- Foreign key를 사용한다면, table을 dump(backup)하고 (그  dump한 자료
를) restore하는 데 있어 그 일련의 순서를 적절하게 지켜야 한다.
- 각각의 table의 정의가 쓸모있고 적절하더라도, (각 Table들이 상호참조
하게  된다면)   단순한  create문으로는  재생성이   불가능한  circular
definition(순환정의)가 쉽게 발생한다.

(역자주: A라는 Table이 B의 자료를 참조하는 foreign key를 담고 있고, B
는 C에 대한 foreign key를, C는 A에 대한 foreign key를  담도록 table이
구성된다면     한번에 A,B,C table을 생성할 수 없다. A,B,C를 만든다음
각각의 foreign key를 지정해 주는 방법을 쓰게 된다.)
외래키의 좋은 점은 단지 다음와 같다. ODBC와 특정한 다른 클라이언트 프
로그램에서 어떻게 테이블이 연결되어 있는지를 볼 수 있고 연결 다이어그
램을 보는데 사용하며 애플리케이션을 만드는데 돕는 점이다.

(역자주: 이 글은 foreign key에 대해서 매우 비판적이다. 하지만, 이것은
foreign key의 일부 기능일뿐이다. 무엇보다도 client가 각  Table내 DATA
의 연관관계에 대해서 빼먹었는지에  대해서 일일이 신경쓰지 않게  하고,
항상 자료의 무결성(정합성[?])을 보장한다는 것은 매우 중요하다.  특히,
Table이 1~20개가 아닌 100단위가 넘어간다면, client에서 일일이  신경쓰
며 programing하는 것도 힘들지만, debugging도 예상보다 힘들어진다.  각
table간의 연결관계를 잘 문서화한다면 programmer들이 foreign key로  고
통받기 보다는 관련  Table을 check해야 되는  수고를 덜게된다.  foreign
key가 기피되는 주된 이유는 table내의  Data를 수정하는 것이 쉽지  않기
때문이다. 현장  실무자의 논리에  어긋나는 요청을  처리하는 데  있어서
foreign key만큼 거추장스런 놈도 없으리라. )

Mysql에서는 곧 외래키 정의를 저장할 수 있도록 해서  클라이언트가 어떻
게 원래의 연결이 만들어졌는지에 대해서  질문하고 답을 받을 수  있도록
할 것이다. 현재의 '.frm' 파일  포맷은 아직 이것을 지원하지 못하고  있
다.

5.2.6 뷰

Mysql은 뷰를 지원하지 않는다. 그렇지만 TODO(이후 개선 목록)에 있다.
MySQL doesn't support views, but this is on the TODO.

5.2.7 `--'을 사용한 주석
일부 다른 SQL 데이터베이스는 '--' 로 주석을 시작한다. mysql 명령 라인
도구가 '--'로 시작하는 모든 줄을 제거할 지라도 Mysql은 '#'을  주석 문
의 시작으로 사용한다.  사용자는 또한  C 명령  스타일인 /*  this is  a
comment */ 를 mysql에서 사용할 수 있다. 7.28 [Comment] 참고.

Mysql은 '--'를 지원하지 않을 것이다; '--'은 퇴보한 주석문 형태로 자동
으로  생성되는 SQL 질의에서 많은 문제를 발생시킨다. 다음의 예제를  보
자. 우리는 자동적으로 payment를  !payment! 의 값으로 입력하도록  하고
있다 :

UPDATE tbl_name SET credit=credit-!payment!

payment의 값이 음수일 때 어떤 일이 생길 것이라 생각하는가?
1--1은 합당한 SQL문이기 때문에  '--'가 주석문의 시작을 의미하는  것을
꺼리는 것이다.

'--' 주석을 포함하는 텍스트 파일의 SQL 프로그램을 가졌다면  다음과 같
이 사용해야 한다:

shell> replace " --" " #" < text-file-with-funny-comments.sql \
        | mysql database

instead of the normal(정상적인 경우 대신???):

shell> mysql database < text-file-with-funny-comments.sql

명령 파일로 '--' 주석을 '#' 주석으로 바꿀 수 있다:

shell> replace " --" " #" -- text-file-with-funny-comments.sql

다음의 명령으로 원래대로 돌려놓자:

shell> replace " #" " --" -- text-file-with-funny-comments.sql

(** 일부  SQL에서 사용하는  --  주석문에서  문제가 생길  수 있으므로  
MYSQL에서는 #을 주석문으로 사용한다는 말이다 **)

5.3 Mysql이 따르고 있는 표준은 무엇인가?
Entry level SQL92. ODBC level 0-2.

5.4 BLOB 와 TEXT 타입의 제한
BLOB 나 TEXT 필드에서 GROUP BY 나 ORDER BY를 사용하길 원하면  그 필드
를 고정   길이 객체로  만들어야 한다.   이렇게 하는  표준적인  방법은
SUBSTRING  펑션을 사용하는 것이다. 예를 보자:

mysql> select comment from tbl_name order by SUBSTRING(comment,20);

이렇게 하지 않으면  정렬할 때 오직  첫 번째 max_sort_lengths  (기본값
=1024)만을 고려된다.

BLOB 와 TEXT 는 기본값을 가질 수 없으며 또한 언제나 NULL  컬럼일 것이
다.

BLOB and TEXT cannot have DEFAULT values and will also always be NULL
columns.

5.5 COMMIT-ROLLBACK없이 어떻게 대치할 수 있을까?
Mysql은 COMMIT-ROLLBACK 을 지원하지 않는다. 문제는  COMMIT-ROLLBACK을
효과적으로 다루기 위해서는 Mysql에서  현재 사용하는 것과 완전히  다른
테이블 설계가 필요하다는 것이다. Mysql은 또한 테이블을 자동  클린업하
는 추가적인 스레드와 더 많은 디스크를 사용할 수 있는 기능이 필요하다.
이러한 기능은 현재보다 mysql을 2-4배 느리게 만든다.  Mysql은 대부분의
다른 SQL 데이터베이스보다 훨씬 더  빠르다. (전형적으로 최소 2-3대  빠
름) 이러한 이유는 Mysql에 COMMIT-ROLLBACK이 없기 때문이다.

당분간은 우리는 SQL 서버 언어의 성능을 향상시키는데 더 주력할 것이다.
대부분
COMMIT-ROLLBACK 기능이 정말로 필요한 경우는 드물다. 또한  이렇게 하는
것이 더 좋은 성능을 낼 수 있다.

일반적으로 트랜잭션이 필요한 루트는 LOCK TABLES를 사용해 코드를 짤 수
있다. 또한 레코드를 업데이트할 때 커서를 사용할 필요가 없다.

우리는 트랜잭션과 커서를 TODO에  넣었지만 우선권이 높은 것은  아니다.
이러한 기능을 수행한다면 CREATE TABLE  의 옵션으로 될 것이다.  이것은
옵션으로 지정한 테이블에서만 작동하며 그 테이블은 느리게  될 것이라는
것을 의미한다.

우리는 100% 보편적인 데이터보다는  정말로 빠른 데이터베이스가  필요하
다.
COMMIT-ROLLBACK 기능을 수행하더라도 속도에 손상이 없다면 우리는  그것
을 지원할 것이다. 당분간은 더 중요하게 해야할 일들이 많이  있다. 우리
가 어떤 것에 우선권을 두고 있는지는 TODO를 참고하자. 상위 단계의 지원
을 받는 고객은 이것을 바꿀 수 있으며 우선권이 변경될 수도 있다.

현재의 문제는 실제로 ROLLBACK  이다. 롤백없이 LOCK TABLES을  이용하여
여러 종류의 COMMIT를 사용할 수 있다. 롤백을 지원하기 위해  Mysql은 업
데이트가 된 모든 예전 레코드를 저장하고 롤백이 일이났을 때  시작 시점
으로 돌아갈 수 있도록 바꾸어야 한다. 예를 들어 이러한 것은  전혀 어렵
지 않다.(현재의 isamlog 는  이런 경우를 위해  사용할 수 있다)  그러나
ALTER/DROP/CREATE TABLE에서 롤백을 수행하는 것은 무척 어렵다.
롤백 사용을 피하기 위해 다음의 전략을 사용할 수 있다:

1. 접근하기 원하는 모든 테이블에 락을 사용. LOCK TABLES ...
2. 조건 테스트(Test conditions)
3. 모든 것이 제대로 된다면 업데이트를 한다.
4. UNLOCK TABLES

일반적으로 가능한 롤백을 이용해  트랜잭션을 사용하는 것보다는  이러한
방법이 훨씬 더 빠르다. 그렇지만  항상 사용가능한 것은 아니다.  이러한
방법으로 해결할 수 없는 유일한 상황은 업데이트중 누군가가 스레드를 죽
였을 때이다. 이런 경우 모든 락은 해제가 된다. 그렇지만  업데이트의 일
부는 실행되지 않을 것이다.

물론 단일 오퍼레이션에서 레코드를 업데이트하는 펑션을 사용할 수 있다.
다음의 테크닉을 사용하며 매우 효율적인 애플리케이션을 만들 수 있다:

- 현재 값과 관련되어 있는 필드를 수정
- 실제로 변화가 생겼을때만 필드를 업데이트

예를 들어, 어떤 고객 정보를 업데이트 할 때 오직 바뀐  데이터만 업데이
트를 한다. 그리고

For example, when we are doing updates on some  customer information,
we update only the customer data that have changed and test only that
none of the changed  data, or data that  depend on the changed  data,
have changed compared to the original row.

변화된 데이터의 테스트는 UPDATE 문에서 WHRE 절을 사용하여 할 수 있다.
레코드가 업데이트되지 않았다면 클라이언트에  다음과 같은 메시지를  준
다:

"당신이 바꾼 데이터 일부가 다른 사용자에 의해 바뀌었습니다". 그러고나
서 우리는 윈도우에서 예전의 레코드와 현재의 레코드를  비교하여 보여준
다. 그러면 사용자는 어떤 고객 정보 레코드를 사용할지 결정할 수 있다.

이렇게 하면 "컬럼 라킹"과 비슷하다. 그렇지만 실제로는 더 빠르다. 왜냐
하면 현재의 값과 관련되어 있는 값의 컬럼만 업데이트하기  때문이다. 이
렇나 전형적인 업데이트문은 다음과 비슷할 것이다:


UPDATE tablename SET pay_back=pay_back+'relative change';

UPDATE customer
        SET
                customer_date='current_date',
                address='new address',
                phone='new phone',
                money_he_owes_us=money_he_owes_us+'new_money'
        WHERE
                customer_id=id   AND    address='old   address'   AND
phone='old phone';

지금 보듯이   이렇게 하면   매우 효율적이며  설사  다른  클라이언트가
pay_back   이나 money_he_owes_us 컬럼의 값을  바꾸었을 때라도 제대로
작동한다.

대부분의 경우, 사용자는 테이블에서 유일한 값(identifiers)을  관리하기
위해 롤백과 테이블 락을 사용하고 싶어한다. 이것은 AUTO_INCREMENT 컬럼
과 SQL  LAST_INSERT_ID() 펑션, 또는 mysql_insert_id() 의 C API 펑션을
사용하여 더욱 효율적으로 사용할 수 있다. 18.4.49  [mysql_insert_id()]
참고.

TcX에서는 언제나 이런 문제를 해결할 수 있기 때문에 결고  low-level 락
을 필요로 하지 않는다. 어떤 경우에는 정말로 로우-락이  필요하다. 그렇
지만 이런 경우는 극소수이다. 로우-레벨 락을 원하면  테이블에서 플래그
컬럼을 사용할 수 있다. 다음과 같다:

UPDATE tbl_name SET row_flag=1 WHERE id=ID;

만약 row가 발견되고 row_flag가 원래의 row에서 이미 1이  아니라면 영향
을 받은 row의 숫자로서 1일 반환한다.

MySQL returns 1 fro the number of affected rows if the row  was found 
and row_flag wasn't already 1 in the original row.



6. Mysql 접근 권한 시스템

mysql 은 진보적이지만 비표준적인 보안/권한 시스템을 가지고 있다. 이번
장에서는 이것이 어떻게 작동하는지를 설명하고 있다.

6.1 권한 시스템이란 무엇인가?
Mysql 권한  시스템의  주요 기능은   데이터베이스에서 select,  insert,
update, delete 권한을 호스트의 사용자 이름과 관련짓는 것이다.

추가적인 기능에는 익명 사용자 기능과 LOAD DATA INFILE 과  관리자 오퍼
레이션과 같은 mysql만의 특수한 권한을 허용하는 부분이 포함되어 있다.

Mysql에서 인증을 목적으로 사용하는  사용자 이름은 유닉스 사용자  이름
(로그인 이름)이나 위도우 사용자 이름고는 전혀 관계가 없다는  것!을 기
억하자. 대부분 mysql 클라이언트는 mysql 사용자 이름으로 현재의 유닉스
사용자 이름을 사용하여 접속하려 할 것이다. 그렇지만 이건  오직 편의를
위해서이다. 클라이언트 프로그램은 -u  나 --user 옵션으로 지정한  다른
이름을 허용한다. 이것은 mysql 사용자 이름에 비밀번호를  설정하지 않으
면 데이터베이스의 보안에 문제가 생길 수 있다는 것을 의미한다. 어떤 이
름을 사용하여 서버에 접속하려고 하는 사람은 각 이름에 비밀번호가 설정
되어 있지 않다면 접속에 성공할 것이다.

유닉스 사용자  이름이 일반적으로  8글자로 제한되어  있는 것과  다르게
mysql 사용자 이름은 16글자까지 사용할 수 있다.

mysql 비밀번호는 유닉스의 비밀번호와 아무 관련이 없다.  유닉스 머신에
로그인할 때 사용하는 비밀번호와 데이터베이스에 접속할 때  사용하는 비
밀번호는 전혀 관련이 없다. 또한 mysql은 유닉스 로그인 프로세스에서 사
용하는 것과 다른 알고리즘으로 비밀번호를 암호화한다.

6.2 mysql 서버에 접속하기

mysql 클라이언트 프로그램은 일반적으로 연결 패러미터(매개 변수)가  필
요하다.: 연결할 호스트, 사용자 이름, 비밀번호. 예를 들어 mysql 클라이
언트는 다음과 같이 시작할 수 있다. (선택 인자는 [ ] 로 닫는다)

shell>; mysql [-h host_name] [-u user_name] [-pyour_pass]
-p와 뒤에 붙은 비밀번호 사이에는 공간이 없다는 것을 기억하자.

-h, -u, -p를 대체할 수 있는 형식으로는
--host=host_name, --user=user_name and --password=your_pass
이 있다.

mysql은 커맨드 라인에서 연결 매개변수가 빠져있을 때는 기본  값을 사용
한다. 기본 호스트 이름은 localhost 이고 기본 사용자 이름은  유닉스 로
그인 이름이다.(-p 가 빠져있으면 비밀번호는 사용하지 않는다) 그래서 만
약 유닉스 사용자 이름이 joe 라면 다음의 명령은 동일하다.:

shell> mysql -h localhost -u joe
shell> mysql -h localhost
shell> mysql -u joe
shell> mysql

다른 mysql 클라이언트도 비슷하게 작동한다.

유닉스 시스템에서 연결을 할 때 사용할 수 있는 기본 값이 있어서 클라이
언트 프로그램을 사용할 때마다 명령행에서 옵션을 사용하지 않아도 된다:

ㅇ 홈 디렉토리의 '.my.cnf' 설정 파일의 [client] 섹션에서  연결 변수를
설정할 수 있다. 파일에서 이와 연관된 섹션은 다음과 같다:
[client]
host=host_name
user=user_name
password=your_pass
4.14.4 [option files] 참고.

ㅇ  환경  변수를  사용하여  연결  변수를  지정할  수  있다.  호스트는
MYSQL_HOST 로 지정할 수 있다. Mysql  사용자 이름은 USER, LOGNAME,  또
는 LOGIN을 사용할 수 있다. (이러한 값들은 이미 유닉스 로그인 이름으로
설정되어  있을  것이다.  그러므로   바꾸지 않는게   좋다)  비밀번호는
MYSQL_PWD로 지정할 수 있다.(그렇지만 이것은 안전하지 않다;  다음 섹션
을 참고하자)

연결 변수가 여러 가지 방법을 지정되었다면 명령행에서 지정한 값이 설정
파일과 환경 변수로 설정한 것보다 우선권을 가진다. 또한 설정 파일의 값
이 환경 변수보다 우선권을 가진다.
6. 2. 1 비밀번호의 보안 유지

다른 사용자가 발견할 수 있게  비밀번호를 지정하는 방법은 권하지  않는
다. 클라이언트 프로그램을 실행할 때 비밀번호를 지정하는 방법은 아래와
같으며 각 방법마다 위험도를 같이 설명하였다:

ㅇ 명령행에서 -pyour_pass 또는 --password=your_pass 옵션 사용.  이 방
법은 편리하지만 위험한 방법이다.  비밀번호를 시스템 상황  프로그램(ps
등)을 통해  볼 수   있기 때문에 다른   사용자가 명랭행으로 볼  수  있
다.(mysql 클라이언트는 일반적으로 초기화되는 동안 명령행 인자를 0으로
덮어씌운다. 그렇지만 값을 볼 수 있는 짧은 틈이 여전히 있다)

ㅇ -p 또는 --password 옵션 사용(비밀번호 값을 지정하지는  않음). 이런
경우 클라이언트 프로그램은 터미널에서 비밀번호를 물어본다:

shell> mysql -u user_name -p
Enter password: ********

클라이언트는 비밀번호를 칠 때 터미널에서 '*' 문자를  보여준다. 그러므
로 다른 사용자가 비밀번호를 볼 수 없다. 다른 사용자가 볼  수 없으므로
명령행에서 비밀번호를 입력하는 것 보다 훨씬 더 안전하다.  그렇지만 이
방법은 비대화식의 스크립트로 클라이언트 프로그램을 사용하면  적절하지
않다.

ㅇ 설정 파일에 비밀번호 저장. 예를 들어 홈 디렉토리의  '.my.cnf' 파일
에서 [client] 섹션에 비밀번호를 지정할 수 있다.
[client]
password=your_pass

비밀번호를 '.my.cnf' 파일에 저장한다면 그 파일은 그룹이나 다른 사용자
가 읽기/쓰기를 할 수 없도록 해야  한다. 파일의 퍼미션이 400 이나  600
인지 확인하자.
4.14.4 [옵션 파일] 참고.

ㅇ 비밀번호를 MYSQL_PWD 환경 변수에 저장할 수 있다. 그렇지만  이 방법
은 정말로 위험하며 사용해서는 안된다. 일부 ps 프로그램은  실행 프로세
스의 환경변수를 보여주는 옵션이 있다; MYSQL_PWD에 설정을 하면 다른 사
람들이 쉽게 비밀번호를 볼 수 있다. 이런 기능의 ps가 없는 시스템일지라
도 프로세스 환경변수를 검색할 수 있는 방법이 없다고 생각하는  것은 현
명하지 못하다.

이중에서 가장 안전한 방법은 클라이언트 프로그램이 비밀번호를 요구하거
나 적절하게 보안이 된 '.my.cnf' 파일에 비밀번호를 지정하는 것이다.

6.3 mysql에서 제공하는 권한

권한과 관련된 정보는  mysql 데이터베이스의(데이터베이스 이름이  mysql
임) user, db, host, table_priv, columns_priv 테이블에 저장된다. mysql
서버는 시작할 때, 그리고 환경을 지정할 때(6.7 [권한 변경] 참고) 이 테
이블의 내용을 읽어들인다.

mysql에서 제공하는 권한을 설정할 때 사용하는 이름은  아래와 같다.테이
블의 컬럼 이름은 grant tables의 각 권한 및 권한이  적용되는 context와
연관되어 있다.

+-------------------+----------------+------------------------------+
|  Privilege (권한) |  Column (컬럼) |  Context (환경)              | 
+-------------------+----------------+------------------------------+
|  select           |  Select_priv   |  tables                      |
+-------------------+----------------+------------------------------+
|  insert           |  Insert_priv   |  tables                      |
+-------------------+----------------+------------------------------+
|  update           |  Update_priv   |  tables                      |
+-------------------+----------------+------------------------------+
|  delete           |  Delete_priv   |  tables                      |
+-------------------+----------------+------------------------------+
|  index            |  Index_priv    |  tables                      |
+-------------------+----------------+------------------------------+
|  alter            |  Alter_priv    |  tables                      |
+-------------------+----------------+------------------------------+
|  create           |  Create_priv   |  databases, table or indexs  |
+-------------------+----------------+------------------------------+
|  drop             |  Drop_priv     |  databases or tables         |
+-------------------+----------------+------------------------------+
|  grant            |  Grant_priv    |  databases or tables         |
+-------------------+----------------+------------------------------+
|  reload           |  Reload_priv   |  server administration       |
+-------------------+----------------+------------------------------+
|  shutdown         |  Shutdown_priv |  server administration       |
+-------------------+----------------+------------------------------+
|  process          |  Process_priv  |  server administration       |
+-------------------+----------------+------------------------------+
|  file             |  File_priv     |  file access on server       |
+-------------------+----------------+------------------------------+

select, insert, update, delete 권한은 데이터베이스의 테이블에서  레코
드에 대한 오퍼레이션을 할 수 있도록 허용한다.

SELECT 문은 오직 실제로 테이블에서 줄(레코드)를 가져올 때만 select 권
한이 필요하다. 서버의 데이터베이스에 접근 권한이 없는 경우라고 하더라
도 특정한 SELECT 문은  사용할 수 있다.  예를 들면 간단한 계산을  위해
mysql 클라이언트를 사용할 수 있다:

mysql> SELECT 1+1;
mysql> SELECT PI()*2;
index(인덱스) 권한은 인덱스를 생성하거나 제거할 수 있다.
alter 권한은 ALTER TABLE 을 사용할 수 있도록 한다.

create 와 drop 권한은 새로운 데이터베이스와 테이블을  생성하거나 존재
하는 데이터베이스와 테이블을 제거할 수 있도록 허용한다.

사용자에게 mysql  데이터베이스의 drop  권한을 허용하면,   그 사용자는
mysql 접근권한 정보가 저장된 데이터베이스를 없앨 수  있다는것!을 명심
하자.

grant 권한은 사용자가 가지고 있는 권한을 다른 사용자가 가질 수 있도록
허용한다.

file 권한은 LOAD DATA INFILE and SELECT ... INTO OUTFILE  문을 이용하
여 서버에 파일을 저장하고 읽을 수 있는 권한을 허용한다. 이러한 권한을
가진 사용자는 mysql 서버가 읽고 쓸 수 있는 파일을 읽고 쓸 수  있는 권
한이 허용된다.

나머지 권한들은 관리자 오퍼레이션에 사용되며 mysqladmin 프로그램의 기
능을 수행한다.

아래의 테이블은 각 관리자 권한에 따라 사용할 수 있는  mysqladmin 명령
을 보여준다:

+--------------------+----------------------------------------------+
|  Privilege (권한)  |  Command permitted to privilege holders      |
|                    |      (권한에 따라 허용되는 명령)             |
+--------------------+----------------------------------------------+
|  reload            |  reload, refresh, flush-privileges           |
|                    |  , flush-hosts, flush-logs, flush-tables     |
+--------------------+----------------------------------------------+
|  shutdown          |  shutdown                                    |
+--------------------+----------------------------------------------+
|  process           |  processlist, kill                           |
+--------------------+----------------------------------------------+

reload 명령은 서버가 grant 테이블을 다시 읽어 들인다.  refresh 명령은
모든 열린 테이블을 닫으며  로그 파일을 열고 닫는다.  flush-privileges
는 reload 명령과 동의어이다. 다른 flush-* 명령은 refresh 와 비슷한 기
능을 수행한다. 그러나 범위에 제한이 있으며 어떤 경우에는 더 선택할 만
하다. 예를 들어  로그 파일만  닫고 다시 열고자  한다면 flush-logs  가
refresh보다 더 나은 선택이다.

(** flush의 옵션으로는 호스트, 로그  파일, 권한 설정, 테이블,  status
variables 설정 변수가 있다. SQL 문에서 또는 mysqladmin  유틸리티를 사
용하면 된다. **)

shutdown 명령은 서버를 셧다운한다. (** 이거 번역 맞아~~?? **)
processlist 명령은 서버에서 실행되고 있는 스레드에 대한 정보를 보여준
다. kill 명령은 서버 스레드를  죽인다. 언제나 자신의 스레드는  보거나
죽일 수 있지만 다른 사용자에 의해 시작된 스레드는 프로세스  권한이 있
어야 보거나 죽일 수 있다.

몇가지 권한은 조심스럽게 허용해야 한다:y:

ㅇ grant(허용) 권한은 사용자가 다른 사용자의 권한을 설정할  수 있도록
허용한다. 다른 권한과 grant 권한을 가진 두 사용자는 권한을  결합할 수
있다.

ㅇ file 권한은 서버에서 모든 사람이 읽기 가능한 파일을 읽는데 남용 될
수 있.... SELECT 문을 이용해 접근할 수 있는 내용...

The file privilege can be abused  to read any world-readable file  on
the server into a database table,  the contents of which can then  be
accessed using SELECT.

(** 굳이 권한을 주지 않아도 이용할 수 있는 것은 권한을 주지 않는게 낫
다는 말이겠지요 **)

ㅇ shutdown 권한은 다른 사용자에가 완전히 서비스를  사용하지 못하도록
남용될 수 있다.

ㅇ  process 권한은 비밀번호를 설정하고 바꾸는 질의를 포함해 현재 수행
하고 있는 질의를 보는데 사용될 수 있다.

ㅇ mysql 데이터베이스에 대한 권한은 비밀번호와 다른 접근  권한 정보를
바꾸는 데 사용될 수 있다. (비밀번호가 암호화되어  저장되었다고 하더라
도, 충분한 권한을 가진 악의있는  사용자는 다른 비밀번호롤 바꿀 수  있
다)

mysql 권한 시스템으로 다룰 수 없는 몇가지가 있다:
ㅇ 접근을 거부할 사용자를 명백하게 지정할 수 없다.  왜냐하면 사용자와
연결을 거부하는 것을 완전하게 연관시킬 수 없기 때문이다.
ㅇ 사용자가 테이터베이스에서 테이블을 만들고 지울 수 있는 권한을 가질
수 있지만 데이터베이스 자체를 만들고 삭제할 수는 없도록 지정할  수 없
다.
(** 그러니까 create 와 drop 권한을 주면 데이터베이스 자체에 대해 제어
할 수 있지요. 그안의 테이블만  만들고 지울 수 있도록 하지는  못한다는
말 **)

6.4 권한 시스템 작동 방법
mysql 권한 시스템은 모든 사용자가 허용된 것만큼만 할 수 있도록 보증한
다. mysql 서버에 연결할 때, 사용자 확인은 연결한 호스트와 사용자가 지
정한 사용자 이름에 의해 결정된다. 시스템은 사용자 확인과  지정한 권한
에 따라 권한을 허용한다.
mysql은 사용자를 확인하는데 호스트이름과  사용자 이름 둘다  사용한다.
왜냐면 인터넷에서 이름이 같다고 같은 사용자라고 생각할 수는 없기 때문
이다.   예를  들어   whitehouse.gov에서  접속하는   사용자   bill  은
microsoft.com에서 접속하는 사용자  bill 과 같은  사람일 필요는  없다.
mysql은 때론 같은 이름을 가지고 있더라도 호스트를 이용해  사용자를 구
별한다 : whitehouse.gov에서 접속하는 bill에게 특정한 권한을 허용할 수
있고 microsoft.com에서 접속하는 bill에게 다른 권한을 허용할 수 있다.

mysql의 접근 제어는 두가지 단계가 있다:

단계 1: 서버에서 사용자가 연결할 수 있는지 없는지 판단

단계 2 (서버에 사용자가 연결이 허용되었을 경우) : 사용자가 수행하려는
명령에 대해 충분한 권한이 있는지 각 요청마다 서버에서 판단.예를 들면,
데이터베이스의 테이블에서 select  rows를 할때, 또는  데이터베이스에서
테이블을 제거할 때 서버에서 테이블에 대한 select 권한이 있는지 데이터
베이스에 대한 제거 권한이 있는지 확인을 한다.

서버는 접근 제어의 각 두 단계에서 mysql 데이터베이스의 user, db, host
테이블을 이용한다.grant 테이블의 필드는 아래와 같다:

+----------------+-----------------+---------------+---------------+
|  Table name    |  user           |  db           |  host         |
+----------------+-----------------+---------------+---------------+
|  Scope fields  |  Host           |  Host         |  Host         |
|  (필드 범위)   +-----------------+---------------+---------------+
|                |  User           |  Db           |  Db           |
|                +-----------------+---------------+---------------+
|                |  Password       |  User         |               |
+----------------+-----------------+---------------+---------------+
|                |  Select_priv    |  Select_priv  |  Select_priv  |
|                +-----------------+---------------+---------------+
|                |  Insert_priv    |  Insert_priv  |  Insert_priv  |
|                +-----------------+---------------+---------------+
|                |  Update_priv    |  Update_priv  |  Update_priv  |
|                +-----------------+---------------+---------------+
|  Privilege     |  Delete_priv    |  Delete_priv  |  Delete_priv  |
|  filelds       +-----------------+---------------+---------------+
|  (권한 필드)   |  Index_priv     |  Index_priv   |  Index_priv   |
|                +-----------------+---------------+---------------+
|                |  Alter_priv     |  Alter_priv   |  Alter_priv   |
|                +-----------------+---------------+---------------+
|                |  Create_priv    |  Create_priv  |  Create_priv  |
|                +-----------------+---------------+---------------+
|                |  Drop_priv      |  Drop_priv    |  Drop_priv    |
|                +-----------------+---------------+---------------+
|                |  Grant_priv     |  Grant_priv   |  Grant_priv   |
|                +-----------------+---------------+---------------+
|                |  Reload_priv    |               |               |
|                +-----------------+---------------+---------------+
|                |  Shutdown_priv  |               |               |
|                +-----------------+---------------+---------------+
|                |  Process_priv   |               |               |
|                +-----------------+---------------+---------------+
|                |  File_priv      |               |               |
+----------------+-----------------+---------------+---------------+

접권 제어의 두번째 단계를 위해(요청 인증), 요청이 테이블에  관계된 것
이라면 추가적으로 tables_priv 와  columns_priv 테이블을 참고한다.  이
테이블의 필드는 다음과 같다:
+-------------------+---------------+----------------+
|  Table name       |  tables_priv  |  columns_priv  |
+-------------------+---------------+----------------+
|  Scope fields     |  Host         |  Host          |
|                   +---------------+----------------+
|                   |  Db           |  Db            |
|                   +---------------+----------------+
|                   |  User         |  User          |
|                   +---------------+----------------+
|                   |  Table_name   |  Table_name    |
|                   +---------------+----------------+
|                   |               |  Column_name   |
+-------------------+---------------+----------------+
|  Privilege field  |  Table_priv   |  Type          |
|                   +---------------+----------------+
|                   |  Column_priv  |                |
+-------------------+---------------+----------------+
|  Other fields     |  Timestamp    |  Timestamp     |
|                   +---------------+----------------+
|                   |  Gran색       |                |
+-------------------+---------------+----------------+
각 승인(grant) 테이블은 필드 범위와 권한 필드로 구성되어 있다.

필드 범위는 테이블에서 각 엔트리의 범위를 결정한다. 다시  말하면 엔트
리가 적용되는 context(환경, 배경)이다. 예를  들면, Host 와 User  값이
'thomas.loc.gov' 와 'bob' 인 user 테이블 엔트리는 thomas.loc.gov 호스
트에서 bob이  연결을 할때  서버에서 인증을   하는데 사용된다.비슷하게
Host, User, db 필드값이  'thomas.loc.gov', 'bob', 'reports' 인 db  테
이블 엔트리는 thomas.loc.gov 호스트에서 bob 이 reports 데이터베이스에
접근할 때 사용된다. tables_priv  와 columns_priv 테이블은  테이블이나
각 엔트리가 적용될 수 있는 테이블/컬럼 조합을 가리키는 범위 필드를 포
함하고 있다.

접권 체크를 하기  위해, HOst  값 비교는  대소문자를 구별하지  않는다.
User,  Password,  Db,  Table_name  값은  대소문자를  구별한다.  mysql
3.22.12 와 이후 버전에서 Column_name 값은 대소문자를 구별하지 않는다.
(3.22.11에서는 대소문자 구별함)

권한 필드는 테이블 엔트리에 승인되는  권한을 가리키며 이는 수행할  수
있는 오페레이션이다. 서버는 사용자의 권한을 완벽하게 설정하기 위해 다
양한 승인(grant) 테이블의 정보를 조합한다. 여기에 사용하는 규칙은 6.6
[Request access]를 참고하자.

범위 필드는 문자열이며 다음과 같이 정의되었다; 기본 값은  빈 문자열이
다:
+--------------+------------+-------------------------------------+
|  Field name  |  Type      |                                     |
+--------------+------------|                                     |
|  Host        |  CHAR(60)  |                                     |
+--------------+------------|                                     |
|  User        |  CHAR(16)  |                                     | 
+--------------+------------|                                     |
|  Password    |  CHAR(16)  |                                     |
+--------------+------------+-------------------------------------+
|  Db          |  CHAR(64)  |  (CHAR(60) for the tables_priv and  |
|              |            |          columns_priv tables)       |
+--------------+------------+-------------------------------------+
user, db, host 테이블에서  모든 권한 필드는 ENUM('N','Y')로  정의되어
있다. -- 각각은 'N' 나 'Y'의 값을 가지며 기본값은 'N' 이다.
(** ENUM 타입은 목록 값중 오직 하나의 값만 가진다.필드 타입 참조 **)

tables_priv 와 columns_priv 테이블에서 권한 필드는 SET  필드로 정의된
다:
(** SET 타입은 목록 값중에 0이나 1개 이상의 값을 가진다 **)
+--------------+-------------+--------------------------------------+
| Table name   | Field name  |  Possible set elements               |
+--------------+-------------+--------------------------------------+
| tables_priv  | Table_priv  | 'Select', 'Insert', 'Update',        |
|              |             | 'Delete', 'Create', 'Drop', 'Grant', |
|              |             | 'References', 'Index', 'Alter'       |
+--------------+-------------+--------------------------------------+
| tables_priv  | Column_priv | 'Select', 'Insert', 'Update',        |
|              |             | 'References'                         |
+--------------+-------------+--------------------------------------+
| columns_priv | Type        | 'Select', 'Insert', 'Update',        |
|              |             | 'References'                         |
+--------------+-------------+--------------------------------------+

간단하게 말해서 서버는 승인(grant) 테이블을 다음과 같이 사용한다:

ㅇ user 테이블의 scope(범위) 필드는 들어오는 연결에 대해  허용할 것인
지 거부할 것인지를 결정한다. 허용된 연결에 대하여, 권한 필드는 사용자
의 전체적인 (superuser) 권한을 가리킨다.

ㅇ db 와 host 테이블은 함께 사용된다:

        - db 테이블의 범위 필드는 어떤 호스트에서 어떤 데이터베이스에
대해    어떤 사용자가 접근할 수 있는지 결정한다. 권한 필드는  어떤 오
퍼레이션        이 허용되었는지를 결정한다.

        - host 테이블은 db 테이블의 엔트리를 여러개의 호스트에 적용하
려고    할 때 db 테이블의 확장을 위해 사용한다.예를 들어, 사용자가 현
재 네트 웍의 여러 호스트에서 데이터베이스를 사용할 수  있도록 하려면,
사용자의        "db" 테이블 엔트리에 Host 값을 비워두고,  "host" 테이
블에 각 호스트   의 엔트리를 넣으면 된다.이러한  절차는 6,6  [Request
access]에 자세  하게 나와 있다.

ㅇ tables_priv 와 columns_priv 테이블은 db 테이블과  비슷하다. 그렇지
만 더 세부적으로 지정할 수 있다: 이 테이블들은  데이터베이스 단계에서
더 나아가 테이블과 컬럼 단계에 적용할 수 있다.

관리 권한(reload, shutdown,기타..)은 오직 user 테이블에서만 지정을 할
수 있다는 것을 기억하자! 왜냐면 관리자 오퍼레이션은 서버  자체에 대한
오퍼레이션이며 특정한 데이터베이스를  지정하는 것이 아니다.  그러므로
이러한 권한은 다른 승인(grant) 테이블에 있을 필요가 없다.실제로, 오직
user 테이블만이 관리자 오퍼레이션을 수행할 수 있는지  없는지를 결정할
때 참고가 된다.

파일(file) 권한도 마찬가지로 user 테이블에서만 지정한다.위와 같은  관
리 권한은 아니다. 그렇지만 서버  호스트에서 파일을 읽거나 쓸 수  있는
권한은 접근하고 있는 데이터베이스와 무관한 것이다.

mysqld 서버는 시작할 때 승인(grant) 테이블을 한번 읽는다. 승인 테이블
을 변경하고 효과를 발휘하려면 6.7 [Privilege changes]를 참고하자.

승인 테이블의 내용을 수정했을 때 원하는대로 권한이  설정되었는지 확인
하는 것은 좋은 생각이다. 유용한 진단 프로그램은  mysqlaccess 스크립트
로서 Yves CArlier 가 mysql distribution 으로 제공하고 있다. 어떻게 작
동하고 있는지 확인하기 위해 mysqlaccess 에 --help 옵션을  주어 실행해
보자. 물론 6.11 [Access denied] 와 6.12 [Security]를 참고하자.

mysqlaccess는 오직 user, db, host 테이블만 점검한다.  테이블이나 컬럼
단계의 권한까지는 점검하지 않는다는 것을 기억하자.

6.5 접근 제어,
단계 1 : 연결 확인(인증)

mysql 서버에 접속하려고 할 때 서버는 사용자 확인과 비밀번호를 통해 접
속을 허용하거나 거부한다. 사용자  확인이 안되면 서버는 접속을  완전히
거부한다. 사용자 확인이 되면 서버는 연결을 받아들이고 2번째 단계로 들
어가며 요청을 기다린다.

사용자 확인은 두가지 정보에 기반하고 있다:

ㅇ 접속하는 호스트
ㅇ mysql 사용자 이름
사용자 확인은 user 테이블의 세가지 범위 필드(Host,  User, Password)를
사용하여 수행된다. 서버는 user 테이블 엔트리의 호스트이름과 사용자 이
름이 맞으며, 비밀번호가 정확할 때만 접속을 받아들인다.

아래와 같이 user 테이블의 범위 필드값을 지정할 수 있다:

ㅇ Host  값은 호스트  이름이나 IP  숫자 또는  로컬 호스트를  가리키는
'localhost' 가 될 것이다.
ㅇ Host 필드에서 '%' 와 '_' 의 와일드카드 문자를 사용할 수 있다.
ㅇ '%'의 Host 값은  모든 호스트 이름을  나타낸다. 공백의 호스트  값은
'%'와 같다. 특정한 호스트에 대한 이러한 값은 당신의 서버에  연결할 수
있다는 것을 참고하자.
ㅇ 와일드카드 문자는 User 필드에는 허용되지 않는다. 그렇지만  모든 유
저에 해당하는 공백으로 둘 수  있다. 연결을 하려는 목록에 공백  사용자
이름이 있다면 클라이언트에서 실제로 지정한 이름 대신에 그 사용자는 익
명 사용자, 이름이 없는 사용자로서 간주된다.
ㅇ Password 필드는 공백으로 될 수 있다.이것은 아무런  비밀번호나 사용
할 수 있다는 것을 의미하는  것은 아니며 사용자는 비밀번호를  지정하지
않고 연결을 해야 한다는 의미이다.

아래의 테이블은 연결 요청에 적용하는 "user" 테이블 목록의  Host, User
값이 어떻게 조합되는지를 보여주는 예제이다:

+--------------------------+---------------------------------------+
| 호스트값/사용자 값       | 목록에 해당하는 연결                  |
+--------------------------+---------------------------------------+
| 'thomas.loc.gov'/'fred'  | thomas.loc.gov 에서 연결하는 fred     |
+--------------------------+---------------------------------------+
| 'thomas.loc.gov'/''      | thomas.loc.gov 에서 연결하는 모든     |
|                          | 사용자                                |
+--------------------------+---------------------------------------+
| '%'/'fred'               | 모든 호스트에서 연결하는 fred         |
+--------------------------+---------------------------------------+
| '%'/''                   | 모든 호스트에서 연결하는 모든 사용자  |
+--------------------------+---------------------------------------+
| '%.loc.gov'/'fred'       | loc.gov 도메인의 모든 호스트에서      |
|                          | 연결하는 fred                         |
+--------------------------+---------------------------------------+
| 'x.y.%'/'fred'           | x.y.net, x.y.com, x.y.edu 등에서      |
|                          | 접속하는 fred (이것은 아마도 유용하지 | 
|                          | 않을 것이다)                          |
+--------------------------+---------------------------------------+
| '144.155.166.177'/'fred' | 144.155.166.177의 IP 주소에서         |
|                          | 접속하는 fred                         |
+--------------------------+---------------------------------------+
| '144.155.166.%'/'fred'   | 144.155.166 클래스 C 서브넷의 모든    |
|                          | 호스트에서 접속하는 fred              |
+--------------------------+---------------------------------------+

Host 필드에서  IP에 와일드  카드를 사용할  수 있기   때문에(예를 들어
'144.155.166.%'     는    서브넷의     모든     호스트에    적용된다)
144.155.166.somewhere 와 같은 호스트 이름을 이용하여 부당하게  이용할
가능성이 생길 수 있다. 이러한 것을 막기 위해 mysql은 숫자와 도트(.)으
로 시작하는 호스트이름은 허용하지 않는다. 1.2.foo.com 과  같은 호스트
라면 이러한 호스트이름은 승인(grant) 테이블의 Host 컬럼과 매치되지 않
는다. IP 숫자만이 IP 와일드 카드 값과 매치시킬 수 있다.

만약 한개 이상의 user table 목록이 있다면 서버는 어떻게  user table을
선택할까? 이런 경우에는 user table의  정렬 순서에 따라 해결을 하며  ,
정열은 서버가 시작할때 수행이 된다. user table이 다음과 같다고 가정해
보자:

+---------------------+-------------+-------------+
|  Host               |  User       |  ...        |
+---------------------+-------------+-------------+
|  %                  |  root       |  ...        |
+---------------------+-------------+-------------+
|  %                  |  jeffrey    |  ...        |
+---------------------+-------------+-------------+
|  localhost          |  root       |  ...        |
+---------------------+-------------+-------------+
|  localhost          |             |  ...        |
+---------------------+-------------+-------------+

서버가 테이블을 읽을 때, 먼저 특정하게 지정된 값이 있는 호스트부터 목
록을 정열한다. (Host 컬럼에서 '%'는 "모든 호스트"를 의미하여 최소한도
로 지정하는 것이다) 목록에서  호스트값이 같으면 먼저 특정하게  지정된
사용자가 있는 것부터 정열한다.(공백으로 되어 있는 User 값은  "모든 사
용자"를 의미하여 최소한도로 지정하는 것이다.) 이렇게 하면 정열된 user
테이블은 다음과 같다:

+---------------------+-------------+-------------+
|  Host               |  User       |  ...        |
+---------------------+-------------+-------------+
|  localhost          |  root       |  ...        |
+---------------------+-------------+-------------+
|  localhost          |             |  ...        |
+---------------------+-------------+-------------+
|  %                  |  jeffrey    |  ...        |
+---------------------+-------------+-------------+
|  %                  |  root       |  ...        |
+---------------------+-------------+-------------+

정열된 순서에 따라 매칭 알고리즘이 적용되며 먼저 매칭되는 것을 사용한
다.   localhost에서   jeffrey가   연결을  하려할때,   Host   컬럼에서
'localhost' 목록이 먼저 매칭된다. 물론 사용자 이름이 공백인 목록은 연
결하는 호스트네임과 사용자 이름에 매칭된다. ('%'/'jeffrey' 목록  또한
매칭이 된다. 그러나 테이블에서 처음으로 매칭되는 것은 아니다.)

다른 예제가 있다. user 테이블이 다음과 같다고 가정해보자:

+---------------------+-------------+-------------+
|  Host               |  User       |  ...        |
+---------------------+-------------+-------------+
|  %                  |  jeffrey    |  ...        |
+---------------------+-------------+-------------+
|  thom               |  as.loc.gov |  ...        |
+---------------------+-------------+-------------+

정열된 테이블은 다음과 같다:
+---------------------+-------------+-------------+
|  Host               |  User       |  ...        |
+---------------------+-------------+-------------+
|  thom as.loc.gov    |             |  ...        |
+---------------------+-------------+-------------+
|  %                  |  jeffrey    |  ...        |
+---------------------+-------------+-------------+
첫번째로   thomas.loc.gov에서  jeffrey가   연결하는   것이  매칭되며,
whitehouse.gov 에서 jeffrey가 연결하는 거은 두번째로 매칭이 된다.

서버에 연결할 때 문제가 생기면, user 테이블을 출력하여 어떤 것이 먼저
매칭되는지 직접 정열을 하면 된다.

6.6 접근 제어, 2단계 : 요청 인증
연결되었다면 서버는 2단계로 들어간다. 연결이 성사되었을 때  각 요구에
대해 사용자가 수행하려는 연산의 유형에 기반하여 서버는  사용자가 충분
한 권한을 가지고 있는지 점검한다.

여기서 승인 테이블의  권한 필드가  작동한다. 권한은  user, db,  host,
table_priv, columns_priv 테이블의 정보를 사용한다. GRANT 와 REVOKE 명
령을 이용하여 권한 테이블을 다룰 수 있다. 7.25 [GRANT] 참고.
(이전에 보았던 각 권한 테이블의  필드 목록을 참고하는 것이 도움이  될
것이다; 6.4[Privilege] 참고.)

user 테이블의 승인 권한은 사용자에게 전체적인 기반을  제공하며 현재의
데이터가 어떤 것인지와는 상관이 없다. 예를 들어, user 테이블에서 사용
자에게 delete 권한을 승인했다면  서버 호스트에서 어떤  데이터베이스의
레코드라도 삭제할 수 있다! 다르게 말해서 user 테이블  권한은 슈퍼유저
권한이며, 슈퍼유저(서버나 데이터베이스 관리자 등)에게만 user 테이블에
대한 권한을 승인하는 것이 좋다. 다른 사용자에게는 user  테이블에서 권
한을 'N'로 설정하고, db와 host 테이블을 사용하여  특정 데이터베이스에
기반한 권한승인을 하는게 좋다.

db 와 host 테이블은 특정 데이터베이스의 권한을 승인한다.  각 테이블의
Host 와 Db 필드에서 와일드카드 문자 '%' 와 '_' 를 사용할 수 있으며 값
이 공백이면 필드 범위(scope fields)에서 모든 값을 허용한다.  '%' Host
값은 "모든 호스트"를 의미한다. db 테이블에서 Host 값이 공백이면 "host
테이블에서 더 자세한 정보를 문의하라"는  의미이다. A '%' or blank  Db
value in the host table means or "any database." (** or의 뜻이 무엇인
지 모르겠네요. host 테이블에서 Db 의 값이 '%' 또는 공백이면  "모든 데
이터베이스"를 의미한다는 말 같은데요 **) User 값이 공백이면 익명 사용
자로 간주된다.

서버가 시작할 때 db 와 host 테이블을 읽고 정열을 한다.(동시에 user 테
이블을 읽는다.) db 테이블은 Host, Db, User 순으로 필드  범위를 정열하
며 host 테이블은 Host, Db 순으로 필드 범위를 정열한다.  user 테이블과
같이 특정하게 지정되어 있는 값이 먼저 정열되고 최소한도로 지정된 값이
나중에 정열된다. 서버에서 매칭되는 목록을 찾을때, 가장 먼저 발견한 것
을 사용한다.

 tables_priv 와 columns_priv 테이블은  특정한 테이블과 컬럼에  관련된
권한을 승인한다.db와 host 테이블의 Host 필드와 같이 와일드카드를 Host
필드에서 사용할 수 있다. 그렇지만 Db, Table_name,  Column_name 필드에
서는 와일드카드나 공백값을 사용할 수 없다.

Host  테이블에서만  와일드카드를  사용할  수  있지만  tables_priv  와
columns_priv 테이블은 db 테이블과 비슷하게 정열이 되며  정열은 간단하
다.

요청 인증 과정은 아래에서 설명한다. 접근-점검 소스 코드에 친숙하다면,
여기서 설명하는 것은 코드에서 사용된 알고리즘과는 약간  다르다는 것을
알 수 있다.여기서의 설명은 코드가 실제로 작동하는 방식과 동일하다. 단
지 설명을 간단하게 하는데서 차이가 있는 것이다.

관리자 요청에 대해서(shutdown, reload  등) 서버는 단지 user  테이블만
체크를 한다. 왜냐면 user  테이블에서만 관리자 권한을 지정하기  때문이
다. 목록에서 요청된 연산을 허용하면 접근이 허용되며 아닌  경우에는 접
근이 거부된다.

예를 들어, mysqladmin shutdown을 실행하고자 하는데 user 테이블 목록에
서는 사용자에게 shutdown 권한을  승인하지 않으면, db나 host  테이블을
체크하지 않더라도 접근이  거부된다. (이러한 테이블에는  Shutdown_priv
컬럼이 없기 때문에 이렇게 할 필요도 없다)

데이터베이스와 관련된 요청에 대해(insert, update 등) 서버는 먼저 user
테이블 목록에서 사용자의 전체(슈퍼유저) 권한을 점검한다. 목록에서  요
청한 연산을 허용하면 접근이 승인된다.

user 테이블에서 전체적인 권한이 불충분하면, 서버는 db 와  host 테이블
을 점검하여 데이터베이스에 관련된 권한을 결정한다:

1. 서버는 db 테이블에서 매칭되는 Host, Db, User 필드를 찾는다. 연결하
려는 사용자의 호스트 이름과 Mysql 사용자 이름이 Host 와 User에 매칭되
다. 사용자가 접근하기 원하는 데이터베이스는 Db 필드에  매칭된다. 적합
한 Host 와 User 목록이 없으면 접근은 거부된다.

2. 매칭되는 db 테이블 목록이  있고 Host 필드가 공백이 아니면,  목록은
사용자의 데이터베이스 관련 권한을 정의한다.

3. 매칭되는 db 테이블 목록의 Host 필드가 공백이면, host 테이블에서 어
떤 호스트가 데이터베이스에 접근할 수 있는지 판단한다는 것을 의미한다.
이런 경우, 더 자세한 정보를 위해 host 테이블에서 매칭되는 Host  와 Db
필드를 찾는다. host 테이블에 매칭되는 목록이 없으면  접근은 거부된다.
매칭되는 목록이 있으면 사용자의 데이터베이스  관련 권한은 db 와  host
테이블 목록에서 권한을 intersection 하여 결정된다.

(** insertection은 교집합을 생각하면 되지요.  and 조건 **) 다시  말해
서, db와 host 테이블 둘  다 'Y'로 되어있을 때 권한이  설정된다.이러한
방법으로 db 테이블에서 일반적인  권한을 승인할 수 있으며,  그러고나서
host 테이블 목록을 사용해 host를 기반으로 하여 선택적으로 권한을 제한
할 수 있다.)


db 와 host 테이블 목록을 이용해 데이터베이스와 관련된 권한  승인을 결
정한 후, 서버는 이러한 정보를 user 테이블에서 승인한  전체적인 권한에
추가한다. 그 결과가 요청한 연산을 허용하면 접근이 허용된다. 다른 방법
으로, 서버는 tables_priv 와 columns_priv 테이블에서 사용자의 테이블과
컬럼 권한을 점검하고 사용자의 권한에 추가한다. 그 결과에  따라 접근이
허용되거나 거부된다.

왜 서버에서 전체적인 사용자 엔트리 권한에 데이터베이스,  테이블, 컬럼
에 관련된 권한을 추가하는지가 명확하지 않다.... 이런 경우 사용자 권한
은 초기에 요청된 연산에 대하여 불충분하다...

(It may not be apparent why the server adds the database-, table- and
column-specific privileges to  the global user  entry privileges  for
those cases in which  the user privileges  are initially found to  be
insufficient for the requested operation.)

요청은 한가지 유형 이상의 권한이 필요하기 때문이다. 예를 들어, INSERT
... SELECT 문을 수행할 때 insert 와 select 권한 둘 다  필요하다. 사용
자의 권한은 user 테이블에서 한가지 권한을 승인하고 db 테이블 엔트리에
서 다른 권한을 승인할 것이다. 이런 경우, 사용자는 이러한  요청을 수행
하기 위해 필요한 권한을 가지고 있다. 그렇지만 서버는  자체적으로 다른
테이블에 대해서는 .....

(In this  case, you  have  the necessary  privileges to   perform the
request, but   the server  cannot  tell that   from either  table  by
itself;) ;

두 엔트리에 의해 승인된 권한이 조합되어야 한다.

host 테이블은 "안전한" 서버 목록을 유지하는데 사용할 수 있다. TcX에서
는, host 테이블에는 지역 네트웍의 모든 시스템이 포함되어 있다. 여기서
는 모든 권한이 허용된다.

안전하지 않는 호스트를 가리키기 위해 host 테이블을 사용할 수 있다. 안
전하다고 생각되지 않는 공개 지역에 위치한 public.your.domain 시스템이
있다고 가정해보자. 사용자는 사용자  네트웍의 모든 호스트에 접근할  수
있으며, host 테이블 엔트리가 다음과 같은 시스템만 제외한다 :

+----------------------+------+-----------------------------------+
|       Host           |  Db  |                ...                |
+----------------------+------+-----------------------------------+
|  public.your.domain  |  %   |  ... (all privileges set to 'N')  |
+----------------------+------+-----------------------------------+
|  %.your.domain       |  %   |  ... (all privileges set to 'Y')  |
+----------------------+------+-----------------------------------+

당연히 접근 권한이 원하는대로 되어 있는지 언제나 승인 테이블에서 목록
을 테스팅해야 한다. (예를 들어 mysqlaccess 를 사용)

6.7 권한 변경시 적용 방법

mysqld 가 시작할 때, 모든 승인 테이블 내용이 메모리로 올라가고 이때부
터 유효하게 된다.

GRANT, REVOKE, SET PASSWORD 를 이용해 승인 테이블에 변경을  하면 바로
서버에서 인식을 한다.

권한 테이블을 직접 변경했다면(INSERT, UPDATE 등을 사용하여), 서버에서
승인  테이블을   재가동하도록  하기   위해  FLUSH   PRIVIEGES  문이나
mysqladmin flush-privileges 를 실행해야 한다.그렇게 하지 않으면  서버
를 다시 시작하기 전까지 변경된 권한이 적용되지 않는다.
서버에서 권한 테이블이 변경되었다는  것을 감지했을 때, 이미  존재하던
클라이언트 연결은 다음과 같이 영향을 받는다:

1. 테이블과 컬럼 권한 변경은 클라이언트의 다음 요청부터 적용된다.
2. 데이터베이스 권한 변경은 다음의 USE db_name 명령부터 적용된다.
3. 전체적인 권한과 비밀번호 변경은 클라이언트가 다음에  연결할 때부터
적용된다.

6.8 초기 mysql 권한설정

mysql을 설치하고 나서, mysql_install_db 스크립트를 실행해서 초기 접근
권한을 설정해야 한다. 4.7.1 [Quick install] 참고. mysql_install_db 스
크립트는 mysqld 서버를 시작하고, 다음과 같이 승인 테이블의  권한을 초
기화한다:

- mysql root 사용자는 슈퍼유저이며 모든 것을 할 수 있다.  로컬 호스트
에서만 연결할 수 있다.

주의 : 처음에  root 비밀번호는 비어있다.  그래서 누구나  비밀번호없이
root로 연결할 수있고 모든 권한을 승인받는다.

- 익명 사용자는 'test' 나 'test_' 로 시작하는 데이터베이스에  대한 모
든 권한을 승인받는다. 모든 사용자가 로컬 호스트에서 연결할  수 있으며
익명 사용자로 간주된다.

- 다른 권한은 거부된다. 예를  들어 일반 사용자는 mysqladmin  shutdown
이나 mysqladmin processlist 를 사용할 수 없다.

설치했을 때 초기 권한이 폭넓게  설정되어 있기 때문에 가장 먼저  mysql
root 사용자의 비밀번호를  설정해야 한다. 다음과  같이 설정하면  된다.
(PASSWORD() 함수를 이용해 비밀번호를 설정해야 한다!):

shell> mysql -u root mysql
mysql> UPDATE user SET Password=PASSWORD('new_password')
        WHERE user='root';
mysql> FLUSH PRIVILEGES;

or
shell> mysqladmin -u root password new_password
(** PASSWORD() 라는 함수를 사용하지 않아도 되므로 편리함.  또한 SQL문
에서 grant 명령을 이용해 설정할 수도 있지요 **)

첫번째 방법을 사용하면 직접 user 테이블의 비밀번호를 업데이트한다. 이
경우 서버가 다시 승인 테이블을 읽도록 해야 한다.(FLUSH  PRIVILEGES 사
용). 왜냐하면 다른 방법으로는 변경사항을 알릴 수 없기 때문이다.

(** 승인 테이블을 다시 읽지 않아서 이전에 설정했던  비밀번호가 제대로
안되는 경우가 있을 것입니다. 꼭 기억하고 있어야해요 **)

root 비밀번호가 설정되었으면 서버에 root로 접속할때마다 비밀번호를 명
시해야 한다.

추가로 셋업을 하거나 테스트할 때는 비밀번호를 설정할 필요가 없기 때문
에 root 비밀번호를 빈값으로 남겨두고 싶을 것이다. 그렇지만  실제 작업
을 하기 전에는 반드시 비밀번호를 설정했는지 확인해야 한다.

기본권한을 어떻게 설정하는지 mysql_install_db 스크립트를 살펴보자. 다
른 사용자에게 권한을 어떻게 설정할지 이것을 기본으로 사용할 수 있다.

위에서   설명한   것과   다르게    초기   권한을   설정하기   원하면,
mysql_install_db 스크립트를 실행하기 전에 수정하면 된다.

완전하게 승인 테이블을 다시 만들기 위해 mysql 데이터베이스를 포함하는
디렉토리의 '*ISM'  과 '*.ISD'  파일을 제거해야  한다. (이  디렉토리는
database 디렉토리에서 'mysq''이라는 이름이 붙어있다. mysqld --help 해
서 database 디렉토리의 목록을 볼  수 있다.) 원하는대로 권한을  수정한
후 mysql_install_db 스크립트를 실행하자.

6.9 mysql에 새로운 사용자 권한 추가하기

두가지 방법으로 사용자를 추가할 수 있다 : GRANT 문 사용 또는 mysql 승
인 테이블 직접 조작. GRANT 문을 사용하는 것이 더 선호되는 방법이다.

아래의 예제는 새로운 사용자를 설정하기 위해 어떻게 mysql 클라이언트를
사용하는지 보여준다. 이 예제는 이전에 설명했던것과 같이 기본값에 따라
권한을 설정하는 것으로 가정한다.  이것은 설정을 바꾸기 위해  mysqld가
실행되고 있는 같은  시스템에 있어야 한다는  것을 말한다.  (**초기값은
localhost에서만 접속 가능하므로**) 또한 mysql root 사용자로  접속해야
하고 root 사용자는 mysql 데이터베이스에 대한 insert 권한과  reload 관
리자 권한이 있어야 한다. root 사용자의 비밀번호를  바꾸었으면, 아래와
같이 mysql 명령행 상태에서 비밀번호를 명시해야 한다.

GRANT 문을 이용해 새로운 사용자를 추가할 수 있다:

shell> mysql --user=root mysql
mysql> GRANT ALL PRIVILEGES ON *.* TO monty@localhost
        IDENTIFIED BY 'something' WITH GRANT OPTION;
mysql> GRANT ALL PRIVILEGES ON *.* TO monty@"%"
        IDENTIFIED BY 'something' WITH GRANT OPTION;
mysql> GRANT RELOAD,PROCESS ON *.* TO admin@localhost;
mysql> GRANT USAGE ON *.* TO dummy@localhost;

위 GRANT 문에서는 세 명의 사용자를 설정한다:

monty : 어느 곳에서든 서버에 연결할 수 있는 완전한  슈퍼유저이지만 비
밀번호를 사용해야 한다. 우리는 monty@localhostmonty@"%"를  사용한
GRANT 문에 대해서 반드시 논의를 해야 한다. localhost  목록을 추가하지
않으면, mysql_install_db 에 의해 생성된 localhost 의 익명 사용자 목록
(등록?)이 로컬 호스트에서 접속할때  우선권을 갖는다. 왜냐하면  지정된
Host 필드 값이 있으며 정열 순서에서 먼저 오기 때문이다. (** 승인 테이
블의 정열 순서가 특정한 Host를 지정한 것부터 시작하는 것을 기억하자.

admin : 비밀번호 없이 localhost에서 접속할 수 있으며 reload와 process
관리자 권한을 승인받은 사용자. 이경우 사용자가 mysqladmin processlist
뿐만 아니라 mysqladmin reload, mysqladmin refresh, mysqladmin flush-*
명령을 실행할 수 있다.데이터베이스와  관련된 권한은 승인되지  않았다.
이것은 추가적인 GRANT 문을 사용해 나중에 승인할 수 있다.

dummy : 비밀번호없이 연결할 수 있지만 오직 localhost에서만  연결 가능
한 사용자. 권한 유형(privilege type)이 USAGE 이기 때문에  전체적인 권
한이 'N'로 설정되어 있다. USAGE 는 아무런 권한도 설정하지  않는다. 나
중에 데이터베이스와 관련된 권한을 승인할 수 있다.


또한 동일한 사용자 접근 정보를 INSERT 문을 통해 직접 추가할 수 있으며
이경우에는 서버가 승인 테이블을 다시 읽도록 알려주어야  한다.(**FLUSH
PRIVILEGES 사용**)

shell> mysql --user=root mysql
mysql>                INSERT                 INTO                user
VALUES('localhost','monty',PASSWORD('something'),
        'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y')
mysql> INSERT INTO user VALUES('%','monty',PASSWORD('something'),
        'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y')
mysql> INSERT INTO user SET Host='localhost',User='admin',
        Reload_priv='Y', Process_priv='Y';
mysql> INSERT INTO user (Host,User,Password)
                VALUES('localhost','dummy',");
mysql> FLUSH PRIVILEGES;

mysql 버전에  따라 위에서  'Y'  값이 다를  수 있다는   것을 기억하자.
3.22.11 버전 이후에서 사용할 수 있는 확장된 INSERT 문은  여기서 admin
사용자에게 사용되었다.

슈퍼유저를 설정하기 위해 권한필드를 'Y'로 한 user 테이블  목록만 만들
면 된다는 것을 기억하자. db 나 host 테이블 목록은 필요없다.  (** 관리
자 권한은 db나 host 테이블과는 전혀 관련이 없다. db는 접속할  수 있는
데이터베이스에 대해 상세하게 설정하고 host 테이블은 db테이블을  좀 더
정교하게 설정하기 위해 필요한 것이다. 관리자 권한은 오직  user 테이블
만 관련되어있다 **)

마지막 INSERT 문(dummy 사용자)에서는 user 테이블의 권한 컬럼이 명확하
게 설정되지 않았다. 왜냐면 이 컬럼의 기본값은 'N'로 되어  있기 때문이
다.

다음의   예제에서는  custom   이라는   사용자를  추가한다.   custom은
localhost,   server.domain,   whitehouse.gov에서  접속할   수   있다.
localhost에서는   bankaccount  데이터베이스에만   접속할   수  있으며
whitehouse.gov에서는 expenses 데이터베이스에, 모든 세  호스트상에서는
customer 데이터베이스에 접속하길 원한다. 모든 세 호스트상에서  stupid
라는 비밀번호를 사용하길 원한다.

GRANT 문을 이용 이러한 사용자 권한을 설정하기 위해 다음의 명령을 실행
하자:

shell> mysql --user=root mysql
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
        ON bankaccount.*
        TO custom@localhost
        IDENTIFIED BY 'stupid';
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
        ON expenses.*
        TO custom@whitehouse.gov
        IDENTIFIED BY 'stupid';
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
        ON customer.*
        TO custom@'%'
        IDENTIFIED BY 'stupid';

승인 테이블을 직접 수정해 사용자 권한을 설정하려면 다음의 명령을 사용
하자. (마지막에 FLUSH PRIVILEGES 를 사용해야 한다는 것을 기억하자):

shell> mysql --user=root mysql
mysql> INSERT INTO user (Host,User,Password)
        VALUES('localhost','custom',PASSWORD('stupid'));
mysql> INSERT INTO user (Host,User,Password)
        VALUES('server.domain','custom',PASSWORD('stupid'));
mysql> INSERT INTO user (Host,User,Password)
        VALUES('whitehouse.gov','custom',PASSWORD('stupid'));
mysql> INSERT INTO db
 
       (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,
        Create_priv,Drop_priv)
        VALUES
        ('localhost','bankaccount','custom','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO db
 
       (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,
        Create_priv,Drop_priv)
        VALUES
 
       ('whitehouse.gov','expenses','custom','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO db
 
       (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,
        Create_priv,Drop_priv)
        VALUES('%','customer','custom','Y','Y','Y','Y','Y','Y');
mysql> FLUSH PRIVILEGES;

처음의 세가지 INSERT 문은 custom 사용자가 비밀번호를  사용하여 다양한
호스트에서 접속할 수 있도록 user 테이블 목록을 추가한다.  그렇지만 그
에게 어떠한 퍼미션도 승인하지 않는다. (모든 권한은 기본값으로  'N' 이
다) 다음의 세가지 INSERT 문은 적절한 호스트에서 접속을 할  때, custom
에게 bankaccount, expenses, customer 데이터베이스에 대한 권한을  승인
하는 db 테이블 목록을 추가한다. 일반적으로 승인 테이블을  직접 수정하
였으면, 변경된 권한을 적용하기 위해 서버가 승인 테이블을  다시 읽도록
해 주어야 한다.

특정한 사용자가 특정한 도메인의 시스템에서 접속할 수  있도록 설정하고
자 한다면, 다음과 같이 GRANT 문을 설정할 수 있다:

mysql> GRANT ...
        ON *.*
        TO myusername@"%.mydomainname.com"
        IDENTIFIED BY 'mypassword';

승인 테이블을 직접 수정하려면 다음과 같이 한다:

mysql> INSERT INTO  user VALUES ('%.mydomainname.com',  'myusername',
PASSWORD('mypassword'),...);
mysql> FLUSH PRIVILEGES;
~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~

승인 테이블을 다루기 위해 xmysqladmin, mysql_webadmin, xmysql  프로그
램을 사용할 수 있다. http://www.mysql.com/Contrib 에서 이러한  유틸리
티를 찾을 수 있다.


6.10 비밀번호 설정 방법
앞의 예제는 중요한 원칙을 보여준다  : INSERT 나 UPDATE 문에서  공백이
아닌 비밀번호를 저장할 때 반드시 암호화하기 위해 PASSWORD() 함수를 사
용해야 한다!! user 테이블은 비밀번호를 플레인텍스트(**일반 텍스트  파
일**)가 아니라 암호화된 형태로 저장하기 때문이다. 이러한  사실을 잊어
버리면 다음과 같이 비밀번호를 설정하려고 할 것이다:

shell> mysql -u root mysql
mysql> INSERT INTO user (Host,User,Password)
        VALUES('%','jeffrey','bLa81m0');
mysql> FLUSH PRIVILEGES;

플레인텍스트 값   'bLa81m0' 은  user 테이블에   비밀번호로 저장이  된
다.jeffrey라는 사용자가 이 비밀번호를 사용해 서버에 연결하려고  할 때
mysql 클라이언트를 이 비밀번호를 암호화해서 그 결과를  서버로 보낸다.
서버는 암호화된 비밀번호('bLa81m0'이 아니다)를 user 테이블의 비밀번호
(플레인텍스트 'bLa81m0' 값이다)와 비교한다. 비교는 실패하고 서버는 연
결을 거부한다:

shell> mysql -u jeffrey -pbLa81m0 test
Access denied
비밀번호는 user 테이블에 입력될  때 반드시 암호화되어야 하기  때문에,
INSERT 문은 다음과 같이 사용해야 한다:

mysql> INSERT INTO user (Host,User,Password)
        VALUES('%','jeffrey',PASSWORD('bLa81m0'));

또한 SET PASSWORD 문을 사용할 때도 PASSWORD() 함수를 사용해야 한다:

mysql> SET PASSWORD FOR jeffrey@"%" = PASSWORD('bLa81m0');

참고 : PASSWORD() 함수는 비밀번호 암호화를 수행한다.  그렇지만 유닉스
에서 비밀번호를 암호화하는 방법과는  다르다. 유닉스 비밀번호와  mysql
비밀번호가 동일할 때 PASSWORD()가 유닉스 비밀번호 파일(** /etc/passwd
파일 **)에 암호화되어 저장된 값과 같다고 생각하면 안 된다.

GRANT ... IDENTIFIED BY 문이나 mysqladmin password 명령을 사용해 비밀
번호를 설정하면 PASSWORD() 함수는 필요없다. 둘다 비밀번호를  암호화해
서 저장한다:

mysql> GRANT USAGE ON *.* TO jeffrey@"%" IDENTIFIED BY 'bLa81m0';
shell> mysqladmin -u jeffrey password bLa81m0

(** 당근, GRANT 문이나 mysqladmin password 명령을 사용하는게 편하겠지
요? mysql의 암호화 알고리즘이  유닉스와 다르듯 유닉스 계정과  mysql의
사용자는 전혀 다르다는 것도 다시 한번 기억하고 있어야 합니다.**)

6.11 접근 거부 에러가 나는 이유
mysql 서버에 연결하려 할 때 접근 거부 에러가 나면,  아래에서 설명하는
것에 따라 해결 방법을 찾을 수 있다:

-  초기   승인  테이블  내용을   설정하기  위해  mysql을   설치한  후
mysql_install_db 스크립트를 실행하였는가? 실행하지 않았다면  스크립트
를 실행하자. 6.8 [Default privileges]  참고. 다음 명령을 이용해  초기
권한을 시험해 볼 수 있다:

        shell> mysql -u root test

에러없이 서버에 접속할 수 있을 것이다. mysql  데이터베이스 디렉토리에
'user.ISD' 파일이 있는지 확인해보아야  한다. (일반적으로 'mysql  설치
디렉토리/var/mysql/user.IDS' 이다)

- 설치를 새로하고 난후 , 서버에 연결하고 사용자와 접근  권한을 설정해
야 한다:

        shell> mysql -u root mysql

초기에 mysql root 사용자만 비밀번호가 없기 때문에 서버에 연결할 수 있
다. 보안문제가 있기  때문에, 다른 mysql  사용자를 설정하기 전에  먼저
root의 비밀번호를 설정해야 한다.

root로 접속하려하는데 다음의 에러가 났다고 가정하자:

        Access denied for user: '@unknown' to database mysql

이것은 user 테이블에 User 컬럼 = root 라는 목록이 없고, mysqld가 사용
자 클라이언트의 호스트이름을 해석할 수 없다는 것을 의미한다.  이런 경
우  --skip-grant-tables  옵션을  이용해  서버를  다시  시작해야  하고
'/etc/hosts' 를 편집하거나 '\windows\hosts' 파일에 사용자 호스트 목록
을 추가해야 한다.

- 3.22.11  이전 버전에서  3.22.11이나 이후   버전으로 업데이트했다면,
mysql_fix_privilege_tables 스크립트를 실행했는가? 하지 않았다면  실행
하자. mysql 3.22.11에서 GRANT 문 기능이 가능해지면서 승인 테이블 구조
가 바뀌었다.
- (INSERT 나 UPDATE 문을 사용해) 승인 테이블을 직접 고쳤고  변화가 아
직 반영되지  않은 것으로   보이면, FLUSH PRIVILEGES  문을  사용하거나
mysqladmin flush-privileges 명령을 사용해  서버가 승인 테이블을  다시
읽도록 해야 한다는 것을 기억하자.그렇지 않으면 서버가 재시작하기 전까
지는 변화된 것이 반영되지 않는다. root 비밀번호를 설정하고  나서 권한
을 flush 하기까지는 비밀번호를 명시할 필요가 없다. 왜냐면 서버는 아직
비밀번호를 바꾸었는지 모르기 때문이다.

- 세션 중간에 권한이 변경된 것으로 보이면 슈퍼유저가 바꾸었을 것이다.
승인 테이블을 재시작하는 것은 새로운 클라이언트 접속에  영향을 미치지
만 이미 존재하고 있던 연결은 6.7 [Privileges  changes]에서 설명한대로
영향을 미친다.

- 시험하기 위해, mysqld 대몬에  --skip-grant-tables 옵션을 주어  시작
하자. 그러고나서 mysql 승인 테이블을 변경할 수 있고 변경된것이 원하는
대로 작동하는지를 체크하는 mysqlaccess 스크립트를 사용할 수  있다. 원
하는대로 수정이 되었으면 mysqld 서버가 새로운 승인 테이블로 시작할 있
도록 mysq1admin flush-priveleges 를 실행한다.
주의 : 승인테이블을 재로딩하는 것은 --skip-grant-tables 옵션을 무효화
한다. 이를 통해 서버를 다운시키고 다시 재시작하지 않고도  승인 테이블
을 시작할 수 있다.

- 펄, Python,  ODBC 프로그램에서 접근하는데  문제가 있다면, mysql  -u
user_name db_name 또는 mysql -u user_name -pyour_pass db_name 으로 서
버에 접속을 시도해보자. (-p 와 비밀번호사이에는 공백이 없다는 것을 기
억하자. 또한 --password=your_pass 형태로도 사용할 수 있다) mysql 클라
이언트로 접속이 되면 프로그램에 문제가 있는 것이며 접근 권한에는 문제
가 없다.

- 비밀번호가 제대로 작동하지 않으면, INSERT, UPDATE, SET  PASSWORD 문
에서 비밀번호를 설정하면서 PASSWORD()  함수를 반드시 사용해야  한다는
것을 기억하자.  PASSWORD() 함수는   GRANT ... INDENTIFIED  BY  문이나
mysqladmin password 명령을 사용했다면 불필요하다. 6.10 [Passwords] 참
고.

- localhost 는 지역 호스트 이름과 같은 말이다. 또한 호스트를 명백하게
설정하지 않은 경우 클라이언트에서 연결하려는 호스트의 기본값이다.  그
러나 MIT-pthreads를 사용하는 시스템에서는 localhost로의 연결이 제대로
작동하지 않는다. (localhost 연결은 유닉스 소켓을 통해  만들어진다. 그
렇지만 MIT-pthreads에서는 유닉스 소켓을  지원하지 않는다.) 이와  같은
시스템에서 문제를 피하려면, 서버에 호스트 이름을 명확하게 말해주기 위
해 --host 옵션을 사용해야 한다. 그러면 mysqld 서버에 TCP/IP 연결을 만
든다. 이경우, 서버 호스트
의 user 테이블 목록에 실제  호스트이름이 있어야 한다. (서버와  동일한
호스트에서 클라이언트 프로그램을 실행한다고 하더라도 마찬가지이다.)
- mysql -u user_name db_name 으로 데이터베이스에 접속하려 할  때 접근
거부 에러가 나면,  user 테이블에  문제가 있을 것이다.  mysql -u  root
mysql 를 실행하여 점검하고 다음의 SQL 문을 사용하자:

        mysql> SELECT * FROM user;

여기서 사용자의 호스트이름과 mysql 사용자 이름과 맞는 Host 와 User 컬
럼의 목록이 포함되어 있어야 한다.

- Access denied 에러 메시지는 접속하려는 사용자와 호스트 이름, 그리고
비밀번호를 사용했는지 여부를 보여줄 것이다. 일반적으로 user  테이블에
에러 메시지에서 보여준 호스트 이름과 사용자 이름과 정확하게 맞는 목록
을 가지고 있어야 한다.

- 다른 시스템에서 mysql 서버에 접속할 때 다음의 에러 메시지가 나오면,
user 테이블에 연결을 하려고 하는 호스트 이름이 없다는 것을 말한다:

        Host ... is not allowed to connect to this MySQL server

(서버 호스트에서!) 명령행 유틸리티인  mysql을 사용하여 user  테이블에
연결하고자 하는 사용자/호스트 이름을  추가하여 해결할 수 있다.  mysql
3.22 를 사용하고 있지 않고 연결하고자 하는 시스템의 IP  숫자나 호스트
이름을 모른다면, user 테이블에 Host  컬럼 값으로 '%' 목록을  입력하고
서버 시스템에서 --log 옵션을 사용해 mysqld 를  재시작하자. 클라이언트
시스템에서 연결을 시도한 후 mysql 로그에는 어떻게 실제로  연결을 했는
지에 대한 정보가 들어있다. (그러고나서 '%'를 로그에 나온  실제 호스트
이름으로 바꾼다. 그렇지 앟으면 보안에 문제가 생길 수 있다.)

- mysql -u root test 는 작동을 하는데 mysql -h your_hostname  -u root
test 에서 접근에 에러가 나면, user 테이블에 정확한 호스트 이름이 없을
것이다. 일반적인 문제는 user 테이블의 Host 값에는 완전하지  않은 호스
트 이름(** 도메인은 빼고 호스트 이름만 넣은 경우**) 이  들어가 있는데
시스템의 네임 해석 루틴은 FQDN(fully-qualified domain name -  ** 완전
한 도메인 이름과 호스트 이름을 사용**)으로 처리하는  경우이다(또는 거
꾸로 해석).예를 들어, user 테이블에 호스트 이름이 'taejun'  으로 되어
있는데, DNS는 mysql에 호스트 이름이 'taejun.subnet.se'라고 알려줄  수
있으며 이경우는 제대로 작동하지 않을 것이다.
user 테이블에 Host 컬럼 값으로서 해당하는 IP 숫자나 호스트  이름을 추
가하자. (대신, user 테이블에 Host 값으로 와일드카드 문자를  포함할 수
있다. 예를 들어, 'taejun.%'. 그러나 호스트이름 끝에 '%'를 사용하는 것
은 안전하지 않으며 권하지도 않는다!)

-  mysql  -u   user_name test   는  작동하는데   mysql -u   user_name
other_db_name 은 작동하지 않는다면  db 테이블에 other_db_name  목록이
없는 경우이다.

- 서버 시스템에서 mysql -u  user_name db_name 은 작동을 하는데,  다른
클라이언트 시스템에서 mysql -u user_name db_name 은 작동을  하지 않는
다면, user 테이블이나 db 테이블에 클라이언트 시스템의 목록이  없는 것
이다.

- 왜 Access denied 가 나는지 해결하지 못하면, user  테이블에서 와일드
카드('%' 또는 '_')를 포함하고 있는 Host 값을 가진 목록을  모두 제거하
자. 매우 일반적인 에러는 Host='%' 그리고 User='some user'로 입력을 하
고나서 , 이렇게 하면 같은 시스템에서 연결할 때 localhost를  지정할 수
있도록 허용한다고 생각하는 것이다. 이것이 제대로 작동하지 않는 이유는
기본 권한에 Host='localhost' 와  User='' 목록이 포함되어 있기  때문이
다. Host  값에 '%'  보다 더  분명한 'localhost'  목록이 있기  때문에,
localhost에서 접속할 때 새로운 목록보다 먼저 선택이 된다. 정확한 절차
는 두번째 항목으로 Host='localhost' 와 User='some_user' 를 입력하거나
Host='localhost' 와 User='' 를 제거하는 것이다.

- 다음의 에러가 나는 경우:

        Access to database denied

db 나 host  테이블에 문제가  있을 것이다. 선택한  db 테이블의  목록에
Host 컬럼이 비어있다면, host 테이블에 db 테이블 목록에  적용되는 호스
트 이름이 있는지를 확인해야 한다.
(** 일반적으로 db 테이블에 host  값을 비워두는 경우, host  테이블에서
접근하는 호스트를 제어할 수 있다 **)

- 다음의 에러가 나는 경우:

        Access to database denied

SELECT ... INTO OUTFILE 또는 LOAD DATA INFILE 의 SQL 문을 사용하는 경
우, user 테이블 목록에서 file 권한이 설정되어 있지 않았을 것이다.
- 다른 모든 것이 실패하였을 경우, mysqld 대몬을 디버깅  옵션으로 시작
하자. (예를 들어, --debug=d,general,query) 그러면 각 명령에서  생기는
정보와 함께, 접속을 시도하는 호스트와 사용자에 대한 정보를  출력할 것
이다. G.1 [Debugging] 참고.

- mysql 승인 테이블에서 다른 문제에 부딪쳤고 이 문제를 메일링리스트에
알려야겠다고 느끼면,  mysql  승인 테이블을   덤프하여 제공해야  한다.
mysqldump  mysql  명령을  사용해  테이블을  덤프할  수  있다.  언제나
mysqlbug 스트립트를 사용하여 문제를 올리자.

- 다음의 에러가 나는 경우, mysqld 대몬이 실행되지 않거나  잘못된 소켓
이나 포트로 연결하려고 시도하는 경우:

        Can't connect to local MySQL server
        Can't connect to MySQL server on some_hostname

먼저 mysqld 대몬이 실제로 작동하는지 ps를 이용해 확인한다.  소켓 파일
이    있느지    확인하고    점검을   해    보아야    한다.(일반적으로
`/tmp/mysql.sock' 임) 또는 telnet host_name 3306으로 접속을  시도해보
자. 더   자세한 정보를   위해 mysqladmin  version  과  mysqladmin  -h
host_name version 을 사용해 볼 수 있다. 물론 참고할 것이 있는지 mysql
데이타 디렉토리의 에러 로그 파일을 점검해보자.

클라이언트 프로그램은 설정 파일이나 환경 변수에서 지정한  연결 패러미
터를 사용할 수 있다는 것을 기억하자. 명령행에서 지정하지  않았는데 클
라이언트가 잘못된 기본 연결 패러미터를 보내는 것으로 생각되면,  홈 디
렉토리에서 환경변수나 '.my.cnf' 파일을 점검해보자. 비록 여기서 지정한
연결 패러미터와는 관계가 멀지만  시스템의 전반적인 mysql 설정  파일을
점검해 볼 수 있다. 4.14.4 [Option files] 참고.  클라이언트에서 아무런
옵션도 주지 않았는데 Access denied 에러 메시지가 나오는 경우, 옵션 파
일 중에서  예전의 비밀번호를  지정하지 않았는지  확인해 보자.  4.14.4
[Option files] 참고.

6.12 크랙커에 대비하여 mysql을 안전하게 하는 방법

mysql 서버에 연결할 때 일반적으로 비밀번호를 사용해야  한다. 비밀번호
는 연결할 때 단순한 텍스트로 전송되지 않는다.

서버/클라이언트 연결은 암호화되지 않는다; 모든 정보는 연결을 볼 수 있
는 누구라도 읽을 수 있는 텍스트로 전송된다. 이 문제에 대해  걱정이 되
면 문제를 어렵게 하기 위해 압축 프로토콜(mysql 3.22 이상 버전)을 사용
할 수  있다. 보안을   더 확실하게 하기   위해 ssh를 설치할  수  있다.
(http://www.cs.hut.fi/ssh 참고) 이것을 이용해 mysql 서버와 클라이언트
사이에 암호화된 TCP/IP 연결을 사용할 수 있다.

mysql 시스템의 보안을 유지하게 위해 다음의 제안을 신중하게 고려하자:

- 모든 mysql 사용자가 비밀번호를 사용. 어떤 사용자가  비밀번호가 없으
면 'mysql - u 사용자이름' 을 이용해 간단하게 그 사용자로  로그인할 수
있다는 것을 기억하자. 이것은 클라이언트/서버 애플리케이션의  일반적인
작동방법이다. mysql_install_db 스크립트를 실행하기 전에 이 스크립트를
수정하여 모든 사용자의 비밀번호를 바꿀 수 있다. 또는 mysql  root 사용
자의 비밀번호를 바꿀 때는 다음과 같이 하면 된다:

shell> mysql -u root mysql
mysql> UPDATE user SET Password=PASSWORD('new_password')
        WHERE user='root';
mysql> FLUSH PRIVILEGES;

- mysql 데몬을 유닉스의 root  사용자로 시작하지 말자. mysqld 는  다른
사용자가 실행할 수 있다.또한 보안을 더 엄격하게 하기 위해 mysql이라는
유닉스 사용자를 만들 수 있다. 다른 유닉스 사용자로  mysqld를 실행하면
user 테이블에서 root 사용자 이름을 바꿀 필요가 없다. mysqld를 다른 유
닉스 사용자가 시작하기 위해 mysql.server 스크립트를 수정하면 된다. 일
반적으로 su 명령을  사용한다. 더 자세한  내용은 16.7 [Changing  mysql
user] 참고.

- mysqld를 실행할 수 있는 사용자만이 데이터베이스  디렉토리에 읽기/쓰
기 권한을 가지고 있는지 확인.

- 모든 사용자에게 process 권한을 주지 말자. mysqladmin processlist 을
출력하면 현재 실행하는 쿼리의 내용을 볼 수 있다. 그러므로 이러한 명령
을  실행할  권한이  있는  사용자는  다른  사용자의  UPDATE  user  SET
password=PASSWORD(_'no_secure') 질의를 볼 수 있다.mysql은 process  권
한을 가진 사용자를 위해 추가적인(extra) 연결을 저장한다.그래서  mysql
root 사용자는 모든 일반적인 연결이 사용되었어도 로그인하고  점검을 할
수 있다.

- 모든 사용자에게 file 권한을 주지 말자. 이러한 권한이  있는 사용자는
mysqld 대몬의 권한이 있는 파일  시스템의 어느 곳에라도 파일을  저장할
수 있다.좀 더 안전하게 하기 위해 SELECT ... INTO OUTFILE  로 생성되는
모든 파일은 모든 사용자가 읽기만 할 수 있으며 이미 존재하는 파일을 덮
어씌울 수 없다.

(** file 권한은 LOAD DATA INFILE , SELECT .. INTO OUTFILE 문을 이용하
여 서버에 파일을 저장하고 읽을 수 있는 권한을 허용한다. 이러한 권한을
가진 사용자는 mysql 서버가 읽고 쓸 수 있는 파일을 읽고 쓸 수  있는 권
한이 허용된다. 일반 사용자에게 이런 권한을 줄 필요는 없다.  필요한 부
분만 권한을 주는 것이 좋다. 권한을 남용말자. **)

- DNS 를 신뢰하지 못한다면 승인 테입르에서 호스트이름 대신  IP를 사용
하자. 기본적으로 mysqld 의  --secure 옵션은 호스트이름을 안전하게  한
다. 어떤 경우 와일드카드 문자가 포함된 호스트이름 값을  사용할때는 매
우 조심해야 한다.

- mysql.server 스크립트에서 유닉스 root 사용자의 비밀번호를 넣는다면,
이 스크립트는 오직 root만이 읽을 수 있도록 해야 한다.

다음의 mysqld 옵션은 보안과 관련되어 있다:

--secure : gethostbyname() 시스템 콜에  의해 리턴된 IP 숫자가  원래의
호스트이름을 resolve 한 것과 같은지를 점검한다. 이것은 어떤 사람이 다
른 호스트 이름을 에뮬레이터해서 접근하는 것을 어렵게 만든다.  이 옵션
은 또한 호스트이름이 온전한지에 대한 점검을 추가한다. 해석하는데 때로
는 시간이 많이 걸려서 mysql 3.21에서는 기본적으로 설정이 되어 있지 않
다. mysql 3.22에서는 호스트이름을 캐쉬하고 이 옵션이  기본적으로 설정
되어 있다.

(** 함수 gethostbyname()은 호스트 이름을 인자로 받아 그에 해당하는 IP
주소 및 기타 정보를 해당하는 구조체에 담아 그 구조체의  포인터를 리턴
하는 함수입니다. 쉽게 말해서 호스트 이름을 넣으면 해당 IP 주소를 찾아
주지요.**)

--skip-grant-tables : 이 옵션을 사용하면 서버가 권한 시스템을 전혀 사
용하지 않는다. 그러면 모든 사용자가 모든 데이터베이스에 접속할  수 있
다!

(mysqladmin reload 를 실행하여 실행중인 서버가 승인 테이블을 사용하도
록 할 수 있다.)


--skip-name-resolve : 호스트이름이 해석되지 않는다. 승인 테이블의  모
든 Host 컬럼값은 반드시 IP 숫자이거나 로컬호스트이어야 한다.

--skip-networking : 네트웍을 통한 TCP/IP 연결을 허용안함. mysqld와 모
든   연결은  유닉스   도메인  소켓을   통해  만들어진다.   이  옵션은
MIT-pthreads를 사용하는 시스템에서는 제대로 작동을 하지 않는다.  왜냐
면 MIT-pthreads 패키지는 유닉스 소켓을 지원하지 않기 때문이다.

(** 리눅스를 사용하는 사람들에게는  상관없겠죠? 유닉스 도메인  소켓을
지원하니깐. 이와 비슷하게 postgres도 6.3버전 이후부터인가요? 기본적으
로 유닉스 도메인 소켓으로 바뀌었지요. **)

7. MySQL language reference.
7.1 Literals: how to write strings and numbers
7.1.1 Strings
A string is  a sequence  of characters, surrounded  by either  single
quote (`'') or double quote (`"') characters. Examples:

'a string'
"another string"

Within a  string, certain  sequences have  special meaning.   Each of
these sequences begins with  a backslash (`\'),  known as the  escape
character. MySQL recognizes the following escape sequences:

\0      An ASCII 0 (NUL) character.
\n      A newline character.
\t      A tab character.
\r      A carriage return character.
\b      A backspace character.
\'      A single quote (`'') character.
\"      A double quote (`"') character.
\\      A backslash (`\') character.
\%      A `%' character. This is used to search for literal instances
        of `%' in contexts where  `%' would otherwise be  interpreted
        as a wildcard character.
\_      A `_' character. This is used to search for literal instances
        of `_' in contexts where  `_' would otherwise be  interpreted
        as a wildcard character.
There are several ways to include quotes within a string:
A `'' inside a string quoted with `'' may be written as `'''.
A `"' inside a string quoted with `"' may be written as `""'.
You can precede the quote character with an escape character (`\').
A `'' inside a string quoted with `"' needs no special  treatment and
need not be doubled or escaped. In the same way, `"' inside  a string
quoted with `'' needs no special treatment.
The  SELECT  statements  shown  below  demonstrate  how  quoting  and
escaping work:

mysq> SELECT 'hello', '"hello"', '""hello""', 'hel''lo', '\'hello';
+-------+---------+-----------+--------+--------+
| hello | "hello" | ""hello"" | hel'lo | 'hello |
+-------+---------+-----------+--------+--------+

mysql> SELECT "hello", "'hello'", "''hello''", "hel""lo", "\"hello";
+-------+---------+-----------+--------+--------+
| hello | 'hello' | ''hello'' | hel"lo | "hello |
+-------+---------+-----------+--------+--------+

mysql> SELECT "This\nIs\nFour\nlines";
+--------------------+
| This
Is
Four
lines |
+--------------------+
If you want to insert binary  data into a BLOB column, the  following
characters must be represented by escape sequences:
NUL     ASCII 0. You should represent  this by `\0' (a backslash  and
        an ASCII `0' character).
\       ASCII 92, backslash. Represent this by `\\'.
'       ASCII 39, single quote. Represent this by `\''.
"       ASCII 34, double quote. Represent this by `\"'.

 If   you  write   C  code,   you  can   use  the   C   API  function
mysql_escape_string() to escape characters for the INSERT  statement.
See section  . In  Perl, you  can use  the quote  method of   the DBI
package to convert special characters to the proper escape sequences.
See section .

You should use an  escape function on  any string that might  contain
any of the special characters listed above!
7.1.2 Numbers
Integers are represented as a sequence of digits. Floats use `.' as a
decimal separator. Either type  of number may  be preceded by `-'  to
indicate a negative value.
Examples of valid integers:

1221
0
-32

Examples of valid floating-point numbers:

294.42
-32032.6809e+10
148.00
An integer may be used in a floating-point context; it is interpreted
as the equivalent floating-point number.

7.1.3 Hexadecimal values
MySQL supports hexadecimal values. In number context these  acts like
an integer (64 bit  precision). In string  context these acts like  a
binary string  where  each pair   of hex  digits is   converted to  a
character.

mysql> SELECT 0xa+0
       -> 10
mysql> select 0x5061756c;
       -> Paul

Hexadecimal strings is  often used by  ODBC to  give values for  BLOB
columns.

7.1.4 NULL values
The NULL value means ``no data'' and is different from values such as
0 for numeric types or the empty string for string types. See section
18.15 Problems whit NULL values.
NULL may be  represented by  \N when using  the text  file import  or
export formats  (LOAD  DATA INFILE,  SELECT  ... INTO  OUTFILE).  See
section  7.15 LOAD DATA INFILE syntax.

7.1.5 Database, table, index, column and alias names
Database, table, index, column  and alias names  all follow the  same
rules in MySQL:

A name   may consist  of alphanumeric   characters from  the  current
character set and  also `_'  and `$'.  The default  character set  is
ISO-8859-1 Latin1;  this may  be changed  by recompiling   MySQL. See
section  9.1.1 The charater set used for data and sorting.

A database, table, index  or column name can  be up to 64  characters
long. An alias name can be up to 256 characters long.

A name may  start with  any character  that is  legal in  a name.  In
particular, a name may  start with a  number (this differs from  many
other database  systems!). However,  a name  cannot consist{{<EM>
}}
only{{</EM>
}}
o
f
 numbers.

It is  recommended that  you do  not use  names like  1e, because  an
expression like  1e+1 is  ambiguous.  It may  be interpreted   as the
expression 1e + 1 or as the number 1e+1.

You cannot  use the  `.' character  in names  because it  is used  to
extend the format by which you can refer to columns  (see immediately
below).

In MySQL you can refer to a column using any of the following forms:

+--------------------+---------------------------------------------+
| Column reference   |  Meaning                                    |
+--------------------+---------------------------------------------+
| col_name           | Column col_name from whichever table used   |
|                    | in the query contains a column of that name |
+--------------------+---------------------------------------------+
| tbl_name.col_name  | Column col_name from table tbl_name         |
|                    | of the current database                     |
+--------------------+---------------------------------------------+
| db_name.tbl        | Column col_name from table tbl_name         |
| _name.col_name     | of the database db_name. This form is       |
|                    | available in MySQL 3.22 or later.           |
+--------------------+---------------------------------------------+

You need  not specify  a tbl_name  or db_name.tbl_name  prefix for  a
column reference   in a   statement unless  the  reference  would  be
ambiguous. For  example, suppose  tables  t1 and  t2 each   contain a
column c, and you retrieve c in a SELECT statement that uses  both t1
and t2. In this case, c is  ambiguous because it is not unique  among
the tables used in  the statement, so  you must indicate which  table
you mean by writing  t1.c or t2.c.  Similarly, if you are  retrieving
from a table t in  database db1 and from  a table t in database  db2,
you must  refer to  columns  in those  tables as   db1.t.col_name and
db2.t.col_name.

 The syntax   .tbl_name means   the table  tbl_name  in  the  current
database. This  syntax is  accepted for  ODBC compatibility,  because
some ODBC programs prefix table names with a `.' character.


7.1.5.1 Case sensitivity in names

In MySQL, databases  and tables correspond  to directories and  files
within those directories. Consequently,  the case sensitivity of  the
underlying  operating  system  determines  the  case  sensitivity  of
database and table  names. This  means database and  table names  are
case sensitive in Unix and case insensitive in Win32.

Note: Although  database and  table names  are case   insensitive for
Win32, you  should not   refer to a   given database or  table  using
different cases within the same query. The following query  would not
work because it refers to a table both as my_table and as MY_TABLE:

SELECT * FROM my_table WHERE MY_TABLE.col=1;

Column names are case insensitive in all cases.
Aliases on tables are case  sensitive. The following query would  not
work because it refers to the alias both as a and as A:

mysql> SELECT col_name FROM tbl_name AS a
        WHERE a.col_name = 1 OR A.col_name = 2;

Aliases on columns are case insensitive.


7.2 Column types
MySQL supports a number  of column types,  which may be grouped  into
three categories:  numeric types,  date and  time types,   and string
(character) types. This section first gives an overview of  the types
available and  summarizes the  storage requirements  for each  column
type, then provides a more detailed description of the  properties of
the types in each category. The overview is intentionally  brief. The
more  detailed  descriptions  should   be consulted   for  additional
information about  particular column   types, such as  the  allowable
formats in which you can specify values.

The column types supported by  MySQL are listed below. The  following
code letters are used in the descriptions:

M       Indicates the maximum display size. The maximum legal display
        size is 255.
D       Applies to floating-point types  and indicates the number  of
        digits following the decimal point.
Square   brackets   (`['   and    `]')   indicate   parts   of   type      
       specifiers that are optional.

Note  that  if  you  specify   ZEROFILL for   a column,   MySQL  will
automatically add the UNSIGNED attribute to the column.

TINYINT[(M)] [UNSIGNED] [ZEROFILL]
 A very small integer. The signed range is -128 to 127.  The unsigned
range is 0 to 255.

SMALLINT[(M)] [UNSIGNED] [ZEROFILL]
 A small integer. The signed range  is -32768 to 32767. The  unsigned
range is 0 to 65535.

MEDIUMINT[(M)] [UNSIGNED] [ZEROFILL]
 A medium-size integer. The signed range is -8388608 to  8388607. The
unsigned range is 0 to 16777215.

NT[(M)] [UNSIGNED] [ZEROFILL]
 A  normal-size   integer.  The   signed range   is   -2147483648  to
2147483647. The unsigned range is 0 to 4294967295.

NTEGER[(M)] [UNSIGNED] [ZEROFILL]
 This is a synonym for INT.

BIGINT[(M)] [UNSIGNED] [ZEROFILL]
 A  large  integer.  The  signed  range  is  -9223372036854775808  to
9223372036854775807. The unsigned range is 0 to 18446744073709551615.
Note that  all  arithmetic is   done using signed   BIGINT or  DOUBLE
values, so   you shouldn't  use unsigned   big integers  larger  than
9223372036854775807 (63 bits) except with bit functions! Note that -,
+ and * will  use BIGINT arithmetic  when both arguments are  INTEGER
values! This means that if you multiply two big integers  (or results
from functions that return integers)  you may get unexpected  results
if the result is larger than 9223372036854775807.

FLOAT(precision) [ZEROFILL]
 A floating-point number. Cannot be  unsigned. precision can be 4  or
8.  FLOAT(4)  is   a single-precision   number  and   FLOAT(8) is   a
double-precision number. These  types are like  the FLOAT and  DOUBLE
types described  immediately below.  FLOAT(4) and  FLOAT(8) have  the
same ranges as the  corresponding FLOAT and  DOUBLE types, but  their
display size and number of decimals is undefined. In MySQL 3.23, this
is  a  true   floating point   value.  In   earlier MySQL   versions,
FLOAT(precision) always has 2 decimals.  This syntax is provided  for
ODBC compatibility.

FLOAT[(M,D)] [ZEROFILL]
 A   small  (single-precision)   floating-point  number.   Cannot  be
unsigned. Allowable values are -3.402823466E+38 to  -1.175494351E-38,
0 and 1.175494351E-38 to 3.402823466E+38.

DOUBLE[(M,D)] [ZEROFILL]
 A normal-size  (double-precision) floating-point  number. Cannot  be
unsigned.   Allowable   values    are   -1.7976931348623157E+308   to
-2.2250738585072014E-308,    0    and   2.2250738585072014E-308    to
1.7976931348623157E+308.

DOUBLE PRECISION[(M,D)] [ZEROFILL]

REAL[(M,D)] [ZEROFILL]
 These are synonyms for DOUBLE.

DECIMAL(M,D) [ZEROFILL]
 An unpacked floating-point number. Cannot be unsigned.  Behaves like
a CHAR column: ``unpacked'' means the  number is stored as a  string,
using one character for each digit  of the value, the decimal  point,
and, for negative numbers, the `-' sign. If D is 0, values  will have
no decimal point  or fractional  part. The maximum  range of  DECIMAL
values is the same as  for DOUBLE, but the  actual range for a  given
DECIMAL column may be constrained by the choice of M and D.  In MySQL
3.23 the M argument no longer includes the sign or the decimal point.
(This is according to ANSI SQL.)

NUMERIC(M,D) [ZEROFILL]
 This is a synonym for DECIMAL.

DATE
 A date. The supported range  is '1000-01-01' to '9999-12-31'.  MySQL
displays DATE values in 'YYYY-MM-DD' format, but allows you to assign
values to DATE columns using either strings or numbers.

DATETIME
 A date  and time  combination. The  supported range   is '1000-01-01
00:00:00' to '9999-12-31 23:59:59'. MySQL displays DATETIME values in
'YYYY-MM-DD HH:MM:SS'  format, but  allows you  to assign   values to
DATETIME columns using either strings or numbers.

TIMESTAMP[(M)]
 A timestamp. The range is  '1970-01-01 00:00:00' to sometime in  the
year  2037.  MySQL  displays   TIMESTAMP values   in  YYYYMMDDHHMMSS,
YYMMDDHHMMSS, YYYYMMDD or YYMMDD format, depending on whether M is 14
(or missing),   12, 8  or 6,   but allows  you to   assign values  to
TIMESTAMP columns using either strings or numbers. A TIMESTAMP column
is useful for  recording the  date and time  of an  INSERT or  UPDATE
operation because it is automatically set to the date and time of the
most recent operation if you don't give it a value yourself.  You can
also set it  to the  current date  and time  by assigning  it a  NULL
value. See section 7.2.6 Date and time types

TIME
 A time. The  range is  '-838:59:59' to  '838:59:59'. MySQL  displays
TIME values in 'HH:MM:SS' format, but allows you to assign  values to
TIME columns using either strings or numbers.

YEAR
 A year.  The allowable  values are  1901 to  2155,  and 0000.  MySQL
displays YEAR values in YYYY format, but allows you to  assign values
to YEAR columns using  either strings or  numbers. (The YEAR type  is
new in MySQL 3.22.)

CHAR(M) [BINARY]
 A fixed-length string that is always right-padded with spaces to the
specified length when stored. The range of M is 1 to  255 characters.
Trailing spaces are removed when the value is retrieved.  CHAR values
are sorted and compared in case-insensitive fashion unless the BINARY
keyword is given.

VARCHAR(M) [BINARY]
 A variable-length string. Note: Trailing spaces are removed when the
value is stored (this differs  from the ANSI SQL specification).  The
range of M  is 1  to 255 characters.  VARCHAR values  are sorted  and
compared in  case-insensitive fashion  unless the  BINARY keyword  is
given. See section 7.6.1 Silent column specification changes.

TINYBLOB

TINYTEXT
 A BLOB  or TEXT  column  with a  maximum length   of 255 (2^8  -  1)
characters. See section  7.6.1 Silent column specification changes.

BLOB

TEXT
 A BLOB or  TEXT column with  a maximum  length of 65535  (2^16 -  1)
characters. See section  7.6.1 Silent column specification changes.

MEDIUMBLOB

MEDIUMTEXT
 A BLOB or TEXT column with a  maximum length of 16777215 (2^24 -  1)
characters. See section  7.6.1 Silent column specification changes.


LONGBLOB

LONGTEXT
 A BLOB or TEXT column with a maximum length of 4294967295 (2^32 - 1)
characters. See section  7.6.1 Silent column specification changes.

ENUM('value1','value2',...)
 An enumeration. A string object that can have only one value, chosen
from the list of values 'value1', 'value2', ..., or NULL. An ENUM can
have a maximum of 65535 distinct values.

SET('value1','value2',...)
 A set. A string object  that can have zero  or more values, each  of
which must be chosen from the list of values 'value1',  'value2', ...
A SET can have a maximum of 64 members.

7.2.1 Column type storage requirements
The storage requirements for  each of the  column types supported  by
MySQL are listed below by category.

7.2.2 Numeric types
+--------------------+---------------------------+
|  Column type       |  Storage required         | 
+--------------------+---------------------------+
|  TINYINT           |  1 byte                   | 
+--------------------+---------------------------+
|  SMALLINT          |  2 bytes                  | 
+--------------------+---------------------------+
|  MEDIUMINT         |  3 bytes                  | 
+--------------------+---------------------------+
|  INT               |  4 bytes                  | 
+--------------------+---------------------------+
|  INTEGER           |  4 bytes                  | 
+--------------------+---------------------------+
|  BIGINT            |  8 bytes                  | 
+--------------------+---------------------------+
|  FLOAT(4)          |  4 bytes                  | 
+--------------------+---------------------------+
|  FLOAT(8)          |  8 bytes                  | 
+--------------------+---------------------------+
|  FLOAT             |  4 bytes                  | 
+--------------------+---------------------------+
|  DOUBLE            |  8 bytes                  | 
+--------------------+---------------------------+
|  DOUBLE PRECISION  |  8 bytes                  | 
+--------------------+---------------------------+
|  REAL              |  8 bytes                  | 
+--------------------+---------------------------+
|  DECIMAL(M,D)      |  M bytes (D+2, if M < D)  | 
+--------------------+---------------------------+
|  NUMERIC(M,D)      |  M bytes (D+2, if M < D)  | 
+--------------------+---------------------------+

7.2.3 Date and time types
+--------------------+---------------------------+
|  Column type       |  Storage required         | 
+--------------------+---------------------------+
|  DATETIME          |  8 bytes                  |
+--------------------+---------------------------+
|  DATE              |  3 bytes                  |
+--------------------+---------------------------+
|  TIMESTAMP         |  4 bytes                  |
+--------------------+---------------------------+
|  TIME              |  3 bytes                  |
+--------------------+---------------------------+
|  YEAR              |  1 byte                   |
+--------------------+---------------------------+
+--------------------------+--------------------------------------+
|  Column type             |  Storage required                    |
+--------------------------+--------------------------------------+
|  CHAR(M)                 |  M bytes, 1 <= M <= 255              |
+--------------------------+--------------------------------------+
|  VARCHAR(M)              |  L+1 bytes, where L <= M and         |
|                          |  1 <= M <= 255                       |
+--------------------------+--------------------------------------+
|  TINYBLOB, TINYTEXT      |  L+1 bytes, where L < 2^8            |
+--------------------------+--------------------------------------+
|  BLOB, TEXT              |  L+2 bytes, where L < 2^16           |
+--------------------------+--------------------------------------+
|  MEDIUMBLOB, MEDIUMTEXT  |  L+3 bytes, where L < 2^24           |
+--------------------------+--------------------------------------+
|  LONGBLOB, LONGTEXT      |  L+4 bytes, where L < 2^32           |
+--------------------------+--------------------------------------+
|  ENUM('value1',          |  1 or 2 bytes, depending             |
|  'value2',...)           |  on the number of enumeration values |
|                          |  (65535 values maximum)              |
+--------------------------+--------------------------------------+
|  SET('value1',           |  1, 2, 3, 4 or 8 bytes, depending    |
|  'value2',...)           |  on the number of set members        |
|                          |  (64 members maximum)                |
+--------------------------+--------------------------------------+

VARCHAR and the BLOB  and TEXT types  are variable-length types,  for
which the storage requirements depend on the actual length  of column
values (represented by L in the preceding table), rather than  on the
type's maximum possible size. For  example, a VARCHAR(10) column  can
hold a string  with a  maximum length  of 10  characters. The  actual
storage required is  the length  of the string  (L), plus  1 byte  to
record the length of the  string. For the string  'abcd', L is 4  and
the storage requirement is 5 bytes.

The BLOB and  TEXT types require  1, 2, 3  or 4  bytes to record  the
length of the column value, depending on the maximum  possible length
of the type.

If a  table includes  any variable-length  column types,   the record
format will  also  be variable-length.   Note that when   a table  is
created, MySQL may under  certain conditions change  a column from  a
variable-length type   to a  fixed-length type,   or vice-versa.  See
section  7.6.1 Silent column specification changes.

The size of an ENUM object  is determined by the number of  different
enumeration values. 1 byte  is used for  enumerations with up to  255
possible values. 2 bytes are used  for enumerations with up to  65535
values.

The size of a SET object is determined by the number of different set
members. If the  set size is  N, the  object occupies (N+7)/8  bytes,
rounded up to 1, 2, 3, 4 or 8  bytes. A SET can have a maximum of  64
members.

7.2.5 Numeric types
All integer types can have  an optional attribute UNSIGNED.  Unsigned
values can be used when you want to allow only positive numbers  in a
column and you need a little bigger numeric range for the column.

All numeric types can have an optional attribute ZEROFILL. Values for
ZEROFILL columns   are left-padded  with zeroes   up to  the  maximum
display length when  they are  displayed. For example,  for a  column
declared as INT(5) ZEROFILL, a value of 4 is retrieved as 00004.

When asked to store a value in  a numeric column that is outside  the
column  type's  allowable  range,   MySQL clips   the value   to  the
appropriate endpoint  of the  range and  stores the   resulting value
instead.

For example, the range of an INT column is -2147483648 to 2147483647.
If you try  to insert -9999999999  into an INT  column, the value  is
clipped to the lower endpoint of the range, and -2147483648 is stored
instead. Similarly, if  you try to  insert 9999999999, 2147483647  is
stored instead.
If the INT column is UNSIGNED, the size of the column's range  is the
same but its endpoints shift  up to 0 and  4294967295. If you try  to
store -9999999999 and  9999999999, the  values stored  in the  column
become 0 and 4294967296.

Conversions that occur due to  clipping are reported as  ``warnings''
for ALTER   TABLE, LOAD  DATA INFILE,   UPDATE and  multi-row  INSERT
statements.

The maximum display size (M) and number of decimals (D) are  used for
formatting and calculation of maximum column width.

MySQL will store any value that fits a column's storage type  even if
the value exceeds the display size. For example, an INT(4) column has
a display size of 4. Suppose you insert a value which has more than 4
digits into the column, such as 12345. The display size  is exceeded,
but the allowable range of the INT  type is not, so MySQL stores  the
actual value, 12345. When retrieving the value from the column, MySQL
returns the actual value stored in the column.

The DECIMAL type  is considered a  numeric type  (as is its  synonym,
NUMERIC), but such  values are  stored as strings.  One character  is
used for each digit of  the value, the decimal  point (if D > 0)  and
the `-' sign (for negative numbers).  If D is 0, DECIMAL and  NUMERIC
values contain no decimal point or fractional part.

The maximum range of  DECIMAL values is the  same as for DOUBLE,  but
the actual range for a given DECIMAL column may be constrained by the
choice of   M and  D.  For example,   a type  specification  such  as
DECIMAL(4,2) indicates a maximum length  of four characters with  two
digits after the decimal  point. Due to the  way the DECIMAL type  is
stored, this specification results in  an allowable range of -.99  to
9.99, much less than the range of a DOUBLE.

To avoid some rounding problems, MySQL always rounds  everything that
it stores  in any  floating-point column  to the  number of  decimals
indicated by the column specification. Suppose you have a column type
of FLOAT(8,2). The number of decimals is 2, so a value such  as 2.333
is rounded to two decimals and stored as 2.33.

7.2.6 Date and time types
The date and time types are DATETIME, DATE, TIMESTAMP, TIME and YEAR.
Each of these  has a range  of legal  values, as well  as a  ``zero''
value that is used when you specify an illegal value.
Here are some  general considerations  to keep in  mind when  working
with date and time types:

MySQL retrieves values for  a given date or  time type in a  standard
format, but it attempts to interpret a variety of formats  for values
that you supply (e.g., when you specify a value to be assigned  to or
compared to  a date  or time  type). Nevertheless,  only the  formats
described in the  following sections  are supported.  It is  expected
that you  will supply  legal values,  and unpredictable   results may
occur if you use values in other formats.

Although MySQL  tries  to interpret  values  in several  formats,  it
always expects the  year part of  date values  to be leftmost.  Dates
must be given in year-month-day order (e.g., '98-09-04'), rather than
in  the   month-day-year or   day-month-year   orders commonly   used
elsewhere (e.g., '09-04-98', '04-09-98').

MySQL automatically converts a date or time type value to a number if
the value is used in a numeric context, and vice versa.

When MySQL encounters a value for a date or time type that is  out of
range or otherwise illegal for the type, it converts the value to the
``zero'' value for  that type.  (The exception  is that  out-of-range
TIME values  are clipped  to  the appropriate  endpoint of   the TIME
range.) The table below  shows the format  of the ``zero'' value  for
each type:

+---------------+------------------------------------+
|  Column type  |  ``Zero'' value                    | 
+---------------+------------------------------------+
|  DATETIME     |  '0000-00-00 00:00:00'             | 
+---------------+------------------------------------+
|  DATE         |  '0000-00-00'                      | 
+---------------+------------------------------------+
|  TIMESTAMP    |  00000000000000                    | 
|               |  (length depends on display size)  | 
+---------------+------------------------------------+
|  TIME         |  '00:00:00'                        | 
+---------------+------------------------------------+
|  YEAR         |  0000                              | 
+---------------+------------------------------------+

The ``zero'' values are special, but  you can store or refer to  them
explicitly using the values shown in the table. You can also  do this
using the values '0' or 0, which are easier to write.
``Zero'' date   or time  values used   through MyODBC  are  converted
automatically to NULL in MyODBC 2.50.12 and above, because ODBC can't
handle such values.

7.2.6.1 Y2K issues and date types
MySQL itself is Y2K-safe (see section  1.6 Year 2000 compliance), but
input values presented to MySQL may not be. Any input containing
2-digit year values is ambiguous, since the century is unknown. Such
values must be interpreted into 4-digit form since MySQL stores years
internally using four digits.

For DATETIME, DATE, TIMESTAMP and YEAR types, MySQL interprets dates
with ambiguous year values using the following rules:
Year values in the range 00-69 are converted to 2000-2069.
Year values in the range 70-99 are converted to 1970-1999.
Remember that these rules provide only reasonable guesses as to what
your data mean. If the heuristics used by MySQL don't produce the
correct values, you should provide unambiguous input containing
4-digit year values.

7.2.6.2 The DATETIME, DATE and TIMESTAMP types
The DATETIME, DATE and TIMESTAMP types are related. This section
describes their characteristics, how they are similar and how they
differ.

The DATETIME type is used when you need values that contain both date
and time information. MySQL retrieves and displays DATETIME values in
'YYYY-MM-DD HH:MM:SS'  format.  The supported   range is  '1000-01-01
00:00:00'  to   '9999-12-31  23:59:59'.  (``Supported''   means  that
although earlier values might work,  there is no guarantee that  they
will.)

The DATE type is used when you need only a date value, without a time
part. MySQL   retrieves and   displays DATE  values  in  'YYYY-MM-DD'
format. The supported range is '1000-01-01' to '9999-12-31'.
The TIMESTAMP  column  type provides   a type  that you   can use  to
automatically mark INSERT or UPDATE operations with the  current date
and time. If you have multiple TIMESTAMP columns, only the  first one
is updated automatically.

Automatic updating of the first TIMESTAMP column occurs under  any of
the following conditions:

The column is  not specified  explicitly in  an INSERT  or LOAD  DATA
INFILE statement.

The column is  not specified  explicitly in an  UPDATE statement  and
some other column  changes value. (Note  that an  UPDATE that sets  a
column to  the value  it already  has will  not  cause the  TIMESTAMP
column to be  updated, because  if you set  a column  to its  current
value, MySQL ignores the update for efficiency.)

You explicitly set the TIMESTAMP column to NULL.

TIMESTAMP columns other than the first may also be set to the current
date and time. Just set the column to NULL, or to NOW().
You can  set any   TIMESTAMP column to   a value different  than  the
current date and time by setting it explicitly to the  desired value.
This is true even  for the first TIMESTAMP  column. You can use  this
property if,  for example,  you want  a TIMESTAMP  to be  set to  the
current date and time when  you create a row,  but not to be  changed
whenever the row is updated later:

Let MySQL   set the   column when   the row  is  created.  This  will
initialize it to the current date and time.

When you perform subsequent updates to other columns in the  row, set
the TIMESTAMP column explicitly to its current value.

On the other hand,  you may find  it just as easy  to use a  DATETIME
column that you initialize to NOW() when the row is created and leave
alone for subsequent updates.

TIMESTAMP values may range from the beginning of 1970 to  sometime in
the year 2037, with a resolution of one second. Values  are displayed
as numbers.

The format in  which MySQL  retrieves and  displays TIMESTAMP  values
depends on the display size, as  illustrated by the table below.  The
`full' TIMESTAMP format is  14 digits, but  TIMESTAMP columns may  be
created with shorter display sizes:

+-----------------+------------------+
|  Column type    |  Display format  | 
+-----------------+------------------+
|  TIMESTAMP(14)  |  YYYYMMDDHHMMSS  | 
+-----------------+------------------+
|  TIMESTAMP(12)  |  YYMMDDHHMMSS    | 
+-----------------+------------------+
|  TIMESTAMP(10)  |  YYMMDDHHMM      | 
+-----------------+------------------+
|  TIMESTAMP(8)   |  YYYYMMDD        | 
+-----------------+------------------+
|  TIMESTAMP(6)   |  YYMMDD          | 
+-----------------+------------------+
|  TIMESTAMP(4)   |  YYMM            | 
+-----------------+------------------+
|  TIMESTAMP(2)   |  YY              | 
+-----------------+------------------+

All TIMESTAMP  columns  have the  same  storage size,  regardless  of
display size. The most common display sizes are 6, 8, 12, and 14. You
can specify an  arbitrary display  size at table  creation time,  but
values of 0 or greater than 14 are coerced to 14. Odd-valued sizes in
the range from 1 to 13 are coerced to the next higher even number.
You can specify DATETIME,  DATE and TIMESTAMP  values using any of  a
common set of formats:

As a string  in either 'YYYY-MM-DD  HH:MM:SS' or 'YY-MM-DD  HH:MM:SS'
format. A ``relaxed''  syntax is  allowed--any non-numeric  character
may be used as  the delimiter between date  parts or time parts.  For
example,   '98-12-31   11:30:45',  '98.12.31   11+30+45',   '98/12/31
11*30*45' and '98@12@31 11^30^45' are equivalent.

As  a  string   in either   'YYYY-MM-DD'  or   'YY-MM-DD' format.   A
``relaxed'' syntax  is allowed  here, too.  For example,  '98-12-31',
'98.12.31', '98/12/31' and '98@12@31' are equivalent.

As  a  string  with  no  delimiters  in  either  'YYYYMMDDHHMMSS'  or
'YYMMDDHHMMSS' format,  provided that  the string  makes sense   as a
date.   For   example,   '19970523091528'  and   '970523091528'   are
interpreted as '1997-05-23 09:15:28',  but '971122459015' is  illegal
(it has a nonsensical minute part) and becomes '0000-00-00 00:00:00'.
As a  string with  no  delimiters in  either 'YYYYMMDD'   or 'YYMMDD'
format, provided that the string makes sense as a date.  For example,
'19970523' and '970523' are interpreted as '1997-05-23', but '971332'
is illegal  (it has  nonsensical  month and  day parts)   and becomes
'0000-00-00'.

As a number in either YYYYMMDDHHMMSS or YYMMDDHHMMSS format, provided
that the number makes  sense as a  date. For example,  19830905132800
and 830905132800 are interpreted as '1983-09-05 13:28:00'.

As a number in  either YYYYMMDD or  YYMMDD format, provided that  the
number makes sense as  a date. For  example, 19830905 and 830905  are
interpreted as '1983-09-05'.

As the result of a function  that returns a value that is  acceptable
in  a  DATETIME,  DATE  or   TIMESTAMP context,   such as   NOW()  or
CURRENT_DATE.

Illegal DATETIME,  DATE  or TIMESTAMP  values  are converted  to  the
``zero''  value  of  the  appropriate  type  ('0000-00-00  00:00:00',
'0000-00-00' or 00000000000000).

For values specified as strings that include date part delimiters, it
is not necessary to specify two  digits for month or day values  that
are less than 10. '1979-6-9' is the same as  '1979-06-09'. Similarly,
for values specified as strings that include time part delimiters, it
is not necessary  to specify  two digits  for hour,  month or  second
values that  are less  than 10.  '1979-10-30 1:2:3'  is  the same  as
'1979-10-30 01:02:03'.

Values specified as numbers should be 6, 8, 12 or 14 digits  long. If
the number is 8 or 14 digits long, it is assumed to be in YYYYMMDD or
YYYYMMDDHHMMSS format  and that  the year  is given  by  the first  4
digits. If the number is 6 or 12 digits long, it is assumed to  be in
YYMMDD or YYMMDDHHMMSS format and that the year is given by the first
2 digits. Numbers that are not  one of these lengths are  interpreted
as though padded with leading zeros to the closest length.

Values specified as non-delimited strings are interpreted using their
length as given. If the string is  8 or 14 characters long, the  year
is assumed to be given by the first 4 characters. Otherwise  the year
is assumed  to be  given by  the first  2 characters.  The string  is
interpreted from left to right to find year, month, day, hour, minute
and second values, for  as many parts as  are present in the  string.
This means   you should  not  use strings   that have  fewer  than  6
characters. For example,  if you specify  '9903', thinking that  will
represent March, 1999, you  will find that  MySQL inserts a  ``zero''
date into your table. This is  because the year and month values  are
99 and 03, but the day part is missing (zero), so the value is  not a
legal date.

TIMESTAMP columns store  legal values using  the full precision  with
which the value was specified,  regardless of the display size.  This
has several implications:

Always specify year, month,  and day, even  if your column types  are
TIMESTAMP(4) or  TIMESTAMP(2). Otherwise,  the value  will not   be a
legal date and 0 will be stored.

If  you  use  ALTER  TABLE   to widen   a narrow   TIMESTAMP  column,
information will be displayed that previously was ``hidden''.
Similarly, narrowing a TIMESTAMP column does not cause information to
be lost, except in the sense that less information is shown  when the
values are displayed.

Although TIMESTAMP  values are  stored to  full precision,   the only
function that operates  directly on  the underlying  stored value  is
UNIX_TIMESTAMP(). Other functions operate on the formatted  retrieved
value. This means you cannot use functions such as HOUR() or SECOND()
unless the relevant part  of the TIMESTAMP  value is included in  the
formatted value. For example,  the HH part  of a TIMESTAMP column  is
not displayed unless the  display size is at  least 10, so trying  to
use HOUR() on shorter TIMESTAMP values produces a meaningless result.
You can to some extent assign values of one date type to an object of
a different date type. However, there  may be some alteration of  the
value or loss of information:

If you assign  a DATE value  to a DATETIME  or TIMESTAMP object,  the
time part of the  resulting value is  set to '00:00:00', because  the
DATE value contains no time information.

If you assign  a DATETIME or  TIMESTAMP value to  a DATE object,  the
time part of the  resulting value is  deleted, because the DATE  type
stores no time information.

Remember that although DATETIME, DATE and TIMESTAMP values all can be
specified using the same  set of formats, the  types do not all  have
the same range  of values.  For example, TIMESTAMP  values cannot  be
earlier than 1970 or later than 2037. This means that a date  such as
'1968-01-01', while legal as a DATETIME or DATE value, is not a valid
TIMESTAMP value and  will be converted  to 0 if  assigned to such  an
object.

Be aware of certain pitfalls when specifying date values:

The relaxed format  allowed for  values specified as  strings can  be
deceiving. For example, a value such as '10:11:12' might look  like a
time value  because of  the `:'   delimiter, but if  used in   a date
context will  be  interpreted as  the  year '2010-11-12'.  The  value
'10:45:15' will be converted  to '0000-00-00' because  '45' is not  a
legal month.

Year values specified as two digits are ambiguous, since  the century
is unknown. MySQL interprets 2-digit year values using  the following
rules:

Year values in the range 00-69 are converted to 2000-2069.
Year values in the range 70-99 are converted to 1970-1999.

7.2.6.3 The TIME type
MySQL retrieves and  displays TIME  values in  'HH:MM:SS' format  (or
'HHH:MM:SS' format for  large hours  values). TIME  values may  range
from '-838:59:59' to '838:59:59'. The reason the hours part may be so
large is that the TIME type may be used not only to represent  a time
of day (which must be less than 24 hours), but also elapsed time or a
time interval between two events (which  may be much greater than  24
hours, or even negative).

You can specify TIME values in a variety of formats:

As  a   string  in  'HH:MM:SS'   format.  A  ``relaxed''   syntax  is
allowed--any non-numeric   character may  be used   as the  delimiter
between time   parts. For   example, '10:11:12'  and  '10.11.12'  are
equivalent.

As a string with no delimiters  in 'HHMMSS' format, provided that  it
makes sense   as a  time.  For example,   '101112' is  understood  as
'10:11:12', but  '109712' is  illegal (it  has a   nonsensical minute
part) and becomes '00:00:00'.

As a number in HHMMSS format, provided that it makes sense as a time.
For example, 101112 is understood as '10:11:12'.

As the result of a function  that returns a value that is  acceptable
in a TIME context, such as CURRENT_TIME.
For TIME   values specified  as  strings that   include a  time  part
delimiter, it  is not  necessary  to specify  two digits   for hours,
minutes or seconds values that are less than 10. '8:3:2' is  the same
as '08:03:02'.

Be careful about assigning  ``short'' TIME values  to a TIME  column.
MySQL interprets   values using  the assumption   that the  rightmost
digits represent seconds.  (MySQL interprets TIME  values as  elapsed
time, rather than as  time of day.) For  example, you might think  of
'11:12', '1112' and 1112 as  meaning '11:12:00' (12 minutes after  11
o'clock), but MySQL  interprets them  as '00:11:12'  (11 minutes,  12
seconds). Similarly, '12' and 12 are interpreted as '00:00:12'.

Values that lie outside  the TIME range  but are otherwise legal  are
clipped to   the appropriate  endpoint of   the range.  For  example,
'-850:00:00'  and  '850:00:00'  are  converted  to  '-838:59:59'  and
'838:59:59'.

Illegal TIME  values are  converted to  '00:00:00'. Note   that since
'00:00:00' is itself  a legal TIME  value, there is  no way to  tell,
from a value of  '00:00:00' stored in  a table, whether the  original
value was specified as '00:00:00' or whether it was illegal.

7.2.6.4 The YEAR type

The YEAR type is a 1-byte type used for representing years.
MySQL retrieves and displays YEAR values in YYYY format. The range is
1901 to 2155.

You can specify YEAR values in a variety of formats:

As a four-digit string in the range '1901' to '2155'.
As a four-digit number in the range 1901 to 2155.
As a two-digit string in the range '00' to '99'. Values in the ranges
'00' to '69' and  '70' to '99'  are converted to  YEAR values in  the
ranges 2000 to 2069 and 1970 to 1999.

As a two-digit number in the range 1 to 99. Values in the ranges 1 to
69 and 70 to 99  are converted to YEAR  values in the ranges 2001  to
2069 and 1970 to 1999. Note  that the range for two-digit numbers  is
slightly different than  the range for  two-digit strings, since  you
cannot specify zero directly as a  number and have it be  interpreted
as 2000. You must specify  it as a string '0'  or '00' or it will  be
interpreted as 0000.

As the result of a function  that returns a value that is  acceptable
in a YEAR context, such as NOW().

Illegal YEAR values are converted to 0000.

7.2.7 String types
The string types are CHAR, VARCHAR, BLOB, TEXT, ENUM and SET.

7.2.7.1 The CHAR and VARCHAR types
The CHAR and VARCHAR  types are similar, but  differ in the way  they
are stored and retrieved.

The length of a CHAR column is  fixed to the length that you  declare
when you create the table. The length can be any value between  1 and
255. When CHAR values are  stored, they are right-padded with  spaces
to the specified  length. When  CHAR values  are retrieved,  trailing
spaces are removed.

Values in   VARCHAR columns   are variable-length  strings.  You  can
declare a VARCHAR column to be any length between 1 and 255,  just as
for CHAR columns. However,  in contrast to  CHAR, VARCHAR values  are
stored using only as many characters as are needed, plus one  byte to
record the length.  Values are not  padded; instead, trailing  spaces
are removed when values are stored. (This space removal  differs from
the ANSI SQL specification.)

If you assign a value  to a CHAR or  VARCHAR column that exceeds  the
column's maximum length, the value is truncated to fit.

The table below illustrates the differences between the two  types of
columns by showing the result  of storing various string values  into
CHAR(4) and VARCHAR(4) columns:

+------------+---------+----------+------------+-----------------+
| Value      | CHAR(4) | Storage  | VARCHAR(4) | Storage require |
|            |         | required |            | required        |
+------------+---------+----------+------------+-----------------+
| ''         | ' '     | 4 bytes  | ''         | 1 byte          |
+------------+---------+----------+------------+-----------------+
| 'ab'       | 'ab '   | 4 bytes  | 'ab'       | 3 bytes         |
+------------+---------+----------+------------+-----------------+
| 'abcd'     | 'abcd'  | 4 bytes  | 'abcd'     | 5 bytes         |
+------------+---------+----------+------------+-----------------+
| 'abcdefgh' | 'abcd'  | 4 bytes  | 'abcd'     | 5 bytes         |
+------------+---------+----------+------------+-----------------+
The values retrieved from the CHAR(4) and VARCHAR(4) columns  will be
the same in each case, because trailing spaces are removed  from CHAR
columns upon retrieval.

Values in   CHAR and  VARCHAR  columns are   sorted and  compared  in
case-insensitive fashion, unless the  BINARY attribute was  specified
when the table was  created. The BINARY  attribute means that  column
values are sorted and compared in case-sensitive fashion according to
the ASCII order of the machine where the MySQL server is running.

The BINARY  attribute is  ``sticky''.  This means  that if   a column
marked BINARY  is used  in  an expression,  the whole   expression is
compared as a BINARY value.

MySQL may silently change the type of a CHAR or VARCHAR column at
table creation time. See section  7.6.1 Silent column specification
changes.

7.2.7.2 The BLOB and TEXT types

A BLOB is a binary  large object that can  hold a variable amount  of
data. The four  BLOB types  TINYBLOB, BLOB,  MEDIUMBLOB and  LONGBLOB
differ only in the  maximum length of the  values they can hold.  See
section  7.2.1 Column type storage requirements.

The  four   TEXT types   TINYTEXT,  TEXT,   MEDIUMTEXT and   LONGTEXT
correspond to the four BLOB types  and have the same maximum  lengths
and storage requirements. The only  difference between BLOB and  TEXT
types is that sorting and  comparison is performed in  case-sensitive
fashion for BLOB values and case-insensitive fashion for TEXT values.
In other words, a TEXT is a case-insensitive BLOB.

If you assign  a value  to a  BLOB or  TEXT column  that exceeds  the
column type's maximum length, the value is truncated to fit.

In most respects, you  can regard a TEXT  column as a VARCHAR  column
that can be  as big as  you like.  Similarly, you can  regard a  BLOB
column as a VARCHAR BINARY column. The differences are:

You can have  indexes on BLOB  and TEXT  columns with MySQL  versions
3.23.2 and newer. Older versions of MySQL did not support this.

There is no  trailing-space removal  for BLOB and  TEXT columns  when
values are stored, as there is for VARCHAR columns.
BLOB and TEXT columns cannot have DEFAULT values.

MyODBC defines   BLOB values  as LONGVARBINARY   and TEXT  values  as
LONGVARCHAR.

Because BLOB and TEXT  values may be extremely  long, you may run  up
against some constraints when using them:

If you want to use GROUP BY or ORDER BY on a BLOB or TEXT column, you
must convert   the column   value into  a  fixed-length  object.  The
standard way to do this is with the SUBSTRING function. For example:

mysql> select comment  from tbl_name,substring(comment,20) as  substr
        ORDER BY substr;

If you don't  do this, only  the first  max_sort_length bytes of  the
column are used when sorting. The default value of max_sort_length is
1024; this value can be changed using the -O option when starting the
mysqld server. You can group on an expression involving BLOB  or TEXT
values by specifying the column position or by using an alias:

mysql> select id,substring(blob_col,1,100) from tbl_name
           GROUP BY 2;
mysql> select id,substring(blob_col,1,100) as b from tbl_name
           GROUP BY b;

The maximum size of a BLOB or TEXT object is determined by  its type,
but the largest value  you can actually  transmit between the  client
and server is determined  by the amount  of available memory and  the
size of the communications buffers. You can change the message buffer
size, but you  must do so  on both  the server and  client ends.  See
section  10.1 Tuning server parameters.
Note that each  BLOB or  TEXT value  is represented  internally by  a
separately-allocated object. This is in contrast to all  other column
types, for which storage is allocated once per column when  the table
is opened.

7.2.7.3 The ENUM type
An ENUM is a string object whose value normally is chosen from a list
of allowed   values that  are enumerated   explicitly in  the  column
specification at table creation time.

The value may  also be the  empty string ("")  or NULL under  certain
circumstances:

If you insert an invalid  value into an ENUM  (that is, a string  not
present in the list of allowed values), the empty string  is inserted
instead as a special error value.

If an ENUM  is declared  NULL, NULL  is also  a legal  value for  the
column, and the  default value is  NULL. If an  ENUM is declared  NOT
NULL, the default value is the  first element of the list of  allowed
values.

Each enumeration value has an index:

Values  from   the  list   of  allowable   elements  in   the  column
specification are numbered beginning with 1.

The index value of the empty string error value is 0. This means that
you can use the  following SELECT statement  to find rows into  which
invalid ENUM values were assigned:

mysql> SELECT * FROM tbl_name WHERE enum_col=0;

The index of the NULL value is NULL.
For example, a  column specified as  ENUM("one", "two", "three")  can
have any of the values shown below.  The index of each value is  also
shown:
+-----------+---------+
|  Value    |  Index  | 
+-----------+---------+
|  NULL     |   NULL  | 
+-----------+---------+
|  ""       |   0     | 
+-----------+---------+
|  "one"    |   1     | 
+-----------+---------+
|  "two"    |   2     | 
+-----------+---------+
|  "three"  |   3     | 
+-----------+---------+
An enumeration can have a maximum of 65535 elements.
Lettercase is irrelevant when  you assign values  to an ENUM  column.
However, values   retrieved from  the column   later have  lettercase
matching the values that were used to specify the allowable values at
table creation time.

If you retrieve  an ENUM  in a  numeric context,  the column  value's
index is returned. If you store a number into an ENUM, the  number is
treated as an  index, and  the the  value stored  is the  enumeration
member with that index.

ENUM  values  are  sorted  according   to the   order in   which  the
enumeration members  were listed   in the column  specification.  (In
other  words,  ENUM  values  are  sorted  according  to  their  index
numbers.) For example, "a" sorts  before "b" for ENUM("a", "b"),  but
"b" sorts  before "a"  for  ENUM("b", "a").  The empty   string sorts
before non-empty  strings,  and NULL  values  sort before  all  other
enumeration values.

If you want to get all possible values for an ENUM column, you should
use: SHOW COLUMNS FROM table_name LIKE enum_column_name and parse the
ENUM definition in the second column.

7.2.7.4 The SET type
A SET is a string object that  can have zero or more values, each  of
which must be chosen from a list of allowed values specified when the
table is  created. SET  column values  that consist  of multiple  set
members are  specified  with members  separated  by commas  (`,').  A
consequence of  this  is that  SET  member values  cannot  themselves
contain commas.

For example, a  column specified  as SET("one", "two")  NOT NULL  can
have any of these values:

""
"one"
"two"
"one,two"

A SET can have a maximum of 64 different members.

MySQL stores SET values  numerically, with the  low-order bit of  the
stored value corresponding to the first set member. If you retrieve a
SET value in  a numeric  context, the  value retrieved  has bits  set
corresponding to the set members that make up the column value.  If a
number is stored  into a SET  column, the  bits that are  set in  the
binary representation of the number determine the set members  in the
column value. Suppose a column is specified  as SET("a","b","c","d").
Then the members have the following bit values:
+--------------+------------------+----------------+
|  SETmember   |  Decimal value   |  Binary value  | 
+--------------+------------------+----------------+
|  a           |  1               |  0001          | 
+--------------+------------------+----------------+
|  b           |  2               |  0010          | 
+--------------+------------------+----------------+
|  c           |  4               |  0100          | 
+--------------+------------------+----------------+
|  d           |  8               |  1000          | 
+--------------+------------------+----------------+
If you assign a value of 9 to this column, that is 1001 in binary, so
the first and fourth SET value  members "a" and "d" are selected  and
the resulting value is "a,d".

For a value containing more than one SET element, it does  not matter
what order the elements are listed  in when you insert the value.  It
also doesn't not matter how many  times a given element is listed  in
the value. When  the value is  retrieved later,  each element in  the
value will appear once, with  elements listed according to the  order
in which they were specified at table creation time. For  example, if
a column is specified as SET("a","b","c","d"), then "a,d",  "d,a" and
"d,a,a,d,d" will all appear as "a,d" when retrieved.

SET  values   are  sorted   numerically. NULL   values   sort  before
non-NULLSET values.

Normally, you  perform  a SELECT   on a  SET column   using the  LIKE
operator or the FIND_IN_SET() function:

mysql> SELECT * FROM tbl_name WHERE set_col LIKE '%value%';
mysql> SELECT * FROM tbl_name WHERE FIND_IN_SET('value',set_col)>

But the following will also work:

mysql> SELECT * FROM tbl_name WHERE set_col = 'val1,val2';
mysql> SELECT * FROM tbl_name WHERE set_col & 1;

The first of these  statements looks for  an exact match. The  second
looks for values containing the first set member.

If you want to get all possible values for an SET column,  you should
use: SHOW COLUMNS FROM table_name LIKE set_column_name and  parse the
SET definition in the second column.


7.2.8 Choosing the right type for a column
For the most efficient  use of storage, try  to use the most  precise
type in all cases. For example, if an integer column will be used for
values in the range  between 1 and  99999, MEDIUMINT UNSIGNED is  the
best type.

Accurate representation of  monetary values is  a common problem.  In
MySQL, you should use the DECIMAL  type. This is stored as a  string,
so no   loss of   accuracy should   occur. If  accuracy  is  not  too
important, the DOUBLE type may also be good enough.

For high  precision, you  can always  convert to  a fixed-point  type
stored in  a BIGINT.  This allows  you to  do  all calculations  with
integers and convert results back to floating-point values  only when
necessary.

See section  10.17 What are the different row formats? Or, when
should VARACHAR/.

7.2.9 Column indexes
All MySQL column types can be indexed except BLOB and TEXT types. Use
of indexes on  the relevant columns  is the best  way to improve  the
performance of SELECT operations.

A table may have up  to 16 indexes. The  maximum index length is  256
bytes, although this may be changed when compiling MySQL.
You cannot index a  column that may  contain NULL values, so  indexed
columns must be declared NOT NULL.

For CHAR and  VARCHAR columns, you  can index a  prefix of a  column.
This is much faster  and requires less  disk space than indexing  the
whole column. The  syntax to  use in  the CREATE  TABLE statement  to
index a column prefix looks like this:

KEY index_name (col_name(length))

The example below creates an index for the first 10 characters of the
name column:

mysql> CREATE TABLE test (
           name CHAR(200) NOT NULL,
           KEY index_name (name(10)));

7.2.10 Multiple-column indexes

MySQL can create indexes on multiple columns. An index may consist of
up to 15 columns.  (On CHAR and  VARCHAR columns you  can also use  a
prefix of the column as a part of an index).

A multiple-column index can be  considered a sorted array  containing
values that are created  by concatenating the  values of the  indexed
columns.

MySQL uses multiple-column  indexes in  such a way  that queries  are
fast when you specify  a known quantity for  the first column of  the
index in a  WHERE clause, even  if you don't  specify values for  the
other columns.

Suppose a table is created using the following specification:
mysql> CREATE TABLE test (
                id INT NOT NULL,
                last_name CHAR(30) NOT NULL,
                first_name CHAR(30) NOT NULL,
                PRIMARY KEY (id),
                INDEX name (last_name,first_name));

Then the index name  is an index  over last_name and first_name.  The
index will be used for queries  that specify values in a known  range
for last_name, or for both  last_name and first_name. Therefore,  the
name index will be used in the following queries:

mysql> SELECT * FROM test WHERE last_name="Widenius";
mysql> SELECT * FROM test WHERE last_name="Widenius"
                          AND first_name="Michael";
mysql> SELECT * FROM test WHERE last_name="Widenius"
                          AND         (first_name="Michael"        OR          
               first_name="Monty");
mysql> SELECT * FROM test WHERE last_name="Widenius"
                          AND first_name >="M" AND first_name < "N";
However, the name index will NOT be used in the following queries:
mysql> SELECT * FROM test WHERE first_name="Michael";
mysql> SELECT * FROM test WHERE last_name="Widenius"
                          OR first_name="Michael";

For more information  on the manner  in which  MySQL uses indexes  to
improve query performance, see section  10.4 How MySQL uses indexes.

7.2.11 Using column types from other database engines
To make it easier  to use code  written for SQL implementations  from
other vendors, MySQL maps column types  as shown in the table  below.
These mappings make it  easier to move  table definitions from  other
database engines to MySQL:
+----------------------+------------------------+
|  Other vendor type   |  MySQL type            | 
+----------------------+------------------------+
|  BINARY(NUM)         |  CHAR(NUM) BINARY      | 
+----------------------+------------------------+
|  CHAR VARYING(NUM)   |  VARCHAR(NUM)          | 
+----------------------+------------------------+
|  FLOAT4              |  FLOAT                 | 
+----------------------+------------------------+
|  FLOAT8              |  DOUBLE                | 
+----------------------+------------------------+
|  INT1                |  TINYINT               | 
+----------------------+------------------------+
|  INT2                |  SMALLINT              | 
+----------------------+------------------------+
|  INT3                |  MEDIUMINT             | 
+----------------------+------------------------+
|  INT4                |  INT                   | 
+----------------------+------------------------+
|  INT8                |  BIGINT                | 
+----------------------+------------------------+
|  LONG VARBINARY      |  MEDIUMBLOB            | 
+----------------------+------------------------+
|  LONG VARCHAR        |  MEDIUMTEXT            | 
+----------------------+------------------------+
|  MIDDLEINT           |  MEDIUMINT             | 
+----------------------+------------------------+
|  VARBINARY(NUM)      |  VARCHAR(NUM) BINARY   | 
+----------------------+------------------------+

Column type mapping occurs  at table creation  time. If you create  a
table with types  used by  other vendors  and then  issue a  DESCRIBE
tbl_name statement,   MySQL reports  the table   structure using  the
equivalent MySQL types.

7.3 Functions for use in SELECT and WHERE clauses

A select_expression   or where_definition   in a  SQL  statement  can
consist of any expression using the functions described below.

An expression that contains NULL always produces a NULL  value unless
otherwise indicated   in the   documentation for  the  operators  and
functions involved in the expression.

Note: There must  be no whitespace  between a  function name and  the
parenthesis following  it. This  helps the  MySQL parser  distinguish
between function  calls  and references  to  tables or  columns  that
happen to have the same name  as a function. Spaces around  arguments
are permitted, though.

For the sake of brevity, examples  display the output from the  mysql
program in abbreviated form. So this:

mysql> select MOD(29,9);
1 rows in set (0.00 sec)
+-----------+
| mod(29,9) |
+-----------+
|         2 |
+-----------+
Is displayed like this:

mysql> select MOD(29,9);
        -> 2

7.3.1 Grouping functions
( ... ) Parentheses. Use these to force the order of evaluation in an
expression.
mysql> select 1+2*3;
        -> 7
mysql> select (1+2)*3;
        -> 9

7.3.2 Normal arithmetic operations
The usual arithmetic operators are  available. Note that in the  case
of -,   + and  *,  the result   is calculated  with  BIGINT  (64-bit)
precision if both arguments are integers!
+       Addition
mysql> select 3+5;
        -> 8

-       Subtraction
mysql> select 3-5;
        -> -2
*       Multiplication
mysql> select 3*5;
        -> 15
mysql> select 18014398509481984*18014398509481984.0;
        -> 324518553658426726783156020576256.0
mysql> select 18014398509481984*18014398509481984;
        -> 0

The result of the last expression is incorrect because the  result of
the  integer  multiplication  exceeds  the  64-bit  range  of  BIGINT
calculations.

/       Division
mysql> select 3/5;
        -> 0.60
Division by zero produces a NULL result:
mysql> select 102/(1-1);
        -> NULL
A  division  will  be  calculated  with  BIGINT  arithmetic  only  if
performed in a context where its result is converted to an integer!

7.3.3 Bit functions

MySQL uses BIGINT  (64-bit) arithmetic for  bit operations, so  these
operators have a maximum range of 64 bits.

|       Bitwise OR
mysql> select 29 | 15;
        -> 31

&       Bitwise AND
mysql> select 29 & 15;
        -> 13

<<      Shifts a longlong (BIGINT) number to the left.
mysql> select 1 << 2
        -> 4
>>      Shifts a longlong (BIGINT) number to the right.
mysql> select 4 >> 2
        -> 1

BIT_COUNT(N)
 Returns the number of bits that are set in the argument N.
mysql> select BIT_COUNT(29);
        -> 4

7.3.4 Logical operations
All logical functions return 1 (TRUE) or 0 (FALSE).

NOT
!       Logical NOT.   Returns 1  if the   argument is  0,  otherwise 
        returns 0. Exception: NOT NULL returns NULL.
mysql> select NOT 1;

        -> 0
mysql> select NOT NULL;
        -> NULL
mysql> select ! (1+1);
        -> 0
mysql> select ! 1+1;
        -> 1
The last example returns 1 because the expression evaluates  the same
way as (!1)+1.

OR
||      Logical OR. Returns  1 if either  argument is  not 0 and  not
        NULL
.
mysql> select 1 || 0;
        -> 1
mysql> select 0 || 0;
        -> 0
mysql> select 1 || NULL;
        -> 1

AND
&       Logical AND.  Returns 0  if  either argument  is 0   or NULL,
        otherwise returns 1.
mysql> select 1 && NULL;
        -> 0
mysql> select 1 && 0;
        -> 0

7.3.5 Comparison operators
Comparison operations result  in a value  of 1  (TRUE), 0 (FALSE)  or
NULL. These functions work for both numbers and strings.  Strings are
automatically converted to numbers and  numbers to strings as  needed
(as in Perl).

MySQL performs comparisons using the following rules:
·If one or both arguments are NULL, the result of the  comparison is
NULL.
·If both arguments in a  comparison operation are strings, they  are
compared as strings.
·If both arguments are integers, they are compared as integers.
·Hexadecimal values are treated as binary strings if not compared to
a number.
·If one of the arguments is  a TIMESTAMP or DATETIME column and  the
other argument   is a   constant, the  constant  is  converted  to  a
timestamp before the comparison is performed. This is done to be more
ODBC-friendly.
·In all other  cases, the arguments  are compared as  floating-point
(real) numbers.

By default, string comparisons  are done in case-independent  fashion
using the current character set (ISO-8859-1 Latin1 by  default, which
also works excellently for English).
The examples below  illustrate conversion of  strings to numbers  for
comparison operations:
mysql> SELECT 1 > '6x';
         -> 0
mysql> SELECT 7 > '6x';
         -> 1
mysql> SELECT 0 > 'x6';
         -> 0
mysql> SELECT 0 = 'x6';
         -> 1

=       Equal
mysql> select 1 = 0;
        -> 0
mysql> select '0' = 0;
        -> 1
mysql> select '0.0' = 0;
        -> 1
mysql> select '0.01' = 0;
        -> 0
mysql> select '.01' = 0.01;
        -> 1

<>
!=      Not equal
mysql> select '.01' <> '0.01';
        -> 1
mysql> select .01 <> '0.01';
        -> 0
mysql> select 'zapp' <> 'zappp';
        -> 1

<=      Less than or equal
mysql> select 0.1 <= 2;
        -> 1

<       Less than
mysql> select 2 <= 2;
        -> 1

>=      Greater than or equal
mysql> select 2 >= 2;
        -> 1


>       Greater than
mysql> select 2 > 2;
        -> 0

<=>     Null safe equal
mysql> select 1 <=> 1, NULL <=> NULL, 1 <=> NULL;
        -> 1 1 0

expr BETWEEN min AND max
 If expr is greater  than or equal  to min and expr  is less than  or
equal to max,  BETWEEN returns  1, otherwise  it returns  0. This  is
equivalent to the expression (min <= expr AND expr <= max) if all the
arguments are of the same type. The first argument  (expr) determines
how the comparison is  performed. If expr  is a string expression,  a
case-insensitive string  comparison  is done.  If  expr is  a  binary
string, a case-sensitive  string comparison  is done. If  expr is  an
integer expression,   an integer  comparison is   done. Otherwise,  a
floating-point (real) comparison is done.
mysql> select 1 BETWEEN 2 AND 3;
        -> 0
mysql> select 'b' BETWEEN 'a' AND 'c';
        -> 1
mysql> select 2 BETWEEN 2 AND '3';
        -> 1
mysql> select 2 BETWEEN 2 AND 'x-3';
        -> 0

expr IN (value,...)
 Returns 1 if expr is any of the values in the IN list,  else returns
0. If   all values  are  constants, then   all values  are  evaluated
according to the type of expr and sorted. The search for the  item is
then done using a binary search. This  means IN is very quick if  the
IN  value  list  consists  entirely   of constants.   If expr   is  a
case-sensitive string expression, the string comparison is  performed
in case-sensitive fashion.

mysql> select 2 IN (0,3,5,'wefwf');
        -> 0
mysql> select 'wefwf' IN (0,3,5,'wefwf');
        -> 1

expr NOT IN (value,...)
 Same as NOT (expr IN (value,...)).

ISNULL(expr)
 If expr is NULL, ISNULL() returns 1, otherwise it returns 0.
mysql> select ISNULL(1+1);
        -> 0
mysql> select ISNULL(1/0);
        -> 1
Note that a comparison of NULL values using = will always be false!
COALESCE(list)
 Returns first non-NULL element in list.
mysql> select COALESCE(NULL,1);
        -> 1
mysql> select COALESCE(NULL,NULL,NULL);
        -> NULL

INTERVAL(N,N1,N2,N3,...)
 Returns 0 if  N <  N1, 1  if N  < N2 and  so on.  All arguments  are
treated as numbers. It is required that N1  < N2 < N3 < ... < Nn  for
this function to work correctly. This  is because a binary search  is
used (very fast).

mysql> select INTERVAL(23, 1, 15, 17, 30, 44, 200);
        -> 3
mysql> select INTERVAL(10, 1, 10, 100, 1000);
        -> 2
mysql> select INTERVAL(22, 23, 30, 44, 200);
        -> 0

7.3.6 String comparison functions
 Normally,  if  any  expression   in a   string comparison   is  case
sensitive, the comparison is performed in case-sensitive fashion.
expr LIKE pat [ESCAPE 'escape-char']

 Pattern matching  using SQL  simple regular   expression comparison.
Returns 1 (TRUE) or  0 (FALSE). With LIKE  you can use the  following
two wildcard characters:

+------+----------------------------------------------------------+
|  %   |  Matches any number of characters, even zero characters  | 
+------+----------------------------------------------------------+
|  _   |  Matches exactly one character                           | 
+------+----------------------------------------------------------+
mysql> select 'David!' LIKE 'David_';
        -> 1
mysql> select 'David!' LIKE '%D%v%';
        -> 1

To test for literal  instances of a  wildcard character, precede  the
character with the escape character. If you don't specify  the ESCAPE
character, `\' is assumed:

+------+---------------------------+
|  \%  |  Matches one % character  | 
+------+---------------------------+
|  \_  |  Matches one _ character  | 
+------+---------------------------+
mysql> select 'David!' LIKE 'David\_';
        -> 0
mysql> select 'David_' LIKE 'David\_';
        -> 1
To specify a different escape character, use the ESCAPE clause:
mysql> select 'David_' LIKE 'David|_' ESCAPE '|';
        -> 1
LIKE is allowed on numeric expressions! (This is a MySQL extension to
the ANSI SQL LIKE.)
mysql> select 10 LIKE '1%';
        -> 1

Note: Because MySQL uses the C escape syntax in strings (e.g., `\n'),
you must  double any  `\' that   you use in  your LIKE   strings. For
example, to search for `\n', specify it as `\\n'. To search  for `\',
specify it  as  `\\\\' (the   backslashes are stripped   once by  the
parser, and another time  when the pattern  match is done, leaving  a
single backslash to be matched).

expr NOT LIKE pat [ESCAPE 'escape-char']
 Same as NOT (expr LIKE pat [ESCAPE 'escape-char']).

expr REGEXP pat

expr RLIKE pat
 Performs a pattern match of a string expression expr against a
pattern pat. The pattern can be an extended regular expression. See
section  H Desciption of MySQL regular expretion syntax. Returns 1 if
expr matches pat, otherwise returns 0. RLIKE is a synonym for REGEXP,
provided for mSQL compatibility. Note: Because MySQL uses the C
escape syntax in strings (e.g., `\n'), you must double any `\' that
you use in your REGEXP strings.

mysql> select 'Monty!' REGEXP 'm%y%%';
        -> 0
mysql> select 'Monty!' REGEXP '.*';
        -> 1
mysql> select 'new*\n*line' REGEXP 'new\\*.\\*line';
        -> 1

REGEXP and RLIKE use the current character set (ISO-8859-1  Latin1 by
default) when deciding the type of a character.
expr NOT REGEXP pat

expr NOT RLIKE pat
 Same as NOT (expr REGEXP pat).

STRCMP(expr1,expr2)
STRCMP() returns  0 if  the strings  are the  same, -1  if the  first
argument is smaller  than the  second according to  the current  sort
order, and 1 otherwise.
mysql> select STRCMP('text', 'text2');
        -> -1
mysql> select STRCMP('text2', 'text');
        -> 1
mysql> select STRCMP('text', 'text');
        -> 0

7.3.7 Cast operators
BINARY
 The BINARY  operator  casts the   string following it   to a  binary
string. This is an easy way to  force a column comparison to be  case
independent even if the column isn't defined as BINARY or BLOB.

mysql> select "a" = "A";
        -> 1
mysql> select BINARY "a" = "A";
        -> 0
BINARY was introduced in MySQL 3.23.0

7.3.8 Control flow functions
IFNULL(expr1,expr2)
 If expr1 is not NULL, IFNULL() returns expr1, else it returns expr2.
IFNULL() returns a numeric or string value, depending on  the context
in which it is used.

mysql> select IFNULL(1,0);
        -> 1
mysql> select IFNULL(0,10);
        -> 0
mysql> select IFNULL(1/0,10);
        -> 10
mysql> select IFNULL(1/0,'yes');
        -> 'yes'

IF(expr1,expr2,expr3)
 If expr1 is TRUE (expr1  <> 0 and expr1  <> NULL) then IF()  returns
expr2, else it returns  expr3. IFNULL() returns  a numeric or  string
value, depending on the context in which it is used.


mysql> select IF(1>2,2,3);
        -> 3
mysql> select IF(1<2,'yes','no');
        -> 'yes'
mysql> select IF(strcmp('test','test1'),'yes','no');
        -> 'no'

expr1 is evaluated as an integer  value, which means that if you  are
testing floating-point or  string values,  you should do  so using  a
comparison operation.

mysql> select IF(0.1,1,0);
        -> 0
mysql> select IF(0.1<>0,1,0);
        -> 1

In the first case above, IF(0.1)  returns 0 because 0.1 is  converted
to an integer value, resulting  in a test of  IF(0). This may not  be
what you   expect. In  the  second case,   the comparison  tests  the
original floating-point  value to  see whether  it is   non-zero. The
result of the comparison is used as an integer.

CASE value  WHEN [compare-value]  THEN result   [WHEN [compare-value]
THEN result ...] [ELSE result] END

CASE WHEN [condition] THEN result [WHEN [condition] THEN  result ...]
[ELSE result] END
 The first version returns the result where  value=compare-value. The
second version returns the  result for the  first condition which  is
true. If there was  no matching result  value, then the result  after
ELSE is returned. If there is no ELSE part then NULL is returned.
mysql> SELECT CASE 1 WHEN 1 THEN "one" WHEN 2 THEN "two"  ELSE "more"
END;
       -> "one"
mysql> SELECT CASE WHEN 1>0 THEN "true" ELSE "false" END;
       -> "true"
mysql> SELECT CASE BINARY "B" when "a" then 1 when "b" then 2 END;
       -> NULL

7.3.9 Mathematical functions
All mathematical functions return NULL in case of an error.
-       Unary minus. Changes the sign of the argument.

mysql> select - 2;
        -> -2

Note that if this operator is used with a BIGINT, the return value is
a BIGINT! This means that you  should avoid using - on integers  that
may have the value of -2^63!

ABS(X)  Returns the absolute value of X.
mysql> select ABS(2);
        -> 2
mysql> select ABS(-32);
        -> 32

This function is safe to use with BIGINT values.

SIGN(X) Returns the sign of the argument as -1, 0 or 1,  depending on
        whether X is negative, zero, or positive.

mysql> select SIGN(-32);
        -> -1
mysql> select SIGN(0);
        -> 0
mysql> select SIGN(234);
        -> 1
MOD(N,M)
%       Modulo (like the % operator in C). Returns the remainder of N
        divided by M.
mysql> select MOD(234, 10);
        -> 4
mysql> select 253 % 7;
        -> 1
mysql> select MOD(29,9);
        -> 2
This function is safe to use with BIGINT values.

FLOOR(X)        Returns the largest integer value not greater than X.
mysql> select FLOOR(1.23);
        -> 1
mysql> select FLOOR(-1.23);
        -> -2
Note that the return value is converted to a BIGINT!

CEILING(X)      Returns the smallest integer value not less than X.
mysql> select CEILING(1.23);
        -> 2
mysql> select CEILING(-1.23);
        -> -1

Note that the return value is converted to a BIGINT!

ROUND(X)        Returns the argument X, rounded to an integer.
mysql> select ROUND(-1.23);
        -> -1
mysql> select ROUND(-1.58);
        -> -2
mysql> select ROUND(1.58);
        -> 2
Note that the return value is converted to a BIGINT!
ROUND(X,D)      Returns the argument  X, rounded to  a number with  D
                decimals. If D is 0, the result will have  no decimal
                point or fractional part.
mysql> select ROUND(1.298, 1);
        -> 1.3
mysql> select ROUND(1.298, 0);
        -> 1
Note that the return value is converted to a BIGINT!

EXP(X)  Returns the  value  of e  (the  base of  natural  logarithms)
        raised to the power of X.
mysql> select EXP(2);
        -> 7.389056
mysql> select EXP(-2);
        -> 0.135335

LOG(X)          Returns the natural logarithm of X.
mysql> select LOG(2);
        -> 0.693147
mysql> select LOG(-2);
        -> NULL

If you want the log  of a number X to  some arbitary base B, use  the
formula LOG(X)/LOG(B).

LOG10(X)        Returns the base-10 logarithm of X.
mysql> select LOG10(2);
        -> 0.301030
mysql> select LOG10(100);
        -> 2.000000
mysql> select LOG10(-100);
        -> NULL


POW(X,Y)
POWER(X,Y)      Returns the value of X raised to the power of Y.
mysql> select POW(2,2);
        -> 4.000000
mysql> select POW(2,-2);
        -> 0.250000

SQRT(X)         Returns the non-negative square root of X.
mysql> select SQRT(4);
        -> 2.000000
mysql> select SQRT(20);
        -> 4.472136

PI()    Returns the value of PI.
mysql> select PI();
        -> 3.141593

COS(X)          Returns the cosine of X, where X is given in radians.
mysql> select COS(PI());
        -> -1.000000

SIN(X)          Returns the sine of X, where X is given in radians.
mysql> select SIN(PI());
        -> 0.000000

TAN(X)          Returns the   tangent of   X, where  X  is  given  in  
                 radians.
mysql> select TAN(PI()+1);
        -> 1.557408
ACOS(X)         Returns the arc cosine of X, that is, the value whose
                cosine is X. Returns NULL if X is not in the range -1
                to 1.

mysql> select ACOS(1);
        -> 0.000000

mysql> select ACOS(1.0001);
        -> NULL
mysql> select ACOS(0);
        -> 1.570796

ASIN(X)         Returns the arc sine of  X, that is, the value  whose
                sine is X. Returns NULL if  X is not in the range  -1
                to 1.
mysql> select ASIN(0.2);
        -> 0.201358
mysql> select ASIN('foo');
        -> 0.000000

ATAN(X)         Returns the  arc tangent  of X,  that is,   the value
                whose tangent is X.
mysql> select ATAN(2);
        -> 1.107149
mysql> select ATAN(-2);
        -> -1.107149

ATAN2(X,Y)      Returns the arc tangent of the two variables X and Y.
                It is similar to calculating  the arc tangent of Y  /
                X, except that the signs  of both arguments are  used
                to determine the quadrant of the result.
mysql> select ATAN(-2,2);
        -> -0.785398
mysql> select ATAN(PI(),0);
        -> 1.570796


COT(X)          Returns the cotangent of X.
mysql> select COT(12);
        -> -1.57267341
mysql> select COT(0);
        -> NULL

RAND()
RAND(N)         Returns a random floating-point value in the  range 0
                to 1.0. If an integer argument N is specified,  it is
                used as the seed value.

mysql> select RAND();
        -> 0.5925
mysql> select RAND(20);
        -> 0.1811
mysql> select RAND(20);
        -> 0.1811
mysql> select RAND();
        -> 0.2079
mysql> select RAND();
        -> 0.7888

You can't use  a column  with RAND() values  in an  ORDER BY  clause,
because ORDER BY would evaluate  the column multiple times. In  MySQL
3.23, you can however  do: SELECT *  FROM table_name ORDER BY  RAND()
This is  useful  to get   a random sample   of a  set SELECT  *  FROM
table1,table2 WHERE a=b AND c<d ORDER BY RAND() LIMIT 1000.

LEAST(X,Y,...)
 With two or  more arguments, returns  the smallest  (minimum-valued)
argument. The arguments are compared using the following rules:

If the return value is used  in an INTEGER context, or all  arguments
are integer-valued, they are compared as integers.
If the return value is used in  a REAL context, or all arguments  are
real-valued, they are compared as reals.

If any   argument is   a case-sensitive  string,  the  arguments  are
compared as case-sensitive strings.

In other   cases, the   arguments are  compared  as  case-insensitive
strings.

mysql> select LEAST(2,0);
        -> 0
mysql> select LEAST(34.0,3.0,5.0,767.0);
        -> 3.0
mysql> select LEAST("B","A","C");
        -> "A"

In MySQL  versions prior  to 3.22.5,  you can  use  MIN() instead  of
LEAST.

GREATEST(X,Y,...)
 Returns the  largest (maximum-valued)  argument. The   arguments are
compared using the same rules as for LEAST.

mysql> select GREATEST(2,0);
        -> 2
mysql> select GREATEST(34.0,3.0,5.0,767.0);
        -> 767.0
mysql> select GREATEST("B","A","C");
        -> "C"

In MySQL  versions prior  to 3.22.5,  you can  use  MAX() instead  of
GREATEST.
DEGREES(X)
        Returns the argument X, converted from radians to degrees.
mysql> select DEGREES(PI());
        -> 180.000000

RADIANS(X)
        Returns the argument X, converted from degrees to radians.
mysql> select RADIANS(90);
        -> 1.570796

TRUNCATE(X,D)
        Returns the number X, truncated to D decimals.

mysql> select TRUNCATE(1.223,1);
        -> 1.2
mysql> select TRUNCATE(1.999,1);
        -> 1.9
mysql> select TRUNCATE(1.999,0);
        -> 1

7.3.10 String functions
String-valued functions return NULL if the length of the result would
be greater than the max_allowed_packet server parameter. See section
10,1 Tuning server parame.

For functions that operate on string positions, the first position is
numbered 1.

ASCII(str)
 Returns the  ASCII  code value   of the leftmost   character of  the 
string str. Returns 0 if str is the empty string. Returns NULL if str
is NULL.
mysql> select ASCII('2');
        -> 50
mysql> select ASCII(2);
        -> 50
mysql> select ASCII('dx');
        -> 100

CONV(N,from_base,to_base)
 Converts numbers between  different number bases.  Returns a  string
representation of the number N, converted from base from_base to base
to_base. Returns NULL  if any  argument is  NULL. The  argument N  is
interpreted as an integer,  but may be specified  as an integer or  a
string. The minimum base is 2 and the maximum base is 36.  If to_base
is a negative number, N is regarded as a signed number.  Otherwise, N
is treated as unsigned. CONV works with 64-bit precision.

mysql> select CONV("a",16,2);
        -> '1010'
mysql> select CONV("6E",18,8);
        -> '172'
mysql> select CONV(-17,10,-18);
        -> '-H'
mysql> select CONV(10+"10"+'10'+0xa,10,10);
        -> '40'

BIN(N)
 Returns a string representation of the binary value of N, where N is
a longlong   (BIGINT) number.  This is   equivalent to  CONV(N,10,2).
Returns NULL if N is NULL.
mysql> select BIN(12);
        -> '1100'



OCT(N)
 Returns a string representation of the octal value of N, where  N is
a longlong number. This is  equivalent to CONV(N,10,8). Returns  NULL
if N is NULL.
mysql> select OCT(12);
        -> '14'

HEX(N)
 Returns a string representation of the hexadecimal value of N, where
N is a longlong (BIGINT) number. This is equivalent to CONV(N,10,16).
Returns NULL if N is NULL.
mysql> select HEX(255);
        -> 'FF'

CHAR(N,...)
CHAR() interprets  the arguments  as integers  and returns   a string
consisting of the characters given by the ASCII code values  of those
integers. NULL values are skipped.
mysql> select CHAR(77,121,83,81,'76');
        -> 'MySQL'
mysql> select CHAR(77,77.3,'77.3');
        -> 'MMM'

CONCAT(X,Y,...)
 Returns the string  that results from  concatenating the  arguments.
Returns NULL if any argument is NULL. May have more than 2 arguments.

mysql> select CONCAT('My', 'S', 'QL');
        -> 'MySQL'
mysql> select CONCAT('My', NULL, 'QL');
        -> NULL


LENGTH(str)

OCTET_LENGTH(str)

CHAR_LENGTH(str)

CHARACTER_LENGTH(str)           Returns the length of the string str.
mysql> select LENGTH('text');
        -> 4
mysql> select OCTET_LENGTH('text');
        -> 4

LOCATE(substr,str)
POSITION(substr IN str)
 Returns the position of the first occurrence of substring  substr in
string str. Returns 0 if substr is not in str.

mysql> select LOCATE('bar', 'foobarbar');
        -> 4
mysql> select LOCATE('xbar', 'foobar');
        -> 0

LOCATE(substr,str,pos)
 Returns the position of the first occurrence of substring  substr in
string str, starting at position pos.  Returns 0 if substr is not  in
str.
mysql> select LOCATE('bar', 'foobarbar',5);
        -> 7

INSTR(str,substr)
 Returns the position of the first occurrence of substring  substr in
string str. This is  the same as  the two-argument form of  LOCATE(),
except that the arguments are swapped.

mysql> select INSTR('foobarbar', 'bar');
        -> 4
mysql> select INSTR('xbar', 'foobar');
        -> 0

LPAD(str,len,padstr)
 Returns the string str, left-padded with the string padstr until str
is len characters long.
mysql> select LPAD('hi',4,'??');
        -> '??hi'

RPAD(str,len,padstr)
 Returns the string  str, right-padded with  the string padstr  until
str is len characters long.
mysql> select RPAD('hi',5,'?');
        -> 'hi???'

LEFT(str,len)
        Returns the leftmost len characters from the string str.
mysql> select LEFT('foobarbar', 5);
        -> 'fooba'

RIGHT(str,len)
        Returns the rightmost len characters from the string str.
mysql> select RIGHT('foobarbar', 4);
        -> 'rbar'
mysql> select SUBSTRING('foobarbar' FROM 4);
        -> 'rbar'

SUBSTRING(str,pos,len)

SUBSTRING(str FROM pos FOR len)


MID(str,pos,len)
 Returns a substring len characters long from string str, starting at
position pos. The variant form that uses FROM is ANSI SQL92 syntax.
mysql> select SUBSTRING('Quadratically',5,6);
        -> 'ratica'

SUBSTRING(str,pos)
SUBSTRING(str FROM pos)
 Returns a substring from string str starting at position pos.
mysql> select SUBSTRING('Quadratically',5);
        -> 'ratically'

SUBSTRING_INDEX(str,delim,count)
 Returns the substring from string str after count occurrences of the
delimiter delim. If count is positive, everything to the left  of the
final delimiter (counting  from the  left) is returned.  If count  is
negative, everything to  the right of  the final delimiter  (counting
from the right) is returned.
mysql> select SUBSTRING_INDEX('www.mysql.com', '.', 2);
        -> 'www.mysql'
mysql> select SUBSTRING_INDEX('www.mysql.com', '.', -2);
        -> 'mysql.com'

LTRIM(str)
        Returns the string str with leading space characters removed.
mysql> select LTRIM('  barbar');
        -> 'barbar'

RTRIM(str)
 Returns the string str with trailing space characters removed.
mysql> select RTRIM('barbar   ');
        -> 'barbar'
TRIM([[BOTH | LEADING | TRAILING] [remstr] FROM] str)
 Returns the  string str  with all  remstr prefixes   and/or suffixes
removed. If  none of  the specifiers  BOTH, LEADING  or TRAILING  are
given, BOTH  is  assumed. If   remstr is not   specified, spaces  are
removed.
mysql> select TRIM('  bar   ');
        -> 'bar'
mysql> select TRIM(LEADING 'x' FROM 'xxxbarxxx');
        -> 'barxxx'
mysql> select TRIM(BOTH 'x' FROM 'xxxbarxxx');
        -> 'bar'
mysql> select TRIM(TRAILING 'xyz' FROM 'barxxyz');
        -> 'barx'

SOUNDEX(str)
 Returns a soundex string  from str. Two  strings that sound  ``about
the same''  should have   identical soundex strings.  A  ``standard''
soundex string  is  4 characters  long,  but the  SOUNDEX()  function
returns an arbitrarily long  string. You can  use SUBSTRING() on  the
result to  get a  ``standard'' soundex  string. All  non-alphanumeric
characters are ignored in the  given string. All international  alpha
characters outside the A-Z range are treated as vowels.

mysql> select SOUNDEX('Hello');
        -> 'H400'
mysql> select SOUNDEX('Quadratically');
        -> 'Q36324'

SPACE(N)
 Returns a string consisting of N space characters.

mysql> select SPACE(6);
        -> '      '

REPLACE(str,from_str,to_str)
 Returns the  string  str with   all all occurrences   of the  string
from_str replaced by the string to_str.

mysql> select REPLACE('www.mysql.com', 'w', 'Ww');
        -> 'WwWwWw.mysql.com'

REPEAT(str,count)
 Returns a string consisting of the string str repeated  count times.
If count <= 0, returns an empty string. Returns NULL if str  or count
are NULL.

mysql> select REPEAT('MySQL', 3);
        -> 'MySQLMySQLMySQL'

REVERSE(str)
 Returns the string str with the order of the characters reversed.
mysql> select REVERSE('abc');
        -> 'cba'

INSERT(str,pos,len,newstr)
 Returns the string str, with the substring beginning at position pos
and len characters long replaced by the string newstr.
mysql> select INSERT('Quadratic', 3, 4, 'What');
        -> 'QuWhattic'

ELT(N,str1,str2,str3,...)
 Returns str1 if N = 1, str2 if N  = 2, and so on. Returns NULL if  N
is less than 1 or greater than the number of arguments. ELT()  is the
complement of FIELD().
mysql> select ELT(1, 'ej', 'Heja', 'hej', 'foo');
        -> 'ej'
mysql> select ELT(4, 'ej', 'Heja', 'hej', 'foo');
        -> 'foo'
FIELD(str,str1,str2,str3,...)
 Returns the index of str in the str1, str2, str3, ...  list. Returns
0 if str is not found. FIELD() is the complement of ELT().
mysql> select FIELD('ej', 'Hej', 'ej', 'Heja', 'hej', 'foo');
        -> 2
mysql> select FIELD('fo', 'Hej', 'ej', 'Heja', 'hej', 'foo');
        -> 0

FIND_IN_SET(str,strlist)
 Returns a value  1 to N  if the string  str is  in the list  strlist
consisting of N  substrings. A string  list is  a string composed  of
substrings separated by `,'  characters. If the  first argument is  a
constant string   and the   second is   a column  of  type  SET,  the
FIND_IN_SET() function is optimized to use bit arithmetic!  Returns 0
if str is not in strlist or  if strlist is the empty string.  Returns
NULL if either argument is NULL. This function will not work properly
if the first argument contains a `,'.

mysql> SELECT FIND_IN_SET('b','a,b,c,d');
        -> 2

MAKE_SET(bits,str1,str2,...)
 Returns a  set  (a string  containing  substrings separated  by  `,'
characters) consisting of the strings that have the corresponding bit
in bits set.  str1 corresponds to  bit 0,  str2 to bit  1, etc.  NULL
strings in str1, str2, ... are not appended to the result.

mysql> SELECT MAKE_SET(1,'a','b','c');
        -> 'a'
mysql> SELECT MAKE_SET(1 | 4,'hello','nice','world');
        -> 'hello,world'
mysql> SELECT MAKE_SET(0,'a','b','c');
        -> ''

EXPORT_SET(bits,on,off,[separator,[number_of_bits]])
 Returns a string where for  every bit set in  'bit', you get a  'on'
string and for every reset bit  you get an 'off' string. Each  string
is separated with 'separator' (default ',') and only 'number_of_bits'
(default 64) of 'bits' is used.
mysql> select EXPORT_SET(5,'Y','N',',',4)
        -> Y,N,Y,N

LCASE(str)
LOWER(str)
 Returns the  string str  with all  characters changed   to lowercase
according to   the current  character set   mapping (the  default  is
ISO-8859-1 Latin1).
mysql> select LCASE('QUADRATICALLY');
        -> 'quadratically'

UCASE(str)
UPPER(str)
 Returns the  string str  with all  characters changed   to uppercase
according to   the current  character set   mapping (the  default  is
ISO-8859-1 Latin1).

mysql> select UCASE('Hej');
        -> 'HEJ'

LOAD_FILE(file_name)
 Reads the file and returns the  file contents as a string. The  file
must be on  the server,  you must specify  the full  pathname to  the
file, and you must have the file privilege. The file must be readable
by all and be  smaller than max_allowed_packet.  If the file  doesn't
exist or can't be read due to one of the above reasons,  the function
returns NULL.


mysql> UPDATE   table_name SET  blob_column=LOAD_FILE("/tmp/picture")
WHERE id=1;

There is no string function to convert  a number to a char. There  is
no need  for one,  because MySQL  automatically converts   numbers to
strings as necessary, and vice versa:
mysql> SELECT 1+"1";
        -> 2
mysql> SELECT CONCAT(2,' test');
        -> '2 test'

If a string function is given a binary string as an argument, the
resulting string is also a binary string. A number converted to a
string is treated as a binary string. This only affects comparisons.

7.3.11 Date and time functions
See section  7.2.6 Date and time types for a description of the range
of values each type has, and the valid formats in which date and time
values may be specified.

Here is an example that uses date functions. The query  below selects
all records with a date_col value from within the last 30 days:
mysql> SELECT something FROM table
           WHERE TO_DAYS(NOW()) - TO_DAYS(date_col) <= 30;

DAYOFWEEK(date)
 Returns the weekday index for date (1 = Sunday, 2 = Monday, ...  7 =
Saturday). These index values correspond to the ODBC standard.
mysql> select DAYOFWEEK('1998-02-03');
        -> 3

WEEKDAY(date)
 Returns the weekday index for date (0 = Monday, 1 = Tuesday, ... 6 =
Sunday).

mysql> select WEEKDAY('1997-10-04 22:23:00');
        -> 5
mysql> select WEEKDAY('1997-11-05');
        -> 2

DAYOFMONTH(date)
 Returns the day of the month for date, in the range 1 to 31.
mysql> select DAYOFMONTH('1998-02-03');
        -> 3

DAYOFYEAR(date)
 Returns the day of the year for date, in the range 1 to 366.
mysql> select DAYOFYEAR('1998-02-03');
        -> 34

MONTH(date)
        Returns the month for date, in the range 1 to 12.
mysql> select MONTH('1998-02-03');
        -> 2

DAYNAME(date)
        Returns the name of the weekday for date.
mysql> select DAYNAME("1998-02-05");
        -> 'Thursday'

MONTHNAME(date) Returns the name of the month for date.
mysql> select MONTHNAME("1998-02-05");
        -> 'February'


QUARTER(date)
 Returns the quarter of the year for date, in the range 1 to 4.
mysql> select QUARTER('98-04-01');
        -> 2

WEEK(date)
WEEK(date,first)
 With a single argument, returns the week for date, in the range 0 to
52, for locations  where Sunday  is the first  day of  the week.  The
two-argument form of WEEK()  allows you to  specify whether the  week
starts on Sunday or Monday. The  week starts on Sunday if the  second
argument is 0, on Monday if the second argument is 1.

mysql> select WEEK('1998-02-20');
        -> 7
mysql> select WEEK('1998-02-20',0);
        -> 7
mysql> select WEEK('1998-02-20',1);
        -> 8

YEAR(date)
 Returns the year for date, in the range 1000 to 9999.
mysql> select YEAR('98-02-03');
        -> 1998

HOUR(time)
        Returns the hour for time, in the range 0 to 23.
mysql> select HOUR('10:05:03');
        -> 10

MINUTE(time)
        Returns the minute for time, in the range 0 to 59.
mysql> select MINUTE('98-02-03 10:05:03');
        -> 5
SECOND(time)
        Returns the second for time, in the range 0 to 59.
mysql> select SECOND('10:05:03');
        -> 3

PERIOD_ADD(P,N)
 Adds N months to period P (in the format YYMM or YYYYMM).  Returns a
value in the format YYYYMM. Note that the period argument P is{{<EM>
}}
not{{</EM>
}}
a
 date value.
mysql> select PERIOD_ADD(9801,2);
        -> 199803

PERIOD_DIFF(P1,P2)
 Returns the number of  months between periods P1  and P2. P1 and  P2
should be   in the  format  YYMM or   YYYYMM. Note  that  the  period
arguments P1 and P2 are not date values.
mysql> select PERIOD_DIFF(9802,199703);
        -> 11

DATE_ADD(date,INTERVAL expr type)

DATE_SUB(date,INTERVAL expr type)
ADDDATE(date,INTERVAL expr type)

SUBDATE(date,INTERVAL expr type)
 These functions  perform date  arithmetic. They  are new   for MySQL
3.22.  ADDDATE()  and  SUBDATE()  are  synonyms  for  DATE_ADD()  and
DATE_SUB(). date is a DATETIME or DATE value specifying  the starting
date. expr is an expression specifying the interval value to be added
or substracted from the starting date. expr is a string; it may start
with a `-' for negative intervals.  type is a keyword indicating  how
the expression should be interpreted. The EXTRACT()  function returns
the corresponding interval from the  date. The following table  shows
how the type and expr arguments are related:
+----------------+----------------------+--------------------------+
|  typevalue     |  Meaning             |  Expectedexprformat      |
+----------------+----------------------+--------------------------+
|  SECOND        |  Seconds             |  SECONDS                 |
+----------------+----------------------+--------------------------+
|  MINUTE        |  Minutes             |  MINUTES                 |
+----------------+----------------------+--------------------------+
|  HOUR          |  Hours               |  HOURS                   |
+----------------+----------------------+--------------------------+
|  DAY           |  Days                |  DAYS                    |
+----------------+----------------------+--------------------------+
|  MONTH         |  Months              |  MONTHS                  |
+----------------+----------------------+--------------------------+
|  YEAR          |  Years               |  YEARS                   |
+----------------+----------------------+--------------------------+
|  MINUTE_SECOND |  Minutes and seconds |  "MINUTES:SECONDS"       |
+----------------+----------------------+--------------------------+
|  HOUR_MINUTE   |  Hours and minutes   |  "HOURS:MINUTES"         |
+----------------+----------------------+--------------------------+
|  DAY_HOUR      |  Days and hours      |  "DAYS HOURS"            |
+----------------+----------------------+--------------------------+
|  YEAR_MONTH    |  Years and months    |  "YEARS-MONTHS"          |
+----------------+----------------------+--------------------------+
|  HOUR_SECOND   |  Hours, minutes,     |  "HOURS:MINUTES:SECONDS" |
+----------------+----------------------+--------------------------+
|  DAY_MINUTE    |  Days, hours, minutes|  "DAYS HOURS:MINUTES"    |
+----------------+----------------------+--------------------------+
|  DAY_SECOND    |  Days, hours, minutes|  "DAYS HOURS:MINUTES     |
|                |  ,seconds            |   :SECONDS"              |
+----------------+----------------------+--------------------------+
MySQL allows any non-numeric delimiter  in the expr format. The  ones
shown in the table are the suggested delimiters. If the date argument
is a DATE value  and your calculations  involve only YEAR, MONTH  and
DAY parts  (that is,  no time  parts), the  result is  a DATE  value.
Otherwise the result is a DATETIME value.

mysql> SELECT DATE_ADD("1997-12-31 23:59:59",
                       INTERVAL 1 SECOND);
        -> 1998-01-01 00:00:00
mysql> SELECT DATE_ADD("1997-12-31 23:59:59",
                       INTERVAL 1 DAY);
        -> 1998-01-01 23:59:59
mysql> SELECT DATE_ADD("1997-12-31 23:59:59",
                       INTERVAL "1:1" MINUTE_SECOND);
        -> 1998-01-01 00:01:00
mysql> SELECT DATE_SUB("1998-01-01 00:00:00",
                       INTERVAL "1 1:1:1" DAY_SECOND);
        -> 1997-12-30 22:58:59
mysql> SELECT DATE_ADD("1998-01-01 00:00:00",
                       INTERVAL "-1 10" DAY_HOUR);
        -> 1997-12-30 14:00:00
mysql> SELECT DATE_SUB("1998-01-02", INTERVAL 31 DAY);
        -> 1997-12-02
mysql> SELECT EXTRACT(YEAR FROM "1999-07-02");
       -> 1999
mysql> SELECT EXTRACT(YEAR_MONTH FROM "1999-07-02 01:02:03");
       -> 199907
mysql> SELECT EXTRACT(DAY_MINUTE FROM "1999-07-02 01:02:03");
       -> 20102

If you specify an interval value that is too short (does  not include
all the interval parts that would be expected from the type keyword),
MySQL assumes you have  left out the  leftmost parts of the  interval
value. For example, if you specify a type of DAY_SECOND, the value of
expr is expected to have days,  hours, minutes and seconds parts.  If
you specify a  value like  "1:10", MySQL  assumes that  the days  and
hours parts are missing and the value represents minutes and seconds.
In other words, "1:10" DAY_SECOND is  interpreted in such a way  that
it is equivalent to  "1:10" MINUTE_SECOND. This  is analogous to  the
way that MySQL  interprets TIME values  as representing elapsed  time
rather than as time of day. If you use incorrect dates, the result is
NULL. If you add MONTH, YEAR_MONTH or YEAR and the resulting date has
a day that is larger than the maximum day for the new month,  the day
is adjusted to the maximum days in the new month.

mysql> select DATE_ADD('1998-01-30', Interval 1 month);
        -> 1998-02-28

Note from the preceding example that  the word INTERVAL and the  type
keyword are not case sensitive.

TO_DAYS(date)
        Given a date date,  returns a daynumber  (the number of  days
        since year 0).
mysql> select TO_DAYS(950501);
        -> 728779
mysql> select TO_DAYS('1997-10-07');
        -> 729669

TO_DAYS() is not intended for use with values that precede the advent
of the Gregorian calendar (1582).

FROM_DAYS(N)
        Given a daynumber N, returns a DATE value.

mysql> select FROM_DAYS(729669);
        -> '1997-10-07'

FROM_DAYS() is  not intended  for use  with values  that precede  the
advent of the Gregorian calendar (1582).

DATE_FORMAT(date,format)
 Formats the date value according to the format string. The following
specifiers may be used in the format string:
+------+------------------------------------------+
|  %M  |  Month name (January..December)          | 
+------+------------------------------------------+
|  %W  |  Weekday name (Sunday..Saturday)         | 
+------+------------------------------------------+
|  %D  |  Day of the month with english suffix    | 
|      |  (1st, 2nd, 3rd, etc.)                   | 
+------+------------------------------------------+
|  %Y  |  Year, numeric, 4 digits                 | 
+------+------------------------------------------+
|  %y  |  Year, numeric, 2 digits                 | 
+------+------------------------------------------+
|  %a  |  Abbreviated weekday name (Sun..Sat)     | 
+------+------------------------------------------+
|  %d  |  Day of the month, numeric (00..31)      | 
+------+------------------------------------------+
|  %e  |  Day of the month, numeric (0..31)       | 
+------+------------------------------------------+
|  %m  |  Month, numeric (01..12)                 | 
+------+------------------------------------------+
|  %c  |  Month, numeric (1..12)                  | 
+------+------------------------------------------+
|  %b  |  Abbreviated month name (Jan..Dec)       | 
+------+------------------------------------------+
|  %j  |  Day of year (001..366)                  | 
+------+------------------------------------------+
|  %H  |  Hour (00..23)                           | 
+------+------------------------------------------+
|  %k  |  Hour (0..23)                            | 
+------+------------------------------------------+
|  %h  |  Hour (01..12)                           | 
+------+------------------------------------------+
|  %I  |  Hour (01..12)                           | 
+------+------------------------------------------+
|  %l  |  Hour (1..12)                            | 
+------+------------------------------------------+
|  %i  |  Minutes, numeric (00..59)               | 
+------+------------------------------------------+
|  %r  |  Time, 12-hour (hh:mm:ss [AP]M)          | 
+------+------------------------------------------+
|  %T  |  Time, 24-hour (hh:mm:ss)                | 
+------+------------------------------------------+
|  %S  |  Seconds (00..59)                        | 
+------+------------------------------------------+
|  %s  |  Seconds (00..59)                        | 
+------+------------------------------------------+
|  %p  |  AM or PM                                | 
+------+------------------------------------------+
|  %w  |  Day of the week (0=Sunday..6=Saturday)  | 
+------+------------------------------------------+
|  %U  |  Week (0..52), where Sunday is the       | 
|      |  first day of the week                   | 
+------+------------------------------------------+
|  %u  |  Week (0..52), where Monday is the       | 
|      |  first day of the week                   | 
+------+------------------------------------------+
|  %%  |  A literal `%'.                          | 
+------+------------------------------------------+
All  other  characters   are just   copied  to   the result   without
interpretation.

mysql> select DATE_FORMAT('1997-10-04 22:23:00', '%W %M %Y');
        -> 'Saturday October 1997'
mysql> select DATE_FORMAT('1997-10-04 22:23:00', '%H:%i:%s');
        -> '22:23:00'
mysql> select DATE_FORMAT('1997-10-04 22:23:00',
                          '%D %y %a %d %m %b %j');
        -> '4th 97 Sat 04 10 Oct 277'
mysql> select DATE_FORMAT('1997-10-04 22:23:00',
                          '%H %k %I %r %T %S %w');
        -> '22 22 10 10:23:00 PM 22:23:00 00 6'
As of  MySQL  3.23, the   % is  required before   a format  specifier
characters. In earlier versions of MySQL, % was optional.

TIME_FORMAT(time,format)
 This is used like the  DATE_FORMAT() function above, but the  format
string may contain  only those format  specifiers that handle  hours,
minutes and seconds. Other specifiers produce a NULL value or 0.

CURDATE()
CURRENT_DATE
 Returns today's date as a value in 'YYYY-MM-DD' or  YYYYMMDD format,
depending on whether  the function  is used  in a  string or  numeric
context.
mysql> select CURDATE();
        -> '1997-12-15'
mysql> select CURDATE() + 0;
        -> 19971215

CURTIME()

CURRENT_TIME
 Returns the current time as a value in 'HH:MM:SS' or  HHMMSS format,
depending on whether  the function  is used  in a  string or  numeric
context.
mysql> select CURTIME();
        -> '23:50:26'
mysql> select CURTIME() + 0;
        -> 235026

NOW()
SYSDATE()

CURRENT_TIMESTAMP
 Returns the   current date   and time  as  a  value  in  'YYYY-MM-DD
HH:MM:SS' or YYYYMMDDHHMMSS format, depending on whether the function
is used in a string or numeric context.
mysql> select NOW();
        -> '1997-12-15 23:50:26'
mysql> select NOW() + 0;
        -> 19971215235026

UNIX_TIMESTAMP()
UNIX_TIMESTAMP(date)
 If called with no argument, returns a Unix timestamp  (seconds since
'1970-01-01 00:00:00' GMT). If UNIX_TIMESTAMP() is called with a date
argument, it  returns the  value  of the  argument as   seconds since
'1970-01-01 00:00:00'  GMT. date  may be  a DATE  string, a  DATETIME
string, a TIMESTAMP, or a number in the format YYMMDD or  YYYYMMDD in
local time.

mysql> select UNIX_TIMESTAMP();
        -> 882226357
mysql> select UNIX_TIMESTAMP('1997-10-04 22:23:00');
        -> 875996580

When UNIX_TIMESTAMP is used on a TIMESTAMP column, the  function will
receive     the     value      directly,     with     no     implicit
``string-to-unix-timestamp'' conversion.
FROM_UNIXTIME(unix_timestamp)
 Returns a representation of the  unix_timestamp argument as a  value
in 'YYYY-MM-DD   HH:MM:SS' or   YYYYMMDDHHMMSS format,  depending  on
whether the function is used in a string or numeric context.

mysql> select FROM_UNIXTIME(875996580);
        -> '1997-10-04 22:23:00'
mysql> select FROM_UNIXTIME(875996580) + 0;
        -> 19971004222300

FROM_UNIXTIME(unix_timestamp,format)
 Returns a  string representation  of the  Unix timestamp,  formatted
according  to  the  format   string. format   may contain   the  same
specifiers as   those listed   in the  entry  for  the  DATE_FORMAT()
function.

mysql> select FROM_UNIXTIME(UNIX_TIMESTAMP(),
                            '%Y %D %M %h:%i:%s %x');
        -> '1997 23rd December 03:43:30 x'

SEC_TO_TIME(seconds)
 Returns the   seconds argument,   converted to  hours,  minutes  and
seconds, as  a value  in 'HH:MM:SS'  or HHMMSS  format, depending  on
whether the function is used in a string or numeric context.

mysql> select SEC_TO_TIME(2378);
        -> '00:39:38'
mysql> select SEC_TO_TIME(2378) + 0;
        -> 3938

TIME_TO_SEC(time)
 Returns the time argument, converted to seconds.

mysql> select TIME_TO_SEC('22:23:00');
        -> 80580
mysql> select TIME_TO_SEC('00:39:38');
        -> 2378

7.3.12 Miscellaneous functions
DATABASE()
 Returns the current database name.
mysql> select DATABASE();
        -> 'test'
If there is no current database, DATABASE() returns the empty string.

USER()

SYSTEM_USER()

SESSION_USER()
        Returns the current MySQL user name.
mysql> select USER();
        -> 'davida@localhost'
In MySQL 3.22.11 or later, this includes the client hostname  as well
as the username.  You can extract  just the  username part like  this
(which works whether or not the value includes a hostname part):
mysql> select substring_index(USER(),"@",1);
        -> 'davida'

PASSWORD(str)
 Calculates a password string from  the plaintext password str.  This
is the  function that  is  used for  encrypting MySQL   passwords for
storage in the Password column of the user grant table.

mysql> select PASSWORD('badpwd');
        -> '7f84554057dd964b'
PASSWORD() encryption is non-reversible. PASSWORD() does not  perform
password  encryption  in  the   same way   that Unix   passwords  are
encrypted. You should not assume that if your Unix password  and your
MySQL password  are the  same,  PASSWORD() will  result in   the same
encrypted  value  as  is  stored  in  the  Unix  password  file.  See
ENCRYPT().

ENCRYPT(str[,salt])
 Encrypt str using the  Unix crypt() system  call. The salt  argument
should be a string with 2 characters.

mysql> select ENCRYPT("hello");
        -> 'VxuFAJXVARROc'

If crypt() is not available on your system, ENCRYPT()  always returns
NULL. ENCRYPT() ignores  all but the  first 8  characters of str,  at
least on some systems. This will be determined by the behavior of the
underlying crypt() system call.

ENCODE(str,pass_str)
 Encrypt str using pass_str as  the password. To decrypt the  result,
use DECODE(). The results is a binary string. If you want to  save it
in a column, use a BLOB column type.

DECODE(crypt_str,pass_str)
 Descrypts the   encrypted string  crypt_str using   pass_str as  the
password. crypt_str should be a string returned from ENCODE().

MD5(string)
 Calculates a MD5 checksum for the string. Value is returned as  a 32
long hex number that may, for example, be used as a hash key.


mysql> select MD5("testing")
        -> 'ae2b1fca515949e5d54fb22b8ed95575'

This is a "RSA Data Security, Inc. MD5 Message-Digest Algorithm".

LAST_INSERT_ID([expr])
 Returns the last automatically generated value that was inserted
into an AUTO_INCREMENT column. See section 20.4.29 mysql_insert_id().

mysql> select LAST_INSERT_ID();
        -> 195

The last  ID that  was generated  is maintained  in the  server on  a
per-connection basis. It will  not be changed  by another client.  It
will not even be changed if you update another  AUTO_INCREMENT column
with a non-magic value (that is, a value that is not NULL and not 0).
If expr is  given as  an argument  to LAST_INSERT_ID()  in an  UPDATE
clause,  then   the  value   of  the  argument   is  returned   as  a
LAST_INSERT_ID() value. This can be used to simulate sequences: First
create the table:

mysql> create table sequence (id int not null);
mysql> insert into sequence values (0);

Then the table can be used to generate sequence numbers like this:

mysql> update sequence set id=LAST_INSERT_ID(id+1);

You can generate sequences without calling LAST_INSERT_ID(),  but the
utility of  using the  function this   way is that  the ID   value is
maintained in the server as the last automatically generated value.

You  can  retrieve   the new   ID  as  you   would read   any  normal
AUTO_INCREMENT value in MySQL. For example, LAST_INSERT_ID() (without
an  argument)   will  return   the  new  ID.   The  C   API  function
mysql_insert_id() can also be used to get the value.

FORMAT(X,D)
 Formats the   number X   to a  format  like  '#,###,###.##'  with  D
decimals. If  D  is 0,  the  result will  have  no decimal  point  or
fractional part.

mysql> select FORMAT(12332.1234, 2);
        -> '12,332.12'
mysql> select FORMAT(12332.1,4);
        -> '12,332.1000'
mysql> select FORMAT(12332.2,0);
        -> '12,332'

VERSION()
        Returns a string indicating the MySQL server version.
mysql> select VERSION();
        -> '3.22.19b-log'

GET_LOCK(str,timeout)
 Tries to obtain a lock with a  name given by the string str, with  a
timeout of  timeout  seconds. Returns   1 if the   lock was  obtained
successfully, 0  if  the attempt   timed out,  or NULL   if an  error
occurred (such as running out of memory or the thread was killed with
mysqladmin kill). A lock is released when you execute RELEASE_LOCK(),
execute a new GET_LOCK() or the thread terminates. This  function can
be used to implement application locks or to simulate record locks.

mysql> select GET_LOCK("lock1",10);
        -> 1
mysql> select GET_LOCK("lock2",10);
        -> 1
mysql> select RELEASE_LOCK("lock2");
        -> 1
mysql> select RELEASE_LOCK("lock1");
        -> NULL

Note that the  second RELEASE_LOCK()  call returns  NULL because  the
lock "lock1"  was automatically   released by the  second  GET_LOCK()
call.

RELEASE_LOCK(str)
 Releases the lock  named by the  string str  that was obtained  with
GET_LOCK(). Returns 1 if the lock was released, 0 if the  lock wasn't
locked by this thread  (in which case the  lock is not released)  and
NULL if the named lock  didn't exist. The lock  will not exist if  it
was never obtained by a call to GET_LOCK() or if it already  has been
released.

BENCHMARK(count,expr)
 The BENCHMARK()  function executes  the expression   expr repeatedly
count times. It  may be  used to time  how fast  MySQL processes  the
expression. The result value is always 0. The intended use is  in the
mysql client, which reports query execution times.

mysql> select BENCHMARK(1000000,encode("hello","goodbye"));
+----------------------------------------------+
| BENCHMARK(1000000,encode("hello","goodbye")) |
+----------------------------------------------+
|                                            0 |
+----------------------------------------------+
1 row in set (4.74 sec)

The time reported is elapsed time on the client end, not CPU  time on
the server end. It  may be advisable  to execute BENCHMARK()  several
times, and interpret the result with regard to how heavily loaded the
server machine is.

7.3.13 Functions for use with GROUP BY clauses
If you use  a group function  in a statement  containing no GROUP  BY
clause, it is equivalent to grouping on all rows.

COUNT(expr)
 Returns a count of the number of non-NULL rows retrieved by a SELECT
statement.

mysql> select student.student_name,COUNT(*)
           from student,course
           where student.student_id=course.student_id
           GROUP BY student_name;

COUNT(*) is optimized to return very quickly if the  SELECT retrieves
from one table, no other columns are retrieved and there is  no WHERE
clause. For example:

mysql> select COUNT(*) from student;

COUNT(DISTINCT expr,[expr...])
 Returns a count of the number of different values.

mysql> select COUNT(DISTINCT results) from student;

In MySQL you can get the number of distinct  expressions combinations
by giving a list of expressions. In  ANSI SQL you would have to do  a
concatenation of all expressions inside CODE(DISTINCT ..).

AVG(expr)       Returns the average value of expr.
mysql> select student_name, AVG(test_score)
           from student
           GROUP BY student_name;

MIN(expr)
MAX(expr)
 Returns the minimum or  maximum value of  expr. MIN() and MAX()  may
take a string  argument; in  such cases  they return  the minimum  or
maximum string value.

mysql> select student_name, MIN(test_score), MAX(test_score)
           from student
           GROUP BY student_name;

SUM(expr)
        Returns the sum of expr. Note  that if the return set has  no
        rows, it returns NULL!

STD(expr)

STDDEV(expr)
 Returns the standard deviation of expr. This is an extension to ANSI
SQL. The  STDDEV()  form of   this function is   provided for  Oracle
compatability.

BIT_OR(expr)
 Returns the  bitwise OR  of all  bits in  expr.  The calculation  is
performed with 64-bit (BIGINT precision.

BIT_AND(expr)
 Returns the bitwise  AND of  all bits  in expr.  The calculation  is
performed with 64-bit (BIGINT precision.

MySQL has  extended the  use of   GROUP BY. You  can use   columns or
calculations in  the SELECT  expressions which  don't appear   in the
GROUP BY part. This stands for any possible value for this group. You
can use  this  to get   better performance by   avoiding sorting  and
grouping on unnecessary items. For  example, you don't need to  group
on customer.name in the following query:

mysql> select order.custid,customer.name,max(payments)
       from order,customer
       where order.custid = customer.custid
       GROUP BY order.custid;
In ANSI SQL,  you would  have to add  customer.name to  the GROUP  BY
clause. In MySQL, the name is redundant.

Don't use this feature if the columns you omit from the GROUP BY part
aren't unique in the group!

In some  cases, you  can use  MIN() and  MAX() to  obtain a  specific
column value even if it isn't  unique. The following gives the  value
of column from  the row  containing the  smallest value  in the  sort
column:

substr(MIN(concat(sort,space(6-length(sort)),column),7,length(column)
))
 Note that if you  are using MySQL  3.22 (or earlier)  or if you  are
trying to follow ANSI SQL, you  can't use expressions in GROUP BY  or
ORDER BY clauses.  You can work  around this  limitation by using  an
alias for the expression:

mysql> SELECT id,FLOOR(value/100) AS val FROM tbl_name
           GROUP BY id,val ORDER BY val;

In MySQL 3.23 you can do:
mysql> SELECT id,FLOOR(value/100) FROM tbl_name ORDER BY RAND();
7.4 CREATE DATABASE syntax

CREATE DATABASE db_name
 CREATE DATABASE creates a database with the given name. Rules for
allowable database names are given in section  7.1.5 Database, table,
index, column and alias. An error occurs if the database already
exists.

 Databases in MySQL are  implemented as directories containing  files
that correspond to tables in the database. Since there are  no tables
in a  database when  it  is initially  created, the   CREATE DATABASE
statement only creates a directory under the MySQL data directory.
 You can also create databases with mysqladmin. See section
12.1 Overview of the different MySQL pro.

7.5 DROP DATABASE syntax

DROP DATABASE [IF EXISTS] db_name
 DROP DATABASE  drops all  tables  in the  database and   deletes the
database. Be VERY careful with this command!

DROP DATABASE returns the number of files that were removed  from the
database directory.  Normally,  this is  three  times the  number  of
tables, since each table corresponds to a `.ISD' file, a  `.ISM' file
and a `.frm' file.

In MySQL 3.22 or later, you can use the keywords IF EXISTS to prevent
an error from occurring if the database doesn't exist.
 You can also drop databases with mysqladmin. See section
12.1 Overview of the different MySQL pro.


7.6 CREATE TABLE syntax
CREATE    [TEMPORARY]     TABLE    [IF    NOT     EXISTS]    tbl_name
(create_definition,...)
[table_options] [select_statement]

create_definition:
        col_name type   [NOT NULL   | NULL]  [DEFAULT  default_value]
[AUTO_INCREMENT]
                [PRIMARY KEY] [reference_definition]
        or      PRIMARY KEY (index_col_name,...)
        or      KEY [index_name] KEY(index_col_name,...)
        or      INDEX [index_name] (index_col_name,...)
        or      UNIQUE [INDEX] [index_name] (index_col_name,...)
        or      [CONSTRAINT    symbol]    FOREIGN   KEY    index_name
(index_col_name,...)
                [reference_definition]
        or      CHECK (expr)
type:
        TINYINT[(length)] [UNSIGNED] [ZEROFILL]
  or    SMALLINT[(length)] [UNSIGNED] [ZEROFILL]
  or    MEDIUMINT[(length)] [UNSIGNED] [ZEROFILL]
  or    INT[(length)] [UNSIGNED] [ZEROFILL]
  or    INTEGER[(length)] [UNSIGNED] [ZEROFILL]
  or    BIGINT[(length)] [UNSIGNED] [ZEROFILL]
  or    REAL[(length,decimals)] [UNSIGNED] [ZEROFILL]
  or    DOUBLE[(length,decimals)] [UNSIGNED] [ZEROFILL]
  or    FLOAT[(length,decimals)] [UNSIGNED] [ZEROFILL]
  or    DECIMAL(length,decimals) [UNSIGNED] [ZEROFILL]
  or    NUMERIC(length,decimals) [UNSIGNED] [ZEROFILL]
  or    CHAR(length) [BINARY]
  or    VARCHAR(length) [BINARY]
  or    DATE
  or    TIME
  or    TIMESTAMP
  or    DATETIME
  or    TINYBLOB
  or    BLOB
  or    MEDIUMBLOB
  or    LONGBLOB
  or    TINYTEXT
  or    TEXT
  or    MEDIUMTEXT
  or    LONGTEXT
  or    ENUM(value1,value2,value3,...)
  or    SET(value1,value2,value3,...)

index_col_name:
        col_name [(length)]

reference_definition:
        REFERENCES tbl_name [(index_col_name,...)]
                   [MATCH FULL | MATCH PARTIAL]
                   [ON DELETE reference_option]
                   [ON UPDATE reference_option]

reference_option:
        RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT

table_options:
    type = [ISAM | MYISAM | HEAP]
or    AUTO_INCREMENT = #
or    AVG_ROW_LENGTH = #
or    CHECKSUM = [0 | 1]
or    COMMENT = "string"
or    MAX_ROWS = #
or    MIN_ROWS = #
or    PACK_KEYS = [0 | 1]
or    PASSWORD = "string"
or    DELAY_KEY_WRITE = [0 | 1]
select_statement:
    [IGNORE | REPLACE] SELECT ...  (Some legal select statement)

CREATE TABLE creates a table with the given name in the current
database. Rules for allowable table names are given in section
7.1.5 Database, table, index, column and alias. An error occurs if
there is no current database or if the table already exists.

In MySQL 3.22 or later, the table name can be specified as
db_name.tbl_name. This works whether or not there is a current
database.

In MySQL 3.23, you  can use the TEMPORARY  keyword when you create  a
table.  A  temporary  table   will automatically   be deleted   if  a
connection dies and the name is  per connection. This means that  two
different connections  can both  use the  same temporary   table name
without conflicting with each other or with an existing table  of the
same name. (The existing table is hidden until the temporary table is
deleted).

In MySQL 3.23 or  later, you can  use the keywords  IF NOT EXISTS  so
that an error does not occur  if the table already exists. Note  that
there is no verification that the table structures are identical.
Each table  tbl_name is  represented by  some files  in the  database
directory. In the case of ISAM-type tables you will get:

+----------------+--------------------------------+
|  File          |  Purpose                       | 
+----------------+--------------------------------+
|  tbl_name.frm  |  Table definition (form) file  | 
+----------------+--------------------------------+
|  tbl_name.ISD  |  Data file                     | 
+----------------+--------------------------------+
|  tbl_name.ISM  |  Index file                    | 
+----------------+--------------------------------+

For more information on the  properties of the various column  types,
see section 7.2 Column type.
·If neither NULL nor NOT NULL is specified, the column is treated as
though NULL had been specified.
·An integer column may have the additional attribute AUTO_INCREMENT.
When  you  insert  a  value  of  NULL  (recommended)  or  0  into  an
AUTO_INCREMENT column, the column is  set to value+1, where value  is
the largest value for the column currently in the table.
 AUTO_INCREMENT sequences begin with 1. See section  20.4.29 mysql
insert id(). If you delete the row containing the maximum value for
an AUTO_INCREMENT column, the value will be reused. If you delete all
rows in the table, the sequence starts over. Note: There can be only
one AUTO_INCREMENT column per table, and it must be indexed. To make
MySQL compatible with some ODBC applications, you can find the last
inserted row with the following query:

SELECT * FROM tbl_name WHERE auto_col IS NULL

·NULL values are handled differently for TIMESTAMP columns  than for
other column types. You  cannot store a  literal NULL in a  TIMESTAMP
column; setting the column  to NULL sets it  to the current date  and
time. Because TIMESTAMP  columns behave  this way, the  NULL and  NOT
NULL attributes do not apply in the normal way and are ignored if you
specify them. On the other hand, to make it easier for  MySQL clients
to use TIMESTAMP columns, the server reports that such columns may be
assigned NULL values  (which is  true), even  though TIMESTAMP  never
actually will contain  a NULL value.  You can see  this when you  use
DESCRIBE tbl_name  to get  a  description of  your table.   Note that
setting a TIMESTAMP  column to 0  is not  the same as  setting it  to
NULL, because 0 is a valid TIMESTAMP value.

·If no DEFAULT value is specified for a column,  MySQL automatically
assigns one. If  the column  may take NULL  as a  value, the  default
value is NULL.  If the column  is declared as  NOT NULL, the  default
value depends on the column type:

        For  numeric  types  other   than those   declared  with  the  
          AUTO_INCREMENT   attribute,  the   default  is   0.   For  an   
            AUTO_INCREMENT column, the default value is the next value in
        the sequence.

        For date and time types other than TIMESTAMP, the  default is
        the appropriate ``zero''  value for the  type. For the  first
        TIMESTAMP column in a table, the default value is the current
        date and time. See section  7.2.6 Date and time types.

        For string types other  than ENUM, the  default is the  empty
        string. For ENUM, the default is the first enumeration value.
        KEY is a synonym for INDEX.

·In MySQL, a  UNIQUE key  can have  only distinct  values. An  error
occurs if  you try  to add  a  new row  with a  key that  matches  an
existing row.

·In MySQL a PRIMARY KEY  is the same thing  as a unique KEY that  is
named PRIMARY. A table  can have only one  PRIMARY KEY. If you  don't
have a PRIMARY KEY and some  applications ask for the PRIMARY KEY  in
your tables, MySQL will  return the first  UNIQUE key as the  PRIMARY
KEY.

·A PRIMARY KEY can be  a multiple-column index. However, you  cannot
create a multiple-column index using the PRIMARY KEY key  attibute in
a column specification. Doing so will mark only that single column as
primary. You must use the PRIMARY KEY(index_col_name, ...) syntax.
·If you don't assign a name to an index, the index will  be assigned
the same name as  the first index_col_name,  with an optional  suffix
(_2, _3, ...) to make it unique. You can see index names for  a table
using SHOW INDEX FROM tbl_name. See section 7.20 SHOW syntax (Get
information about tables, columns,...).

·Only the MyISAM  table type  supports indexes on  columns that  can
have NULL values. In  other cases you  must declare such columns  NOT
NULL or an error results.

·With col_name(length) syntax, you can  specify an index which  uses
only a part of a CHAR or VARCHAR column. This can make the index file
much smaller. See section  7.2.9 Column indexs.

·Only the  MyISAM table  type  supports indexing  on BLOB   and TEXT
columns. When putting  an index  on a BLOB  or TEXT  column you  MUST
always specify the length of the index:

CREATE TABLE test (blob_col BLOB, index(blob_col(10)));

·When you use ORDER BY or GROUP BY with a TEXT or BLOB  column, only
the first max_sort_length bytes are used. See section  7.2.7.2 The
BLOB and TEXT types.
 
·The FOREIGN KEY, CHECK and REFERENCES clauses don't actually do
anything. The syntax for them is provided only for compatibility, to
make it easier to port code from other SQL servers and to run
applications that create tables with references. See section
5.3 Functionality missing from MySQL.

·Each NULL column  takes one bit  extra, rounded  up to the  nearest
byte.


·The maximum record length in bytes can be calculated as follows:
row length = 1
             + (sum of column lengths)
             + (number of NULL columns + 7)/8
             + (number of variable-length columns)


·The table_options and SELECT options  is only implemented in  MySQL
3.23 and above. The different table types are:

+----------+----------------------------------------------------+
|  ISAM    |  The original table handler                        | 
+----------+----------------------------------------------------+
|  MyISAM  |  The new binary portable table handler             | 
+----------+----------------------------------------------------+
|  HEAP    |  The data for this table is only stored in memory  | 
+----------+----------------------------------------------------+
 See section 10.18 MySQL table type. The other table options are used
to optimize the behavior of the table. In most cases, you don't have
to specify any of them. The options work for all table types, if not
otherwise indicated.
+-----------------+------------------------------------------------+
| AUTO_INCREMENT  | The next auto_increment value you want         |
|                 | to set for your table (MyISAM)                 |
+-----------------+------------------------------------------------+
| AVG_ROW_LENGTH  | An approximation of the average row length for |
|                 | your table                                     |
+-----------------+------------------------------------------------+
| CHECKSUM        | Set this to 1 if you want MySQL to maintain a  |
|                 | checksum for all rows (makes the table a       |
|                 | little slower to update but makes it easier    |
|                 | to find corrupted tables) (MyISAM)             |
+-----------------+------------------------------------------------+
| COMMENT         | A 60 character comment for your table          |
+-----------------+------------------------------------------------+
| MAX_ROWS        | Max number of rows you plan to store           |
|                 | in the table                                   |
+-----------------+------------------------------------------------+
| MIN_ROWS        | Minimum number of rows you plan to store       |
|                 | in the table                                   |
+-----------------+------------------------------------------------+
| PACK_KEYS       | Set this to 1 if you want to have smaller      |
|                 | index. This usually makes updates slower and   |
|                 | reads faster (MyISAM, ISAM).                   |
+-----------------+------------------------------------------------+
| PASSWORD        | Encrypt the .frm file with a password. This    |
|                 | option doesn't do anything in the standard     |
|                 | MySQL version.                                 |
+-----------------+------------------------------------------------+
| DELAY_KEY_WRITE | Set this to 1 if want to delay key table       |
|                 | updates until the table is closed (MyISAM).    |
+-----------------+------------------------------------------------+

When you use  a MyISAM table,  MySQL uses the  product of max_rows  *
avg_row_length to decide how big the resulting table will be.  If you
don't specify any of the above options, the maximum size for  a table
will be 4G (or 2G if your operating systems only supports 2G tables).

·If you  specify a  SELECT after  the CREATE  STATEMENT, MySQL  will
create new fields for all elements in the SELECT. For example:

mysql> CREATE TABLE test (a int not null auto_increment,
           primary key (a), key(b))
           TYPE=HEAP SELECT b,c from test2;


This will create  a HEAP table  with 3 columns.  Note that the  table
will automatically be deleted if any errors occur while  copying data
into the table.

7.6.1 Silent column specification changes

In some cases,  MySQL silently  changes a  column specification  from
that given in  a CREATE TABLE  statement. (This  may also occur  with
ALTER TABLE.)

·VARCHAR columns with a length less than four are changed to CHAR.

·If any column in a table has  a variable length, the entire row  is
variable-length as  a  result. Therefore,  if  a table  contains  any
variable-length columns  (VARCHAR, TEXT  or BLOB),  all CHAR  columns
longer than three  characters are changed  to VARCHAR columnss.  This
doesn't affect how you use the columns in any way; in  MySQL, VARCHAR
is just  a different  way to  store characters.  MySQL performs  this
conversion because it saves space and makes table operations faster.

See section 10.17 What are the different row formats? Or, when should
VARCHAR/CHAR be used?.

·TIMESTAMP display sizes must be even and in the range from 2 to 14.
If you specify a display  size of 0 or  greater than 14, the size  is
coerced to 14. Odd-valued sizes in the range from 1 to 13 are coerced
to the next higher even number.

·You cannot store a literal NULL  in a TIMESTAMP column; setting  it
to NULL  sets it  to the  current date  and  time. Because  TIMESTAMP
columns behave this  way, the  NULL and  NOT NULL  attributes do  not
apply in the normal way and are ignored if you specify them. DESCRIBE
tbl_name always reports that a TIMESTAMP column may be  assigned NULL
values.

·MySQL maps certain column types used by other SQL  database vendors
to MySQL  types. See  section 7.2.11  Using column  types from  other
database engines.

 If you want to  see whether or  not MySQL used  a column type  other
than the one you specified, issue a DESCRIBE tbl_name statement after
creating or altering your table.

 Certain other column type changes may occur if you compress  a table
using pack_isam. See section  10.17 What are the different row
format? Or, when should VARCHAR/.

7.7 ALTER TABLE syntax
ALTER [IGNORE] TABLE tbl_name alter_spec [, alter_spec ...]

alter_specification:
        ADD [COLUMN] create_definition [FIRST | AFTER column_name ]
  or    ADD INDEX [index_name] (index_col_name,...)
  or    ADD PRIMARY KEY (index_col_name,...)
  or    ADD UNIQUE [index_name] (index_col_name,...)
  or    ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT}
  or    CHANGE [COLUMN] old_col_name create_definition
  or    MODIFY [COLUMN] create_definition
  or    DROP [COLUMN] col_name
  or    DROP PRIMARY KEY
  or    DROP INDEX key_name
  or    RENAME [AS] new_tbl_name
  or    table_option
 ALTER TABLE allows you to change the structure of an existing table.
For example,   you can  add  or delete   columns, create  or  destroy
indexes, change the type  of existing columns,  or rename columns  or
the table itself. You can also  change the comment for the table  and
type of the table. See section  7.6 CREAT TABLE syntax

 If you use ALTER TABLE to change a column specification but DESCRIBE
tbl_name indicates that your column  was not changed, it is  possible
that MySQL ignored your modification for one of the reasons described
in section  7.6.1 Silent column specification changes.

For example, if  you try to  change a VARCHAR  column to CHAR,  MySQL
will still use  VARCHAR if the  table contains other  variable-length
columns.

 ALTER TABLE works by making a temporary copy of the  original table.
The alteration is performed on the  copy, then the original table  is
deleted and the new one is renamed.  This is done in such a way  that
all updates are automatically redirected to the new table without any
failed updates. While ALTER TABLE is executing, the original table is
readable by  other  clients. Updates   and writes to   the table  are
stalled until the new table is ready.

        ·To use   ALTER TABLE,   you need  select,  insert,  delete,  
        update, create and drop privileges on the table.

        ·IGNORE is a MySQL extension to ANSI SQL92. It  controls how
        ALTER TABLE works if there  are duplicates on unique keys  in
        the new table. If IGNORE isn't specified, the copy is aborted
        and rolled back. If IGNORE  is specified, then for rows  with
        duplicates on a unique key, only  the first row is used;  the
        others are deleted.

        ·You can issue multiple ADD, ALTER, DROP and  CHANGE clauses
        in a single ALTER TABLE statement. This is a  MySQL extension
        to ANSI SQL92, which allows only one of each clause per ALTER
        TABLE statement.

        ·CHANGE col_name,  DROP col_name  and DROP  INDEX are  MySQL
        extensions to ANSI SQL92.

        ·MODIFY is an Oracle extension to ALTER TABLE.
        ·The optional word COLUMN  is a pure  noise word and can  be
        omitted.
        ·If you use ALTER TABLE tbl_name RENAME AS  new_name without
        any other   options, MySQL   simply renames  the  files  that  
        correspond to the table tbl_name. There is no need  to create
        the temporary table.

        ·create_definition clauses use the  same syntax for ADD  and
        CHANGE as for  CREATE TABLE. Note  that this syntax  includes
        the column name, not  just the column  type. See section  7.6
        CREATE TABLE syntax.

        ·You  can  rename  a  column  using  a  CHANGE  old_col_name  
         create_definition clause. To do so,  specify the old and  new
        column names and the type that the column currently  has. For
        example, to rename an INTEGER column from a to b, you  can do
        this:

        mysql> ALTER TABLE t1 CHANGE a b INTEGER;

        If you  want to  change a  column's type  but  not the  name,
        CHANGE syntax still  requires two column  names even if  they
        are the same. For example:
        mysql> ALTER TABLE t1 CHANGE b b BIGINT NOT NULL;

        However, as of  MySQL 3.22.16a,  you can also  use MODIFY  to
        change a column's type without renaming it:

        mysql> ALTER TABLE t1 MODIFY b BIGINT NOT NULL;

        ·If you use CHANGE or MODIFY  to shorten a column for  which
        an index exists on part of  the column (for instance, if  you
        have an   index on  the  first 10   characters of  a  VARCHAR 
         column), you cannot make the  column shorter than the  number
        of characters that are indexed.

        ·When you change a column type using CHANGE or MODIFY, MySQL
        tries to convert data to the new type as well as possible.

        ·In MySQL 3.22 or later, you can use FIRST or ADD  ... AFTER
        col_name to  add a  column at  a specific  position within  a
        table row. The default is to add the column last.

        ·ALTER COLUMN specifies a new default value for a  column or
        removes the old default value. If the old default  is removed
        and the column can be NULL,  the new default is NULL. If  the
        column cannot be NULL, MySQL assigns a default value. Default
        value assignment  is described  in section  7.6 CREATE  TABLE
        syntax.

        ·DROP INDEX removes an index.  This is a MySQL extension  to
        ANSI SQL92.

        ·If columns are dropped from  a table, the columns are  also
        removed from  any index  of which  they are  a  part. If  all
        columns that  make up  an  index are  dropped, the   index is
        dropped as well.


        ·DROP PRIMARY KEY drops the primary index. If no  such index
        exists, it drops the first UNIQUE index in the  table. (MySQL
        marks the first UNIQUE key as  the PRIMARY KEY if no  PRIMARY
        KEY was specified explicitly.)

        ·With the C API function mysql_info(), you can find  out how
        many records were copied, and (when IGNORE is used)  how many
        records were deleted due to duplication of unique key values.


        ·The  FOREIGN  KEY,  CHECK   and REFERENCES   clauses  don't  
         actually do anything.  The syntax for  them is provided  only
        for compatibility, to make it easier to port code  from other
        SQL servers and to run  applications that create tables  with
        references. See section  5.3 Functionality missing form MySQL


Here is an example  that shows some  of the uses  of ALTER TABLE.  We
begin with a table t1 that is created as shown below:

mysql> CREATE TABLE t1 (a INTEGER,b CHAR(10));

To rename the table from t1 to t2:
mysql> ALTER TABLE t1 RENAME t2;

To change column a from INTEGER to TINYINT NOT NULL (leaving the name
the same), and to change column  b from CHAR(10) to CHAR(20) as  well
as renaming it from b to c:

mysql> ALTER TABLE t2 MODIFY a TINYINT NOT NULL, CHANGE b c CHAR(20);

To add a new TIMESTAMP column named d:
mysql> ALTER TABLE t2 ADD d TIMESTAMP;
To add an index on column d, and make column a the primary key:

mysql> ALTER TABLE t2 ADD INDEX (d), ADD PRIMARY KEY (a);

To remove column c:

mysql> ALTER TABLE t2 DROP COLUMN c;

To add a new AUTO_INCREMENT integer column named c:

mysql> ALTER TABLE t2 ADD c INT UNSIGNED NOT NULL AUTO_INCREMENT,
           ADD INDEX (c);
Note that   we indexed  c, because   AUTO_INCREMENT columns  must  be
indexed, and also  that we  declare c  as NOT  NULL, because  indexed
columns cannot be NULL.

When you add an  AUTO_INCREMENT column, column  values are filled  in
with sequence numbers for you automatically.

7.8 OPTIMIZE TABLE syntax
OPTIMIZE TABLE tbl_name
OPTIMZE TABLE should be used  if you have deleted  a large part of  a
table  or   if  you   have made   many   changes  to  a   table  with
variable-length  rows  (tables  that   have VARCHAR,   BLOB  or  TEXT
columns). Deleted   records are   maintained in  a  linked  list  and
subsequent INSERT operations reuse old record positions. You  can use
OPTIMIZE TABLE to reclaim the unused space.

OPTIMIZE TABLE  works by  making  a temporary  copy of   the original
table. The old table is copied  to the new table (without the  unused
rows), then the original table is deleted and the new one is renamed.
This is  done  in such   a way  that all   updates are  automatically
redirected to   the new   table without  any  failed  updates.  While
OPTIMIZE TABLE is executing, the original table is readable  by other
clients. Updates and writes  to the table  are stalled until the  new
table is ready.

7.9 DROP TABLE syntax

DROP TABLE [IF EXISTS] tbl_name [, tbl_name,...]
DROP TABLE removes one or more  tables. All table data and the  table
definition are removed,  so be  careful with this  command! In  MySQL
3.22 or later, you can use the keywords IF EXISTS to prevent an error
from occurring for tables that don't exist.

7.10 DELETE syntax
DELETE [LOW_PRIORITY] FROM tbl_name
    [WHERE where_definition] [LIMIT rows]
DELETE deletes rows from tbl_name that satisfy the condition given by
where_definition, and returns the number of records deleted.

If you issue  a DELETE with  no WHERE clause,  all rows are  deleted.
MySQL does this by recreating the  table as an empty table, which  is
much faster than deleting each row. In this case, DELETE returns zero
as the number of affected records. (MySQL can't return the  number of
rows that were actually deleted,  since the recreate is done  without
opening the   data files.   As long  as  the  table  definition  file
`tbl_name.frm' is valid, the table can be recreated this way, even if
the data or index files have become corrupted.).

If you really want to know how many records are deleted when  you are
deleting all rows, and are willing to suffer a speed penalty, you can
use a DELETE statement of this form:

mysql> DELETE FROM tbl_name WHERE 1>0;
Note that this is MUCH slower than DELETE FROM tbl_name with no WHERE
clause, because it deletes rows one at a time.
If you specify the keyword  LOW_PRIORITY, execution of the DELETE  is
delayed until no other clients are reading from the table.

Deleted records are maintained in a linked list and subsequent INSERT
operations reuse old  record positions. To  reclaim unused space  and
reduce file sizes, use the OPTIMIZE TABLE statement or the isamchk
utility to reorganize tables. OPTIMIZE TABLE is easier, but isamchk
is faster. See section 7.8 OPIMIZE TABLE syntax, and section
3.4.3 Tableoptimization.

The MySQL-specific LIMIT rows option  to DELETE tells the server  the
maximum number of rows  to be deleted  before control is returned  to
the client. This can be used to ensure that a specific DELETE command
doesn't take too much time. You can simply repeat the  DELETE command
until the number of affected rows is less than the LIMIT value.

7.11 SELECT syntax
SELECT     [STRAIGHT_JOIN]    [SQL_SMALL_RESULT]     [SQL_BIG_RESULT]
[HIGH_PRIORITY]
       [DISTINCT | DISTINCTROW | ALL]
   select_expression,...
   [INTO OUTFILE 'file_name' export_options]
   [FROM table_references
       [WHERE where_definition]
       [GROUP BY col_name,...]
       [HAVING where_definition]
        [ORDER BY  {unsigned_integer |   col_name | formula}  [ASC  |
DESC] ,...]
       [LIMIT [offset,] rows]
       [PROCEDURE procedure_name] ]
SELECT is used  to retrieve rows  selected from  one or more  tables.
select_expression indicates the columns you want to  retrieve. SELECT
may also be used to retrieve  rows computed without reference to  any
table. For example:

mysql> SELECT 1 + 1;
         -> 2


All keywords used must be given in exactly the order shown above. For
example, a HAVING  clause must  come after  any GROUP  BY clause  and
before any ORDER BY clause.

        ·A SELECT expression  may be  given an alias  using AS.  The
        alias is used as the expression's column name and can be used
        with ORDER BY or HAVING clauses. For example:

        mysql> select concat(last_name,', ',first_name) AS full_name
            from mytable ORDER BY full_name;

        ·The FROM table_references clause indicates the tables  from
        which to retrieve rows. If you name more than one  table, you
        are performing a  join. For information  on join syntax,  see
        section 7.12 JOIN syntax.

        ·You can refer to a column as col_name, tbl_name.col_name or
        db_name.tbl_name.col_name. You need not specify a tbl_name or
        db_name.tbl_name prefix for  a column reference  in a  SELECT
        statement  unless  the  reference  would  be  ambiguous.  See  
        section  7.1.5 Database, table, index, column and alias names
        , for examples of ambiguity that require the more explicit
        column reference forms.


        ·A table   reference may   be aliased  using  tbl_name  [AS]  
        alias_name.

        mysql> select t1.name, t2.salary from employee AS t1, info AS
t2
                   where t1.name = t2.name;
        mysql> select t1.name, t2.salary from employee t1, info t2
                   where t1.name = t2.name;

        ·Columns selected for output may be referred to in  ORDER BY
        and GROUP BY  clauses using column  names, column aliases  or
        column positions. Column positions begin with 1.

        mysql> select college, region, seed from tournament
                   ORDER BY region, seed;
        mysql> select college, region AS r, seed AS s from tournament
                   ORDER BY r, s;
        mysql> select college, region, seed from tournament
                   ORDER BY 2, 3;

        To sort in reverse order,  add the DESC (descending)  keyword
        to the name of the column in the ORDER BY clause that you are
        sorting by.  The  default is  ascending  order; this  may  be
        specified explicitly using the ASC keyword.

        ·The HAVING clause can refer to any column or alias named in
        the select_expression. It is applied last, just  before items
        are sent  to  the client,  with  no optimization.  Don't  use
        HAVING for  items that  should be  in the  WHERE clause.  For
        example, do not write this:

        mysql> select col_name from tbl_name HAVING col_name > 0;


        Write this instead:

        mysql> select col_name from tbl_name WHERE col_name > 0;

        In MySQL 3.22.5  or later,  you can also  write queries  like
this:

        mysql> select user,max(salary) from users
                   group by user HAVING max(salary)>10;

        In older MySQL versions, you can write this instead:
        mysql> select user,max(salary) AS sum from users
                   group by user HAVING sum>10;
        ·STRAIGHT_JOIN forces the  optimizer to join  the tables  in
        the order in which  they are listed  in the FROM clause.  You
        can use this to speed up  a query if the optimizer joins  the
        tables in non-optimal order. See section 7.21  EXPLAIN syntax
        (Get information about a SELECT).

        ·SQL_SMALL_RESULT can be used with  GROUP BY or DISTINCT  to
        tell the optimizer that the result set will be small. In this
        case, MySQL  will  use fast  temporary  tables to  store  the
        resulting table instead of using sorting. SQL_SMALL_RESULT is
        a MySQL extension to ANSI SQL92.

        ·The LIMIT clause  can be  used to constrain  the number  of
        rows returned by the SELECT statement. LIMIT takes one or two
        numeric arguments.  If two   arguments are given,  the  first
        specifies the offset of the  first row to return, the  second
        specifies the maximum number of rows to return. The offset of
        the initial row is 0 (not 1).

        mysql> select * from table LIMIT 5,10;  # Retrieve rows 6-15
        If one argument is given, it indicates the maximum  number of
        rows to return.

        mysql> select * from table  LIMIT 5;     # Retrieve  first 5
rows

        In other words, LIMIT n is equivalent to LIMIT 0,n.

        ·The SELECT  ... INTO   OUTFILE 'file_name' form  of  SELECT
        writes the selected rows  to a file.  The file is created  on
        the server   host, and   cannot already  exist  (among  other  
        things, this   prevents database  tables and   files such  as
        `/etc/passwd' from being destroyed).  You must have the  file
        privilege on the server host to use this form of SELECT.

        SELECT ...   INTO OUTFILE  is the   complement of  LOAD  DATA 
        INFILE;  the  syntax  for  the  export_options  part  of  the  
         statement consists of the same FIELDS and LINES  clauses that
        are used with  the LOAD  DATA INFILE  statement. See  section
        7.15 LOAD DATA  INFILE Syntax.  In the  resulting text  file,
        only the following characters are  escaped by the ESCAPED  BY
        character:

        ◎ The ESCAPED BY character
        ◎ The first character in FIELDS TERMINATED BY
        ◎ The first character in LINES TERMINATED BY

        Additionally, ASCII 0 is converted to ESCAPED BY  followed by
        0 (ASCII  48). The  reason for  the above  is  that you  MUST
        escape  any  FIELDS  TERMINATED   BY, ESCAPED   BY  or  LINES  
          TERMINATED BY characters to reliably be able to read the file
        back. ASCII 0 is escaped to make it easier to view  with some
        pagers. As the resulting file doesn't have to conform  to the
        SQL syntax, nothing else need be escaped.

7.12 JOIN syntax
 MySQL supports   the following   JOIN syntaxes  for  use  in  SELECT
statements:
table_reference, table_reference
table_reference [CROSS] JOIN table_reference
table_reference INNER JOIN table_reference
table_reference STRAIGHT_JOIN table_reference
table_reference LEFT [OUTER] JOIN table_reference ON conditional_expr
table_reference LEFT [OUTER] JOIN table_reference USING (column_list)
table_reference NATURAL LEFT [OUTER] JOIN table_reference
{   oj   table_reference   LEFT   OUTER   JOIN   table_reference   ON
conditional_expr }

 The last   LEFT OUTER   JOIN syntax  shown  above  exists  only  for
compatibility with ODBC.
        ·A  table  reference  may   be aliased   using  tbl_name  AS  
          alias_name or tbl_name alias_name.

        mysql> select t1.name, t2.salary from employee AS t1, info AS
t2
                   where t1.name = t2.name;

        ·INNER JOIN and , (comma) are semantically  equivalent. Both
        do a full join between the tables used. Normally, you specify
        how the tables should be linked in the WHERE condition.

        ·The ON conditional is any conditional of the form  that may
        be used in a WHERE clause.

        ·If there is  no matching record  for the  right table in  a
        LEFT JOIN, a row with all columns set to NULL is used for the
        right table. You can use this fact to find records in a table
        that have no counterpart in another table:
        mysql> select table1.* from table1
                   LEFT JOIN table2 ON table1.id=table2.id
                   where table2.id is NULL;

        This example finds all rows in  table1 with an id value  that
        is not present in  table2 (i.e., all  rows in table1 with  no
        corresponding row in table2). This assumes that  table2.id is
        declared NOT NULL, of course.

        ·The USINGcolumn_list clause  names a list  of columns  that
        must exist in both tables. A USING clause such as:

        A LEFT JOIN B USING (C1,C2,C3,...)

        is defined to be semantically  identical to an ON  expression
        like this:

        A.C1=B.C1 AND A.C2=B.C2 AND A.C3=B.C3,...

        ·The NATURAL  LEFT  JOIN of   two tables is   defined to  be 
        semantically equivalent to  a LEFT JOIN  with a USING  clause
        that names all columns that exist in both tables.
        ·STRAIGHT_JOIN is identical  to JOIN, except  that the  left
        table is always read before the right table. This can be used
        for those  (few)  cases where  the  join optimizer  puts  the
        tables in the wrong order.

Some examples:
mysql> select * from table1,table2 where table1.id=table2.id;
mysql> select * from table1 LEFT JOIN table2 ON table1.id=table2.id;
mysql> select * from table1 LEFT JOIN table2 USING (id);
mysql> select * from table1 LEFT JOIN table2 ON table1.id=table2.id
           LEFT JOIN table3 ON table2.id=table3.id;
See section  10.6 How MySQL optimize LEFT

7.13 INSERT syntax
    INSERT [LOW_PRIORITY | DELAYED] [IGNORE]
        [INTO] tbl_name [(col_name,...)]
        VALUES (expression,...),(...),...
or  INSERT [LOW_PRIORITY | DELAYED] [IGNORE]
        [INTO] tbl_name [(col_name,...)]
        SELECT ...
or  INSERT [LOW_PRIORITY | DELAYED] [IGNORE]
        [INTO] tbl_name
        SET col_name=expression, col_name=expression, ...

 INSERT inserts  new rows  into  an existing  table. The   INSERT ...
VALUES   form    of   the    statement   inserts   rows    based   on
explicitly-specified values. The INSERT ... SELECT form  inserts rows
selected from another  table or  tables. The INSERT  ... VALUES  form
with multiple value lists is supported in MySQL 3.22.5 or  later. The
col_name=expression syntax is supported in MySQL 3.22.10 or later.

tbl_name is the table into which rows should be inserted.  The column
name list or  the SET  clause indicates which  columns the  statement
specifies values for.
        ·If you  specify no  column list  for INSERT  ... VALUES  or
        INSERT ... SELECT, values for all columns must be provided in
        the VALUES() list  or by the  SELECT. If  you don't know  the
        order of the columns in  the table, use DESCRIBE tbl_name  to
        find out.

        ·Any column  not explicitly  given  a value  is set   to its
        default value. For example, if you specify a column list that
        doesn't name all  the columns in  the table, unnamed  columns
        are set to their default values. Default value  assignment is
        described in section 7.6 CREATE TABLE syntax.

        ·An expression may refer to any column that was  set earlier
        in a value list. For example, you can say this:

        mysql> INSERT INTO tbl_name (col1,col2) VALUES(15,col1*2);

        But not this:

        mysql> INSERT INTO tbl_name (col1,col2) VALUES(col2*2,15);

        ·If you specify the  keyword LOW_PRIORITY, execution of  the
        INSERT is delayed until no other clients are reading from the
        table.

        ·If you specify the  keyword IGNORE in  an INSERT with  many
        value rows, any rows which  duplicate an existing PRIMARY  or
        UNIQUE key in the table are ignored and are not  inserted. If
        you do not specify IGNORE, the insert is aborted if  there is
        any row that duplicates an existing key value. You  can check
        with the mysql_info()  how many rows  were inserted into  the
        table.

        ·If MySQL was  configured using the  DONT_USE_DEFAULT_FIELDS
        option,  INSERT  statements  generate  an  error  unless  you  
        explicitly specify  values for   all columns that  require  a
        non-NULL value. See section  4.7.3 Typical configure options.
 
        ·The following conditions hold for a INSERT INTO  ... SELECT
        statement:

                ◎ The query cannot contain an ORDER BY clause.
                ◎ The target  table of the  INSERT statement  cannot
                appear in the FROM clause  of the SELECT part of  the
                query, because it's forbidden  in ANSI SQL to  SELECT
                from the  same table  into which  you are  INSERTing.
                (The problem is that  the SELECT possibly would  find
                records that were  inserted earlier  during the  same
                run. When  using  sub-select clauses,  the  situation
                could easily be very confusing!)
                ◎ AUTO_INCREMENT columns work as usual.

 If you use INSERT ... SELECT  or a INSERT ... VALUES statement  with
multiple value lists, you can use the C API function  mysql_info() to
get information about the query. The format of the information string
is shown below:

Records: 100 Duplicates: 0 Warnings: 0

Duplicates indicates the  number of  rows that  couldn't be  inserted
because they would duplicate some existing unique index value.

Warnings indicates the  number of  attempts to  insert column  values
that were problematic in  some way. Warnings  can occur under any  of
the following conditions:

        ·Inserting NULL into  a column  that has  been declared  NOT
        NULL. The column is set to its default value.

        ·Setting a numeric column to  a value that lies outside  the
        column's range.   The value  is clipped   to the  appropriate
        endpoint of the range.

        ·Setting a numeric column to a value such as '10.34  a'. The
        trailing garbage is stripped  and the remaining numeric  part
        is inserted. If the value doesn't  make sense as a number  at
        all, the column is set to 0.

        ·Inserting a  string  into a  CHAR,  VARCHAR, TEXT  or  BLOB
        column that exceeds the column's maximum length. The value is
        truncated to the column's maximum length.

        ·Inserting a  value  into a   date or time   column that  is 
        illegal for   the column  type.  The column   is set  to  the 
         appropriate ``zero'' value for the type.

 The DELAYED  option for  the INSERT  statement is   a MySQL-specific
option that is very  useful if you have  clients that can't wait  for
the INSERT to complete. This is common when you use MySQL for logging
and you also periodically run SELECT statements that take a long time
to complete. DELAYED was introduced in  MySQL 3.22.15. It is a  MySQL
extension to ANSI SQL92.

 Another major benefit of using  INSERT DELAYED is that inserts  from
many clients are bundled together and  written in one block. This  is
much faster than doing many separate inserts.

Note that currently the queued rows  are only stored in memory  until
they are inserted into the table. This means that if you  kill mysqld
the hard way  (kill -9) or  if mysqld  dies unexpectedly, any  queued
rows that weren't written to disk are lost!

 The following happens when you use  the DELAYED option to INSERT  or
REPLACE. In  this  description, the  ``thread''  is the  thread  that
received an INSERT DELAYED command and ``handler'' is the thread that
handles all INSERT DELAYED statements for a particular table.

        ·When a thread executes a  DELAYED statement for a table,  a
        handler thread is  created to handle  all DELAYED  statements
        for the table, if no such handler already exists.
        The thread checks whether or  not the handler has acquired  a
        DELAYED lock already; if not, it tells the handler  thread to
        do so. The DELAYED lock can be obtained even if other threads
        have a READ or WRITE lock on the table. However,  the handler
        will wait for all ALTER TABLE locks or FLUSH TABLES to ensure
        that the table structure is up to date.

        ·The thread  executes the  INSERT statement  but instead  of
        writing the row to the table it puts a copy of the  final row
        into a  queue that  is  managed by  the handler   thread. Any
        syntax errors  are noticed  by the  thread and   reported the
        client program.

        ·The client can't  report the  number of  duplicates or  the
        AUTO_INCREMENT value for the  resulting row; it can't  obtain
        them from the server, because  the INSERT returns before  the
        insert operation has been  completed. If you  use the C  API,
        the mysql_info() function doesn't return anything meaningful,
        for the same reason.

        ·The update log is  updated by the  handler thread when  the
        row is  inserted  into the  table.  In case  of  multiple-row
        inserts, the  update log  is updated  when the  first row  is
        inserted.

        ·After every   delayed_insert_limit rows  are  written,  the
        handler checks whether or not any SELECT statements are still
        pending. If so, it allows these to execute before continuing.

        ·When the handler has no more  rows in its queue, the  table
        is unlocked. If no new  INSERT DELAYED commands are  received
        within    delayed_insert_timeout    seconds,   the    handler    
            terminates.

        ·If more than delayed_queue_size rows are pending already in
        a specific handler  queue, the  thread waits  until there  is
        room in the queue. This is  useful to ensure that the  mysqld
        server doesn't use all memory for the delayed memory queue.


        ·The handler thread will show  up in the MySQL process  list
        with delayed_insert in the Command column. It will  be killed
        if you execute a  FLUSH TABLES command  or kill it with  KILL
        thread_id. However, it will first store all queued  rows into
        the table before exiting. During this time it will not accept
        any new INSERT commands from  another thread. If you  execute
        an INSERT DELAYED  command after this,  a new handler  thread
        will be created.

        ·Note that the above means that INSERT DELAYED commands have
        higher priority than  normal INSERT commands  if there is  an
        INSERT DELAYED handler already running! Other update commands
        will have  to wait  until the  INSERT DELAY  queue is  empty,
        someone kills  the handler  thread (with  KILL thread_id)  or
        someone executes FLUSH TABLES.

        ·The following  status variables  provide information  about
        INSERT DELAYED commands:
+--------------------------+--------------------------------------+
| Delayed_insert_threads   | Number of handler threads            |
+--------------------------+--------------------------------------+
| Delayed_writes           | Number of rows written with          |
|                          | INSERT DELAYED                       |
+--------------------------+--------------------------------------+
| Not_flushed_delayed_rows | Number of rows waiting to be written |
+--------------------------+--------------------------------------+
You can view these variables by issuing a SHOW STATUS statement or by
executing a mysqladmin extended-status command.

7.14 REPLACE syntax
    REPLACE [LOW_PRIORITY | DELAYED]
        [INTO] tbl_name [(col_name,...)]
        VALUES (expression,...)
or  REPLACE [LOW_PRIORITY | DELAYED]
        [INTO] tbl_name [(col_name,...)]
        SELECT ...
or  REPLACE [LOW_PRIORITY | DELAYED]
        [INTO] tbl_name
        SET col_name=expression, col_name=expression,...

REPLACE works exactly like INSERT, except that if an old record in
the table has the same value as a new record on a unique index, the
old record is deleted before the new record is inserted. See section
7.13 INSERT syntax.

7.15 LOAD DATA INFILE syntax
LOAD DATA [LOW_PRIORITY]  [LOCAL] INFILE  'file_name.txt' [REPLACE  |
IGNORE]
    INTO TABLE tbl_name
    [FIELDS
        [TERMINATED BY '\t']
        [OPTIONALLY] ENCLOSED BY '']
        [ESCAPED BY '\\' ]]
    [LINES TERMINATED BY '\n']
    [IGNORE number LINES]
    [(col_name,...)]

 The LOAD DATA INFILE  statement reads rows from  a text file into  a
table at a very  high speed. If the  LOCAL keyword is specified,  the
file is read  from the client  host. If LOCAL  is not specified,  the
file must be  located on  the server.  (LOCAL is  available in  MySQL
3.22.6 or later.)
 For security reasons, when reading text files located on the server,
the files must either reside in the database directory or be readable
by all. Also, to use LOAD DATA INFILE on server files, you  must have
the file privilege  on the  server host. See  section 6.5  Privileges
provided by MySQL.

 Using LOCAL will be a bit slower than letting the server  access the
files directly, since the contents of  the file must travel from  the
client host to the server  host. On the other  hand, you do not  need
the file privilege to load local files.

 You can also load  data files by  using the mysqlimport utility;  it
operates by sending  a LOAD DATA  INFILE command  to the server.  The
--local option causes mysqlimport to read data files from  the client
host. You can specify the --compress option to get better performance
over slow networks if  the client and  server support the  compressed
protocol.

 When locating   files on   the server  host,  the  server  uses  the
following rules:

        ·If an  absolute  pathname is  given,  the server  uses  the
        pathname as is.

        ·If a relative pathname with one or more  leading components
        is given, the server  searches for the  file relative to  the
        server's data directory.

        ·If a  filename with  no leading  components is   given, the
        server looks for the  file in the  database directory of  the
        current database.

Note that these  rules mean a  file given  as `./myfile.txt' is  read
from  the   server's  data  directory,   whereas  a  file   given  as
`myfile.txt' is  read  from the  database  directory of  the  current
database. Note also that for statements such as those below, the file
is read from the database directory for db1, not db2:

mysql> USE db1;
mysql> LOAD DATA INFILE "./data.txt" INTO TABLE db2.my_table;

 The REPLACE and  IGNORE keywords control  handling of input  records
that duplicate existing records on unique key values. If  you specify
REPLACE, new rows replace existing rows that have the same unique key
value. If you specify IGNORE,  input rows that duplicate an  existing
row on a unique  key value are skipped.  If you don't specify  either
option, an error occurs when a duplicate key value is found,  and the
rest of the text file is ignored.

 If you load  data from  a local file  using the  LOCAL keyword,  the
server has no way to stop transmission  of the file in the middle  of
the operation, so the  default bahavior is the  same as if IGNORE  is
specified.

LOAD DATA INFILE is the complement of SELECT ... INTO OUTFILE. See
section 7.11 SELECT syntax. To write data from a database to a file,
use SELECT ... INTO OUTFILE. To read the file back into the database,
use LOAD DATA INFILE. The syntax of the FIELDS and LINES clauses is
the same for both commands. Both clauses are optional, but FIELDS
must precede LINES if both are specified.

If you specify a  FIELDS clause, each  of its subclauses  (TERMINATED
BY, [OPTIONALLY] ENCLOSED BY and ESCAPED BY) is also optional, except
that you must specify at least one of them.


If you don't specify a FIELDS clause, the defaults are the same as if
you had written this:

FIELDS TERMINATED BY '\t' ENCLOSED BY '' ESCAPED BY '\\'

 If you don't specify a LINES clause,  the default is the same as  if
you had written this:

LINES TERMINATED BY '\n'

In other words, the defaults cause LOAD DATA INFILE to act as follows
when reading input:

        ·Look for line boundaries at newlines
        ·Break lines into fields at tabs
        ·Do not  expect fields  to be  enclosed within   any quoting
        characters
        ·Interpret occurrences of  tab, newline or  `\' preceded  by
        `\' as literal characters that are part of field values

Conversely, the   defaults cause   SELECT ...  INTO  OUTFILE  to  act  
follows when writing output:
        ·Write tabs between fields
        ·Do not enclose fields within any quoting characters
        ·Use `\' to  escape instances  of tab, newline  or `\'  that
        occur within field values
        ·Write newlines at the ends of lines
Note that  to write  FIELDS ESCAPED  BY '\\',  you  must specify  two
backslashes for the value to be read as a single backslash.

The IGNORE number  LINES option  can be used  to ignore  a header  of
column names at the start of the file:
mysql> LOAD DATA  INFILE "/tmp/file_name"  into table  test IGNORE  1
LINES;
When you use SELECT ... INTO OUTFILE in tandem with LOAD  DATA INFILE
to write data from a database into a file and then read the file back
into the database later, the field and line handling options for both
commands must match. Otherwise, LOAD  DATA INFILE will not  interpret
the contents of the file properly.

Suppose you use SELECT ... INTO  OUTFILE to write a file with  fields
delimited by commas:

mysql> SELECT * FROM table1 INTO OUTFILE 'data.txt'
           FIELDS TERMINATED BY ','
           FROM ...

To read the comma-delimited file back in, the correct statement would
be:

mysql> LOAD DATA INFILE 'data.txt' INTO TABLE table2
           FIELDS TERMINATED BY ',';

If instead you  tried to read  in the file  with the statement  shown
below, it wouldn't work because it instructs LOAD DATA INFILE to look
for tabs between fields:

mysql> LOAD DATA INFILE 'data.txt' INTO TABLE table2
           FIELDS TERMINATED BY '\t';

The likely result is that each  input line would be interpreted as  a
single field.

LOAD DATA INFILE  can be used  to read  files obtained from  external
sources, too. For example,  a file in  dBASE format will have  fields
separated by commas and  enclosed in double  quotes. If lines in  the
file are terminated by newlines, the command shown  below illustrates
the field and line handling options you would use to load the file:
mysql> LOAD DATA INFILE 'data.txt' INTO TABLE tbl_name
           FIELDS TERMINATED BY ',' ENCLOSED BY '"'
           LINES TERMINATED BY '\n';

Any of the field or line handling options may specify an empty string
(''). If not empty,  the FIELDS [OPTIONALLY]  ENCLOSED BY and  FIELDS
ESCAPED BY values must be  a single character. The FIELDS  TERMINATED
BY and LINES TERMINATED BY values may be more than one character. For
example,   to   write  lines   that   are   terminated  by   carriage
return-linefeed pairs,  or  to read  a  file containing  such  lines,
specify a LINES TERMINATED BY '\r\n' clause.

FIELDS [OPTIONALLY]   ENCLOSED BY  controls quoting   of fields.  For
output (SELECT ... INTO  OUTFILE), if you  omit the word  OPTIONALLY,
all fields are enclosed by the  ENCLOSED BY character. An example  of
such output (using a comma as the field delimiter) is shown below:

        "1","a string","100.20"
        "2","a string containing a , comma","102.20"
        "3","a string containing a \" quote","102.20"
        "4","a string containing a \", quote and comma","102.20"

If you specify OPTIONALLY, the ENCLOSED BY character is used  only to
enclose CHAR and VARCHAR fields:
        1,"a string",100.20
        2,"a string containing a , comma",102.20
        3,"a string containing a \" quote",102.20
        4,"a string containing a \", quote and comma",102.20
Note that occurrences  of the  ENCLOSED BY character  within a  field
value are escaped by  prefixing them with  the ESCAPED BY  character.
Also note  that if  you specify   an empty ESCAPED  BY value,   it is
possible to generate output that cannot be read properly by LOAD DATA
INFILE. For  example, the  output just  shown above  would appear  as
shown below if the escape character is empty. Observe that the second
field in the fourth line contains a comma following the  quote, which
(erroneously) appears to terminate the field:

        1,"a string",100.20
        2,"a string containing a , comma",102.20
        3,"a string containing a " quote",102.20
        4,"a string containing a ", quote and comma",102.20

For input, the ENCLOSED  BY character, if  present, is stripped  from
the ends of field values. (This is true whether or not  OPTIONALLY is
specified;  OPTIONALLY  has  no   effect on   input  interpretation.)
Occurrences of the ENCLOSED BY  character preceded by the ESCAPED  BY
character are  interpreted as  part of  the current  field value.  In
addition, duplicated ENCLOSED BY  characters occurring within  fields
are interpreted as single ENCLOSED BY characters if the  field itself
starts with  that  character. For   example, if ENCLOSED   BY '"'  is
specified, quotes are handled as shown below:

        "The ""BIG"" boss"  -> The "BIG" boss
        The "BIG" boss      -> The "BIG" boss
        The ""BIG"" boss    -> The ""BIG"" boss

FIELDS ESCAPED BY controls how  to write or read special  characters.
If the FIELDS ESCAPED BY character is not empty, it is used to prefix
the following characters on output:
        ·The FIELDS ESCAPED BY character
        ·The FIELDS [OPTIONALLY] ENCLOSED BY character
        ·The first character of the  FIELDS TERMINATED BY and  LINES
        TERMINATED BY values
        ·ASCII 0  (what is   actually written following  the  escape
        character is ASCII '0', not a zero-valued byte)
If the  FIELDS  ESCAPED BY   character is empty,   no characters  are
escaped. It is probably  not a good idea  to specify an empty  escape
character, particularly if field values  in your data contain any  of
the characters in the list just given.

For  input,  if  the  FIELDS  ESCAPED  BY  character  is  not  empty,
occurrences  of  that  character   are stripped   and  the  following
character is taken literally as part of a field value. The exceptions
are an escaped `0' or `N' (e.g., \0 or \N if the escape  character is
`\'). These sequences are interpreted as ASCII 0 (a zero-valued byte)
and NULL. See below for the rules on NULL handling.

For more information about `\'-escape syntax, see section
7.1 Literals: how to write string and nimbers.

In certain cases, field and line handling options interact:
        ·If LINES   TERMINATED BY  is an   empty string  and  FIELDS 
        TERMINATED BY is  non-empty, lines are  also terminated  with
        FIELDS TERMINATED BY.

        ·If the FIELDS TERMINATED BY  and FIELDS ENCLOSED BY  values
        are both empty  (''), a fixed-row  (non-delimited) format  is
        used. With fixed-row format,  no delimiters are used  between
        fields. Instead, column values are written and read using the
        ``display'' widths of the columns.  For example, if a  column
        is declared  as INT(7),  values for  the column   are written
        using 7-character fields. On input, values for the column are
        obtained by   reading 7   characters. Fixed-row  format  also
        affects handling of NULL values; see below.


Handling of NULL  values varies,  depending on the  FIELDS and  LINES
options you use:

        ·For the default FIELDS and LINES values, NULL is written as
        \N for output and \N is read as NULL for input  (assuming the
        ESCAPED BY character is `\').

        ·If FIELDS ENCLOSED BY is not empty, a field  containing the
        literal word NULL as its value is read as a NULL  value (this
        differs from the word NULL enclosed within FIELDS ENCLOSED BY
        characters, which is read as the string 'NULL').

        ·If FIELDS ESCAPED BY is empty, NULL is written as  the word
        NULL.

        ·With fixed-row format (which happens when FIELDS TERMINATED
        BY and FIELDS ENCLOSED BY are both empty), NULL is written as
        an empty string. Note that  this causes both NULL values  and
        empty strings   in the  table to   be indistinguishable  when
        written to  the file  since they  are both  written as  empty
        strings. If you need  to be able to  tell the two apart  when
        reading the   file back  in, you   should not  use  fixed-row 
        format.

Some cases are not supported by LOAD DATA INFILE:

        ·Fixed-size rows (FIELDS TERMINATED  BY and FIELDS  ENCLOSED
        BY both empty) and BLOB or TEXT columns.
        ·If you  specify one  separator that  is the  same  as or  a
        prefix  of  another,  LOAD  DATA  INFILE  won't  be  able  to  
          interpret the  input  properly. For   example, the  following
        FIELDS clause would cause problems:

        FIELDS TERMINATED BY '"' ENCLOSED BY '"'
        ·If FIELDS ESCAPED BY is empty, a field value  that contains
        an occurrence of  FIELDS ENCLOSED BY  or LINES TERMINATED  BY
        followed by the  FIELDS TERMINATED BY  value will cause  LOAD
        DATA INFILE to stop reading a  field or line too early.  This
        happens because LOAD  DATA INFILE  cannot properly  determine
        where the field or line value ends.

The following example loads all columns of the persondata table:

mysql> LOAD DATA INFILE 'persondata.txt' INTO TABLE persondata;


No field list is specified, so LOAD DATA INFILE expects input rows to
contain a field for each table  column. The default FIELDS and  LINES
values are used.

If you wish to load only some  of a table's columns, specify a  field
list:

mysql> LOAD DATA INFILE 'persondata.txt'
           INTO TABLE persondata (col1,col2,...);

You must also specify a field list if the order of the fields  in the
input file  differs from   the order of   the columns in  the  table.
Otherwise, MySQL cannot tell how to match up input fields  with table
columns.

If a row has too few fields, the columns for which no input  field is
present are   set to  default values.   Default value  assignment  is
described in section  7.6 CREATE TABLE syntax.

An empty field  value is  interpreted differently than  if the  field
value is missing:

        ·For string types, the column is set to the empty string.
        ·For numeric types, the column is set to 0.
        ·For  date  and  time  types,  the  column  is  set  to  the  
           appropriate ``zero'' value  for the type.  See section  7.2.6
        Date and time types.

 TIMESTAMP columns are only set to the current date and time if there
is a NULL value  for the column, or  (for the first TIMESTAMP  column
only) if the TIMESTAMP column is left out from the field list  when a
field list is specified.

 If an input row  has too many fields,  the extra fields are  ignored
and the number of warnings is incremented.

 LOAD DATA INFILE  regards all  input as  strings, so  you can't  use
numeric values for ENUM  or SET columns the  way you can with  INSERT
statements. All ENUM and SET values must be specified as strings!

 If you are using the C API, you can get information about  the query
by calling the API  function mysql_info() when  the LOAD DATA  INFILE
query finishes. The format of the information string is shown below:

Records: 1  Deleted: 0  Skipped: 0  Warnings: 0

 Warnings occur  under  the same  circumstances  as when  values  are
inserted via the INSERT statement (see section 7.13 INSERT syntax),
except that LOAD DATA INFILE  also generates warnings when there  are
too few or too  many fields in  the input row.  The warnings are  not
stored anywhere;  the number   of warnings can   only be used  as  an
indication if everything went well. If  you get warnings and want  to
know exactly why you got  them, one way to  do this is to use  SELECT
... INTO OUTFILE into another file and compare this to  your original
input file.


 For more information about the efficiency of INSERT versus LOAD DATA
INFILE and speeding up LOAD DATA INFILE, see section  10.12 How to
arrange a table to be as fast/small as possible

7.16 UPDATE syntax
UPDATE [LOW_PRIORITY] tbl_name SET
col_name1=expr1,col_name2=expr2,...
    [WHERE where_definition] [LIMIT #]

 UPDATE updates columns in existing  table rows with new values.  The
SET clause  indicates which  columns to  modify and  the values  they
should be given.  The WHERE  clause, if given,  specifies which  rows
should be updated. Otherwise all rows are updated.

 If you specify the keyword LOW_PRIORITY, execution of the  UPDATE is
delayed until no other clients are reading from the table.

 If you access a column from  tbl_name in an expression, UPDATE  uses
the current value of the column. For example, the following statement
sets the age column to one more than its current value:

mysql> UPDATE persondata SET age=age+1;

 UPDATE assignments are  evaluated from left  to right. For  example,
the following statement doubles the age column, then increments it:

mysql> UPDATE persondata SET age=age*2, age=age+1;

 If you set  a column to  the value it  currently has, MySQL  notices
this and doesn't update it.


UPDATE returns  the number  of rows  that were  actually changed.  In
MySQL 3.22  or later,  the C  API function  mysql_info() returns  the
number of  rows that   were matched and   updated and the  number  of
warnings that occurred during the UPDATE.

In MySQL 3.23 you can use LIMIT # to ensure that only a  given number
of rows are changed.

7.17 USE syntax
USE db_name
The USE db_name statement tells MySQL to use the db_name  database as
the default  database for  subsequent queries.  The database  remains
current until the end of the session, or until another  USE statement
is issued:

mysql> USE db1;
mysql> SELECT count(*) FROM mytable;      # selects from db1.mytable
mysql> USE db2;
mysql> SELECT count(*) FROM mytable;      # selects from db2.mytable

Making a particular database  current by means  of the USE  statement
does not preclude you from  accessing tables in other databases.  The
example below accesses the author table from the db1 database and the
editor table from the db2 database:

mysql> USE db1;
mysql> SELECT author_name,editor_name FROM author,db2.editor
           WHERE author.editor_id = db2.editor.editor_id;

 The USE statement is provided for Sybase compatibility.


7.18 FLUSH syntax (clearing caches)
FLUSH flush_option [,flush_option]

You should use the  FLUSH command if  you want to  clear some of  the
internal caches  MySQL uses.  To  execute FLUSH,  you must   have the
reload privilege.

flush_option can be any of the following:
+--------------+----------------------------------------------------+
|  HOSTS       |  Empties the host cache tables. You should         |
|              |  flush the host tables if some of your hosts       |
|              |  change IP number or if you get the error          |
|              |  message Host ... is blocked. When more than       |
|              |  max_connect_errors errors occur in a row for      |
|              |  a given host while connection to the MySQL        |
|              |  server, MySQL assumes something is wrong and      |
|              |  blocks the host from further connection requests. |
|              |  Flushing the host tables allows the host to       |
|              |  attempt to connect again. See section             |
|              |  18.2.3 Host '...' is blicked error.) You can start|
|              |  mysqld with -O max_connection_errors=999999999    |
|              |  to avoid this error message.                      |
+--------------+----------------------------------------------------+
|  LOGS        |  Closes and reopens the standard and update log    |
|              |  files. If you have specified the update log file  |
|              |  without an extension, the extension number of     |
|              |  t he new update log file will be incremented by   |
|              |  o ne relative to the previous file.               |
+--------------+----------------------------------------------------+
|  PRIVILEGES  |  Reloads the privileges from the grant tables      |
|              |  in the mysql database.                            |
+--------------+----------------------------------------------------+
|  TABLES      |  Closes all open tables.                           |
+--------------+----------------------------------------------------+
|  STATUS      |  Resets most status variables to zero.             |
+--------------+----------------------------------------------------+
You can  also  access each   of the  commands shown   above with  the
mysqladmin utility,  using  the flush-hosts,   flush-logs, reload  or
flush-tables commands.


7.19 KILL syntax
KILL thread_id
 Each connection to  mysqld runs in  a separate  thread. You can  see
which threads are running with the SHOW PROCESSLIST command, and kill
a thread with the KILL thread_id command.

If you have the process privilege, you can see and kill  all threads.
Otherwise, you can see and kill only your own threads.

 You can  also use  the mysqladmin  processlist and   mysqladmin kill
commands to examine and kill threads.

7.20 SHOW syntax (Get information about tables, columns,...)
   SHOW DATABASES [LIKE wild]
or SHOW TABLES [FROM db_name] [LIKE wild]
or SHOW COLUMNS FROM tbl_name [FROM db_name] [LIKE wild]
or SHOW INDEX FROM tbl_name [FROM db_name]
or SHOW STATUS
or SHOW VARIABLES [LIKE wild]
or SHOW PROCESSLIST
or SHOW TABLE STATUS [FROM db_name] [LIKE wild]

SHOW provides  information about  databases, tables,  columns or  the
server. If the  LIKE wild  part is  used, the  wild string  can be  a
string that uses the SQL `%' and `_' wildcard characters.

You can use db_name.tbl_name as  an alternative to the tbl_name  FROM
db_name syntax. These two statements are equivalent:

mysql> SHOW INDEX FROM mytable FROM mydb;
mysql> SHOW INDEX FROM mydb.mytable;

 SHOW DATABASES lists the databases on the MySQL server host. You can
also get this list using the mysqlshow command.

 SHOW TABLES lists the tables in  a given database. You can also  get
this list using the mysqlshow db_name command.

Note: If a user  doesn't have any privileges  for a table, the  table
will not show up in the output from SHOW TABLES or mysqlshow db_name.

SHOW COLUMNS lists the columns in a given table. If the  column types
are different than  you expect  them to be  based on  a CREATE  TABLE
statement, note   that MySQL   sometimes changes  column  types.  See
section 7.6.1 Silent column specification changes.

 The DESCRIBE statement provides information similar to SHOW COLUMNS.
See section 7.22 DESCRIBE syntax (Get information about columns)

 SHOW TABLE STATUS (new in version 3.23) works likes SHOW STATUS, but
provides a lot of information about each table. You can also get this
list using  the mysqlshow   --status db_name command.  The  following
columns are returned:
+-------------------+--------------------------------------------+-
|  Name             |  Name of the table                         | 
+-------------------+--------------------------------------------+-
|  Type             |  Type of table (NISAM, MyISAM or HEAP)     | 
+-------------------+--------------------------------------------+-
|  Rows             |  Number of rows                            | 
+-------------------+--------------------------------------------+-
|  Avg_row_length   |  Average row length                        | 
+-------------------+--------------------------------------------+-
|  Data_length      |  Length of the data file                   | 
+-------------------+--------------------------------------------+-
|  Max_data_length  |  Max length of the data file               | 
+-------------------+--------------------------------------------+-
|  Index_length     |  Length of the index file                  | 
+-------------------+--------------------------------------------+-
|  Data_free        |  Number of allocated but not used bytes    | 
+-------------------+--------------------------------------------+-
|  Auto_increment   |  Next autoincrement value                  | 
+-------------------+--------------------------------------------+-
|  Create_time      |  When the table was created                | 
+-------------------+--------------------------------------------+-
|  Update_time      |  When the data file was last updated       | 
+-------------------+--------------------------------------------+-
|  Check_time       |  When one last run a check on the table    | 
+-------------------+--------------------------------------------+-
|  Create_options   |  Extra options used with CREATE TABLE      | 
+-------------------+--------------------------------------------+-
|  Comment          |  The comment used when creating the table  | 
|                   |  (or some information why MySQL couldn't   | 
|                   |  access the table information).            | 
+-------------------+--------------------------------------------+-
 SHOW FIELDS is a synonym for SHOW COLUMNS and SHOW KEYS is a synonym
for SHOW INDEX. You can also  list a table's columns or indexes  with
mysqlshow db_name tbl_name or mysqlshow -k db_name tbl_name.

SHOW INDEX returns  the index  information in a  format that  closely
resembles the SQLStatistics call in  ODBC. The following columns  are
returned:
+---------------+---------------------------------------------------+
|  Table        |  Name of the table                                |
+---------------+---------------------------------------------------+
|  Non_unique   |  0 if the index can't contain duplicates.         |
+---------------+---------------------------------------------------+
|  Key_name     |  Name of the index                                |
+---------------+---------------------------------------------------+
|  Seq_in_index |  Column sequence number in index, starting with 1.|
+---------------+---------------------------------------------------+
|  Column_name  |  Column name.                                     |
+---------------+---------------------------------------------------+
|  Collation    |  How the column is sorted in the index.           |
|               |  In MySQL, this can have values A (Ascending)     |
|               |  or NULL (Not sorted).                            |
+---------------+---------------------------------------------------+
|  Cardinality  |  Number of unique values in the index.            |
|               |  This is updated by running isamchk -a.           |
+---------------+---------------------------------------------------+
|  Sub_part     |  Number of indexed characters if the column       |
|               |  is only partly indexed. NULL if the entire       |
|               |  key is indexed.                                  |
+---------------+---------------------------------------------------+

SHOW STATUS   provides server   status information  (like  mysqladmin
extended-status). The output resembles  that shown below, though  the
format and numbers may differ somewhat:

+--------------------------+--------+
| Variable_name            | Value  |
+--------------------------+--------+
| Aborted_clients          | 0      |
| Aborted_connects         | 0      |
| Created_tmp_tables       | 0      |
| Delayed_insert_threads   | 0      |
| Delayed_writes           | 0      |
| Delayed_errors           | 0      |
| Flush_commands           | 2      |
| Handler_delete           | 2      |
| Handler_read_first       | 0      |
| Handler_read_key         | 1      |
| Handler_read_next        | 0      |
| Handler_read_rnd         | 35     |
| Handler_update           | 0      |
| Handler_write            | 2      |
| Key_blocks_used          | 0      |
| Key_read_requests        | 0      |
| Key_reads                | 0      |
| Key_write_requests       | 0      |
| Key_writes               | 0      |
| Max_used_connections     | 1      |
| Not_flushed_key_blocks   | 0      |
| Not_flushed_delayed_rows | 0      |
| Open_tables              | 1      |
| Open_files               | 2      |
| Open_streams             | 0      |
| Opened_tables            | 11     |
| Questions                | 14     |
| Running_threads          | 1      |
| Slow_queries             | 0      |
| Uptime                   | 149111 |
+--------------------------+--------+

The status variables listed above have the following meaning:
+-----------------------+-------------------------------------------+
|  Aborted_clients      |  Number of connections that has been      |
|                       |  aborted because the client has died      |
|                       |  without closing the connection properly. |
+-----------------------+-------------------------------------------+
|  Aborted_connects     |  Number of tries to connect to the MySQL  |
|                       |  server that has failed.                  |
+-----------------------+-------------------------------------------+
|  Created_tmp_tables   |  Number of implicit temporary tables      |
|                       |  that has been created while executing    |
|                       |  statements.                              |
+-----------------------+-------------------------------------------+
|  Delayed_insert       |  Number of delayed insert handler         |
|  _threads             |  threads in use.                          |
+-----------------------+-------------------------------------------+
|  Delayed_writes       |  Number of rows written with              |
|                       |  INSERT DELAYED.                          |
+-----------------------+-------------------------------------------+
|  Delayed_errors       |  Number of rows written with INSERT       |
|                       |  DELAYED for which some error occurred    |
|                       |  (probably duplicate key).                |
+-----------------------+-------------------------------------------+
|  Flush_commands       |  Number of executed FLUSH commands.       |
+-----------------------+-------------------------------------------+
|  Handler_delete       |  Number of requests to delete a row       |
|                       |  from a table.                            |
+-----------------------+-------------------------------------------+
|  Handler_read_first   |  Number of request to read first the      |
|                       |  row in a table.                          |
+-----------------------+-------------------------------------------+
|  Handler_read_key     |  Number of request to read a row based    |
|                       |  on a key.                                |
+-----------------------+-------------------------------------------+
|  Handler_read_next    |  Number of request to read next row       |
|                       |  in key order.                            |
+-----------------------+-------------------------------------------+
|  Handler_read_rnd     |  Number of request to read a row based    |
|                       |  on a fixed position.                     |
+-----------------------+-------------------------------------------+
|  Handler_update       |  Number of requests to update a row       |
|                       |  in a table.                              |
+-----------------------+-------------------------------------------+
|  Handler_write        |  Number of requests to insert a row       |
|                       |  in a table.                              |
+-----------------------+-------------------------------------------+
|  Key_blocks_used      |  The number of used blocks in the         |
|                       |  key cache.                               |
+-----------------------+-------------------------------------------+
|  Key_read_requests    |  The number of request to read a key      |
|                       |  block from the cache.                    |
+-----------------------+-------------------------------------------+
|  Key_reads            |  The number of physical reads of a key    |
|                       |  block from disk.                         |
+-----------------------+-------------------------------------------+
|  Key_write_requests   |  The number of request to write a key     |
|                       |  block to the cache.                      |
+-----------------------+-------------------------------------------+
|  Key_writes           |  The number of physical writes of a key   |
|                       |  block to disk.                           |
+-----------------------+-------------------------------------------+
|  Max_used_connections |  The maximum number of connections        |
|                       |  that has been in use simultaneously.     |
+-----------------------+-------------------------------------------+
|  Not_flushed_key      |  Keys blocks in the key cache that has    |
|  _blocks              |  changed but hasn't yet been flushed      |
|                       |  to disk.                                 |
+-----------------------+-------------------------------------------+
|  Not_flushed_delayed  |  Number of rows waiting to be written     |
|  _rows                |  in INSERT DELAY queues.                  |
+-----------------------+-------------------------------------------+
|  Open_tables          |  Number of tables that are open.          |
+-----------------------+-------------------------------------------+
|  Open_files           |  Number of files that are open.           |
+-----------------------+-------------------------------------------+
|  Open_strems          |  Number of streams that are open          |
|                       |  (used mainly for logging)                |
+-----------------------+-------------------------------------------+
|  Opened_tables        |  Number of tables that has been opened.   |
+-----------------------+-------------------------------------------+
|  Questions            |  Number of questions asked from to the    |
|                       |  server.                                  |
+-----------------------+-------------------------------------------+
|  Running_threads      |  Number of currently open connections.    |
+-----------------------+-------------------------------------------+
|  Slow_queries         |  Number of queries that has taken more    |
|                       |  than long_query_time                     |
+-----------------------+-------------------------------------------+
|  Uptime               |  How many seconds the server has          |
|                       |  been up.                                 |
+-----------------------+-------------------------------------------+
Some comments about the above:
        ·If Opened_tables is big, then your table_cache  variable is
        probably too small.
        ·If key_reads is  big, then your  key_cache is probably  too
        small.   The  cache   hit   rate  can   be  calculated   with   
              key_reads/key_read_requests.
        ·If Handler_read_rnd is big, then you have a probably  a lot
        of queries that requires  MySQL to scan  whole tables or  you
        have joins that doesn't use keys properly.

SHOW  VARIABLES  shows  the  values  of  the  some  of  MySQL  system
variables. You can  also get  this information  using the  mysqladmin
variables command. If the default values are unsuitable, you  can set
most of these variables using command-line options when mysqld starts
up. The  output resembles  that shown  below, though  the format  and
numbers may differ somewhat:
+------------------------+--------------------------+
| Variable_name          | Value                    |
+------------------------+--------------------------+
| back_log               | 5                        |
| connect_timeout        | 5                        |
| basedir                | /my/monty/               |
| datadir                | /my/monty/data/          |
| delayed_insert_limit   | 100                      |
| delayed_insert_timeout | 300                      |
| delayed_queue_size     | 1000                     |
| join_buffer_size       | 131072                   |
| flush_time             | 0                        |
| key_buffer_size        | 1048540                  |
| language               | /my/monty/share/english/ |
| log                    | OFF                      |
| log_update             | OFF                      |
| long_query_time        | 10                       |
| low_priority_updates   | OFF                      |
| max_allowed_packet     | 1048576                  |
| max_connections        | 100                      |
| max_connect_errors     | 10                       |
| max_delayed_threads    | 20                       |
| max_heap_table_size    | 16777216                 |
| max_join_size          | 4294967295               |
| max_sort_length        | 1024                     |
| max_tmp_tables         | 32                       |
| net_buffer_length      | 16384                    |
| port                   | 3306                     |
| protocol-version       | 10                       |
| record_buffer          | 131072                   |
| skip_locking           | ON                       |
| socket                 | /tmp/mysql.sock          |
| sort_buffer            | 2097116                  |
| table_cache            | 64                       |
| thread_stack           | 131072                   |
| tmp_table_size         | 1048576                  |
| tmpdir                 | /machine/tmp/            |
| version                | 3.23.0-alpha-debug       |
| wait_timeout           | 28800                    |
+------------------------+--------------------------+

See section  10.1 Tuning server parame.

SHOW PROCESSLIST shows you  which threads are  running. You can  also
get this information using the mysqladmin processlist command. If you
have the process privilege, you  can see all threads. Otherwise,  you
can see only your own threads. See section 7.19 KILL syntax.

7.21 EXPLAIN syntax (Get information about a SELECT)

EXPLAIN SELECT select_options

When you precede a SELECT  statement with the keyword EXPLAIN,  MySQL
explains how it would process the SELECT, providing information about
how tables are joined and in which order.

With the help of EXPLAIN,  you can see when  you must add indexes  to
tables to get a faster SELECT that uses indexes to find  the records.
You can also  see if  the optimizer joins  the tables  in an  optimal
order. To force  the optimizer  to use a  specific join  order for  a
SELECT statement, add a STRAIGHT_JOIN clause.

For non-simple joins, EXPLAIN returns  a row of information for  each
table used in  the SELECT  statement. The  tables are  listed in  the
order  they  would  be  read.   MySQL resolves   all joins   using  a
single-sweep multi-join method.  This means  that MySQL  reads a  row
from the first table, then finds a matching row in the  second table,
then in the third table and so on. When all tables are  processed, it
outputs the selected  columns and backtracks  through the table  list
until a table is  found for which there  are more matching rows.  The
next row is read from this  table and the process continues with  the
next table.

Output from EXPLAIN includes the following columns:

table   The table to which the row of output refers.
type    The join type. Information about  the various types is  given
        below.
possible_keys
        The possible_keys column indicates which indexes MySQL  could
use to find the rows in the table. If this column is     empty, there
are no relevant indexes.  In this case, you  may  be able to  improve
the performance of your  query by                 examining the WHERE
clause to see if it refers to some       column or columns that would
be suitable for indexing. If     so, create an appropriate index  and
check the query with     EXPLAIN again. To  see what indexes a  table
has, use                SHOW INDEX FROM tbl_name.
key     The  key  column  indicates   the key   that  MySQL  actually  
          decided to use. The key is NULL if no index was chosen.
key_len
        The key_len  column  indicates the  length  of the  key  that
        MySQL decided to use. The length is NULL if the key is  NULL.
ref     The ref column shows which columns or constants  are     used
with the key to select rows from the table.

rows    The rows column indicates the number of rows  MySQL      must
examine to execute the query.

Extra   If the Extra column includes the text Only index,  this means
        that information   is retrieved  from the   table using  only
        information in the index tree. Normally, this is  much faster
        than scanning the entire table. If the Extra  column includes
        the text where  used, it means  that a  WHERE clause will  be
        used to restrict which rows will be matched against  the next
        table or sent to the client.
The different join types are listed below, ordered from best to worst
type:

system
        The table  has only   one row (=   system table). This  is  a 
special case of the const join type.
const   The table has at most one matching row, which will  be   read
at  the   start of   the  query.   Since  there  is   only one   row,  
       values from the column in this row can be regarded as    constants by
the rest of the optimizer. const tables are very         fast as they
are read only once!
eq_ref
        One row will  be read  from this table  for each  combination
        of rows  from the  previous tables.  This the   best possible
        join type, other than  the const types.  It is used when  all
        parts of  an index  are used  by the  join and  the index  is
        UNIQUE or a PRIMARY KEY.
ref     All rows with matching  index values will  be read from  this
        table for each combination of rows from the  previous tables.
        ref is used if  the join uses only  a leftmost prefix of  the
        key, or if the key is not  UNIQUE or a PRIMARY KEY (in  other
        words, if the join  cannot select a  single row based on  the
        key value). If the key that is used matches only a few  rows,
this join type is good.
range   Only rows that are in a given range will be  retrieved, using
        an index to select the  rows. The ref column indicates  which
        index is used.
index   This is the same as ALL,  except that only the index tree  is
        scanned. This is usually faster  than ALL, as the index  file
        is usually smaller than the data file.
ALL     A full table scan will be  done for each combination of  rows
        from the previous tables.  This is normally  not good if  the
        table is the first table  not marked const, and usually  very
        bad in all other cases. You normally can avoid ALL  by adding
        more indexes,  so that  the  row can  be retrieved   based on
        constant values or column values from earlier tables.

 You can get a good indication of  how good a join is by  multiplying
all values in the rows column of the EXPLAIN output. This should tell
you roughly how many  rows MySQL must  examine to execute the  query.
This  number  is  also  used  when  you  restrict  queries  with  the
max_join_size variable. See section 10.1 Tuning server parameters.

 The  following   example  shows   how  a   JOIN  can   be  optimized
progressively using the information provided by EXPLAIN.

 Suppose you have the SELECT statement shown below, that  you examine
using EXPLAIN:

EXPLAIN SELECT tt.TicketNumber, tt.TimeIn,
            tt.ProjectReference, tt.EstimatedShipDate,
            tt.ActualShipDate, tt.ClientID,
            tt.ServiceCodes, tt.RepetitiveID,
            tt.CurrentProcess, tt.CurrentDPPerson,
            tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
            et_1.COUNTRY, do.CUSTNAME
        FROM tt, et, et AS et_1, do
        WHERE tt.SubmitTime IS NULL
            AND tt.ActualPC = et.EMPLOYID
            AND tt.AssignedPC = et_1.EMPLOYID
            AND tt.ClientID = do.CUSTNMBR;

For this example, assume that:
        ·The columns being compared have been declared as follows:
        +---------+--------------+---------------+
        |  Table  |  Column      |  Column type  | 
        +---------+--------------+---------------+
        |  tt     |  ActualPC    |  CHAR(10)     | 
        +---------+--------------+---------------+
        |  tt     |  AssignedPC  |  CHAR(10)     | 
        +---------+--------------+---------------+
        |  tt     |  ClientID    |  CHAR(10)     | 
        +---------+--------------+---------------+
        |  et     |  EMPLOYID    |  CHAR(15)     | 
        +---------+--------------+---------------+
        |  do     |  CUSTNMBR    |  CHAR(15)     | 
        +---------+--------------+---------------+
       
        ·The tables have the indexes shown below:
        +---------+------------------------------+
        |  Table  |  Index                       |
        +---------+------------------------------+
        |  tt     |  ActualPC                    |
        +---------+------------------------------+
        |  tt     |  AssignedPC                  |
        +---------+------------------------------+
        |  tt     |  ClientID                    |
        +---------+------------------------------+
        |  et     |  EMPLOYID (primary key)      |
        +---------+------------------------------+
        |  do     |  CUSTNMBR (primary key)      |
        +---------+------------------------------+

        ·The tt.ActualPC values aren't evenly distributed.
 Initially, before any optimizations have been performed, the EXPLAIN
statement produces the following information:

table type possible_keys                key  key_len ref  rows  Extra
et    ALL  PRIMARY                      NULL NULL    NULL 74
do    ALL  PRIMARY                      NULL NULL    NULL 2135
et_1  ALL  PRIMARY                      NULL NULL    NULL 74
tt    ALL  AssignedPC,ClientID,ActualPC NULL NULL    NULL 3872
      range checked for each record (key map: 35)

Since type is ALL for each table, this output indicates that MySQL is
doing a full join for all tables!  This will take quite a long  time,
as the product of the number of rows in each table must  be examined!
For the case at hand, this is 74 * 2135 * 74 * 3872  = 45,268,558,720
rows. If the  tables were bigger,  you can only  imagine how long  it
would take...

One problem here  is that MySQL  can't (yet)  use indexes on  columns
efficiently if   they are   declared differently.  In  this  context,
VARCHAR and CHAR are the same  unless they are declared as  different
lengths. Since tt.ActualPC is declared as CHAR(10) and et.EMPLOYID is
declared as CHAR(15), there is a length mismatch.

To fix  this disparity  between column  lengths, use  ALTER TABLE  to
lengthen ActualPC from 10 characters to 15 characters:

mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);

Now tt.ActualPC and et.EMPLOYID are both VARCHAR(15).


Executing the EXPLAIN statement again produces this result:
+------+------+-----------+-------+----+------+----+----------------+
| table|type  |possible_  | key   |key | ref  |rows| Extra          |
|      |      |keys       |       |_len|      |    |                |
+------+------+-----------+-------+----+------+----+----------------+
| tt   | ALL  |Assigned PC| NULL  |NULL| NULL | 872| where used     |
|      |      |,ClientID  |       |    |      |    |                |
|      |      |,ActualPC  |       |    |      |    |                |
+------+------+-----------+-------+----+------+----+----------------+
| do   | ALL  |  PRIMARY  | NULL  |NULL| NULL |2135| range checked  |
|      |      |           |       |    |      |    | for each       |
+------+------+-----------+-------+----+------+----| record         |
| et_1 | ALL  |  PRIMARY  | NULL  |NULL| NULL | 74 | (key map:1)    |
+------+------+-----------+-------+----+------+----+----------------+
| et   |eq_ref|  PRIMARY  |PRIMARY|15  |tt.Act|  1 |                |
|      |      |           |       |    | ualPC|    |                |
+------+------+-----------+-------+----+------+----+----------------+
 This is not  perfect, but is  much better (the  product of the  rows
values is now less by a factor of 74). This version is executed  in a
couple of seconds.

A second  alteration  can be   made to eliminate   the column  length
mismatches for the  tt.AssignedPC = et_1.EMPLOYID  and tt.ClientID  =
do.CUSTNMBR comparisons:

mysql> ALTER TABLE tt MODIFY AssignedPC VARCHAR(15),
                      MODIFY ClientID   VARCHAR(15);

Now EXPLAIN produces the output shown below:

+------+------+-----------+-------+----+-------------+-----+-------+
| table|type  |possible_  | key   |key | ref         |rows | Extra |
|      |      |keys       |       |_len|             |     |       |
+------+------+-----------+-------+----+-------------+-----+-------+
| et   |ALL   |PRIMARY    | NULL  |NULL| NULL  74    |     |       |
+------+------+-----------+-------+----+-------------+-----+-------+
| tt   |ref   |AssignedPC,|Actual | 15 | et.EMPLOYID | 52  | where |
|      |      |ClientID,  |PC     |    |             |     | used  |
|      |      |ActualPC   |       |    |             |     |       |
+------+------+-----------+-------+----+-------------+-----+-------+
| et_1 |eq_ref|PRIMARY    |PRIMARY| 15 |tt.AssignedPC|  1  |       |
+------+------+-----------+-------+----+-------------+-----+-------+
| do   |eq_ref|PRIMARY    |PRIMARY| 15 |tt.ClientID  |  1  |       |
+------+------+-----------+-------+----+-------------+-----+-------+
This is ``almost'' as good as it can get.

The remaining problem is that, by default, MySQL assumes  that values
in the tt.ActualPC column are evenly distributed, and that  isn't the
case for the tt  table. Fortunately, it is  easy to tell MySQL  about
this:

shell> isamchk --analyze PATH_TO_MYSQL_DATABASE/tt
shell> mysqladmin refresh

Now the join is ``perfect'', and EXPLAIN produces this result:
+------+------+-----------+-------+----+-------------+-----+-------+
| table|type  |possible_  | key   |key | ref         |rows | Extra |
|      |      |keys       |       |_len|             |     |       |
+------+------+-----------+-------+----+-------------+-----+-------+
| tt   |All   |AssignedPC,| NULL  |NULL|             | 3872| where |
|      |      |ClientID,  |       |    |             |     | used  |
|      |      |ActualPC   |       |    |             |     |       |
+------+------+-----------+-------+----+-------------+-----+-------+
| et   |eq_ref|PRIMARY    |PRIMARY| 15 |tt.ActualPC  |  1  |       |
+------+------+-----------+-------+----+-------------+-----+-------+
| et_1 |eq_ref|PRIMARY    |PRIMARY| 15 |tt.AssignedPC|  1  |       |
+------+------+-----------+-------+----+-------------+-----+-------+
| do   |eq_ref|PRIMARY    |PRIMARY| 15 |tt.ClientID  |  1  |       |
+------+------+-----------+-------+----+-------------+-----+-------+

Note that the rows column in the output from EXPLAIN is an ``educated
guess'' from  the MySQL  join  optimizer; To  optimize a   query, you
should check if the numbers are even close to the truth. If  not, you
may get  better performance  by using  STRAIGHT_JOIN in   your SELECT
statement and trying to list the  tables in a different order in  the
FROM clause.

7.22 DESCRIBE syntax (Get information about columns)
{DESCRIBE | DESC} tbl_name {col_name | wild}

DESCRIBE provides information about  a table's columns. col_name  may
be a column name or a string containing the SQL `%' and  `_' wildcard
characters.

 If the column types are different  than you expect them to be  based
on a CREATE TABLE statement, note that MySQL sometimes changes column
types. See section 7.6.1 Silent column specification changes.

 This statement is provided for Oracle compatibility.
The SHOW statement provides similar information. See section
7.20 SHOW syntax (Get information about table, columns,...)

7.23 LOCK TABLES/UNLOCK TABLES syntax
LOCK TABLES tbl_name [AS alias] {READ | [LOW_PRIORITY] WRITE}
            [, tbl_name {READ | [LOW_PRIORITY] WRITE} ...]
...
UNLOCK TABLES

 LOCK TABLES  locks  tables for  the  current thread.  UNLOCK  TABLES
releases any locks held  by the current  thread. All tables that  are
locked by  the current  thread are  automatically unlocked   when the
thread issues  another LOCK  TABLES, or  when the  connection to  the
server is closed.
 If a thread obtains  a READ lock  on a table,  that thread (and  all
other threads) can only  read from the table.  If a thread obtains  a
WRITE lock on a table, then only the thread holding the lock can READ
from or WRITE to the table. Other threads are blocked.

 Each thread  waits (without  timing out)  until it  obtains all  the
locks it has requested.

 WRITE locks normally have higher priority than READ locks, to ensure
that updates are processed  as soon as  possible. This means that  if
one thread obtains  a READ lock  and then  another thread requests  a
WRITE lock, subsequent READ lock  requests will wait until the  WRITE
thread has gotten the lock and released it. You can  use LOW_PRIORITY
WRITE locks to  allow other threads  to obtain  READ locks while  the
thread  is  waiting   for the   WRITE  lock.   You should   only  use
LOW_PRIORITY WRITE locks if you  are sure that there will  eventually
be a time when no threads will have a READ lock.

 When you use  LOCK TABLES,  you must lock  all tables  that you  are
going to use!  If you are  using a  table multiple times  in a  query
(with aliases),  you must  get a  lock for  each  alias! This  policy
ensures that table locking is deadlock free.

 Note that you  should NOT lock  any tables that  you are using  with
INSERT DELAYED. This is because that in this case the INSERT  is done
by a separate thread.

 Normally, you  don't  have to   lock tables, as   all single  UPDATE
statements are atomic; no other  thread can interfere with any  other
currently executing SQL  statement. There  are a few  cases when  you
would like to lock tables anyway:


        ·If you  are going  to run  many operations  on  a bunch  of
        tables, it's much faster to lock the tables you are  going to
        use. The downside  is, of  course, that no  other thread  can
        update a READ-locked  table and  no other thread  can read  a
        WRITE-locked table.

        ·MySQL doesn't  support a  transaction environment,   so you
        must use LOCK  TABLES if  you want  to ensure  that no  other
        thread comes  between a  SELECT and  an UPDATE.   The example
        shown below requires LOCK TABLES in order to execute safely:


        mysql> LOCK TABLES trans READ, customer WRITE;
        mysql>  select  sum(value)   from trans   where  customer_id=  
                some_id;
        mysql> update customer set total_value
                =sum_from_previous_statement
           where customer_id=some_id;
        mysql> UNLOCK TABLES;

        Without LOCK TABLES,  there is a  chance that another  thread
        might insert a new row  in the trans table between  execution
        of the SELECT and UPDATE statements.

 By    using    incremental     updates    (UPDATE    customer    SET
value=value+new_value) or   the LAST_INSERT_ID()  function,  you  can
avoid using LOCK TABLES in many cases.

 You can also solve some cases by using the user-level lock functions
GET_LOCK() and RELEASE_LOCK(). These locks are saved in a  hash table
in  the   server  and   implemented  with   pthread_mutex_lock()  and
pthread_mutex_unlock() for high speed. See section 7.3.12
Miscellaneous functions.

 See section 10,11  How MySQL  locks table, for  more information  on
locking policy.

7.24 SET OPTION syntax
SET [OPTION] SQL_VALUE_OPTION= value, ...
 SET OPTION sets  various options  that affect the  operation of  the
server or your client. Any option you set remains in effect until the
current session ends,  or until  you set  the option  to a  different
value.

CHARACTER SET character_set_name | DEFAULT
 This maps all strings from and to the client with the given mapping.
Currently the only option for character_set_name is  cp1251_koi8, but
you can easily add new mappings by editing the  `sql/convert.cc' file
in the MySQL source distribution. The default mapping can be restored
by using a character_set_name value of DEFAULT. Note that  the syntax
for setting  the CHARACTER  SET option  differs from  the syntax  for
setting the other options.

PASSWORD = PASSWORD('some password')
 Set the password for  the current user.  Any non-anonymous user  can
change his own password!

PASSWORD FOR user = PASSWORD('some password')
 Set the password  for a specific  user on  the current server  host.
Only a user with access to the  mysql database can do this. The  user
should be given in user@hostname format, where user and  hostname are
exactly as  they are  listed in   the User and  Host columns   of the
mysql.user table entry. For  example, if you  had an entry with  User
and Host fields of 'bob' and '%.loc.gov', you would write:

mysql> SET PASSWORD FOR bob@"%.loc.gov" = PASSWORD("newpass");
SQL_BIG_TABLES = 0 | 1
 If set to 1, all temporary tables are stored on disk rather  than in
memory. This will be a little slower, but you will not get  the error
The table tbl_name is full for  big SELECT operations that require  a
large temporary table. The  default value for  a new connection is  0
(i.e., use in-memory temporary tables).

SQL_BIG_SELECTS = 0 | 1
 If set to 1, MySQL will abort if a SELECT is attempted that probably
will take a very long time. This is useful when an  inadvisable WHERE
statement has been issued.  A big query is  defined as a SELECT  that
probably will  have  to examine  more  than max_join_size  rows.  The
default value for a new connection is 0 (which will allow  all SELECT
statements).

SQL_LOW_PRIORITY_UPDATES = 0 | 1
 If set to  1, all INSERT,  UPDATE and  DELETE statements wait  until
there is no pending SELECT on the affected table.

SQL_SELECT_LIMIT = value | DEFAULT
 The maximum number of records to return from SELECT statements. If a
SELECT has a LIMIT clause, the LIMIT takes precedence over  the value
of SQL_SELECT_LIMIT.   The default  value for   a new  connection  is
``unlimited''. If you have changed  the limit, the default value  can
be restored by using a SQL_SELECT_LIMIT value of DEFAULT.

SQL_LOG_OFF = 0 | 1
 If set to 1, no  logging will be done  to the standard log for  this
client, if the client has the process privilege. This does not affect
the update log!


SQL_LOG_UPDATE = 0 | 1
 If set to  0, no  logging will  be done to  the update  log for  the
client, if the client has the process privilege. This does not affect
the standard log!

TIMESTAMP = timestamp_value | DEFAULT
 Set the  time for  this client.  This is  used to  get the  original
timestamp if you use the update log to restore rows.

LAST_INSERT_ID = #
 Set the value to be  returned from LAST_INSERT_ID(). This is  stored
in the update  log when you  use LAST_INSERT_ID()  in a command  that
updates a table.

INSERT_ID = #
 Set the  value to   be used by   the following INSERT  command  when
inserting an  AUTO_INCREMENT  value. This  is  mainly used  with  the
update log.

7.25 GRANT and REVOKE syntax
GRANT priv_type [(column_list)] [, priv_type [(column_list)] ...]
    ON {tbl_name | * | *.* | db_name.*}
    TO user_name [IDENTIFIED BY 'password']
        [, user_name [IDENTIFIED BY 'password'] ...]
    [WITH GRANT OPTION]

REVOKE priv_type [(column_list)] [, priv_type [(column_list)] ...]
    ON {tbl_name | * | *.* | db_name.*}
    FROM user_name [, user_name ...]

 GRANT is implemented in  MySQL 3.22.11 or  later. For earlier  MySQL
versions, the GRANT statement does nothing.
 The GRANT and REVOKE commands  allow system administrators to  grant
and revoke rights to MySQL users at four privilege levels:
Global level
 Global privileges apply to  all databases on  a given server.  These
privileges are stored in the mysql.user table.

Database level
 Database privileges apply to all  tables in a given database.  These
privileges are stored in the mysql.db and mysql.host tables.

Table level
 Table privileges  apply  to all   columns in a   given table.  These
privileges are stored in the mysql.tables_priv table.

Column level
 Column privileges apply to  single columns in  a given table.  These
privileges are stored in the mysql.columns_priv table.

For examples of how GRANT works, see section 6.11 Adding new user
privileges to MySQL.

 For the GRANT and REVOKE  statements, priv_type may be specified  as
any of the following:

ALL PRIVILEGES      FILE                RELOAD
ALTER               INDEX               SELECT
CREATE              INSERT              SHUTDOWN
DELETE              PROCESS             UPDATE
DROP                REFERENCES          USAGE

 ALL  is  a  synonym  for  ALL  PRIVILEGES.  REFERENCES  is  not  yet
implemented. USAGE is currently a  synonym for ``no privileges''.  It
can be used when you want to create a user that has no privileges.


To revoke the grant privilege from  a user, use a priv_type value  of
GRANT OPTION:

REVOKE GRANT OPTION ON ... FROM ...;

The only priv_type  values you can  specify for  a table are  SELECT,
INSERT, UPDATE, DELETE, CREATE, DROP, GRANT, INDEX and ALTER.

The only priv_type values you can specify for a column (that is, when
you use a column_list clause) are SELECT, INSERT and UPDATE.

 You can set global  privileges by using ON  *.* syntax. You can  set
database privileges by using ON db_name.* syntax. If you specify ON *
and you have a current database, you will set the privileges for that
database. (Warning: If you specify ON * and you{{<EM>
}}
don't{{</EM>
}}
h
a
v
e   a  current  database,  you  will affect the global privileges!)


 In order  to accommodate  granting rights  to users   from arbitrary
hosts, MySQL  supports specifying  the user_name  value in   the form
user@host. If you want  to specify a  user string containing  special
characters (such   as `-'),   or a  host  string  containing  special
characters or wildcard characters  (such as `%'),  you can quote  the
user or host name (e.g., 'test-user'@'test-hostname').

 You   can  specify   wildcards   in  the   hostname.  For   example,
user@"%.loc.gov" applies to user for any host in the  loc.gov domain,
and  user@"144.155.166.%"  applies  to  user  for  any  host  in  the
144.155.166 class C subnet.

 The simple form user is a  synonym for user@"%". Note: If you  allow
anonymous users   to connect   to the  MySQL  server  (which  is  the
default), you  should  also add  all  local users  as  user@localhost
because otherwise the anonymous user entry for the local host  in the
mysql.user table will  be used when  the user tries  to log into  the
MySQL server from the local  machine! Anonymous users are defined  by
inserting entries with  User='' into  the mysql.user  table. You  can
verify if this applies to you by executing this query:

mysql> SELECT Host,User FROM mysql.user WHERE User='';

 For the moment, GRANT only supports host, table, database and column
names up   to 60  characters long.   A user  name can   be up  to  16
characters.

 The privileges for a table or column are formed from the  logical OR
of the privileges at each of the four privilege levels.  For example,
if the mysql.user  table specifies that  a user  has a global  select
privilege, this can't be denied by an entry at the database, table or
column level.

 The privileges for a column can be calculated as follows:
global privileges
        OR (database privileges AND host privileges)
        OR table privileges
        OR column privileges

 In most  cases, you  grant  rights to  a user   at only one  of  the
privilege levels, so life isn't normally as complicated as  above. :)
The details  of the   privilege-checking procedure are  presented  in
section 6 The MySQL access privilege system.

 If you grant  privileges for a  user/hostname combination that  does
not exist in  the mysql.user  table, an  entry is  added and  remains
there until deleted with a DELETE command. In other words,  GRANT may
create user table entries, but REVOKE will not remove them;  you must
do that explicitly using DELETE.
 In MySQL 3.22.12 or later, if a  new user is created or if you  have
global grant  privileges, the  user's  password will  be set   to the
password specified by the IDENTIFIED BY  clause, if one is given.  If
the user already had a password, it is replaced by the new one.

 Warning: If you create a new  user but do not specify an  IDENTIFIED
BY clause, the user has no password. This is insecure.

 Passwords can also be set with the SET PASSWORD command. See section
7.24 SET OPTION syntax.

 If you grant  privileges for a  database, an  entry in the  mysql.db
table is created if needed. When all privileges for the database have
been removed with REVOKE, this entry is deleted.

 If a user doesn't have any privileges  on a table, the table is  not
displayed when the user requests a list of tables (e.g., with  a SHOW
TABLES statement).

 The WITH GRANT OPTION clause gives  the user the ability to give  to
other users any privileges  the user has  at the specified  privilege
level. You should be careful to whom you give the grant privilege, as
two users with different privileges may be able to join privileges!
 You cannot grant another user  a privilege you don't have  yourself;
the grant privilege allows you to give away only those privileges you
possess.

 Be aware  that  when you  grant  a user  the  grant privilege  at  a
particular privilege level, any privileges the user already possesses
(or is given in the future!) at that level are also grantable by that
user. Suppose you grant a user the insert privilege on a database. If
you then grant the select privilege on the database and  specify WITH
GRANT OPTION, the user can give  away not only the select  privilege,
but also insert. If you then  grant the update privilege to the  user
on the  database, the   user can give   away the insert,  select  and
update.

 You should not grant  alter privileges to a  normal user. If you  do
that, the user can  try to subvert  the privilege system by  renaming
tables!

 Note that if you are using  table or column privileges for even  one
user, the server examines table  and column privileges for all  users
and this will slow down MySQL a bit.

 When mysqld starts, all privileges  are read into memory.  Database,
table and   column privileges  take effect   at once  and  user-level
privileges take effect the next time the user connects. Modifications
to the  grant tables   that you perform   using GRANT or  REVOKE  are
noticed by the  server immediately.  If you modify  the grant  tables
manually (using INSERT,  UPDATE, etc.),  you should  execute a  FLUSH
PRIVILEGES statement or run  mysqladmin flush-privileges to tell  the
server to reload  the grant  tables. See section  6.9 When  privilege
changes take effect.

The biggest differences between  the ANSI SQL  and MySQL versions  of
GRANT are:
        ·ANSI SQL doesn't have  global or database-level  privileges
        and ANSI SQL doesn't support  all privilege types that  MySQL
        supports.

        ·When you drop a table in  ANSI SQL, all privileges for  the
        table are revoked. If you revoke a privilege in ANSI SQL, all
        privileges that were granted based on this privilege are also
        revoked. In   MySQL, privileges   can be  dropped  only  with  
        explicit REVOKE commands or  by manipulating the MySQL  grant
        tables.

7.26 CREATE INDEX syntax
CREATE [UNIQUE] INDEX index_name ON tbl_name  (col_name[(length]),...
)

 The CREATE INDEX  statement doesn't  do anything in  MySQL prior  to
version 3.22. In 3.22  or later, CREATE INDEX  is mapped to an  ALTER
TABLE statement   to create  indexes. See   section 7.7  ALTER  TABLE
syntax.

 Normally, you create all  indexes on a table  at the time the  table
itself is created with CREATE TABLE. See section  7.6 CREATE TABLE
syn. CREATE INDEX allows you to add indexes to existing tables.

 A column list of the form (col1,col2,...) creates  a multiple-column
index. Index values  are formed  by concatenating the  values of  the
given columns.

 For CHAR and VARCHAR columns, indexes  can be created that use  only
part of a column,  using col_name(length) syntax.  (On BLOB and  TEXT
columns the length is required). The statement shown below creates an
index using the first 10 characters of the name column:

mysql> CREATE INDEX part_of_name ON customer (name(10));

 Since most names  usually differ  in the first  10 characters,  this
index should not be much slower than an index created from the entire
name column. Also,  using partial  columns for indexes  can make  the
index file much  smaller, which could  save a lot  of disk space  and
might also speed up INSERT operations!

 Note that you can only  add a index on  a column that can have  NULL
values or  on a  BLOB/TEXT column  if you  are  useing MySQL  version
3.23.2 or newer and are using the MyISAM table type.
 For more information about how MySQL uses indexes, see section
10.4 How MySQL uses inde.

7.27 DROP INDEX syntax
DROP INDEX index_name

DROP INDEX doesn't  do anything in  MySQL prior  to version 3.22.  In
3.22 or later, DROP  INDEX is mapped to  an ALTER TABLE statement  to
drop the index. See section  7.7 ALTER TABLE syntax.

7.28 Comment syntax
The MySQL server supports the # to end of line, -- to end of line and
/* in-line or multiple-line */ comment styles:

mysql> select 1+1;     # This comment continues to the end of line

mysql> select 1+1;     -- This comment continues to the end of line

mysql> select 1 /* this is an in-line comment */ + 1;

mysql> select 1+
/*
this is a
multiple-line comment
*/
1;

Note that the  -- comment  style requires you  to have  at least  one
space after the --!

Although the server  understands the comment  syntax just  described,
there are some limitations on the way that the mysql client parses /*
... */ comments:

        ·Single-quote  and  double-quote  characters  are  taken  to
        indicate the  beginning of  a quoted  string, even   within a
        comment. If the quote is not matched by a second quote within
        the comment,   the parser  doesn't realize   the comment  has
        ended. If you are running  mysql interactively, you can  tell
        that it  has gotten  confused like  this because   the prompt
        changes from mysql> to '> or ">.

        ·A semicolon is taken to indicate the end of the current SQL
        statement and anything following it to indicate the beginning
        of the next statement.

 These limitations apply  both when you  run mysql interactively  and
when you put commands in a file and tell mysql to read its input from
that file with mysql < some-file.

 MySQL doesn't support the `--' ANSI SQL comment style. See section
5.3.7 '--' as the start of a cc..

7.29 CREATE FUNCTION/DROP FUNCTION syntax
CREATE FUNCTION function_name RETURNS {STRING|REAL|INTEGER}
       SONAME shared_library_name

DROP FUNCTION function_name

 A user-definable function (UDF) is a way to extend MySQL with  a new
function that works like  native (built in)  MySQL functions such  as
ABS() and CONCAT().

 CREATE FUNCTION saves the function's  name, type and shared  library
name in the  mysql.func system table.  You must  have the insert  and
delete  privileges  for  the   mysql database   to create   and  drop
functions.

 All active  functions  are reloaded  each  time the  server  starts,
unless you start mysqld with the --skip-grant-tables option.  In this
case, UDF initialization  is skipped  and UDFs  are unavailable.  (An
active function is one that has been loaded with CREATE  FUNCTION and
not removed with DROP FUNCTION.)

 For instructions on writing user-definable functions, see sectio
14 Adding new funtion to My. For the UDF mechanism to work, functions
must be written in C or C++ and your operating system must support
dynamic loading.

7.30 Is MySQL picky about reserved words?
 A common problem  stems from trying  to create  a table with  column
names that use the names of datatypes or functions built  into MySQL,
such as TIMESTAMP or GROUP. You're allowed to do it (for example, ABS
is an allowed column name), but  whitespace is not allowed between  a
function name and the `(' when  using functions whose names are  also
column names.

 The following words are explicitly  reserved in MySQL. Most of  them
are forbidden   by ANSI  SQL92  as column   and/or table  names  (for
example, group). A few are reserved  because MySQL needs them and  is
(currently) using a yacc parser:

+-----------------+--------------+---------------+---------------+
|  action         |  add         |  all          |  alter        |
+-----------------+--------------+---------------+---------------+
|  after          |  and         |  as           |  asc          |
+-----------------+--------------+---------------+---------------+
|  auto_increment |  between     |  bigint       |  bit          |
+-----------------+--------------+---------------+---------------+
|  binary         |  blob        |  bool         |  both         |
+-----------------+--------------+---------------+---------------+
|  by             |  cascade     |  char         |  character    |
+-----------------+--------------+---------------+---------------+
|  change         |  check       |  column       |  columns      |
+-----------------+--------------+---------------+---------------+
|  constraint     |  create      |  cross        |  current_date |
+-----------------+--------------+---------------+---------------+
|  current_time   |  current     |  data         |  database     |
|                 |  _timestamp  |               |               |
+-----------------+--------------+---------------+---------------+
|  databases      |  date        |  datetime     |  day          |
+-----------------+--------------+---------------+---------------+
|  day_hour       |  day_minute  |  day_second   |  dayofmonth   |
+-----------------+--------------+---------------+---------------+
|  dayofweek      |  dayofyear   |  dec          |  decimal      |
+-----------------+--------------+---------------+---------------+
|  default        |  delete      |  desc         |  describe     |
+-----------------+--------------+---------------+---------------+
|  distinct       |  distinctrow |  double       |  drop         |
+-----------------+--------------+---------------+---------------+
|  escaped        |  enclosed    |  enum         |  explain      |
+-----------------+--------------+---------------+---------------+
|  exists         |  fields      |  first        |  float        |
+-----------------+--------------+---------------+---------------+
|  float4         |  float8      |  foreign      |  from         |
+-----------------+--------------+---------------+---------------+
|  for            |  full        |  function     |  grant        |
+-----------------+--------------+---------------+---------------+
|  group          |  having      |  hour         |  hour_minute  |
+-----------------+--------------+---------------+---------------+
|  hour_second    |  ignore      |  in           |  index        |
+-----------------+--------------+---------------+---------------+
|  infile         |  insert      |  int          |  integer      |
+-----------------+--------------+---------------+---------------+
|  interval       |  int1        |  int2         |  int3         |
+-----------------+--------------+---------------+---------------+
|  int4           |  int8        |  into         |  if           |
+-----------------+--------------+---------------+---------------+
|  is             |  join        |  key          |  keys         |
+-----------------+--------------+---------------+---------------+
|  last_insert_id |  leading     |  left         |  like         |
+-----------------+--------------+---------------+---------------+
|  lines          |  limit       |  load         |  lock         |
+-----------------+--------------+---------------+---------------+
|  long           |  longblob    |  longtext     |  low_priority |
+-----------------+--------------+---------------+---------------+
|  match          |  mediumblob  |  mediumtext   |  mediumint    |
+-----------------+--------------+---------------+---------------+
|  middleint      |  minute      |  minute_second|  month        |
+-----------------+--------------+---------------+---------------+
|  monthname      |  natural     |  numeric      |  no           |
+-----------------+--------------+---------------+---------------+
|  not            |  null        |  on           |  option       |
+-----------------+--------------+---------------+---------------+
|  optionally     |  or          |  order        |  outer        |
+-----------------+--------------+---------------+---------------+
|  outfile        |  partial     |  password     |  precision    |
+-----------------+--------------+---------------+---------------+
|  primary        |  procedure   |  processlist  |  privileges   |
+-----------------+--------------+---------------+---------------+
|  quarter        |  read        |  real         |  references   |
+-----------------+--------------+---------------+---------------+
|  rename         |  regexp      |  reverse      |  repeat       |
+-----------------+--------------+---------------+---------------+
|  replace        |  restrict    |  returns      |  rlike        |
+-----------------+--------------+---------------+---------------+
|  second         |  select      |  set          |  show         |
+-----------------+--------------+---------------+---------------+
|  smallint       |  soname      |  sql_big_     |  sql_big_     |
+-----------------+--------------+---------------+---------------+
|                 |              |  tables       |  selects      |
+-----------------+--------------+---------------+---------------+
|  sql_select_    |  sql_low_    |  sql_log_off  |  sql_log_     |
|  limit          |  priority_   |               |  update       |
|                 |  updates     |               |               |
+-----------------+--------------+---------------+---------------+
|  straight_join  |  starting    |  status       |  string       |
+-----------------+--------------+---------------+---------------+
|  table          |  tables      |  terminated   |  text         |
+-----------------+--------------+---------------+---------------+
|  time           |  timestamp   |  tinyblob     |  tinytext     |
+-----------------+--------------+---------------+---------------+
|  tinyint        |  trailing    |  to           |  use          |
+-----------------+--------------+---------------+---------------+
|  using          |  unique      |  unlock       |  unsigned     |
+-----------------+--------------+---------------+---------------+
|  update         |  usage       |  values       |  varchar      |
+-----------------+--------------+---------------+---------------+
|  variables      |  varying     |  varbinary    |  with         |
+-----------------+--------------+---------------+---------------+
|  write          |  where       |  year         |  year_month   |
+-----------------+--------------+---------------+---------------+
|  zerofill       |              |               |               |
+-----------------+--------------+---------------+---------------+

 The following symbols (from the table above) are disallowed  by ANSI
SQL but allowed by MySQL as column/table names. This is  because some
of these  names are  very natural   names and a  lot of   people have
already used them.

        ·ACTION
        ·BIT
        ·DATE
        ·ENUM
        ·NO
        ·TEXT
        ·TIME
        ·TIMESTAMP


8 MySQL Tutorial
mysql이라는 클라이언트 프로그램을 이용하여 MySQL을 익혀 보도록  하자.
mysql은 간단히 데이터베이스를 만들고 사용할 수 있게 해  주는 프로그램
으로 '터미널 모니터'혹은 간단히 '모니터'라고도 한다.

mysql은 대화식 프로그램으로서 서버에 연결하고, 질문을 수행하고,  결과
를 화면에 보여주는 일을 한다. mysql은 배치 모드(batch  mode)에서도 사
용할 수 있다:

미리 파일에 sql 명령문을 넣어두고 mysql에게 파일의  명령을 수행하라고
하면 된다(뒤에서 알아 보겠지만  'mysql -vvv < batch_test.txt'  식으로
사용하면 된다).
mysql의 옵션들을 보려면 --help 옵션을 붙여서 실행하면 된다:

shell> mysql --help

이 튜토리얼에서는 mysql에 설치되어 있으며 접근할 수 있는 MySQL 서버가
있다는 것을 가정한다. 그렇지 않으면 MySQL 관리자에게  문의하라(여러분
이 관리자라면MySQL 문서 의 다른 부분을 살펴볼 필요가 있을 것이다).

본 튜토리얼에서는 데이터베이스를 설계하고  사용하는 모든 과정을  다룬
다. 이미 존재하는 데이터베이스를 사용하는 것에만 관심이 있다면 데이터
베이스와 그 안에 있을 테이블을  만드는 방법을 설명한 절은  건너띄어도
좋다.

튜토리얼 성격의 글이라 자세한 것은 설명되지 않는다. 여기에  언급된 것
에 대해 더자세히 알고 싶거든 MySQL의 관련 매뉴얼을 보면된다.

shell>은 쉘 프롬프트를, mysql>은 MySQL 프롬프트를 나타낸다.

1. 서버에 연결하기/연결끊기

서버에 접속하려면 mysql 명령을 내릴 때 MySQL 사용자 이름과  대개의 경
우 패스워드를 써 주어야 할 것이다. 서버가 여러분이 로긴한 컴퓨터가 아
닌 것에서 운영된다면 호스트 이름도 써 줄 필요가 있을 것이다(호스트 이
름, 사용자 이름, 패스워드).모든 것을  알았다면 다음처럼 연결할 수  있
다:

shell> mysql -h host -u user -p
Enter password: ********
******* 부분은 패스워드다. 'Enter password' 프롬프트가 보이면  패스워
드를 쳐주면 된다. 성공하면 간단한 소개 메시지들을 보고  'mysql>' 프롬
프트를 볼 수 있을 것이다.

shell> mysql -h host -u user -p
Enter password: ********

 Welcome to the MySQL monitor. Commands end with ; or \g.
 Your MySQL connection id is 459 to server version: 3.22.20a-log
Type 'help' for help.
mysql>

'mysql>' 프롬프트가 의미하는 바는 준비되었으니 명령어를 입력하라는 말
이다.
어떻게 설치하는 가에 따라 MySQL은 로컬 호스트(LOCAL  host)에서 운영되
는 서버에 "무명의 사용자(anonymous user)"로 접속할 수 있게 한다. 이럴
경우에는 단순히

shell> mysql

처럼 해서 연결할 수 있다.성공적으로 접속하였다면 'mysql>'  프롬프트에
서 언제든지  'QUIT'이라고 쳐서 서버에서 나올 수 있다:

mysql> QUIT
Bye

Ctrl키와 D 키를 동시에 눌러 빠져 나올 수도 있다.
이어지는 절에서 나오는 대부분의  예는 서버에 연결한 상태라는 것을  가
정한다. 'mysql>' 프롬프트는 서버에 연결된 상태라는 것을 나타낸다.

2. 질문 하기(Entering Queries)

이전 절에서 언급하였듯이 서버에  접속된 것을 확인하자. 이렇게  한다고
작업할 데이터베이스 어떤 것도 선택하는 것은 아니지만 어쨋든 접속은 해
야 한다. 지금 상황에서는  데이터베이스안에 테이블을 만들고,  테이블에
자료를 올리고, 테이블에서  자료를 빼 내는 것보다는 질문하는 법을 약간
이라도 배우는 게 더 중요하다. 이번 절에서는 명령어 입력의 기본 원칙을
몇가지 질문 예를 통해 알아 본다. 예를 통해 어떻게  mysql이 동작하는지
익숙해 질 것이다.

아래에 MySQL의 버전과 오늘 날짜를 출력하는 명령어를  보인다. 'mysql>'
프롬프트다음에 나오는 대로 쳐 넣자. 그리고 엔터키를 친다.

mysql> SELECT version(), current_date;
+-----------+-------------+
|     version()     |     current_date      |
+-----------+-------------+
|   3.22.20a-log   |    1999-03-19      |
+-----------+-------------+
1 row in set (0.01 sec)
mysql>

이 예로부터 mysql에 대한 몇가지 것들을 알 수 있다:
● 명령은 SQL 문과 그 뒤에오는 세미콜론(;)으로 이루어 진다(세미콜론이
필요없는 예외가 있긴하다. QUIT이 그 중 하나다. 나중에 이것에  대해 다
시 언급하겠다).
● 여러분이 명령을 내리면, mysql은  서버로 그 명령을 보내어  실행되게
하고, 그 결과를보여 주고 다시 명령 대기 상태 프롬프트('mysqld>')를 낸
다.
● mysql은 테이블 형식(행과 열로 이루어진)으로 결과를 보여준다. 첫 행
은 각 열에 대한라벨을 갖고 있다.  두 번째 행 부터는 질문의 답이  놓인
다. 보통, 열의 라벨은 데이터  베이스 테이블에서 가져오는 열의  이름이
다. 방금 보인 것처럼 테이블 열이 아닌 표현식(expression)을  사용할 때
는 라벨명은 그 표현식이 된다.
● mysql은 행의 수와 명령 실행 시간(대략적인 서버 성능 측정 도구다)을
보여 준다. 명령 실행  시간은 정확한 값은  아니다. 왜냐하면 이  시간은
wall clock time(CPU 시간이 아니다)이라는  것과 서버 부하 및  네트워크
부하에 의한 지연시간에 영향을 받기 때문이다(앞으로 나올 예에서는 지금
설명한 부분은 나타내지 않겠다).
키워드('예약어'라고 합니다. 미리 예약된 것이라 마음대로 사용할  수 없
는 이름입니다)는 대문자로 하던, 소문자로 하던 상관없다. 아래  세 개의
명령은 다 동일하다:
mysql> SELECT VERSION(), CURRENT_DATE;
mysql> SELECT version(), current_date;
mysql> seLect vErSiOn(), current_DATE;

다른 예를 하나 더 살펴 보자.

 mysql을 간단한 계산기로 사용한 예이다:

mysql> SELECT SIN(PI()/4), (4+1)*5;
+------------------------+
| SIN(PI()/4) | (4+1)*5) |
+-------------+----------+
|  0.707107   |    25    |
+-------------+----------+

지금까지의 예에서 명령어는 비교적 짧았고, 한 줄 짜리였다. 한줄에 여러 
명령을기술할 수 있다. 각 명령을 세미콜론으로 끝내기만 하면 된다:

mysql> SELECT version(); SELECT now();
+---------------+
| version()     |
+---------------+
| 3.22.20a -log |
+---------------+

+---------------------+
| now()               |
+---------------------+
| 1999-03-19 00:15:33 |
+---------------------+

명령어는 한줄에 모두 다 기술해야만 하는 건 아니다. 긴 명령인  경우 몇
줄에 걸쳐기술할 수 있다. mysql은 세미콜론을 보고  어디서 명령이  끝나
는 지를 분간한다(mysql은 임의의 포맷을 갖는 입력을 받아  들인다: 입력
줄을 모아 세미콜론을 볼 때까지 실행한다).

여러 줄을 걸쳐 명령을 준 예를 보자:
mysql> SELECT     -> user()     -> ,     -> current_date;
+--------------------+--------------+
| user()             | current_date |
+--------------------+--------------+
| joesmith@localhost | 1999-03-18   |
+--------------------+--------------+

여러줄 입력할 때 첫줄을 입력하고 엔터키를 쳤을 때 프롬프트가 'mysql>'
에서 '->'로 바뀐 것을 주목하라. 이것은 아직 명령이 다 완성되지는 않았
으며, 따라서
더 입력을 기다린다라고 mysql이  여러분에게 알리는 것이다.  프롬프트는
여러분의
친절한 안내자다. 귀한 정보를 여러분에게 알려 준다.  프롬프트가 알려주
는 것들을  통해 mysql이 무엇을 기다리고  있는지 항상 알 수 있을 것이
다.명령어 입력 도중 취소하려면 \c를 쳐주면 된다:

mysql> SELECT     -> user()     -> \cmysql>

프롬프트 변화를 잘 보라. \c를  친후 'mysql>'로 바뀌었다. 새  명령어를
받아들일 준비가 되었다는 것을 알리는 것이다.

다음 표는 마주치게 될 프롬프트들과 그 의미를 설명한 것이다.
+------------+------------------------------------------------------+
|  프롬프트  |  의미                                                | 
+------------+------------------------------------------------------+
|  mysql>    |  새 명령을 받아 들일 준비가 되었음.                  | 
+------------+------------------------------------------------------+
|  ->        |  명령어를 여러 줄에 기술할 때 다음 줄을 기다리고     | 
|            |  있음을 의미.                                        | 
+------------+------------------------------------------------------+
|  '>        |  다음줄 입력을 나타낸다. 현재 '로 시작하는 문자열을  | 
|            |  수집하는 중이라는 것을 나타냄. (문자열 입력을       | 
|            |  끝내려면 문자열을 다 입력 한 후 '를 붙여 줄 것)     | 
+------------+------------------------------------------------------+
|  ">        |  '>와 같다. 단지 차이는 문자열을 '가 아니라 " 로     | 
|            |  두른다는 점이다.                                    | 
+------------+------------------------------------------------------+

세미콜론을 붙이는 것을 잊어버려 우연히  혹은 실수로 여러 줄에  걸치는
명령을 입력할 때가 종종 있다. 이  경우 물론 mysql은 입력을 더  기다린
다:

mysql> SELECT user()
    ->

이럴 때는 mysql은 세미콜론을 기다리고 있는 것이다(여러분은  명령을 제
대로 완전히 다 입력했다고 생각하지만 mysql은 그렇지  않다. 세미콜론이 
빠졌기 때문이다). 프롬프트가 바뀐 것을 눈치 채지 못한다면 결과를 기다
리며 한참 동안의 시간을 낭비할  수도 있다. 세미콜론을 쳐 주어  명령을
완성하면 실행결과를 볼 수 있을 것이다:
mysql> SELECT user()
    -> ;
+--------------------+
| user()             |
+--------------------+
| joesmith@localhost |
+--------------------+
'>와 ">는 문자열을 모으는 중에 나타나는 프롬프트이다.  MySQL에서는 문
자들을  ' 나 " 로 둘러싸면 문자열이 된다(예를 들면 'hello', "goodbye"
등이다). 또한 여러 줄에 걸쳐 문자열을 입력할 수도 있다.'> 나  "> 프롬
프트가 나타나면 이것은 '나 "로 시작하는 문자열을 포함하는 명령어를 쳐
넣었으나 닫는 ' 나 " 를  아직 쳐 넣지 않았다는 것을 의미하는  것이다.
여러 줄에 걸쳐 문자열을 입력할 때는 상관없다. 하지만 문자열을 여러 줄
에 입력하고 자 하는 경우가 얼마나 될까? 그다지 많지 않다. 대부분의 경
우, '> 나 "> 프롬프트는 닫는 ' 나  " 를 빼먹었다고 알려주는 의미일 것
이다. 예를 들면 다음과 같다:

mysql> SELECT  * FROM  my_table WHERE  name =  "Smith And  age <  30;      
">
위와 같은 SELECT 문을 입력하고 엔터키를 치고 결과를 기다린다해도 아무
결과도 볼 수 없을 것이다. "왜 아무 반응도 없을  것일까?"라고 이상하게
생각하지 말고 "> 프롬프트가 나태내는 의미를 생각해 보자.  문자열을 닫
는 인용 부호를 빼먹었다는 것을  알리고 있다. 사실 위의 문장은  잘못이
있다. "Smith 다음에 "를 빼먹은 것이다.

자, 어떻게 해야 할까?
가장 간단한 방법은 명령을 취소하는 것이다. 그러나 간단히  \c를 칠수는
없다. 왜냐하면 \c도 "를 입력하기  전까지는 문자열의 일부로 취급  받을
것이기 때문이다. 대신 "\c를 입력하면 된다:
mysql> SELECT  * FROM  my_table WHERE  name =  "Smith AND  age <  30;      
"> "\cmysql>
프롬프트가 mysql>로 되돌려 졌다. 물론 이것은 "새 명령어 실행  준비 완
료"의 뜻이다.
'>와 ">가 의미하는 바를 기억하는 것은 중요하다. 잘못하여 닫는 인용 부
호을 빼먹었을 때 계속 입력하는 것들은 모두 무시되는 듯하게  보이기 때
문이다(여기에는QUIT도 포함된다). 현재 명령을 취소하기 전에 닫는  인용
부호를 꼭 써야한다는 것을모르면 이것은 매우 혼동스러운 일일 것이다.

3. 데이터 베이스 만들고 사용하기

명령어 입력 방법을 알았으니 데이터 베이스를 만들고 사용해 볼  때가 되
었다.

집에서 애완동물을 키운다고 가정해 보자.
애완동물 각각에 대해서 여러 가지 정보를 두고 유지하고 싶을 것이다. 데
이터 베이스를 만들고 그 안에 테이블을 만들어서 여기에 원하는 데이터를
넣어두면 된다. 그렇게 하면 테이블에서 자료를 가져와서 애완동물에   대
한 여러 가지 정보들을 알아 낼 수 있다. 이 절에서는  이러한 것들을포함
하여 다음과  같은 사항들을 다루어 본다:

● 데이터 베이스 만들기
● 테이블 만들기
● 테이블에 자료 넣기
● 테이블에서 자료 빼 내기
● 여러개의 테이블 사용하기

데이터  베이스  이름을   menagerie('동물원'이라는 뜻이다)라고   짓자.
menagerie 데이터 베이스는 매우 간단하나 실제 생활에서도 간단한 데이터
베이스를 사용하는경우가 있다. 예를 들면 지금 만들고자 하는  데이터 베
이스를 가축을 사육하는 농부나 애완동물의 치료 기록을  남겨두어야 하는
수의사에 의해 사용되어 질 수 있다.

SHOW 문을 사용하여 현재 서버가 유지  중인 데이터 베이스 목록을 볼  수
있다:

mysql> SHOW DATABASES;
+----------+
| Database |
+----------+
| mysql    |
| test     |
| tmp      |
+----------+

실제 목록은 위와 다를 수 있다. 하지만 mysql, test 데이터베이스는 항상
볼 수 있을 것이다. mysql 데이터베이스는 사용자 접근 권한  정보를 갖고
있는 중요한 데이터베이스이다. test는 말 그대로 연습하기 위해  있는 데
이터베이스다. test 데이터베이스가 있다면  다음처럼 해서 사용할 수  있
다:

mysql> USE test
Database changed

QUIT처럼 USE 문은 세미콜론이 필요하지 않다는 것을 기억하자(세미콜론으
로 끝내도 상관없다. 그냥 간단하게 모든 문을 세미콜론으로  끝낸다고 기
억해 두는 것도 좋다). USE 문은 또한 한 줄에 기술해야 한다는 것도 반드
시 기억하자. test 데이터베이스에 접근할 수 있으면 이것을 사용할 수 있
다. 하지만 동일한 데이터베이스(이 경우엔 test)에 접근할 수  있는 사람
이 여러 사람이라면 여러분이 만들어 놓은 어떤 자료라도 다른  사람에 의
해 접근이 가능하다. 이것은 삭제 및 변경될 소지가 있다는  말이다. 그래
서 MySQL 관리자에게 여러분만의 데이터베이스를 사용할 권한을 달라고 요
청해야 한다. 여기서는 menagerie라고 하자. 관리자는 다음과 같은 명령문
을 실행할 필요가 있다:

mysql> GRANT ALL ON menagerie.* TO your_mysql_name;

your_mysql_name은 물론 허락해 줄 MySQL 계정명으로 대치해야 한다.

3.1 데이터베이스 만들고 선택하기

관리자가 접근 권한을 설정할 때 데이터베이스를 만들어  주었다면 그것을
그냥 사용하면 된다. 그렇지 않으면 다음 처럼 하여 손수 여러분이 만들어
주면 된다:

mysql> CREATE DATABASE menagerie;

유닉스에서는 데이터베이스 이름은 대소문자를 구별한다(SQL 키워드는  그
렇지 않다).  따라서  데이터베이스 이름을   항상 'menagerie'로  해야지
Menagerie, MENAGERIE, meNaGerIE같은 것은 안된다. 테이블 이름도 마찬가
지로 대소문자를 구분한다. 데이터베이스를 만든다고 사용하겠다고 알리는
것은 아니다. 명시적으로 사용하겠다고해야 한다:

mysql> USE menagerie
Database changed

데이터베이스는 한번만 만들면 되지만  사용할 때마다 use 문을  이용하여
사용할 데이터 베이스를 선택해야 한다. 당연한 논리가 아닐까? 다른 방법
으로는 mysql을 시작할 때 데이터베이스 이름을 써 주어도 된다:

shell> mysql -h host -u user -p menagerie
Enter password: ********

여기서 menagerie가 패스워드는 아니다. 혼동하지 마라. 패스워드를  쓰려
면 공백없이 바로 -p 뒤에 붙여 써 주어야 한다(하지만 이  방법은 보안상
바람직한 방법이 절대 아니다. 패스워드가 글자그대로 화면에 보이기 때문
이다. 누가 어깨 너머로 보고있다면 어떻게 할 것인가? 패스워드가 글자그
대로 화면에 써진다는 것은 정말 위험하다. 현명한 관리자  및 사용자라면
-p 뒤에 패스워드를 적어 주는 "짓"은 하지 않을 것이다.  MySQL 개발자들
은 왜 이렇게 했을까?). 패스워드가 아니라 사용할 데이터베이스이름이다.

3.2 테이블 만들기
데이터베이스를 만드는 것은 쉽다. 만들고 난 직후에는 다음 처럼

mysql> SHOW TABLES;
Empty set (0.00 sec)

데이터베이스는 비어 있다. 당연하다. 이제 막 만들었는데  들어있는 것이
있을 리 없다. SHOW TABLES;  문은 선택된 데이터베이스에 있는  테이블을
보이는 명령이다. 정말로 중요하고 어렵게 느껴지는 것은  데이터베이스를
어떻게 설계할 것인가이다. 어떤 테이블이 필요하고 이 안에  무슨 자료들
을 넣어야 할지를 생각해야 한다.

여기서의 예에서는 각 애완 동물마다 한 개의 레코드를 두어야 할 것이다.
pet 테이블이라고 이름 짓자. 각 테이블에는 애완 동물의 이름, 소유주(식
구 이름이 될것이다), 종, 성(암컷인지 수컷인지) 등등의 정보를 입력하고
싶을 것이다. 나이는? 나이도 필요할 것 같지만 시간에 따라  변하는 것이
나이이므로 나이에 대한 정보를 자주 갱신해 주어야 할 것이다. 보다 나은
방법을 강구해야 한다. 이런 상황을 미리 염두에 두고 설계해야 한다는 것
이 데이터베이스 설계시 겪는  어려움이 아닐까 생각한다. 나이는  시간에
따라 변하므로 출생일을 기록해두고  현재 날짜와의 차이로부터  계산하면
좋을 것이다. MySQL은 몇가지 산술 루틴을 제공하므로 이것은
어려운 일이 아니다. 나이대신 출생일을 기록해 두는 것은 다음 두가지 이
점이 있다:
● 다가오는 애완 동물의 생일을 미리 알려주는 일에 사용할 수 있다(동물
에게 생일이라... 비현실적인 것 같지만, 이것은 다른 관점에서 생각해 볼
수 있다.
여러분의 고객의 생일은 어떤가? 언제 생일 축하카드를 보내야 하는 지 알
필요가 있지 않은가?).

● 오늘 날짜 말고 다른 날짜를 기준으로도 나이를 계산할 수  있다. 예를
들어 사망일을 기록해 놓으면 애완 동물의 수명을 알 수 있을  것이다. 애
완 동물에 대한 정보로서 다른 것들도 생각할 수 있겠지만 이정도로 해 두
자. 충분하다.
CREATE TABLE 문으로 테이블에 둘 자료 구조를 명시할 수 있다:

mysql> CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20),
    -> species VARCHAR(20), sex CHAR(2), birth DATE, death DATE);

CREATE TABLE 다음에 테이블 이름을 써 주고 괄호 안에 열의 이름과 그 열
의 자료형을 한짝으로 하여 쉼표로 구분하여 열거해 주면 된다.
name,  owner,  species,  sex,birth등이  열의  이름이며,  VARCHAR(20),
CHAR(2), DATE가 자료형이다. 자료형이란 말 그대로 자료의  형태이다. 자
료는 문자열일 수 있고, 날짜 일  수 있고, 수 일수 있다.다음 표와  같은
테이블이 만들어 진다:

pet TABLE
   열    1열   2열   3열    4열   5열   6열
열이름  name owner species sex  birth death

어떻게 자료를 입력하느냐에 따라 다르겠지만 진돗개 "용감이"의  경우 다
음처럼 될 수 있다.
 name    owner   species  sex  birth     death
----------------------------------------------
용감이   홍길동    개     수컷 1998-3-4   NULL

VARCHAR는 길이가 변하는 문자열에 사용한다. 이름, 소유주,  종은 길이가
고정적이지 않은 문자열을 그 자료형으로  할 때 적당할 것이다.  VARCHAR
형의 열들에 대해, 길이는 반드시  모두 같은 필요도 없고 20으로  고정될
필요도 없다. 1에서 255사이의 길이를 가질 수 있다. 적당하게  잡아 주면
된다(나중에 ALTER TABLE 문으로 조정할 수도 있다).  테이블을  만들었으
니 데이터베이스내 테이블 목록에 추가되었는가 확인하자:

mysql> SHOW TABLES;
+--------------------+
|Tables in menagerie |
+--------------------+
| pet                |
+--------------------+

테이블이 명시한대로 만들었는지 확인하기 위해서는 DESCRIBE 문을 사용한
다:
mysql> DESCRIBE pet;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name    | varchar(20) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
| owner   | varchar(20) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
| species | varchar(20) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
| sex     | char(2)     | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
| birth   |  date       | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
| death   |  date       | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+

Filed 부분과 Type 부분을 보고 열의 이름과 자료형을 확인하자. DESCRIBE
는 언제든지 사용할 수 있다. 테이블 네의 열의 이름 및  자료형을 잊었을
때 사용하면 유용하다.

3.3 테이블에 자료를 넣어 보자.
테이블을 만든 후에는  테이블에 자료를  넣어야  한다. LOAD  DATA 혹은
insert 문을 사용하면 된다.

애완 동물 자료가 다음과 같다고 가정하자(MySQL은 YYYY-MM-DD  형식의 날
짜 포맷을요구한다).

name    owner   species sex     birth           death    
-----------------------------------------
Fluffy  Harold  cat     f       1993-02-04
Claws   Gwen    cat     m       1994-03-17
Buffy   Harold  dog     f       1989-05-13
Fang    Benny   dog     m       1990-08-27
Bowser  Dianne  dog     m       1998-08-31      1995-07-29
Chirpy  Gwen    bird    f       1998-09-11
Whistler        Gwen    bird            1997-12-09
Slim    Benny   snake   m       1996-04-29

여러분은 빈 테이블에서 시작하므로 미리  파일에 각 동물에 대한  자료를
적어 두고 파일에서 읽어서 테이블을 채우면 좋을 것이다.
pet.txt라는 파일(파일이름은 아무것이든 상관없다)에 한 줄에 하나의  레
코드를 기록하면 된다. 다음 처럼:

# cat pet.txt
Fluffy  Harold  cat     f       1993-02-04
-이후 생략-

열의 값들은 탭키 하나로 구분하며 CREATE TABLE 문에 명시한 순서대로 각
열의 값들을 열거해야 한다. 생략해도 되는 값(위에서 죽은  날짜와 성)에
대해서는 NULL값을 사용할 수  있다. 텍스트 파일에서 NULL값을  나타내기
위해서는 \N 이라고 써주면 된다. 예를 들어 Whistler의 예는 다음과 같을
것이다.

Whistler        Gwen    bird \N 1997-12-09 \N

pet.txt 파일을 로드하기 위해서는 다음처럼 LOAD DATA  문을 사용한다:

mysql> LOAD DATA LOCAL INFILE "pet.txt" INTO TABLE pet;

사용형식은 다음과 같다:
LOAD DATA LOCAL INFILE "파일이름" INTO TABLE 테이블이름;
한번에 한 개의 레코드를 추가하고자 할 때가 있을 것이다.  CREATE TABLE
문을 사용하였을때 열거한 순서대로 각  열의 값을 적어 주면 된다.  물론
맞는 자료형으로 말이다:

mysql> insert into pet
    ->  values('Puffball',  'Diane',  'hamster',  'f',  '1999-03-30',
NULL);

문자열 및 날짜를 작은 따옴표 ' 로 인용하였다. 위에서  언급한 NULL값도
입력할 수 있다(\N이라고 적으면 안된다).

3.4 테이블로부터 정보를 검색해 보자.
SELECT 문을 사용하면 된다. 일반 형식은 다음과 같다:

SELECT <검색대상> FROM <테이블> WHERE <검색조건>

<검색대상>은 무엇을 보고 싶은가를 알리는 것이다. 여러 열을  쉼표로 구
분하여 적을 수 있으며 모든 열을 의미하는 *를 쓸 수도 있다. WHERE 부분
은 생략할  수 있다. WHERE 문을 쓸 때는 검색조건을 써 준다. 검색조건을
만족하지 않는 행은 검색대상에서 제외된다.


3.4.1 모든 데이터를 검색하자.
가장 간단한 SELECT 문의 형태로 다음 처럼 사용할 수 있다:

mysql> SELECT * FROM pet;
+----------+--------+---------+------+------------+------------+
| name     | owner  |species  | sex  | birth      | death      |
+----------+--------+---------+------+------------+------------+
| Fluffy   | Harold | cat     | f    | 1993-02-04 | NULL       |
| Claws    | Gwen   | cat     | m    | 1994-03-17 | NULL       |
| Buffy    | Harold | dog     | f    | 1989-05-13 | NULL       |
| Fang     | Benny  | dog     | m    | 1990-08-27 | NULL       |
| Bowser   | Diane  | dog     | m    | 1998-08-31 | 1995-07-29 |
| Chirpy   | Gwen   | bird    | f    | 1998-09-11 | NULL       |
| Whistler | Gwen   | bird    | NULL | 1997-12-09 | NULL       |
| Slim     | Benny  | snake   | m    | 1996-04-29 | NULL       |
| Puffball | Diane  | hamster | f    | 1999-03-30 | NULL       |
+----------+--------+---------+------+------------+------------+

이런 식으로 SELECT문을 사용하는 것은  테이블의 전체 정보를 보고자  할
때 유용하다. 방금 막 초기  데이터 뭉치를 올렸을 때 제대로  올려졌는지
확인코자 사용할 수 있다. 사람 사는 일이 그렇듯, 방금 본 결과에는 잘못
된 것이 있다:
Bower의 출생일자가 사망일자보다 늦다. 죽은 뒤에 태어났다?!
확인해 보니 birth는 1989-08-31이 되어야 함을 알수 있었다고  하면 이를
어떻게 고칠까?
두가지 방법을 사용할 수 있다:

● 파일 pet.txt를 편집하여 수정한다. 테이블을 비운후 pet.txt에서 다시
읽어 들인다:
mysql> DELETE FROM pet;
mysql> LOAD DATA LOCAL infile "pet.txt" into TABLE pet;

하지만 이렇게 하면  3.3절에서 개별적으로  insert문을 이용하여  입력한
Puffball에대해서 다시 입력해야 한다.

더 간단하고 바람직한 방법은?

● 잘못된 곳만 수정한다. UPDATE 문을 사용한다:

mysql> UPDATE pet SET birth="1989-08-31" WHERE name="Bowser";

위에서 볼 수 있듯이, 전체 테이블 내용을 보는 것은 쉽다. 그러나   보통
이렇게 하지는 않는다. 테이블 크기가 커지면 어떻게 할 것인가?  어떤 자
료들을 검색할 때 그 많은 것을 일일이 다 볼것인가? 대신  특별한 조건을
만족하는 자료들만 뽑아서 보길 원할 것이다.

3.4.2 주어진 조건에 맞는 특정 행만을 검색해 보자.
여러분은 테이블에서 특별한 행들만 뽑아낼  수 있다. 예를 들어  Bower의
생일이 정말로 바뀌었는 가 확인하기 위해 Bower의 레코드만 뽑아낼 수 있
다:

mysql> SELECT * FROM pet WHERE name = "Bower";
+--------+-------+---------+-----+------------+------------+
| name   | owner | species | sex | birth      | death      |
+--------+-------+---------+-----+------------+------------+
| Bowser | Diane | dog     | m   | 1989-08-31 | 1995-07-29 |
+--------+-------+---------+-----+------------+------------+

birth 열의 값이 1998년도가  아닌 1989년으로 올바르게 수정됨을  확인할
수 있다.
문자열 비교는 대소문자를 무시하는  비교다. 따라서 "bowser",  "BOWSER"
등은 같은 문자열을 의미한다(위에서는 "Bowser"를 사용했다).어떤 열에대
해서도 조건을 명시해 줄수가  있다. 예를 들어 1998년 이후에 태어난  동
물을 알고 싶다면 birth 열을 대상으로 검사하면 된다:

mysql> SELECT * FROM pet WHERE birth >= "1998-1-1"
+----------+-------+---------+-----+------------+------+
| name     | owner | species | sex | birth      |death |
+----------+-------+---------+-----+------------+------+
| Chirp y  | Gwen  | bird    | f   | 1998-09-11 | NULL |
| Puffball | Diane | hamster | f   | 1999-03-30 | NULL |
+----------+-------+---------+-----+------------+------+
조건을 조합할 수 도 있다:
mysql> SELECT * FROM pet WHERE species = "dog" AND sex = "f";
+----------+--------+---------+-----+------------+-------+
| name     | owner  | species | sex | birth      | death |
+----------+--------+---------+-----+------------+-------+
| Buffy    | Harold | dog     | f   | 1989-05-13 | NULL  |
+----------+--------+---------+-----+------------+-------+
위의 예는 개이면서 숫컷인 동물을 검색하는 것이다. 위에서는  AND를  사
용하였지만 OR를 사용할 수도 있다:

mysql> SELECT * FROM pet WHERE species = "snake" OR species = "bird";
+----------+-------+---------+------+------------+-------+
| name     | owner | species | sex  | birth      | death |
+----------+-------+---------+------+------------+-------+
| Chirpy   | Gwen  | bird    | f    | 1998-09-11 | NULL  |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL  |
| Slim     | Benny | snake   | m    | 1996-04-29 | NULL  |
+----------+-------+---------+------+------------+-------+

AND와 OR를 섞어서 사용할 수 있다. 이렇게 할 때는  그룹지어지는 조건들
을 괄호로 묶는 것이 좋다:

mysql> SELECT * FROM pet WHERE (species = "cat" AND sex = "m")
    -> OR (species = "dog" AND sex = "f");

+-------+--------+--------+-----+------------+-------+
| name  | owner  |species | sex | birth      | death |
+-------+--------+--------+-----+------------+-------+
| Claws | Gwen   | cat    | m   | 1994-03-17 | NULL  |
| Buffy | Harold | dog    | f   | 1989-05-13 | NULL  |
+-------+--------+--------+-----+------------+-------+

3.4.3 특정한 열 선택하기
테이블에서 한행 전체를 보기 보다는 "관심 거리" 열들만 보고  싶다면 보
고자 하는  열 이름을 다음처럼 사용하면 된다(아래 예는 name, birth열을
보고 싶은 경우이다):
mysql> SELECT name, birth FROM pet;
+----------+------------+
| name     | birth      |
+----------+------------+
| Fluffy   | 1993-02-04 |
| Claws    | 1994-03-17 |
| Buffy    | 1989-05-13 |
| Fang     | 1990-08-27 |
| Bowser   | 1989-08-31 |
| Chirpy   | 1998-09-11 |
| Whistler | 1997-12-09 |
| Slim     | 1996-04-29 |
| Puffball | 1999-03-30 |
+----------+------------+

소유주만 보고자 할 때:
mysql> SELECT owner FROM pet;
+--------+
| owner  |
+--------+
| Harold |
| Gwen   |
| Harold |
| Benny  |
| Diane  |
| Gwen   |
| Gwen   |
| Benny  |
| Diane  |
+--------+
하지만 중복된 행이 있다.

중복된 행을 없애 보자:

mysql> SELECT distinct owner FROM pet;
+--------+
| owner  |
+--------+
| Benny  |
| Diane  |
| Gwen   |
| Harold |
+--------+

distinct 키워드를 사용하면 된다.

WHERE 절을 이용하여 행의 선택과 열의 선택을 조합할 수 있다. 예를 들어
개와 고양이에 대해서만 이름, 종, 생일을 알고자 할 때 다음 처럼 SELECT
문을 사용할 수 있다:

mysql> SELECT name, species, birth FROM pet
    -> WHERE species = "dog" OR species = "cat";

+--------+---------+------------+
| name   | species | birth      |
+--------+---------+------------+
| Fluffy | cat     | 1993-02-04 |
| Claws  | cat     | 1994-03-17 |
| Buffy  | dog     | 1989-05-13 |
| Fang   | dog     | 1990-08-27 |
|Bowser  | dog     | 1989-08-31 |
+--------+---------+------------+

3.4.4 행 정열하기
지금까지 보인 예에서 결과가 전혀 정렬되어 있지 않았음을 알 수 있을 것
이다.
결과가 정렬된다면 원하는 자료를 훨씬  쉽게 알아 볼 수 있다.  정렬하기
위해서는 ORDER BY 절을 사용하면  된다. 아래는 생일을 기준으로  정렬한
것이다.

mysql> SELECT name, birth FROM pet ORDER BY birth; 
+----------+------------+
| name     | birth      |
+----------+------------+
| Buffy    | 1989-05-13 |
| Bowser   | 1989-08-31 |
| Fang     | 1990-08-27 |
| Fluffy   | 1993-02-04 |
| Claws    | 1994-03-17 |
| Slim     | 1996-04-29 |
| Whistler | 1997-12-09 |
| Chirpy   | 1998-09-11 |
| Puffball | 1999-03-30 |
+----------+------------+


역순으로 정렬하려먼 desc 키워드를 열 이름 뒤에 적어준다:

mysql> SELECT name, birth FROM pet ORDER BY birth desc;

+----------+------------+
| name     | birth      |
+----------+------------+
| Puffball | 1999-03-30 |
| Chirpy   | 1998-09-11 |
| Whistler | 1997-12-09 |
| Slim     | 1996-04-29 |
| Claws    | 1994-03-17 |
| Fluffy   | 1993-02-04 |
| Fang     | 1990-08-27 |
| Bowser   | 1989-08-31 |
| Buffy    | 1989-05-13 |
+----------+------------+

하나의 열에 대해서만이 아니라 여러 열에 대해서도 정렬할 수 있다. 예를
들어 먼저 동물 이름으로 정렬하고 각 동물에 대해 생일로  정렬하되 가장
어린 것(가장 늦게 태어난 것;  역순 정렬)이 먼저 오게 하려면  다음처럼
한다:

mysql> SELECT name, species, birth  FROM pet ORDER BY species,  birth
DESC;

+----------+---------+------------+
| name     | species | birth      |
+----------+---------+------------+
| Chirpy   | bird    | 1998-09-11 |
| Whistler | bird    | 1997-12-09 |
| Claws    | cat     | 1994-03-17 |
| Fluffy   | cat     | 1993-02-04 |
| Fang     | dog     | 1990-08-27 |
| Bowser   | dog     | 1989-08-31 |
| Buffy    | dog     | 1989-05-13 |
| Puffball | hamster | 1999-03-30 |
| Slim     | snake   | 1996-04-29 |
+----------+---------+------------+
DESC 키워드는 바로 그 앞에 열 이름(birth)에만 적용된다는  것을 주의하
라. species는 여전히 오름차순으로 정렬된다.

3.4.5 날짜 계산MySQL은 날짜를 다루는 몇가지 함수를 제공해 준다.
애완 동물의 나이가 얼마나 되는 지 계산하려면 오늘 날짜와  출생시 날짜
를 구하고, 두 날짜를 일수로 환산한  후, 그 차를 연간 일수 즉  365일로
나누어 주면 될 것이다:

mysql> SELECT name, (to_days(now())-to_days(birth))/365 FROM pet;
+----------+-------------------------------------+
| name     | (TO_DAYS(NOW())-TO_DAYS(birth))/365 |
+----------+-------------------------------------+
| Fluffy   |                                6.15 |
| Claws    |                                5.04 |
| Buffy    |                                9.88 |
| Fang     |                                8.59 |
| Bowser   |                                9.58 |
| Chirpy   |                                0.55 |
| Whistler |                                1.30 |
| Slim     |                                2.92 |
| Puffball |                                0.00 |
+----------+-------------------------------------+

여기서 두가지 사항을 개선해 보자. 결과가 이름 혹은 나이 순으로   정렬
되었으면
좋겠고, 나이에 해당하는 라벨명을 표현식 그대로 쓰는 것 보다는 "age"같
은 것으로하는 것이 좋을 것이다:

mysql> SELECT name, (to_days(now())-to_days(birth))/365 as age
    -> FROM pet ORDER BY name;

나이순으로 정렬하려면 ORDER BY name 대신 ORDER BY age로 써 주면 된다.
사망시 나이도 비슷한 방법으로 알아 낼 수 있다:

mysql> SELECT name, (to_days(death)-to_days(birth))/365 as age
    -> FROM pet WHERE death is not null ORDER BY age;

now()대신 death를 사용하면 된다. 여기서 아직 죽지 않은  동물의 수명을
조사한다는 것은 무의미하기 때문에 death 필드가 null이 아닌  경우를 조
건으로 해 주었음을 주의하자. 조심할 것은 death is not null처럼 조건을
주어야 한다. death != null 처럼 주어서는 안된다. null값에 비교 연산자
를 적용할 수 없다. 나중에 이 문제는 다시 다룰 것이다.

다음 달에 생일인 동물을 알려면  어떻게 해야 할까? 이러한 문제를  위해
MySQL은 날짜에서 연도나, 달을 계산하는 함수를 제공한다:
year(), month, day().month()예를 통해 알아 보자:

mysql> SELECT name, birth, month(birth) FROM pet;
+----------+------------+--------------+
| name     | birth      | MONTH(birth) |
+----------+------------+--------------+
| Fluffy   | 1993-02-04 |            2 |
| Claws    | 1994-03-17 |            3 |
| Buffy    | 1989-05-13 |            5 |
| Fang     | 1990-08-27 |            8 |
| Bowser   | 1989-08-31 |            8 |
| Chirpy   | 1998-09-11 |            9 |
| Whistler | 1997-12-09 |           12 |
| Slim     | 1996-04-29 |            4 |
| Puffball | 1999-03-30 |            3 |
+----------+------------+--------------+

month는 달에 해당하는 수를  반환해 주며, 물론 그  범위는 1에서 12까지
이다.
다음달을 나태내기 위해서는 1을 더한 달을 명시해주면 된다:

mysql> SELECT name, birth FROM pet WHERE month(birth) = 10;
그런데 문제가 있다. 12월인 경우  13을  명시해 주어야 하나?  13월이란
없다.
현재 달이 몇월이 던지 상관없도록 새로운 조건식을 생각해 내야 한다. 여
기에 두 가지를 소개한다:

● month(date_add(now(), interval 1 month));
now()는 현재 날짜 및 시간을  반환해 준다. 여기에 1달이라는 기간을  더
해주고 달로 바꾸면 해결된다.

● mod(month(now()), 12) + 1;mod는 어떤 수를 다른 수로 나눈 나머지 값
을 반환하는 함수이다. 첫 번째 인자를 두번째 인자로 나눈 결과를 반환한
다. 여기서는 현재  달 month(now())를 12로 나눈 뒤 다음 달을  나타내기
위해 1을 더해 준다. 이번   달이 12월이라면 12로 나눈나머지가 0이므로
여기에 1을 더해 다음달 1월을 나태내 줄 수 있다.완전한 SQL문은 각각 다
음과 같다:

mysql> SELECT name, birth FROM pet
    -> WHERE MONTH(birth) = MONTH(DATE_ADD(NOW(), INTERVAL 1 MONTH));
mysql> SELECT name, birth FROM pet
    -> WHERE MONTH(birth) = MOD(MONTH(NOW()),12) + 1;

3.4.6 null 값에 대해 NULL값은 특별한 값이다.
익숙해 질 때 까지 혼동될 것이다. 개념적으로 NULL이 의미하는  바는 "빠
진, 빼먹은 값", "아직 정해지지 않은 불확정 값"을 의미한다.

이것은 다른 값들과는 다르게 취급된다. NULL에는 산술 비교  연산을 수행
할 수 없다. 어떤 값과 NULL값을 =, <, !=을 이용하여 비교하는 것은 의미
가 없다. 불확정 값을 어떻게 확정된 값과 비교할 수 있을 것인가? 다음을
보라:

mysql> SELECT 1 = NULL, 1 != NULL, 1 < NULL, 1 > NULL;
+----------+-----------+----------+----------+
| 1 = NULL | 1 != NULL | 1 < NULL | 1 > NULL |
+----------+-----------+----------+----------+
| NULL     | NULL      | NULL     | NULL     |
+----------+-----------+----------+----------+

위에서 보듯 불확정 값과 확정값과의  비교는 불확정값이 된다. 의미가 없
다. 다음과같이 하면 의미가 있다:

mysql> SELECT 1 IS NULL, 1 IS NOT NULL;
+-----------+---------------+
| 1 IS NULL | 1 IS NOT NULL |
+-----------+---------------+
| 0         | 1             |
+-----------+---------------+

MySQL에서 거짓 값은 0으로 참값은 1로 나타낸다.


3.4.7 패턴 일치
패턴 일치 기능은 매우 유용한 기능이다. 보다 빠르고 정교하게 원하는 조
건을 명세하여 검색할 수 있게 해주는 기능이기 때문이다.

MySQL은 표준 SQL 패턴 뿐만아니라 유닉스에서 사용하는 정규 표현식에 해
당하는 패턴 일치 기능도 지원한다.

SQL에서 _ 은 임의 한 문자를 의미하며, %는 임의의 수의 문자(0개의 문자
를 포함)를 가르킨다. SQL 패턴은 대소문자를 비교하지 않는다. LIKE 이후
에 패턴을 준다는 것을기억 하자. 아래 예를 보라:

b로 시작하는 이름에 대해서 검색할 때:

mysql> SELECT * FROM pet WHERE name LIKE "b%";
+--------+--------+---------+-----+------------+-----------+
| name   | owner  | species | sex | birth      | death      |
+--------+--------+---------+-----+------------+-----------+
| Buffy  | Harold | dog     | f   | 1989-05-13 | NULL       |
| Bowser | Diane  | dog     | m   | 1989-08-31 | 1995-07-29 |
+--------+--------+---------+-----+------------+-----------+

fy로 끝나는 이름에 대해서 검색할 때:

mysql> SELECT * FROM pet WHERE name LIKE "%fy";
+--------+--------+---------+-----+------------+-------+
| name   | owner  | species | sex | birth      | death |
+--------+--------+---------+-----+------------+-------+
| Fluffy | Harold | cat     | f   | 1993-02-04 | NULL  |
| Buffy  | Harold | dog     | f   | 1989-05-13 | NULL  |
+--------+--------+---------+-----+------------+-------+

w를 포함하는 이름을 검색할 때:

mysql> SELECT * FROM pet WHERE name LIKE "%w%";
+----------+-------+---------+------+------------+------------+
| name     | owner | species | sex  | birth      | death      |
+----------+-------+---------+------+------------+------------+
| Claws    | Gwen  | cat     | m    | 1994-03-17 | NULL       |
| Bowser   | Diane | dog     | m    | 1989-08-31 | 1995-07-29 |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL       |
+----------+-------+---------+------+------------+------------+

정확하게 5개의 글자로 이루어진 이름에 대해서 검색할 때는?
mysql> SELECT * FROM pet WHERE name LIKE "_____";
+-------+--------+---------+-----+------------+-------+
| name  | owner  | species | sex | birth      | death    |
+-------+--------+---------+-----+------------+-------+
| Claws | Gwen   | cat     | m   | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f   | 1989-05-13 | NULL  |
+-------+--------+---------+-----+------------+-------+

밑줄을 5개 적어 준다.
이젠 정규표현에 기반한 패턴일치에 대해 알아 보자.

정규표현식에 사용되는 문자              설명
        .                       문자 하나
        *                       앞에 나온 문자의 0개 이상의 반복
        ^                       문자열 처음
        $                       문자열 끝
        [,]                     괄호안의 문자들에 일치
        {,}                     반복을 나타낼 때. 예로 n번 반복할 때
                                {n}으로 적는다.

여기서 SQL 패턴은 전체  값과 일치해야 "일치한다"고 하지만  정규표현은
값의 어느 부분과 일치해도 "일치한다"고 단정한다는 것을 유의해야 한다.
예를 들어, SELECT * FROM pet WHERE name REGEXP "ffy";와 SELECT * FROM
pet WHERE name LIKE "ffy";는 전혀 다른 결과를 낳는다.
문자 a나 b나 c중 하나를  가르키는 표현은 [abc]이다. 범위를  주어서 표
현할 수도 있다. 정규표현은 대소문자를 구별한다. 따라서  대문자던 소문
자던 상관없이 알파벳  문자 하나를 가르키는 표현은 [a-zA-Z]로 해야  한
다. *는 0개 이상의 문자들이라고 했다. x* 는 x, xx, xxx ...  에 해당한
다. [0-9]*는 7, 12,  345, 678등의 임의의  길이를 갖는 수를  나타낸다.
^abc는 줄 처음에 abc로 시작하는  패턴을 abc$는 abc로 끝나는  문자열을
의미한다. 정규 표현식을 쓸 때는 LIKE대신 REGEXP을  사용한다.예를 보며
익혀 보자.


이름이 소문자 b 혹은 대문자 B로 시작하는 조건으로 검색:

mysql> SELECT * FROM pet WHERE name REGEXP "^[bB]";
+--------+--------+---------+-----+------------+------------+
| name   | owner  | species | sex | birth      | death      |
+--------+--------+---------+-----+------------+------------+
| Buffy  | Harold | dog     | f   | 1989-05-13 | NULL          |
| Bowser | Diane  | dog     | m   | 1989-08-31 | 1995-07-29 |
+--------+--------+---------+-----+------------+------------+

이름이 fy로 끝날 때($를 사용한다):
mysql> SELECT * FROM pet WHERE name REGEXP "fy$";
+--------+--------+---------+-----+------------+-------+
| name   | owner  | species | sex | birth      | death |
+--------+--------+---------+-----+------------+-------+
| Fluffy | Harold | cat     | f   | 1993-02-04 | NULL     |
| Buffy  | Harold | dog     | f   | 1989-05-13 | NULL     |
+--------+--------+---------+-----+------------+-------+

정확하게 5개의 문자로 이름어진 값은 다음 정규 표현에 일치한다:

        ^.....$

이것은 반복 연산자를 이용하여 다음처럼 쓸 수도 있다.

        ^.{5}$

3.4.8 행수 세기
누가 어떤 애완 동물을 몇이나 소유했는지 어떻게 알아낼 수 있을까?
이에 대한 답으로 count()함수를  사용하면 되며 적당하게 조건을  부여해
주면 된다.

mysql> SELECT COUNT(*) FROM pet;
+----------+
| COUNT(*) |
+----------+
|        9 |
+----------+
각 소유주가 소유한 애완동물의 수는 다음 처럼 하면 확인 할 수 있다:
mysql> SELECT owner, COUNT(*) FROM pet GROUP BY owner;
+--------+----------+
| owner  | COUNT(*) |
+--------+----------+
| Benny  |       2  |
| Diane  |       2  |
| Gwen   |       3  |
| Harold |       2  |
+--------+----------+
각 owner의 모든 레코드들을 한데 묶기  위해 GROUP BY 절을 사용한  것을 
주목하라. 이렇게 하지 않으면 에러 메시지를 보게 될 것이다.

mysql> SELECT owner, COUNT(owner) FROM pet;
ERROR    1140    at    line    1:    Mixing    of    GROUP    columns
(MIN(),MAX(),COUNT()...)
with no GROUP columns is illegal if there is no GROUP BY clause

COUNT()와 GROUP BY는 데이터에 여러 모양으로 특성을  부여하는 데  쓸모
가 있다. 다음 예제들도 참고하자:

각 종에 해당하는 동물의 수:
mysql> SELECT species, COUNT(*) FROM pet GROUP BY species;
+---------+----------+
| species | COUNT(*) |
+---------+----------+
| bird    |        2 |
| cat     |        2 |
| dog     |        3 |
| hamster |        1 |
| snake   |        1 |
+---------+----------+
성에 따른 동물의 수:
mysql> SELECT sex, COUNT(*) FROM pet GROUP BY sex;
+------+----------+
| sex  | COUNT(*) |
+------+----------+
| NULL |        1 |
| f    |        4 |
| m    |        4 |
+------+----------+

NULL은 "값을 모름"의 의미이다. 종과 성에 따른 동물의 수:
mysql> SELECT species, sex, COUNT(*) FROM pet GROUP BY species, sex;
+---------+------+----------+
| species | sex  |COUNT(*)  |
+---------+------+----------+
| bird    | NULL |       1  |
| bird    | f    |       1  |
| cat     | f    |       1  |
| cat     | m    |       1  |
| dog     | f    |       1  |
| dog     | m    |       2  |
| hamster | f    |       1  |
| snake   | m    |       1  |
+---------+------+----------+
바로 위의 경우와는 달리, 특정한 동물에   대해서만 조사해 볼 수 도 있
다. 개와 고양이의 경우에만 각 성에 대해 몇마리인지 조사해 보자:
mysql> SELECT species, sex, COUNT(*) FROM pet
    -> WHERE species = "dog" OR  species = "cat"        -> GROUP BY
species, sex;
+---------+------+----------+
| species | sex  | COUNT(*) |
+---------+------+----------+
| cat     | f    |        1 |
| cat     | m    |        1 |
| dog     | f    |        1 |
| dog     | m    |        2 |
+---------+------+----------+

3.5 테이블 여러개 사용하기
pet 테이블은 애완동물에 대한 정보를  갖고 있다. 수의사에 치료  받으러
갔던 횟수나 새끼를 나은 날짜 같은 사건들에 대한 다른  정보를 기록하고
싶다면 별도의 테이블이필요할 것이다. 테이블은 다음과 같은 조건을 요구
할 것이다:

● 해당 동물의 이름을 갖고 있어야  한다. 어떤 애완 동물에게 일어난 사
건인지 분별해야 하기 때문이다.
● 언제 일어난 일인지 알기 위해 날짜 정보가 필요하다.● 어떤 사건인지
묘사해 둘 필요가 있다.
● 사건을 분류하려면 사건 유형을 나타내는 필드도 있으면  좋을 것이다.
이와 같은 조건을 생각하여, 다음 처럼 테이블을 만들어 보자:

mysql> CREATE TABLE event (name VARCHAR(20), date DATE,
     -> type VARCHAR(15), remark VARCHAR(255));

pet 테이블의 경우 처럼 파일로부터 데이터를 테이블로 올리자. event.txt
에 다음처럼 적혀 있다고 하자.
Fluffy  1995-05-15      litter  4 kittens, 3 female, 1 male 
Buffy   1993-06-23      litter  5 puppies, 2 female, 3 male 
Buffy   1994-06-19      litter  3 puppies, 3 female 
Chirpy  1999-03-21      vet     needed beak straightened
Slim    1997-08-03      vet     broken rib 
Bowser  1991-10-12      kennelFang      1991-10-12      kennel
Fang    1998-08-28      birthday        Gave him a new chew toy 
Claws   1998-03-17      birthday        Gave him a new flea collar 
Whistler        1998-12-09      birthday        First birthday

다음 처럼 테이블을 채우자:

mysql> LOAD DATA LOCAL INFILE "event.txt" INTO TABLE event;

지금까지 pet 테이블을 다루면서 배웠듯이 event 테이블에 대해 여러 가지
질의를 해 볼 수 있을 것이다. 하지만 정보가 불충분할 때는  어떻게 하는
가? 새끼를 낳았을 때 어미의 나이를  알려면 어떻게 해야 하는가?  event
테이블을 통해 언제 새끼를 낳았는지는 알 수 있지만  어미의 나이라든가,
소유주라든가 하는 것은  pet 테이블을  통해서 알아  내야 한다.  따라서
SELECT 문을 사용할 때 두 개의 테이블이 필요하다:

mysql> SELECT pet.name, (TO_DAYS(date) - TO_DAYS(birth))/365 AS age,
    -> remark FROM pet, event
    -> WHERE pet.name = event.name AND type = "litter";

+--------+------+-----------------------------+
| name   | age  | remark                      |
+--------+------+-----------------------------+
| Fluffy | 2.27 | 4 kittens, 3 female, 1 male |
| Buffy  | 4.12 | 5 puppies, 2 female, 3 male |
| Buffy  | 5.10 | 3 puppies, 3 female         |
+--------+------+-----------------------------+

위의 예로부터 몇가지 알아 두어야 할 사항이 있다:
● FROM 절에 사용할 테이블을 모두 적어 주어야 한다. 이것들 모두로부터
의 정보가 필요하기 때문이다.
● 여러 테이블에서 정보를 뽑아 합할 때는 한 테이블의 레코드가 다른 테
이블의 레코드와 어떻게 일치하는 지 명시해 주어야 한다. 여기서는 두 테
이블 모두  name 필드를  갖고 있으므로  이것을 이용하면   된다. 위에서
WHERE 절에pet.name =  event.name 조건을 줌으로써 두 개의 테이블의  같
은 동물에 해당하는 레코드에 대해서 질의를 하게 된다. 서로 다르다면 의
미가 없다.
● 두 테이블 모두 name 필드를 갖고  있으므로 어느 테이블에 속하는  필
드인지를 구분하기 위해  <테이블이름>.<필드이름>의 형식으로 적어  주었
다. 즉 테이블 이름과필드 이름을 점으로 구분하여 적어 준다.  위에서 테
이블은 서로 달랐다. 하지만  동일한 테이블에 대해서 위에서처럼  사용할
필요가 있을 때가 있다. 예를 들어 개의 수컷과 암컷을 짝지어  주려면 어
떻게 해야 하는가? 동일한 테이블에 대해서 성이 같은지  다른지 검사해야
한다. 다음 한 예를 든다:

mysql> SELECT p1.name, p1.sex, p2.name, p2.sex, p1.species
    -> FROM pet AS p1, pet AS p2
    -> WHERE p1.species =  p2.species AND p1.sex  = "f" AND p2.sex  =
"m";
+--------+------+--------+------+---------+
| name   | sex  | name   | sex  | species |
+--------+------+--------+------+---------+
| Fluffy | f    | Claws  | m    | cat     |
| Buffy  | f    | Fang   | m    | dog     |
| Buffy  | f    | Bowser | m    | dog     |
+--------+------+--------+------+---------+

3.6 배치 모드(일괄 처리 모드)로 사용하기
지금까지는 대화식으로 사용하였다. 질의를 쳐  넣고 결과를 보는 식의 반
복적인 작업이었다. 작업 내용 전부를 파일에 기술해 준 후 한꺼번에 처리
할 수도 있다. 이렇게 하는  작업을 배치 작업이라고 한다는 것쯤은  알아
두자. 다음과 같은 식으로 사용한다:

shell> mysql < batch-file

작업 내용을 써 둔 파일 내용을 표준 입력으로 받으면 된다.  호스트명 및
사용자 명,패스워드를 입력할 필요가 있으면 추가로 써 준다:

shell> mysql -h host -u user -p < batch-file
Enter password: ********

여러분이 배치 모드에서 사용할 파일을 작성하는 것은 바로 스크립트를 작
성하는 것이다.   기본 결과는   대화식으로 할  때와는  다르다.  SELECT
DISTINCT species FROM  pet을 시켰을 때 대화식과 배치 모드에서의  결과
를 보자. 내용은 같지만 형식이 다르다.대화식:
+---------+
| species |
+---------+
| bird    |
| cat     |
| dog     |
| hamster |
| snake   |
+---------+

배치 모드:speciesbirdcatdoghamstersnake
배치 모드에서도 대화 모드에서와  같은 형식으로의 출력을 원하면  mysql
실행시 -t 옵션을 주면 된다. 만약 실행되는 명령어도 출력에 포함하고 싶
다면 -vvv를 붙여라.

그렇다면 무슨 유익이 있길래 배치 모드를 사용할까? 다음에  몇가지 적어
두었다.
● 질의를 자주 한다면 스크립트로 만들어 두는 것이 실행할  때마다 매번
다시 쳐 넣어주는 수고를 없애 준다.
● 이미 작성한 스크립트를 수정하여  개선할 수 있고 새로운  스크립트를
작성할 수 있는 잇점이 있다.
● 여러 줄에 걸치는 매우 복잡한 질의를 수행할 때는 배치 모드가 적당할
것이다.
실수를 했을 때 대화모드라면 전부 다시 쳐 넣어 주어야 한다.  배치 모드
일때는 파일만  수정해 주면 된다.  하지만 MySQL은 readline 라이브러리
(히스토리기능을 구현한 라이브러리)기능을 사용하므로 대화 모드일  때도
다시 명령어를 쳐 넣는 수고를 크게 덜 수 있다.
● 출력 결과가 굉장히 많다면 배치모드로 실행시키고 페이저(일정한 페이
지 줄수로 문서를 보여주는 프로그램을 통칭하는 명칭)를 통해  보면 좋을
것이다. 다음 처럼:

shell> mysqlk < batch-file | less

● 출력 결과를 다른 파일로 저장할 수 있다. 저장된 파일은  추가 작업의
출발점으로 활용될 수 있다.

shell> mysql < batch-file > mysql.out

● 작성한 스크립트를 다른 사람과 공유할 수 있다. 다른 사람도 여러분이
작성한 스크립트를 실행할 수 있으며 참고할 수 있다.
● 어떤 작업은 성격상 배치모드에서만 실행할 수 있다. 일정한 시간 간격
으로 어떤 작업을 할 때는 cron을 이용하여 배치모드에서 처리할  수 밖에
없다.


9. mysql 서버 기능(functions)

9.1 mysql에서 지원하는 언어
 mysqld의 에러 메시지는  다음의 언어로 나타날  수 있습니다. :  Czech,
Dutch, English(기본값), 기타 등등 (** 기타 언어는 매뉴얼  참고. 한글,
일본어 등 동양권은 여기서 빠져있습니다. 9.1.1 절을 참고하세요. **)

 mysqld를 특정한 언어로 시작하려면 --language=lang 또는 -L  lang 옵션
을 사용하면 됩니다.

예> # mysqld--language=swedish

또는

# mysqld --language=/usr/local/share/swedish

모든 언어는 소문자로 표기합니다.
 언어 파일은  (기본값으로) 'mysql_base_dir/share/LANGUAGE/'에  있습니
다.
 에러 메시지  파일을  업데이트하려면 'errmsg.txt'   파일을  편집하고
'errmsg.sys' 파일을 만들기 위해 다음의 명령을 사용합니다.

# comp_err errmsg.txt errmsg.sys

만약 mysql의 새로운 버전으로 업그레이드하면 새로운 'errmsg.txt'  파일
로 위와 같은 과정을 거쳐야 합니다.

9.1.1 데이터와 정열에 사용하는 문자 셋
 기본적으로 mysql은 ISO-8859-1 (Latin 1) 문자 셋을 사용합니다.  이 문
자셋은 미국과 서유럽에서 사용하는 문자 셋입니다.

 문자셋은 이름에 어떤 문자셋을 사용할 수 있는지 그리고  SELECT 문에서
ORDER BY 와 GROUP BY 문을 사용할 때 어떻게 정렬되는지를 결정합니다.

 컴파일할때 configure에서 --with-charset=charset 옵션을 사용하여 문자
셋을 바꿀 수 있습니다. 자세한 것은 4.7.1 을 참고하세요.
(** 한글을 사용하기 위해서 이  부분은 중요합니다. 우리는  컴파일할때
문자셋을 sjis로 바꾸어 주면 됩니다. sjis는 일본어 2바이트 문자   셋입
니다. 이렇게 해야 정렬이 제대로 되고 정규표현식을 사용할 수 있습니다.
**) 

9.1.2 새로운 문자셋 추가
** 생략 **

9.1.3 멀티바이트 문자 지원
멀티 바이스 문자셋을 만들면 _MB 매크로를 사용할 수 있습니다.
** 생략 **

9.2 업데이트 로그

 mysqld를 시작할 때 --log-update=file_name 옵션을  사용하면 mysqld는
데이타를 업데이트한 모든 sql 문을 포함하는 로그 파일을 기록합니다. 파
일은 data 디렉토리(** 보통 mysql 설치 디렉토리 밑의 data 디렉토리 **)
에 기록되며  file_name.# 형태로   됩니다. #은 mysqladmin  refresh  나
mysqladmin flush -logs, FLUSH LOGS 문, 또는 서버를  재시작할때마다 증
가됩니다.

--log 나   -l 옵션을   사용하면 파일  이름은  'hostname.log'가  되며,
restart나 refreshes를 해도 새로운 로그 파일이 만들어지지 않습니다. 기
본적으로 mysql.server 스크립트는 -l 옵션으로 mysql 서버를 시작합니다.
production enviroment(?)를 사용하여 시작할때 더 나은 성능이  필요하면
mysql.server에서 -l 옵션을 제거할 수 있습니다.

 업데이트 로그는 지능적이서 실제로 데이타가 업데이트될 때만 로그 기록
을 남깁니다. 그래서 WHERE를 사용한 UPDATE나 DELETE에서  해당하는 레코
드를 찾지 못하면 로그 파일에 기록하지 않습니다. 또한 이미 존재하는 값
을 사용할때도 Update 문은 무시됩니다.
 업데이트 로그 파일에서  데이터베이스를  업데이트하려면  다음과 같이  
하면 됩니다. (로그 파일이 'file_name.#'의 형태라고 가정)

# ls -1 -t -r file_name.[0-9]* | xargs cat | mysql

 ls는 정확한 순서로 모든 로그 파일을 가져올 때 사용합니다.

 이것은 데이터베이스에 손상이 생긴 뒤 백업 파일로 복구할 때 유용하며,
백업과 손상이 생긴 시간 사이에 일어난 업데이트를 다시 할때(redo) 사용
할 수 있습니다.

 또한 다른 호스트에 미러링된 데이터베이스를  가지고 있으며 마스터  데
이터베이스에 생긴 변화를 복사할때 업데이트 로그를 사용할 수 있습니다.

9.3 mysql 테이블 최대 크기
 mysql 자체는 테이블 최대 크기가  4G이며 운영 시스템은 각자의  고유한
파일 크기  제한이 있습니다.   리눅스에서는 현재 2G  입니다.  솔라리스
2.5.1에서는 4G이며, 솔라리스 2.6에서는 1000G가 될 것입니다. 현재 테이
블 크기 제한은 4G이거나, (MYSQL 제한) 운영 시스템 제한입니다. 4G 이상
으로 확대하기 위해 앞으로 mysql을 바꿀 것입니다. 부록 F를 참고하세요.

 거대 테이블을 읽기 전용으로 하면 많은 테이블을 하나로  모으고 압축하
는 pack_isam 을 사용할  수 있습니다. pack_isam은 일반적으로  테이블을
최소 50%  압축하여 효과적으로 더 큰 테이블을 사용할 수 있습니다. 12.3
[pack_isam]을 참고하세요.

 다른 솔루션은 MERGE 라이브러리에 포함되어 있으며 identical  테이블을
모아 하나로  관리할 수  있습니다. (여기서  Identical은 모든  테이블이
identical 컬럼 정보로 만들어진다는 것을 의미합니다.) 현재  MERGE는 인
덱스를 지원하지 않기  때문에 테이블의  모음(collection)만을 검색하는
데만 사용할 수 있습니다. 가까운  시일내에 여기에 인덱스를 추가할 것입
니다.

(** 참고로 읽기 전용 테이블은 mysql에 라이센스를 지불하고 구입했을 때
만 만들 수 있습니다. 단지 읽기만 하는 것은 가능하지만 압축이  된 읽기
전용 테이블은 만들 수 없습니다. **)

10. mysql의 최대 성능 향상 방법

10.1 버퍼 크기 조정
mysqld 서버가 사용하는 기본 버퍼 크기는 다음의 명령으로 알 수 있다.

shell> mysqld --help

이 명령은 모든 mysqld 옵션의 목록과 설정 변수를 보여준다. 출력되는 내
용은 기본값을 포함하고 있으며 다음과 비슷하다.

Possible variables for option --set-variable (-O) are:
back_log              current value: 5
connect_timeout       current value: 5
join_buffer           current value: 131072
key_buffer            current value: 1048540
long_query_time       current value: 10
max_allowed_packet    current value: 1048576
max_connections       current value: 90
max_connect_errors    current value: 10
max_join_size         current value: 4294967295
max_sort_length       current value: 1024
net_buffer_length     current value: 16384
record_buffer         current value: 131072
sort_buffer           current value: 2097116
table_cache           current value: 64
tmp_table_size        current value: 1048576
thread_stack          current value: 131072
wait_timeout          current value: 28800
mysqld 서버가 현재 가동중이면 다음의 명령을 통해 실제 변수값을  볼 수
있다.

shell> mysqladmin variables

각 옵션은 밑에서 설명한다. 버퍼  크기, 길이, 스택 크기는  바이트이다.
'K'(킬로바이트) 나 'M'(메가바이트)를 앞에  붙여 값을 지정할 수  있다.
예를 들면 16M는 16 메가바이트를 가리킨다. 대소문자는 구별하지 않는다.
16M 와 16m은 같다.

-back_log

mysql이 가질 수 있는 최대 연결 요청의 수. 이것은 main  mysql 스레드가
매우 짧은 시간동안 매우 많은 연결 요청을 받을 때 기능을 한다. 이때 메
인 스레드가 연결을 체크하고 새로운 스레드를 시작하는데는  약간의 시간
이 걸린다.(그러나 아주 짧은 시간임) back_log 값은  mysql이 순간적으로
새로운 요청에 답하는 것을 멈추기전에 이 짧은 시간동안 얼마나  많은 요
청을 쌓아두고 있는지를 지정한다. 매우 짧은 시간동안 매우  많은 연결이
예상될때만 이 값을 증가시켜야 한다.

다른 말로 이 값은 tcp/ip 연결을 받는 listen queue의 크기이다. 각 운영
체제마다 이러한 큐의 크기에 한계가  있다. Unix system call  listen(2)
매뉴얼페이지에 자세한 정보가 있다. back_log값의 한계는 운영체제  문서
를 확인해봐라. back_log를 최대값보다 더 높여도 효과가 없다.

-connect_timeout
Bad handshake에 반응하기 전에 연결 패킷을 mysql 서버에서  기다리는 시
간.(초)

-join_buffer
(인덱스를 사용하지 않는 조인의)  full-join에서 사용하는 버퍼의  크기.
버퍼는 두 테이블 사이에서 각 full-join마다 한번 할당이 된다. 인덱싱을
추가하지 못할 때 조인 버퍼를  증가시키면 full join의 속도를  향상시킬
수 있다. (일반적으로 빠르게 조인을 하는 가장 좋은 방법은인덱스를 추가
하는 것이다)

-key_buffer
인덱스 블락은 버퍼링되고 모든  스레드에서 공유한다. 키 버퍼는  인덱스
블락에서 사용하는 버퍼의 크기이다. 인덱스가 많은 테이블에서  delete나
insert 작업을 많이 하면 키 버퍼값을 증가시키는 것이 좋다. 더  빠른 속
도를 내려면 LOCK TABLES를 사용하자. [Lock Tables] 참고.

-max_allowed_packet
한 패킷의 최대 크기. 메시지 버퍼는 net_buffer_length  바이트로 초기화
되지만 필요하면 최대 허용 패킷 바이트를 증가시킬 수  있다.기본값은 큰
패킷을 잡기에는 작다. 거대 BLOB 컬럼을 사용한다면 값을  증가시켜야 한
다. 사용자가 원하는 최대 blob만큼 크게 해야 한다.
 
-max_connections
동시 클라이언트 숫자.  mysqld가 필요로하는 파일  지시자(descriptor)의
숫자만큼 값을 늘려야 한다. 밑에서 파일 디스크립터 제한에  대한 내용을
참고하자.

-max_connect_errors
호스트에서 최대 연결 에러이상의 interrupted 연결이 있으면 더  많은 연
결을 위해 호스트는 block화된다. FLUSH HOSTS 명령으로 호스트의 block을
해제할 수 있다.

-max_join_size
최대 조인 크기이상으로 레크도를 읽는 조인을 하면 에러가 난다. 만약 사
용자가 where 문을 사용하지 않고 시간이 많이 걸리면서  몇백만개의 레코
드를 읽는 조인을 수행하려 하면 이 값을 설정한다.

-max_sort_length
BLOB나 TEXT 값으로 정열할때 사용하는 바이트의 숫자. (각 값중  오직 첫
번째 max_sort_length 바이트만 사용된다. 나머지는 무시된다)

-net_buffer_length
질의에서 통신 버퍼가 초기화되는  크기. 일반적으로 바뀌지 않지만  매우
적은 메모리를 가지고 있을 때 예상되는 질의에 맞게 세팅할 수 있다. (이
것은 클라이언트에 가는 예상된 sql  문의 길이이다. 질의문이 이  크기를
넘으면 버퍼는 자동으로 max_allowed_packet 바이트까지 증가한다)

-record_buffer
순차적인 검색을 하는 각 스레드에서 각 검색 테이블에 할당하는  버퍼 크
기. 순차적인 검색을 많이 하면 이 값을 증가시켜야 한다.

-sort_buffer
정렬이 필요한 각 스레드에서 할당하는 버퍼 크기. order by 나  group by
오퍼레이션을 빠르게 하려면 이 값을 증가시킨다. 16.4 [임시 파일] 참고.

-table_cache
모든 스레드에서 열 수 있는  테이블의 숫자. mysqld가 필요로 하는  파일
디스크립터의 숫자만큼 이 값을 증가시켜라. mysql은 각 유일한 오픈 테이
블에서 두개의 파일 디스크립터가 필요하다. 파일 디스크립터 제한을   참
고한다. 테이블 캐쉬가 어떻게 작동하는지는 10.6 [테이블 캐쉬]를 참고한
다.
 
-tmp_table_size
임시 테이블이 이 값을 넘으면 mysql은 "The Table tbl_name is full"이라
는 에러 메시지를 낸다. 매우 많은 group by 질의를 사용하면 이  값을 증
가시켜야 한다.

-thread_stack
각 스레드의 스택 사이즈. creash-me test(**역자주 :  데이터베이스의 벤
치마킹을 하는 테스트입니다. 말그대로 데이터베이스를 죽여주지요)  에서
잡히는 많은 제한은 이 값에 달려있다. 기본값은 일반적으로 충분히 크다.
11장의 [벤치마크] 참조

-wait_timeout
연결을 끊기전에 연결 활동(activity)을 서버에서 기다리는 시간(초).

table_cache 와 max_connections는 서버가 열  수 있는 최대 파일  갯수에
영향을 미친다. 이 값을 증가시키면 운영시스템에서 오픈 파일 디스크립터
의 per-process 숫자의 한계까지 올릴 수 있다. (** ... imposed  by your
operating system on the per-process number of open  file descriptors.
번역이 이상하므로 영문 참고)
그러나 많은 시스템에서 이 한계를 증가시킬수 있다. 이렇게 하려면 각 시
스템에서 이 한계를 변화시키는 방법이 매우 다양하므로  운영체제 문서를
참고해야 한다.

table_cache 는 max_connections 와 관계가 있다. 예를 들면 200개의 연결
이 있으면 최소 200 * n 의 테이블 캐쉬를 가져야 한다. 여기서  n은 조인
에서 테이블의 최대 숫자이다.

mysql은 매우 유용한 알고리즘을 사용하기 때문에 일반적으로는 매우 적은
메모리로 사용할 수 있으며 메모리가 많을 수록 성능이 더 많이 향상된다.

많은 메모리와 많은 테이블을 가졌고 중간정도 숫자의클라이언트에서 최대
의 성능을 원한다면 다음과 같이 사용한다.


shell> safe_mysqld -O key_buffer=16M -O table_cache=128 \
           -O sort_buffer=4M -O record_buffer=1M &

메모리가 적고 연결이 많으면 다음과 같이 사용한다.

shell> safe_mysqld -O key_buffer=512k -O sort_buffer=100k \
           -O record_buffer=100k &

또는:

shell> safe_mysqld -O key_buffer=512k -O sort_buffer=16k \
           -O table_cache=32 -O record_buffer=8k -O net_buffer=1K &

매우 많은 연결이 있을 때 mysqld가 각 연결마다 최소한의  메모리를 사용
하도록 설정하지 않았다면 "swapping problems" 문제가 생길 것이다.

mysqld에서 옵션을 바꾸었으면 그것은 서버의 해당하는 인스턴스에만 영향
을 미친다는 것을 기억하자.

옵션을 바꾸었을때의 효과를 보기 위해 다음과 같이 해보자.

shell> mysqld -O key_buffer=32m --help

마지막에 --help 옵션이 들어간 것을 기억하자. 그렇지 않으면  커맨드 라
인에서 사용한 옵션의 효력은 출력에는 반영되지 않을 것이다.

10.2 메모리 사용 방법 <메모리 최적화>

아래에서 설명하는 목록은 mysqld 서버가 메모리를 사용하는  방법에 대해
서 나타내고 있다. 메모리 사용과 관련된 서버의 변수 이름이 주어진다.

- 키 버퍼(변수 key_buffer)는 모든 스레드에서 공유한다.  서버에서 사용
하는 다른 버퍼는 필요한대로 할당이 된다.

- 각 연결은  각 스레드마다의 특정한  공간을 사용한다. 스택(64k,  변수
thread_stack) , 연결 버퍼(변수 net_buffer_length), result 버퍼  (변수
net_buffer_length)   등.   연결   버퍼와   result   버퍼는   필요할때
max_allowed_packet 까지 동적으로 증가된다. 질의가 수행될 때 현재의 질
의문의 복사문이 또한 할당이 된다. (** When a query is running  a copy
of the current query string is also alloced.)

- 모든 스레드는 같은 기본 메모리를 공유한다.
- 메모리 맵은 아직 지원이 안된다. (압축 테이블을 제외하고.  그러나 이
것은 다른 이야기이다) 왜냐하면 4GB의 32비트 메모리 공간은 대부분의 대
형 테이블에서 충분히 크기가 않기 때문이다. 우리가 64비트  주소 공간을
가진 시스템을 가지게 될 때 우리는 메모리 맵핑을 위한  일반적인 지원을
추가할 것이다.

- 테이블에서 순차적인 검색을 하는 각 요청은 read 버퍼에  할당이 된다.
(변수 record_buffer)

- 모든 조인은 한번에 수행이 되며 대부분의 조인은 임시 테이블을 생성하
지 않고 수행이 된다. 대부분의 테이블은 메모리  기반(HEAP) 테이블이다.
거대 길이의 레코드를 가졌거나 BLOB 컬럼을 포함한 임시 테이블은 디스크
에 저장이 된다. 현재의  문제는 메모리 기반 테이블이  tmp_table_size를
초과했을때 "The table tbl_name  is full"이라는 에러가 생기는  것이다.
가까운 시일안에 필요할때 자동적으로 메모리 기반(HEAP) 테이블을 디스크
기반(NISAM) 테이블로 바꾸도록 고칠 것이다. 이 문제를  해결하기 위해서
mysqld의 tmp_table_size 옵션을 설정하여  임시 테이블 크기를  늘이거나
클라이언트 프로그램에서 SQL_BIG_TABLES라는  sql 옵션을 설정하여야  한
다. 7.24 SET OPTION 을 참고하자.
mysql 3.20에서 임시 테이블의  최대 크기는 record_buffer*16이다.  3.20
버전을 사용하고  있다면 record_buffer의  값을 증가시켜야   한다. 또한
mysqld를 시작할 때 --big-tables 옵션을 사용하여 항상 임시 테이블을 디
스크에 저장할 수 있지만 질의 속도에 영향을 미친다.

- 정열을 하는 대부분의 요청은  정렬 버퍼와 하나나 두개의 임시  파일을
할당한다. 16.4의 [임시 파일]을 참고한다.
 
- 대부분의 파징(parsing)과 계산은 지역 메모리에서 이루어진다. 작은 아
이템에는 메모리 overhead가  필요없고 일반적인  느린 메모리  할당(slow
memory allocation)과 freeing(메모리  해제)는 무시된다. 메모리는  오직
예상지 못한 거대 문자열에서 할당이 된다.( mallloc() 과 free() 사용)

- 각 인덱스 파일은 한번에 열리며 각 병행수행되는 스레드에서 데이터 파
일은 한번에 열린다. 각 병행수행 스레드마다 테이블 구조, 각  컬럼의 컬
럼 구조, 3 * n  의 버퍼 크기가 할당된다.  ( n은 최대 레코드  길이이며
BLOB 컬럼은 해당하지 않는다) BLOB는 BLOB 데이터의 길이에 5에서 8 바이
트를 더한 값을 사용한다.

- BLOB 컬럼을 가진 각 테이블에서 버퍼는 거대 BLOB 값을 읽을 수 있도록
동적으로 커진다. 테이블을 검색하면  버퍼는 최대 BLOB의 값만큼  버퍼가
할당이 된다.

- 모든 사용중인 테이블의 테이블 핸들러는 캐쉬에 저장되며  FIFO로 관리
가 된다. 일반적으로 캐쉬는 64 엔트리를 갖는다. 동시에 두개의  실행 스
레드에서 테이블을 사용하면 캐쉬는 테이블의 두 엔트리를 포함한다. 10.6
[테이블 캐쉬]를 참고한다.
- mysqladmin flush-tables 명령은 사용하지 않는 모든 테이블을  닫고 현
재 실행되는 스레드가 끝날 때  모든 사용중인 테이블을 닫는다고  표시한
다. 이것은 효과적으로 사용중인 메모리를 해제한다.

ps 와 다른 시스템 상황 프로그램은 mysqld가 많은 메모리를  사용하고 있
다고 보고할 것이다. 이것은 다른 메모리 주소의  스레드-스택때문에 생긴
다. 예를 들면 솔라리스의 ps 는 스택사이의 사용하지 않는 메모리를 사용
하는 메모리로 간주한다. 이것은 swap -s를 이용 사용가능한  스왑을 체크
하여 확인할수 있다. 우리는 mysqld를 상용 메모리 유출  측정 프로그램으
로 테스팅해서 mysqld에는 메모리 유출이 없다.

10.3 속도 향상에 영향을 미치는 컴파일/링크 방법
                                        <컴파일시 최적화하기>

다음 테스트의 대부분은 리눅스와 mysql 벤치마크를 가지고  수행되었지만
다른 운영 시스템에도 암시해주는 것이 있다.

static으로 링크를 할때 가장 빠른 실행 속도를 얻을 수 있다. 데이터베이
스에 연결하기 위해 TCP/IP보다는 유닉스 소켓을 사용하면 더 좋은 성능을
낼 수 있다.

리눅스에서 pgcc와 -O6을 사용하면 가장 빠르다. 'sql_yacc.cc'를 이 옵션
으로 컴파일하려면 gcc/pgcc는 모든 성능을 내기 위해 많은 메모리가 필요
하기 때문에 180M의 메모리가 필요하다. 또한 mysql을 설정할때 libstdc++
라이브러리를 포함하지 않기 위해 CXX=gcc라고 설정해야 한다.

- pgcc를 사용하고 모두다 -O6 옵션으로 컴파일하면 mysqld  서버는 gcc로
컴파일한 것보다 11% 빨라진다.

- 동적으로 링크하면 (-static을 사용하지 않고) 13% 느려진다.
    If you connect using TCP/IP rather than Unix sockets,  the result
is 7.5% slower.
- 유닉스 소켓을 사용하는 것보다 tcp/ip로 연결하는 것이 7.5% 느려진다.

- On a Sun sparcstation 10, gcc 2.7.3 is 13% faster than Sun  Pro C++
4.2.
- On Solaris 2.5.1, MIT-pthreads is 8-12% slower than  Solaris native
threads. (** 번역을 안한 이후. 리눅스랑 상관없으니깐... **)
TcX에서 제공한 mysql 리눅스 배포판은 pgcc로 컴파일되었고  정적으로 링
크되었다.

10.4 How MySQL uses indexes

prefix- and   end-space compressed.  See section   7.26 CREATE  INDEX
syntax (Compatibility function).

모든 인덱스(PRIMARY, UNIQUE and INDEX()) 는 B-trees 에 저장된다. 문자
열은 자동적으로 앞 뒤의 공간(?)이 압축된다. 7.26 [인덱스 생성] 참고.

인덱스의 사용 :
- WHERE 문에서 해당하는 레코드 빨리  찾기
- 조인을 수행할때 다른 테이블에서 레코드 가져오기
- 특정 키에서 MAX() 나 MIN() 값 찾기
- 소팅이나 그룹화할때 인덱스 키를 사용하면 테이블을 정열하거나 그룹화
한다. 키에  DESC가 붙으면 역순으로 인덱스를 읽는다.
- 어떤 경우에는 데이터 파일에 묻지 않고 값을 가져온다.  어떤 테이블에
서 사용하는 모든 컬럼이 숫자이고 특정 키로 형성되어있으면 빠른 속도로
인덱스 트리에서 값을 가져올 수 있다.

다음 예제를 보자.

mysql> SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;

다중 컬럼 인덱스가 col1 과 col2에 있으면 해당하는 레코드를  직접 가져
올 수 있다.  분리된 단일 컬럼 인덱스가 col1 과 col2 에 있으면  최적화
기는 어떤 인덱스가 더 적은 레코드를 가졌는지 확인하고 레코드를 가져오
기 위해 그 인덱스를 사용하도록 결정한다.

테이블이 다중 컬럼 인덱스를 가졌다면 최적화기가 레코드를  찾는데 어떤
인덱스키를 사용할 수 있다. 예를  들면 세가지 컬럼 인덱스(col1,  col2,
col3)를 가졌다면 (col1), (col1,col2) (col1,col2,col3) 인덱스를 사용하
여 검색을 할 수 있다. 

MySQL can't use a partial index if the columns don't form  a leftmost
prefix of the index.
Suppose you have the SELECT statements shown below:
(** 해석이 잘 안되는데 예제를 보시면 무슨 말인지 알 수 있을 것임**)

mysql> SELECT * FROM tbl_name WHERE col1=val1;
mysql> SELECT * FROM tbl_name WHERE col2=val2;
mysql> SELECT * FROM tbl_name WHERE col2=val2 AND col3=val3;

If an index exists  on (col1,col2,col3), only  the first query  shown
above uses the index.
The second and third queries  do involve indexed columns, but  (col2)
and (col2,col3) are not leftmost prefixes of (col1,col2,col3).

인덱스가 (col1,col2,col3)로 있다면 위의 질의중 오직 첫번째  질의만 인
덱스를 사용한다. 두번째 및 세번째 질의은 인덱스된 컬럼이  포함되어 있
지만 (col2) 와 (col2,col3)는 (col1,col2,col3) 인덱스에 해당하지  않는
다.

MySQL also uses indexes for LIKE comparisons if the argument  to LIKE
is a constant string  that doesn't start  with a wildcard  character.
For example, the following SELECT stat ements use indexes:

mysql은 또한 LIKE의 인수가 와일드카드 문자로 시작하지 않는  상수 문자
열일이라면 LIKE 비교문에서 인덱스를 사용한다. 예를 들어 다음의 SELECT
문은 인덱스를 사용한다.

mysql> select * from tbl_name where key_col LIKE "Patrick%";
mysql> select * from tbl_name where key_col LIKE "Pat%_ck%";

첫번째 문장에서는 "Patrick" <= key_col  < "Patricl" 을 가진  레코드만
고려된다. 두번째 문장에서는 "Pat" <= key_col < "Pau" 을 가진 레코드만
고려된다.
다음의 SELECT 문은 인덱스를 사용하지 않는다:

mysql> select * from tbl_name where key_col LIKE "%Patrick%";
mysql> select * from tbl_name where key_col LIKE other_col;

첫번째 문장에서 LIKE 값은 와일드카드 문자로 시작하고 있다.  두번째 문
장에서는 LIKE 값이 상수가 아니다.

10.5 WHERE 문에서 최적화하기
(이번 절은 완전한 내용을 포함하고 있지는 않다. mysql은  많은 최적화방
법이 있다.)

In general, when you want to make a slow SELECT ... WHERE faster, the
first thing to  check is whether  or not  you can add  an index.  All
references between   different tables  should usually   be done  with
indexes. You can use the  EXPLAIN command to determine which  indexes
are  used  for  a  SELECT.  See  section  7.21  EXPLAIN  syntax  (Get
information about a SELECT).
일반적으로 느린 SELECT ... WHERE 문을 빠르게 하려면 가장  먼저 확인해
야 할 것이  인덱스 추가 문제이다.  다른 테이블사이에서 모든  레퍼런스
(references 참조)는 일반적으로 인덱스에 의해 수행된다. SELECT  문에서
어떤 인덱스를 사용하는지 결정하기 위해 EXPLAIN 명령을 사용할 수 있다.
7.21 [Explain]을 참고.

mysql에서 수행하는 최적화는 다음과 같다.

- 불필요한 삽입어 제거

       ((a AND b) AND c OR (((a AND b) AND (c AND d))))
    -> (a AND b ANDc) OR (a AND b AND c AND d)

-상수 폴딩(folding)

       (a<b AND b=c) AND a=5
    -> b>5 AND b=c AND a=5
- 상수 조건 제거(상수 폴딩때문에  필요)

       (B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6)
    -> B=5 OR B=6
- 인덱스에서 사용되는 상수 표현은 한번에 계산된다.
 (Constant expressions used by indexes are evaluated only once.)

- WHERE 절이 없는 단일 테이블의 COUNT(*)는 테이블 정보에서  직접 값을
가져온다.
단일 테이블에서 사용된 NOT NULL 표현도 이와 같이 수행된다. 
- 유효하지 않은 상수 표현은 미리 제거된다. mysql은  불가능하고 해당하
는 레코드가 없는 SELECT 문을 빠르게 감지한다.

- GROUP BY 나 그룹 펑션(COUNT(), MIN() ...)을 사용하지  않으면 HAVING
은 WHERE 에 합쳐진다.
(** HAVING 절에서는 인덱스를 사용하지 못함. 그러므로  가능한 HAVING절
을 사용하지 않는게 속도면에서 좋다 **)

- 각 서브 조인에서 빠르게 WHERE 문을 계산하고 가능한한  레코드를 제외
하도록 간소하게 WHERE 문이 만들어진다.

- mysql은 일반적으로 최소한의  레코드를 찾기 위해 인덱스를  사용한다.
=, >, >=, <, <=, BETWEEN 그리고  'something%' 처럼 앞이  와일드카드로
시작하지 않는 LIKE  문등을 사용하여 비교를  할 때 인덱스를  사용한다.
(** 10.4 절에서 설명하였듯이  like 를 사용할때 와일드카드로  시작하는
like 문을 사용하면 인덱스를 사용하지 않는다. 일정한 단어로만 시작하는
컬럼에서 자료를 찾을 때 유용할 것이다. **)

- Any index that doesn't span all  AND levels in the WHERE clause  is
not used to optimize the query.

다음의 WHERE 문은 인덱스를 사용한다.:

... WHERE index_part1=1 AND index_part2=2
... WHERE index=1 OR A=10 AND index=2      /* index = 1 OR index = 2
*/
... WHERE index_part1='hello' AND index_part_3=5
          /* optimized like "index_part1='hello'" */

다음의 WHERE 문은 인덱스를 사용하지 않는다.:
... WHERE index_part2=1  AND index_part3=2   /* index_part_1  is not
used */
... WHERE index=1 OR A=10                  /* No index */
... WHERE index_part1=1 OR index_part2=10  /* No index spans all rows
*/


- 질의에서 다른 테이블보다 모든 상수 테이블을 먼저 읽는다.  상수 테이
블은 다음과 같다.
        ㅇ빈 테이블이나 1개의 레코드만 있는 테이블
        ㅇWHERE 문에서 UNIQUE 인덱스나  PRIMARY KEY 를 사용하고  모든
        인덱스는 상수 표현으로된 테이블

        다음의 테이블은 상수 테이블로 사용된다.

    mysql> SELECT * FROM t WHERE primary_key=1;
    mysql> SELECT * FROM t1,t2
               WHERE t1.primary_key=1 AND t2.primary_key=t1.id;

- 모든 가능성을 시도하여 테이블을 조인하는데 가장 좋은 조인 조합을 찾
는다. (ORDER BY나 GROUP BY의 모든 컬럼이 동일한 테이블에서  나오면 조
인을 할때 이 테이블이 먼저 선택된다)

- ORDER BY 문과 다른 GROUP BY 문이 있을 때, 또는 ORDER BY 나 GROUP BY
가 조인 큐의 첫번째 테이블이 아닌 다른 테이블의 컬럼을  포함하고 있으
면 임사 테이블을 만든다.

- 각 테이블 인덱스를 찾고 레코드의 30%미만을 사용하는 (best) 인덱스가
사용된다. 그런 인덱스가 없으면 빠른 테이블 검색이 사용된다.

- 어떤 경우에는 mysql은 데이터 파일을 조회하지 않고 인덱스에서 레코드
를 읽을 수 있다. 인덱스에서 사용한 모든 컬럼이 숫자라면 질의를 처리하
는데 단지 인덱스 트리만을 사용한다.

- 각 레코드가 출력되기 전에 HAVING 절에 맞지 않는 레코드는 건너뛴다.

다음은 매우 빠른 질의의 예이다:
mysql> SELECT COUNT(*) FROM tbl_name;
mysql> SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;
mysql> SELECT MAX(key_part2) FROM tbl_name
           WHERE key_part_1=constant;
mysql> SELECT ... FROM tbl_name
           ORDER BY key_part1,key_part2,... LIMIT 10;
mysql> SELECT ... FROM tbl_name
           ORDER BY key_part1 DESC,key_part2 DESC,... LIMIT 10;
다음의 커리는 인덱스 트리만을 사용하여 값을 구한다.(인덱스  컬럼은 숫
자라고 가정):

mysql> SELECT key_part1,key_part2 FROM tbl_name WHERE key_part1=val;
mysql> SELECT COUNT(*) FROM tbl_name
           WHERE key_part1=val1 and key_part2=val2;
mysql> SELECT key_part2 FROM tbl_name GROUP BY key_part1;

다음의 질의는 개별적인 정열을 하지 않고 정열된 순서대로 열을 가져오는
데 인덱스를 사용한다:

mysql> SELECT ... FROM tbl_name ORDER BY key_part1,key_part2,...
mysql> SELECT  ... FROM  tbl_name ORDER  BY key_part1  DESC,key_part2
DESC,...

10.6 테이블 열고 닫는 방법

open 테이블의 캐쉬는 table_cache의  최대값까지 커질 수 있다.  (기본값
64 ; 이 값은 mysqld에서 -0 table_cache=# 으로 바꿀 수 있다) 캐쉬가 꽉
찼을때, 그리고 다른 스레드가 테이블을  열려고 할 때, 또는  mysqladmin
refresh 나 mysqladmin  flush-tables를 사용할때를 제외하고는  테이블은
결코 닫히지 않는다.

테이블 캐쉬가 꽉 차면 서버는  캐쉬 엔트리를 사용하도록 조절하기  위해
다음의 절차를 사용한다.

- 가장 먼저 사용했던 순서대로 현재 사용하지 않는 테이블을 닫는다.
- 캐쉬가 꽉 찼고 어떤  테이블도 닫히지 않지만 새로운 테이블을  열어야
한다면 캐쉬가 필요한 만큼 임시적으로 확장된다.
- 캐쉬가 임시적으로 확장된 상태이고 테이블을 사용할 수  없는 상황으로
가면 테이블을 닫고 캐쉬를 해제한다.

테이블은 각 동시병행적인 접근때마다 열린다. 동일한 테이블에  접근하는
두개의 스레드가 있거나 같은 질의에서 테이블에 두번  접근하면(with AS)
테이블을 두번 열여야 한다는  의미이다. 테이블의 첫번째 개방은  두개의
파일 디스크립터를 가진다. ; 추가적인 테이블의 개방은 하나의 파일 디스
크립터를 가질 뿐이다. 처음에 개방에 사용하는 추가적은 파일 디스크립터
는 인덱스 파일에 사용된다. ;  이 디스크립터는 모든 스레드에서  공유된
다.

10.6.1 데이터베이스에서 많은 수의 테이블을 만들때의 단점

디렉토리에 많은 파일이 있다면 open, close 그리고  create 오퍼레이션은
느려질 것이다. 서로 다른 많은 테이블에서 SELECT 문을  수행하면 테이블
캐쉬가 꽉 찰 때 약간의 overhead가 있을 것이다. 왜냐면 개방된 테이블이
있다면 다른 테이블은 닫혀야 하기 때문이다. 테이블 캐쉬를 크게 해서 이
러한 오우버헤드를 줄일 수 있다.

10.7 많은 테이블을 여는 이유

mysqladmin status 를 실행할 때 다음과 같이 나올 것이다:

Uptime: 426  Running  threads: 1  Questions:  11082 Reloads:  1  Open
tables: 12

단지 6테이블을 사용했는데 이러한 결과는 당황스러울 것이다.

mysql은 멀티스레드를 사용한다. 그래서 동시에 같은 테이블에서  많은 질
의를 할 수 있다. 같은 파일에 대하여 다른 상황을 가지는  두개의 스레드
에 대한 문제를 줄이기 위해 테이블은 각 동시병행적인 스레드마다 독립적
으로 개방된다. 이것은 테이타 파일에서 약간의 메모리와 하나의 추가적인
파일 디스크립터를 사용한다. 모든 스레드에서 인덱스 파일은 공유된다.

10.8 데이터베이스와 테이블에서 심볼릭 링크 사용
데이터베이스 디렉토리에서 테이블과 데이터베이스를 다른 위치로  옮기고
새로운 위치로 심볼릭 링크를 사용할 수 있다. 이렇게 하는 것을  원할 경
우가 있다. 예를 들면 데이터베이스를 더 여유공간이 많은 파일시스템으로
옮기는 경우 등.

mysql에서 테이블이 심볼링 링크되었다는 것을 감지하면 심볼링 링크가 가
리키는 테이블을 대신 사용할 수 있다. realpath() call 을 지원하는 모든
시스템에서 작동한다. (최소한 리눅스와 솔라리스는 realpath()를  지원한
다) realpath()를 지원하지 않는  시스템에서 동시에 실제 경로와  심볼릭
링크된 경로에 접근하면 안된다. 이런 경우에는 업데이트 된후에 테이블이
모순될 수 있다.

mysql은 기본값으로 데이터베이스 링크를 지원하지 않는다.  데이터베이스
간에 심볼릭 링크를 사용하지 않는 작동을 잘 할 것이다. mysql 데이터 디
렉토리에 db1 데이터베이스가 있고 db1을 가리키는 db2 심볼릭  링크를 만
들었다고 해보자:
shell> cd /path/to/datadir
shell> ln -s db1 db2

이제 db1에 tbl_a라는 테이블이  있다면 db2에도 tbl_a가 나타날  것이다.
한 스레드가 db1.tbl_a를 업데이트하고 다른 스레드가 db2.tbl_a를 업데이
트하면 문제가 생길 것이다.

정말로 이 기능이 필요하면 , `mysys/mf_format.c'에서 다음의  코드를 수
정해야 한다.:

if (!lstat(to,&stat_buff))  /* Check if it's a symbolic link */
    if (S_ISLNK(stat_buff.st_mode) && realpath(to,buff))

위 코드를 다음과 같이 수정한다 :

if (realpath(to,buff))

10.9 테이블에 락 거는 방법

mysql의 모든 락은 deadlock-free  이다. 언제나 질의를 시작할때  한번에
모든 필요한 락을 요청하고 언제나 같은 순서대로 테이블에 락을  걸어 관
리한다.

WRITE 락을 사용하는 방법은 다음과 같다:

- 테이블에 락이 없으면 그 테이블에 write 락을 건다.
- 이런 경우가 아니라면 write 락 큐에 락을 요청한다.

READ 락을 사용하는 방법은 다음과 같다:

- 테이블에 write 락이 없으면 그 테이블에 read 락을 건다.
- 이런 경우가 아니라면 read 락 큐에 락을 요청한다.

락이 해제되었을 때 락은 write 락 큐의 스레드에서 사용할 수  있으며 그
러고 나서 read 락 큐의 스레드에서 사용한다.

테이블에서 업데이트를 많이 하면 SELECT  문은 더 이상 업데이트가  없을
때까지 기다린다는 것을 의미한다.
이러한 문제를 해결하기 위해 테이블에서 INSERT 와  SELECT 오퍼레이션을
많이 사용하는 경우에 다음과 같이 하면 된다. 임시 테이블에 레코드를 입
력하고 한번에 임시 테이블에서 실제 테이블로 레코드를 업데이트한다.

다음의 예를 보자:

mysql> LOCK TABLES real_table WRITE, insert_table WRITE;
mysql> insert into real_table select * from insert_table;
mysql> delete from insert_table;
mysql> UNLOCK TABLES;

만약 어떤  경우에 SELECT문에   우선권을 주고 싶다면  INSERT  옵션에서
LOW_PRIORITY or HIGH_PRIORITY 옵션을 사용할 수 있다. 7.13 [Insert] 참
고. (** LOW_PRIORITY를 지정하면 클라이언트에서 테이블을 읽지  않을 때
까지 INSERT 문 수행이 미루어진다. **)

단일 큐를 사용하기 위해 `mysys/thr_lock.c' 의 락킹 코드를 바꿀  수 있
다. 이런 경우 write 락과 read 락은 같은 우선권을 가지며 어떤 애플리케
이션에서는 유용할 수 있다.

10.10 테이블을 빠르고 작게 배열하는 방법
                                <** 테이블 최적화 **>
다음은 테이블에서 최대의성능을 내는 방법과 저장 공간을 절약할 수 있는
테크닉이다:

- 가능한한 NOT NULL로 컬럼을 선언한다. 속도가 빨라지며 각  컬럼마다 1
비트를 절약할 수 있다.

- default 값을 가질 때 유리하다. 입력되는 값이 기본값과 다를  때만 확
실하게  값이  입력된다.  INSERT   문에서 첫번째   TIMESTAMP  컬럼이나
AUTO-INCREAMENT   컬럼의    값을   입력할   필요가    없다.   18.4.49
[mysql_insert_id()] 참고.

- 가능한한 테이블을 작게 만드려면 더 작은 integer 타입을 사용하자. 예
를 들면 MEDIUMINT 가 보통 INT 보다 좋다.

- 가변 길이 컬럼이 없다면(VARCHAR,  TEXT or BLOB columns), 고정  길이
레코드 포맷이 사용된다. 이 경우 속도는 더 빠르지만 불행히도(흑흑~) 낭
비되는 공간이 더 많다. 10.14 [Row format] 참고.

- mysql이 질의를 효과적으로 최적화하기 위해 많은 양의 데이터를 입력한
후 isamchk --analyze를 실행하자. 이렇게 하면 동일한 값을 가진 줄의 평
균 숫자를 가리키는 각 인덱스의 값을 업데이트한다. (물론 unique 인덱스
에서는 항상 1이다)

- 인덱스와  인덱스에 따른  데이타를 정열하려면   isamchk --sort-index
--sort-records=1 을 사용하자.(if you want to sort on index 1).
인덱스에 따라 정렬된 모든 레코드를 읽기 위해 unique 인덱스를 가졌다면
이렇게 하는 것이 속도를 빠르게 하는 가장 좋은 방법이다.

- INSERT 문에서 가능한 다중 값 목록을 사용하자. 개별적인  SELECT 문보
다 훨씬 빠르다. 데이타를 테이블에 입력할 때 LOAD DATA INFILE을 사용하
자. 많은 INSERT 문을 사용하는 것보다 보통 20배 빠르다. 7.15 [Load] 참
고.

많은 인덱스를 가진 테이블에 데이타를 입력할때 다음의  과정을 사용하면
속도를 향상시킬 수 있다.
1. mysql이나 Perl 에서 CREATE TABLE로 테이블을 만든다.
2. mysqladmin flush-tables 실행. (** 열린 테이블을 모두 닫음 **)
3. isamchk --keys-used=0  /path/to/db/tbl_name 사용. 테이블에서  모든
인덱스 사용을 제거한다.
4. LOAD DATA INFILE 를 이용 테이블에 데이타를 입력.
5. pack_isam을 가지고 있고 테이블을 압축하기 원하면 pack_isam을 실행.
6. isamchk -r -q /path/to/db/tbl_name 를 이용 인덱스를 다시 생성.
7. mysqladmin flush-tables 실행.

- LODA DATA INFILE 과 INSERT  문에서 더 빠른 속도를 내려면 키  버퍼를
증가시킨다. mysqld나 safe_mysqld에서 -O key_buffer=# 옵션을  사용하면
된다. 예를 들어 16M는 풍부한 램을 가졌다면 훌륭한 값이다.
- 다른 프로그램을 사용하여 데이타를 텍스트 파일로 덤프할때 SELECT ...
INTO OUTFILE 을 사용하자. 7.15 [LOAD DATA INFILE] 참고.
- 연속으로 다량의 insert와 update를 할 때 LOCK TABLE을  사용하여 테이
블에 락을  걸면 속도를  향상시킬  수 있다.  LOAD DATA   INFILE 그리고
SELECT ...INTO OUTFILE 는 원자적이기 때문에 LOCK TABLE을  사용하면 안
된다. 7.23 [LOCK TABLES/UNLOCK TABLES] 참고.

테이블이 얼마나  단편화되었는지  점검하려면 '.ISM'  파일에서  isamchk
-evi 를 실행한다. 1
3장 [Maintenance] 참고.

10.11 INSERT 문에서 속도에 영향을 미치는 부분
                                <** insert 최적화 **>

insert 하는 시간은 다음와 같이 구성된다:

    Connect: (3)
    Sending query to server: (2)
    Parsing query: (2)
    Inserting record: (1 x size of record)
    Inserting indexes: (1 x indexes)
    Close: (1)

(숫자)는 비례적인 시간이다. 이것은 테이블을 개방할때 초기의  overhead
를 고려하고 있지는 않다. (매 동시병행적으로 수행되는 질의마다 발생)

The size of the table slows down the insertion of indexes by N  log N
(B-trees).

테이블의 크기는 N log N(B-trees)에 따라 인덱스의 입력이 느려진다. (**
말이 좀 이상. 테이블이 커짐에 따라 인덱스 생성도  느려진다는 뜻이겠죵
**)
 
테이블에 락을 걸거나 insert 문에서 다중 값 목록을 사용하여  입력 속도
를 빠르게 할 수 있다. 다중 값 목록을 사용하면 단일 insert 보다 5배 정
도 속도가 빨라진다.


mysql> LOCK TABLES a WRITE;
mysql> INSERT INTO a VALUES (1,23),(2,34),(4,33);
mysql> INSERT INTO a VALUES (8,26),(6,29);
mysql> UNLOCK TABLES;

주요한 속도 차이는 모든 INSERT 문이 완료되고 난 후에 한번에 인덱스 버
퍼가 쓰여기지 때문에 생긴다. 보통  서로 다른 여러 INSERT 문이  있으면
많은 인덱스 버퍼 플러쉬가 있을 것이다. 모든 줄을 단일 문으로 입력하면
락은 필요없다.

락킹은 또한 다중 연결 테스트의 총 시간을 줄일 수는 있다.  그러나 어떤
스레드에서는 총 대기시간은 증가할 수 있다.(왜냐면 락을  기다리기 때문
이다)
예를 들어보자:
thread 1 does 1000 inserts
thread 2, 3, and 4 does 1 insert
thread 5 does 1000 inserts

락을 사용하지 않으면 2, ,3 4는 1과 5 전에 끝마칠 것이다.  락을 사용하
면 2,3,4는 아마도 1이나 5 전에  끝나지 않을 것이다. 그러나 총  시간은
40% 빨라진다.

INSERT, UPDATE, DELETE 오퍼레이션은 mysql에서 매우 빠르다.  그렇기 때
문에 줄에서 5개  이상의 insert나 update를 할 때 락을 추가하면 더 좋은
성능을 얻을 수 있다. 줄에 매우 많은 자료를 입력한다면 다른 스레드에서
테이블에 접근하도록 하기 위해  때때로(각 1000줄마다) UNLOCK  TABLES를
사용하는 LOCK TABLES 실행하면 된다. 이렇게 하면 좋은 성능을 낼  수 있
다. (** 열심히 입력을 하고 중간에 락을 풀었다가 다시 락을 거는  것 반
복함**)

물론 LOAD DATA INFILE 이 더 빠르다.

10.12 DELETE 문에서 속도에 영향을 미치는 부분
                                <** DELETE 문 최적화 **>

레코드를 삭제하는 시간은 정확히 인덱스 숫자에 비례한다. 레코드를 빠르
게 지우기 위해 인덱스 캐쉬의 크기를 증가시킬 수 있다. 기본  인덱스 캐
쉬는 1M 이다; 빠르게  삭제하기 위해 증가되어야 한다.(충분한  메모리를
가지고 있다면 16M로 하자)


10.13 mysql에서 최대 속도를 얻는 방법

벤치마킹을 시작하자! mysql 벤치마크 스위트에서 어떤 프로그램을 사용할
수 있다. (일반적으로 'sql-bench' 디렉토리에 있음) 그리고  입맞에 맞게
수정하자. 이렇게 하면 당신의 문제를 해결할 수 있는 다른 해결책을 찾을
수 있으며 당신에게 가장 빠른 해결책을 테스트할 수 있다.

- mysqld를 적절한 옵션으로 시작하자. 메모리가 많을수록 속도가 빠르다.
  10.1 [MySQL parameters] 참고.

- SELECT 문의 속도를 빠르게 하기 위해 인덱스를 만들자.

10.4 [MySQL indexes] 참고.
- 가능한 효율적으로 컬럼 타입을 최적화하자. 예를 들면 가능한 NOT NULL
로 컬럼을 정의하자. 10.10 [Table efficiency] 참고.
- --skip-locking 옵션은SQL 요청에서 파일 락킹을 없앤다. 속도가 빨라지
지만 다음의 과정을 따라야 한다:
        ㅇ isamchk로  테이블을  체크하거나 수리하기  전에  mysqladmin
                flush-tables  로   모든  테이블을   플러시해야  한다.
(isamchk -d             tbl_name은 언제나 허용된다. 왜냐하면 이건  단
순히 테이블의 정보를 보 여주기 때문이다)
        ㅇ 동시에 뜬 두개의 mysql 서버가 동일한 테이블을 업데이트하려
한다면  동일한 데이터 파일에 두개의 mysql 서버를 띄우면 안된다.

        --skip-locking 옵션은  MIT-pthreads로 컴파일할때  기본값이다.
왜냐면 모든 플랫폼의 MIT-pthreads에서 flock()가 완전하게 지원이  되지
않기 때문이다.

- 업데이트에 문제가 있다면 업데이트를 미루고 나중에 하자. 많은 업데이
트를 하는 것이 한번에 하나를 업데이트하는 것보다 더 빠르다.

- FreeBSD 시스템에서 MIT-pthreads에 문제가 있으면 FreeBSD 3.0 이후 버
전으로 업데이트 하는것이 좋다. 이렇게 하면 유닉스 소켓을  사용하는 것
이 가능하며(FreBSD에서 유닉스  소켓이 MIT-pthreads에서 TCP/IP  연결을
사용하는 것보다 빠르다) 그리고 스레드 패키지가 조정(intergrated?)되어
야 한다.

- 테이블이나 컬럼 단계를 체크하는 GRANT는 성능을 떨어뜨린다.
10.14 로우 포맷과 다른 점은 무엇인가?
                언제 VARCHAR/CHAR을 사용해야 하는가?

mysql은 실제의 SQL VARCHAR 타입이 없다. 그대신 mysql은  레코드를 저장
하고 이것을 VARCHAR로 에뮬레이트하는데 세가지 방법이 있다.

테이블에 VARCHAR, BLOB, TEXT 컬럼이 없으면 고정 row  size를 사용한다.
그외에는 동적 row size를 사용한다. CHAR 과 VARCHAR  컬럼은 애플리케이
션의 관점에서 동일하게 취급된다; 둘다 trailing space는  컬럼을 가져올
때 제거된다.

isamchk -d 를 이용 테이블에서 사용하는 포맷을 체크할 수 있다.
(-d 는 "테이블 묘사"를 의미)

mysql은 세가지 다른 테이블 포맷을 가지고 있다; 고정길이, 다이나믹, 압
축.

고정 길이 테이블
- 기본 포맷. 테이블에 VARCHAR, BLOB, TEXT 컬럼이 없을 때 사용.
- 모든 CHAR, NUMERIC, DECIMAL  컬럼은 컬럼 길이에 space-padded  이다.
(** space-padded를 무엇이라고 번역해야 할지 애매모호해서 **)
- 매우 빠름
- 캐쉬하기 쉽다
- 손상 후 복구가 쉽다. 왜냐면 고정된 위이에 레코드가  위치하기 때문이
다.
- 많은 양의 레코드가 지워졌거나 운영 시스템에서 자유 공간을 늘리길 원
치 않는다면 (isamchk를 이용) 재조직화할 필요없다.
- 보통 다이나믹 테이블보다 많은 디스크 공간을 필요로 한다.

다이나믹 테이블
- 테이블이 VARCHAR, BLOB, TEXT 컬럼을 포함하고 있을 때 사용.
- 모든 문자열 컬럼은 다이나믹하다.(4보다  작은 길이를 가진 문자열  제
외)

- 컬럼이 문자열 컬럼에서 비었거나 ('') 숫자형 컬럼에서 0(NULL 값을 가
진 컬럼과 동일한 것이 아니다)  을 나타내는 비트맵이 모든 레코드  앞에
선행된다. 문자열 컬럼에서 trailing space를 제거한 후 zero의 길이를 가
지거나 숫자형 컬럼이 zero의 값을 가지면 비트 맵으로 표시되고 디스크에
저장되지 않는다. 비지 않은 문자는 문자내용에 길이 바이트만큼 추가되어
저장된다.

- 보통 고정 길이 테이블보다 디스크 공간 절약.
- 줄의 길이를 확장하는 정보를 가지고 줄을 업데이트하면  줄은 단편화될
것이다. 이런 경우 더 좋은 성능을  위해 때때로 isamchk -r 을  실행해야
한다. 통계적으로(?) isamchk -ei tbl_name을 사용하자.
- 손상후 복구가 어렵다. 왜냐면 레코드가 많은 조각드로 단편화되고 링크
(단편)가 없어지기 때문이다.
- 다이나믹 사이즈 테이블의 예상되는 열 길이 :
    3
    + (number of columns + 7) / 8
    + (number of char columns)
    + packed size of numeric columns
    + length of strings
    + (number of NULL columns + 7) / 8

각 링크마다 6 바이트가 더 있다. 다이나믹 레코드는 업데이트로 레코드가
늘어날때마다 링크된다. 각 새로운 링크는 최소 20바이트일  것이며, 그래
서 다음의 확장은 아마도 동일한  링크로 될 것이다. 그게 아니라면  다른
링크가 있을 것이다. isamchk -ed  로 얼마나 많은 링크가 있는지  체크할
수 있다. 모든 링크는 isamchk -r 로 제거할 수 있다.(** ?? **)
 
There is a  penalty of 6  bytes for  each link. A  dynamic record  is
linked whenever an update causes  an enlargement of the record.  Each
new link will  be at  least 20 bytes,  so the  next enlargement  will
probably go in the same link. If not, there will be another link. You
may check how many links there are with isamchk -ed. All links may be
removed with isamchk -r.

GC

- 읽기 전용 테이블은 pack_isam 유틸리티로 만들 수 있다. 확장 mysql 이
메일 지원을 구입한 모든 고객은 내부적인 용도로 pack_isam을  사용할 권
리가 주어진다.
- 압축해제 코드는 모든 mysql 배포판에 있으므로 pack_isam이  없는 고객
도 pack_isam으로 압축된 테이블을 읽을 수 있다. (테이블이  같은 플랫폼
에서 압축되어 있는한)
- 매우 적은 디스크 용량을 사용.
- 각 레코드는 개별적으로 압축이 된다.( 매우 적은 액세스  overhead) 레
코드의 헤더는 테이블의 가장 큰  레코드에 따라 (1-3 바이트)  고정된다.
각 컬럼은 다르게 압축이 된다. 압축 타입은 다음과 같다:
        ㅇ 일반적으로 각 컬럼마다 다른 Huffman 테이블이다.
        ㅇ Suffic 공간 압축
        ㅇ Prefix 공간 압축
        ㅇ 0 값을 가진 숫자는 1비트로 저장.
        ㅇ integer 컬럼의 값이 작은 범위를 가졌다면, 컬럼은 최대한 작
은 타입 으로 저장된다. 예를 들면 BIGINT 컬럼은 모든 값이  0부터 255라
면      TINIINT 컬럼(1바이트)로 저장된다.
        ㅇ 컬럼이 몇가지 가능한 값으로만 구성되어 있다면, 컬럼 타입은
ENUM    으로 변환된다.
        ㅇ 컬럼은 위 압축 방법을 조합하여 사용한다.

- 고정 길이나 다이나믹  길이의 테이블을 다룰  수 있다. 그러나  BLOB나
TEXT 컬럼은 다룰 수 없다.
- isamchk로 압축을 해재할 수 있다.

mysql은 다른 인덱스 타입을  지원한다. 그러나 일반적인 타입은  NISAM이
다. 이것은 B-tree 인덱스이며 모든 키의 갑을 합하여 (키 길이+4)*0.67로
인덱스 파일의 크기를 대강 계산할 수 있다. (이것은 모든 키가 정렬된 순
서로 입력된 가장 나쁜 경우이다)

String indexes are  space compressed. If  the first  index part is  a
string, it will  also be prefix  compressed. Space compression  makes
the index file  smaller if the  string column has  a lot of  trailing
space or is  a VARCHAR column  that is  not always used  to the  full
length. Prefix compression helps  if there are  many strings with  an
identical prefix.

문자열 인덱스는 공간이 압축된다.  첫번째 인덱스 부분이  문자열이라면,
prefix가 압축된다.
문자열 컬럼이 다량의 trailing  space를 가졌거나 언제나 완전한  길이를
사용하지 않는 VARCHAR 컬럼일 때 space 압축은 인덱스 파일을 더 작게 만
든다. prefix 압축은 많은 문자열에 동일한 prefix가 있을 때 유용하다.

{{<!-- This HTML file has been  created by texi2html 1.52 (hacked  by
david@detron.se)
     from /dr1/my/masters/mysql/Docs/manual.texi  on 2   Febuary 1999
-->
}}

11. mysql 벤치마크 스위트

여기에는 mysql 벤치마크 스위트(그리고 crash-me)에 대한 기술적인  설명
이 들어가야 한다. 그렇지만 아직 작성이 되지 않았다. 현재로서는 배포판
의 'bench' 디렉토리에서 코드와 결과를 살펴보아야 한다.(또한 다음의 웹
페이지에서 살펴볼 수 있다.

이것은 사용자에게  주어진 SQL 수행이 제대로 수행되는지 아닌지를  알려
주는 벤치마크이다.

crash-me 는 실제로 질의를  수행하여 데이터베이스에서 지원하는  기능과
능력, 제한사항 등을 측정하는 프로그램이다. 예를 들어 다음의 사항을 측
정한다:

        ㅇ 지원하는 컬럼 타입
        ㅇ 지원하는 인덱스 숫자
        ㅇ 지원하는 펑션
        ㅇ 질의의 최대 크기
        ㅇ VARCHAR 컬럼의 최대 크기


12. mysql 유틸리티

12.1 다양한 mysql 프로그램 개요

mysql client 라이브러리를 사용하여 서버와 통신을 하는 모든 mysql 클라
이언트는 다음의 환경 변수를 사용한다:
+-------------------+--------------------------------------------+
|  Name             |  Description                               | 
+-------------------+--------------------------------------------+
|  MYSQL_UNIX_PORT  |  기본 소켓. 로컬호스트에서 접속할 때 사용  | 
+-------------------+--------------------------------------------+
|  MYSQL_TCP_PORT   |  기본 TCP/IP port                          | 
+-------------------+--------------------------------------------+
|  MYSQL_PWD        |  기본 패스워드                             | 
+-------------------+--------------------------------------------+
|  MYSQL_DEBUG      |  디버깅할 때 Debug-trace 옵션              | 
+-------------------+--------------------------------------------+
|  TMPDIR           |  임시 테이블/파일이 생성되는 디렉토리      | 
+-------------------+--------------------------------------------+
MYSQL_PWD 를 사용하는 것은 보안에 취약하다. 6.2 [Connecting] 참고.

'mysql' 클라이언트는 명령행 라인 히스토리에 환경 변수를 저장하기 위해
MYSQL_HISTFILE 이라는 파일을 사용한다.

모든 MYSQL 프로그램은 매우 다양한 옵션이 있다. 그러나 모든 MYSQL 프로
그램에서 --help 옵션을 제공한다. --help 옵션을 이용해 프로그램의 다양
한 옵션에 대한 모든 정보를 볼 수 있다. 예를 들어, mysql --help  를 해
보자.

아래의 목록은 mysql 프로그램에 대해서 설명하고 있다:

isamchk : mysql 테이블 정보  보기, 점검, 최적화, 복구 유틸리티.  많은
기능이 있기 때문에 별도의 장에서 자세히 설명하고 있다. 13장 참고.

make_binary_release :  컴파일된 mysql  바이너리 버전을   만든다. 다른
myql 사용자의 편의를 위해  ftp.tcx.e의 '/pub/mysql/Incoming' 에  올리
자.

msql2mysql : msql 프로그램을 mysql로 변환하는 쉘 스크립트.  모든 경우
를 다룰 수는 없지만 변환할때 유용할 것이다.

mysql : 간단한 SQL 쉘. (GNU readline 호환성있음) 상호대화식 및 비대화
식으로 사용할 수 있다. 대화식으로 사용하는 경우, 질의  결과는 아스키-
테이블 포맷으로 출력된다. 비대화식으로 사용할 경우, 결과는  텝으로 분
리된 포맷으로 출력된다. (출력 포맷은  명령행 라인 옵션을 이용해  바꿀
수 있다) 다음과 같이 스크립트를 사용할 수 있다:

shell> mysql database < script.sql > output.tab

클라이언트에서 메모리가 부족해서 문제가 생기면 --quick 옵션을  사용하
자.  그러면  질의  결과를  가져오기  위해  mysql_store_result()  대신
mysql_use_result()를 사용한다.

mysqlaccess : host, user, database 조합의 접근 권한 점검 스크립트.

mysqladmin : 데이터베이스 생성 및 삭제, 승인 테이블  재로딩, 디스크에
테이블 플러싱,  로그 파일   재오픈 등을 수행하는  관리자용  유틸리티.
mysqladmin은 또한 서버에서 버전, 프로세스, 상태(status) 정보를 확인할
수 있다.

mysqlbug : mysql 버그 레포트 스크립트. mysql의 버그를 알릴  때 사용하
는 스크립트.
mysqld : SQL 대몬. 항상 실행되고 있어야 한다.

mysqldump : mysql 데이터베이스를 SQL문 형태의 파일이나  탭으로 구분된
텍스빚 파일로 덤프하는 유틸리티.

mysqlimport : LOAD DATA INFILE을 사용해 텍스트 파일의 자료를 테이블에
입력하는 유틸리티. 12.2 참고.

mysqlshow : 데이터베이스, 테이블, 컬럼과 인덱스에 대한 정보 출력

mysql_install_db : 기본 권한으로 MYSQL 승인 테이블 생성.  처음 설치했
을때만 수행된다.
replace : msql2mysql에서 사용되는 유틸리티이다. 그렇지만 다양하게  적
용할 수 있다. 파일이나 표준 입력의  문자열을 교체할 수 있다. 먼저  긴
문자열을 매칭하기 위해 제한된 상황의 시스템에서 사용하자(**  ??) 문자
열을 교체하는데 사용할 수 있다. 예를 들어 다음의 명령어는  파일에서 a
와 b를 교체한다:

shell> replace a b b a -- file1 file2 ...

safe_mysqld : mysqld 대몬을  시작하는 스크립트. 에러가 났을때  서버를
재시작하고 로그 파일에 실행 정보를 기록하는 등 몇가지 안정  대책이 있
다.

12. 2 텍스트 파일에서 데이터 입력(수입?)하기

mysqlimport 는 명령행 인터페이스에서 LOAD DATA INFILE sql 문을 제공한
다. 대부분의 옵션은 LOAD DATA INFILE 과 동일하다. 7.15 [Load] 참고.
다음과 같이 사용한다:

shell> mysqlimport [options] filename ...

명령행에서 지정한 텍스트 파일에 대하여, mysqlimport는 파일이름에서 확
장자를 제거한다.

그리고 파일의 내용을 어떤  테이블에 넣을 것인지 결정하는데  사용한다.
예를 들어 vkdlfdlfmadl 'patient.txt', 'patient.text', 'patient'는  모
두 patient라는 테이블 이름으로 입력될 것이다.

mysqlimport 는 다음의 옵션을 지원한다:
-C, --compress : 서버, 클라이언트에서 압축을 지원하면 서버/클라이언트
사이에서 모든 정보를 압축한다
-#, --debug[=option_string] : 프로그램 사용 추적(디버깅용)

-d, --delete : 텍스트 파일에서 입력하기 전에 테이블을 비움

--fields-terminated-by=...
--fields-enclosed-by=...
--fields-optionally-enclosed-by=...
--fields-escaped-by=...
--fields-terminated-by=...
        : LODA DATA INFILE 에서의 옵션과 동일한 기능을 갖는 옵션
-f, --force : 에러 생략.텍스트 파일을 위한 테이블이 없으면, 다른 남아
있는 파일을  계속 처리(if  a table  for a  text  file doesn't  exist,
continue processing any remaining files) 이 옵션이 없는  경우, 테이블
이 없으면 빠져나온다

--help : 도움말 출력

-h host_name, --host=host_name : 지정한 호스트의 mysql  서버에 데이타
입력. 기본값은 localhost

-i, --ignore : --replace 옵션의 정보 참고.

-l, --lock-tables : 텍스트 파일로 처리하기 전에 모든 테이블에 쓰기 락
을 건다. 그러면 서버에서 모든 테이블이 동기화될 수 있다.

-L, --local : 클라이언트에서 입력 파일 읽음.  기본적으로, localhost에
서 접속하면 텍스트 파일은 서버에 있다고 가정된다.

-pyour_pass, --password[=your_pass] : 서버에 연결할 때 사용하는  비밀
번호. '=your_pass'를 지정하지 않으면 터미널에서 비밀번호를 물어봄

-P port_num, --port=port_num : 호스트에 연결할 때 사용하는  TCP/IP 숫
자. (localhost가 아닌 호스트에서 접속할 때 사용. 예를 들어  유닉스 소
켓을 사용하는 경우)

-r, --replace : --replace 와 --ignore 옵션은 unique key 값의 레코드가
중복되어 있을   경우에 사용된다.  --replcae 를   명시할 경우,  동일한
unique key 값을 가지고 있는 레코드를 대체한다. --ignore 를  명시할 경
우, unique key 값이 동일한  레코드는 생략된다. 두 옵션 모두  명시하지
않는다면, 중복되는 키 갑이 발견되면 에러를 출력하고 텍스트  파일의 나
머지 부분은 생략이 된다.

-s, --silent : 침묵 모드. 에러가 발생했을 때만 출력.

-S /path/to/socket, --socket=/path/to/socket : 로컬호스트(기본 호스트
값)에서 접속할 때 사용하는 소켓 파일.

-u user_name, --user=user_name : 서버에 연결할 때 사용하는 mysql 사용
자 이름. 기본값은 유닉스 로그인 이름.

-v, --verbose : Verbose 모드.  프로그램의 수행에 대한 상세한 정보  출
력.
-V, --version : 버전 정보 출력.

12.3 mysql 압축 읽기 전용 테이블 생성기
** 이 부분은 번역 생략. 간단하게 소개만 합니다 **

pack_isam 은 10 라이센스 이상을  구입하거나 extended support 를  받을
때 사용할 수 있는 추가 유틸리티. 바이너리로만 배포하므로  특정한 플랫
폼에서만 사용 가능. 압축률은 40%-70% 정도이다. 메모리맵을  사용하므로
(mmap()) mmap()가 작동되지 않으면 문제가 생긴다. 압축하고나서는  읽기
전용 테이블이 되며 BLOB 칼럼은 압축하지 못한다.

공개적으로 구할 수 있는 mysql에서 압축 읽기 전용  테이블을 생성하지는
못하지만 읽는 것은 가능하다. 기타 자세한 내용은 매뉴얼을 참고하자.

13. 테이블 유지보수 및 파손 복구에 isamchk 사용하기
데이터베이스 테이블의 정보를 얻을 때, 테이블 점검, 복구 및  최적화 할
때 isamchk 유틸리티를 사용할 수 있다. 다음의 섹션은  어떻게 isamchk를
사용하는지(옵션에 대한 상세한 설명  포함), 테이블 유지 계획을  어떻게
설정할 것인지, 어떻게 isamchk의 다양한 기능을 수행하기 위해 isamchk를
사용하는지에 대해 설명하고 있다.
13.1 isamchk 명령어 사용법

isamchk 는 다음과 같이 사용한다:

shell> isamchk [options] tbl_name

옵션은  isamchk로  무엇을  할  것인지  지정한다.  아래에서  설명한다.
(isamchk --help 명령으로 옵션의  목록을 볼 수  있다) 옵션이 없을  때,
isamchk는 단지 테이블을 점검한다. 더 많은 정보를 얻으려 하거나 특정한
작업이 필요하면 아래에서 설명하는데로 옵션을 지정한다.

tbl_name은 점검하기 원하는 데이터베이스 테이블이다. 데이터베이스 디렉
토리가 아닌 다른 곳에서 isamchk를 실행하면, 파일의 경로를 지정해야 한
다. 왜냐하면 isamchk는 데이터베이스의 위치에 대해서 알지  못하기 때문
이다. 실제로, isamchk는 작업하려는 파일이 데이터베이스 디렉토리에  있
는지 아닌지 신경을 쓰지 않는다; 데이터베이스 테이블에 해당하는 파일을
다른 곳으로 복사하고 그곳에서 복구 작업을 할 수 있다.

원한다명 isamchk의 명령행에서 여러개의 테이블을 사용할 수  있다. 또한
이름을 인덱스 파일 이름('.ISM' 가 붙음)으로 지정할 수 있으며  이런 경
우 '*.ISM' 패턴을 사용하여 디렉토리의  모든 테이블을 지정할 수  있다.
예를 들어 데이터베이스 디렉토리에 있다면 다음과 같이  디렉토리의 모든
테이블을 점검할 수 있다:

shell> isamchk *.ISM

If you  are not  in the  database directory,  you can  check all  the
tables there by specifying the path to the directory:

데이터베이스 디렉토리에 있지 않으면, 디렉토리의 경로를 지정하여  모든
테이블을 점검할 수 있다.

shell> isamchk /path/to/database_dir/*.ISM

또한 mysql data 디렉토리의 경로를 사용한 와일드 카드를  지정하여 모든
데이터베이스의 모든 테이블을 점검할 수 있다:

shell> isamchk /path/to/datadir/*/*.ISM
isamchk는 다음의 옵션을 지원한다:

-a, --analyze
 Analyze the distribution of keys. This will make some joins in MySQL
faster. 키의 분포를 분석. mysql에서 특정한 조인을 빠르게 만든다.

-#, --debug=debug_options
 Output   debug    log.   The    debug_options   string    often   is
'd:t:o,filename'.
 디버그 로그 출력. debug_options 문자는 흔지 'd:t:o,filename' 이다.

-d, --description
 테이블의 정보 출력

-e, --extend-check
 테이블을 매우 상세하게 점검. 아주 특정한 경우에만 필요하다. 일반적으
로 isamchk는 이 옵션이 없어도 모든 에러를 찾을 수 있다.

-f, --force
 Overwrite old temporary files.  If you use  -f when checking  tables
(running isamchk without -r), isamchk will automatically restart with
-r on any table for which an error occurs during checking.  이전의 오
래된 임시  파일을 덮어씀.   테이블을 점검할때 -f를  사용하면(-r  없이
isamchk를 실행) isamchk는 점검하는 동안 에러가 발생하는 테이블에서 자
동으로 -r 옵션을 시작한다.

--help
 도움말 출력.

-i, --information
 점검을 한 테이블의 통계 정보 출력.

-k #, --keys-used=#
 Used with -r. Tell the NISAM table handler to update only  the first
# indexes. Higher-numbered indexes are deactivated. This can  be used
to get  faster inserts!  Deactivated indexes  can be   reactivated by
using isamchk

-r.
-r 과 함께 사용. NISAM  테이블 핸들러에 첫 # 인덱스만  업데이트하라는
것을 알려준다. Higher-numberd(?)  인덱스가 해제된다. 이것은  insert를
빠르게 할때 사용한다! 해제된 인덱스는 isamchk -r 을 사용하여 재활성화
된다.

-l, --no-symlinks
복구할때 심볼릭 링크를 따르지 않는다. 일반적으로 isamchk는  심볼릭 링
크가 가리키는 테이블을 복구한다.

-q, --quick
빠르게 복구하기 위해 -r 과 함께 사용. 일반적으로 원래의 데이타 파일은
건드리지 않는다; 두번째 -q를 지정하여 원래의 데이타 파일을 사용하도록
할 수 있다.

-r, --recover
복구 모드. 유일하지 않는 unique  키만 제외하고 거의 모든 것을  복구한
다.

-o, --safe-recover
복구 모드. 구식 복구 방법을 사용; -r을 사용하여 복구하는  것보다 느리
다. 그렇지만 -r이 다룰 수 없는 몇가지 경우에 사용할 수 있다.
(** -r이 다룰 수 없는 경우란 무엇인지 잘 모르겠네요... **)

-O var=option, --set-variable var=option
변수값 설정. 설정가능한 변수는 아래에서 설명.

-s, --silent
침묵 모드. 에러가 발생할 때만 출력을 한다. 두개의  -s(-ss)를 사용하면 
isamchk에서 매우 조용하게 작업을 할 수 있다.

-S, --sort-index
 Sort index blocks. This speeds up "read-next" in applications.
인덱스 블락 정열. 애플리케이션에서 "read-next" 속도를 향상.(??)

-R index_num, --sort-records=index_num
인덱스에 따라 레코드를 정렬. 이 작업을 하면 데이타를 집중시킬 수 있고
이 인덱스를 사용한 SELECT 와 ORDER BY 작업의 속도를 증가시킬 수 있다.
(처음에는 정렬하는 시간이 매우  느리다!) 테이블의 인덱스 번호를  찾기
위해 SHOW INDEX를 사용한다. SHOW INDEX는 isamchk에서 사용하는 것과 같
은 순서로 테이블의 인덱스를 보여준다. 인덱스는 1번부터  시작하여 번호
가 매겨진다.

-u, --unpack
 Unpack a table that was packed with pack_isam.
pack_isam 으로 압축된 테이블의 압축 해제.

-v, --verbose
Verbose 모드. 정보를 출력. -d 와 -e 와 함께 사용할 수  있다. 여러개의
-v를 사용(-vv, -vvv)하여 더 자세하게 볼 수 있다.

-V, --version   isamchk 버전 출력.

-w, --wait
테이블에 락이 걸려 있으면 기다림.
--set-variable (-O) 옵션의 설정 가능한 변수는 다음과 같다:

keybuffer             default value: 520192
readbuffer            default value: 262136
writebuffer           default value: 262136
sortbuffer            default value: 2097144
sort_key_blocks       default value: 16
decode_bits           default value: 9

13.2 isamchk 메모리 사용법

Memory allocation is important when you run isamchk. isamchk  uses no
more memory than you specify with the -O options. If you are going to
use isamchk on  very large files,  you should  first decide how  much
memory you want it to use. The default is to use only about 3M to fix
things. By  using  larger values,   you can get   isamchk to  operate
faster. For example,  if you have  more than 32M  RAM, you could  use
options such as  these (in addition  to any  other options you  might
specify):

메모리 할당은 isamchk를 실행할 때 중요하다.isamchk는 -O 옵션에서 지정
한 것 이상으로 메모리를 사용하지  않는다. 매우 큰 파일에서  isamchk를
사용하려 하면, 얼마마 많은 메모리를 사용할 것인지 먼저 결정해야 한다.
기본값은 문제를 고치는데 3M를 사용한다. 더 많은 값을 사용해 더 빠르게
isamchk를 사용할 수 있다. 예를 들어 32M 이상 램을 가지고  있다면 다음
과 같은 옵션을 사용할 수 있다. (사용자가 지정한 옵션에 추가하여):
shell> isamchk -O sortbuffer=16M -O keybuffer=16M \
           -O readbuffer=1M -O writebuffer=1M ...
-O sortbuffer=16M 를 사용하면 대부분의 경우에는 충분하다.

Be aware  that isamchk  uses  temporary files  in TMPDIR.   If TMPDIR
points to a  memory file  system, you may  easily get  out of  memory
errors.
isamchk 는 TMPDIR의 임시 파일을 사용한다는 것에 주의하자. 만약 TMPDIR
이 메모리 파일 시스템을 가리킨다면 쉽게 메모리 에러에서 벗어날  수 있
다.

13. 3 테이블 유지보수 설정

문제가 생길때를 기다리는 것보다 정기적으로 테이블을 점검하는 게 좋다.
유지보수 계획를 위하여 isamchk -s 를 사용해 테이블을 점검할  수 있다.
-s 옵션을 사용하면 isamchk가 침묵 모드로 작동을 하며  에러가 발생했을
때만 메시지를 출력한다.

서버를 시작할때 테이블을 점검하는 것도 좋은 생각이다. 예를  들어 업데
이트 도중에 시스템이 리부팅을 했을 때마다 일반적으로 영향을 받은 모든
테이블(이것을 "expected crashed  table"이라고 한다)을 점검해야  한다.
만약 오래된 '.pid' (프로세스 ID)  파일이 재부팅후에 남아 있다면  최근
24시간 동안   변경이 된  모든 테이블을   점검하기 위해  safe_mysqld에
isamchk를 실행하는 테스트를 추가해야 한다.('.pid' 파일은 mysqld가  시
작할때 만들어지며 일반적으로 mysqld가 종료될때 제거된다. 시스템이  시
작할때 '.pid' 파일이 있다는  것은 mysqld가 비정상적으로  종료되었다는
것을 나타낸다.)

더 좋은 테스트는 최근에 변경된 시간이 '.pid' 파일보다 최근인 테이블을
점검하는 것이다.

또한 일반적인 시스템 운영중에 정기적으로 테이블을 점검할 수 있다. TcX
에서는 'crontab' 파일에 다음의 라인을 사용하여 일주일에 한번씩 우리의
중요한 테이블을 점검하도록 cron 작업을 돌린다:
35 0 * * 0 /path/to/isamchk -s /path/to/datadir/*/*.ISM

이렇게 하면 손상된 테이블에 대한 정보를 출력하여 필요할때 테이블을 점
검하고 복구할 수 있다.
몇년동안 우리는 예상하지 못하게 테이블이 손상(하드웨어 문제가 아닌 다
른 이유로 문제가 생긴 테이블)된 경우가 없어서 우리에겐 일주일만으로도
충분하다. (이것은 정말로 진실이다)

우리만큼 mysql에 대해 신뢰를 할 때까지 최근 24시간동안  업데이트된 모
든 테이블에 대해 매일 밤마다 isamchk -s 를 실행할 것을 추천한다.

13.4 테이블 정보 얻기

테이블에 대한 정보나 통계를 얻기 위해 아래의 명령을  사용하자. 뒤에서
자세하게 정보에 대해 설명할 것이다.

isamchk -d tbl_name
테이블에 대한 정보를 얻기  위해 "describe(설명) 모드"로 isamchk를  실
행. mysql 서버를 --skip-locking 옵션을  사용해 시작하면, isamchk는 서
버가 실행되는 동안 업데이트된 테이블에서 에러가 난 것을  보고한다. 그
러나 isamchk는 describe 모드에서 테이블을 변경하지 못하기 때문에 데이
타를 읽을 위험이 없다.

isamchk -d -v tbl_name
isamchk가 수행하는 것에 대해 더 자세한 정보를 보기위해 -v 옵션을 추가
하여 verbose 모드로 수행할 수 있다.

isamchk -eis tbl_name
테이블에서 가장 중요한 정보만 보여준다. 전체 테이블을 다  읽어야 하기
때문에 속도가 느리다.

isamchk -eiv tbl_name
-eiv 와 비슷하지만 현재 무엇이 진행되고 있는지  보여준다.

isamchk -d 출력 예제:

ISAM file:     company.ISM
Data records:           1403698  Deleted blocks:         0
Recordlength:               226
Record format: Fixed length
table description:
Key Start Len Index   Type
1   2     8   unique  double
2   15    10  multip. text packed stripped
3   219   8   multip. double
4   63    10  multip. text packed stripped
5   167   2   multip. unsigned short
6   177   4   multip. unsigned long
7   155   4   multip. text
8   138   4   multip. unsigned long
9   177   4   multip. unsigned long
    193   1           text

isamchk -d -v 출력 예제:

ISAM file:     company.ISM
Isam-version:  2
Creation time: 1996-08-28 11:44:22
Recover time:  1997-01-12 18:35:29
Data records:           1403698  Deleted blocks:              0
Datafile: Parts:        1403698  Deleted data:                0
Datafilepointer (bytes):      3  Keyfile pointer (bytes):     3
Max datafile length: 3791650815  Max keyfile length: 4294967294
Recordlength:               226
Record format: Fixed length

table description:
Key Start Len Index   Type                       Root Blocksize
Rec/key
1   2     8   unique  double                 15845376      1024      
1
2   15    10   multip. text packed  stripped  25062400      1024       
2
3   219   8   multip. double                 40907776      1024     
73
4   63    10   multip. text packed  stripped  48097280      1024       
5
5   167   2   multip.  unsigned short        55200768      1024    
4840
6   177   4   multip.  unsigned long         65145856      1024    
1346
7   155   4   multip. text                   75090944      1024   
4995
8   138   4   multip.  unsigned long         85036032      1024      
87
9   177   4   multip.  unsigned long         96481280      1024     
178
    193   1           text
Example of isamchk -eis 출력 예제:

Checking ISAM file: company.ISM
Key:  1:  Keyblocks used:  97%  Packed:    0%  Max levels:  4
Key:  2:  Keyblocks used:  98%  Packed:   50%  Max levels:  4
Key:  3:  Keyblocks used:  97%  Packed:    0%  Max levels:  4
Key:  4:  Keyblocks used:  99%  Packed:   60%  Max levels:  3
Key:  5:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
Key:  6:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
Key:  7:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
Key:  8:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
Key:  9:  Keyblocks used:  98%  Packed:    0%  Max levels:  4
Total:    Keyblocks used:  98%  Packed:   17%

Records:          1403698    M.recordlength:     226    Packed:       
     0%
Recordspace used:     100%   Empty space:          0%  Blocks/Record: 
 1.00
Recordblocks:     1403698    Deleteblocks:         0
Recorddata:     317235748    Deleted data:         0
Lost space:             0    Linkdata:             0

User time 1626.51, System time 232.36
Maximum resident set size 0, Integral resident set size 0
Non physical pagefaults 0, Physical pagefaults 627, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 639, Involuntary context switches 28966

isamchk -eiv 출력 예제:

Checking ISAM file: company.ISM
Data records: 1403698   Deleted blocks:       0
- check file-size
- check delete-chain
index  1:
index  2:
index  3:
index  4:
index  5:
index  6:
index  7:
index  8:
index  9:

No recordlinks

- check index reference
- check data record references index: 1
Key:  1:  Keyblocks used:  97%  Packed:    0%  Max levels:  4
- check data record references index: 2
Key:  2:  Keyblocks used:  98%  Packed:   50%  Max levels:  4
- check data record references index: 3
Key:  3:  Keyblocks used:  97%  Packed:    0%  Max levels:  4
- check data record references index: 4
Key:  4:  Keyblocks used:  99%  Packed:   60%  Max levels:  3
- check data record references index: 5
Key:  5:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
- check data record references index: 6
Key:  6:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
- check data record references index: 7
Key:  7:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
- check data record references index: 8
Key:  8:  Keyblocks used:  99%  Packed:    0%  Max levels:  3
- check data record references index: 9
Key:  9:  Keyblocks used:  98%  Packed:    0%  Max levels:  4
Total:    Keyblocks used:   9%  Packed:   17%

- check records and index references
[LOTS OF ROW NUMBERS DELETED]
Records:          1403698    M.recordlength:     226    Packed:       
     0%
Recordspace used:     100%   Empty space:          0%  Blocks/Record: 
 1.00
Recordblocks:     1403698    Deleteblocks:         0
Recorddata:     317235748    Deleted data:         0
Lost space:             0    Linkdata:             0
User time 1639.63, System time 251.61
Maximum resident set size 0, Integral resident set size 0
Non physical pagefaults 0, Physical pagefaults 10580, Swaps 0
Blocks in 4 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 10604, Involuntary context switches 122798

다음은 앞의 예제에서 사용한 테이블의 데이타와 인덱스 파일 크기이다:

-rw-rw-r--   1 monty    tcx     317235748 Jan 12 17:30 company.ISD
-rw-rw-r--   1 davida   tcx      96482304 Jan 12 18:35 company.ISM


isamchk가 출력하는 정보 타입에  대한 설명은 아래와 같다.  "keyfile"은
인덱스 파일이다. "Record"와 "row"는 같은 말이다.

ISAM file
        ISAM (index) 파일 이름.

Isam-version
        ISAM 포맷 버전. 현재는 항상 2.

Creation time
        데이타 파일 생성 시간.

Recover time
        인덱스/데이타 파일이 최근에 복구된 시간.

Data records
        테이블에 있는 레코드 수.

Deleted blocks
        지워진 블락이 차지하고 있는  공간. 이러한 공간을 줄이기  위해
테이블의          최적화를 할 수 있다. 13.5.3 [최적화] 참고.

Datafile: Parts
        동적인 레코드 포맷을 위해, 얼마나 많은 데이타 블락이 있는지를
알림.           단편화된 레코드가 없는  최적화된 테이블에서는 Data
records 와 같다.

Deleted data
        회수하지 않은 지원진 데이타의 바이트 수. 이렇나 공간을 없애기
위해 테          이블을 최적화할 수 있다. 13.5.3 [최적화] 참고.

Datafile pointer
        데이타 파일 포인터의 크기로 바이트수. 일반적으로 2,3,4,5 바이
트이다.          대부분의 테이블은 2바이트로 관리를 한다. 그렇지만 아
직까지 mysql에서          는 제어를 할 수 없다. 고정 테이블에서 이는
레코드 주소(address)이다.          동적 테이블에서 이는 바이트 주소이
다.

Keyfile pointer
        인덱스 파일 포인터의 크기로 바이트. 일반적으로 1,2,3 바이트이
다. 대부          분의 테이블은 2바이트로 관리를 한다. 그렇지만 이 크
기는 mysql에서 자          동으로 계산이 된다. 항상 블락 주소이다.

Max datafile length
        테이블의 데이터 파일(.ISD 파일)의 최대 크기. 바이트.

Max keyfile length
        테이블의 key file(.ISM 파일)의  최대 크기. 바이트.

Recordlength
        각 레코드가 차지하고 있는 공간. 바이트.

Record format
        테이블의 레코드를 저장하는데 사용된 포맷. 위에서 보여준  예제
는 고정         길이를 사용하고 있다.
        다른 값은 Compressed 와 Packed 이다.

table description
        테이블의 모든 키의 목록. 각 키에 대한 자세한 설명은 다음과 같
다:
Key
        키의 숫자.
Start
        Where in the record this index part starts.
        레코드에서 인덱스가 시작하는 위치.


Len
 How long this index part is. For packed numbers, this  should always
be the full length of the column. For strings, it may be shorter than
the full length of the indexed column, because you can index a prefix
of a string column.
        인덱스 부분의 길이. For  packed numbers(꽉 찬 숫자를  위해???
번역이 이상..), 컬럼의 총길이가 되어야 한다. 문자열에서는 인덱스된 컬
럼의 총 길이보다 작아야 한다. 왜냐하면 문자열 컬럼 앞에 인덱....
Index
        이 인덱스에 같은 값이 여러개  존재할 수 있는지 없는지를  나타
냄.
Type
        인덱스 부부의 데이터 타입. packed, stripped, empty  옵션을 가
진              NISAM 데이타 타입이다.
Root
        루트 인덱스 블락의 주소.
Blocksize
        각 인덱스 블락의 크기.기본값은 1024이다. 그렇지만 이  값은 컴
파일 할          때 변경할 수 있다.
Rec/key
 This is a statistical value used by the optimizer. It tells how many
records there are per value for this  key. A unique key always has  a
value of 1. This may be updated  after a table is loaded (or  greatly
changed) with isamchk -a.  If this is not  updated at all, a  default
value of 30 is given.
        최적화기(optimizer)에서 사용하는  통계적인 값.  It tells  how
many records there are per value  for this key. unique 키는 항상  1의
값을 가진다. 이 값은 isamchk -a로 테이블이 로딩된 후에(또는 매우 많이
변경되었을때) 업데이트된다. 전혀 업데이트되지 않으면 기본값으로  30이
주어진다.

위의 첫번째 예제에서, 9번째 키는 두부분을 가진 멀티-파트 키이다.

Keyblocks used
 What percentage of the keyblocks are  used. Since the table used  in
the examples had just been  reorganized with isamchk, the values  are
very high (very near the theoretical maximum).
        키블락이 사용하고 있는 비율.(%) 예제의 테이블은 isamchk로  재
조직화(reorganize)되었기 때문에 값이 매우 높다.(이론적인 최대값에  매
우 근접)

Packed
MySQL tries to pack keys with a common suffix. This can only  be used
for CHAR/VARCHAR/DECIM
AL keys. For long strings  like names, this can significantly  reduce
the space  used.  In the  third  example above,  the  4th key  is  10
characters long and a 60% reduction in space is achieved.
....  이름과 같은 long 문자열에서 공간 사용을 상당히 줄인다. 위의  네
번째 예제에서 4번째 키는 10 문자 long 이고 60%의 공간이 줄었다.
(** 번역이 잘 안되는데 접미사같은 것을 붙여서 문자열 등의 공간을 줄인
다 머 그런 것이겠지요 **)

Max levels
        How deep the B-tree for this  key is. Large tables with  long
keys get high values.

Records
        테이블의 레코드수.
M.recordlength
        평균 레코드 길이. 고정 길리 레코드의 테이블에서 이  값은 레코
드 길이와 같다.

Packed
        MySQL strips spaces from the end of strings. The Packed value
indicates the percentage savings achieved by doing this.

mysql은 문자끝의 공백을 제거한다.Packed value는 이렇게 해서 절약된 공
간의 비율을 말한다.

Recordspace used
        데이타 파일이 사용하는 공간의 비율.

Empty space
        데이타 파일이 사용하지 않는 공간의 비율.

Blocks/Record
        레코드당 평균 블락수.(즉, 단편화된 레코드가 몇개의 링크로  구
성되어 있는지) 고정-포맷 테이블에서는 항상 1이다. 이 값은  가능한한 1
에 가깝게 유지해야한다. 이 값이 너무 커지면 isamchk로 테이블을 최적화
(reorganize)해야 한다. 13.5.3 [Optimizaiton] 참고.

Recordblocks
        사용하는 블락(링크)수. 고정 포맷에서 이값은 레코드수와 같다.

Deleteblocks
        삭제된 블락(링크)수.
Recorddata
        데이타 파일이 사용하는 바이트수.

Deleted data
        데이터 파일에서 삭제된(사용하지 않는) 바이트수.

Lost space
        레코드가 매우 짧은 길이로 업데이트되면 약간의 공간을  잃게 된
다. 이 값은 이러한 공간의 bytes 합계이다.

Linkdata
        동적 테이블 포맷을  사용할 때,레코드  조각은 포이터로  링크된
다.Linkdata는 이런 포인터에서 사용하는 저장 공간의 합이다.

pack_isam으로 테이블을 압축했으면, isamchk -d 는 각 테이블  컬럼에 대
한 추가적인 정보를 출력한다.이러한 정보와 그 정보가 무엇을 의미하는지
에 대해서는 12.3 [pack_isam]을 참고.

13.5 파손 복구에 isamchk 사용하기.
mysql에서 데이타를 저장하는데 사용하는 파일 포맷은 광범위하게  테스트
되었다. 그렇지만 데이터베이스 테이블의 손상될 수 있는 외부적인 상황이
항상 있다:

ㅇ 쓰기 도중에 mysqld 프로세스가 죽었을때.
ㅇ 예상치 못하게 컴퓨터가 셧다운되었을 때
        (예를 들어, 컴퓨터의 전원이 나가는 경우)
ㅇ 하드웨어 에러

이번 절에서는 mysql 데이터베이스에서 data의 손상을 체크하고 이에 대처
하는 방법에 대해서 설명한다.
손상 복구 작업을 할 때 테이터베이스의 각 테이블 tbl_name은 데이터베이
스 디렉토리의 세 파일에 조응한다는 것에 대해서 이해하고 있는  것이 중
요하다:

파일                    용도
`tbl_name.frm'            테이블 정의(형식) 파일
`tbl_name.ISD'            데이타 파일
`tbl_name.ISM'           인덱스 파일
세가지 파일 타입은 다양한  방법으로 손상을 당한다. 그렇지만  대부분의
문제는 데이타 파일과 인덱스 파일에서 생긴다.

isamchk는 '.ISD' (데이타) 파일의 복사복을 만들어 작업을  한다. 이전의
'.ISD' 파일을 제거하고 새로운 파일을 이전의 파일 이름으로 바꾸면서 복
구 작업을 마친다. --quick 옵션을 사용하면 isamchk는 임시  '.ISD' 파일
을 만들지 않는다. 대신 '.ISD' 파일이 정확하다고 가정하여  '.ISD' 파일
은 손대지  않고 새로운   인덱스 파일만 생성한다.  isamchk는  자동으로
'ISD' 파일이 손상되었는지 확인하고 손상되었을 경우 복구 작업을 중지하
므로 --quick 옵션을 사용하는 것은 안전하다. 두개의 -quick 옵션을 사용
할 수 있다. 이런 경우 특정한 에러(중복된 키 등)에서 취소를  하지는 않
지만 '.ISD' 파일을 수정하여 문제를 해결하려고 한다.일반적으로  두개의
--quick 옵션을 사용하는 것은  복구작업을 수행하기 위한 디스크  공간이
거의 없을 경우에만 유용하다. 이런 경우 isamchk를  수생하기전에 최소한
백업을 해 놓아야 한다.

13.5.1 에러가 났을때 테이블 점검 방법

테이블을 점검하기 위해 다음의 명령을 사용한다:
:
isamchk tbl_name
        모든 에러의 99.99%를 발견할 수 있다. 이 경우  발견하지 못하는
것은 데이타 파일과 관련된 손상이다.(일반적으로 거의 생기지 않는다) 테
이블을 점검하고자 한다면 일반적으로는 아무런 옵션을 주지 않거나 -s 나
--silent 옵션을 주어 isamchk를 수행하는 것이다.

isamchk -e tbl_name
        이 옵션은 모든 데이터를  완전하게 점검한다.( -e 는  "extended
check" 를 의미한다) 모든 키가 정확한 레코드를 가리키고  있는지 점검한
다.It does a check-read of every key for each row to verify that they
indeed point to the correct row. 많은 키를 가진 큰  테이블에서는 시간
이 많이 걸린다. isamchk는 일반적으로 첫번째 에러를 발견하면 실행을 멈
춘다. 더 많은 정보를 얻고자  한다면, --verbose (-v) 옵션을 추가할  수
있다. 이 옵션을 추가하면 isamchk는  최대 20개의 에러가 있을 때까지 계
속 실행을 한다. 일반적으로는 간단한 isamchk (테이블 이름  외에 아무런
인수도 없는)만으로 충분하다.

isamchk -e -i tbl_name
        위의 명령과 같다. 그렇지만 -i 옵션을 붙이면  isamchk가 정보의
통계를 출력한다.

13.5.2 테이블 복구방법
손상된 테이블의 징후는 일반적으로 질의가 갑자기 중지되고  다음과 같은
에러를 낸다:
ㅇ`tbl_name.frm' is locked against change
ㅇCan't find file `tbl_name.ISM' (Errcode: ###)
ㅇGot error ### from table handler (Error 135 is an exception in this
case)
ㅇUnexpected end of file
ㅇRecord file is crashed

이런 경우, 테이블을 고쳐야 한다. isamchk는 일반적으로 잘못된  것을 감
지하고 대부분을 고친다.

복구 과정은 아래에서 설명하는대로 4단계가 있다.시작하기 전에  먼저 데
이타베이스 디렉토리로 이동하고(cd 명령  이용) 테이블 파일의  퍼미션을
확인해야 한다. mysqld를 실행할 수 잇는 유닉스 사용자가 읽을 수 있는지
확인해야 한다. (또한 작업을 하려는 사용자. 왜냐하면 점검하려는 파일에
접근해야 하기 때문이다) 파일을 수정해야 한다면 파일에 쓰기  권한이 있
어야 한다.

1단계 : 테이블 점검

isamchk *.ISM 또는 (충분한 시간이 있다면 isamchk -e  *.ISM). 불필요한
정보를 보지 않으려면 -s (silent) 옵션을 사용한다.

isamchk에서 에러가 있다고 알리는 테이블만 고쳐야 한다.  이런 테이블의
경우는 2단계로 넘어간다.

점검하면서 에러를 만났을 때(out of memory 에러 등) 또는  isamchk가 기
능을 멈추었을 때 3단계로 넘어간다.

2단계 : 쉽고 안전한 복구
먼저 isamchk -r -r tbl_name을  시도한다. (-r -q는 "빠른 복구  모드"를
의미) 이경우 데이타 파일은 손대지  않고 인덱스 파일 복구를  시도한다.
데이타 파일이 제대로 되어 있고 삭제 링크가 데이타 파일내의  정확한 위
치를 가리키고 있다면, 원활하게  작동을 하고 테이블을 고칠  것이다.(If
the data file contains everything that it should and the delete links
point at the correct locations within the data file, this should work
and the table is fixed.) 다음 테이블을 고치자. 그게 아니라면  다음 과
정을 사용한다:
        1. 진행하기 전에 데이타 파일의 백업본 만들기
        2. isamchk -r tbl_name 사용.(-r  는 "복구 모드" 의미)  그러면
데이타  파일에서 정확하지 않은 레코드와 삭제된 레코드를 제거하고 인덱
스 파일 을 재구성한다.
        3. 앞의 과정이 실패하면, isamchk --safe_recover tbl_name을 사
용.     Safe recovery 모드는 구식 복구 방법을 사용하며  일반적인 복구
모드로  할 수 없는 몇가지 경우에 사용할 수 있다.(그렇지만 더 느리다)

점검하면서 에러를 만났을 때(out of memory 에러 등) 또는  isamchk가 기
능을 멈추었을 때 3단계로 넘어간다.

3단계 : 어려운 복구

인덱스 파일의 첫 16k 블락이 파괴되거나 정확하지 않은 정보를 가지고 있
을 때, 또는 인덱스 파일이 없는 경우에만 이번 단계까지 온다. 이경우 새
로운 인덱스 파일을 만들어야 한다. 다음과 같이 하자:

        1. 데이타 파일을 안전한 장소로 이동.
        2.  새로운(빈)  데이타와  인덱스  파일을  만들기  위해  table
description 파  일을 사용:
                shell> mysql db_name
                mysql> DELETE FROM tbl_name;
                mysql> quit
        3. 이전의 데이타 파일을 새롭게 만든 데이터 파일로 복사. (이전
의 데이 타 파일을 새로운 파일로 옮기지는 말자; 잘못되었을 경우 복사본
을 유지 하길 원할 것이다)
2단계로 가자. isamchk -r -q는 이제 제대로 작동을 할 것이다.  (무한 루
프가 되면 안된다. This shouldn't be an endless loop).

4단계 : 매우 어려운 복구
description 파일  또한 손상을   입었을 경우에만 이번  단계까지  온다.
description 파일은 테이블을 만든 이후에 변경이 되지 않기 때문에, 이러
한 경우는 결코 생겨서는 안된다.
        1. 백업본에서 description 파일을 복구해 3단계로 넘어간다.  또
한 인덱스        파일을 복구할 수  있고 2단계로  넘어간다. 뒤의  경우
isamchk -r로 시 작을 해야 한다.
        2. 백업본이 없지만 정확히 어떻게 테이블을 만들었는지  알고 있
다면, 다        른 데이터베이스에 테이블의 복사본을 만든다. 새로운 데
이타 파일을 제  거하고 다른 데이터베이스의 description 과 인덱스 파일
을 손상된 데이  터베이스로 옮긴다. 이렇게 하면 새로운  description 과
인덱스 파일을   얻을 수 있지만  데이타 파일만 따로 남아있다.  2단계로
가서 인덱스 파일        을 재구성한다.

13.5.3 테이블 최적화
레코드를 삭제하거나 업그레이드 하면서  생긴 단편화된 레코드를  모으고
불필요하고 낭비된 공간을 제거하기  위해 복구 모드로 isamchk를  실행한
다:

        shell> isamchk -r tbl_name
SQL OPTIMIZE TABLE 문을 이용하여  같은 방법으로 테이블을 최적화할  수
있다. OPTIMIZE TABLE 은 쉽지만 isamchk가 더 빠르다.

또한 isamchk는 테이블의 성능을 향상시킬 수 있는 몇가지  옵션을 사용할
수 있다:

-S, --sort-index
        high-low 순서로 인덱스 트리 블락을 정열. 검색을 최적화하고 키
에 의한         테이블 검색을 빠르게 한다.
-R index_num, --sort-records=index_num
        인덱스에 따라 레코드를 정열. 데이타를 지역화하고 이  인덱스를
사용하는        SELECT 와  ORDER BY  오퍼레이션의 속도를  향상시킨다.
(처음에는 정    열을 하는 시간이 엄청 느리다!) 테이블의 인덱스 번호를
확인하려면      SHOW INDEX를 사용하면 되며, SHOW INDEX는 isamchk가 인
덱스를  검색하는 순서대로 테이블의 인덱스를 보여준다. 인덱스는  1번부
터 번호 가 매겨진다.
-a, --analyze
        테이블에서 키의 분포를 분석. 나중에 테이블에서 레코드를  가져
올 때 조        인의 성능을 향상시킨다.

14 Adding new functions
                                                 to MySQL
There are two ways to add new functions to MySQL:

You can add  the function through  the user-definable function  (UDF)
interface. User-definable functions are added and removed dynamically
using the CREATE FUNCTION and DROP FUNCTION statements. See section
7.29 CREATE FUNCTION/DROP FUNCTION syntax.

You can  add the  function as  a native  (built  in) MySQL  function.
Native functions  are  compiled into  the  mysqld server  and  become
available on a permanent basis.

Each method has advantages and disadvantages:

If you write a user-definable  function, you must install the  object
file in addition to the server  itself. If you compile your  function
into the server, you don't need to do that.

You can add  UDFs to  a binary MySQL  distribution. Native  functions
require you to modify a source distribution.

If you upgrade your MySQL distribution, you can continue to  use your
previously-installed UDFs. For native functions, you must repeat your
modifications each time you upgrade.

Whichever method you use to add new functions, they may be  used just
like native functions such as ABS() or SOUNDEX().

14.1 Adding a new user-definable function
For the UDF mechanism to work, functions must be written in C  or C++
and your operating  system must  support dynamic  loading. The  MySQL
source distribution includes a file `sql/udf_example.cc' that defines
5 new functions. Consult this file to see how UDF calling conventions
work.

For each function that you want to use in SQL statements,  you should
define corresponding C (or C++)  functions. In the discussion  below,
the name ``xxx'' is used for an example function name. To distinquish
between SQL   and C/C++  usage, XXX()   (uppercase) indicates  a  SQL
function call, and xxx() (lowercase) indicates a C/C++ function call.

The C/C++ functions  that you  write to implement  the inferface  for
XXX() are:

xxx() (required)
 The main function. This  is where the  function result is  computed.
The correspondence between the SQL type and return type of your C/C++
function is shown below:
+------------+--------------+
|  SQL type  |  C/C++ type  | 
+------------+--------------+
|  STRING    |  char *      | 
+------------+--------------+
|  INTEGER   |  long long   | 
+------------+--------------+
|  REAL      |  double      | 
+------------+--------------+
xxx_init() (optional)
 The initialization function for xxx(). It can be used to:
Check the number of arguments to XXX()
Check that the arguments are  of a required type, or,  alternatively,
tell MySQL to coerce  arguments to the types  you want when the  main
function is called Allocate any memory required by the main function
Specify the maximum length of the result
Specify (for REAL functions) the maximum number of decimals
Specify whether or not the result can be NULL

xxx_deinit() (optional)
 The deinitialization function  for xxx(). It  should deallocate  any
memory allocated by the initialization function.

When a SQL  statement invokes XXX(),  MySQL calls the  initialization
function xxx_init() to  let it  perform any required  setup, such  as
argument checking  or memory   allocation. If xxx_init()  returns  an
error, the SQL  statement is aborted  with an  error message and  the
main and deinitialization  functions are not  called. Otherwise,  the
main function xxx() is called once for each row. After all  rows have
been processed, the deinitialization function xxx_deinit() is  called
so it can perform any required cleanup.

All functions must be  thread-safe (not just  the main function,  but
the initialization  and  deinitialization functions   as well).  This
means that  you are  not allowed  to allocate  any  global or  static
variables that change! If you need memory, you should allocate  it in
xxx_init() and free it in xxx_deinit().

14.1.1 UDF calling sequences
The main function should  be declared as  shown below. Note that  the
return type  and parameters  differ, depending  on whether   you will
declare the SQL function XXX() to  return STRING, INTEGER or REAL  in
the CREATE FUNCTION statement:

For STRING functions:

char *xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *result, unsigned long *length,
              char *is_null, char *error);
For INTEGER functions:

long long xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);
For REAL functions:

double xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);
The initialization and deinitialization  functions are declared  like
this:


my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
void xxx_deinit(UDF_INIT *initid);
The initid parameter is passed to all three functions. It points to a
UDF_INIT structure that  is used to  communicate information  between
functions. The   UDF_INIT structure  members are   listed below.  The
initialization function should fill in any members that it  wishes to
change. (To use the default for a member, leave it unchanged.)

my_bool maybe_null
xxx_init() should set maybe_null to 1  if xxx() can return NULL.  The
default value is 1 if any of the arguments are declared maybe_null.

unsigned int decimals
 Number of  decimals. The  default  value is  the maximum   number of
decimals in the arguments passed to the main function.  (For example,
if the function is passed 1.34,  1.345 and 1.3, the default would  be
3, since 1.345 has 3 decimals.

unsigned int max_length
 The maximum length of the  string result. The default value  differs
depending on the result type  of the function. For string  functions,
the default  is  the length   of the longest   argument. For  integer
functions, the default is 21 digits. For real functions,  the default
is 13 plus the number of decimals indicated by initid->decimals. (For
numeric functions,  the length  includes any  sign or   decimal point
characters.)

char *ptr
 A pointer  that the   function can use   for its own  purposes.  For
example, functions   can use   initid->ptr to  communicate  allocated
memory between  functions. In   xxx_init(), allocate the  memory  and
assign it to this pointer:

initid->ptr = allocated_memory;
In xxx() and xxx_deinit(), refer to initid->ptr to use  or deallocate
the memory.

14.1.2 Argument processing
The args  parameter points  to  a UDF_ARGS  structure which   has the
members listed below:

unsigned int arg_count

 The number  of arguments.  Check this  value in   the initialization
function if you  want your function  to be  called with a  particular
number of arguments. For example:

if (args->arg_count != 2)
{
    strcpy(message,"XXX() requires two arguments");
    return 1;
}

enum Item_result *arg_type

 The  types  for   each argument.   The  possible   type values   are
STRING_RESULT,  INT_RESULT   and REAL_RESULT.   To   make sure   that
arguments are of a given  type and return an  error if they are  not,
check the arg_type array in the initialization function. For example:

if (args->arg_type[0] != STRING_RESULT
      && args->arg_type[1] != INT_RESULT)
{
    strcpy(message,"XXX() requires a string and an integer");
    return 1;
}

As an alternative  to requiring  your function's arguments  to be  of
particular types, you can use the initialization function to  set the
arg_type elements to the types you want. This causes MySQL  to coerce
arguments to those  types for  each call  to xxx().  For example,  to
specify coercion of the first two arguments to string and integer, do
this in xxx_init():

args->arg_type[0] = STRING_RESULT;
args->arg_type[1] = INT_RESULT;

char **args
args->args communicates  information to  the initialization  function
about the general nature  of the arguments  your function was  called
with. For a constant argument i, args->args[i] points to the argument
value. (See   below for  instructions  on how   to access  the  value
properly.)  For  a  non-constant  argument,  args->args[i]  is  0.  A
constant argument is an expression that uses only constants,  such as
3 or 4*7-2  or SIN(3.14).  A non-constant argument  is an  expression
that refers to values that may change from row to row, such as column
names or functions that are  called with non-constant arguments.  For
each invocation of the main function, args->args contains  the actual
arguments that are passed for the row currently being processed.

Functions can refer to an argument i as follows:

An argument of type STRING_RESULT is given as a string pointer plus a
length, to allow handling of binary data or data of arbitrary length.
The string contents  are available  as args->args[i]  and the  string
length is args->lengths[i].  You should not  assume that strings  are
null-terminated.

For an argument of type INT_RESULT, you must cast args->args[i]  to a
long long value:

long long int_val;
int_val = *((long long*) args->args[i]);

For an argument of type REAL_RESULT, you must cast args->args[i] to a
double value:

double    real_val;
real_val = *((double*) args->args[i]);
unsigned long *lengths
 For the  initialization function,  the lengths  array indicates  the
maximum string length for each  argument. For each invocation of  the
main function,  lengths contains  the actual  lengths of   any string
arguments that are passed for the row currently being  processed. For
arguments of types INT_RESULT or REAL_RESULT, lengths  still contains
the maximum   length of   the argument  (as  for  the  initialization
function).

14.1.3 Return values and error handling

The initialization function should return 0 if no error  occurred and
1  otherwise.  If   an error   occurs,  xxx_init()   should store   a
null-terminated error message in  the message parameter. The  message
will   be   returned  to   the   client.   The   message  buffer   is
MYSQL_ERRMSG_SIZE characters long,  but you  should try  to keep  the
message to less than  80 characters so  that it fits  the width of  a
standard terminal screen.

The return value of  the main function  xxx() is the function  value,
for long long and double functions. For string functions,  the string
is returned in the result and length arguments. result is a buffer at
least 255 bytes  long. Set these  to the contents  and length of  the
return value. For example:


memcpy(result, "result string", 13);
*length = 13;
The string function return value normally also points to the result.

To indicate a return value of NULL in the main function,  set is_null
to 1:

*is_null = 1;

To indicate  an error  return in  the main  function,  set the  error
parameter to 1:

*error = 1;

If xxx() sets *error to 1 for any row, the function value is NULL for
the current   row and   for any  subsequent  rows  processed  by  the
statement in which XXX() was invoked. (xxx() will not even  be called
for subsequent rows.) Note: In  MySQL versions prior to 3.22.10,  you
should set both *error and *is_null:

*error = 1;
*is_null = 1;

14.1.4 Compiling and installing user-definable functions
Files implementing UDFs must  be compiled and  installed on the  host
where the  server  runs. This   process is described   below for  the
example UDF  file  `udf_example.cc' that  is  included in  the  MySQL
source distribution. This file contains the following functions:
metaphon() returns a metaphon string of the string argument.  This is
something like a soundex string, but it's more tuned for English.

myfunc_double() returns the sum of the ASCII values of the characters
in its arguments, divided by the sum of the length of its arguments.

myfunc_int() returns the sum of the length of its arguments.

lookup() returns the IP number for a hostname.

reverse_lookup() returns the hostname for an IP number.  The function
may be called with a string "xxx.xxx.xxx.xxx" or four numbers.

A dynamically-loadable file should be  compiled as a sharable  object
file, using a command something like this:

shell> gcc -shared -o udf_example.so myfunc.cc

You can easily find out the correct compiler options for  your system
by running this command in the  `sql' directory of your MySQL  source
tree:

shell> make udf_example.o

You should  run  a compile   command similar  to the   one that  make
displays, except that you should remove the -c option near the end of
the line and add -o udf_example.so to  the end of the line. (On  some
systems, you may need to leave the -c on the command.)

Once you compile a shared object containing UDFs, you must install it
and   tell  MySQL   about  it.   Compiling  a   shared   object  from
`udf_example.cc'    produces   a    file    named   something    like
`udf_example.so' (the exact name may vary from platform to platform).
Copy this file to some directory searched by ld, such  as `/usr/lib'.
On many   systems, you  can set   the LD_LIBRARY  or  LD_LIBRARY_PATH
environment variable to point  at the directory  where you have  your
UDF function files. The  dopen manual page  tells you which  variable
you should use on your system. You should set this in mysql.server or
safe_mysqld and restart mysqld.

After the library is installed, notify mysqld about the new functions
with these commands:

mysql>    CREATE   FUNCTION    metaphon    RETURNS   STRING    SONAME
"udf_example.so";
mysql>   CREATE   FUNCTION    myfunc_double   RETURNS   REAL   SONAME
"udf_example.so";
mysql>   CREATE   FUNCTION    myfunc_int   RETURNS   INTEGER   SONAME
"udf_example.so";
mysql> CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
mysql>   CREATE  FUNCTION   reverse_lookup   RETURNS  STRING   SONAME
"udf_example.so";

Functions can be deleted using DROP FUNCTION:

mysql> DROP FUNCTION metaphon;
mysql> DROP FUNCTION myfunc_double;
mysql> DROP FUNCTION myfunc_int;
mysql> DROP FUNCTION lookup;
mysql> DROP FUNCTION reverse_lookup;

The CREATE FUNCTION  and DROP FUNCTION  statements update the  system
table func  in the  mysql  database. The  function's name,   type and
shared library name are saved in the table. You must have  the insert
and delete  privileges for  the  mysql database  to create   and drop
functions.


You should not use CREATE FUNCTION to add a function that has already
been created. If you need to reinstall a function, you  should remove
it with DROP FUNCTION and then reinstall it with CREATE FUNCTION. You
would need to do this, for example, if you recompile a new version of
your function, so  that mysqld  gets the new  version. Otherwise  the
server will continue to use the old version.
Active functions are reloaded each time the server starts, unless you
start mysqld with the --skip-grant-tables  option. In this case,  UDF
initialization is   skipped and   UDFs are  unavailable.  (An  active
function is one  that has been  loaded with  CREATE FUNCTION and  not
removed with DROP FUNCTION.)

14.2 Adding a new native function
The procedure for adding  a new native  function is described  below.
Note that you cannot  add native functions  to a binary  distribution
since the procedure  involves modifying MySQL  source code. You  must
compile MySQL yourself from a source distribution. Also note  that if
you migrate to another version of MySQL (e.g., when a new  version is
released), you   will need  to  repeat the   procedure with  the  new
version.

To add a new native MySQL function, follow these steps:

Add one  line  to `lex.h'   that defines  the function   name in  the
sql_functions[] array.

Add two lines to `sql_yacc.yy'. One indicates the preprocessor symbol
that yacc should define (this should be added at the beginning of the
file). Then define the function  parameters and add an ``item''  with
these parameters to  the simple_expr  parsing rule.  For an  example,
check all occurrences of SOUNDEX in `sql_yacc.yy' to see how  this is
done.

In `item_func.h', declare  a class inheriting  from Item_num_func  or
Item_str_func, depending on whether your function returns a number or
a string.

In `item_func.cc', add one  of the following declarations,  depending
on whether you are defining a numeric or string function:

double   Item_func_newname::val()
longlong Item_func_newname::val_int()
String  *Item_func_newname::Str(String *str)

You should probably also define the following function:

void Item_func_newname::fix_length_and_dec()

This function should at least calculate max_length based on the given
arguments.  max_length  is  the  maximum  number  of  characters  the
function may return. This function should also set maybe_null =  0 if
the main function can't return a  NULL value. The function can  check
if any of  the function  arguments can  return NULL  by checking  the
arguments maybe_null variable.

All functions must be thread-safe.

For string functions, there are some additional considerations  to be
aware of:

The String *str argument provides a string buffer that may be used to
hold the result.

The function should return the string that holds the result.

All current  string  functions try  to  avoid allocating  any  memory
unless absolutely necessary!

15. mysql ODBC 지원

mysql은 MyODBC 프로그램을 통해 ODBC에 대한 지원을 제공한다.

15.1 MyODBC 를 지원하는 운영체제

MyODBC 는 윈도우95와 NT에서 32비트 ODBC(2.50) 레벨 0 드라이버이다. 우
리는 누군가가 윈도우 3.x 에 이 프로그램을 포팅해주길 바란다.
(** 당근, 저는 능력안됨 **)

15.2 MyODBC에 문제가 있는 경우

ODBC는  액세스,   Admndemo.exe,  C++-빌더,  Centura   Team  Developer
(formerly Gupta SQL/Windows), 콜드퓨전(솔라리스용), 크리스탈  레포트,
델파이, 엑셀,  iHTML, FileMaker  Pro, 폭스프로,  노츠 Notes  4.5/4.6,
SBSS, perl DBD-ODBC, 파라독스, 파워빌더, VC++, 비주얼 베이직에서 테스
팅되었다.

MyODBC 에서 잘 작동하는 다른  애플리케이션이 있으면 myodbc@tcx.se  에
메일을  보내주세요~ (** 이정도는 해 주어야 서로서로 좋겠지용**)

어려움에 부딪치면, ODBC 매니저에서의 로그 파일과 MyODBC 로그에 대해서
알고 싶다. 이런 파일이 있으면 문제를 조금이나마 줄이는데 도움이 될 것
이다.

MyODBC log 파일을 얻으려면 MyODBC 연결/설정 화면의 'Trace  MyODBC' 옵
션에 체크를 한다.
로그는 `c:\myodbc.log' 에 기록될 것이다. 이 옵션이  제대로 작동하려면
MYSQL2.DLL 이 아니라 MYSQL.DLL 을 사용해야 한다는 것을 기억하자!

15.3 MyODBC 와 잘 작동하는 프로그램

대부분의 프로그램은 myodbc 와 잘 작동한다. 그렇지만 아래의  각 목록에
있는 것은 우리가 직접 테스팅하였거나 다른 사람들이  제대로 작동한다고
확증해 준 것이다::
ㅇ액세스
액세스와는 잘 작동한다:
        - 테이블에서 프라이머리 키를 지정해야 한다.
        - 업데이트가 되길 원하는 모든 테이블에 timestamp를  가지고 있
어야 한 다.
        - double float 필드만을 사용해야 한다. single floats  와 비교
를 하면         실패한다.
        - mysql에 연결할 때 'Return matching rows' 옵션 필드에 체크를
하자.
        - NT에서의 액세스는 BLOB 컬럼을 OLE OBJECTS 로 보고한다. 대신
        MEMO 컬럼을 가지길 원하면 ALTER  TABLE 을 이용해 컬럼을  TEXT
        로 바꾸자.

ㅇ 엑셀
잘 작동한다 몇가지 팁이 있다:

        dates 에서 문제가 있으면 CONCAT() 함수를 사용하여 string 으로
서      select 를 사용하자.

        예를 들면:

        select      CONCAT(rise_time),     CONCAT(set_time)      from
sunrise_sunset;

문자열로서 값을 가져오며 엑셀97에서 time 값이 제대로 인식될 것이다.

이 예제에서 CONCAT()의 목적은 ODBC가 컬럼을 "string  type"으로 생각하
도록 속이는 것이다. CONCAT() 가  없으면, ODBC는 컬럼을 time  타입으로
인식하며 엑셀은 이것을 이해하는데 실패한다.

이것은  엑셀의  버그라는  것을  기억하자.  엑셀은  자동적으로  문자열
(string)을 time으로 변환한다. 소스가 텍스트 파일이라면 잘  될 것이다.
그렇지만 소스가 각 컬럼의 정확한 타입을 보고하는 ODBC 연결이라면 분명
히 어리석은 짓이다.

ㅇ odbcadmin
ODBC 테스트 프로그램.

ㅇ 델파이
DBE 3.2  나  이후 버전을   사용해야 한다. mysql에   연결할 때  'Don't
optimize column width' 옵션 필드에 체크를 한다.
물론, 여기에는 myodbc 를 위한 ODBC 목록과 BDE 목록을 세팅하기 위해 잠
재적으로 유용한 델파이 코드가 있다. (BDE 목록은 델파이  슈퍼 페이지에
서 공개로 받을 수 있는 BDE  Alias 데이터를 필요로 한다) : (Thanks  to
Bryan Brunton bryan@flesherfab.com for this)

fReg:= TRegistry.Create;
          fReg.OpenKey('\Software\ODBC\ODBC.INI\DocumentsFab', True);
          fReg.WriteString('Database', 'Documents');
          fReg.WriteString('Description', ' ');
          fReg.WriteString('Driver', 'C:\WINNT\System32\myodbc.dll');
          fReg.WriteString('Flag', '1');
          fReg.WriteString('Password', ");
          fReg.WriteString('Port', ' ');
          fReg.WriteString('Server', 'xmark');
          fReg.WriteString('User', 'winuser');
          fReg.OpenKey('\Software\ODBC\ODBC.INI\ODBC  Data  Sources',
True);
          fReg.WriteString('DocumentsFab', 'MySQL');
          fReg.CloseKey;
          fReg.Free;

          Memo1.Lines.Add('DATABASE NAME=');
          Memo1.Lines.Add('USER NAME=');
          Memo1.Lines.Add('ODBC DSN=DocumentsFab');
          Memo1.Lines.Add('OPEN MODE=READ/WRITE');
          Memo1.Lines.Add('BATCH COUNT=200');
          Memo1.Lines.Add('LANGDRIVER=');
          Memo1.Lines.Add('MAX ROWS=-1');
          Memo1.Lines.Add('SCHEMA CACHE DIR=');
          Memo1.Lines.Add('SCHEMA CACHE SIZE=8');
          Memo1.Lines.Add('SCHEMA CACHE TIME=-1');
          Memo1.Lines.Add('SQLPASSTHRU MODE=SHARED AUTOCOMMIT');
          Memo1.Lines.Add('SQLQRYMODE=');
          Memo1.Lines.Add('ENABLE SCHEMA CACHE=FALSE');
          Memo1.Lines.Add('ENABLE BCD=FALSE');
          Memo1.Lines.Add('ROWSET SIZE=20');
          Memo1.Lines.Add('BLOBS TO CACHE=64');
          Memo1.Lines.Add('BLOB SIZE=32');

          AliasEditor.Add('DocumentsFab','MySQL',Memo1.Lines);

ㅇ C++빌더
BDE 3.0과 테스팅을 했다. 유일하게 알려진 문제는 테이블  스키마를 변경
할 대로 질의 필드가 업데이트되지 않는다. 그런데 BDE는 문제는  아닌 것
같지만 index PRIMARY 에서만 프라이머리 키를 인식하지 못하는 것으로 보
인다.
 Tested with BDE 3.0. The only  known problem is that when the  table
schema changes, query fields  are not updated.  BDE however does  not
seem to recognize primary keys,  only the index PRIMARY, though  this
has not been a problem.

ㅇ 비주얼 베이직
테이블을 업데이트 가능하게 하려면  테이블의 프라이머리 키를  정의해야
한다.
(** 이 부분은 제가 출력한  ps 파일에는 없는데 웹사이트에는  있네요...
**)

15.4 ODBC 관리자 프로그렘 설정 방법

윈도우 95에서 서버의 이름을 지정하는 것에는 세가지 방법이 있다:

- 서버의 IP 주소 사용.
-'lmhosts' 파일에 다음의 정보 추가:
        ip hostname
예를 들면
        194.216.84.21 my
- DNS를 사용하여 PC를 설정

"ODBC setup" 을 채우는 예제:

Windows DSN name:   taejun
Description:        This is my love database
MySql Database:     love
Server:             194.216.84.21
User:               taejun
Password:           my_password
Port:
윈도우즈 DSN 이름 필드의 값은 윈도우즈 ODBC 셋업에서 유일한 값이다.

ODBC 셋업 화면에서 서버, 유저, 패스워드, 포드 필드에 값을 지정할 필요
는 없다. 그러나 지정을 해두면 그 값이 연결을 시도할 때  기본값으로 사
용된다. 그때마다 값을 바꿀 수 있다.

포트 번호가 주어지지 않으면, 기본 포트(3306)이 사용된다.
15.5 ODBC에서 AUTO_INCREMENT 컬럼의 값 가져오기

일반적인 문제는 INSERT 에서 어떻게  자동으로 생성되는 ID 값을  가져올
수  있느냐이다.   ODBC에서  다음과   같이  할   수  있다.   (auto  가
AUTO_INCREMENT 필드라고 가정):

INSERT INTO foo (auto,text) VALUES(NULL,'text');
SELECT LAST_INSERT_ID();

또는, 다른 테이블에 ID를 입력한다면 다음과 같이 할 수 있다:

INSERT INTO foo (auto,text) VALUES(NULL,'text');
INSERT INTO foo2 (id,text) VALUES(LAST_INSERT_ID(),'text');

다른 ODBC 애플리케이션(최소한 델파이와 액세스)에서 유용하게  사용하기
위해 다음의 질의를 새롭게 입력된 열을 찾는데 사용할 수 있다:

SELECT * FROM tbl_name WHERE auto IS NULL;

17. 일반적인 문제 해결 방법
17. 1. 데이터베이스 복사(복제?)
데이터베이스를 복사하는 가장 일반적인 방법은 업데이트 로그를 이용하는
것이다. 9.2 [The update log] 참고. 이 경우 마스터로 작동하는 데이터베
이스 하나(데이터가 변경된 곳)와 슬레이브로 작동하는 다른  하나의 데이
터베이스가   필요하다.  슬레이브를   업데이트하려면   단지  mysql   <
update_log을 하면 된다. 슬레이드 데이터베이스에 맞는 호스트, 유저, 패
스워드 옵션을 지정한다. 그리고 입력값으로 마스터 데이터베이스의  업데
이트 로그를 사용한다.

테이블에서 삭제한 것이 없다면, 마지막으로 복사를 한 후 (마지막으로 복
사한 시간을  비교) 테이블에서  입력되거나 변경된  열을 찾아내기  위해
TIMESTAMP 컬럼을 사용할 수 있고 미러링되는 데이터베이스에 변경된 자료
만 복사를 한다.

업데이트 로그(for deletes)와 timestamps(on both sides)를 같이  사용하
여 두가지 방법으로 업데이트하는 시스템을 만들 수 있다. 그러나 이런 경
우 두가지 ends(?)에서 변경된 동일한 데이터에서 충돌을 관리할  수 있어
야 한다. 아마도 어떤 것이 업데이트되었는지 결정하기 위해  예전 버전을
유지하고 싶을 것이다.
It is  possible to  make a  two-way updating  system  using both  the
update log (for deletes) and timestamps (on both sides). But  in that
case you must  be able to  handle conflicts when  the same data  have
been changed in both ends. You probably want to keep the  old version
to help with deciding what has been updated.

이 경우 복사(복제)는 SQL문으로 이루어지기 때문에, 데이터베이스를 업데
이트하는 문장에서 다음의 함수를 사용해서는 안된다; 여기에서는 원본 데
이터베이스와 동일한 값을 반환하지 않을 수 있다:

     DATABASE()
     GET_LOCK() and RELEASE_LOCK()
     RAND()
     USER(), SYSTEM_USER() or SESSION_USER()
     VERSION()

timestamp는 필요한 경우에 미러되는  곳으로 보내기지 때문에 모든  time
함수는 안전하게 사용할 수 있다. LAST_INSERT_ID() 또한 안전하게 사용할
수 있다.

All time functions are safe to use,  as the timestamp is sent to  the
mirror if needed.
LAST_INSERT_ID() is also safe to use.

17.2 데이터베이스 백업

mysql 테이블은 파일로 저장되기 때문에  백업하기가 쉽다.  일관된 백업
작업을 위해   관련된 테이블에   LOCK TABLES를  실행하자.  7.23  [LOCK
TABLES/UNLOCK TABLES syntax]를 참고. 단지 읽기 락만이  필요하다; 데이
터베이스 디렉토리의 파일 복사본을  만드는 동안에도 다른  스레드에서는
테이블에 질의를  계속 할  수 있다.  SQL 레벨의  백업을 하고자  한다면
SELECT INTO OUTFILE을 사용할 수 있다.

데이터베이스를 백업하는 다른 방법은 mysqldump 프로그램을 사용하는  것
이다:

데이터베이스에 대한 풀 백업 실행:
    
shell> mysqldump --tab=/path/to/some/dir --lock-tables --opt
     서버에서  업데이트를  하지  않는한  간단하게  모든  테이블  파일
(`*.frm', `*.ISD' , `*.ISM' 파일)을 복사할 수 있다. mysqld가 실행되고
있으면 멈추어야 한다. 그러고나서 --log-update 옵션으로 다시 시작하자.
''hostname.n'의 형식을 가진 로그 파일이 생성될 것이다. n은 mysqladmin
refresh, mysqladmin flush-logs, the FLUSH LOGS 문, 또는 서버를 재시작
할때마다 증가하는 숫자이다. 이렇게 생긴 로그 파일을  이용해 mysqldump
를 수행하고 나서 데이터베이스에 변화된 내용을 복사(복제)하는데 필요한
정보를 얻을 수 있다.
    
복원하고자 한다면, 먼저 isamchk  -r을 사용해 테이블을 복구하자.  모든
경우 99.9%가 제대로 수행된다. isamchk가 실패하면 다음의 과정대로 따르
자:
      
     기존의 mysqldump 백업을 복원한다.
     업데이트 로그에서 업데이트를 다시 수행하기 위해 다음의 명령을 실
행한다:
    
shell> ls -1 -t -r hostname.[0-9]* | xargs cat | mysql
ls는 정확한 순서대로 로그 파일을 가져오는데 사용된다.

또한 SELECT * INTO OUTFILE 'file_name' FROM tbl_name을  이용해 선택적
인 백업을 할  수 있으며 레코드가  중복되는 것을  방지하기 위해  LOAD
DATA INFILE 'file_name' REPLACE ...  을 이용해 복원할 수 있다. 이경우
에는 테이블에서 PRIMARY 키나  UNIQUE 키가 필요하다. REPLACE  키워드는
새로운 레코드에서 unique 키 값이  같은 이전의 레코드가 중복되는  경우
이전의 레코드를 새로운 레코드로 교체한다.

17.3 같은 머신에서 여러개의 mysqld 서버 실행하기

같은 머신에서 다중의 서버를 사용하길 원할 수 있다. 예를  들어, 이전의
서버를 그대로 두고 새로운 mysql 릴리즈를 테스팅하는 경우가 있을 수 있
다. 또는 다른 고객들에게 독립적인 mysql 설치를 제공하길 원하는 인터넷
서비스 제공자일 수 있다.

다중 서버를 사용하길 원하면, 가장 쉬운 방법은 다른 TCP/IP 포트와 소켓
파일로 서버를컴파일해서 서버에서 동일한 TCP/IP 포트나 소켓  파일을 청
취하지 않도록 할 수 있다.

이미 사용하고 있는 서버가 기본 포트와 소켓 파일로 구성되어  있다고 가
정해보자. 그러면 다음과 같은 명령으로 새로운 서버를 설정하자:
shell> ./configure  --with-tcp-port=port_number \
             --with-unix-socket=file_name \
             --prefix=/usr/local/mysql-3.22.9
여기서 port_number와 file_name은 기본 포트 숫자 및 소켓 파일의 경로와
는 달라야하며, --prefix 값은 현재 설치되어 있는 mysql과는 다른 디렉토
리를 지정해야 한다.

Here port_number and file_name should  be different than the  default
port number and socket file  pathname, and the --prefix value  should
specify an installation directory different than the one  under which
the existing MySQL installation is located.

다음의 명령으로 현재 실행되고 있는 mysql 서버의 소켓과  파일을 확인할
수 있다:
shell>; mysqladmin -h hostname --port port_number variables

사용하고 있는 포트에서 실행되고 있는 mysql 서버가 있다면, 소켓 이름을
포함해 mysql의 가장 중요한 설정 변수의 목록을 알 수 있다. 다중 mysqld
서버를 시작하고 중지시키기  위해 시스템의 초기화  스크립트(일반적으로
''mysql.server')를 수정해야 한다.

다른 포트와 소켓으로 서버를 시작하기 위해 새로운 mysql  서버를 재컴파
일할 필요는 없다.
safe_mysqld를 시작할 때 옵션으로 포트와 소켓을 지정할 수 있다:
shell>; /path/to/safe_mysqld --socket=file-name --port=file-name

동일한 데이터베이스 디렉토리에서 로그를 기록하도록 하면서 또  다른 서
버를 실행하고자 한다면, safe_mysqld 에 --log 와 --log-update  를 이용
해 로그 파일의 이름을 지정해 주어야 한다. 그렇지 않으면 두  서버가 같
은 로그 파일에 기록을 하려고 할 것이다.

주의 : 일반적으로 동일한 데이터베이스의 자료를 업데이트하는 두개의 서
버를 사용해서는 안된다! 운영체제에서 fault-free 시스템  로킹(locking)
을 지원하지 않는다면,  좋지 않은 결과를 보게 될 것이다.

(** fault-free란 정확히 무엇인지 모르겠군요. 아마도 락을  거는데 문제
가 있다면 그 락을 해제하는 것으로 보입니다. **)

두번재   서버에서  다른   데이터베이스  디렉토리를   사용하기  원하면
safe_mysqld 에 --datadir=path 옵션을 사용할 수 있다.
다른 포트에서 실행중인 mysql  서버에 접근하길 원한다면 다음의  방법을
사용할 수 있다:
        ㅇ   클라이언트에서  다음의   옵션을   가지고  시작.   --host
'hostname'         --port=port-numer    or   [--host    localhost]
--socket=file-name.
        ㅇ C나 펄 프로그램에서 mysql 서버에 접속할 때 포트와  소켓 인
자를 준 다.
        ㅇ    클라이언트를   시작하기    전에    MYSQL_UNIX_PORT   와    
                      MYSQL_TCP_PORT 환경 변수를 설정. 일반적으로 특정한 소
켓이나 포       트를 사용하면,  'login' 파일에 환경 변수를  설정하자.
12.1 [Programs]         참고.
        ㅇ 홈 디렉토리에서 '.my.cnf' 파일에 기본 소켓과 TCP/IP 소켓을
지정.   4.15.4 [Option files] 참고.

18 MySQL client tools
                                                and APIs

18.1 MySQL C API

C API 코드는 MYSQL에  함께 배포되며, 이것은 mysqlclient  라이브러리에
포함되어져 C 프로그램으로 하여금 데이터베이스에 접근할 수 있도록 해준
다. MySQL 소스  배포판의 많은 클라이언트들이  C로 코딩되어 있으며,  C
API를 사용하는 방법에 대한 설명을 찾고자 할 때 이 클라이언트들의 코드
를 참조할 수 있다.

18.2 C API datatypes

+--------------------+---------------------------------------------+
| 데이터 형식        |             설        명                    |
+--------------------+---------------------------------------------+
| MYSQL              | 데이터 베이스와의 하나의 연결을 다루는      |
|                    | 구조체로서 거의 모든 MySQL 함수들에서       |
|                    | 사용된다.                                   |
+--------------------+---------------------------------------------+
| MYSQL_RES          | 행을 리턴하는 질의들(SELECT, SHOW,          |
|                    | DESCRIBE, EXPLAIN)의 결과를 표현하는        |
|                    | 구조체로서 어떤 질의의 결과로서 리턴되는    |
|                    | 정보들을 결과 셋(result set)이라고 부른다.  |
+--------------------+---------------------------------------------+
| MYSQL_ROW          | 한 행의 데이터를 표현하는 데이터 형식이다.  |
|                    | 현재 이것은 counted byte string들의         |
|                    | 배열로 구현되어 있다. 필드값이 이진         |
|                    | 데이터를 포함할 수 있는 경우에는 값 내부에  |
|                    | null 바이트를 가지고 있을 수 있기 때문에    |
|                    | 이 데이터 형식을 null-terminated string으로 |
|                    | 다룰 수 없다.                               |
|                    | 행들은 mysql_fetch_row() 함수를 호출        |
|                    | 함으로써 얻어올 수 있다.                    |
+--------------------+---------------------------------------------+
| MYSQL_FIELD        | 필드 명, 형식, 크기와 같은 필드에 대한      |
|                    | 정보를 포함하는 구조체이다. 이 구조체의     |
|                    | 멤버들은 아래에서 상세히 기술된다.          |
|                    | 각각의 필드에 대하여 mysql_fetch_field()    |
|                    | 함수를 호출함으로써 각 필드에 대한 MYSQL_   |
|                    | FIELD 구조체의 값을 얻을 수 있다. 필드에    |
|                    | 들어있는 실제 값은 이 구조체의 멤버가       |
|                    | 아니며, 실제 데이터는 MYSQL_ROW 구조체에    |
|                    | 들어가게 된다.                              |
+--------------------+---------------------------------------------+
| MYSQL_FIELD_OFFSET | MySQL 필드 리스트에 들어가는 변위           |
|                    | (offset)을 표현하는 type-safe 형식이다      |
|                    | (mysql_field_seek() 함수에서 사용된다).     |
|                    | Offset은 행 내에서의 필드의 개수이며        |
|                    | 0에서 시작한다.                             |
+--------------------+---------------------------------------------+
| my_ulonglong       | 행들의 개수, mysql_affected_rows(),         |
|                    | mysql_num_rows(), mysql_insert_id()         |
|                    | 함수에서 사용되는 형식이다.                 |
|                    | 이 형식은 0에서 1.84e19까지의 값을          |
|                    | 가질 수 있다. 어떤 시스템에서는             |
|                    | my_ulonglong 형식의 값을 출력하는데         |
|                    | 문제가 있을 수 있는데, 이 경우에 이 값을    |
|                    | 출력하기 위해서는 이것을 unsigned long      |
|                    | 형식으로 변환한 후 '%lu' 프린트 형식을      |
|                    |사용한다. 예를 들면 다음과 같다:             |
|                    |                                             |
|                    |printf (Number of rows: %lu\n",              |
|                    |(unsigned long) mysql_num_rows(result));     |
+--------------------+---------------------------------------------+

MYSQL_FIELD 구조체는 다음과 같은 멤버들을 가진다:

char * name     - 필드의 이름

char * table
        - 이 필드가 계산된 필드(calculated field)가 아닌 경우,  이 필
드를 가 지고 있는 테이블의 이름. 계산된 필드인 경우 NULL  포인터를 가
진다

char * def
        - 이 필드의 디폴트 값(mysql_list_fields() 함수를 사용하는  경
우에만 설       정된다)

enum enum_field_types type
        - 필드의 형식(type). 필드 형식은  다음 테이블 값 중에  하나이
다.
+-----------------------+-------------------------------------------+
|  Type value           |  Type meaning                             | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_TINY       |  TYNYINT field                            | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_SHORT      |  SMALLINT field                           | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_LONG       |  INTEGER field                            | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_INT24      |  MEDIUMINT field                          | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_LONGLONG   |  BIGINT field                             | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_DECIMAL    |  DECIMAL or NUMERIC field                 | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_FLOAT      |  FLOAT field                              | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_DOUBLE     |  DOUBLE or REAL field                     | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_TIMESTAMP  |  TIMESTAMP field                          | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_DATE       |  DATE field                               | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_TIME       |  TIME field                               | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_DATETIME   |  DATETIME field                           | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_YEAR       |  YEAR field                               | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_STRING     |  String (CHAR or VARCHAR) field           | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_BLOD       |  BLOD or TEXT field (use max_length       | 
|                       |  to dete rmine the maximum length)        | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_SET        |  SET field                                | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_ENUM       |  ENUM field                               | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_NULL       |  NULL-type field                          | 
+-----------------------+-------------------------------------------+
|  FILE_TYPE_CHAR       |  Deprecated: use FIELD_TYPE_TINY instead  | 
+-----------------------+-------------------------------------------+
 

IS_NUM() 매크로(macro)를 이용하여  필드가 정수형(numeric  type)인가를
테스트할 수 있다.

IS_NUM()에 형식값(type value)를  인수로 넘겼을  때 필드가  정수형이면
'TRUE'를 리턴한다:

if (IS_NUM(field->type)) {
    printf("Field is numeric\n");
}

unsigned int length
        - 필드의 길이(width)

unsigned int max_length
        - 결과 셋에 대한 필드의 최대 길이(maximum width).

mysql_list_fields() 함수를 사용하는 경우 이 멤버에는 필드의 최대 길이
가 저장된다.

unsigned int flags
        - 필드에 대한 Different bit-flags. 플래그 값은 0  혹은 다음의
비트 셋 (bit set) 중에 하나이다.

+-----------------------+------------------------------------------+
|    플래그 값          |            의 미                         | 
+-----------------------+------------------------------------------+
|  NOT_NULL_FLAG        |  Field can't be NULL                     | 
+-----------------------+------------------------------------------+
|  PRI_KEY_FLAG         |  Field is part of a primary key          | 
+-----------------------+------------------------------------------+
|  UNIQUE_KEY_FLAG      |  Field is part of a unique key           | 
+-----------------------+------------------------------------------+
|  MULTIPLE_KEY_FLAG    |  Field is part of a key                  | 
+-----------------------+------------------------------------------+
|  UNSIGNED_FLAG        |  Field has the UNSIGNED attribute        | 
+-----------------------+------------------------------------------+
|  ZEROFILL_FLAG        |  Field has the ZEROFILL attribute        | 
+-----------------------+------------------------------------------+
|  BINARY_FLAG          |  Field has the BINARY attribute          | 
+-----------------------+------------------------------------------+
|  AUTO_INCREMENT_FLAG  |  Field has the AUTO_INCREMENT attribute  | 
+-----------------------+------------------------------------------+
|  ENUM_FLAG            |  Field is an ENUM (deprecated)           | 
+-----------------------+------------------------------------------+
|  BLOB_FLAG            |  Field is a BLOB or TEXT (deprecated)    | 
+-----------------------+------------------------------------------+
|  TIMESTAMP_FLAG       |  Field is a TIMESTAMP (deprecated)       | 
+-----------------------+------------------------------------------+

BLOB_FLAG, ENUM_FLAG, TIMESTAMP_FLAG 플래그들은 이들 플래그들이  자신
의 형식(type)의 속성을 표시하기 보다는 필드의 형식을  표시하기 때문에
이 플래그들을 사용하는  것은 좋지  않다. 그  대신 'field->type'  값을
FIELD_TYPE_BLOB, FIELD_TYPE_ENUM, 혹은 FIELD_TYPE_TIMESTAMP과  비교하
는 것이 좋다. 아래의 예제는 플래그 값들의 전형적인 사용법에 대하여 설
명한다:

if (field->flags & NOT_NULL_FLAG) {
        printf("Field can't be null\n");
}

플래그 값의 불리언 상태(boolean status)를 결정하는데 다음과 같은 매크
로(macro)들을 사용할 수 있다:
+----------------------+--------------------------------------------+
|  IS_NOT_NULL(flags)  |  이 필드가 'NOT NULL'로 정의되어 있으면 참 | 
+----------------------+--------------------------------------------+
|  IS_PRI_KEY(flags)   |  이 필드가 primary key인 경우에 참         | 
+----------------------+--------------------------------------------+
|  IS_BLOB(flags)      |  이 필드가 BLOB이나 TEXT인 경우게 참       | 
+----------------------+--------------------------------------------+

unsigned int decimals
        - 정수형 필드(numeric field)인 경우 자리수(number of decimal)

18.3 C API function overview

+--------------------+---------------------------------------------+
|  Function          |  Description                                |
+--------------------+---------------------------------------------+
|  Mysql_affected    |  가장 최근의 UPDATE, DELETE, INSERT         |
|  _rows()           |  질의에 의한 결과 행의 개수를 리턴한다.     |
+--------------------+---------------------------------------------+
|  Mysql_close()     |  서버와의 연결을 종료한다.                  |
+--------------------+---------------------------------------------+
|  Mysql_connect()   |  서버와 연결하며 이 함수를 사용하기         |
|                    |  보다는 mysql_real_connect() 함수를         |
|                    |  사용하는 것이 권장된다.                    |
+--------------------+---------------------------------------------+
|  Mysql_create      |  DB를 생성한다. 이 함수를 사용하기          |
|  _db()             |  보다는 SQL 명령어인 CREATE DATABASE        |
|                    |  문을 사용하는 것이 좋다.                   |
+--------------------+---------------------------------------------+
|  Mysql_data        |  질의 결과 셋에서 임의의 행을 찾는다.       |
|  _seek()           |                                             |
+--------------------+---------------------------------------------+
|  mysql_debug()     |  주어진 문자열로 DBUG_PUSH를 수행한다.      |
+--------------------+---------------------------------------------+
|  mysql_drop        |  DB를 드롭한다. 이 함수를 사용하기          |
|  _db()             |  보다는 SQL 명령어인 DROP DATABASE          |
|                    |  문을 사용하는 것이 좋다.                   |
+--------------------+---------------------------------------------+
|  mysql_dump        |  서버로 하여금 디버깅 정보를 로그에         |
|  _debug_info()     |  남기도록 한다                              |
+--------------------+---------------------------------------------+
|  mysql_eof()       |  결과셋의 마지막 행이 읽혀졌는가를          |
|                    |  표시한다. 이 함수를 사용하기보다는         |
|                    |  mysql_errno()나 mysql_error()함수가        |
|                    |  사용된다.                                  |
+--------------------+---------------------------------------------+
|  mysql_errno()     |  가장 최근에 수행된 MySQL 함수에 대한       |
|                    |  에러넘버를 리턴한다.                       |
+--------------------+---------------------------------------------+
|  mysql_error()     |  가장 최근에 수행된 MySQL 함수에 대한       |
|                    |  에러메세지를 리턴한다.                     |
+--------------------+---------------------------------------------+
|  mysql_escape      |  Escapes special characters in a            |
|  _string()         |  string for use in a SQL statement.         |
+--------------------+---------------------------------------------+
|  mysql_fetch       |  테이블의 다음 필드의 형식(type)을          |
|  _field()          |  리턴한다.                                  |
+--------------------+---------------------------------------------+
|  mysql_fetch       |  주어진 필드 번호에 대한 필드 형식을        |
|  _field_direct()   |  리턴한다.                                  |
+--------------------+---------------------------------------------+
|  mysql_fetch       |  모든 필드 구조에 대한 배열을 리턴한다.     |
|  _fields()         |                                             |
+--------------------+---------------------------------------------+
|  mysql_fetch       |  현재 행의 모든 컬럼들의 길이를 리턴한다.   |
|  _lengths()        |                                             |
+--------------------+---------------------------------------------+
|  mysql_fetch       |  결과셋으로부터 다음 행을 가져온다.         |
|  _row()            |                                             |
+--------------------+---------------------------------------------+
|  mysql_field       |  컬럼커서를 특정 컬럼으로 놓는다.           |
|  _seek()           |                                             |
+--------------------+---------------------------------------------+
|  mysql_free        |  결과셋에 의해 사용된 메모리를              |
|  _result()         |  해제한다.                                  |
+--------------------+---------------------------------------------+
|  mysql_get         |  클라이언트의 버전 정보를 리턴한다.         |
|  _client_info()    |                                             |
+--------------------+---------------------------------------------+
|  mysql_get         |  현재 연결에 대한 정보를 가진               |
|  _host_info()      |  문자열을 리턴한다.                         |
+--------------------+---------------------------------------------+
|  mysql_get         |  연결에 사용된 프로토콜 버전을              |
|  _proto_info()     |  리턴한다.                                  |
+--------------------+---------------------------------------------+
|  mysql_get         |  서버의 버전 넘버를 리턴한다.               |
|  _server_info()    |                                             |
+--------------------+---------------------------------------------+
|  mysql_info()      |  가장 최근에 수행된 질의에 대한             |
|                    |  정보를 리턴한다.                           |
+--------------------+---------------------------------------------+
|  mysql_init()      |  MYSQL 구조체를 생성하거나                  |
|                    |  초기화한다.                                |
+--------------------+---------------------------------------------+
|  mysql_insert      |  AUTO_INCREMENT 필드에 대하여               |
|  _id()             |  가장 최근에 생성된 ID를 리턴.              |
+--------------------+---------------------------------------------+
|  mysql_list        |  간단한 정규식에 의해 매칭되는              |
|  _dbs()            |  DB 이름들을 리턴한다.                      |
+--------------------+---------------------------------------------+
|  mysql_list        |  간단한 정규식에 의해 매칭되는              |
|  _fields()         |  필드 이름들을 리턴한다.                    |
+--------------------+---------------------------------------------+
|  mysql_list        |  현재 서버의 쓰레드들의 리스트를            |
|  _processes()      |  리턴한다.                                  |
+--------------------+---------------------------------------------+
|  mysql_list        |  간단한 정규식에 의해 매칭되는              |
|  _tables()         |  테이블 이름들을 리턴한다.                  |
+--------------------+---------------------------------------------+
|  mysql_num         |  결과셋 내의 컬럼 개수를 리턴한다.          |
|  _fields()         |                                             |
+--------------------+---------------------------------------------+
|  mysql_num         |  결과셋 내의 행 개수를 리턴한다             |
|  _rows()           |                                             |
+--------------------+---------------------------------------------+
|  mysql_ping()      |  서버와의 연결이 제대로 수행되고            |
|                    |  있나를 체크하며, 필요하면 서버와           |
|                    |  재연결한다.                                |
+--------------------+---------------------------------------------+
|  mysql_query()     |  Null terminated 문자열로 주어지는          |
|                    |  SQL 질의를 수행한다.                       |
+--------------------+---------------------------------------------+
|  mysql_real        |  MySQL 서버와 연결한다.                     |
|  _connect()        |                                             |
+--------------------+---------------------------------------------+
|  mysql_real        |  Counted 문자열로 주어진 SQL 질의를         |
|  _query()          |  수행한다.                                  |
+--------------------+---------------------------------------------+
|  mysql_reload()    |  서버로 하여금 grant 테이블을 다시          |
|                    |  리로드하게 한다.                           |
+--------------------+---------------------------------------------+
|  mysql_row_seek()  |  mysql_row_tell() 함수가 리턴한 값을        |
|                    |  가지고 결과셋 내의 한 행을 찾는다.         |
+--------------------+---------------------------------------------+
|  mysql_row_tell()  |  Row cursor 위치를 리턴한다.                |
+--------------------+---------------------------------------------+
|  mysql_select      |  특정 DB에 연결한다.                        |
|  _db()             |                                             |
+--------------------+---------------------------------------------+
|  mysql_shutdown()  |  DB 서버를 셧다운시킨다.                    |
+--------------------+---------------------------------------------+
|  mysql_stat()      |  서버의 상태를 표시하는 문자열을 리턴한다.  |
+--------------------+---------------------------------------------+
|  mysql_store       |  전체 결과셋을 클라이언트로 가져온다.       |
|  _result()         |                                             |
+--------------------+---------------------------------------------+
|  mysql_thread      |  현재 쓰레드의 ID를 리턴한다.               |
|  _id()             |                                             |
+--------------------+---------------------------------------------+
|  mysql_use         |  Initiates a row-by-row result set          |
|  _result()         |  retrieval.                                 |
+--------------------+---------------------------------------------+

서버에 연결할 때는 먼저  mysql_init() 함수를 호출함으로써  connection
handler를 초기화한 후, 그  handler와 hostname, user name,  password와
같은 다른 정보들을 가지고 mysql_real_connect() 함수를 호출한다.  모든
작업이 끝나면 mysql_close() 함수를 호출함으로써 연결을 종료한다.

연결이    이루어져    있는    동안    클라이언트는    mysql_query()나
mysql_real_query() 함수를 이용하여  서버에 SQL 질의를  보낼 수  있다.
mysql_query()는    질의가    null-terminated    string이어야    하며,
mysql_real_query()는 counted string이어야 한다. 만약 string이  바이너
리 데이터(중간에  NULL 바이트를   가질 수도 있는)인  경우에는  반드시
mysql_real_query()를 사용하여야 한다.

SELECT   질의가   아닌   질의(INSERT,  UPDATE,   DELETE)에   대해서는
mysql_affected_row() 함수를 호출함으로써 얼마나 많은 행이  affect되어
졌나를 알 수 있다.

SELECT 질의의 경우, select되어진  행들을 결과셋(result set)으로  얻게
된다(SHOW, DESCRIBE, EXPLAIN과  같은 문은 행을  리턴하는 것에  있어서
SELECT와 비슷하며, 이들은 SELECT 문과 동일하게 처리되어야 한다)

클라이언트가  결과셋을   처리하는  방법에는  두가지가   있다.  하나는
mysql_store_result() 함수를 호출함으로써 전체 결과 셋을 한번에 얻어오
는 것으로써, 이 함수는 질의에 의해 리턴된 모든 행들을 서버로부터 얻어
내어 클라이언트에 저장한다. 두번째는 mysql_use_result() 함수를 호출함
으로써 클라이언트가 행별로 결과셋을  얻어오는 것이다. 이 함수는  행을
얻어오기 위한작업을 초기화하기만 하며 실제로 서버로부터 행을 가져오지
는 않는다.

위 두가지 경우 모두, mysql_fetch_row() 함수를 호출함으로써  행들에 접
근하게 된다.
mysql_store_result()의 경우 mysql_fetch_row()는 서버로부터 이미 fetch
된 행들에 접근하며, mysql_use_result()의 경우, mysql_fetch_row()는 실
제로 서버로부터 행들을 얻어온다. 각 행의 데이터 값의 크기에 대한 정보
는 mysql_fetch_lengths() 함수를 호출함으로써 얻을 수 있다.

결과 셋을 이용한 작업이 끝나면 mysql_free_result()를 호출함으로써  결
과 셋을 위해 사용된 메모리를 해제한다.

이 두가지 retrieval 체계는 상호 보완적이다. 클라이언트  프로그램은 각
각의 요구에  따라 적절한  접근 방법을  선택하여야 한다.  실질적으로는
mysql_store_result()가 주로 사용된다.

mysql_store_result()의 이점은 행들이 클라이언트로 fetch된 이후에는 클
라이언트가   행들에   순차적으로   접근할   수   있을   뿐만   아니라
mysql_data_seek()나 mysql_row_seek()를 이용하여  결과 셋 안에서  현행
위치(current row position)를 변경함으로써 결과 셋 내에서  앞뒤로 움직
일 수 있다는 것이다. 또한 mysql_num_rows()를 호출함으로써 얼마나 많은
행들이 있나를 알 수도 있다. 다른 한편으로,  mysql_store_result()를 위
해 요구되는 메모리가 많기  때문에 out-of-memory 상태를 만날  가능성이
많다.

mysql_use_result()의 이점은 한번에 하나의 행만을 관리하기 때문에 결과
셋을 위한 메모리가 적게 필요하다는 것이며 또한 더 적은 메모리 할당 부
하로 인하여 더 속도가 빠를 수  있다. 단점은 서버를 tying up하는  것을
피하기 위하여 각 행을 빠르게 처리해주어야 하며 결과 셋  내에서 랜덤하
게 행들에 접근할 수 없고 단지 순차적으로만 접근할 수 있다.  또한 모든
행들을 받기 전에는 결과 셋 내에 얼마나 많은 행들이 있나를 알  수가 없
다. 결과 셋 중간에 찾고자 하는 정보를 찾았더라도 모든 행을  끝까지 받
고 있어야 한다.

API를 이용함으로써 질의가 SELECT인가 아닌가를 모른 상태에서도  클라이
언트가 질의에 적절히 대응할 수 있게 할 수 있는데,  이는 mysql_query()
(혹은 mysql_real_query())를 호출한 후에 mysql_store_result()를 호출함
으로써 가능하다. 만약 결과  셋이 성공(succeed)이면 질의가  SELECT이며
행들을 읽을  수  있다. 만약   실패이면 mysql_num_fields()를  호출하여
result가 정말로 기대되어지는가를 결정할 수 있으며, mysql_num_fields()
가 0을 리턴하면  질의는 아무 데이터도  리턴하지 않는다(이것은  질의가
INSERT, UPDATE, DELETE 등임을 의미한다). 만약 mysql_num_fields()가  0
이 아니면 질의는 반드시 행을 리턴하여야 하는데 리턴하지 않았음을 의미
하며, 이는 질의가 SELECT였으며 수행에 실패하였음을 알려준다.
mysql_store_result()과 mysql_use_result()은 결과 셋을 구성하는 필드들
에 대한  정보(필드의 개수,  이름,  형식 등)를  얻을 수   있게 해준다.
mysql_fetch_field()를 각각 호출하여 행 내에서의 필드 정보를  순차적으
로 접근하거나, mysql_fetch_field_direct()를 호출함으로써 얻은 행 내의
필드 번호를  통하여 접근할  수 있다.  현쟁의 필드   커서 위치(current
field cursor position)는 mysql_field_seek() 함수를 호출함으로써  변경
될 수  있다. Setting  the field  cursor affects  subsequent calls  to
mysql_fetch_field(). 또한 mysql_fetch_fields()를 호출함으로써  한꺼번
에 모든 필드 정보를 얻을 수도 있다.

에러를 찾고 리포팅하기  위해서 MySQL은 mysql_errno()와  mysql_error()
함수를 통하여 에러 정보에 접근할 수 있게 해준다. 이 두 함수는 가장 최
근에 invoke된 성공하거나 실패할 수 있는 함수에 대한 에러 코드 혹은 에
러 메시지를 리턴하며, 이를 통하여 언제 에러가 발생하고  그것이 무엇인
가를 알 수 있다.

18.4 C API function descriptions

아래의 함수 설명에 있어서 NULL로 표시한 인수나 리턴값은  C 프로그래밍
언어에서의 NULL의 의미이며, MySQL에서의 NULL 값을 의미하는  것이 아니
다.

어떤 값을 리턴하는 함수들은 보통 포인터나 정수를 리턴한다. 만약 별 다
른 표시가 없다면 포인터를 리턴하는 함수들은 성공하였을 때 NULL이 아닌
값을, 에러를 표시하기 위해서는 NULL 값을 리턴한다. 정수를 리턴하는 함
수의 경우에는 성공하였을 경우 0을, 에러인 경우에는 0이 아닌 값을 리턴
한다.

함수가 에러를 리턴한 경우에는 mysql_errno(), mysql_error() 함수를  이
용하여 에러를 체크할 수 있다.

18.4.1 mysql_affected_rows()
my_ulonglong mysql_affected_rows(MYSQL *mysql)

18.4.1.1 Description
마지막으로 수행된 UPDATE, DELETE, INSERT 질의에 의한 행의 수를 리턴하
며, 주로 UPDATE, DELETE, INSERT 문에 대한 mysql_query() 함수가 호출된
후 즉시 호출된다.

SELECT 문의 경우 mysql_affected_rows() 함수는 mysql_num_row()  함수처
럼 동작한다.
mysql_affected_rows() 함수는 현재 매크로(macro)로 구현되어 있다.

18.4.1.2 Return values
0보다 큰 정수는  affected되거나 retrieved된 행의  수를 의미한다.  0은
WHERE 조건에 합당하는 레코드가 없거나 아무 질의도 아직  수행되지 않았
음을 의미한다. -1은 질의가 에러를 리턴하였거나 혹은 SELECT  질의인 경
우 mysql_affected_rows() 함수가 mysql_store_result() 함수의 호출 이전
에 호출되었음을 의미한다.

18.4.1.3 Errors
없음.

18.4.1.4 Example
mysql_query(&mysql,"UPDATE   products    SET   cost=cost*1.25   WHERE
group=10");
printf("%d products updated",mysql_affected_rows(&mysql));

18.4.2 mysql_close()
void mysql_close(MYSQL *mysql)

18.4.2.1 Description
현재 열려있는   서버와의 연결을   끊는다. 만약  연결  핸들(connection
handle)이 mysql_init()이나 mysql_real_connect()에 의해서 자동으로  할
당되었다면   mysql_close()는   mysql에   의해  지정된   연결   핸들을
deallocation한다.

18.4.2.2 Return values
없음.

18.4.2.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.3 mysql_connect()
MYSQL *mysql_connect(MYSQL   *mysql, const   char *host,  const  char
*user, const char *passwd)

18.4.3.1 Description
이 함수를 사용하는 것은 권장되지 않으며, 대신 mysql_real_connect() 함
수가 주로 사용된다.

mysql_connect() 함수는 호스트에서 돌아가고 있는 MySQL DB  엔진과의 연
결을 시도한다.
Mysql_connect()는 mysql_get_client_info() 함수를 제외한 다른 모든 API
함수를 수행하기 이전에 반드시 성공적으로 수행되어져야 한다.

함수의 인수들이 가지는 의미는 mysql_real_connect() 함수와 동일하다.

18.4.3.2 Return values
mysql_real_connect() 함수와 동일.

18.4.3.3 Errors
mysql_real_connect() 함수와 동일.

18.4.4 mysql_create_db()
int mysql_create_db(MYSQL *mysql, const char *db)

18.4.4.1 Description
'db' 인수로 주어진 이름의 데이터베이스를 생성한다.

이 함수를 사용하는 것은 권장되지 않으며, 대신 'CREATE  DATABASE'와 같
은 SQL 문을 mysql_query() 함수를 이용하여 수행하는 것이 좋다.
 
18.4.4.2 Return values
데이터베이스가 성공적으로 생성된 경우 0, 에러가 발생한 경우  0이 아닌
값을 리턴한다.

18.4.4.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.4.4 Example
if(mysql_create_db(&mysql, "my_database"))
{
   fprintf(stderr, "Failed  to create  new database.   Error: %s\n",
mysql_error(&mysql));
}


18.4.5 mysql_data_seek()
void mysql_data_seek(MYSQL_RES *result, unsigned int offset)

18.4.5.1 Description
결과 셋 내에서 임의의 행을 찾는다. 이를 위해서는 결과 셋  구조체가 질
의에 의한 전체 결과를 포함하고 있어야 하며,  따라서 mysql_data_seek()
함수는     mysql_use_result()     함수와는     사용할     수     없고
mysql_store_result() 함수와 사용한다.

Offset 인수는 0에서 mysql_num_rows(result)-1 사이의 값을 가져야 한다.

18.4.5.2 Return values
None.

18.4.5.3 Errors
None.

18.4.6 mysql_debug()
void mysql_debug(char *debug)

18.4.6.1 Description
debug   인수로   주어진   문자열을   가지고   DBUG_PUSH를   수행한다.
mysql_debug() 함수는 Fred Fish debug 라이브러리를 사용한다. 이 함수를
사용하기 위해서는 반드시 클라이언트 라이브러리를 디버깅을  지원하도록
컴파일하여야 한다. 19.10 절에서  MySQL 서버의 디버깅에 대하여  논의하
며, 19.11절에서 MySQL 클라이언트의 디버깅에 대하여 논의한다.

18.4.6.2 Return values
None.

18.4.6.3 Errors
None.

18.4.6.4 Example
아래와 같은   호출은 클라이언트  라이브러리로  하여금  클라이언트의에
'/tmp/client.trace' 파일에 trace file을 생성하게 해준다:
mysql_debug("d:t:O,/tmp/client.trace");

18.4.7 mysql_drop_db()
int mysql_drop_db(MYSQL *mysql, const char *db)

18.4.7.1 Description
db 인수로 주어진 이름의 데이터베이스를 드롭시킨다.

이 함수를 사용하는 것은 권장되지 않으며, 대신 'DROP  DATABASE'와 같은
SQL 문을 mysql_query() 함수를 이용하여 수행하는 것이 좋다.

18.4.7.2 Return values
데이터베이스가 성공적으로 드롭된 경우 0을, 에러가 발생한 경우  0이 아
닌 값을 리턴한다.

18.4.7.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.
18.4.7.4 Example
if(mysql_drop_db(&mysql, "my_database"))
  fprintf(stderr,  "Failed  to  drop   the database:   Error:  %s\n",
mysql_error(&mysql));

18.4.8 mysql_dump_debug_info()
int mysql_dump_debug_info(MYSQL *mysql)

18.4.8.1 Description
서버로 하여금 로그에 디버깅 정보를 기록하게 한다. 연결되어  있는 사용
자(connected user)는 이 작업을 위한 권한을 가지고 있어야 한다.

18.4.8.2 Return values
명령이 성공하면 0, 에러가 발생하면 0이 아닌 값을 리턴한다.

18.4.8.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.9 mysql_eof()
my_bool mysql_eof(MYSQL_RES *result)

18.4.9.1 Description
이 함수를 사용하는  것은 권장되지 않으며,  대신 mysql_errno()  함수나
mysql_error() 함수를 사용한다.
 mysql_eof() 함수는 결과 셋의 마지막 행이 읽혀졌나를 판단한다.

mysql_store_result() 함수로부터 성공적으로 결과 셋을 얻었다면  클라이
언트는 한번의 작업으로 전체 셋을 얻게된다. 이  경우 mysql_fetch_row()
함수로부터 리턴되는 NULL은 항상 결과 셋의 마지막에  도달하였음을 의미
하게 되며 mysql_eof() 함수를 호출할 필요가 없다.
한편으로 mysql_use_result() 함수를 사용하여  결과 셋을 얻었다면  셋의
행들은
mysql_fetch_row() 함수를 호출할 때마다 서버로부터 하나씩 얻어진다. 이
과정 중에 연결 에러가 발생할  수 있기 때문에 myql_fetch_row()  함수가
리턴하는 NULL 값은 반드시 결과 셋의 마지막에  도달하였음을 의미한다고
볼 수 없다. 이 경우 mysql_eof() 함수를 사용하여 무엇이  일어났나를 결
정할 수 있다. 이때 mysql_eof() 함수는 결과 셋의 마지막에  도달한 경우
라면 0이 아닌 값을 리턴하며, 에러인 경우에는 0을 리턴하게 된다.

표준 mySQL 에러 함수인  mysql_errno()와 mysql_error() 함수가 더  많은
정보를 제공하기 때문에 이들을 사용하는 것이 좋다…

18.4.9.2 Return values
에러이면 0을, 결과 셋의 마지막에 도달하였으면 0이 아닌 값을 리턴한다.

18.4.9.3 Errors
None.

18.4.9.4 Example
다음은 mysql_eof()의 사용예를 보여준다:

mysql_query(&mysql,"SELECT * FROM some_table");
result = mysql_use_result(&mysql);
while((row = mysql_fetch_row(result)))
{
    // do something with data
}
if(!mysql_eof(result))  // mysql_fetch_row() failed due to an error
{
    fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}

However, you  can achieve  the same  effect with  the standard  MySQL
error functions:

mysql_query(&mysql,"SELECT * FROM some_table");
result = mysql_use_result(&mysql);
while((row = mysql_fetch_row(result)))
{
    // do something with data
}
if(mysql_errno(&mysql))  // mysql_fetch_row() failed due to an error
{
    fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}


18.4.10 mysql_errno()
unsigned int mysql_errno(MYSQL *mysql)

18.4.10.1 Description
mysql 인수에 의해 지정된 연결에 대하여 mysql_errno()는 가장 최근에 수
행된 API 함수에 대한 에러  코드를 리턴한다. 0을 리턴하면 아무  에러도
발생하지 않음을  의미한다. 클라이언트  에러 메시지   넘버들은 MYSQL의
'errmsg.h' 헤더  파일에 나열되어  있으며, 서버  에러 메시지  넘버들은
'mysqld_error.h' 파일에 있다.

18.4.10.2 Return values:
에러 코드 값을 리턴. 아무 에러도 없으면 0을 리턴한다.

18.4.10.3 Errors
None.

18.4.11 mysql_error()
char *mysql_error(MYSQL *mysql)

18.4.11.1 Description
mysql 인수에 의해 지정된 연결에 대하여 mysql_error()는 가장 최근에 수
행된 API 함수에 대한 에러  메시지를 리턴한다. 비어 있는  문자열("")을
리턴하면 아무 에러도 발생하지  않음을 의미한다. 이것은 다음의  두가지
테스트가 동일함을 의미한다:

if(mysql_errno(&mysql)) {
    // an error occurred
}

if(mysql_error(&mysql)[0] != '\0') {
    // an error occurred
}

클라이언트 에러 메시지의 언어는 MySQL 클라이언트 라이브러리를  재컴파
일함으로써 변경될 수 있다. 현재  몇가지 다른 언어로 된 에러  메시지를
선택할 수 있으며, 9.1 절에서는 MySQL에 의해 지원되는 언어들을 다룬다.

18.4.11.2 Return values
에러를 기술하는 문자열. 에러가 없는 경우에는 비어있는 문자열을 리턴한
다.

18.4.11.3 Errors
None.


18.4.12 mysql_escape_string()
unsigned int mysql_escape_string(char *to, const char *from, unsigned
int length)

18.4.12.1 Description
from 인수에 있는 문자열을 서버로 넘겨질 escaped SQL 문으로 인코딩하여
to 인수에 넣는다.

인코딩되어지는 문자들은 NUL(ASCII 0), `\n', `\r', `\', `'' 등이다(7.1
절에서는 문자열과 숫자들을 사용하는 방법에 대하여 다룬다).

from 인수인  문자열은 length   인수에서 주어진 바이트  수만큼의  길이
(terminating NULL을 제외한)를  가져야 한다. to  인수는 반드시  적어도
length 인수에서 주어진 길이의 2배에 1을 더한 만큼의 바이트가 할당되어
야 한다.  mysql_escape_string() 함수가   리턴할 때 to  인수의  내용은
null-terminated 문자열이 된다. 리턴값은 인코딩되어진 문자열의  길이이
며, 이것은 terminating null 문자가 포함되지 않은 길이다.

to 인수에 들어가는 문자열은 null-terminated 문자열이지만 이  문자열을
strlen() 함수나 strcpy() 함수에서 사용하여서는 안된다. 만약 from 인수
로 주어진 문자열이 null을 포함하고 있다면 mysql_escape_string()  함수
는 이 문자를 '\'을 앞에 붙여서 to 인수에 넣게 되며, 이것을  위의 함수
들은 여전히 terminating null로 인식하게 된다.

또한 이러한 내부적인 null 바이트는 mysql_query()에 의해서 terminating
null로 여겨지기 때문에  질의를 적절하게 수행할  수 없게 된다.  따라서
mysql_escape_query() 함수를 이용하여  질의를 만든 경우  mysql_query()
함수를 사용하기 보다는 mysql_real_query() 함수를 사용하는 것이 좋다.

18.4.12.2 Example
char query[1000],*end;

end = strmov(query, "INSERT INTO test_table values(");
*end++ = '\";
end += mysql_escape_string(query,"What's this",11);
*end++ = '\";
*end++ = ',';
*end++ = '\";
end += mysql_escape_string(query,"binary data: \0\r\n",16);
*end++ = '\";
*end++ = ')';

if (mysql_real_query(&mysql,query,(int) (end - query))) {
   fprintf(stderr, "Failed to insert row, Error: %s\n",
           mysql_error(&mysql));
}

예제에서 사용된 strmov() 함수는 mysqlclient 라이브러리에 포함되어  있
으며,   strcpy()  함수와   비슷하게  동작을   하지만   첫번째  인수의
terminating null에 대한 포인터를 리턴하는 것이 다르다.

18.4.12.3 Return values
terminating null 문자를 제외한 to 인수에 들어간 값의 길이

18.4.12.4 Errors
None.

18.4.13 mysql_fetch_field()
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result)


18.4.13.1 Description
결과 셋의 한 컬럼의 정의(definition)를 MYSQL_FIELD 구조체에 담아서 리
턴한다. 결과 셋의 모든 컬럼들에  대한 정보를 얻고자 하는 경우에는  이
함수를   반복하여  호출한다.   더  이상의   필드가   남아있지  않으면
mysql_fetch_field() 함수는 NULL을 리턴한다.

새로운 SELECT 질의가 수행될 때마다 mysql_fetch_field()는 새로운  첫번
째 필드에 대한 정보를  리턴하기 위해 초기화된다.  mysql_fetch_field()
함수에 의해 리턴되는 필드는 또한 mysql_field_seek() 함수의  호출에 의
해 영향을 받는다.

SELECT    문을    수행하기    위해    msyql_query()를    호출하였지만
mysql_store_result()를 호출하지 않은 경우, mysql_fetch_field()를 호출
하여 BLOB 필드의 길이를 요청하게  되면 MySQL은 디폴트 blob 길이인  8K
바이트를 리턴한다. 일단 한번 결과를 얻어오게 되면 field->max_length는
주어진 질의에 있는 컬럼의 가장 큰 값의 길이를 가지게 된다.

18.4.13.2 Return values
현재 컬럼에 대한 MYSQL_FIELD 구조체를  리턴하며, 더 이상 남은  필드가
없으면 NULL을 리턴한다.

18.4.13.3 Errors
None.

18.4.13.4 Example
MYSQL_FIELD *field;

while((field = mysql_fetch_field(result)))
{
    printf("field name %s\n", field->name);
}

18.4.14 mysql_fetch_fields()
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *result)

18.4.14.1 Description
결과 셋에 대한 모든 MYSQL_FIELD 구조체의 배열을 리턴한다. 각각의 구조
체는 결과 셋 내의 각 컬럼들에 대한 필드 정의에 대한 정보를  가지고 있
다.

18.4.14.2 Return values
결과 셋의 모든 컬럼들에 대한 MYSQL_FIELD 구조체의 배열

18.4.14.3 Errors
None.

18.4.14.4 Example
unsigned int num_fields;
unsigned int i;
MYSQL_FIELD *fields;

num_fields = mysql_num_fields(result);
fields = mysql_fetch_fields(result);
for(i = 0; i < num_fields; i++) {
   printf("Field %u is %s\n", i, fields[i].name);
}

18.4.15 mysql_fetch_field_direct()
MYSQL_FIELD *mysql_fetch_field_direct(MYSQL_RES *result, unsigned int
fieldnr)

18.4.15.1 Description
결과 셋 내에서 fieldnr 인수로 주어진 필드 번호의 필드에 대한  필드 정
의를 MYSQL_FIELD 구조체로 리턴한다. 이 함수를 이용하여  임의의 컬럼에
대한    정의를     얻어올    수    있다.     fieldnr    값은    0에서
mysql_num_fields(result)-1 사이의 값을 가져야 한다.

18.4.15.2 Return values
지정된 필드에 대한 MYSQL_FIELD 구조체

18.4.15.3 Errors
None.

18.4.15.4 Example
unsigned int num_fields;
unsigned int i;
MYSQL_FIELD *field;

num_fields = mysql_num_fields(result);
for(i = 0; i < num_fields; i++) {
    field = mysql_fetch_field_direct(result, i);
    printf("Field %u is %s\n", i, field->name);
}


18.4.16 mysql_fetch_lengths()
unsigned long *mysql_fetch_lengths(MYSQL_RES *result)

18.4.16.1 Description
결과 셋 내의 현재 행의 컬럼들의 길이를 리턴한다. 만약 필드  값들을 복
사하고자 하는 경우 이 길이 정보는 최적화(optimization)을  위해 유용하
게 사용할 수 있다. strlen() 함수를 호출할 필요가 없기  때문이다. 특히
결과 셋이 이진 데이터를 포함하고  있는 경우에는 strlen() 함수가  NULL
문자를 포함하고 있는 필드에 대하여 잘못된 결과를 리턴할 수  있기 때문
에 데이터의 크기를 결정하는데 이 함수를 사용하여야 한다.

빈 컬럼이나 NULL 값을 가지는 컬럼에  대한 길이는 0이 된다. 이  두가지
경우를 구분하는 방법은 mysql_fetch_row() 함수에 대한 설명을 참조한다.

18.4.16.2 Return values
각 컬럼(null  termination character를  제외한)의 크기를   가지고 있는
unsigned long 정수형의 배열. 에러가 발생한 경우에는 NULL을 리턴한다.

18.4.16.3 Errors
mysql_fetch_lengths()는  결과  셋의   현재 행에   대해서만  유효하다.
mysql_fetch_row() 함수를 호출하기 전에  이 함수를 호출하거나 결과  셋
내의 모든 행들을 얻어온 후에 호출하는 경우에 NULL을 리턴한다.

18.4.16.4 Example
MYSQL_ROW row;
unsigned long *lengths;
unsigned int num_fields;
unsigned int i;

row = mysql_fetch_row(result);
if (row) {
    num_fields = mysql_num_fields(result);
    lengths = mysql_fetch_lengths(result);
    for(i = 0; i < num_fields; i++) {
         printf("Column   %u  is   %lu   bytes   in  length.\n",   i,
lengths[i]);
    }
}

18.4.17 mysql_fetch_row()
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)

18.4.17.1 Description
결과 셋의 다음 행을 얻어온다. mysql_store_result() 함수를 호출한 후에
사용한 경우, 더 이상 얻어올 행들이 없으면  mysql_fetch_row()는 NULL을
리턴한다. mysql_use_result() 함수를 호출한 후에 사용한 경우,  더 이상
얻어올 행들이 없거나 에러가 발생하였을 때 NULL을 리턴한다.

행 내부의  값들의  개수는 mysql_num_fields(result)에  의해  주어진다.
mysql_fetch_row()의 호출에 의하여 행이  얻어졌을 때 행 내부의  값들은
row[0]부터 row[mysql_num_fields(result)-1]까지의 값들로 참조될 수  있
다. 행 내부에 NULL 값이 있는 경우에는 NULL 포인터로 표시된다.

행 내부의 필드 값들의 길이들은 mysql_fetch_lengths() 함수를  호출함으
로써 얻어질 수 잇다.
비어있는 필드나 NULL을 포함하는 필드들은 모두 0의 길이를 가지며, 필드
값에 대한 포인터를 체크함으로써 이 두가지 경우를 구분할 수 있다. 만약
포인터가 NULL이면 해당 필드가 NULL인 경우이며, 아니면 비어있는 필드임
을 의미한다.

18.4.17.2 Return values
다음 행에 대한 MYSQL_ROW 구조체. 더 이상 얻어올 행이 없거나 에러가 발
생한 경우 NULL을 리턴한다.
 
18.4.17.3 Errors
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.


18.4.17.4 Example
MYSQL_ROW row;
unsigned int num_fields;
unsigned int i;
num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result))) {
   unsigned long *lengths;
   lengths = mysql_fetch_lengths(result);
   for(i = 0; i < num_fields; i++) {
       printf("[%.*s] ", (int) lengths[i], row[i] ? row[i] : "NULL");
   }
   printf("\n");
}

18.4.18 mysql_field_seek()
MYSQL_FIELD_OFFSET         mysql_field_seek(MYSQL_RES        *result,
MYSQL_FIELD_OFFSET offset)

18.4.18.1 Description
필드   커서를   offset   인수로    주어진   위치로   설정한다.   다음
mysql_fetch_field() 함수의 호출은 해당 offset로 이동한 만큼에  해당되
는 컬럼에 대한 필드 정보를 얻어오게 된다.

행의 첫부분으로 보내기 위해서는  offset을 0으로 주고 함수를  호출하면
된다.

18.4.18.2 Return values
필드 커서의 이전 값(previous value)

18.4.18.3 Errors
None.

18.4.19 mysql_field_tell()
MYSQL_FIELD_OFFSET mysql_field_tell(MYSQL_RES *result)

18.4.19.1 Description
마지막 mysql_fetch_field() 함수에 의해 사용된 필드 커서의 위치를 알려
준다. 이 리턴값을 mysql_field_seek()  함수의 offset 인수로 사용할  수
있다.
18.4.19.2 Return values
필드 커서의 현재 변위(current offset)

18.4.19.3 Errors                None.

18.4.20 mysql_free_result()
void mysql_free_result(MYSQL_RES *result)

18.4.20.1 Description
mysql_store_result(), mysql_use_result(), mysql_list_dbs() 등에  의한
결과셋을 위해 할당되었던 메모리를  해제한다. 결과 셋을 가지고  작업을
끝내고 나면 반드시 mysql_free_result() 함수를 호출하여 메모리를  해제
해 주어야 한다.

18.4.20.2 Return values
None.

18.4.20.3 Errors
None.

18.4.21 mysql_get_client_info()
char *mysql_get_client_info(void)

18.4.21.1 Description
클라이언트 라이브러리 버전(client library version)에 대한 정보를 표시
하는 문자열을 리턴한다.

18.4.21.2 Return values
MySQL client library version을 표시하는 문자열

18.4.21.3 Errors                None.

18.4.22 mysql_get_host_info()
char *mysql_get_host_info(MYSQL *mysql)

18.4.22.1 Description
서버 호스트 네임을 포함하는 현재 사용되고 있는  연결(connection)의 형
식(type)을 기술하는 문자열을 리턴한다.
18.4.22.2 Return values
서버 호스트 네임(server host name)과 연결 형식(connection  type)을 나
타내는 문자열

18.4.22.3 Errors
None.

18.4.23 mysql_get_proto_info()
unsigned int mysql_get_proto_info(MYSQL *mysql)

18.4.23.1 Description
현재 연결에 사용되고 있는 프로토콜의 버전을 리턴한다.

18.4.23.2 Return values
현재 연결에 사용되고 있는 프로토콜 버전을 나타내는 unsigned integer

18.4.23.3 Errors
None.

18.4.24 mysql_get_server_info()
char *mysql_get_server_info(MYSQL *mysql)

18.4.24.1 Description
서버 버전 넘버(server version number)를 표시하는 문자열을 리턴한다.

18.4.24.2 Return values
서버 버전 넘버를 표시하는 문자열

18.4.24.3 Errors                None.

18.4.25 mysql_info()
char *mysql_info(MYSQL *mysql)

18.4.25.1 Description
가장 최근에 수행된 질읭 peogks 정보를 제공하는 문자열을  리턴한다. 문
자열의 형식(format)은 질의의 타입에 따라 달라지며, 아래와  같다. 문자
열은 질의에 대한 적절한 값들을 포함하게 된다.
INSERT INTO ... SELECT ...
String format: Records: 100 Duplicates: 0 Warnings: 0
INSERT INTO ... VALUES (...),(...),(...)...
String format: Records: 3 Duplicates: 0 Warnings: 0
LOAD DATA INFILE ...
String format: Records: 1 Deleted: 0 Skipped: 0 Warnings: 0
ALTER TABLE
String format: Records: 3 Duplicates: 0 Warnings: 0

18.4.25.2 Return values
가장 최근에 수행된 질의에 대한 부가적인 정보를 표현하는  문장열. 해당
질의에 대하여 아무런 정보도 얻을 수 없는 경우 NULL을 리턴한다.

18.4.25.3 Errors
None.

18.4.26 mysql_init()
MYSQL *mysql_init(MYSQL *mysql)

18.4.26.1 Description
mysql_real_connect() 함수를 위한 MYSQL 객체를 할당(allocate)하거나 초
기화(initialize)한다.
만약 mysql 인수가 NULL  포인터이면 mysql_init() 함수는 새로운  객체를
할당, 초기화하여 그 객체를 리턴한다. NULL 포인터가 아니면 객체는 초기
화된 후 객체의 주소가 리턴된다.
만약 mysql_init() 함수가 새로운 객체를 할당하게 되면 그 객체는 연결을
닫기 위해 mysql_close() 함수가 호출될 때 해제되게 된다.

18.4.26.2 Return values
초기화된 MYSQL * handle. 새로운 객체를 할당하기 위한  메모리가 부족한
경우 NULL을 리턴한다.

18.4.26.3 Errors
메모리가 부족한 경우 NULL이 리턴된다.

18.4.27 mysql_insert_id()
my_ulonglong mysql_insert_id(MYSQL *mysql)
18.4.27.1 Description
AUTO_INCREMENT 필드에 대하여 가장 최근에 생성된 ID를 리턴한다.  이 함
수는
AUTO_INCREMENT 필드를 포함하는 테이블에  INSERT 질의를 수행한  이후에
사용된다.

18.4.27.2 Return values
가장 최근에 갱신된 AUTO_INCREMENT 필드의 값

18.4.27.3 Errors
None.

18.4.28 mysql_kill()
int mysql_kill(MYSQL *mysql, unsigned long pid)

18.4.28.1 Description
서버로 하여금 pid 인수로 주어진 쓰레드(thread)를 죽이도록(kill)  요청
한다.

18.4.28.2 Return values
성공인 경우 0을, 에러가 발생한 경우 0이 아닌 값을 리턴한다.

18.4.28.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.29 mysql_list_dbs()
MYSQL_RES *mysql_list_dbs(MYSQL *mysql, const char *wild)

18.4.29.1 Description
wild 인수로 지정된 간단한 정규식(simple regular expression)에  매치되
는 서버 상의 데이터베이스 이름들을 포함하고 있는 결과  셋을 리턴한다.
wild 인수는 '%', '_'와 같은  와일드카드 문자를 포함할 수 있으며  모든
데이터베이스  이름을   얻고자 하는   경우  NULL을   지정할 수   있다.
mysql_list_dbs() 함수를 호출하는 것은 'SHOW databases [LIKE  wild]'와
같은 질의를 수행하는 것과 비슷하다.

반드시 mysql_free_result() 함수를 이용하여 결과 셋을 해제시켜  주어야
한다.

18.4.29.2 Return values
성공한 경우 MYSQL_RES 결과 셋을  리턴하며, 에러인 경우 NULL을  리턴한
다.

18.4.29.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_OUT_OF_MEMORY
        Out of memory.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.30 mysql_list_fields()
MYSQL_RES *mysql_list_fields(MYSQL *mysql,  const char *table,  const
char *wild)

18.4.30.1 Description
wild 인수로 지정된 간단한 정규식(simple regular expression)에  매치되
는 테이블 상의 필드명들을 포함하고 있는 결과 셋을 리턴한다. wild 인수
는 '%', '_'와 같은 와일드카드  문자를 포함할 수 있으며 모든  필드명을
얻고자 하는 경우 NULL을 지정할 수 있다. mysql_list_fields() 함수를 호
출하는 것은 'SHOW fields FROM table [LIKE wild]'와 같은 질의를 수행하
는 것과 비슷하다.

반드시 mysql_free_result() 함수를 이용하여 결과 셋을 해제시켜  주어야
한다.

18.4.30.2 Return values
성공한 경우 MYSQL_RES 결과 셋을  리턴하며, 에러인 경우 NULL을  리턴한
다.

18.4.30.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.31 mysql_list_processes()
MYSQL_RES *mysql_list_processes(MYSQL *mysql)

18.4.31.1 Description
현재의   서버  쓰레드들을   기술하는  결과   셋을   리턴한다.  이것은
'mysqladmin processlist'에 의한 결과와 동일한 정보를 보여준다.
반드시 mysql_free_result() 함수를 이용하여 결과 셋을 해제시켜  주어야
한다.

18.4.31.2 Return values
성공한 경우 MYSQL_RES 결과 셋을  리턴하며, 에러인 경우 NULL을  리턴한
다.

18.4.31.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.32 mysql_list_tables()
MYSQL_RES *mysql_list_tables(MYSQL *mysql, const char *wild)

18.4.32.1 Description
wild 인수로 지정된 간단한 정규식(simple regular expression)에  매치되
는 현재 데이터베이스 상의 테이블명들을 포함하고 있는 결과 셋을 리턴한
다. wild 인수는 '%', '_'와 같은 와일드카드 문자를 포함할 수 있으며 모
든 테이블명을 얻고자 하는 경우 NULL을 지정할 수 있다.
mysql_list_tables() 함수를 호출하는 것은 'SHOW tables [LIKE  wild]'와
같은 질의를 수행하는 것과 비슷하다.

반드시 mysql_free_result() 함수를 이용하여 결과 셋을 해제시켜  주어야
한다.

18.4.32.2 Return values
성공한 경우 MYSQL_RES 결과 셋을  리턴하며, 에러인 경우 NULL을  리턴한
다.

18.4.32.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.33 mysql_num_fields()
unsigned int mysql_num_fields(MYSQL_RES *result)
혹은,
unsigned int mysql_num_fields(MYSQL *mysql)

18.4.33.1 Description
결과 셋 내의 컬럼의 개수를 리턴한다.

mysql_num_fields()를 호출할 때 결과 셋에 대한 포인터 혹은 연결 핸들을
가지고 호출할 수 있다. 만약 mysql_store_result() 함수가 NULL을 리턴한
경우(즉 결과 셋 포인터를 얻어올 수  없을 때) 연결 핸들을 사용하게  되
며,     이     경우    mysql_num_fields()     함수를     호출함으로써
mysql_store_result() 함수가 비어있지 않은 결과(non-empty result)를 생
성하였나의 여부를 결정할 수 있다. 이것은 클라이언트 프로그램으로 하여
금 질의가 SELECT (혹은 SELECT-like) 문인가 아닌가를 알지  못하는 상태
에서 적절한 행동을 취할 수 있도록 해준다. 아래에 예가 있다.
18.4.33.2 Return values
결과 셋 내의 필드 개수를 표시하는 unsigned integer.

18.4.33.3 Errors                None.
18.4.33.4 Example
MYSQL_RES *result;
unsigned int num_fields;
unsigned int num_rows;

if (mysql_query(&mysql,query_string)) {
    // error
}
else {   // query succeeded, process any data returned by it
    result = mysql_store_result(&mysql);
    if (result) {   // there are rows
          num_fields = mysql_num_fields(result);
        // retrieve rows, then call mysql_free_result(result)
    }
    else {    // mysql_store_result()  returned nothing;  should it
have?
        if(mysql_num_fields(&mysql) == 0) {
            // query does not return data
            // (it was not a SELECT)
            num_rows = mysql_affected_rows(&mysql);
        }
        else {   // mysql_store_result() should have returned data
            fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
        }
    }
}

또 다른 대안은 mysql_num_fields(&mysql) 대신에  mysql_errno(&mysql)을
호출하는 것이다.
이 경우, 질의가 SELECT 문인가를 msyql_num_fields()의 리턴값으로  결정
하기보다는 mysql_store_result()로부터의 에러를 직접 체크하게 된다.


18.4.34 mysql_num_rows()
my_ulonglong mysql_num_rows(MYSQL_RES *result)

18.4.34.1 Description
결과 셋 내의 행의 개수를 리턴한다.

mysql_num_rows()   함수의  사용   여부는   결과   셋을  얻기   위해서
mysql_store_result() 함수를 사용하는가  mysql_use_result() 함수를 사
용하는가에  달려있다.  만약  mysql_store_result()  함수를  사용한다면
mysql_num_rows() 함수는 즉시 호출될 수 있다. 만약  mysql_use_result()
함수를 사용한다면 mysql_num_rows()  함수는 결과 셋  내의 모든  행들이
retrieve되기 이전에는 올바
른 값을 리턴하지 못한다.

18.4.34.2 Return values
결과 셋 내의 행의 개수
 
18.4.34.3 Errors
None.

18.4.35 mysql_ping()
int mysql_ping(MYSQL *mysql)

18.4.35.1 Description
서버에 대한 연결이 제대로 동작하고 있나를 확인하며, 만약  연결이 끊어
진 경우에는 자동으로 재연결을 시동한다.

이 함수는 클라이언트가 너무 오랫동안 쉬고 있을 때 서버가  연결을 끊었
나를 체크하고 필요하다면 다시 연결하기 위해 사용될 수 있다.

18.4.35.2 Return values
서버가 살아있으면 0, 에러인 경우 0이 아닌 값을 리턴한다.

18.4.35.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.36 mysql_query()
int mysql_query(MYSQL *mysql, const char *query)

18.4.36.1 Description
null-terminated 문자열인 query 인수에 지정된 SQL 질의를  수행한다. 질
의는 반드시 하나의 SQL 문으로 이루어져야 하며, 질의문에 세미콜론(;)이
나 '\g'를 붙여서는 안된다.

mysql_query()는 이진 데이터를 포함하는 질의에는 사용할 수  없다. 이진
데이터는 중간에 NULL 문자를 포함할 수 있으며 이경우  mysql_query() 함
수는 거기서  질의 문자열이   끝난 것으로 인식하기  때문에  이경우에는
mysql_real_query() 함수를 사용하여야 한다.

18.4.36.2 Return values
성공이면 0, 에러이면 0이 아닌 값을 리턴.

18.4.36.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.37 mysql_real_connect()
MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const  char
*user, const  char *passwd,  const char  *db, uint  port, const  char
*unix_socket, uint client_flag)

18.4.37.1 Description
mysql_real_connect() 함수는 호스트에서 돌아가고 있는 MySQL 데이터베이
스      엔진에      연결을      시도한다.      mysql_real_connect()는
mysql_get_client_info() 함수를 제외한 다른  모든 API 함수를  수행하기
이전에 반드시 성공적으로 수행되어져야 한다.

사용하는 인수들은 다음과 같다:

mysql 인수는 MYSQL  connection 구조체이거나 NULL이  될 수 있다.  만약
mysql 인수가 NULL이면 C API는 자동으로 연결 구조체를 위한 메모리를 할
당하고 mysql_close()가 호출될 때 이를 해제한다. 이경우  단점은 연결이
실패한 경우  에러 메시지를  얻어올 수   없다는 것이다(mysql_errno()나
mysql_error() 함수로부터 에러 메시지를 얻기 위해서는 유효한  MYSQL 포
인터를 제공하여야  한다). mysql  인수가 NULL이  아니면 이미  존재하는
MYSQL 구조체의 주소가 되어야 한다. 이 경우, mysql_real_connect() 함수
를 호출하기 전에 반드시  mysql_init() 함수를 호출하여 MYSQL  구조체를
초기화해 주어야 한다. 다음의 예를 보라.

host 인수는 호스트명이 되거나 IP 어드레스가 될 수 있다. 만약  host 인
수가 NULL이거나 "localhost"인 경우 로컬 호스트로의 접속으로 간주된다.
만약 OS가 소켓(socket-Unix)이나 파이프(pipe-Win32)를 지원한다면  서버
와의 연결을 위하여 TCP/IP 대신 이들을 사용할 수 있다.

user 인수에는 사용자의  MySQL 로그인 아이디를  넣어준다. User  인수가
NULL이면 현재 사용자로 간주된다. Unix 환경에서는 현재 시스템 로그인명
이 사용되며, Windows ODBC에서는 현재 사용자명이 명확히  지정되어야 한
다. 15.4절에서는 ODBC 관리 프로그램에서 각 필드들을 어떻게  채울 것인
가에 대하여 논의한다.

passwd 인수에는 사용자에 대한 패스워드를 넣어준다. 만약 passwd가 NULL
이면 user 테이블에서 패스워드 필드가 비어있는 사용자들만이  매치를 위
해 체크되어진다. 이것은 데이터베이스 관리자로 하여금 데이터베이스  사
용자가 자신의 패스워드를 명시하였나에 따라서 다른 권한을  가지도록 하
는  MySQL  권한  시스템을   설정할 수   있도록 해준다.   주의할  것은
mysql_real_connect() 함수를 호출하기 전에 패스워드를 암호화하려고  하
지 말아야 한다는 것이며, 패스워드의 암호화는 클라이언트  API에 의하여
자동으로 수행된다.

db 인수는 사용할 데이터베이스의 이름을 넣어준다. 만약 db  인수가 NULL
이면 디폴트 데이터베이스에 연결을 시도하게 된다. port 인수가 0이 아니
면 port 인수에 명시된 포트 번호를 TCP/IP 연결을 위한 포트 번호로 사용
하게 된다. host 인수가 연결의 형식을 결정한다는 것에 주의한다.

unix_socket 인수가 NULL이 아니면  이 인수 문자열이 사용될  socket이나
named pipe가 된다. client_flag 값은  보통 0이지만 매우 특별한  경우에
다음의 플래그들의 조합으로 설정할 수 있다.

+---------------------+----------------------------------------+
|  Flag name          |  Flag meaning                          | 
+---------------------+----------------------------------------+
|  CLIENT_FOUND_ROWS  |  Return the number of found rows,      | 
|                     |  not the number of affected rows.      | 
+---------------------+----------------------------------------+
|  CLIENT_NO_SCHEMA   |  Don't allow the db_name.tbl_name.     | 
|                     |  col_name syntax. This is for ODBC;    | 
|                     |  it causes the parser to generate      | 
|                     |  an error if you use that syntax,      | 
|                     |  which is is useful for trapping bugs  | 
|                     |  in some ODBC programs.                | 
+---------------------+----------------------------------------+
|  CLIENT_COMPRESS    |  Use compression protocol              | 
+---------------------+----------------------------------------+
|  CLIENT_ODBC        |  The client is an ODBC client.         | 
|                     |  This changes mysqld to be more        | 
|                     |  ODBC-friendly.                        | 
+---------------------+----------------------------------------+

18.4.37.2 Return values
연결이 성공하면 MYSQL * 연결 핸들(connection handle)을 리턴하며, 연결
에 실패하면 NULL을 리턴한다. 연결이 성공한 경우 리턴 값은 첫번째 인수
에 NULL을 지정하지 않았다면 그 인수와 동일하다.

18.4.37.3 Errors
CR_CONN_HOST_ERROR
        MySQL 서버로의 연결에 실패
CR_CONNECTION_ERROR
        로컬 MySQL 서버로의 연결에 실패
CR_IPSOCK_ERROR
        IP 소켓의 생성에 실패
CR_OUT_OF_MEMORY
        메모리가 없음(Out of memory).
CR_SOCKET_CREATE_ERROR
        Unix 소켓의 생성에 실패
CR_UNKNOWN_HOST
        해당 호스트명에 대한 IP 주소를 찾을 수 없음.
CR_VERSION_ERROR
        일치하지 않는 프로토콜 버전을 사용하는 클라이언트  라이브러리
를 가지 고 서버에 접속  하려 시도함으로 인하여 프로토콜 불일치가 발생
함.

        이것은 매우 오래된 클라이언트 라이브러리를 사용하여 새로운 버
전의 서 버에 접속하고자 하는 경우에 발생한다. 이런 경우에는 서버를 시
동할 때         '--old-protocol' 옵션을 넣어서 시동하면 해결할  수 있
다.
CR_NAMEDPIPEOPEN_ERROR;
        Win32 환경에서 named pipe를 생성하는데 실패함.
CR_NAMEDPIPEWAIT_ERROR;
        Win32 환경에서 named pipe를 기다리는데 실패함.
CR_NAMEDPIPESETSTATE_ERROR;
        Win32 환경에서 pipe 핸들러를 얻는데 실패함.

18.4.37.4 Example
MYSQL mysql;

mysql_init(&mysql);
if
(!mysql_real_connect(&mysql,"host","user","passwd","database",0,NULL,
0)) {
    fprintf(stderr, "Failed  to connect  to database:   Error: %s\n",
mysql_error(&mysql));
}

18.4.38 mysql_real_query()
int mysql_real_query(MYSQL *mysql,  const char  *query, unsigned  int
length)

18.4.38.1 Description
query 인수에 지정된 SQL 질의를  수행하며, query 인수는 length  인수에
지정된 길이를 가져야 한다. 질의는 하나의 SQL 문으로  이루어져야 하며,
세미콜론이나 '\g'를 SQL문에 붙여서는 안된다.

이진 데이터를 포함하는 질의  데이터인 경우 mysql_query() 함수가  아닌
mysql_real_query() 함수를 사용하여야 한다. 또한  mysql_real_query()함
수는 질의문의 길이를 얻기 위한  strlen() 함수를 수행하지 않기  때문에
mysql_query() 함수에 비하여 수행 속도가 빠르다.

18.4.38.2 Return values
질의에 성공하면 0, 에러가 발생하면 0이 아닌 값을 리턴한다.

18.4.38.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.39 mysql_reload()
int mysql_reload(MYSQL *mysql)

18.4.39.1 Description
MySQL 서버로 하여금 grant 테이블을 다시 읽도록 요청한다.  현재 연결되
어 있는 사용자는 반드시 권한 설정을 다시 읽어야 한다.

이 함수를 사용하는 것은 권장되지 않으며, 대신 'FLUSH  PRIVILEGES' SQL
문을
mysql_query() 함수를 이용하여 수행하는 것이 좋다.

18.4.39.2 Return values
성공하면 0, 에러면 0이 아닌 값을 리턴한다.
 
18.4.39.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.40 mysql_row_seek()
MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES  *result,  MYSQL_ROW_OFFSET
offset)

18.4.40.1 Description
결과 셋 내에 행 커서(row cursor)를 임의의 위치로 설정한다.  이를 위해
서는 결과 셋  구조체가 전체  질의 결과를 가지고  있어야 하며,  따라서
mysql_row_seek() 함수는  mysql_use_result() 함수와는  사용할 수  없고
mysql_store_result() 함수와 사용할 수 있다.
offset 인수는 mysql_row_tell() 함수나 mysql_row_seek() 함수를  호출한
리턴값이 되어야 하며, 이 값은 단순한 행 번호를 의미하는  것이 아니다.
만약 행  번호를 이용하여  결과 셋  내에서 행을  찾고자 하는  경우에는
mysql_data_seek() 함수를 사용하여야 한다.

18.4.40.2 Return values
행    커서(row   cursor)의    이전값(previous    value).   이    값은
mysql_row_seek() 함수의 인수로 넘겨진다.

18.4.40.3 Errors
None.

18.4.41 mysql_row_tell()
MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *result)

18.4.41.1 Description
마지막 mysql_fetch_row() 함수 호출에 의한 행 커서의 현재  위치를 리턴
한다. 이값은 mysql_row_seek() 함수의 인수로 넘겨진다.

mysql_row_tell() 함수는 반드시 mysql_store_result() 함수 뒤에  사용되
어야 하며,
mysql_use_result() 함수 뒤에는 사용할 수 없다.

18.4.41.2 Return values
행 커서의 현재 변위(The current offset of the row cursor).

18.4.41.3 Errors
None.

18.4.42 mysql_select_db()
int mysql_select_db(MYSQL *mysql, const char *db)

18.4.42.1 Description
db 인수에 지정한 데이터베이스를 mysql 인수가 가진 연결에 대한 현재 작
업 데이터베이스가 되도록 한다. 이후의 질의에 대해서 여기서  지정한 데
이터베이스가 데이터베이스를 명시하지 않은  테이블 참조에 대한  디폴트
데이터베이스로 사용된다.

연결되어 있는 사용자가 db 인수에 지정된 데이터베이스를  사용할 권한을
가지고 있다는 것이 인증되지 않으면 mysql_select_db() 함수는 실패한다.

18.4.42.2 Return values
성공이면 0, 에러이면 0이 아닌 값

18.4.42.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.43 mysql_shutdown()
int mysql_shutdown(MYSQL *mysql)

18.4.43.1 Description
데이터베이스 서버로 하여금 셧다운되도록 요청하며, 현재 연결된  사용자
가 셧다운 권한을 가지고 있어야 한다.

18.4.43.2 Return values
성공이면 0, 에러이면 0이 아닌 값을 리턴한다.

18.4.43.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.


18.4.44 mysql_stat()
char *mysql_stat(MYSQL *mysql)

18.4.44.1 Description
'mysqladmin status' 명령어에 의해 제공되는 정보와 비슷한  정보를 포함
하고 있는 문자열을 리턴한다. 이 문자열에는 구동시간(uptime), 실행되고
있는 쓰레드 수, 질문(question), 재시동(reload), 열려있는 테이블  등에
대한 정보가 포함되어 있다.

18.4.44.2 Return values
서버 상태를 기술하는 문자열. 에러이면 NULL을 리턴한다.

18.4.44.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.45 mysql_store_result()
MYSQL_RES *mysql_store_result(MYSQL *mysql)

18.4.45.1 Description
데이터를 얻어오는 질의(SELECT, SHOW, DESCRIBE, EXPLAIN)의 수행에 대하
여 mysql_store_result() 혹은 mysql_use_result() 함수를 호출한다.

mysql_store_result() 함수는 전체  질의 결과를 클라이언트로  읽어오며,
MYSQL_RES 구조체를 할당하고 결과를 이 구조체에 넣어준다. 아무 행도 리
턴되지 않으면 빈  결과 셋(empty result  set)이 리턴된다(빈 결과  셋은
NULL 리턴 값과는 다르다).

mysql_store_result() 함수를 호출하면 mysql_num_rows() 함수를 호출하여
결과 셋 내에 얼마나 많은 행들이 있는가를 알아 볼 수 있다.

mysql_fetch_row() 함수를 호출하여 결과 셋으로부터 행들을 얻어올 수 있
으며, 혹은 mysql_row_seek()와 mysql_row_tell() 함수를 통하여 결과  셋
내에서의 현행 위치를 얻거나 위치를 설정해줄 수 있다.

결과셋을 통한 작업이 끝나면 반드시 mysql_free_result() 함수를  호출해
준다.

18.4.45.2 Return values
결과가 저장된 MYSQL_RES 결과 구조체(result structure). 에러이면  NULL
을 리턴.

18.4.45.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_OUT_OF_MEMORY
        Out of memory.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.46 mysql_thread_id()
unsigned long mysql_thread_id(MYSQL *mysql)

18.4.46.1 Description
현재 연결에 대한 쓰레드 ID를 리턴한다. 이 값은 mysql_kill() 함수의 인
수로 사용되어 쓰래드를 kill 하는데 사용될 수 있다.

18.4.46.2 Return values
현재 연결에 대한 쓰레드 ID.

18.4.46.3 Errors
None.

18.4.47 mysql_use_result()
MYSQL_RES *mysql_use_result(MYSQL *mysql)
18.4.47.1 Description
데이터를 얻어오는 질의(SELECT, SHOW, DESCRIBE, EXPLAIN)의 수행에 대하
여 mysql_store_result() 혹은 mysql_use_result() 함수를 호출한다.

mysql_use_result() 함수는   결과 셋을  얻어오는  작업을  초기화하지만
mysql_store_result() 함수처럼 실제로 결과 셋을 읽어서  클라이언트에게
넘겨주지는 않는다. 대신 mysql_fetch_row() 함수를 각각 호출함으로써 각
행이 하나씩 얻어진다. 이것은 질의의 결과를 임시 테이블이나  지역 버퍼
에   저장하지    않고   서버로부터    직접   읽어오게    되며,   이는
mysql_store_result() 함수에 비하여 좀더 빠르고 훨씬 적은  양의 메모리
를 차지한다. 클라이언트는 현재 행에 대한 메모리와  max_allowed_packet
바이트까지 증가할 수 있는 통신 버퍼(communication buffer)만을  할당하
게 된다.

하지만 클라이언트 측에서 각 행별로 많은 작업을 수행하고 있거나 사용자
가 ^S(stop scroll)을 입력할 수도 있는 스크린으로 결과를 내보내는 경우
에는 mysql_use_result() 함수를 사용해서는 안된다. 이것은 서버를  멈추
게 하고 다른 쓰레드들로 하여금 데이터가 얻어지고 있는 테이블을 갱신하
는 것을 막게 된다.

mysql_use_result()  함수를  사용할  때는   NULL 값이   리턴될  때까지
mysql_fetch_row() 함수를 수행해야 한다.  그렇지 않으면 아직  얻어오지
않은 행들이 다음 질의에  대한 결과 셋으로  넘어가서 리턴되게 된다.  C
API는 'Commands out of sync' 에러를 주게 된다.

mysql_use_result()     함수로부터      리턴되는     결과에     대해서
mysql_data_seek(),         mysql_row_seek(),        mysql_row_tell(),
mysql_num_rows(), mysql_affected_
rows()과 같은 함수들을 사용할 수 없으며, 혹은 mysql_use_result() 함수
가 종료할 때까지 다른 질의를 수행할 수 없다(그러나 모든  행들을 fetch
한 후에는 mysql_num_rows() 함수가 실제로 fetch되어진 행의 개수를 리턴
한다).

결과 셋에 대한 작업이 끝나면 반드시 mysql_free_result()를  호출하여야
한다.

18.4.47.2 Return values
MYSQL_RES 결과 구조체(result structure). 에러이면 NULL을 리턴한다.


18.4.47.3 Errors
CR_COMMANDS_OUT_OF_SYNC
        Commands were executed in an improper order.
CR_OUT_OF_MEMORY
        Out of memory.
CR_SERVER_GONE_ERROR
        The MySQL server has gone away.
CR_SERVER_LOST
        The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
        An unknown error occurred.

18.4.48 mysql_query() 함수가 성공하였는데 mysql_store_result() 함수가
가끔 NULL을 리턴하는 이유는 무엇인가?

이런 경우가 있을 수 있으며 이것은 다음 중 하나의  상황이 발생하였음을
의미한다:

        ·malloc()이 실패한 경우(예를 들어 결과 셋이 너무 큰 경우).
        ·데이터를 읽을 수 없는 경우(연결에 에러가 생기는 등).
        ·질의가 아무  데이터도  리턴하지 않는  경우(INSERT,  UPDATE,
                DELETE 문 등의 경우).

질의문이     비어있지     않은(non-empty)     결과를     생성하였나는
mysql_num_fields()  함수를   호출함으로써  체크해볼  수   있다.  만약
mysql_num_fields() 함수가 0을 리턴하면 결과셋이 빈 것이고 가장 마지막
질의가 INSERT, DELETE와 같이 값을 리턴하지 않는 질의문임을 의미한다.
mysql_num_fields() 함수가 0이 아닌 값을 리턴하는 경우는 질의문이 비어
있지   않는   결과를   생성해야만   함을  의미한다.   자세한   내용은
mysql_num_fields() 함수의 설명을 참조한다.

mysql_errno() 함수나 mysql_error() 함수를 호출함으로써 에러를  테스트
해볼 수 있다.

18.4.49 What results can I get from a query?

질의의 결과로 리턴되는 결과 셋에 덧붙여 다음과 같은 정보들을  또한 얻
을 수 있다:
- mysql_affected_rows()는 INSERT, UPDATE, DELETE 문을 수행할  때 가장
최근에 수행된 질의에 의해  affected된 행들의 개수를 리턴한다.  예외는
DELETE가 WHERE 구문없이 사용되어  테이블이 truncate되는 경우이다.  이
경우 mysql_affected_rows() 함수는 affected된 레코드의 숫자로서 0을 리
턴한다.
-   mysql_num_rows()는   결과   셋   내의   행의   개수를   리턴한다.
mysql_store_result()      함수와      함께     사용되는      경우에는
mysql_store_result()가 리턴되자마자 최대한 빨리 mysql_num_rows()가 호
출되어진다.     mysql_use_result()와    함께     사용되는    경우에는
mysql_fetch_row()가 모든 행에 대해서 수행된 후에만 mysql_num_rows()가
호출될 수 있다.
- mysql_insert_id()는 AUTO_INCREMENT 인덱스를 가진 테이블에 행을 추가
한 경우 가장 최근에 새로 생성된 ID를 리턴한다.
- 몇몇  질의들(LOAD DATA  INFILE  ..., INSERT  INTO ...   SELECT ...,
UPDATE)은 부가적인 정보를 리턴한다. 이 결과들은 mysql_info()에 의하여
리턴된다. mysql_info()는 리턴할 부가적인 정보가 없는 경우 NULL을 리턴
한다.

18.4.50 How can I get the unique ID for the last inserted row?
AUTO_INCREMENT 속성을 가지는 컬럼을 포함하고 있는 테이블에  새로운 레
코드를 삽입하는 경우, mysql_insert_id() 함수를 호출함으로써 가장 최근
에 생성된 ID를 얻어올 수 있다.

다음의 코드를 수행함으로써 AUTO_INCREMENT 인덱스가 사용되었나를  체크
해 볼 수 있다. 또한 질의가 AUTO_INCREMENT 인덱스를 가진 INSERT 문인가
를 체크할 수 있다:

if (mysql_error(&mysql)[0] == 0  && mysql_num_fields(result) == 0  &&
mysql_insert_id(&mysql) != 0)
{
    used_id = mysql_insert_id(&mysql);
}
하나의 테이블에서 생성된 ID를 다른 테이블에 삽입하는데  사용하고자 하
는 경우 다음과 같은 SQL 문을 사용한다:

INSERT INTO foo (auto,  text) VALUES(NULL, 'text');             #
generate ID by inserting NULL
INSERT INTO foo2 (id, text) VALUES(LAST_INSERT_ID(), 'text');  #  use
ID in second table

18.4.51 Problems linking with the C API
어떤 시스템에서는 C API를 linking하여 컴파일할 때 다음과  같은 에러가
발생할 수 있다:
gcc -g -o client test.o -L/usr/local/lib/mysql -lmysqlclient -lsocket
-lnsl

Undefined       first referenced
symbol          in file
floor            /usr/local/lib/mysql/libmysqlclient.a(password.o)
ld: fatal: Symbol referencing errors. No output written to client

위와 같은  에러가 발생하는  경우에는 컴파일  시에 -lm을  추가함으로써
math 라이브러리를 함께 포함해 준다.

18.4.52 How to make a thread-safe client
클라이언트는 거의 thread-safe하다. 가장 큰 문제점은 'net.c'에서  소켓
을 읽는 서브루틴들이 interrupt-safe하지 못하다는 것이다. 이것은  서버
로부터 읽는 작업이 오래 걸릴 때 클라이언트가 작업을 중단할 수 있는 기
능을 원할 것이라는 생각에서 그렇게 작성되어졌다.

표준 클라이언트 라이브러리는 쓰레드 옵션없이 컴파일되어진다.

Thread-safe 클라이언트를 만들기 위해서는 -lmysys, -lstring, -ldbug 라
이브러리와 서버가 사용하는 net_serv.o를 함께 사용한다.

Threaded client를 사용하면 'thr_alarm.c' 파일에 있는 루틴들을  활용할
수 있다. mysys 라이브러리의 루틴들을 사용할 때 기억해야할 단 한가지는
my_init() 함수를 가장 먼저 호출해야 한다는 것이다!

mysql_real_connect()를 제외한 모든 함수들은 현재 thread-safe하다.  다
음의 내용에서는 thread-safe 클라이언트 라이브러리를 컴파일하는 방법과
그것을 thread-safe 방식으로 사용하는 방법에 대하여  기술한다(여기서는
mysql_real_connect() 함수에 대한 예를 설명하며, mysql_connect()의  경
우에도 동일하다).

mysql_real_connect() 함수를 thread-safe하게 만들기 위해서는 다음의 명
령으로 클라이언트를 재컴파일하여야 한다:
shell> CPPFLAGS=-DTHREAD_SAFE_CLIENT ./configure ...

pthread 라이브러리가 디폴트로 포함되어지지 않기 때문에 표준  클라이언
트(standard client)를 링킹할 때 정의되지 않은 심볼(undefined symbols)
로 인한 에러가 발생할 수 있다.
결과로 나오는 'libmysqld.a' 라이브러리는 이젠 thread-safe하다. 이것은
두개의 thread가 mysql_real_connect()에 의해  리턴된 동일한 연결  핸들
(connection handle)을   동시에 질의하지   않는 한  클라이언트  코드가
thread-safe하다는 것을 의미한다; 클라이언트/서버 프로토콜은 주어진 연
결에 대하여 동시에 하나의 요청만을 허용한다. 동일한 연결에  대하여 복
수개의    thread를   사용하길    원하는    경우에는   mysql_query()와
mysql_store_result()의 호출   묶음(call combination)에  대하여  mutex
lock을 해야 한다.

일단 mysql_store_result()가  준비되면 lock은  해제될 수   있으며 다른
thread들은 동일한 연결에 대하여 query를 할 수 있다(다시  말해서, 서로
다른    thread들이   적절한    locking    프로토콜을   사용하는    한
mysql_store_result()과 함께 생성된 서로 다른 MYSQL_RES 포인터들을  사
용할 수 있다). POSIX thread로 프로그램한다면 mutex lock을 생성하고 해
제하는데 pthread_mutex_lock()와 pthread_mutex_
unlock() 함수를 사용할 수 있다.

mysql_store_result()가 아닌   mysql_use_result()를 사용하는  경우에는
lock이 mysql_use_result()와 mysql_fetch_row()에 대한 호출을  surround
할 필요가 있다. 그러나 threaded client는 mysql_use_result()를  사용하
지 않는 것이 가장 좋다.

19. 타 DB와 비교

19.1 Mysql/mSQL 비교

이번장은 Mysql 개발자가 쓴 글이므로 이점을 염두에 두고 보아야 한다.
그렇지만 개발자들이 알고 있는대로 솔직하게 작성되었다.

지원되는 함수, 타입, 제한사항등은 crash-me 웹 페이지를 참고하면 된다.


<성능>
속도 비교와 관련해서는 Mysql 벤치마크를 참고한다. (11장)  mSQL은 스레
드 생성 때문에 발생하는 과부하가 없고, 파져(parser)가  작으며, 기능이
적고 보안이 간단하다. 그래서 다음과 같은 경우에는 mSQL의  속도가 빠르
다.

- 연결/해제를 반복하면서 각 연결때마다 간단한 질의를 하는 테스트
-몇개의 컬럼과 키만 있는 아주 간단한 테이블에 자료을 입력(INSER)하는
경우
- CREATE TABLE, DROP TABLE
- 인덱스 없이 SELECT 하는 경우(테이블 검색이 매우 간단)

이런 작업들은 매우 간단해서, 시작시 높은 부하가 걸리는 경우에 좋다.
연결이 되고나서는 Mysql이 더 높은 성능을 보여준다.

그 외에는 다음과 같은 면에서 Mysql이 mSQL( 및 다른 SQL 프로그램) 보다
빠르다.

- 복합적인 SELECT 연산
- 대량의 자료를 검색 (Mysql은 성능이 뛰어나고 빠르며 안전한 프로토콜
  을 사용한다)
- 가변길이 문자열을 가진 테이블. Mysql은 효율적으로 되어있으며 VARCHA
  R 컬럼에 인덱스를 사용할 수 있다.
- 많은 컬럼을 가진 테이블을 다루는 경우
- 레코드 길이가 큰 테이블을 다루는 경우
- 많은 표현식(expression)을 가진 SELECT 문을 사용하는 경우
- 거대한 테이블에서 SELECT문을 사용하는 경우
- 동시에 다중으로 연결을 처리해야할 때. Mysql은 완전한 멀티스레드를
  지원한다. 각 연결은 자신의 스레드를 가지며 이는 스레드에서  다른 스

  드를 기다릴 필요가 없다는 것을 의미한다. (현재의 스레드가 다른 스레

  에서 접근하기 원하는 테이블을 수정하고 있는 경우는 제외) mSQL에서는
  하나의 연결이 성립되면 그 연결에서 질의를 수행하든 하지 않든 상관없

  첫 번째 연결이 끝날때까지 다른 것들은 기다려야한다. 첫  번째 연결이

  제되면 다음 연결이 성립되고 또 다른 것들은 다시 기다려야한다.
- 조인. mSQL에서는 SELECT에서 테이블의 정렬 순서를 변경하면 엄청나게
  느려진다. 벤치마킹 프로그램을 사용하였을 때, 그 시간은 Mysql보다 최

  15000배 느렸다. mSQL에서는 최적의 순서에 따른 테이블을  정열하기 위

  조인 최적화기가 없기 때문이다. 그렇지만, 테이블을 mSQL2의 적절한 정

  순서에 따라 정확하게 테이블을 구성하고 WHERE 가 간단하고  인덱스 컬

  을 사용하면 조인은 상대적으로 빨라진다. 11장 Mysql 벤치마킹 참고.
-  ORDER BY and GROUP BY.
- DISTINCT
- TEXT 나  BLOB 컬럼 사용


<SQL 특징>
-  GROUP BY, HAVING. mSQL은 GROUP BY를 지원하지 않는다. Mysql은
   완전하게 GROUP BY 와 HAVING을 지원하며 다음의 함수를 지원한다.
   COUNT(), AVG(), MIN(), MAX(), SUM() and STD(). COUNT(*)는 하나의
   테이블에서 SELECT를 사용하고, 컬럼은 검색하지 않으며 WHERE 문이
   없을 경우 매우 빠르게 최적화되어있다. MIN() 과 MAX()는 문자열
   인자를 가진다.
- INSERT 와 UPDATE에서 계산가능.
        예)Mysql> UPDATE SET x=x*10+y WHERE x<20;
- 알리아싱. Mysql에는 컬럼 알리아싱이 있다.
- 컬럼 이름 한정. Mysql에서 컬름 이름이 질의에 사용된 테이블에서 유일
하다면,    완전한 한정자를 쓸 필요가 없다. (** table_name.column_name
이런 식으로    하지 않아도 된다는 말입니다**)
- SELECT문에서 다양한 함수 지원. 7.3 함수 참조.

<디스크 공간 효율성>
테이블을 얼마나 작게 만들 수 있는가를 말한다. Mysql에는 매우 정밀한
타입이 많으므로 매우 적은 공간만을 차지하도록 테이블을 만들 수 있다.
예를 들어 유용한 데이터타입중 하나는 MEDIUMINT로 3 바이트 long이다.
1억건의 레코드가 있는 경우, 각 레코드당 1 바이트를 절약하는 것은 매우
중요하다. mSQL2는 지원하는 데이터 타입이 제한되어있어서 테이블을 작게
만들기가 어렵다.

<안정성>
안정성을 객관적으로 판단하는 것은 쉬운 일이 아니다. Mysql의 안정성은
1.5장 참고. 우리는(Mysql 개발팀)은 mSQL의 안정성에 대한 경험이
없으므로 이에 대해서는 말할 것이 없다.

<가격>
주요한 주제중의 하나가 라이센스이다. Mysql은 mSQL보다 유연한 라이센스
정책을 가지고 있으며 가격도 저렴하다. 어떤 제품을 선택하든 최소한
라이센스나 이메일 지원에 대한 비용을 고려해야한다. (물론 판매하는
제품에 Mysql이 포함되어있으면 라이센스가 반드시 필요하다)

<펄 인터페이스>
Mysql은 몇가지 추가된 기능과 함께 mSQL과 동일한 펄 인터페이스를
가지고 있다.


<JDBC(Java)>
Mysql은 현재 4가지 JDBC 드라이버가 있다.
- The gwe driver: A Java interface by GWE technologies (not supported
anymore).
- The jms driver: An improved gwe driver by Xiaokun Kelvin ZHU
- The twz driver: A type 4 JDBC driver by Terrence W. Zellers and
educational use.
- The mm driver: A type 4 JDBC driver by Mark Matthews{{</LI>}}
추천하는 드라이버는 twz나 mm 드라이버이다. 둘다 훌륭하게 작동하는
것으로 보고되었다.
mSQL에 JDBC 드라이버가 있는 것은 알고 있지만 그에 대한 비교 경험은
없다.

<개발속도>
Mysql은 매우 작은 규모의 개발팀을 가지고 있지만, 모두 C와 C++ 코딩을
매우 빠르게 하는데  익숙해있다. 스레드, 함수, GROUP BY 등등 mSQL에는
아직 구현되지 않은 것들이 많아서 따라오려면 멀었다. mSQL의 최근
'HISTORY' 파일과 Mysql 레퍼런스 매뉴얼의 뉴스 섹션을 비교해보자.
어떤 것이 빠르게 개발되고 있는지 명확할 것이다.

<유틸리티 프로그램>
mSQL과 Mysql에는 매우 다양한 서드-파티 툴이 있다. mSQL에서 Mysql로
포팅하는 것이 매우 쉽기 때문에, mSQL에서 사용가능한 대부분의 애플리
케이션은 Mysql에서도 사용할 수 있다.

Mysql에는 간단한 mSQL2Mysql 프로그램이 들어있는데 mSQL과 Mysql 사이의
대부분의 C-API 함수의 스펠링을 바꿀 수 있다. mSQL에서 Mysql 로 클라이
언트 프로그램을 포팅하는 것은 일반적으로 그다지 시간이 걸리지 않는다.

19.1.1 mSQL 툴을 Mysql로 바꾸기
경험상 보았을 때, mSQL C-API를 사용하는 mSQL-tcl 과 mSQLjava를
Mysql C-API  에서 작동할 수 있도록 변환하는 것은 몇시간이면 충분하다.

변환과정은 다음과 같다:

1. 소스에서 mSQL2Mysql 스크립트를 실행한다. 여기에는 replace 프로그램
이 필요한데, Mysql과 함께 배포된다.
2. 컴파일
3. 모든 컴파일 에러 수정
 mSQL C API와 Mysql C API 의 차이점은 다음과 같다.
- Mysql uses a Mysql structure as a connection type (mSQL uses an
int).
-  Mysql_connect() takes a pointer to a Mysql structure as a
parameter. It is easy to define one globally or to use malloc() to
get one. Mysql_connect() also takes 2 parameters for specifying the
user and password. You may set these to NULL, NULL for default use.
- Mysql_error() takes the Mysql structure as a parameter. Just add
the parameter to your old mSQL_error() code if you are porting old
code.
- Mysql returns an error number and a text error message for all
errors. mSQL returns only a text error message.
- Some incompatibilities exist as a result of Mysql supporting
multiple connections to the server from the same process.{{</LI>}}
( Mysql은 동일한 프로세스에서 서버에 다중 연결을 지원하므로 호환되지
않는 부분이 존재한다.)

19.1.2 mSQL 과 Mysql  클라이언트/서버 통신 프로토콜 차이점
mSQL과 Mysql을 둘다 지원하지 못하거나 힘든 몇가지 차이점이 있다.

Mysql 프로토콜이  mSQL 프로토콜과 다른 몇가지 주요 차이점은 다음과
같다.
- 메시지 버퍼에 많은 검색 행을 가지고 있다.
- 메시지 버퍼는 질의나 결과가 현재의 버퍼보다 크면, 서버와 클라이언트
에서 설정한 제한선까지 동적으로 확장이 될 수 있다.
- 중복되거나 분실된 패킷을 잡아 모든 패킷의 숫자를 셀 수 있다.
- 모든 컬럼 값은 아스키로 전송된다. 컬럼과 행의 길이는 바이너리 코딩
(1, 2, 3 바이트)으로 꾸려져 전송된다.
- Mysql can read in the result unbuffered (without having to store
  the full set in the client).
- 단일 쓰기/읽기 락이 30초이상 걸리면, 서버에서 연결을 해제한다.
- 연결이 8시간 이상 쉬고 있으면, 서버에서 연결을 해제한다.

19.1.3  How mSQL 2.0 SQL syntax differs from Mysql
(** 굳이 번역하지 않습니다. 심심하면 한번 읽어보세요. mSQL 써본지
워낙 오래되었고 그다지 쓸 일이 없어서요**)

Column types
Mysql

Has the following additional types (among others; see see section
7.6)
ENUM type for one of a set of strings.
SET type for many of a set of strings.
BIGINT type for 64-bit integers.{{</LI>
}}

Mysql also supports the following additional type attributes:
UNSIGNED option for integer columns.
ZEROFILL option for integer columns.
AUTO_INCREMENT option for integer columns that are a PRIMARY KEY.
DEFAULT value for all columns.{{</LI>
}}

mSQL2
mSQL column types correspond to the Mysql types shown below:{{<TBODY>
}}

mSQLtype        Corresponding Mysql type
CHAR(len)       CHAR(len)
TEXT(len)       TEXT(len). len is the maximal length. And LIKE works.
INT             INT. With many more options!
REAL            REAL. Or FLOAT. Both 4- and 8-byte versions are avail
able.
UINT            INT UNSIGNED
DATE            DATE. Uses ANSI SQL format rather than mSQL's own.
TIME            TIME
MONEY   DECIMAL(12,2). A fixed-point value with two decimals.
{{</TBODY>
}}

{{</DD>
}}Index creation
Mysql
Indexes may be specified at table creation time with the CREATE TABLE
statement.
mSQL
Indexes must be created after the table has been created, with
separate CREATE INDEX statements.{{</DD>
}}

To insert a unique identifier into a table
Mysql
Use AUTO_INCREMENT as a column type specifier.
mSQL
Create a SEQUENCE on a table and select the _seq column.{{</DD>
}}
To obtain a unique identifier for a row
Mysql
Add a PRIMARY KEY or UNIQUE key to the table.
mSQL
Use the _rowid column. Observe that _rowid may change over time depen
ding on many factors.{{</DD>
}}

To get the time a column was last modified
Mysql
Add a TIMESTAMP column to the table. This column is automatically set
to the current date and time for INSERT or UPDATE statements if you
don't give the column a value or if you give it a NULL value.
mSQL
Use the _timestamp column.{{</DD>
}}
NULL value comparisons
Mysql
Mysql follows ANSI SQL and a comparison with NULL is always NULL.
mSQL
In mSQL, NULL = NULL is TRUE. You must change =NULL to IS NULL and <>
NULL to IS NOT NULL when porting old code from mSQL to Mysql.{{</DD>
}}


String comparisons
Mysql
Normally, string comparisons are performed in case-independent
fashion with the sort order determined by the current character set
(ISO-8859-1 Latin1 by default). If you don't like this, declare your
columns with the BINARY attribute, which causes comparisons to be
done according to the ASCII order used on the Mysql server host.
mSQL
All string comparisons are performed in case-sensitive fashion with
sorting in ASCII order.{{</DD>
}}

Case-insensitive searching
Mysql
LIKE is a case-insensitive or case-sensitive operator, depending on
the columns involved. If possible, Mysql uses indexes if the LIKE
argument doesn't start with a wildcard character.
mSQL
Use CLIKE.{{</DD>
}}
Handling of trailing spaces
Mysql
Strips all spaces at the end of CHAR and VARCHAR columns. Use a TEXT
column if this behavior is not desired.
mSQL
Retains trailing space.{{</DD>
}}
WHERE clauses
Mysql
Mysql correctly prioritizes everything (AND is evaluated  before OR).
To get mSQL behavior in Mysql, use parentheses (as shown below).mSQL
Evaluates everything from left to right. This means that some logical
calculations with more than three arguments cannot be expressed in
any way. It also means you must change some queries when you upgrade
to Mysql. You do this easily by adding parentheses. Suppose you have
the following mSQL query:
Mysql> SELECT * FROM table WHERE a=1 AND b=2 OR a=3 AND b=4;
To make Mysql evaluate this the way that mSQL would, you must add
parentheses:
Mysql> SELECT * FROM table WHERE (a=1 AND (b=2 OR (a=3 AND (b=4)
)));
{{</DD>
}}Access control
Mysql
Has tables to store grant (permission) options per user, host and
database.
mSQL
Has a file `mSQL.acl' in which you can grant read/write privileges
for users.


19.2 PostgreSQL과 Mysql 비교
PostgreSQL은 사용자-정의 타입, 트리거, 룰, 트랜잭션 지원 등 몇가지
향상된 기능을 가지고 있다. 그렇지만 ANSI SQL 과 ODBC의 표준 타입이나
함수 지원이 미약하다. 지원되거나 지원되지 않는 제한, 타입, 함수는
crash-me 웹 페이지를 참고하자.
(http://www.Mysql.com/crash-me-choose.htmy)

일반적으로 PostgreSQL은 Mysql보다 느리다. 11장 [벤치마크] 참고. 이것
은 대부분 트랜잭션 시스템 때문이다. 정말로 트랜잭션이 필요하고 속도를
희생할 생각이 있다면 PostgreSQL을 고려해볼 수 있다.


=======================================================================


                            - 정         보 -


## 원본 : mysql 3.21 Reference Manual PostScript 매뉴얼
(현재 최신 매뉴얼은 3.22 레퍼런스 매뉴얼이며 3.22.14b-gamma입니다.)

5, 6, 9, 10, 11, 12, 13, 15, 17장 번역 : 문태준(taejun@hitel.net)
18장 중 C-API 부분 번역 : 권용찬 (golmong@cre.co.kr)

설치와 관련된 부분은 kldp.org의 database 항목에서 관련된  정보가 있으
므로 참고하시면 됩니다.



                                         << MySQL Reference Manual >>

                                                 version : MySQL 3.21

                                                              K.N.U.T
                                                 Software Engineering
                                                              9801260
                                                             최 연 기
                                                          1999. 9. 29

이 포스트를..

덧글 쓰기 엮인글 쓰기

[펌] 초보자도 알아야할 MySQL 튜닝 MYSQL

2005/01/10 17:41

복사 http://blog.naver.com/meetjava/100009251344

출처 dohyah님의 블로그 | dohyah
원문 http://blog.naver.com/dohyah/140009231142
1.반드시 컴파일 하라! 10-30% 속도 향상 !
소스를 가지고 컴파일 하세요. MySQL 메뉴얼에 따르면 10-30% 속도가 빠르다고 합니다. RPM 이나 바이너리 설치를 하지 마세요 !  

1-2.최신 버전을 사용하라
최신 버전이 좋은 점은 자동 튜닝 하는 것 입니다. 버그를 수정 하구요. 되도록 이면 최신 버전을 사용하세요 !!

2. HEAP 테이블이 가장 빠르다!
일반적으로 가장 많이 쓰이는 테이블 타입은 MyISAM 타입 입니다. MyISAM 타입은 무자게 빠르며, 대용량에도 강합니다. 그러나 트랜잭션은 지원되지 않습니다. 이노디비(InnoDB) 는 트랜잭션이 지원 됩니다. 쇼핑몰에서는 반드시 사용해야 합니다

^^ HEAP 테이블 타입은 가장 빠르며, 단점은 메모리에 있기 때문에, MySQL에 중지 될 경우 모두 날아 갑니다. 검색을 하고 재검색을 다시 하는 경우, 임시 검색 테이블을 만들어 놓는 것도 좋은 방법입니다.  


HEAP 테이블 메뉴얼 !
http://www.mysql.com/doc/H/E/HEAP.html

HEAP 테이블 만들기 !
mysql>CREATE TABLE email_addresses TYPE=HEAP
(     ->email char(55) NOT NULL,    
       ->name char (30) NOT NULL,    
       ->PRIMARY KEY(email) );  


3.mysql 서버 top 보기
mysql  서버의 메모리 상황을 보여 주는 프로그램 입니다. 리눅스나 유닉스의 top 기능을 mysql 에서 가능하게 한것 입니다

. top 정보는 튜닝의 기본 이기 땜시 자주 자주 보아야 합니다. ^^ http://public.yahoo.com/~jzawodn/mytop/ PHP 소스 자료실에 파일 다운 로드 하시면 됩니다.  


4.mysql_connect Vs mysql_pconnect
서버 메모리가 최소 2G 이상일 경우 mysql_pconnect 를 추천 함다 ! 연결을 계속 하지 않기 땜시 빠릅니다. ! 그러나 메모리가 2G 이하 일 경우는 mysql_connect 사용하세요 !  


5.int,smallint,tinyint 데이터형 !  
int 는 굉장히 큰수 입니다. 4바이트를 차지 하구요. tinyint 는 몇백 까지만 됩니다. 1바이트 구요. 쓸데 없이 int 를 사용하지 마세용 !! 4바이트와 1바이트는 4배 차이 입니다.조그만것 1개 1개가 모여 서버 부하를 일으 킵니다.!! 데이터 량이 얼마만큼 들어가는지 체크 하고 데이터형을 선택 하세요 ^^ 만약 쓸데없는 데이터 형이다 싶으면 alter table 로 데이터 형을 바꾸세요 !  


6.인덱스의 사용  
인덱스는 반드시 필요한 곳에만 넣으세요 ! 인덱스를 줄 경우 하드 용량을 더 차지 하기 때문에 속도를 떨어 뜨릴 수 있습니다. 모든 칼럼에 인덱스를 주는 것은 절대 추천 하지 않습니다. 1개의 테이블에 주키외에 2-3 개 이상의 인덱스는 주지 마세요! 주키는 당근 인덱스 입니다. ^^

CREATE TABLE albums (     id        INTEGER      NOT NULL AUTO_INCREMENT

PRIMARY KEY,     title     VARCHAR(80)NOT NULL,          INDEX title_idx (title) ); ☞Alter Table 로 인덱스 추가

ALTER TABLE albums ADD INDEX title_idx (title)    결합 인덱스의 경우 너무 많은 인덱스를 사용할 경우 CPU 오버헤드나 하드 오버헤드를 불러 일으 킵니다. 적당히 사용하세요 ^^  

http://www.mysql.com/doc/I/n/Indexes.html
http://www.mysql.com/doc/M/u/Multiple-column_indexes.html
http://www.mysql.com/doc/O/R/ORDER_BY_optimisation.html


6-1. 바보 같은 인덱스의 사용 ?  
인덱스는 %$search% 가 먹지 않습니다. 그런디 게시판 제목(Subject) 에 인덱스 걸어 놓고 , 검색을 %$search% 이렇게 하면 될까요? 인덱스 거나 안거나 똑같습니다. !! $search% 이렇게 사용하세요. 그런디.. $search%  사용하면 제목 처음 단어 밖에 검색이 안됩니다. 그렇다면 ? 다른 검색 방법은 ?  


7.UDF의 사용  
MySQL은 스토어드 프로시져 같은 개념이 존재 하지 않습니다. 그대신 C 언어로 만든 함수를 사용할 수 있습니다. 조금더 빠른 쿼리를 원한다면 UDF 를 사용해보세요 !  

UDF 함수 보기  
http://empyrean.lib.ndsu.nodak.edu/~nem/mysql/udf/

http://www.mysql.com/doc/A/d/Adding_functions.html

스토어드 프로시져가 먼뎅?
스토어드 프로시져는 쉽게 말해 MS-SQL 함수 입니다.
오라클에도 아마 있을검당..^^

그러니까 게시판에서 내용을 넘길때나 불러 올때
mysql 쿼리가 3-4 번 정도 이루어 집니다. 또는 ms- sql 쿼리가 이루어지죵..
3-4 번 정도 쿼리가 되면..그만큼 디비 접속이 잦아 지기 때문에..
속도가 느려 집니다.

많게는 10번 정도의 insert into 와 update 가 이루어집니다.

그래서 ms - sql 자체 내에 인서트 함수 나 목록 보기 함수를 만들어 놓습니다.
글구 1번의 ms-sql 접속만 해서 인서트 함수를 불러서 처리하는 것입니다.

그렇기 때문에 2-3 번의 쿼리가 절약 되서 빠르다는 것이죵..ㅋㅋㅋ
또는 10번의 쿼리 할것을 MS-SQL 스토어드 프로시져를 1번만 호출 함으로 해서 디비 접속이 절약이 되죵..ㅌㅌ

UDF 를 꼭 사용해야 하는가? 안해도 됩니다.만... 사용하면 좋은점 많습니다. 새로운 함수를 추가 할 수 있으므로 ^^ MS-SQL의 스토어드 프로시져 기능 비스므리 하게 사용할 수 도 있구요... UDF 나 MS-SQL 스토어드 프로시져의 사용법을 익히기 보다는 캐슁을 연구하세용 ^^ 동적인 PHP 를 정적인 HTML 로 만드는 방법을요... 또는 UDF 에서 MS-SQL 스토어드 프로시져 처럼 사용이 가능 합니다. 그 부분을 연구하세요. www.zdnet.co.kr 이나 www.zdnet.com 가시면 기사 파일이 1000,29920,2892.html 파일 이란것을 보게 됩 니다. 어키 구현된것일까요? zdnet 게네 들은 강좌를 원래 부터 HTML 로 만들어서 올리는 것일까용?? HTML 로 만드는 부분도 많이 생각 해야 합니다. 강좌가 1만개 라면, 1만개의 파일이 생성 됩니다.

zdnet 의 경우는 조회수가 10만-20만을 넘는 초대형 사이트 이기 때문에 HTML 로 만드는 것이 퍼포먼스가 좋습니 다. UDF

배워 두면..남주지 않습니다.  


8.조인보다는 쿼리를 나누어라!  
조인(Join)하는 것보다 쿼리를 2개로 나누는 것이 속도가 빠릅니다. 조인을 생각 하기 이전에 쿼리를 나누는 것을 생각 하세요 ^^ 어쩔 수 없는 경우는 당근 조인 해야죠. 글고 서브쿼리는 아직 지원 안됩니다. Ms-SQL이나 오라클에서 서브쿼리 보다는 서브쿼리를 하지 않는 방향의 데이터 정규화를 하세요 ^^ 조인 보다 서브쿼리가 느리다. 서버 쿼리 보다는 조인을 사용하세요 ^^  

9.full text index와 search  
3.23.23 부터 mysql 에서는 full text index 를 지원 합니다. 자세한 사항은 아래 !  
http://www.mysql.com/documentation/mysql/bychapter/manual_Reference.html#Fulltext_Search
http://www.mysql.com/doc/F/u/Fulltext_Fine-tuning.html


10. SELECT * FROM sometable
SELECT * FROM sometable 에서 * 모든을 사용하는 것은 무식한 방법 입니다. 모든 칼럼을 불러오는 경우는 드물거든요.

SELECT code,tadate,see FROM sometable 사용할 것만 불러 오세요 ^^  

11.데이터베이스 정규화  
테이블을 아무렇게나 만들면 안됩니다. 데이터베이스 정규화 원칙에 의거, 테이블을 나눌것은 나누고 만드시는 것이 좋습니다. 제1 정규화, 제2 정규화 정도는 사용하셔야 합니다. 게시판을 만들때 아직도 테이블 1개에 만드시나요? 온라인 폴 만들때 , 테이블 1개에 만드시나요?  


12.REPLACE INTO문 사용하기
REPLACE INTO albums VALUES (6, 'tood.net')     insert 문대신에 replace 문을 사용해보세요. 메뉴얼 보시고 연구하세요

^^ 주키일 경우 사용하시면 됩니다.  

13. explain 사용하기  
explain 를 사용하여 테이블의 키 값이 얼마나 잘 활용 되는지 알 수 있습니다. EXPLAIN SELECT, SHOW VARIABLES, SHOW

STATUS, SHOW PROCESSLIST  
http://www.mysql.com/doc/E/X/EXPLAIN.html


17.BLOB과 TEXT는 분리하라
BLOB과 TEXT 칼럼은 테이블을 분리 하는 것이 좋다. 다른 칼럼의 내용 보다 크기 때문이다 ! OPTIMIZE TABLE 명령을 자주 사용해라 ! Not null 로 지정 하는 것이 빠르다. varchar 보다 char 이 훨빠르다. 
 

'sql' 카테고리의 다른 글

mysql 동시접속자수  (0) 2009.01.07
mysql tunning  (2) 2009.01.07
Mysql Data Type  (1) 2009.01.07
mysql query  (0) 2009.01.07
mysql password 잊어버렸을때  (0) 2009.01.07
Posted by 으랏차
,

Mysql Data Type

sql 2009. 1. 7. 11:32
TINYINT (-128 ~ 127)
SMALLINT (-32768 ~ 32767)
MEDIUMINT (-8388608 ~ 8388607)
INT (-2147483648 ~ 2147483647)
BIGINT (-9223372036854775808 ~ 9223372036854775807)
FLOAT
DOUBLE
DECIMAL(NUMERIC)
DATE (1000-01-01 ~ 9999-12-31, YYYY-MM-DD)
DATETIME (1000-01-01 00:00:00 ~ 9999-12-31 23:59:59, YYYY-MM-DD HH:MM:SS)
TIMESTAMP (1970-01-01 00:00:00 ~ 2106..., YYYYMMDD[HH[MM[SS]]]) O
TIME (-838:59:59 ~ 838:59:59, HH:MM:SS)
YEAR (1901 ~ 2155, 0000, YYYY)
CHAR
VARCHAR
TINYTEXT (0 ~ 255)
TEXT (0 ~ 65535)
MEDIUMTEXT (0 ~ 16777215)
LONGTEXT (0 ~ 4294967295)
TINYBLOB (0 ~ 255)
BLOB (0 ~ 65535)
MEDIUMBLOB (0 ~ 16777215)
LONGBLOB (0 ~ 4294967295)

'sql' 카테고리의 다른 글

mysql tunning  (2) 2009.01.07
mysql  (0) 2009.01.07
mysql query  (0) 2009.01.07
mysql password 잊어버렸을때  (0) 2009.01.07
mysql 백업  (0) 2009.01.07
Posted by 으랏차
,

mysql query

sql 2009. 1. 7. 11:29
# root암호설정 - root로 로그인하여 해야함
% mysqladmin -u root password '변경암호'
% mysqladmin -u root -p기존암호 password '변경암호'

root암호변경설정
PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
This is done with:
/usr/bin/mysqladmin -u root -p password 'new-password'
/usr/bin/mysqladmin -u root -h ns.dbakorea.pe.kr -p password 'new-password'

DB작업
DB생성: mysql> create database DB명 ( or % mysqladmin -u root -p create DB명 )
DB삭제: mysql> drop database DB명
DB사용: mysql> use DB명 (엄밀히 말하자면, 사용할 'default database'를 선택하는 것이다.)
DB변경: mysql> alter database db명 DEFAULT CHARACTER SET charset (4.1이상에서만 available)

MySQL 연결
mysql -u 사용자 -p DB명 ( or % mysqladmin -u root -p drop DB명 )

데이터파일 실행(sql*loader기능)
mysql>load data infile "데이터파일" into table 테이블명 ;
데이터파일에서 컬럼구분은 탭문자, Null값은 /n로 입력
데이터파일의 위치는 /home/kang/load.txt 와 같이 절대경로로 지정할것.

질의 파일 실행
쉘프롬프트상에서
mysql -u 사용자 -p DB명 < 질의파일
or
mysql프롬프트상에서
mysql> source 질의파일

쉘프롬프트상에서 질의 실행
dbakorea@lion board]$ mysql mysql -u root -pxxxx -e \
> "INSERT INTO db VALUES(
> 'localhost', 'aaa', 'aaa',
> 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y')"


사용자 생성 & 사용자에게 DB할당
shell> mysql --user=root -p mysql

mysql> INSERT INTO user VALUES('localhost','사용자',PASSWORD('비밀번호'),'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO user VALUES('%','사용자',PASSWORD('비밀번호'),'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');

mysql> INSERT INTO db(Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv) VALUES ('localhost','DB명','사용자','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO db(Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv) VALUES('%','DB명','사용자','Y','Y','Y','Y','Y','Y');

mysql> FLUSH PRIVILEGES; (or shell prompt: mysqladmin -u root -pxxxx reload)

CASE 2: GRANT명령을 이용한 사용자 생성(이 방법이 권장된다)
kang이라는 DB를 만들고, 이 DB를 아래에서 나열된 권한을 가진 kang이라는 사용자를 생성
create database kang;
grant SELECT,INSERT,UPDATE,DELETE,CREATE,DROP on kang.* to kang@localhost identified by 'kang';
grant SELECT,INSERT,UPDATE,DELETE,CREATE,DROP on kang.* to kang@'%' identified by 'kang';

mysql> create database kang;
Query OK, 1 row affected (0.00 sec)

mysql> grant SELECT,INSERT,UPDATE,DELETE,CREATE,DROP on kang.* to kang@localhost identified by 'kang';
Query OK, 0 rows affected (0.00 sec)

mysql> grant SELECT,INSERT,UPDATE,DELETE,CREATE,DROP on kang.* to kang@'%' identified by 'kang';
Query OK, 0 rows affected (0.01 sec)

mysql>

여러가지 명령정리
mysql> show variables; 서버의 variables(설정사항)출력
mysql> show variables like 'have_inno%' 조건에 맞는 variables만 출력
mysql> show databases; database목록
mysql> show tables; 현재DB의 테이블목록(temporary table은 출력하지 않음)
mysql> show tables from db명; 지정된 db명이 소유한 테이블목록
mysql> show tables like 'mem%'; 조건에 맞는 테이블목록만 출력
mysql> show index from 테이블명; 인덱스 보기
mysql> show columns from 테이블명; 테이블구조(describe 테이블명, explain 테이블명)
mysql> show table status; 현재 DB의 테이블들의 상태(row수,table type,row길이,..)
mysql> show table status from db명; 지정된 DB의 테이블들의 상태(row수,table type,row길이,..)
mysql> show create table 테이블명; 해당 테이블 생성 SQL문 출력
mysql> rename table 테이블1 to 테이블2; 테이블명 변경(ALTER TABLE 테이블1 RENAME TO 테이블2)
mysql> rename table 테이블1 to 테이블2, 테이블3 to 테이블4; rename multiple tables
mysql> rename table db1명.테이블명 to db2명.테이블명; 테이블을 다른 DB로 이동
mysql> alter table 테이블명 add 컬럼명 데이터타입; 컬럼추가
mysql> alter table 테이블명 del 컬럼명; 컬럼제거
mysql> alter table 테이블명 modify 컬럼명 컬럼타입; 컬럼명에 지정된 컬럼타입의 변경
mysql> alter table 테이블명 change old컬럼명 new컬럼명 컬럼타입 컬럼명 변경
mysql> alter table 테이블명 type=innodb; 테이블type변경
mysql> create table 테이블명(..) type=heap min_rows=10000; 10000row를 수용할 수 있을 만큼 메모리할당(heap type이므로)
mysql> select version(); MySQL서버버전 출력
mysql> create table 테이블2 as select * from 테이블1; 테이블1과 동일한 테이블 생성(with 데이터, as는 생략가능)
mysql> create table 테이블2 as select * from 테이블1 where 1=2; 테이블1과 동일한 구조의 테이블 생성(without 데이터, 1=2는 0으로 할수도 있다.)
mysql> insert into 테이블2 select * from 테이블1; 테이블1의 데이터를 테이블2에 insert


테이블이 존재여부 파악
DROP TABLE IF EXISTS 테이블명;
CREATE TABLE 테이블명 (...);
프로그래밍 언어에서 COUNT(*)를 사용하여 질의가 성공하면 테이블이 존재함을 파악할 수 있다.
ISAM, MyISAM의 경우 COUNT(*)가 최적화되어 상관없으나, BDB, InnoDB의 경우 full scan이 발생하므로 사용하지 마라.
대신 select * from 테이블명 where 0; 을 사용하라. 질의가 성공하면 테이블이 존재하는 것이고, 아니면 존재하지 않는 것이다.



접속
mysql {-h 접속호스트} -u 사용자 -p 사용DB
-h로 다른 서버에 존재하는 MySQL접속시 다음과 같이 MySQL DB에 설정해줘야 한다.
mysql> INSERT INTO user VALUES('접근을 허용할 호스트ip','사용자',PASSWORD('비밀번호'),'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y', 'Y','Y','Y','Y');
mysql> INSERT INTO db(Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv) VALUES('접근을 허용할 호스트ip','사용DB','사용자','Y','Y','Y','Y','Y','Y');
mysql> FLUSH PRIVILEGES; or 쉴프롬프트상에서 % mysqladmin -u root -p flush-privileges


검색조건(where)
regular expression을 지원하다니 신기하군..
mysql> select * from work where 열명 regexp "정규표현식";


백업 & 복구
mysqldump {-h 호스트} -u 사용자 -p DB명 > 백업파일
mysql {-h 호스트} -u 사용자 -p DB명 < 백업파일

mysqldump -u root -p --opt db_dbakorea > dbakorea.sql
mysqldump -u root -p --opt db_board | mysql ---host=remote-host -C database (상이한 머쉰)
mysql -u dbakorea -p db_dbakorea < dbakorea.sql

mysqldump -u root -p --opt db_dbakorea | mysql ---host=ns.dbakorea.pe.kr -C db_dbakorea

테이블 생성구문만을 화면에서 보려면 다음과 같이 --no-data를 사용한다. 테이블명을 생략하면 모든 테이블 출력
mysqldump -u 유저명 -p --no-data db명 테이블명

테이블 검사
isamchk

오라클 sysdate와 동일
insert into test values('12', now());

유닉스 time()함수 리턴값 사용
FROM_UNIXTIME(954788684)
UNIX_TIMESTAMP("2001-04-04 :04:04:04")

MySQL 디폴트 DB&로그파일 위치
/var/lib/mysql
/var/lib디렉토리는 여러 프로세스들이 사용하는 데이터를 저장하는 일종의 파일시스템상의 데이터베이스라고 볼 수 있다.

replace
해당 레코드 존재하면 update하고, 존재하지 않는다면 insert한다.(insert문법과 동일)
replace into test values('maddog','kang myung gyu')'

explain
explain 질의문: 지정한 질의문이 어떻게 실행될 건지를 보여줌
mysql> explain select u.uid, u.name, a.name from sm_user u, sm_addr a where u.uid=a.uid;
+-------+------+-----------------+-----------------+---------+-------+------+-------+
| table | type | possible_keys | key | key_len | ref | rows | Extra |
+-------+------+-----------------+-----------------+---------+-------+------+-------+
| u | ALL | PRIMARY | NULL | NULL | NULL | 370 | |
| a | ref | sm_addr_uid_idx | sm_addr_uid_idx | 11 | u.uid | 11 | |
+-------+------+-----------------+-----------------+---------+-------+------+-------+
2 rows in set (0.01 sec)


temporary table
크기가 큰 테이블에 있는 subset에 대한 질의라면 subset을 temporary table에 저장한 후 질의하는 것이 더 빠를 경우가 있다.
temporary table는 세션내에서만 유효하고(현재 사용자만이 볼수 있다는 뜻), 세션종료시 자동적으로 drop된다.

create temporary table (...);
create temporary table (...) type=heap; 디스크가 아닌 메모리에 테이블 생성

존재하는 permanent table의 테이블명과 동일하게 생성할 수 있으며,
temporary table은 permanent table보다 우선시되어 처리된다.
4.0.7의 감마버전에서 테스트하면 결과는 약간 달라진다. 버그인건지..

mysql> create table test (id varchar(10));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into test values('dbakorea');
Query OK, 1 row affected (0.00 sec)

mysql> create temporary table test(id varchar(10));
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
Empty set (0.00 sec)

mysql> drop table test;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
+----------+
| id |
+----------+
| dbakorea |
+----------+
1 row in set (0.00 sec)



Table Type에 다른 Files on Disk

ISAM .frm (definition) .ISD (data) .ISM (indexes)
MyISAM .frm (definition) .MYD (data) .MYI (indexes)
MERGE .frm (definition) .MRG (list of constituent MyISAM table names)
HEAP .frm (definition)
BDB .frm (definition) .db (data and indexes)
InnoDB .frm (definition)

보통 mysqldump를 사용하여 백업을 수행하여 다른 DB서버에 데이터를 restore하면 된다.
MySQL은 별다른 작업없이 데이터파일을 단순히 복사(copy)하는 것만으로도 다른 서버에
DB을 이동시킬 수 있다. 하지만, 이런 방식이 지원되지 않는 table type도 있다.

ISAM: machine-dependent format하기때문에..
BDB : .db파일에 이미 테이블위치가 encode되어 있기때문에..
MyISAM, InnoDB, MERGE :가능(machine-independent format)

별다른 지정을 하지 않았다면 디폴트 TABLE type이 MyISAM이므로, 무난히 migration할 수 있다.
floating-point컬럼(FLOAT,DOUBLE)이 있다면 이러한 방식이 실패할 수 도 있다.

쉘에서는 mysql이 되는데 PHP에서 mysql.sock error를 내면서 MySQL이 안되는 경우
mysql.sock은 /tmp 아니면 /var/lib/mysql에 생기게 된다.
나의 경우, /var/lib/mysql에 mysql.sock파일이 있는데 PHP에서는 /tmp에서 찾으려하면서 에러를 발생했다.
/usr/bin/safe_mysqld파일에서 다음과 같이 수정한다.
주석(#)이 달린 것이 원래것이고 그 밑에 있는것이 수정한 것이다.

# MYSQL_UNIX_PORT=${MYSQL_UNIX_PORT:-/var/lib/mysql/mysql.sock}
MYSQL_UNIX_PORT=${MYSQL_UNIX_PORT:-/tmp/mysql.sock}

위와 같이 하니 /usr/bin/mysql이 /var/lib/mysql/mysql.sock에서 소켓파일을 찾으려 했다.
socket file을 지정하는 --socket이라는 옵션으로 다음과 같이 지정하면 된다.

mysql --socket=/tmp/mysql.sock -u dbakorea -p db_test

하지만 mysql실행시마다 이렇게 써줘야한다는 것이 상당히 귀찮다. 옵션이 바로 적용되게 설정하자.
mysql은 설정사항을 다음 3가지 파일에서 검색한다.

/etc/my.cnf global options(MySQL 전체적으로 사용되는 옵션 정의)
mysql-data-dir/my.cnf 특정 DB에 적용되는 option (/var/lib/mysql/my.cnf)
~/.my.cnf 사용자 각각의 설정('~'문자는 사용자의 홈디렉토리는 의미)

/usr/share/mysql디렉토리에 예제가 있으므로 참고한다.
소켓파일의 지정은 다음줄을 넣어주면 된다.

socket = /tmp/mysql.sock


== /etc/my.cnf예 ==
# The following options will be passed to all MySQL clients
[client]
#password = your_password
port = 3306
socket = /tmp/mysql.sock

# Here follows entries for some specific programs

# The MySQL server
[mysqld]
port = 3306
socket = /tmp/mysql.sock



MySQL에서 통계처리시
orderby, groupby 는 sort_buffer를 늘여준다.(show variables)

live table(smslog)에서 모든 질의를 처리하지 말고 summary table에 질의결과를 저장해 재질의 처리한다.
summary table이 heap-type table가 가능한지 확인할 것.

INSERT INTO tblTemp2 (fldID) SELECT tblTemp1.fldOrder_ID FROM tblTemp1 WHERE
tblTemp1.fldOrder_ID > 100;


join이 subselect보다 빠르다.
join시 사용되는 컬럼은 동일한 column type과 길이를 가져야만 최적의 속도를 보장한다.
즉, 동일 column type이지만 길이가 다르다면(char(11), char(10)), 동일한 컬럼도메인으로 변경해주는 것이 좋다.
where의 in은 optimize되어 있으므로 빠르다
insert,select는 동시에 수행가능하다.(어떻게?)
explain으로 질의과정 점검


varchar to/from char
conversion varchar를 char로 변경할 경우 모든 컬럼타입을 동시에 변경해야 한다.
반대의 경우, 하나만 char->charchar변경시 다른 모든 컬럼도 varchar로 변경됨
참.. 특이하구만..

mysql> CREATE TABLE chartbl (name VARCHAR(40), address VARCHAR(80));
Query OK, 0 rows affected (0.05 sec)

mysql> desc chartbl;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name | varchar(40) | YES | | NULL | |
| address | varchar(80) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
2 rows in set (0.03 sec)

mysql> alter table chartbl modify name char(40);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> desc chartbl;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name | varchar(40) | YES | | NULL | |
| address | varchar(80) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> alter table chartbl modify name char(40), modify address char(80);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> desc chartbl;
+---------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+----------+------+-----+---------+-------+
| name | char(40) | YES | | NULL | |
| address | char(80) | YES | | NULL | |
+---------+----------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql>


"For each article, find the dealer(s) with the most expensive price."

표준안
SELECT article, dealer, price
FROM shop s1
WHERE price=(SELECT MAX(s2.price)
FROM shop s2
WHERE s1.article = s2.article);

수정안(최적화)
CREATE TEMPORARY TABLE tmp (
article INT(4) UNSIGNED ZEROFILL DEFAULT '0000' NOT NULL,
price DOUBLE(16,2) DEFAULT '0.00' NOT NULL);

LOCK TABLES shop read;

INSERT INTO tmp SELECT article, MAX(price) FROM shop GROUP BY article;

SELECT shop.article, dealer, shop.price FROM shop, tmp
WHERE shop.article=tmp.article AND shop.price=tmp.price;

UNLOCK TABLES;

DROP TABLE tmp;



==============================================================================
MySQL 특성정리
==============================================================================
primary key, foreign key지원
index 지원(15개컬럼, 256byte까지)
MySQL에서의 Stored Script-x개념 => SQL server language
commit-rollback개념 => lock tables(lock table test write -> 트랜잭션.. -> unlock tables)
컬럼명길이: 64자까지, 컬럼 Alias: 256자까지
not case-sensitive: keywords, functions, column, index명
case-sensitive: database, table, alias명
키워드,함수명은 대소문자구별이 없지만, db명과 table명은 Unix계열이라면 case-sensitive하다.
(이는 오브젝트명이 OS의 fs에 따라 저장되기 때문이다. 서버의 lower_case_table_names 변수를
1로 설정하면 오브젝트명은 모두 소문자로 저장되므로 유닉스-윈도간 호환성을 높일 수 있다.

지원되지 않는 부분:
Stored Procedure(5.0이상부터 지원된다고 함)
View(5.0이상부터 지원된다고 함)
Trigger(5.0이상부터 지원된다고 함)
subquery(4.1이상부터 지원된다고 함)
union, union all(4.0이상부터 지원됨)

[테이블 type에 따른 인덱스 특성]
Index Characteristic ISAM MyISAM HEAP BDB InnoDB
NULL values allowed No Yes As of 4.0.2 Yes Yes
Columns per index 16 16 16 16 16
Indexes per table 16 32 32 31 32
Maximum index row size (bytes) 256 500 500 500/1024 500/1024
Index column prefixes allowed Yes Yes Yes Yes No
BLOB/TEXT indexes allowed No Yes(255 bytes max) No Yes (255 bytes max) No


인덱스 생성
- alter table을 이용한 인덱스 생성이 더 flexible함
- 인덱스명은 생략가능

ALTER TABLE 테이블명 ADD INDEX 인덱스명 (인덱스컬럼);
ALTER TABLE 테이블명 ADD UNIQUE 인덱스명 (인덱스컬럼);
ALTER TABLE 테이블명 ADD PRIMARY KEY (인덱스컬럼);
ALTER TABLE 테이블명 ADD FULLTEXT (인덱스컬럼);

CREATE INDEX 인덱스명 ON 테이블명 (인덱스컬럼);
CREATE UNIQUE INDEX 인덱스명 ON 테이블명 (인덱스컬럼);
CREATE FULLTEXT INDEX 인덱스명 ON 테이블명 (인덱스컬럼);

unique인덱스와 primary key인덱스와의 차이
unique은 null허용하지만, primary key는 null허용 안함
unique은 하나의 테이블에 여러개 올 수 있지만, primary key는 하나만 존재

테이블생성시 지정
CREATE TABLE 테이블명
(
... column declarations ...
INDEX 인덱스명 (인덱스컬럼),
UNIQUE 인덱스명 (인덱스컬럼),
PRIMARY KEY (인덱스컬럼),
FULLTEXT 인덱스명 (인덱스컬럼),
...

);


index prefix 생성
- 컬럼의 전체길이중 일부만 인덱스로 사용
- supported for ISAM, MyISAM, HEAP, and BDB tables, but not for InnoDB tables
- 지정되는 길이는 byte단위가 아닌 charater단위이므로, multi-byte character일 경우 주의
- blob, text 컬럼타입일 경우, index prefix 가 유용(255 길이까지 가능)

CREATE TABLE 테이블명
(
name CHAR(30) NOT NULL,
address CHAR(60) NOT NULL,
INDEX (name(10),address(10))
);


인덱스 삭제
DROP INDEX 인덱스명 ON 테이블명;
ALTER TABLE 테이블명 DROP INDEX 인덱스명;
ALTER TABLE 테이블명 DROP PRIMARY KEY;


outer join

[MySQL]
left outer joing : SELECT t1.*, t2.* FROM t1 LEFT OUTER JOIN t2 ON t1.i1 = t2.i2;
right outer joing: SELECT t1.*, t2.* FROM t1 RIGHT OUTER JOIN t2 ON t1.i1 = t2.i2;

[Oracle]
left outer joing : SELECT t1.*, t2.* FROM t1, t2 where t1.i1 = t2.i2(+);
right outer joing: SELECT t1.*, t2.* FROM t1, t2 where t1.i1(+) = t2.i2;

SELECT
student.name, student.student_id,
event.date, event.event_id, event.type
FROM
student, event
LEFT JOIN score ON student.student_id = score.student_id
AND event.event_id = score.event_id
WHERE
score.score IS NULL
ORDER BY
student.student_id, event.event_id;


:= 문장을 이용한 변수의 설정

현재 dbakorea의 데이터베이스강좌게시판에 등록된 총 게시물은 43개이다. 43개의 강좌를 읽은 수(hit수)는 각각 다르다.
평균 hit수를 구해 보자.

mysql> select @total_hit := sum(hit), @total_record := count(*) from zetyx_board_database;
+------------------------+---------------------------+
| @total_hit := sum(hit) | @total_record := count(*) |
+------------------------+---------------------------+
| 3705 | 43 |
+------------------------+---------------------------+
1 row in set (0.00 sec)

mysql> select @total_hit/@total_record as 평균HIT;
+-----------------+
| 평균HIT |
+-----------------+
| 86.162790697674 |
+-----------------+
1 row in set (0.00 sec)



select substring(subject from 9) from zetyx_board_database where substring(subject, 1, 8) = '[ORACLE]';


보통 상용DBMS들이 row-level locking을 지원한다. 쉽게 말해 레코드단위로 락킹한다는 말이다.
반면, MySQL의 MyISAM 테이블타입은 table-level locking을 사용한다.
쉽게 말하면, insert, update, delete작업은 전체 테이블에 락을 걸고 처리된다는 것이다.
row-level락보다 비효율적이지만,.. MySQL은 빠르기 때문에 이 단점이 상쇄된다.

Compressed MyISAM(packed MyISAM)
정적인 테이블데이터는 압축하여 20-60%정도의 공간을 절약할 수 있다.
Production데이터를 CD로 받아서 차후 디스크에 풀지 않고 CD자체로 바로 사용할 수도 있다.
gzip등으로 백업받으면 이를 푸는 과정이 필요할 것이다.
% myisampack dbakorea.myi

데이터베이스 게시판의 Merge Table에 좀 더 자세한 내용을 적어 두었다.


RAID Table
1개의 테이블은 OS상에 3개의 파일로 구성된다.
스키마파일(.frm), data파일(.myd), index파일(.myi)
MySQL의 RAID테이블은 데이터파일(.myd)을 여러개의 파일들로 구성하는 것이다.

create table raid_test (...)
type=myisam raid_type=striped raid_chunks=4 raid_chunsize=8

테이블을 4개의 데이터파일로 나누고, 8kb단위로(8kb stripe) 라운드로빈 방식으로 write가 이루어진다.

This article comes from dbakorea.pe.kr (Leave this line as is)

'sql' 카테고리의 다른 글

mysql tunning  (2) 2009.01.07
mysql  (0) 2009.01.07
Mysql Data Type  (1) 2009.01.07
mysql password 잊어버렸을때  (0) 2009.01.07
mysql 백업  (0) 2009.01.07
Posted by 으랏차
,

'sql' 카테고리의 다른 글

mysql tunning  (2) 2009.01.07
mysql  (0) 2009.01.07
Mysql Data Type  (1) 2009.01.07
mysql query  (0) 2009.01.07
mysql 백업  (0) 2009.01.07
Posted by 으랏차
,

mysql 백업

sql 2009. 1. 7. 11:03

* 백업 하기!


    DB 백업은 콘솔상에서 mysql 빠져 나오신 다음에..

    => mysqldump -u ID -p TABLE명 혹은 DB명 > 백업할 파일이름.sql

    하고 비밀번호 입력하시면 백업이 되구요



* 복구하기

   => mysql -u ID -p < 백업한 파일이름.sql 하시고

   비밀번호 입력하시면 됩니다..


MySQL 접속]
mysql -hDB서버명 -uMySQL아이디 -pMySQL패스워드 데이타베이스명


[MySQL 백업하기]
MySQL 명령어로 백업받기 :
mysqldump -hDB서버명 -u유저명 -p패스워드 [백업할 테이터베이스명] > [외부로 저장할 파일명]
ex) mysqldump -hdb1.guideline.co.kr -uDBuser -pPassword MyDB > MyDB.sql

특정 테이블만 백업 할 경우 :
mysqldump -hDB서버명 -u유저명 -p패스워드 [테이타베이스명] [특정테이블명] > [외부로 저장할 파일명]
ex) mysqldump -hdb1.guideline.co.kr -uDBuser -pPassword MyDB MyTable > MyDB_MyTable.sql


[MySQL 복구하기]
mysql -hDB서버명 -u유저명 -p패스워드 [복구할 테이터베이스명] < [외부로 저장된 파일명]
ex) mysql -hdb1.guideline.co.kr -uDBuser -pPassword MyDB < MyDB.sql




출처 : http://cafe.naver.com/killtodbms/15



추가 내용 : 테이블 구조만 백업받기


mysqldump -h host -u user -ppassword -d database > script.sql

mysqldump -h 218.156.38.111 -u root -p1234 -d test > db_create_script.sql

이렇게 하면 test 데이터베이스 안의 모든 테이블의 구조만 db_create_script.sql 파일로 저장됩니다.

'sql' 카테고리의 다른 글

mysql tunning  (2) 2009.01.07
mysql  (0) 2009.01.07
Mysql Data Type  (1) 2009.01.07
mysql query  (0) 2009.01.07
mysql password 잊어버렸을때  (0) 2009.01.07
Posted by 으랏차
,

tomcat + mysql 한글깨짐

dev 2008. 7. 10. 17:42

1. server.xml 수정은 POST 방식만 지원되고 GET 은 지원안됨. -> 비추


2. 매번 페이지 상단에 정의 -> 추천


<%@ page contentType="text/html;charset=euc_kr" %>
<% request.setCharacterEncoding("euc-kr"); %>


3. 수동 인코딩 및 디코딩 -> 비추

String name = request.getParameter("name"); // 폼에서 가져오기

 

String name = new String(rs.getString("name").getBytes("8859_1"),"KSC5601"));

// DB에서 불러오기

 

4. JDBC 연결방법을 변경 -> 현재 사용중 가장 안정적인듯(?)


  기존에   conn = DriverManager.getConnection("jdbc:mysql://localhost/project","root","root");

  에서
  conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bbs?useUnicode=true&characterEncoding=UTF-8","root","root"); 변경해서 mysql 에 전송


5. (주의)mysql 에서 DB table 생성시


CREATE TABLE `bbs`.`freeboard` (
  `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL DEFAULT '',
  `password` VARCHAR(45),
  `email` VARCHAR(45),
  `subject` VARCHAR(50) NOT NULL DEFAULT '',
  `content` TEXT NOT NULL DEFAULT '',
  `inputdate` VARCHAR(20) NOT NULL DEFAULT '',
  `masterid` INTEGER UNSIGNED NOT NULL DEFAULT 0,
  `readcound` INTEGER UNSIGNED NOT NULL DEFAULT 0,
  `replynum` INTEGER UNSIGNED NOT NULL DEFAULT 0,
  `step` INTEGER UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY(`id`)
)ENGINE=MyISAM DEFAULT CHARSET="euckr";

 

결론 : 2, 4, 5 번작업을 해주면 mysql 로의 입력도 mysql 에서 불러오는 것도

안정적으로 동작합니다.^_^;

 

'dev' 카테고리의 다른 글

xml document handling  (0) 2008.07.24
동적 input 생성  (0) 2008.07.24
[tomcat]web.xml  (0) 2008.07.24
DOM 객체  (0) 2008.05.28
변환행렬...  (0) 2008.05.28
Posted by 으랏차
,