์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ›ก๏ธ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ๊ณผ ์ œ์•ฝ์กฐ๊ฑด ํ™œ์šฉํ•˜๊ธฐ

๐Ÿ›ก๏ธ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ๊ณผ ์ œ์•ฝ์กฐ๊ฑด ํ™œ์šฉํ•˜๊ธฐ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋“ ๋“ ํ•œ ๋ฐฉํŒจ๋ง‰์ด, ์ œ์•ฝ์กฐ๊ฑด ์™„์ „์ •๋ณต!

๐ŸŽฏ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ, ์™œ ์ค‘์š”ํ• ๊นŒ?

์•ˆ๋…•! ์˜ค๋Š˜์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ •๋ง์ •๋ง ์ค‘์š”ํ•œ ๊ฐœ๋…์ธ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ๊ณผ ์ œ์•ฝ์กฐ๊ฑด์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณผ๊ฒŒ. ๐Ÿ˜Š

์ƒ์ƒํ•ด๋ด. ๋„ˆ๊ฐ€ ์˜จ๋ผ์ธ ์‡ผํ•‘๋ชฐ์„ ์šด์˜ํ•˜๋Š”๋ฐ, ์–ด๋А ๋‚  ๊ณ ๊ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ํ™•์ธํ–ˆ๋”๋‹ˆ ์ด๋ฉ”์ผ ์ฃผ์†Œ๊ฐ€ ์—†๋Š” ํšŒ์›์ด ์žˆ๊ณ , ์ฃผ๋ฌธ ํ…Œ์ด๋ธ”์—๋Š” ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ƒํ’ˆ ๋ฒˆํ˜ธ๊ฐ€ ๋“ค์–ด๊ฐ€ ์žˆ๊ณ , ๊ฐ™์€ ์ฃผ๋ฌธ๋ฒˆํ˜ธ๊ฐ€ ์ค‘๋ณต์œผ๋กœ ์—ฌ๋Ÿฌ ๊ฐœ ์žˆ๋‹ค๋ฉด? ๐Ÿคฏ

์ด๊ฑด ๋‹จ์ˆœํžˆ ๋ฐ์ดํ„ฐ๊ฐ€ '์ง€์ €๋ถ„ํ•˜๋‹ค'๋Š” ์ˆ˜์ค€์ด ์•„๋‹ˆ์•ผ. ์‹œ์Šคํ…œ ์ „์ฒด๊ฐ€ ์˜ค์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋Š” ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ์ง€! ๋ฐ”๋กœ ์ด๋Ÿฐ ์ƒํ™ฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์šฐ๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ(Data Integrity)์ด๋ผ๋Š” ๊ฐœ๋…์„ ์‚ฌ์šฉํ•ด.

๐Ÿ’ก ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์ด๋ž€?

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ •ํ™•ํ•˜๊ณ , ์ผ๊ด€๋˜๋ฉฐ, ์œ ํšจํ•œ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•ด. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด "๋ฐ์ดํ„ฐ๊ฐ€ ๋ง๊ฐ€์ง€์ง€ ์•Š๊ณ  ๋ฏฟ์„ ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ"๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ!
๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์˜ 4๊ฐ€์ง€ ํ•ต์‹ฌ ์š”์†Œ ๊ฐœ์ฒด ๋ฌด๊ฒฐ์„ฑ Entity ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ Referential ๋„๋ฉ”์ธ ๋ฌด๊ฒฐ์„ฑ Domain ์‚ฌ์šฉ์ž์ •์˜ User-Defined ๊ธฐ๋ณธํ‚ค ์ œ์•ฝ ์™ธ๋ž˜ํ‚ค ์ œ์•ฝ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™

๐Ÿ” ๊ฐœ์ฒด ๋ฌด๊ฒฐ์„ฑ (Entity Integrity)

์ฒซ ๋ฒˆ์งธ๋กœ ์•Œ์•„๋ณผ ๊ฑด ๊ฐœ์ฒด ๋ฌด๊ฒฐ์„ฑ์ด์•ผ. ์ด๊ฑด ํ…Œ์ด๋ธ”์˜ ๊ฐ ํ–‰(row)์„ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์›์น™์ด์ง€.

์ƒ๊ฐํ•ด๋ด. ํ•™์ƒ ๋ช…๋‹จ์— ํ•™๋ฒˆ์ด ์—†๊ฑฐ๋‚˜, ๊ฐ™์€ ํ•™๋ฒˆ์„ ๊ฐ€์ง„ ํ•™์ƒ์ด ์—ฌ๋Ÿฌ ๋ช… ์žˆ๋‹ค๋ฉด? ๋ˆ„๊ฐ€ ๋ˆ„๊ตฐ์ง€ ๊ตฌ๋ถ„ํ•  ์ˆ˜๊ฐ€ ์—†์ž–์•„! ๐Ÿ˜ฑ

๐Ÿ“Œ ๊ฐœ์ฒด ๋ฌด๊ฒฐ์„ฑ์˜ ํ•ต์‹ฌ ๊ทœ์น™

1๏ธโƒฃ ๋ชจ๋“  ํ…Œ์ด๋ธ”์€ ๊ธฐ๋ณธํ‚ค(Primary Key)๋ฅผ ๊ฐ€์ ธ์•ผ ํ•ด
2๏ธโƒฃ ๊ธฐ๋ณธํ‚ค๋Š” NULL ๊ฐ’์„ ๊ฐ€์งˆ ์ˆ˜ ์—†์–ด
3๏ธโƒฃ ๊ธฐ๋ณธํ‚ค๋Š” ์ค‘๋ณต๋œ ๊ฐ’์„ ๊ฐ€์งˆ ์ˆ˜ ์—†์–ด

์‹ค์ œ SQL๋กœ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š”์ง€ ๋ณผ๊นŒ? ๐Ÿค“

-- ํ•™์ƒ ํ…Œ์ด๋ธ” ์ƒ์„ฑ (๊ธฐ๋ณธํ‚ค ์„ค์ •)
CREATE TABLE students (
    student_id INT PRIMARY KEY,  -- ๊ธฐ๋ณธํ‚ค ์ง€์ •
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    enrollment_date DATE
);

-- ๋˜๋Š” ํ…Œ์ด๋ธ” ๋ ˆ๋ฒจ์—์„œ ๊ธฐ๋ณธํ‚ค ์ง€์ •
CREATE TABLE students (
    student_id INT,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    enrollment_date DATE,
    CONSTRAINT pk_student PRIMARY KEY (student_id)
);

-- ๋ณตํ•ฉ ๊ธฐ๋ณธํ‚ค (์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ ์กฐํ•ฉ)
CREATE TABLE course_enrollment (
    student_id INT,
    course_id INT,
    enrollment_date DATE,
    PRIMARY KEY (student_id, course_id)  -- ๋‘ ์ปฌ๋Ÿผ ์กฐํ•ฉ์ด ๊ธฐ๋ณธํ‚ค
);

์œ„ ์ฝ”๋“œ์—์„œ PRIMARY KEY ์ œ์•ฝ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•˜๋ฉด, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ž๋™์œผ๋กœ:
โœ… NULL ๊ฐ’ ์ž…๋ ฅ์„ ์ฐจ๋‹จํ•ด
โœ… ์ค‘๋ณต๋œ ๊ฐ’ ์ž…๋ ฅ์„ ์ฐจ๋‹จํ•ด
โœ… ์ž๋™์œผ๋กœ ์ธ๋ฑ์Šค๋ฅผ ์ƒ์„ฑํ•ด์„œ ๊ฒ€์ƒ‰ ์†๋„๋ฅผ ๋†’์—ฌ์ค˜

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ

๊ธฐ๋ณธํ‚ค๋กœ ์‚ฌ์šฉํ•  ์ปฌ๋Ÿผ์„ ์„ ํƒํ•  ๋•Œ๋Š” ์ ˆ๋Œ€ ๋ณ€ํ•˜์ง€ ์•Š์„ ๊ฐ’์„ ์„ ํƒํ•ด์•ผ ํ•ด! ์˜ˆ๋ฅผ ๋“ค์–ด ์ด๋ฉ”์ผ ์ฃผ์†Œ๋‚˜ ์ „ํ™”๋ฒˆํ˜ธ๋Š” ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ ๊ธฐ๋ณธํ‚ค๋กœ ์ ํ•ฉํ•˜์ง€ ์•Š์•„. ๋Œ€์‹  ์ž๋™ ์ฆ๊ฐ€ํ•˜๋Š” ID ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ผ๋ฐ˜์ ์ด์•ผ.

๐Ÿ”— ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ (Referential Integrity)

์ด์ œ ๋‘ ๋ฒˆ์งธ, ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž! ์ด๊ฑด ํ…Œ์ด๋ธ” ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ทœ์น™์ด์•ผ.

์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊ฒŒ. ์ฃผ๋ฌธ ํ…Œ์ด๋ธ”์— "์ƒํ’ˆ๋ฒˆํ˜ธ 999๋ฒˆ์„ ์ฃผ๋ฌธํ–ˆ๋‹ค"๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ƒํ’ˆ ํ…Œ์ด๋ธ”์—๋Š” 999๋ฒˆ ์ƒํ’ˆ์ด ์—†๋‹ค๋ฉด? ์ด๊ฑด ๋ง์ด ์•ˆ ๋˜๋Š” ์ƒํ™ฉ์ด์ง€! ๐Ÿค”

์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ์€ ๋ฐ”๋กœ ์ด๋Ÿฐ "์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ์ƒํ™ฉ"์„ ๋ฐฉ์ง€ํ•ด์ค˜.

๐ŸŽฏ ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ์˜ ํ•ต์‹ฌ

์™ธ๋ž˜ํ‚ค(Foreign Key)๋Š” ๋ฐ˜๋“œ์‹œ ์ฐธ์กฐํ•˜๋Š” ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธํ‚ค ๊ฐ’์ด๊ฑฐ๋‚˜ NULL์ด์–ด์•ผ ํ•ด!

์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด: "์—†๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜ ์—†๋‹ค!"
-- ์ƒํ’ˆ ํ…Œ์ด๋ธ” (๋ถ€๋ชจ ํ…Œ์ด๋ธ”)
CREATE TABLE products (
    product_id INT PRIMARY KEY,
    product_name VARCHAR(100) NOT NULL,
    price DECIMAL(10, 2),
    stock_quantity INT
);

-- ์ฃผ๋ฌธ ํ…Œ์ด๋ธ” (์ž์‹ ํ…Œ์ด๋ธ”)
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    product_id INT,
    order_date DATE,
    quantity INT,
    CONSTRAINT fk_product 
        FOREIGN KEY (product_id) 
        REFERENCES products(product_id)
);

-- ์ด์ œ ์ด๋Ÿฐ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•ด:
INSERT INTO orders (order_id, customer_id, product_id, quantity)
VALUES (1, 100, 999, 2);  -- โŒ ์—๋Ÿฌ! product_id 999๋Š” products ํ…Œ์ด๋ธ”์— ์—†์Œ!

์™ธ๋ž˜ํ‚ค ์ œ์•ฝ์กฐ๊ฑด์„ ์„ค์ •ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ž๋™์œผ๋กœ ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ์„ ๊ฒ€์‚ฌํ•ด์ค˜. ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ƒํ’ˆ์„ ์ฃผ๋ฌธํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€! ๐Ÿ’ช

๐ŸŽฎ ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ ์˜ต์…˜๋“ค

์™ธ๋ž˜ํ‚ค๋ฅผ ์„ค์ •ํ•  ๋•Œ ๋ถ€๋ชจ ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ญ์ œ๋˜๊ฑฐ๋‚˜ ์ˆ˜์ •๋  ๋•Œ ์–ด๋–ป๊ฒŒ ํ• ์ง€ ์ •ํ•  ์ˆ˜ ์žˆ์–ด. ์ด๊ฒŒ ์ง„์งœ ์ค‘์š”ํ•œ๋ฐ, ์‹ค๋ฌด์—์„œ ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ฑฐ๋“ ! ๐Ÿ˜Ž

์˜ต์…˜ ์„ค๋ช… ์‚ฌ์šฉ ์˜ˆ์‹œ
CASCADE ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์‚ญ์ œ/์ˆ˜์ • ์‹œ ์ž์‹ ๋ฐ์ดํ„ฐ๋„ ํ•จ๊ป˜ ์‚ญ์ œ/์ˆ˜์ • ์ƒํ’ˆ ์‚ญ์ œ ์‹œ ๊ด€๋ จ ์ฃผ๋ฌธ๋„ ๋ชจ๋‘ ์‚ญ์ œ
SET NULL ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์‚ญ์ œ ์‹œ ์ž์‹์˜ ์™ธ๋ž˜ํ‚ค๋ฅผ NULL๋กœ ์„ค์ • ์นดํ…Œ๊ณ ๋ฆฌ ์‚ญ์ œ ์‹œ ์ƒํ’ˆ์˜ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ NULL๋กœ
SET DEFAULT ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์‚ญ์ œ ์‹œ ์ž์‹์˜ ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์„ค์ • ๋ถ€์„œ ์‚ญ์ œ ์‹œ ์ง์›์„ '๋ฏธ๋ฐฐ์น˜' ๋ถ€์„œ๋กœ
RESTRICT ์ž์‹ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์‚ญ์ œ ๋ถˆ๊ฐ€ ์ฃผ๋ฌธ์ด ์žˆ๋Š” ๊ณ ๊ฐ์€ ์‚ญ์ œ ๋ถˆ๊ฐ€
NO ACTION RESTRICT์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ ์ฒดํฌ ์‹œ์ ์ด ๋‹ค๋ฆ„ ํŠธ๋žœ์žญ์…˜ ์ข…๋ฃŒ ์‹œ์ ์— ์ฒดํฌ
-- CASCADE ์˜ˆ์‹œ: ์ƒํ’ˆ ์‚ญ์ œ ์‹œ ๊ด€๋ จ ์ฃผ๋ฌธ๋„ ์‚ญ์ œ
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    product_id INT,
    quantity INT,
    CONSTRAINT fk_product_cascade
        FOREIGN KEY (product_id)
        REFERENCES products(product_id)
        ON DELETE CASCADE  -- ์ƒํ’ˆ ์‚ญ์ œ ์‹œ ์ฃผ๋ฌธ๋„ ์‚ญ์ œ
        ON UPDATE CASCADE  -- ์ƒํ’ˆ ID ๋ณ€๊ฒฝ ์‹œ ์ฃผ๋ฌธ์˜ ID๋„ ๋ณ€๊ฒฝ
);

-- SET NULL ์˜ˆ์‹œ: ์นดํ…Œ๊ณ ๋ฆฌ ์‚ญ์ œ ์‹œ ์ƒํ’ˆ์˜ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ NULL๋กœ
CREATE TABLE products (
    product_id INT PRIMARY KEY,
    product_name VARCHAR(100),
    category_id INT,
    CONSTRAINT fk_category_null
        FOREIGN KEY (category_id)
        REFERENCES categories(category_id)
        ON DELETE SET NULL  -- ์นดํ…Œ๊ณ ๋ฆฌ ์‚ญ์ œ ์‹œ NULL๋กœ ์„ค์ •
);

-- RESTRICT ์˜ˆ์‹œ: ์ฃผ๋ฌธ์ด ์žˆ๋Š” ๊ณ ๊ฐ์€ ์‚ญ์ œ ๋ถˆ๊ฐ€
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    CONSTRAINT fk_customer_restrict
        FOREIGN KEY (customer_id)
        REFERENCES customers(customer_id)
        ON DELETE RESTRICT  -- ์ฃผ๋ฌธ์ด ์žˆ์œผ๋ฉด ๊ณ ๊ฐ ์‚ญ์ œ ๋ถˆ๊ฐ€
);
๐Ÿ’ก ์‹ค๋ฌด ํŒ

์‹ค๋ฌด์—์„œ๋Š” CASCADE๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ •๋ง ์กฐ์‹ฌํ•ด์•ผ ํ•ด! ์‹ค์ˆ˜๋กœ ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ๋‚ ๋ ค๋ฒ„๋ฆด ์ˆ˜ ์žˆ๊ฑฐ๋“ . ๐Ÿ˜ฑ

๋ณดํ†ต์€ RESTRICT๋‚˜ NO ACTION์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ์ •๋ง ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ CASCADE๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์•ˆ์ „ํ•ด.

๐Ÿ“Š ๋„๋ฉ”์ธ ๋ฌด๊ฒฐ์„ฑ (Domain Integrity)

์„ธ ๋ฒˆ์งธ๋Š” ๋„๋ฉ”์ธ ๋ฌด๊ฒฐ์„ฑ์ด์•ผ! ์ด๊ฑด ๊ฐ ์ปฌ๋Ÿผ์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๊ฐ’์˜ ๋ฒ”์œ„๋‚˜ ํ˜•์‹์„ ์ œํ•œํ•˜๋Š” ๊ฑฐ์ง€.

์˜ˆ๋ฅผ ๋“ค์–ด ๋‚˜์ด ์ปฌ๋Ÿผ์— -50์ด๋‚˜ 300 ๊ฐ™์€ ๋ง๋„ ์•ˆ ๋˜๋Š” ๊ฐ’์ด ๋“ค์–ด๊ฐ€๋ฉด ์•ˆ ๋˜์ž–์•„? ๋˜๋Š” ์ด๋ฉ”์ผ ํ˜•์‹์ด ์•„๋‹Œ ๊ฐ’์ด ์ด๋ฉ”์ผ ์ปฌ๋Ÿผ์— ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ๋„ ๋ง‰์•„์•ผ ํ•˜๊ณ ! ๐ŸŽฏ

๐Ÿ› ๏ธ ๋„๋ฉ”์ธ ๋ฌด๊ฒฐ์„ฑ์„ ์œ„ํ•œ ์ œ์•ฝ์กฐ๊ฑด๋“ค

๋„๋ฉ”์ธ ๋ฌด๊ฒฐ์„ฑ์„ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ œ์•ฝ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด์ž!

1๏ธโƒฃ NOT NULL ์ œ์•ฝ์กฐ๊ฑด

์ปฌ๋Ÿผ์— NULL ๊ฐ’์ด ๋“ค์–ด๊ฐ€๋Š” ๊ฑธ ๋ง‰์•„์ค˜. ํ•„์ˆ˜ ์ž…๋ ฅ ํ•ญ๋ชฉ์„ ์ง€์ •ํ•  ๋•Œ ์‚ฌ์šฉํ•˜์ง€!
CREATE TABLE users (
    user_id INT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,  -- ํ•„์ˆ˜ ์ž…๋ ฅ
    email VARCHAR(100) NOT NULL,    -- ํ•„์ˆ˜ ์ž…๋ ฅ
    phone VARCHAR(20),               -- ์„ ํƒ ์ž…๋ ฅ (NULL ๊ฐ€๋Šฅ)
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
2๏ธโƒฃ UNIQUE ์ œ์•ฝ์กฐ๊ฑด

์ปฌ๋Ÿผ ๊ฐ’์ด ํ…Œ์ด๋ธ” ๋‚ด์—์„œ ์œ ์ผํ•ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉํ•ด. ๊ธฐ๋ณธํ‚ค๋Š” ์•„๋‹ˆ์ง€๋งŒ ์ค‘๋ณต๋˜๋ฉด ์•ˆ ๋˜๋Š” ๊ฐ’์— ์ ์šฉํ•˜์ง€!
CREATE TABLE users (
    user_id INT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,  -- ์ค‘๋ณต ๋ถˆ๊ฐ€
    email VARCHAR(100) NOT NULL UNIQUE,    -- ์ค‘๋ณต ๋ถˆ๊ฐ€
    phone VARCHAR(20) UNIQUE                -- NULL์€ ํ—ˆ์šฉํ•˜๋˜ ๊ฐ’์ด ์žˆ์œผ๋ฉด ์ค‘๋ณต ๋ถˆ๊ฐ€
);

-- ๋˜๋Š” ํ…Œ์ด๋ธ” ๋ ˆ๋ฒจ์—์„œ ์ง€์ •
CREATE TABLE users (
    user_id INT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    CONSTRAINT uq_username UNIQUE (username),
    CONSTRAINT uq_email UNIQUE (email)
);
3๏ธโƒฃ CHECK ์ œ์•ฝ์กฐ๊ฑด

์ด๊ฒŒ ์ง„์งœ ๊ฐ•๋ ฅํ•ด! ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๊ฐ’๋งŒ ์ž…๋ ฅ๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿ’ช
CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    age INT CHECK (age >= 18 AND age <= 70),  -- ๋‚˜์ด๋Š” 18~70์„ธ
    salary DECIMAL(10, 2) CHECK (salary > 0),  -- ๊ธ‰์—ฌ๋Š” ์–‘์ˆ˜
    email VARCHAR(100) CHECK (email LIKE '%@%.%'),  -- ์ด๋ฉ”์ผ ํ˜•์‹ ์ฒดํฌ
    gender CHAR(1) CHECK (gender IN ('M', 'F', 'O')),  -- M, F, O ์ค‘ ํ•˜๋‚˜
    hire_date DATE CHECK (hire_date <= CURRENT_DATE)  -- ์ž…์‚ฌ์ผ์€ ์˜ค๋Š˜ ์ด์ „
);

-- ๋ณต์žกํ•œ CHECK ์ œ์•ฝ์กฐ๊ฑด
CREATE TABLE products (
    product_id INT PRIMARY KEY,
    product_name VARCHAR(100) NOT NULL,
    price DECIMAL(10, 2),
    discount_price DECIMAL(10, 2),
    stock_quantity INT,
    CONSTRAINT chk_price CHECK (price > 0),
    CONSTRAINT chk_discount CHECK (discount_price IS NULL OR discount_price < price),
    CONSTRAINT chk_stock CHECK (stock_quantity >= 0)
);
4๏ธโƒฃ DEFAULT ์ œ์•ฝ์กฐ๊ฑด

๊ฐ’์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์•˜์„ ๋•Œ ์ž๋™์œผ๋กœ ๋“ค์–ด๊ฐˆ ๊ธฐ๋ณธ๊ฐ’์„ ์„ค์ •ํ•ด์ค˜.
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT NOT NULL,
    order_date DATE DEFAULT CURRENT_DATE,  -- ๊ธฐ๋ณธ๊ฐ’: ์˜ค๋Š˜ ๋‚ ์งœ
    status VARCHAR(20) DEFAULT 'PENDING',  -- ๊ธฐ๋ณธ๊ฐ’: 'PENDING'
    is_paid BOOLEAN DEFAULT FALSE,         -- ๊ธฐ๋ณธ๊ฐ’: FALSE
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ์‚ฌ์šฉ ์˜ˆ์‹œ
INSERT INTO orders (order_id, customer_id)
VALUES (1, 100);
-- order_date, status, is_paid, created_at๋Š” ์ž๋™์œผ๋กœ ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •๋จ!
์ œ์•ฝ์กฐ๊ฑด ๋น„๊ตํ‘œ NOT NULL UNIQUE CHECK DEFAULT PRIMARY KEY NULL ๊ฐ’ ์ž…๋ ฅ ๋ฐฉ์ง€ / ํ•„์ˆ˜ ์ž…๋ ฅ ํ•ญ๋ชฉ ์ง€์ • ์ค‘๋ณต ๊ฐ’ ๋ฐฉ์ง€ / NULL์€ ํ—ˆ์šฉ ๊ฐ€๋Šฅ / ์—ฌ๋Ÿฌ ๊ฐœ ์„ค์ • ๊ฐ€๋Šฅ ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๊ฐ’๋งŒ ํ—ˆ์šฉ ์˜ˆ: ๋‚˜์ด ๋ฒ”์œ„, ์ด๋ฉ”์ผ ํ˜•์‹, ์–‘์ˆ˜ ์ฒดํฌ ๋“ฑ ๊ฐ’์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์•˜์„ ๋•Œ ์ž๋™์œผ๋กœ ์„ค์ •๋˜๋Š” ๊ธฐ๋ณธ๊ฐ’ NOT NULL + UNIQUE ์กฐํ•ฉ / ํ…Œ์ด๋ธ”๋‹น 1๊ฐœ๋งŒ ๊ฐ€๋Šฅ ์ž๋™ ์ธ๋ฑ์Šค ์ƒ์„ฑ / ํ–‰์„ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„

๐ŸŽจ ์‚ฌ์šฉ์ž ์ •์˜ ๋ฌด๊ฒฐ์„ฑ (User-Defined Integrity)

๋งˆ์ง€๋ง‰์œผ๋กœ ์‚ฌ์šฉ์ž ์ •์˜ ๋ฌด๊ฒฐ์„ฑ์ด์•ผ! ์ด๊ฑด ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์— ๋”ฐ๋ผ ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ์ •์˜ํ•˜๋Š” ์ œ์•ฝ์กฐ๊ฑด์ด์ง€.

์˜ˆ๋ฅผ ๋“ค์–ด "ํ• ์ธ๊ฐ€๋Š” ์ •๊ฐ€์˜ 50%๋ณด๋‹ค ๋‚ฎ์„ ์ˆ˜ ์—†๋‹ค"๊ฑฐ๋‚˜ "์ฃผ๋ฌธ ์ˆ˜๋Ÿ‰์€ ์žฌ๊ณ  ์ˆ˜๋Ÿ‰์„ ์ดˆ๊ณผํ•  ์ˆ˜ ์—†๋‹ค" ๊ฐ™์€ ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด! ๐ŸŽฏ

๐Ÿ”ง ํŠธ๋ฆฌ๊ฑฐ(Trigger)๋ฅผ ํ™œ์šฉํ•œ ๋ฌด๊ฒฐ์„ฑ

ํŠธ๋ฆฌ๊ฑฐ๋Š” ํŠน์ • ์ด๋ฒคํŠธ(INSERT, UPDATE, DELETE)๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ž๋™์œผ๋กœ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ์•ผ. ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ๋•Œ ์ •๋ง ์œ ์šฉํ•˜์ง€! ๐Ÿ˜Ž

-- ์žฌ๊ณ  ์ˆ˜๋Ÿ‰ ์ฒดํฌ ํŠธ๋ฆฌ๊ฑฐ
CREATE TRIGGER check_stock_before_order
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
    DECLARE available_stock INT;
    
    -- ํ˜„์žฌ ์žฌ๊ณ  ํ™•์ธ
    SELECT stock_quantity INTO available_stock
    FROM products
    WHERE product_id = NEW.product_id;
    
    -- ์žฌ๊ณ ๊ฐ€ ๋ถ€์กฑํ•˜๋ฉด ์—๋Ÿฌ ๋ฐœ์ƒ
    IF NEW.quantity > available_stock THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = '์žฌ๊ณ ๊ฐ€ ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค!';
    END IF;
END;

-- ์ฃผ๋ฌธ ํ›„ ์žฌ๊ณ  ์ž๋™ ๊ฐ์†Œ ํŠธ๋ฆฌ๊ฑฐ
CREATE TRIGGER update_stock_after_order
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
    UPDATE products
    SET stock_quantity = stock_quantity - NEW.quantity
    WHERE product_id = NEW.product_id;
END;

-- ํ• ์ธ๊ฐ€ ๊ฒ€์ฆ ํŠธ๋ฆฌ๊ฑฐ
CREATE TRIGGER validate_discount_price
BEFORE INSERT OR UPDATE ON products
FOR EACH ROW
BEGIN
    IF NEW.discount_price IS NOT NULL THEN
        -- ํ• ์ธ๊ฐ€๋Š” ์ •๊ฐ€์˜ 50% ์ด์ƒ์ด์–ด์•ผ ํ•จ
        IF NEW.discount_price < (NEW.price * 0.5) THEN
            SIGNAL SQLSTATE '45000'
            SET MESSAGE_TEXT = 'ํ• ์ธ๊ฐ€๋Š” ์ •๊ฐ€์˜ 50% ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค!';
        END IF;
    END IF;
END;

๐Ÿ“ ์ €์žฅ ํ”„๋กœ์‹œ์ €(Stored Procedure)๋ฅผ ํ™œ์šฉํ•œ ๋ฌด๊ฒฐ์„ฑ

์ €์žฅ ํ”„๋กœ์‹œ์ €๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์บก์Аํ™”ํ•  ์ˆ˜ ์žˆ์–ด. ๋ฐ์ดํ„ฐ ์ž…๋ ฅ ์ „์— ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์ง€! ๐Ÿ’ช

-- ์•ˆ์ „ํ•œ ์ฃผ๋ฌธ ์ƒ์„ฑ ํ”„๋กœ์‹œ์ €
DELIMITER //

CREATE PROCEDURE create_order_safely(
    IN p_customer_id INT,
    IN p_product_id INT,
    IN p_quantity INT
)
BEGIN
    DECLARE v_stock INT;
    DECLARE v_price DECIMAL(10,2);
    DECLARE v_customer_exists INT;
    
    -- ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
    START TRANSACTION;
    
    -- 1. ๊ณ ๊ฐ ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
    SELECT COUNT(*) INTO v_customer_exists
    FROM customers
    WHERE customer_id = p_customer_id;
    
    IF v_customer_exists = 0 THEN
        ROLLBACK;
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = '์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ณ ๊ฐ์ž…๋‹ˆ๋‹ค!';
    END IF;
    
    -- 2. ์žฌ๊ณ  ํ™•์ธ
    SELECT stock_quantity, price INTO v_stock, v_price
    FROM products
    WHERE product_id = p_product_id
    FOR UPDATE;  -- ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ์œ„ํ•œ ๋ฝ
    
    IF v_stock < p_quantity THEN
        ROLLBACK;
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = '์žฌ๊ณ ๊ฐ€ ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค!';
    END IF;
    
    -- 3. ์ฃผ๋ฌธ ์ƒ์„ฑ
    INSERT INTO orders (customer_id, product_id, quantity, total_price, order_date)
    VALUES (p_customer_id, p_product_id, p_quantity, v_price * p_quantity, CURRENT_DATE);
    
    -- 4. ์žฌ๊ณ  ๊ฐ์†Œ
    UPDATE products
    SET stock_quantity = stock_quantity - p_quantity
    WHERE product_id = p_product_id;
    
    -- ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹
    COMMIT;
END //

DELIMITER ;

-- ์‚ฌ์šฉ ์˜ˆ์‹œ
CALL create_order_safely(100, 5, 2);
๐Ÿ’ก ์žฌ๋Šฅ๋„ท ํ™œ์šฉ ํŒ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„๋‚˜ ์ œ์•ฝ์กฐ๊ฑด ๊ตฌํ˜„์ด ์–ด๋ ต๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด! ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์ด ํ”„๋กœ์ ํŠธ์— ๋งž๋Š” ์ตœ์ ์˜ ์ œ์•ฝ์กฐ๊ฑด ์„ค๊ณ„๋ฅผ ๋„์™€์ค„ ๊ฑฐ์•ผ. ๐Ÿ˜Š

โšก ์ œ์•ฝ์กฐ๊ฑด ๊ด€๋ฆฌํ•˜๊ธฐ

์ œ์•ฝ์กฐ๊ฑด์„ ๋งŒ๋“ค์—ˆ์œผ๋ฉด ๊ด€๋ฆฌ๋„ ํ•  ์ค„ ์•Œ์•„์•ผ๊ฒ ์ง€? ์ œ์•ฝ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•˜๊ณ , ์ˆ˜์ •ํ•˜๊ณ , ์‚ญ์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ๐Ÿ› ๏ธ

โž• ๊ธฐ์กด ํ…Œ์ด๋ธ”์— ์ œ์•ฝ์กฐ๊ฑด ์ถ”๊ฐ€ํ•˜๊ธฐ

-- PRIMARY KEY ์ถ”๊ฐ€
ALTER TABLE users
ADD PRIMARY KEY (user_id);

-- FOREIGN KEY ์ถ”๊ฐ€
ALTER TABLE orders
ADD CONSTRAINT fk_customer
FOREIGN KEY (customer_id)
REFERENCES customers(customer_id)
ON DELETE RESTRICT
ON UPDATE CASCADE;

-- UNIQUE ์ œ์•ฝ์กฐ๊ฑด ์ถ”๊ฐ€
ALTER TABLE users
ADD CONSTRAINT uq_email UNIQUE (email);

-- CHECK ์ œ์•ฝ์กฐ๊ฑด ์ถ”๊ฐ€
ALTER TABLE employees
ADD CONSTRAINT chk_age CHECK (age >= 18 AND age <= 70);

-- NOT NULL ์ œ์•ฝ์กฐ๊ฑด ์ถ”๊ฐ€ (์ปฌ๋Ÿผ ์ˆ˜์ •)
ALTER TABLE users
MODIFY email VARCHAR(100) NOT NULL;

-- DEFAULT ๊ฐ’ ์ถ”๊ฐ€
ALTER TABLE orders
ALTER COLUMN status SET DEFAULT 'PENDING';

๐Ÿ—‘๏ธ ์ œ์•ฝ์กฐ๊ฑด ์‚ญ์ œํ•˜๊ธฐ

-- PRIMARY KEY ์‚ญ์ œ
ALTER TABLE users
DROP PRIMARY KEY;

-- FOREIGN KEY ์‚ญ์ œ
ALTER TABLE orders
DROP FOREIGN KEY fk_customer;

-- UNIQUE ์ œ์•ฝ์กฐ๊ฑด ์‚ญ์ œ
ALTER TABLE users
DROP INDEX uq_email;

-- CHECK ์ œ์•ฝ์กฐ๊ฑด ์‚ญ์ œ
ALTER TABLE employees
DROP CHECK chk_age;

-- NOT NULL ์ œ์•ฝ์กฐ๊ฑด ์ œ๊ฑฐ (์ปฌ๋Ÿผ ์ˆ˜์ •)
ALTER TABLE users
MODIFY email VARCHAR(100) NULL;

-- DEFAULT ๊ฐ’ ์ œ๊ฑฐ
ALTER TABLE orders
ALTER COLUMN status DROP DEFAULT;

๐Ÿ” ์ œ์•ฝ์กฐ๊ฑด ์กฐํšŒํ•˜๊ธฐ

ํ˜„์žฌ ํ…Œ์ด๋ธ”์— ์–ด๋–ค ์ œ์•ฝ์กฐ๊ฑด์ด ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์•Œ์•„๋‘๋ฉด ์ข‹์•„! ๐Ÿ“‹

-- MySQL์—์„œ ์ œ์•ฝ์กฐ๊ฑด ์กฐํšŒ
SELECT 
    CONSTRAINT_NAME,
    CONSTRAINT_TYPE,
    TABLE_NAME,
    COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
    ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
WHERE tc.TABLE_SCHEMA = 'your_database_name'
    AND tc.TABLE_NAME = 'your_table_name';

-- ์™ธ๋ž˜ํ‚ค ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ
SELECT 
    CONSTRAINT_NAME,
    TABLE_NAME,
    COLUMN_NAME,
    REFERENCED_TABLE_NAME,
    REFERENCED_COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'your_database_name'
    AND REFERENCED_TABLE_NAME IS NOT NULL;

-- PostgreSQL์—์„œ ์ œ์•ฝ์กฐ๊ฑด ์กฐํšŒ
SELECT
    con.conname AS constraint_name,
    con.contype AS constraint_type,
    tbl.relname AS table_name,
    col.attname AS column_name
FROM pg_constraint con
JOIN pg_class tbl ON con.conrelid = tbl.oid
JOIN pg_attribute col ON col.attrelid = tbl.oid
WHERE tbl.relname = 'your_table_name';

๐ŸŽฏ ์‹ค์ „ ์˜ˆ์ œ: ์˜จ๋ผ์ธ ์‡ผํ•‘๋ชฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

์ด์ œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ œ ์˜จ๋ผ์ธ ์‡ผํ•‘๋ชฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์„ค๊ณ„ํ•ด๋ณด์ž! ๋ชจ๋“  ์ข…๋ฅ˜์˜ ๋ฌด๊ฒฐ์„ฑ๊ณผ ์ œ์•ฝ์กฐ๊ฑด์„ ์ ์šฉํ•ด๋ณผ ๊ฑฐ์•ผ. ๐Ÿ›’

-- 1. ๊ณ ๊ฐ ํ…Œ์ด๋ธ”
CREATE TABLE customers (
    customer_id INT AUTO_INCREMENT PRIMARY KEY,  -- ๊ฐœ์ฒด ๋ฌด๊ฒฐ์„ฑ
    username VARCHAR(50) NOT NULL UNIQUE,        -- ๋„๋ฉ”์ธ ๋ฌด๊ฒฐ์„ฑ
    email VARCHAR(100) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    phone VARCHAR(20),
    birth_date DATE,
    gender CHAR(1) CHECK (gender IN ('M', 'F', 'O')),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    is_active BOOLEAN DEFAULT TRUE,
    CONSTRAINT chk_email_format CHECK (email LIKE '%@%.%'),
    CONSTRAINT chk_birth_date CHECK (birth_date <= CURRENT_DATE)
);

-- 2. ์นดํ…Œ๊ณ ๋ฆฌ ํ…Œ์ด๋ธ”
CREATE TABLE categories (
    category_id INT AUTO_INCREMENT PRIMARY KEY,
    category_name VARCHAR(50) NOT NULL UNIQUE,
    parent_category_id INT,
    description TEXT,
    CONSTRAINT fk_parent_category
        FOREIGN KEY (parent_category_id)
        REFERENCES categories(category_id)
        ON DELETE SET NULL  -- ๋ถ€๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์‚ญ์ œ ์‹œ NULL๋กœ
);

-- 3. ์ƒํ’ˆ ํ…Œ์ด๋ธ”
CREATE TABLE products (
    product_id INT AUTO_INCREMENT PRIMARY KEY,
    product_name VARCHAR(100) NOT NULL,
    category_id INT,
    description TEXT,
    price DECIMAL(10, 2) NOT NULL,
    discount_price DECIMAL(10, 2),
    stock_quantity INT NOT NULL DEFAULT 0,
    min_order_quantity INT DEFAULT 1,
    max_order_quantity INT DEFAULT 999,
    is_available BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    -- ์ œ์•ฝ์กฐ๊ฑด๋“ค
    CONSTRAINT fk_category
        FOREIGN KEY (category_id)
        REFERENCES categories(category_id)
        ON DELETE SET NULL,
    CONSTRAINT chk_price CHECK (price > 0),
    CONSTRAINT chk_discount CHECK (
        discount_price IS NULL OR 
        (discount_price > 0 AND discount_price < price)
    ),
    CONSTRAINT chk_stock CHECK (stock_quantity >= 0),
    CONSTRAINT chk_order_quantity CHECK (
        min_order_quantity > 0 AND 
        max_order_quantity >= min_order_quantity
    )
);

-- 4. ์ฃผ๋ฌธ ํ…Œ์ด๋ธ”
CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT NOT NULL,
    order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    status VARCHAR(20) DEFAULT 'PENDING',
    total_amount DECIMAL(10, 2) NOT NULL,
    shipping_address VARCHAR(255) NOT NULL,
    payment_method VARCHAR(20),
    
    CONSTRAINT fk_customer
        FOREIGN KEY (customer_id)
        REFERENCES customers(customer_id)
        ON DELETE RESTRICT,  -- ์ฃผ๋ฌธ์ด ์žˆ๋Š” ๊ณ ๊ฐ์€ ์‚ญ์ œ ๋ถˆ๊ฐ€
    CONSTRAINT chk_status CHECK (
        status IN ('PENDING', 'CONFIRMED', 'SHIPPED', 'DELIVERED', 'CANCELLED')
    ),
    CONSTRAINT chk_total_amount CHECK (total_amount >= 0),
    CONSTRAINT chk_payment_method CHECK (
        payment_method IN ('CARD', 'BANK_TRANSFER', 'CASH', 'PAYPAL')
    )
);

-- 5. ์ฃผ๋ฌธ ์ƒ์„ธ ํ…Œ์ด๋ธ”
CREATE TABLE order_items (
    order_item_id INT AUTO_INCREMENT PRIMARY KEY,
    order_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL,
    unit_price DECIMAL(10, 2) NOT NULL,
    subtotal DECIMAL(10, 2) NOT NULL,
    
    CONSTRAINT fk_order
        FOREIGN KEY (order_id)
        REFERENCES orders(order_id)
        ON DELETE CASCADE,  -- ์ฃผ๋ฌธ ์‚ญ์ œ ์‹œ ์ฃผ๋ฌธ ์ƒ์„ธ๋„ ์‚ญ์ œ
    CONSTRAINT fk_product
        FOREIGN KEY (product_id)
        REFERENCES products(product_id)
        ON DELETE RESTRICT,  -- ์ฃผ๋ฌธ๋œ ์ƒํ’ˆ์€ ์‚ญ์ œ ๋ถˆ๊ฐ€
    CONSTRAINT chk_quantity CHECK (quantity > 0),
    CONSTRAINT chk_unit_price CHECK (unit_price > 0),
    CONSTRAINT chk_subtotal CHECK (subtotal = quantity * unit_price),
    CONSTRAINT uq_order_product UNIQUE (order_id, product_id)  -- ๊ฐ™์€ ์ฃผ๋ฌธ์— ๊ฐ™์€ ์ƒํ’ˆ ์ค‘๋ณต ๋ฐฉ์ง€
);

-- 6. ๋ฆฌ๋ทฐ ํ…Œ์ด๋ธ”
CREATE TABLE reviews (
    review_id INT AUTO_INCREMENT PRIMARY KEY,
    product_id INT NOT NULL,
    customer_id INT NOT NULL,
    rating INT NOT NULL,
    comment TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    CONSTRAINT fk_review_product
        FOREIGN KEY (product_id)
        REFERENCES products(product_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_review_customer
        FOREIGN KEY (customer_id)
        REFERENCES customers(customer_id)
        ON DELETE CASCADE,
    CONSTRAINT chk_rating CHECK (rating BETWEEN 1 AND 5),
    CONSTRAINT uq_customer_product UNIQUE (customer_id, product_id)  -- ๊ณ ๊ฐ๋‹น ์ƒํ’ˆ 1๊ฐœ ๋ฆฌ๋ทฐ๋งŒ
);
โš ๏ธ ์‹ค๋ฌด ์ฃผ์˜์‚ฌํ•ญ

1๏ธโƒฃ ์„ฑ๋Šฅ ๊ณ ๋ ค: ์ œ์•ฝ์กฐ๊ฑด์ด ๋งŽ์„์ˆ˜๋ก INSERT/UPDATE ์†๋„๊ฐ€ ๋А๋ ค์งˆ ์ˆ˜ ์žˆ์–ด. ํ•„์š”ํ•œ ์ œ์•ฝ์กฐ๊ฑด๋งŒ ์„ค์ •ํ•˜์ž!

2๏ธโƒฃ ์ธ๋ฑ์Šค ๊ด€๋ฆฌ: UNIQUE์™€ FOREIGN KEY๋Š” ์ž๋™์œผ๋กœ ์ธ๋ฑ์Šค๋ฅผ ์ƒ์„ฑํ•ด. ๋ถˆํ•„์š”ํ•œ ์ œ์•ฝ์กฐ๊ฑด์€ ์ธ๋ฑ์Šค ๋‚ญ๋น„์•ผ!

3๏ธโƒฃ CASCADE ์‹ ์ค‘ํžˆ: ON DELETE CASCADE๋Š” ํŽธ๋ฆฌํ•˜์ง€๋งŒ ์‹ค์ˆ˜๋กœ ๋Œ€๋Ÿ‰ ์‚ญ์ œ๋  ์ˆ˜ ์žˆ์–ด. ์ •๋ง ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋งŒ ์‚ฌ์šฉ!

4๏ธโƒฃ ๋ช…๋ช… ๊ทœ์น™: ์ œ์•ฝ์กฐ๊ฑด ์ด๋ฆ„์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ง€์–ด์•ผ ๋‚˜์ค‘์— ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฌ์›Œ. (์˜ˆ: fk_ํ…Œ์ด๋ธ”๋ช…_์ปฌ๋Ÿผ๋ช…, chk_ํ…Œ์ด๋ธ”๋ช…_์กฐ๊ฑด๋ช…)

๐Ÿš€ ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•: ๋ณตํ•ฉ ์ œ์•ฝ์กฐ๊ฑด๊ณผ ์กฐ๊ฑด๋ถ€ ์ œ์•ฝ

์ด์ œ ์ข€ ๋” ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•์„ ์•Œ์•„๋ณผ๊นŒ? ์‹ค๋ฌด์—์„œ ์ •๋ง ์œ ์šฉํ•œ ํ…Œํฌ๋‹‰๋“ค์ด์•ผ! ๐Ÿ’ก

๐Ÿ”— ๋ณตํ•ฉ ์ œ์•ฝ์กฐ๊ฑด (Composite Constraints)

์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ์„ ์กฐํ•ฉํ•ด์„œ ์ œ์•ฝ์กฐ๊ฑด์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด. ์ด๊ฒŒ ์ƒ๊ฐ๋ณด๋‹ค ์—„์ฒญ ์œ ์šฉํ•ด! ๐Ÿ˜Ž

-- ๋ณตํ•ฉ UNIQUE: ๊ฐ™์€ ๋‚ ์งœ์— ๊ฐ™์€ ๊ณ ๊ฐ์ด ๊ฐ™์€ ์ƒํ’ˆ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ์ฃผ๋ฌธํ•  ์ˆ˜ ์—†์Œ
CREATE TABLE daily_orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT NOT NULL,
    product_id INT NOT NULL,
    order_date DATE NOT NULL,
    quantity INT NOT NULL,
    CONSTRAINT uq_daily_order UNIQUE (customer_id, product_id, order_date)
);

-- ๋ณตํ•ฉ CHECK: ์‹œ์ž‘์ผ๊ณผ ์ข…๋ฃŒ์ผ ๊ฒ€์ฆ
CREATE TABLE promotions (
    promotion_id INT AUTO_INCREMENT PRIMARY KEY,
    promotion_name VARCHAR(100) NOT NULL,
    start_date DATE NOT NULL,
    end_date DATE NOT NULL,
    discount_rate DECIMAL(5, 2),
    CONSTRAINT chk_date_range CHECK (end_date >= start_date),
    CONSTRAINT chk_discount_rate CHECK (
        discount_rate > 0 AND discount_rate <= 100
    )
);

-- ๋ณตํ•ฉ ์™ธ๋ž˜ํ‚ค: ์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ์„ ์กฐํ•ฉํ•œ ์ฐธ์กฐ
CREATE TABLE course_schedule (
    schedule_id INT AUTO_INCREMENT PRIMARY KEY,
    course_code VARCHAR(10),
    semester VARCHAR(10),
    classroom VARCHAR(20),
    CONSTRAINT fk_course FOREIGN KEY (course_code, semester)
        REFERENCES courses(course_code, semester)
);

๐ŸŽญ ์กฐ๊ฑด๋ถ€ ์ œ์•ฝ์กฐ๊ฑด

ํŠน์ • ์กฐ๊ฑด์—์„œ๋งŒ ์ ์šฉ๋˜๋Š” ์ œ์•ฝ์กฐ๊ฑด๋„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด! ์ด๊ฑด ์ง„์งœ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์ด์•ผ. ๐Ÿ”ฅ

-- ์กฐ๊ฑด๋ถ€ CHECK: ํ• ์ธ ์ƒํ’ˆ์ผ ๋•Œ๋งŒ ํ• ์ธ๊ฐ€ ํ•„์ˆ˜
CREATE TABLE products_advanced (
    product_id INT AUTO_INCREMENT PRIMARY KEY,
    product_name VARCHAR(100) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    is_on_sale BOOLEAN DEFAULT FALSE,
    discount_price DECIMAL(10, 2),
    
    CONSTRAINT chk_discount_required CHECK (
        (is_on_sale = FALSE) OR 
        (is_on_sale = TRUE AND discount_price IS NOT NULL AND discount_price < price)
    )
);

-- ์กฐ๊ฑด๋ถ€ UNIQUE: ํ™œ์„ฑ ์ƒํƒœ์ธ ๊ฒƒ๋งŒ ์ค‘๋ณต ๋ถˆ๊ฐ€
-- (MySQL 8.0.13 ์ด์ƒ์—์„œ ์ง€์›ํ•˜๋Š” ํ•จ์ˆ˜ ์ธ๋ฑ์Šค ํ™œ์šฉ)
CREATE TABLE user_sessions (
    session_id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    device_type VARCHAR(20),
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ํ™œ์„ฑ ์„ธ์…˜๋งŒ ์ค‘๋ณต ๋ฐฉ์ง€ํ•˜๋Š” ์ธ๋ฑ์Šค
CREATE UNIQUE INDEX idx_active_session 
ON user_sessions(user_id, device_type, (CASE WHEN is_active THEN 1 END));

-- ์กฐ๊ฑด๋ถ€ ์™ธ๋ž˜ํ‚ค: NULL์ด ์•„๋‹ ๋•Œ๋งŒ ์ฐธ์กฐ ๊ฒ€์ฆ
CREATE TABLE optional_manager (
    employee_id INT PRIMARY KEY,
    employee_name VARCHAR(100) NOT NULL,
    manager_id INT,  -- NULL ํ—ˆ์šฉ
    CONSTRAINT fk_manager
        FOREIGN KEY (manager_id)
        REFERENCES optional_manager(employee_id)
        ON DELETE SET NULL
    -- manager_id๊ฐ€ NULL์ด๋ฉด ์™ธ๋ž˜ํ‚ค ๊ฒ€์ฆ ์•ˆ ํ•จ!
);

โฐ ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ์ œ์•ฝ์กฐ๊ฑด

-- ์˜์—… ์‹œ๊ฐ„ ์ฒดํฌ
CREATE TABLE reservations (
    reservation_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT NOT NULL,
    reservation_time DATETIME NOT NULL,
    
    CONSTRAINT chk_business_hours CHECK (
        HOUR(reservation_time) >= 9 AND 
        HOUR(reservation_time) < 22
    ),
    CONSTRAINT chk_future_reservation CHECK (
        reservation_time > NOW()
    )
);

-- ์œ ํšจ ๊ธฐ๊ฐ„ ์ฒดํฌ
CREATE TABLE coupons (
    coupon_id INT AUTO_INCREMENT PRIMARY KEY,
    coupon_code VARCHAR(20) NOT NULL UNIQUE,
    discount_amount DECIMAL(10, 2) NOT NULL,
    valid_from DATE NOT NULL,
    valid_until DATE NOT NULL,
    is_used BOOLEAN DEFAULT FALSE,
    
    CONSTRAINT chk_valid_period CHECK (valid_until >= valid_from),
    CONSTRAINT chk_not_expired CHECK (
        is_used = TRUE OR valid_until >= CURRENT_DATE
    )
);
์ œ์•ฝ์กฐ๊ฑด ์„ค๊ณ„ ํ”Œ๋กœ์šฐ์ฐจํŠธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ ์‹œ์ž‘ ๊ฐ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธํ‚ค ์„ค์ • (PRIMARY KEY) ํ…Œ์ด๋ธ” ๊ฐ„ ๊ด€๊ณ„ ์ •์˜ (FOREIGN KEY) ํ•„์ˆ˜ ์ž…๋ ฅ ํ•ญ๋ชฉ ์ง€์ • (NOT NULL) ์ค‘๋ณต ๋ฐฉ์ง€ ํ•ญ๋ชฉ ์ง€์ • (UNIQUE) ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™ ๊ฒ€์ฆ ์ถ”๊ฐ€ (CHECK) ๊ธฐ๋ณธ๊ฐ’ ์„ค์ • (DEFAULT) โœ“

๐Ÿ› ์ œ์•ฝ์กฐ๊ฑด ์œ„๋ฐ˜ ์ฒ˜๋ฆฌํ•˜๊ธฐ

์ œ์•ฝ์กฐ๊ฑด์„ ์„ค์ •ํ–ˆ๋‹ค๋ฉด, ์œ„๋ฐ˜ํ–ˆ์„ ๋•Œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€๋„ ์•Œ์•„์•ผ๊ฒ ์ง€? ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ํ›จ์”ฌ ์ข‹์•„์ ธ! ๐Ÿ˜Š

๐ŸŽฏ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ์ „๋žต

-- 1. ํŠธ๋žœ์žญ์…˜๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ
START TRANSACTION;

BEGIN
    -- ์—ฌ๋Ÿฌ ์ž‘์—… ์ˆ˜ํ–‰
    INSERT INTO customers (username, email) 
    VALUES ('john_doe', 'john@example.com');
    
    INSERT INTO orders (customer_id, total_amount)
    VALUES (LAST_INSERT_ID(), 50000);
    
    -- ๋ชจ๋“  ์ž‘์—…์ด ์„ฑ๊ณตํ•˜๋ฉด ์ปค๋ฐ‹
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        -- ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ๋กค๋ฐฑ
        ROLLBACK;
        -- ์—๋Ÿฌ ๋กœ๊น…
        INSERT INTO error_log (error_message, error_time)
        VALUES (SQLERRM, NOW());
END;

-- 2. ์ €์žฅ ํ”„๋กœ์‹œ์ €์—์„œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
DELIMITER //

CREATE PROCEDURE safe_insert_product(
    IN p_name VARCHAR(100),
    IN p_price DECIMAL(10,2),
    IN p_stock INT,
    OUT p_result VARCHAR(100)
)
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        -- ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์ฒ˜๋ฆฌ
        GET DIAGNOSTICS CONDITION 1
            @sqlstate = RETURNED_SQLSTATE,
            @errno = MYSQL_ERRNO,
            @text = MESSAGE_TEXT;
        
        SET p_result = CONCAT('์—๋Ÿฌ ๋ฐœ์ƒ: ', @text);
        ROLLBACK;
    END;
    
    START TRANSACTION;
    
    -- ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
    IF p_price <= 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = '๊ฐ€๊ฒฉ์€ 0๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค!';
    END IF;
    
    IF p_stock < 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = '์žฌ๊ณ ๋Š” ์Œ์ˆ˜์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!';
    END IF;
    
    -- ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
    INSERT INTO products (product_name, price, stock_quantity)
    VALUES (p_name, p_price, p_stock);
    
    COMMIT;
    SET p_result = '์ƒํ’ˆ์ด ์„ฑ๊ณต์ ์œผ๋กœ ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค!';
END //

DELIMITER ;

-- ์‚ฌ์šฉ ์˜ˆ์‹œ
CALL safe_insert_product('๋…ธํŠธ๋ถ', 1500000, 10, @result);
SELECT @result;

๐Ÿ“ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ์˜ ์ฒ˜๋ฆฌ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ œ์•ฝ์กฐ๊ฑด๊ณผ ํ•จ๊ป˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ๋„ ๊ฒ€์ฆ์„ ํ•˜๋ฉด ๋” ์ข‹์€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์–ด! ๐ŸŽจ

-- Python ์˜ˆ์‹œ (Flask + SQLAlchemy)
from flask import Flask, request, jsonify
from sqlalchemy.exc import IntegrityError
import re

@app.route('/api/users', methods=['POST'])
def create_user():
    try:
        data = request.get_json()
        
        # 1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ ๊ฒ€์ฆ
        if not data.get('email'):
            return jsonify({'error': '์ด๋ฉ”์ผ์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค!'}), 400
        
        # ์ด๋ฉ”์ผ ํ˜•์‹ ๊ฒ€์ฆ
        email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        if not re.match(email_pattern, data['email']):
            return jsonify({'error': '์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค!'}), 400
        
        # 2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฝ์ž…
        new_user = User(
            username=data['username'],
            email=data['email']
        )
        db.session.add(new_user)
        db.session.commit()
        
        return jsonify({'message': '์‚ฌ์šฉ์ž๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!'}), 201
        
    except IntegrityError as e:
        db.session.rollback()
        
        # ์ œ์•ฝ์กฐ๊ฑด ์œ„๋ฐ˜ ํƒ€์ž…๋ณ„ ์ฒ˜๋ฆฌ
        error_message = str(e.orig)
        
        if 'Duplicate entry' in error_message:
            if 'email' in error_message:
                return jsonify({'error': '์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค!'}), 409
            elif 'username' in error_message:
                return jsonify({'error': '์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž๋ช…์ž…๋‹ˆ๋‹ค!'}), 409
        
        elif 'foreign key constraint' in error_message.lower():
            return jsonify({'error': '์ฐธ์กฐํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!'}), 400
        
        elif 'check constraint' in error_message.lower():
            return jsonify({'error': '์ž…๋ ฅ๊ฐ’์ด ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!'}), 400
        
        return jsonify({'error': '๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค!'}), 500
    
    except Exception as e:
        return jsonify({'error': '์„œ๋ฒ„ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค!'}), 500

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ

์ œ์•ฝ์กฐ๊ฑด์€ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์„ ๋ณด์žฅํ•˜์ง€๋งŒ, ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์–ด. ์‹ค๋ฌด์—์„œ ๊ผญ ์•Œ์•„์•ผ ํ•  ์ตœ์ ํ™” ํŒ์„ ์•Œ๋ ค์ค„๊ฒŒ! ๐Ÿš€

1๏ธโƒฃ ์ธ๋ฑ์Šค ํ™œ์šฉ

โ€ข FOREIGN KEY์™€ UNIQUE ์ œ์•ฝ์กฐ๊ฑด์€ ์ž๋™์œผ๋กœ ์ธ๋ฑ์Šค๋ฅผ ์ƒ์„ฑํ•ด
โ€ข CHECK ์ œ์•ฝ์กฐ๊ฑด์— ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์ปฌ๋Ÿผ์€ ๋ณ„๋„ ์ธ๋ฑ์Šค ๊ณ ๋ ค
โ€ข ๋ณตํ•ฉ ์ œ์•ฝ์กฐ๊ฑด์˜ ๊ฒฝ์šฐ ์ปฌ๋Ÿผ ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•ด (์„ ํƒ๋„๊ฐ€ ๋†’์€ ์ปฌ๋Ÿผ์„ ์•ž์—!)
-- ์ข‹์€ ์˜ˆ: ์„ ํƒ๋„๊ฐ€ ๋†’์€ ์ปฌ๋Ÿผ์„ ์•ž์—
CREATE INDEX idx_order_search ON orders(status, customer_id, order_date);

-- ๋‚˜์œ ์˜ˆ: ์„ ํƒ๋„๊ฐ€ ๋‚ฎ์€ ์ปฌ๋Ÿผ์„ ์•ž์—
CREATE INDEX idx_order_bad ON orders(status, order_date, customer_id);
-- status๋Š” ๊ฐ’์˜ ์ข…๋ฅ˜๊ฐ€ ์ ์–ด์„œ ์„ ํƒ๋„๊ฐ€ ๋‚ฎ์Œ!
2๏ธโƒฃ ์ œ์•ฝ์กฐ๊ฑด ๋น„ํ™œ์„ฑํ™” (๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ž‘์—… ์‹œ)

๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ INSERTํ•  ๋•Œ๋Š” ์ผ์‹œ์ ์œผ๋กœ ์ œ์•ฝ์กฐ๊ฑด์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋ฉด ์†๋„๊ฐ€ ๋นจ๋ผ์ ธ!
-- MySQL์—์„œ ์™ธ๋ž˜ํ‚ค ์ฒดํฌ ์ž„์‹œ ๋น„ํ™œ์„ฑํ™”
SET FOREIGN_KEY_CHECKS = 0;

-- ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
INSERT INTO orders (customer_id, product_id, quantity)
SELECT customer_id, product_id, quantity
FROM temp_orders;

-- ์™ธ๋ž˜ํ‚ค ์ฒดํฌ ๋‹ค์‹œ ํ™œ์„ฑํ™”
SET FOREIGN_KEY_CHECKS = 1;

-- ์ œ์•ฝ์กฐ๊ฑด ์œ„๋ฐ˜ ๋ฐ์ดํ„ฐ ํ™•์ธ
SELECT o.*
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.customer_id
WHERE c.customer_id IS NULL;
3๏ธโƒฃ ํŠธ๋ฆฌ๊ฑฐ ์ตœ์ ํ™”

ํŠธ๋ฆฌ๊ฑฐ๋Š” ํŽธ๋ฆฌํ•˜์ง€๋งŒ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์–ด. ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋งŒ ์‚ฌ์šฉํ•˜์ž!
-- ๋‚˜์œ ์˜ˆ: ๋งค๋ฒˆ ์ „์ฒด ํ…Œ์ด๋ธ” ์Šค์บ”
CREATE TRIGGER update_product_rating
AFTER INSERT ON reviews
FOR EACH ROW
BEGIN
    UPDATE products
    SET average_rating = (
        SELECT AVG(rating)
        FROM reviews
        WHERE product_id = NEW.product_id
    )
    WHERE product_id = NEW.product_id;
END;

-- ์ข‹์€ ์˜ˆ: ์ฆ๋ถ„ ์—…๋ฐ์ดํŠธ
CREATE TRIGGER update_product_rating_optimized
AFTER INSERT ON reviews
FOR EACH ROW
BEGIN
    UPDATE products
    SET 
        total_rating = total_rating + NEW.rating,
        review_count = review_count + 1,
        average_rating = (total_rating + NEW.rating) / (review_count + 1)
    WHERE product_id = NEW.product_id;
END;
4๏ธโƒฃ ํŒŒํ‹ฐ์…”๋‹ ํ™œ์šฉ

๋Œ€์šฉ๋Ÿ‰ ํ…Œ์ด๋ธ”์€ ํŒŒํ‹ฐ์…”๋‹์„ ์‚ฌ์šฉํ•˜๋ฉด ์ œ์•ฝ์กฐ๊ฑด ๊ฒ€์‚ฌ ์†๋„๊ฐ€ ๋นจ๋ผ์ ธ!
-- ๋‚ ์งœ ๊ธฐ๋ฐ˜ ํŒŒํ‹ฐ์…”๋‹
CREATE TABLE orders_partitioned (
    order_id INT AUTO_INCREMENT,
    customer_id INT NOT NULL,
    order_date DATE NOT NULL,
    total_amount DECIMAL(10, 2),
    PRIMARY KEY (order_id, order_date),
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
)
PARTITION BY RANGE (YEAR(order_date)) (
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p2024 VALUES LESS THAN (2025),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

๐ŸŽ“ ์‹ค๋ฌด ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

๋งˆ์ง€๋ง‰์œผ๋กœ ์‹ค๋ฌด์—์„œ ์ •๋ง ์ค‘์š”ํ•œ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค๋ฅผ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ! ์ด๊ฑด ๋‚ด๊ฐ€ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ๋ฐฐ์šด ์†Œ์ค‘ํ•œ ๊ฒฝํ—˜๋“ค์ด์•ผ. ๐Ÿ’Ž

โœ… DO - ์ด๋ ‡๊ฒŒ ํ•˜์„ธ์š”!

1. ๋ช…ํ™•ํ•œ ๋ช…๋ช… ๊ทœ์น™ ์‚ฌ์šฉ
โ€ข pk_ํ…Œ์ด๋ธ”๋ช…: PRIMARY KEY
โ€ข fk_ํ…Œ์ด๋ธ”๋ช…_์ฐธ์กฐํ…Œ์ด๋ธ”๋ช…: FOREIGN KEY
โ€ข uq_ํ…Œ์ด๋ธ”๋ช…_์ปฌ๋Ÿผ๋ช…: UNIQUE
โ€ข chk_ํ…Œ์ด๋ธ”๋ช…_์กฐ๊ฑด๋ช…: CHECK
โ€ข idx_ํ…Œ์ด๋ธ”๋ช…_์ปฌ๋Ÿผ๋ช…: INDEX

2. ๋ฌธ์„œํ™”
โ€ข ๊ฐ ์ œ์•ฝ์กฐ๊ฑด์˜ ๋ชฉ์ ๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์„ ์ฃผ์„์œผ๋กœ ๋ช…์‹œ
โ€ข ERD(Entity Relationship Diagram)์— ์ œ์•ฝ์กฐ๊ฑด ํ‘œ์‹œ
โ€ข ๋ฐ์ดํ„ฐ ๋”•์…”๋„ˆ๋ฆฌ ์ž‘์„ฑ

3. ๋‹จ๊ณ„์  ์ ์šฉ
โ€ข ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ๋จผ์ € ํ…Œ์ŠคํŠธ
โ€ข ์Šคํ…Œ์ด์ง• ํ™˜๊ฒฝ์—์„œ ์„ฑ๋Šฅ ๊ฒ€์ฆ
โ€ข ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ์‹œ ๋ชจ๋‹ˆํ„ฐ๋ง

4. ๋ฒ„์ „ ๊ด€๋ฆฌ
โ€ข ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์ด๋ ฅ์„ Git์œผ๋กœ ๊ด€๋ฆฌ
โ€ข ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ
โ€ข ๋กค๋ฐฑ ๊ณ„ํš ์ˆ˜๋ฆฝ
โŒ DON'T - ์ด๊ฑด ํ”ผํ•˜์„ธ์š”!

1. ๊ณผ๋„ํ•œ ์ œ์•ฝ์กฐ๊ฑด
โ€ข ๋ชจ๋“  ์ปฌ๋Ÿผ์— CHECK ์ œ์•ฝ์กฐ๊ฑด์„ ๊ฑธ์ง€ ๋งˆ์„ธ์š”
โ€ข ์„ฑ๋Šฅ๊ณผ ๋ฌด๊ฒฐ์„ฑ์˜ ๊ท ํ˜•์„ ์ฐพ์œผ์„ธ์š”

2. CASCADE ๋‚จ์šฉ
โ€ข ON DELETE CASCADE๋Š” ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉ
โ€ข ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” RESTRICT ์‚ฌ์šฉ ๊ถŒ์žฅ

3. ์ œ์•ฝ์กฐ๊ฑด ์—†๋Š” ์„ค๊ณ„
โ€ข "์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ฒ€์ฆํ•˜๋‹ˆ๊นŒ ๊ดœ์ฐฎ์•„"๋Š” ์œ„ํ—˜ํ•œ ์ƒ๊ฐ!
โ€ข ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ ˆ๋ฒจ์˜ ๋ฌด๊ฒฐ์„ฑ ๋ณด์žฅ์€ ํ•„์ˆ˜

4. ํ…Œ์ŠคํŠธ ๋ถ€์กฑ
โ€ข ์ œ์•ฝ์กฐ๊ฑด ์ถ”๊ฐ€ ํ›„ ๋ฐ˜๋“œ์‹œ ํ…Œ์ŠคํŠธ
โ€ข ์—ฃ์ง€ ์ผ€์ด์Šค(๊ฒฝ๊ณ„๊ฐ’) ๊ฒ€์ฆ ํ•„์ˆ˜
-- ์ข‹์€ ์˜ˆ: ๋ช…ํ™•ํ•œ ๋ช…๋ช…๊ณผ ์ฃผ์„
CREATE TABLE products (
    product_id INT AUTO_INCREMENT,
    product_name VARCHAR(100) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    discount_price DECIMAL(10, 2),
    stock_quantity INT NOT NULL DEFAULT 0,
    
    -- ๊ธฐ๋ณธํ‚ค: ์ƒํ’ˆ ๊ณ ์œ  ์‹๋ณ„์ž
    CONSTRAINT pk_products PRIMARY KEY (product_id),
    
    -- ๊ฐ€๊ฒฉ ๊ฒ€์ฆ: ์ •๊ฐ€๋Š” 0๋ณด๋‹ค ์ปค์•ผ ํ•จ
    CONSTRAINT chk_products_price CHECK (price > 0),
    
    -- ํ• ์ธ๊ฐ€ ๊ฒ€์ฆ: ํ• ์ธ๊ฐ€๋Š” ์ •๊ฐ€๋ณด๋‹ค ๋‚ฎ์•„์•ผ ํ•จ
    CONSTRAINT chk_products_discount CHECK (
        discount_price IS NULL OR 
        (discount_price > 0 AND discount_price < price)
    ),
    
    -- ์žฌ๊ณ  ๊ฒ€์ฆ: ์žฌ๊ณ ๋Š” ์Œ์ˆ˜์ผ ์ˆ˜ ์—†์Œ
    CONSTRAINT chk_products_stock CHECK (stock_quantity >= 0),
    
    -- ์ƒํ’ˆ๋ช… ์ค‘๋ณต ๋ฐฉ์ง€
    CONSTRAINT uq_products_name UNIQUE (product_name)
) COMMENT='์ƒํ’ˆ ์ •๋ณด ํ…Œ์ด๋ธ” - 2024.01 ์ƒ์„ฑ';

๐Ÿ” ์‹ค์ „ ๋””๋ฒ„๊น… ๊ฐ€์ด๋“œ

์ œ์•ฝ์กฐ๊ฑด ๊ด€๋ จ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ• ์ง€ ์•Œ์•„๋ณด์ž! ์‹ค๋ฌด์—์„œ ์ž์ฃผ ๋งˆ์ฃผ์น˜๋Š” ์ƒํ™ฉ๋“ค์ด์•ผ. ๐Ÿ”ง

๐Ÿ› ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ์™€ ํ•ด๊ฒฐ๋ฒ•

์—๋Ÿฌ 1: Duplicate entry for key 'PRIMARY'

์›์ธ: ๊ธฐ๋ณธํ‚ค ์ค‘๋ณต
ํ•ด๊ฒฐ: AUTO_INCREMENT ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๊ณ ์œ ํ•œ ๊ฐ’ ์ƒ์„ฑ
-- ๋ฌธ์ œ ์ƒํ™ฉ
INSERT INTO users (user_id, username) VALUES (1, 'john');
INSERT INTO users (user_id, username) VALUES (1, 'jane');  -- โŒ ์—๋Ÿฌ!

-- ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 1: AUTO_INCREMENT ์‚ฌ์šฉ
CREATE TABLE users (
    user_id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50)
);

INSERT INTO users (username) VALUES ('john');
INSERT INTO users (username) VALUES ('jane');  -- โœ… ์ž๋™์œผ๋กœ 2 ํ• ๋‹น

-- ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 2: UUID ์‚ฌ์šฉ
CREATE TABLE users (
    user_id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
    username VARCHAR(50)
);

INSERT INTO users (username) VALUES ('john');  -- โœ… ์ž๋™์œผ๋กœ UUID ์ƒ์„ฑ
์—๋Ÿฌ 2: Cannot add or update a child row: foreign key constraint fails

์›์ธ: ์ฐธ์กฐํ•˜๋Š” ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Œ
ํ•ด๊ฒฐ: ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ๋จผ์ € ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์™ธ๋ž˜ํ‚ค ๊ฐ’ ํ™•์ธ
-- ๋ฌธ์ œ ์ƒํ™ฉ
INSERT INTO orders (order_id, customer_id, product_id)
VALUES (1, 999, 1);  -- โŒ customer_id 999๊ฐ€ customers ํ…Œ์ด๋ธ”์— ์—†์Œ!

-- ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 1: ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ๋จผ์ € ํ™•์ธ
SELECT customer_id FROM customers WHERE customer_id = 999;
-- ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด ๋จผ์ € ๊ณ ๊ฐ ์ƒ์„ฑ

-- ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 2: ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ ์‚ฝ์ž…ํ•˜๋Š” ์ฟผ๋ฆฌ
INSERT INTO orders (order_id, customer_id, product_id)
SELECT 1, c.customer_id, 1
FROM customers c
WHERE c.customer_id = 999;
-- ๊ณ ๊ฐ์ด ์—†์œผ๋ฉด ์•„๋ฌด๊ฒƒ๋„ ์‚ฝ์ž…๋˜์ง€ ์•Š์Œ

-- ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 3: ์™ธ๋ž˜ํ‚ค ์ œ์•ฝ์กฐ๊ฑด ํ™•์ธ ๋ฐ ์ˆ˜์ •
ALTER TABLE orders
DROP FOREIGN KEY fk_customer;

ALTER TABLE orders
ADD CONSTRAINT fk_customer
FOREIGN KEY (customer_id)
REFERENCES customers(customer_id)
ON DELETE RESTRICT
ON UPDATE CASCADE;
์—๋Ÿฌ 3: Check constraint violation

์›์ธ: CHECK ์ œ์•ฝ์กฐ๊ฑด ์œ„๋ฐ˜
ํ•ด๊ฒฐ: ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ๋˜๋Š” ์ œ์•ฝ์กฐ๊ฑด ์ˆ˜์ •
-- ๋ฌธ์ œ ์ƒํ™ฉ
INSERT INTO employees (employee_id, name, age, salary)
VALUES (1, 'John', 15, 3000000);  -- โŒ age < 18

-- ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 1: ์˜ฌ๋ฐ”๋ฅธ ๊ฐ’ ์ž…๋ ฅ
INSERT INTO employees (employee_id, name, age, salary)
VALUES (1, 'John', 25, 3000000);  -- โœ…

-- ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 2: ์ œ์•ฝ์กฐ๊ฑด ํ™•์ธ
SELECT CONSTRAINT_NAME, CHECK_CLAUSE
FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS
WHERE TABLE_NAME = 'employees';

-- ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 3: ํ•„์š”์‹œ ์ œ์•ฝ์กฐ๊ฑด ์ˆ˜์ •
ALTER TABLE employees
DROP CHECK chk_age;

ALTER TABLE employees
ADD CONSTRAINT chk_age CHECK (age >= 16 AND age <= 70);

๐Ÿ”Ž ์ œ์•ฝ์กฐ๊ฑด ์œ„๋ฐ˜ ๋ฐ์ดํ„ฐ ์ฐพ๊ธฐ

-- 1. ์ค‘๋ณต๋œ ๋ฐ์ดํ„ฐ ์ฐพ๊ธฐ (UNIQUE ์œ„๋ฐ˜ ๊ฐ€๋Šฅ์„ฑ)
SELECT email, COUNT(*) as count
FROM users
GROUP BY email
HAVING COUNT(*) > 1;

-- 2. NULL ๊ฐ’ ์ฐพ๊ธฐ (NOT NULL ์œ„๋ฐ˜ ๊ฐ€๋Šฅ์„ฑ)
SELECT *
FROM products
WHERE product_name IS NULL OR price IS NULL;

-- 3. ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ ์œ„๋ฐ˜ ์ฐพ๊ธฐ (FOREIGN KEY ์œ„๋ฐ˜ ๊ฐ€๋Šฅ์„ฑ)
SELECT o.*
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.customer_id
WHERE c.customer_id IS NULL;

-- 4. CHECK ์ œ์•ฝ์กฐ๊ฑด ์œ„๋ฐ˜ ๊ฐ€๋Šฅ์„ฑ ์ฐพ๊ธฐ
SELECT *
FROM employees
WHERE age < 18 OR age > 70 OR salary <= 0;

-- 5. ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚œ ๋ฐ์ดํ„ฐ ์ฐพ๊ธฐ
SELECT *
FROM products
WHERE discount_price >= price  -- ํ• ์ธ๊ฐ€๊ฐ€ ์ •๊ฐ€๋ณด๋‹ค ๋†’๊ฑฐ๋‚˜ ๊ฐ™์Œ
   OR stock_quantity < 0;      -- ์žฌ๊ณ ๊ฐ€ ์Œ์ˆ˜

๐ŸŒŸ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€! ์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ์—ˆ๋‹ค๋ฉด ์ •๋ง ๋Œ€๋‹จํ•ด! ๐Ÿ‘

๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ๊ณผ ์ œ์•ฝ์กฐ๊ฑด์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„์˜ ํ•ต์‹ฌ์ด์•ผ. ์ฒ˜์Œ์—๋Š” ๋ณต์žกํ•˜๊ณ  ๊ท€์ฐฎ๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ œ๋Œ€๋กœ ์„ค์ •ํ•ด๋‘๋ฉด:

โœ… ๋ฐ์ดํ„ฐ ํ’ˆ์งˆ์ด ํ–ฅ์ƒ๋ผ
โœ… ๋ฒ„๊ทธ๊ฐ€ ์ค„์–ด๋“ค์–ด
โœ… ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์›Œ์ ธ
โœ… ์‹ ๋ขฐ์„ฑ ์žˆ๋Š” ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด

๊ธฐ์–ตํ•ด! ์ œ์•ฝ์กฐ๊ฑด์€ ๊ท€์ฐฎ์€ ๊ฒŒ ์•„๋‹ˆ๋ผ ๋ฏธ๋ž˜์˜ ๋‚˜๋ฅผ ๋„์™€์ฃผ๋Š” ์นœ๊ตฌ์•ผ! ๐Ÿ˜Š

๐Ÿ’ก ํ•™์Šต ๋กœ๋“œ๋งต

1๏ธโƒฃ ๊ธฐ์ดˆ: PRIMARY KEY, FOREIGN KEY, NOT NULL ์™„๋ฒฝํžˆ ์ดํ•ด
2๏ธโƒฃ ์ค‘๊ธ‰: UNIQUE, CHECK, DEFAULT ํ™œ์šฉ
3๏ธโƒฃ ๊ณ ๊ธ‰: ํŠธ๋ฆฌ๊ฑฐ, ์ €์žฅ ํ”„๋กœ์‹œ์ €, ๋ณตํ•ฉ ์ œ์•ฝ์กฐ๊ฑด
4๏ธโƒฃ ์‹ค์ „: ์„ฑ๋Šฅ ์ตœ์ ํ™”, ์—๋Ÿฌ ์ฒ˜๋ฆฌ, ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„๋‚˜ ์ œ์•ฝ์กฐ๊ฑด ๊ตฌํ˜„์ด ์–ด๋ ต๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ! ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์ด ํ”„๋กœ์ ํŠธ์— ์ตœ์ ํ™”๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„๋ฅผ ๋„์™€์ค„ ๊ฑฐ์•ผ. ๐Ÿš€

์ด์ œ ๋„ˆ๋„ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ๋งˆ์Šคํ„ฐ! ๐Ÿ’ช
์‹ค์ „ ํ”„๋กœ์ ํŠธ์—์„œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ ์šฉํ•ด๋ณด๊ณ , ๊ณ„์† ์—ฐ์Šตํ•˜๋ฉด์„œ ์‹ค๋ ฅ์„ ํ‚ค์›Œ๋‚˜๊ฐ€์ž!

๊ถ๊ธˆํ•œ ์ ์ด ์žˆ๊ฑฐ๋‚˜ ๋” ์•Œ๊ณ  ์‹ถ์€ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“  ์งˆ๋ฌธํ•ด์ค˜.
ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์ž! ๐ŸŒฑโœจ

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.