나는 SQL을 사용해야 하는 소프트웨어 개발자를 위해 SQL Antipatterns를 쓰고 있고, 따라서 여러분이 SQL을 좀더 효과적으로 사용할수 있도록 도울수 있음
SQL에 대하여 좋은예제와 나쁜예제를 어떻게 구별할 것인가?
구석으로 몰릴 편법이 아니라 좋은 관례를 배우고 있다고 어떻게 확신할수 있을까?
프로그래머 사이에 널리 퍼진 잘못된 생각을 검토함으로써 좋은 관례를 확인하고 강화하는 것은 바람직한 일임
SQL안티패턴을 사용하면 함께 일하는 소프트웨어 개발자에게, 좋은 관례와 이를 사용하지 않았을 때의 결과를 설명하는데 도움이 됨
이책은 안티패턴을 다음과 같은 넘주로 나눈 네개의 부로 구성됨
코딩을 시작하기 전에 데이터베이스에 어떤 정보를 저장할지,데이터를 어떻게 정리하고 서로 연결시키는 거이 좋을지를 결정해야함
여기에는 데이터베이스 테이블과 칼럼, 관계를 계획하는 것이 포함됨
어떤 데이터를 저장하지 결정한 다음에는 DBMS기술을 활용해 데이터를 가능한 효율적으로 관리해야함
여기에는 테이블과 인덱스를 정의하고 데이터 타입을 정하는 것이 포함됨
데이터베이스에 데이터를 입력하고 조회해야하함
SQL쿼리는 SELECT,UPDATE, DELETE같은 데이터 조작언어로 구성됨
SQL은 C++,Java,PHP,Python 또는 Ruby와 같은 다른 언어로 작성되는 애플리케이션 안에서 사용됨
애플리케이션에서 SQL을 사용하는데는 올바른 방법과 잘못된 방법이 있는데, 여기서는 흔히 발생하는 실수를 다룸
각 안티패턴은 다음과 같은 구조로 되어 있음
해결하고자 하는 작업을 임.
문제를 해결하기 위해 안티패턴을 사용하지만 문제가 해결되기보다는 더 많은 문제가 발생함
흔히 생각할수 있는 해법의 속성을 기술하고 이를 안티패턴으로 만드는 예상치 못한 결과를 설명함
프로젝트에서 안티패턴이 사용되려고 할 때 이를 인지하는데 도움이 되는 명확한 단서가 있을 수 있음.
어떤 형태의 장벽에 직명하거나 자신 또는 다른 사람이 하는 말을 들을 때 안티패턴의 존재를 눈치챌 수 있음.
규칙에는 예외가 있게 마련임
보통은 안티패턴으로 생각되는 접근법이 상황에 따라 적절한 것이거나 적어도 아주 나쁜것은 아닌 경우도 있음
안티패턴으로 인한 문제에 빠지지 않으면서 원래의 목표를 달성할 수 있는 더 좋은 해법을 설명함.
SQL문법과 용어에 대해서는 설명하지 않을 것임
성능, 확장적응성, 최적화 같은 주제는 데이터베이스 애플리케이션(특히 웹에서)을 개발하는 사람에게는 중요함 문제이나
데이터베이스 프로그래밍과 관련해 성능문제를 다룬 책이 있으니 다른책을 추천함
나는 모든 데이터베이스제품에 적용되는 문제와 모든데이터베이스 제품에서 작동하는 해법을 제시하려 노력함
SQL언어는 ANSI와 ISO표준으로 지정되었음
모든 데이터베으스 제품이 표준을 지원하기 때문에, 나는 가능한 밴더 중립적인 SQL을 사용해 설명했음
밴더 확장 SQL을 설명할 때는, 이를 명확히 하려했음
대부분의 예제 코드를 PHP로 가능한 쉽게 작성하였음
이책은 데이터베이스 관리자보다는 SQL언어를 사용하는 개발자를 대상으로함
다음은 이 책에서 사용한 관례를 설명함
SQL의 정확한 발은은 '시-퀄'이 아니라 '에스-큐-엘'임
SQL에서 '쿼리'와 '문장'은 어는 정도 같은 뜻으로, 둘다 실행할수있는 완전한 SQL명령문이란 뜻임
여기서는 SELECT문과 INSERT, UPDATE, DELETE문, 그리고 DDL문까지 모두 쿼리라고 할것임
관계형 데이터베이스에서는 ERD(Entity Relationshop Diagram)가 가장 많이 사용됨
테이블은 상자로 표시되고, 관계는 상자를 연결하는 선으로 표시됨
선의 끝은 관계의 카디날러티(cardinality)를 나타내는 표시가 있음
나는 가상의 버그추적 애플리케이션을 위한 데이터베이스를 사용해 SQL안티패턴을 설명할 것임
이 데이터베이스에 대한 ERD가 그림 1.2에 나타나있음
Bugs 테이블과 Accounts테이블은 세개의 줄로 연결되어 있는데 이는세개의 FK(Foreign key,외래키)를 나타내는 것임에 유의하기 바람
DROP TABLE IF EXISTS Accounts;
DROP TABLE IF EXISTS BugStatus;
DROP TABLE IF EXISTS Bugs;
DROP TABLE IF EXISTS Comments;
DROP TABLE IF EXISTS Screenshots;
DROP TABLE IF EXISTS Tags;
DROP TABLE IF EXISTS Products;
DROP TABLE IF EXISTS BugsProducts;
#START:create
CREATE TABLE Accounts (
account_id SERIAL PRIMARY KEY,
account_name VARCHAR(20),
first_name VARCHAR(20),
last_name VARCHAR(20),
email VARCHAR(100),
password_hash CHAR(64),
portrait_image BLOB,
hourly_rate NUMERIC(9,2)
);
CREATE TABLE BugStatus (
status VARCHAR(20) PRIMARY KEY
);
CREATE TABLE Bugs (
bug_id SERIAL PRIMARY KEY,
date_reported DATE NOT NULL,
summary VARCHAR(80),
description VARCHAR(1000),
resolution VARCHAR(1000),
reported_by BIGINT UNSIGNED NOT NULL,
assigned_to BIGINT UNSIGNED,
verified_by BIGINT UNSIGNED,
status VARCHAR(20) NOT NULL DEFAULT 'NEW',
priority VARCHAR(20),
hours NUMERIC(9,2),
FOREIGN KEY (reported_by) REFERENCES Accounts(account_id),
FOREIGN KEY (assigned_to) REFERENCES Accounts(account_id),
FOREIGN KEY (verified_by) REFERENCES Accounts(account_id),
FOREIGN KEY (status) REFERENCES BugStatus(status)
);
CREATE TABLE Comments (
comment_id SERIAL PRIMARY KEY,
bug_id BIGINT UNSIGNED NOT NULL,
author BIGINT UNSIGNED NOT NULL,
comment_date DATETIME NOT NULL,
comment TEXT NOT NULL,
FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id),
FOREIGN KEY (author) REFERENCES Accounts(account_id)
);
CREATE TABLE Screenshots (
bug_id BIGINT UNSIGNED NOT NULL,
image_id BIGINT UNSIGNED NOT NULL,
screenshot_image BLOB,
caption VARCHAR(100),
PRIMARY KEY (bug_id, image_id),
FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id)
);
CREATE TABLE Tags (
bug_id BIGINT UNSIGNED NOT NULL,
tag VARCHAR(20) NOT NULL,
PRIMARY KEY (bug_id, tag),
FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id)
);
CREATE TABLE Products (
product_id SERIAL PRIMARY KEY,
product_name VARCHAR(50)
);
CREATE TABLE BugsProducts(
bug_id BIGINT UNSIGNED NOT NULL,
product_id BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (bug_id, product_id),
FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id),
FOREIGN KEY (product_id) REFERENCES Products(product_id)
);
#END:create
INSERT INTO Accounts () VALUES ();
INSERT INTO Products (product_id, product_name) VALUES
(1, 'Open RoundFile'),
(2, 'Visual TurboBuilder'),
(3, 'ReConsider');
INSERT INTO Bugs (bug_id, summary) VALUES
(1234, 'crash when I save'),
(2345, 'increase performance'),
(3456, 'screen goes blank'),
(5678, 'unknown conflict between products');
INSERT INTO BugsProducts (bug_id, product_id) VALUES
(1234, 1),
(1234, 3),
(3456, 2),
(5678, 1),
(5678, 3);
INSERT INTO Comments (comment_id, bug_id, comment) VALUES
(6789, 1234, 'It crashes!'),
(9876, 2345, 'Great idea!');
INSERT INTO Tags () VALUES ();