1.  일련번호 필드

 

🍊 기본키는 레코드의 유일성을 보장할 뿐만 아니라 관계를 형성하는 중요한 역할

🍊 그러나 기본키가 강제 규정은 아니어서 의무적으로 만들어야 하는 것은 아니며 기본키로 쓸만한 필드가 없는 경우도 발생

 

🔖 다음은 슈퍼마켓의 매출 데이터이며 누가 어떤 상품을 구매했는지 기록

손님
상품
단군
지팡이
고주몽
고등어
박혁거세
계란
대조영
라면
박혁거세
화장지
고주몽
라면

 

  📍 이 경우 중복되지 않는 필드가 없어 기본키로 쓸만한 적당한 필드가 없음

  📍 정보 저장은 가능하지만 레코드를 식별하는 수단이 없어 검색이나 참조가 번거롭다.

     🔎  고주몽이 라면을 반품했다면 구입 기록을 삭제해야 하나 고주몽을 찾아 삭제하면 고등어도 같이 반품되고,

            라면을 찾아 삭제하면 대조영이 산 라면도 삭제 된다.

     🔎  손님과 상품을 모두 점검해서 레코드를 찾아야 하는데, 이 방법도 고주몽이 라면을 어제도 사고 오늘도 샀다면 완벽하지 않음.

 

⚡️ 억지로라도 기본키를 만드는 것이 좋은데 이때 유용한 값이 일련번호

⚡️ 계속 증가하는 일련번호를 붙이면 NULL도 아니고 고유성도 부여할 수 있음

 

 

🔖 위 테이블에 판매번호 필드를 추가

 

판매번호
손님
상품
1
단군
지팡이
2
고주몽
고등어
3
박혁거세
계란
4
대조영
라면
5
박혁거세
화장지
6
고주몽
라면

 

  📍 이제 일련번호만 알면 레코드를 특정할 수 있음

 

 

⚡️ 정수값 하나로 레코드를 식별할 수 있어 다른 조건은 더 필요 없다

⚡️ 판매번호를 PK로 지정하면 인덱스까지 생성하여 검색 속도가 빨라지고 다른 테이블과 관계를 맺을 수도 있음

⚡️ 일련번호를 붙이는 방법이 DBMS별로 조금씩 달라 각각 따로 익혀야 하는 불편함이 있음

 


 

 

2.  시퀀스

 

🍊 오라클과 SQL Server는 일련번호를 관리하는 시퀀스(SEQUENCE) 객체를 지원

      ➡️ 시퀀스는 객체를 초기화해 놓기만 하면 요청할 때마다 증가한 값을 자동으로 돌려줌

 

 

 

3.  자동 생성 시퀀스

 

🍊 시퀀스는 보통 특정 테이블의 필드 하나를 위해 생성하며 공유하는 경우가 적다.

🍊 따로 객체를 만드는 것보다 테이블을 만들 때 아예 시퀀스 필드로 지정하는 것이 효율적

    💡 단, 이 기능은 오라클만 지원

 


 

 

4.  ID 필드

 

🍊 SQL Server는 일련번호를 붙이는 더 간편한 방법을 제공한다.

🍊 자동 생성 시퀀스는 지원하지 않지만 비슷한 기능의 IDENTITY 속성이 있다.

 

 

 

5.  AUTO_INCREMENT

 

💡 MariaDB는 시퀀스를 지원하지 않는다.

💡 대신 IDENTITY와 거의 유사한 AUTO_INCREMENT 구문을 지원한다.

💡 필드 선언문에 AUTO_INCREMENT라고 선언하면 자동 증가하는 일련번호가 매겨진다.

CREATE TABLE tSale
(
saleno INT AUTO_INCREMENT PRIMARY KEY,
customer NCHAR(10),
product NCHAR(30)
);
 

 

INSERT INTO tSale (customer, product) VALUES ('단군', '지팡이');
INSERT INTO tSale (customer, product) VALUES ('고주몽', '고등어');
 

 

📌 saleno는 1부터 1씩 증가하는 일련번호이며 초기값, 증가값은 지정할 수 없다.

📌 삽입할 때 saleno는 빼고 나머지 필드의 값만 지정

 

 

 

DELETE FROM tSale WHERE saleno = 2;
INSERT INTO tSale (customer, product) VALUES ('박혁거세', '계란');
 

 

📌퀀스나 IDENTITY와 마찬가지로 삭제한 일련번호는 재사용하지 않음

📌 2번을 지운 후 삽입하면 3번으로 들어감

 

 

 

 

⚡️ 일련번호를 특정값으로 지정하는 것은 항상 가능하다.

⚡️ 다만 PK 제약상 기존 일련번호와 중복되지는 말아야

 

INSERT INTO tSale (saleno, customer, product) VALUES (2,'고주몽', '고등어');
 

 

 

📌 2번 레코드를 제자리에 다시 삽입.

 

 

 

 


 

 

💡 일련번호를 특정값으로 리셋할 때는 ALTER 명령을 사용

💡 일련번호를 리셋하면 이후 삽입하는 레코드는 리셋한 번호부터 순서대로 증가하는 값을 받는다.

💡 최후값을 알아낼 때는 LAST_INSERT_ID() 함수를 사용

 

ALTER TABLE tSale AUTO_INCREMENT = 100;
INSERT INTO tSale (customer, product) VALUES ('왕건', '너구리');
 

 

 

// 다음은 왕건이 구입한 너구리의 구매 기록을 찾아 짜파게티로 변경.
UPDATE tSale SET product = '짜파게티' WHERE saleno = LAST_INSERT_ID();
 

 


 

💡 자동 증가하는 일련번호 필드를 만드는 방법이 DBMS마다, 버전에 따라 다름

💡 단순히 자동 증가 필드를 만드는 것만이 목적이라면 다음 방법을 쓰는 것이 무난하다.

 


 *
오라클 : saleno INT GENERATED AS IDENTITY PRIMARY KEY

 * SQL Server : saleno INT IDENTITY PRIMARY KEY

 * MariaDB : saleno INT AUTO_INCREMENT PRIMARY KEY

 

⚡️ 이렇게만 해도 자동 증가열의 이점을 충분히 누릴 수 있고, 용어와 구문이 다를 뿐 개념은 같음

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

 

 


 

1.  식별자

🍋 테이블의 특정 레코드를 읽거나 변경하려면 레코드끼리 구분할 수 있는 고유의 키 (=식별자)가 필요

🍋 키는 1) 값이 꼭 있어야 하며, 2) 구분을 위해 고유값을 가져야 함

 

적합한 키
 주민등록번호, 학번, 사번, IP, ISBN
부적합한 키
 키, 몸무게, 제품 색상, 직급
애매한 키
 이름, 핸드폰, 이메일

 

🍋 조건을 만족하는 필드를 후보키 Candidate Key라고 하며 한 테이블에 여러 개의 후보키가 있음

🍋 이 중 레코드를 가장 잘 대표하는 키 하나를 골라 기본키 Primary Key (PK)로 선정

  💡 기본키가 충족해야 할 조건

       1)  대표성 : 레코드를 상징하는 값이어야 한다.

       2)  자주 참조하는 속성 : 기본키에는 기본적으로 인덱스가 생성되어 검색 효율이 좋다.

       3)  가급적 짧은 속성 : 테이블 간의 연결고리가 되므로 비교 속도가 빨라야 한다.

INSERT INTO tCity VALUES ('춘천',1116,27,'n','강원');
 

  📌  iCity는 도시명인 name이 기본 키이며, 위의 쿼리문은 에러가 생김.

  📌  춘천이 테이블에 이미 있는데 또 삽입하면 어떤 레코드가 진짜 춘천에 대한 정보인지 구분 x

         ➡️ 모호함이 생기면 무결성이 깨지므로 기본키에 대해서는 중복을 허락하지 않음

🍋 DBMS는 기본키를 특별하게 관리한다. NULL 금지와 중복 방지는 물론이고 인덱스를 생성하여 검색 속도를 높임.

🍋 기본키는 검색시 조건문에 활용하며 테이블간의 관계를 구성하는 연결고리로 사용

 

💡 테이블에 지정된 키를 보려면 다음 쿼리문을 입력,실행

 

SHOW KEYS FROM 테이블이름;

 


 

2.  기본키 설정

👩🏻‍💻 제약을 선언하는 위치에 따라 컬럼 제약과 테이블 제약이 있다.

👩🏻‍💻 컬럼 제약은 컬럼 선언 뒤에 위치하며 테이블 제약은 모든 컬럼 선언이 끝난 후 마지막 위치에 옴.

 

CREATE TABLE 테이블 ( 
   필드 선언, <- 이 위치에 오면 컬럼 제약 
   필드 선언, 
   필드 선언, 
            <- 이 위치에 오면 테이블 제약 
)

 

 

⚡️ NULL 허용 여부나, 기본값 등 컬럼에 대한 속성은 컬럼 제약으로 지정.

⚡️ 기본키는 컬럼 제약으로 선언할 수 도 있고 테이블 제약으로 선언할 수도 있음.

 

💡 각 제약의 형식

    * 컬럼 기본키 제약 : [CONSTRAINT 이름] PRIMARY KEY

    * 테이블 기본키 제약 : [CONSTRAINT 이름] PRIMARY KEY(대상 필드)

⚡️ PRIMARY KEY 제약은 NOT NULL 속성을 겸함.

⚡️ 제약의 이름을 생략하면 서버가 자동으로 이름을 붙임.

 

CREATE TABLE tCity ( 
  name CHAR(10), 
  area INT NULL , 
  popu INT NULL , 
  metro CHAR(1) NOT NULL, 
  region CHAR(6) NOT NULL, 
  CONSTRAINT PK_tCity_name PRIMARY KEY(name) 
 );

 


 

3.  복합키

👩🏻‍💻 기본키 필드가 꼭 하나여야 한다는 법은 없으며 하나의 필드만으로 레코드를 특정하기 어려운 경우가 있다.

👩🏻‍💻 이런 경우 두 개 이상의 필드를 묶어 기본키로 지정. 이것을 복합키 Composite Key라고 한다.

  ⚡️ tCity 테이블의 경우, 도시명이 중복되지 않는다는 가정을 하고 있지만 현실은 다름

  ⚡️ 경기도 광주, 전라도 광주 처럼 도시 이름이 중복되는 경우가 있는데 tCity 테이블의 구조로는 두 도시 모두 입력할 수 없음

         ➡️ 이름만으로 도시를 특정할 수 없으니 지역과 함께 묶어서 기본키로 정의해야 함.

 

CREATE TABLE tCity ( 
   name CHAR(10) PRIMARY KEY, 
   region CHAR(6) PRIMARY KEY, 
   area INT NULL , 
   popu INT NULL , 
   metro CHAR(1) NOT NULL 
);

 

    📌  name, region 두 개의 필드에 PRIMARY KEY를 지정하면 에러가 남 (테이블당 기본키는 하나만 지정 가능)

    📌  복합키로 지정할 때는 테이블 제약으로 기본키를 지정하며 괄호 안에 필드 목록을 콤마로 구분하여 나열한다.

 

CREATE TABLE tCityCompoKey ( 
  name CHAR(10) NOT NULL, 
  region CHAR(6) NOT NULL, 
  area INT NULL , 
  popu INT NULL , 
  metro CHAR(1) NOT NULL, 
  CONSTRAINT PK_tCity_name_region PRIMARY KEY (name, region) 
);

INSERT INTO tCityCompoKey VALUES ('광주', '전라', 123, 456,'y'); 
INSERT INTO tCityCompoKey VALUES ('광주', '경기', 123, 456,'n');

 

📌 겹치는 name 이 있어도 에러없이 정상적으로 실행

📌 복합키도 중복값을 허용하지 않지만 복합키를 구성하는 개별 키는 중복해도 무방

       ➡️  두 필드가 동시에 같지만 않으면 됨.

 


 

4.  유니크

👩🏻‍💻 유니크 UNIQUE 제약은 필드의 중복값을 방지하여 모든 필드가 고유한 값을 가지도록 강제

👩🏻‍💻 유니크는 기본키를 보조하는 중복 방지 제약

 💡 기본키 제약과의 차이점

      1)  기본키는 NULL을 허용하지 않지만 유니크는 NULL을 허용

            ➡️  단 NULL끼리도 중복 할 수 없어 딱 하나의 NULL만 존재할 수 있음

      2)  UNIQUE와 NOT NULL을 동시에 지정하면 기본키와 유사해짐.

            ➡️  기본키는 테이블당 하나만 지정할 수 있지만 유니크는 개수에 상관없이 얼마든지 지정 가능

      3)  기본키는 자동으로 인덱스를 생성하여 레코드의 정렬 순서를 결정하지만 유니크는 그렇지 않다

             ➡️  인덱스를 생성하더라도 기본키의 인덱스와는 종류와 효율이 다름

  📌  만일 tCity 테이블이 도시끼리 인구수가 같아서는 안된다는 규칙이 있다면 popu필드에 대해 UNIQUE 제약을 설정

 

CREATE TABLE tCityUnique ( 
   name CHAR(10) PRIMARY KEY, 
   area INT NULL , 
   popu INT UNIQUE NULL, 
   metro CHAR(1) NOT NULL, 
   region CHAR(6) NOT NULL 
);

 

  📌  이 테이블의 도시끼리는 인구가 같아서는 안됨. NULL은 가능하지만 하나만 가능.

  📌  NULL을 허가하지 않으려면 UNIQUE NOT NULL로 지정. 두 개 이상의 키를 묶어 복합 UNIQUE 제약을 걸 수도 있음

 

CREATE TABLE tCityUnique ( 
   name CHAR(10) PRIMARY KEY, 
   area INT NULL , 
   popu INT NULL, 
   metro CHAR(1) NOT NULL, 
   region CHAR(6) NOT NULL, 
   CONSTRAINT Unique_tCity_area_popu UNIQUE(area, popu) 
 );

  

  📌  이 테이블의 도시는 이름이 고유해야 하며 area와 popu가 모두 같아서는 안됨. 둘 중 하나라도 달라야 함.

 


 

5.  체크

👩🏻‍💻  체크 제약은 필드의 값 종류를 제한

👩🏻‍💻  모든 속성은 유의미한 범위가 있고 상식적으로 가능한 값과 그렇지 않은 값이 있음.

       ➡️ 예를 들어 도시가 아무리 거대해도 인구 100억을 넘길 수는 없고 면적이 음수가 될 수 없음.

       ➡️ 체크 제약은 이런 무의미한 값을 걸러냄

👩🏻‍💻 타입은 물리적인 형식을 점검하는데 비해 체크는 논리적인 값의 형식을 점검

  ⚡️ 필드 선언문에 CHECK 키워드와 함께 필드값으로 가능한 값을 조건문으로 지정

  ⚡️ WHERE 절의 조건을 지정하는 모든 문법을 쓸 수 있다.

 

CREATE TABLE tCheckTest ( 
   gender CHAR(3) NULL CHECK(gender = '남' OR gender = '여'), 
   grade INT NULL CHECK(grade >= 1 AND grade <= 3), 
   origin CHAR(3) NULL CHECK(origin IN ('동','서','남','북')), 
   name CHAR(10) NULL CHECK(name LIKE '김%') 
);

 

 

  📌  각 필드에 조건문으로 체크 제약을 지정

  📌  성별을 저장하는 gender 필드는 '남', 아니면 '여'만 가능

         ➡️ gender가 둘 중 하나임을 명시하여 이 두 값 이외에는 입력을 금지

  📌  grade는 중고등학교의 학년을 표현하는데 1 ~ 3까지만 유효

         ➡️ 초등학교라면 6까지, 대학교라면 4까지 범위를 늘이면 됨.

         ➡️ 일정 범위내일 때는 최소값, 최대값을 지정하는 대신 BETWEEN AND 조건문이 편리

              ex. grade INT NULL CHECK (grade BETWEEN 1 AND 3)

  📌  origin 필드는 방위를 나타내는데 동서남북 넷 중 하나만 가능

         ➡️ 여러 개의 임의값 중 하나를 지정할 때는 IN 연산자가 편리

  📌  name 필드는 성씨가 김씨인 경우만 받아들임

         ➡️ 부분 문자열을 점검할 때는 LIKE 연산자를 사용

 

 
// 에러 없이 실행 가능한 쿼리문들 
INSERT INTO tCheckTest (gender) VALUES ('여'); 
INSERT INTO tCheckTest (grade) VALUES (1); 
INSERT INTO tCheckTest (origin) VALUES ('동'); 
INSERT INTO tCheckTest (name) VALUES ('김좌진');

// 제약조건을 위반하여 에러 처리가 되는 쿼리문들 
INSERT INTO tCheckTest (gender) VALUES ('노'); 
INSERT INTO tCheckTest (grade) VALUES (0); 
INSERT INTO tCheckTest (origin) VALUES ('중'); 
INSERT INTO tCheckTest (name) VALUES ('청산리');

 

  ⚡️ 삽입할 때 뿐만 아니라 UPDATE할 때도 체크 제약 조건을 점검

 
UPDATE tCheckTest SET grade = 4 WHERE grade IS NOT NULL;

 

  📌 4학년은 체크 조건에 위배되어 아무 레코드도 갱신하지 않음

 

// region 필드는 반드시 체크 제약 조건에 지정한 지역 중의 하나여야 한다. 
region CHAR(6) NOT NULL CHECK (region IN ('경기', '충청', '강원','경상', '전라', '제주'));
INSERT INTO tCity VALUES ('울릉',72,1,'n','우산'); // 이 문장은 에러처리
 

  📌 '우산'이라는 지역명을 인정하지 않기 때문에 에러

  📌 서버는 레코드를 삽입, 변경할 때마다 제약 조건을 점검하며 클라이언트에서 무슨 짓을 하더라도 이 계약 조건을 어길 수 없음

        ∴  따라서 tCity에 있는 모든 도시는 6개 지역 중 하나에 소속됨을 보장 할 수 있음

[ 내용 참고 : IT 학원 강의 ]

 


 

 

1. 무결성 관리

 

👩🏻‍💻 모든 데이터가 결함없이 완벽한 상태를 무결성 Intergrity 이라고 함

 

  📍 시스템이 완벽해도 무결성이 깨지는 경우가 종종 발생

        ➡️수나 에러로 잘못된 데이터를 입력할 수도 있고 불가항력적인 요인으로 멀쩡하던 데이터가 파괴되기도 함

  📍 무결성이 깨지면 데이터를 사용하는 응용 프로그램도 제대로 돌아가지 x

 

 

    🔎  예를 들어 컴퓨터 부품을 파는 쇼핑몰의 주문 테이블을 살펴보자

 

// 날짜 회원 제품 개수
2020-03-04 가희원 하이넥스 메모리 DDR 64G 2
2020-03-09 나종원 엘지 그램 노트북 15 32767
2042-12-25 다민기 ATI 그래픽 카드 -8242
2020-04-01 라진규 NULL 3

  📍 첫 번째 레코드만 정상이고 이후의 레코드는 이상해 보임

        ➡️ 나종원의 노트북 32,767개는 정상 주문으로 보기 힘들고,

             다민기의 경우 주문날짜도 2042년이고 주문 개수도 음수

             라진규는 주문한 제품의 이름을 입력하지 않았음.

 

 

👩🏻‍💻  무결성이 깨지는 원인은 다양. 디스크가 기계적으로 손상되거나 네트워크의 불안정으로 데이터가 손실되는 경우, 가장 큰 원인은 사람의 실수

👩🏻‍💻  발생 가능한 모든 오류에 대처하는 방어 체계로 데이터를 처리하는 단계별로 서버측과 클라이언트측이 각자의 무결성 관리 정책을 제공

  📍 '클라이언트측'에서는 입력할 정보에 꼭 맞는 컨트롤로 오입력을 원천 차단, 유효한 값만 서버로 보냄.

  📍 '서버측'의 무결성 처리는 DBMS가 규칙을 기억하고 있다가 비정상 데이터의 입력을 거부하는 방식

 

 

[ 적용 범위에 따른 무결성 구분 ]

 *  컬럼 무결성 컬럼 하나에 저장되는 원자적인 값을 점검하며 도메인 무결성이라고도함
      ➡️ 타입 지정, 널 허용 여부, 체크, 기본값 등의 제약이 있음.

 *  엔티티 무결성 레코드끼리 중복값을 가지지 않도록 하여 유일한 식별자를 관리.
     예를 들어 주민등록번호가 같은 두 명이 존재하지 않아야 함. 기본키와 유니크 제약이 있음.

 *  참조 무결성 테이블간의 관계를 구성하는 키가 항상 유효하도록 관리하며 외래키 제약으로 관리.

 

 

INSERT INTO tCity VALUES ('대구', '꽤 넓음', '200만명', 'n', '대한민국'); // 실행시 에러가 남
 

  📍 인구나 면적은 수치형이어서 123 같은 숫자만 가능하며 문자열은 저장할 수 없음. 타입이 맞더라도 지정한 길이를 초과 x

  📍 타입은 물리적인 형태만 지정할 뿐 논리적인 값까지 점검하지는 못 함  ex. 음수 or 지나치게 큰 수

        ➡️ 논리적인 값까지 저장할 수 있는 장치가 필요. DBMS는 주로 제약을 사용

 

👩🏻‍💻  '제약 Constraint'은 조건을 위반하는 데이터를 방지하여 완결성을 보장하는 규칙

  📍 제약이 없으면 무결성이 쉽게 깨짐

  📍 편집창에서 필드의 입력값 형태를 미리 지정해두는 것이 바로 제약이며 이 규칙에 맞는 데이터만 입력받음

        ➡️ 쿼리문에서는 NULL, NOT NULL, PRIMARY KEY 같은 속성으로 제약을 지정

 

 


 

2.  NULL 허용

 

👩🏻‍💻 NULL은 아무것도 입력되지 있지 않은 것이며 알 수 없거나 결정되지 않은 특수한 상태를 의미

👩🏻‍💻 필드의 NULL 허용 속성은 NULL 상태가 존재하는지를 지정

 

  ⚡️ 반드시 입력해야 하는 필수 필드는 NULL을 허용 x, 없어도 괜찮은 필드는 NULL을 허용 o

         ➡️ 필수 정보 : 아이디, 이름, 비밀번호, 이메일

        ➡️ 필수 정도 아닌 것 : 홈페이지, 주소, 전화번호

  ⚡️ Nullable (Null 허용), Not Nullable(Null 허용 x)

 

// tCity 테이블의 생성 스크립트
CREATE TABLE tCity (
 name CHAR(10) PRIMARY KEY,
 area INT NULL ,
 popu INT NULL ,
 metro CHAR(1) NOT NULL,
 region CHAR(6) NOT NULL
);
 

  📌  name, metro, region 필드는 PRIMARY KEY 또는 NOT NULL 속성이 있어 NULL을 허용하지 않음을 지정

  📌  반면 area와 pupu 필드는 NULL 속성으로 지정되어 있어 생략 가능

 

 

👩🏻‍💻  INSERT 명령으로 레코드를 삽입할 때 NOT NULL로 지정한 필드는 반드시 입력해여 함

       ➡️ 필수 정보 중 하나라도 빼 먹으면 정상적인 정보가 아니라고 판단하여 레코드 삽입을 거부

// 다음 두 쿼리문은 정상 실행.
INSERT INTO tCity (name, popu, metro, region) VALUES ('울산', 114, 'y', '경상');
INSERT INTO tCity (name, metro, region) VALUES ('삼척', 'n', '강원');
 

 

  📌 울산은 면적을 모르는 상태여서 area 필드는 빼고 입력 ( 생략한 area는 NULL이 됨 )

  📌 삼척은 인구, 면적을 다 생략

 

 


 

// 다음 두 쿼리문은 에러가 남.
INSERT INTO tCity (area, popu, metro, region) VALUES (456, 123, 'n', '충청');
INSERT INTO tCity (name, area, popu) VALUES ('의정부', 456,123);
 

📌 충청도에 있는 어느 도시인데 이름은 없음. 필수 값이 누락되어 에러

📌 의정부는 있지만 소속과 광역시 여부가 없어 역시 무효한 정보이면 DBMS는 이런 무효한 레코드의 삽입을 거부

 


 

⚡️ NULL 허용 여부를 생략해 버리면 어떻게 될까?

 

CREATE TABLE tNullable (
 name CHAR(10) NOT NULL,
 age INT
);
 

  📌  name은 필수 필드임이 분명하나 age는 정수형이라는 것만 밝혔을 뿐 NULL 허용 여부에 대한 지정이 없음

  📌  NULL 허용 여부를 생략하면 DBMS의 디폴트 허용 여부를 따른다.

 

INSERT INTO tNullable (name, age) values ('흥부', 36);
INSERT INTO tNullable (name) values ('놀부');
INSERT INTO tNullable (age) values (44);
 

📌 모든 필드값을 다 입력한 흥부는 에러 없이 입력이 된다.

📌 나이를 생략한 놀부는 DBMS의 설정에 따라 입력될 수도 있고 아닐 수도 있다.

      ➡️ 대부분의 DBMS는 NULL 허용이 디폴트여서 이상 없이 삽입될 확률이 높음

📌 이름을 생략하고 나이 44세만 입력하면 에러

 

 


 

⚡️ NULL 허용 속성은 디폴트는 DBMS마다 다르며 데이터베이스나 연결 수준에서 변경할 수도 있다.

⚡️ 그러나 설정에 영향을 받는 명령은 일관성이 없어 위험함

⚡️ 속성을 생략하지 말고 필드 선언문 끝에 항상 NULL, NOT NULL을 분명히 명시하는 것이 바람직하다.

 

 


 

3.  기본값

 

👩🏻‍💻 NULL 허용 속성은 데이터베이스의 성능을 저해하는 주범

       ➡️ 항상 NULL 상태를 감안하여 필드값이 존재하는지 점검해야 하고 보통의 값과는 다루는 방식이 달라 느릴 수 밖에 없다.

👩🏻‍💻 NULL 허용 대신 기본값 DEFAULT을 사용하는 것이 성능상 유리.

👩🏻‍💻 기본값은 필드값을 지정하지 않을 때 자동으로 입력할 값

      ➡️ 보통 무난한 값을 지정하는데 수치형은 0이 적당하고 문자열을 비워 두거나 'N/A'등을 많이 사용

 

 

⚡️ 도시중 광역시는 몇 개 되지 않으며 대부분 지역에 소속

⚡️ 이럴 때 metro 필드 속성에 DEFAULT 키워드와 함께 디폴트 값을 'n'으로 지정

⚡️ DEFAULT 키워드는 NULL 허용 속성보다 앞에 와야 함 (MariaDB는 순서를 강제하지 않지만 오라클은 순서를 바꾸면 에러 처리)

CREATE TABLE tCityDefault (
 name CHAR(10) PRIMARY KEY,
 area INT NULL ,
 popu INT NULL ,
 metro CHAR(1) DEFAULT 'n' NOT NULL, // 값을 괄호로 감싸 DEFAULT('n')이라고 적어도 됨
 region CHAR(6) NOT NULL
);
 
 

  📌 새로운 레코드를 삽입할 때 metro 필드를 지정하지 않으면 자동으로 'n'을 적용

  📌 기본값은 입력하지 않을 때만 적용하는 것, 값을 직접 지정하면 기본값을 무시하고 지정한 값을 적용

 

INSERT INTO tCityDefault (name, area, popu, region) 
  VALUES ('진주', 712, 34, '경상');
INSERT INTO tCityDefault (name, area, popu, metro, region)
  VALUES ('인천', 1063, 295, 'y', '경기');
 

 

📌 진주시는 면적, 인구, 지역만 지정하고 광역시 여부는 생략.

      ➡️ 필드 목록에 metro가 아예 없는데 이 경우 디폴트가 적용되어 광역시가 아닌 것으로 삽입

📌 인천시는 모든 필드의 값을 다 제공하여 광역시로 등록

 


 

⚡️ 기본값 설정도 편집 가능해서 테이블 생성시와 달라질 수 있다.

 

INSERT INTO tCityDefault VALUES ('강릉', 1111, 22, '강원'); -- 에러
INSERT INTO tCityDefault VALUES ('강릉', 1111, 22, DEFAULT, '강원'); -- 정상 실행
 

  📌 필드 목록을 생략하면 값 목록이 완전해야 함

  📌 metro에 기본값이 있더라도 값 목록에 이 자리는 비우면 안된다.

  📌 값 목록을 완전히 적되 기본값을 적용할 필드값에 DEFAULT라고 적음. 기본값으로 변경할 때도 DEFAULT 키워드를 사용.

 

// 다음 명령을 인천의 metro 필드를 기본값인 'n'으로 변경.
 UPDATE tCityDefault SET metro = DEFAULT WHERE name = '인천'
 

 

 

 

⚡️ 기본값의 유무와 NULL 허용 여부는 완전히 별개의 속성임을 주의

⚡️ 기본값이 지정되어 있더라도 NULL을 직접 입력할 수 있고, UPDATE 명령으로 NULL로 바꿀 수도 있음.

⚡️ 기본값은 생략시 적용할 값일 뿐이지 NULL 허용 여부까지 통제하는 것은 아님.

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

 


 

 

1.  단일행 서브쿼리

 

💡 서브쿼리란? 쿼리문 안에 또 쿼리문이 들어 있는 것

💡 서브쿼리는 다른 쿼리문안에 내장되어 있는 SELECT 문이며 연속적으로 실행할 쿼리를 하나로 합침

💡 SELECT로 할 수 있는 질문은 짧은 단문만 가능, FROM 절이 하나밖에 없어 한 테이블에 있는 정보만 조사할 수 있다.

      ➡️ 실제 작업을 할때는 복잡한 여러 단계의 질문을 한꺼번에 하는 경우가 많이 생김.

     ➡️ 복잡한 쿼리를 실행할 수 있는 방법이 바로 서브쿼리.

 


 

SELECT MAX(popu), name FROM tCity;

  📖  최대 인구수를 가진 도시를 구하는 문제

      🔎  MAX 함수의 결과는 집계한 하나의 값이고 name 필드는 도시명 여러 개여서 이 둘을 같이 출력 불가

      🔎  MAX(popu)는 최대 인구수를 집계한 것이 맞지만 name 도시중 어떤 도시가 그 인구수를 가진 것인지는 알 수 없음.

             ➡️ 즉, 왼쪽 열과 오른쪽 열이 연관성이 없음.

  📖  기계가 이 명령을 알아 들으려면 문법적으로 가능한 질문을 순서대로 해야 한다.

 

SELECT MAX(popu) FROM tCity; // 974

 

  1. 전체 도시의 인구 중 가장 큰 값을 조사

SELECT name FROM tCity WHERE popu = 974;

  2. 이 인구수를 가진 도시를 찾음

 

  📖  최대 인구수를 조사하고 이 수로부터 도시의 이름을 구해야 하니 두 번의 쿼리가 필요

        ➡️ 두 개의 쿼리를 하나로 묶어 실행할 때 쓰는 것이 서브쿼리

 

  💡 이 때 서브쿼리를 감싸는 전체 쿼리를 외부쿼리라 부름

       ➡️ 외부쿼리와 구분하고 실행 순서를 명확히 지정하기 위해 서브쿼리를 괄호로 감쌈

SELECT name FROM tCity WHERE popu = (SELECT MAX(popu) FROM tCity);

  📖  서브쿼리로 최대 인구수를 가진 도시명을 구함

        1. 괄호 안의 서브쿼리를 먼저 실행하여 최대 인구수를 구함.

        2. 서브쿼리를 포함하고 있는 외부쿼리를 실행하여 popu가 서브쿼리가 구한 최대 인구수와 같은 도시를 조사하여 출력.

 

💡 단일행 서브쿼리는 하나의 결과만 리턴하며 주로 WHERE, HAVING 등의 조건절에 사용

💡 복합 질문의 앞쪽 질문에 해당하는 값을 서브 쿼리로 조사해 놓고 외부쿼리에서 그 결과값을 사용하는 식으로 작성한다.

 

 

 

2.  서브쿼리 중첩

 

💡 서브쿼리는 독립적인 하나의 명령이기 때문에 외부쿼리와는 '다른 테이블을 읽을 수도 있음'

💡 순차적으로 실행되므로 두 쿼리문의 FROM 절에 각각 다른 테이블을 지정해도 된다.

// 청바지 배송비가 얼마인지 조사
// 청바지는 상품 테이블 tItem 에 있지만 배송비는 유형 테이블 tCategory에 있어서 두 테이블을 읽어야 함
// 먼저 tItem 테이블에서 청바지는 어떤 유형의 상품인지 조사
SELECT category FROM tItem WHERE item= '청바지';
// 청바지는 '패션' 유형. 다음은 패션 유형의 배송비를 조사.
SELECT delivery FROM tCategory WHERE category = '패션';
// 배송비는 2,000원. 이제 둘을 하나로 합침.
// WHERE 절의 '패션' 자리에 상수 대신 '패션'이라는 결과를 만들어 내는 서브쿼리를 작성하고 괄호를 둘러쌈
SELECT delivery FROM tCategory WHERE category =
(SELECT category FROM tItem WHERE item = '청바지');
 

💡 서브쿼리의 중첩 횟수에는 제약이 없어 서브쿼리내에 또 다른 서브쿼리를 포함할 수 있음

💡 리턴 값이 여러 개면 에러 처리되는데 이때는 TOP 1 (LIMIT 1) 을 넣어 첫 해당 값을 찾으면 된다.

 

 

 

3.  다중행 서브쿼리

 

💡 서브쿼리의 '결과가 하나뿐인 유형'을 단일행 서브쿼리

      ➡️ 단일값이므로 조건절에서 =, <, > 등의 비교 연산자와 함께 사용할 수 있음.

      ➡️ 비교 연산자는 필드의 값과 비교하는 것이어서 '우변이 반드시 하나의 확정된 값'이어야함.

💡 이에 비해 여러 행을 리턴하는 것이 다중행 서브쿼리

      ➡️ 단일값이 아닌 목록을 리턴하기 때문에 값끼리 비교하는 비교 연산자와 함께 사용할 수 없음

 

// 다음 쿼리문은 문법에는 이상이 없지만 데이터가 조건에 맞지 않아 에러 처리.
SELECT price FROM tItem WHERE item =
(SELECT item FROM tOrder WHERE member = '향단');
 

  📖  서브쿼리는 구입목록인 tOrder 테이블에서 향단이가 뭘 샀는지 조사

        ➡️ 안쪽 서브쿼리만 실행해 보면 향단이가 산 것은 대추와 사과 2개

        ➡️ = 연산자는 필드값을 두 개의 결과와 비교할 수 없어 다음 에러를 출력.

[21000][1242] (conn=327) Subquery returns more than 1 row
 

  📖 ' = '연산자로 비교하려면 조건절의 쿼리문은 반드시 하나의 단일 값을 리턴해야만 한다.

  📖  꼭 조사하려면 결과 셋 중 하나의 값만 조사해서 비교하면 됨

 

SELECT price FROM tItem WHERE item =
(SELECT item FROM tOrder WHERE member = '향단'
ORDER BY item LIMIT 0, 1);
 

📖 구입한 상품 중 하나의 상품만 리턴하면 = 연산자로 비교할 수 있음.

📖 그러나 질문대로 향단이가 구입한 모든 상품의 가격을 알고 싶다면 IN 연산자를 사용

 

SELECT item, price FROM tItem WHERE item IN
(SELECT item FROM tOrder WHERE member = '향단');
 

💡  IN은 = 과 달리 여러 개의 값과 비교

  📌 서브쿼리가 두 개 이상의 값을 리턴하면 결과셋을 괄호 안에 나열하며 IN 연산자는 이 값을 순서대로 비교

// 서브쿼리까지 실행한 직후의 외부쿼리는 다음과 같음.
// 2개의 상품에 대해 이름과 가격을 같이 출력.
SELECT item, price FROM tItem WHERE item IN ('대추', '사과');
 

 


 

💡 결과가 없으면 NULL을 리턴하며 조건절에서 필드와 NULL을 비교하는 것은 문법적으로 가능.

      ➡️ 아무 결과셋도 출력하지 않을 뿐 에러는 아니다.

 

 

 

4.  다중열 서브쿼리

 

💡 단일행, 다중행 서브쿼리는 '결과셋의 컬럼이 하나'밖에 없으며 그래서 특정값과 비교할 수 있음.

💡 다중열 서브쿼리는 결과셋의 컬럼이 여러 개이며 한꺼번에 여러 값과 비교

 

  📌  윤봉길과 같은 부서에 근무하는 같은 성별의 직원 목록을 조사

  📌  먼저 윤봉길이 어느 부서에 근무하고 성별이 무엇인지 조사해야 하며 그 결과로부터 조건에 맞는 직원을 찾는다

SELECT depart, gender FROM tStaff WHERE name = '윤봉길'; // 윤봉길 영업부에 근무하는 남자 직원
SELECT * FROM tStaff WHERE depart = '영업부' AND gender = '남'; // 조건에 맞는 직원 목록을 조사
 

 

    🔎  윤봉길 자신까지 포함하여 5명의 직원이 있음

    🔎  임의의 직원에 대해 똑같은 조사를 하려면 두 번 손이 가게 되어서 번거로움

           ➡️ 조사 후 조건에 맞는 목록 출력까지 한 번에 수행하는 서브쿼리가 필요

 

 

  💡 다중열 서브쿼리를 사용하면 두 개의 컬럼을 리턴한 후 한꺼번에 비교

SELECT * FROM tStaff WHERE (depart, gender) =
(SELECT depart, gender FROM tStaff WHERE name = '윤봉길');
 

  📌  WHERE 절에 비교 대상 필드를 괄호 안에 (depart, gender) 적으면 서브쿼리의 컬럼과 1:1로 비교하여 두 필드가 일치하는 레코드를 검색

  📌  단 일괄 비교가 성립하려면 비교 대상과 서브쿼리의 컬럼 개수는 반드시 일치해야 함.

 

 


 

💡  서브쿼리가 다중열이면서 다중행인 결과셋을 리턴하면 복수 레코드의 복수 필드를 한꺼번에 비교 가능

 

  📌  부서별 최고 월급자의 목록을 조사. 여러 개의 부서가 있고 각 부서명과 최고 월급을 비교

 SELECT * FROM tStaff WHERE (depart, salary) IN
   (SELECT depart, MAX(salary) FROM tStaff GROUP BY depart);
 

 

    🔎  안쪽의 서브쿼리는 부서별로 그룹핑하여 부서명과 월급을 구함

    🔎  부서별로 두개의 컬럼이 있고 그런 부서가 세 개있으니 다중열 다중행 결과셋이고 결국 하나의 테이블

 

  📌  그룹핑할 때는 기준 필드나 집계함수만 쓸 수 있어 부서별 최고 월급까지만 조사할 수 있고 그 직원이 누구인지는 알 수 없음

         ➡️ 그러나 외부쿼리로 한 번 감싸 부서명과 최고 월급을 조건으로 사용하면 모든 필드 조사할 수 있음.

  📌  결과셋이 다중행이므로 반드시 IN 연산자를 비교해야 함

 

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]



 


 

1. UPDATE

UPDATE 테이블이름 SET 필드 = 값, 필드 = 값 ··· WHERE 조건 ;

 

👩🏻‍💻 레코드의 필드 값을 변경할 때 사용

     a. UPDATE 키워드 다음에 변경 대상 테이블을 지정

     b. SET 키워드 다음에 필드에 값을 대입하는 대입문이 오며 콤마로 끊어 복수 개의 필드를 한꺼번에 변경

     c. WHERE 절에는 변경 대상 레코드를 지정하는 조건문을 작성.

👩🏻‍💻 조건문을 생략하면 모든 레코드가 갱신

👩🏻‍💻 조건에 맞는 특정 레코드를 찾아 변경하는 것이 일반적이므로 UPDATE 문도 DELETE 문과 마찬가지로 통상 WHERE 절과 함께 사용.

 

UPDATE tCity SET popu = 1000, region = '충청' WHERE name = '서울';
 

🔽

 

📌 서울의 인구를 1000만명으로, 지역을 충청도로 변경

 

 

 

UPDATE tCity SET popu = 1000, region = '충청';

 

🔽

 

📌  WHERE 절을 뺄수 있는데, 이 경우에는 모든 데이터가 변경이 되는 사고가 발생

 

 

👩🏻‍💻 SET 문에는 필드끼리 연산도 가능

UPDATE tCity SET popu = popu * 2 WHERE name = '오산';
 

🔽

📌  popu의 원래 값을 읽어 그 두 배 값을 다시 popu에 대입하여 오산의 인구를 두 배로 늘림.

[ 내용 참고 ; IT 학원 강의 ]

'Database > MySQL' 카테고리의 다른 글

[MySQL] 제약조건 | 무결성, NULL 허용, 기본값  (1) 2024.02.24
[MySQL] 서브쿼리 (SubQuery)  (1) 2024.02.24
[MySQL] DELETE 문  (1) 2024.02.24
[MySQL] INSERT 문  (0) 2024.02.23
[MySQL] GROUP BY, HAVING  (0) 2024.02.23

 


 

 

1. DELETE

 

DELETE FROM 테이블 WHERE 조건 ;
 

👩🏻‍💻 레코드를 삭제할 때 사용한다.

👩🏻‍💻 삭제는 항상 레코드 단위로 수행하므로 필드에 대한 지정은 없음.

👩🏻‍💻 삭제 동작은 특정 조건에 맞는 레코드를 찾아 제거하는 경우가 대부분이어서 WHERE 절이 항상 뒤따라 온다.

 

DELETE FROM tCity WHERE name = '부산';
SELECT * FROM tCity;
 

⬇️

 

📌  도시명이 '부산'인 레코드를 삭제

 

 

 

 

2. 삭제 안전 장치

 

👩🏻‍💻  삭제시 문제가 되는 부분은  1) 조건절을 빼거나 2) 잘못된 조건을 사용하는 경우

👩🏻‍💻  조회만 하는 SELECT 명령은 테이블을 변경하지 않아 실수해도 조건을 바꿔 다시 조회하면 됨.

       INSERT 명령은 테이블을 변경하지만 혹시라도 잘못 삽입했으면 다시 지우면 되어서 그다지 위험하지 않음

       하지만 잘못 내린 DELETE 명령은 즉시처리해 버림.

👩🏻‍💻  DELETE문은 특정 레코드를 삭제하기 위해 WHERE 절과 함께 사용하지만 실수로 WHERE 절을 생략해 버리면 모든 데이터가 삭제됨

 

 

📌  만일 월급이 300 초과인 직원을 삭제한다면 다음 절차대로 작업

// 1. DELETE 명령을 내리기 전에 먼저 SELECT로 삭제 대상 레코드를 고름
      SELECT * FROM tStaff WHERE salary > 300;
// 2. 조건을 맞는지 확인 후 제거
      DELETE FROM tStaff WHERE salary > 300;
 

 

 

 

3. TRUNCATE

 

TRUNCATE TABLE 테이블 ;
 

👩🏻‍💻  테이블 초기화 할 때 쓰는 명령어

👩🏻‍💻  DELETE 명령은 수십만건의 레코드를 일일이 지우게 되면 서버에 부하가 생긴다.

       ➡️ 이 경우 TRUNCATE 명령을 사용하는 것이 효율적

       ➡️ 물리적인 기억 장소를 깨끗이 비우고 임시 영역에 로그도 남기지 않아 훨씬 빠르다

 

TRUNCATE TABLE tCity;

 

🔽

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

 

'Database > MySQL' 카테고리의 다른 글

[MySQL] 서브쿼리 (SubQuery)  (1) 2024.02.24
[MySQL] UPDATE 문  (1) 2024.02.24
[MySQL] INSERT 문  (0) 2024.02.23
[MySQL] GROUP BY, HAVING  (0) 2024.02.23
[MySQL] 집계 함수 (aggregate function)  (0) 2024.02.23

+ Recent posts