diff --git a/image.png b/image.png new file mode 100644 index 000000000..1a8793e99 Binary files /dev/null and b/image.png differ diff --git a/queries.sql b/queries.sql new file mode 100644 index 000000000..953eb1722 --- /dev/null +++ b/queries.sql @@ -0,0 +1,176 @@ +/* +================================================== +🐼 Panda Market - queries.sql +================================================== +*/ + +/* +1️⃣ λ‚΄ 정보 μ—…λ°μ΄νŠΈ ν•˜κΈ° +- λ‹‰λ„€μž„μ„ "test"둜 μ—…λ°μ΄νŠΈ +- ν˜„μž¬ λ‘œκ·ΈμΈν•œ μœ μ € id =1 +*/ +UPDATE users +SET nickname = 'test' +WHERE id = 1; + + +/* +2️⃣ λ‚΄κ°€ μƒμ„±ν•œ μƒν’ˆ 쑰회 +- user_id = 1 +- μ΅œμ‹ μˆœ μ •λ ¬ +- 10κ°œμ”© νŽ˜μ΄μ§€λ„€μ΄μ…˜ (3νŽ˜μ΄μ§€ β†’ OFFSET 20) +*/ +SELECT id, name, price, created_at +FROM products +WHERE user_id = 1 +ORDER BY created_at DESC +LIMIT 10 OFFSET 20; + + +/* +3️⃣ λ‚΄κ°€ μƒμ„±ν•œ μƒν’ˆμ˜ 총 개수 +- user_id = 1 +*/ +SELECT COUNT(*) AS total_products +FROM products +WHERE user_id = 1; + + +/* +4️⃣ λ‚΄κ°€ μ’‹μ•„μš” λˆ„λ₯Έ μƒν’ˆ 쑰회 +- user_id = 1 +- μ΅œμ‹ μˆœ μ •λ ¬ +- 10κ°œμ”© νŽ˜μ΄μ§€λ„€μ΄μ…˜ (3νŽ˜μ΄μ§€ β†’ OFFSET 20) +*/ +SELECT + p.id, + p.name, + p.price, + l.created_at AS liked_at +FROM likes l +JOIN products p ON p.id = l.product_id +WHERE l.user_id = 1 +ORDER BY l.created_at DESC +LIMIT 10 OFFSET 20; + + +/* +5️⃣ λ‚΄κ°€ μ’‹μ•„μš” λˆ„λ₯Έ μƒν’ˆμ˜ 총 개수 +- user_id = 1 +*/ +SELECT COUNT(*) AS total_liked_products +FROM likes +WHERE user_id = 1 AND product_id IS NOT NULL; + + +/* +6️⃣ μƒν’ˆ 생성 +- user_id = 1 +*/ +INSERT INTO products (user_id, name, description, price) +VALUES (1, 'ν…ŒμŠ€νŠΈ μƒν’ˆ', 'ν…ŒμŠ€νŠΈμš© μƒν’ˆμž…λ‹ˆλ‹€.', 15000); + + +/* +7️⃣ μƒν’ˆ λͺ©λ‘ 쑰회 +- μƒν’ˆλͺ…에 "test"κ°€ ν¬ν•¨λœ μƒν’ˆ +- μ΅œμ‹ μˆœ μ •λ ¬ +- 10κ°œμ”© νŽ˜μ΄μ§€λ„€μ΄μ…˜ (1νŽ˜μ΄μ§€ β†’ OFFSET = 0) +- 각 μƒν’ˆμ˜ μ’‹μ•„μš” 개수λ₯Ό 포함 +*/ +SELECT + p.id, + p.name, + p.price, + COUNT(l.id) AS like_count, + p.created_at +FROM products p +LEFT JOIN likes l ON l.product_id = p.id +WHERE p.name ILIKE '%test%' +GROUP BY p.id +ORDER BY p.created_at DESC +LIMIT 10 OFFSET 0; + + +/* +8️⃣ μƒν’ˆ 상세 쑰회 +- 1번 μƒν’ˆ 쑰회 +- μ’‹μ•„μš” 수 포함 +*/ +SELECT + p.id, + p.name, + p.description, + p.price, + p.created_at, + COUNT(l.id) AS like_count +FROM products p +LEFT JOIN likes l ON l.product_id = p.id +WHERE p.id = 1 +GROUP BY p.id; + + +/* +9️⃣ μƒν’ˆ 정보 μˆ˜μ • +- 1번 μƒν’ˆ μˆ˜μ • +- user_id = 1 (본인 μƒν’ˆλ§Œ μˆ˜μ • κ°€λŠ₯) +*/ +UPDATE products +SET + name = 'μˆ˜μ •λœ μƒν’ˆλͺ…', + description = 'μƒν’ˆ μ„€λͺ…이 μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + price = 18000 +WHERE id = 1 AND user_id = 1; + + +/* +πŸ”Ÿ μƒν’ˆ μ‚­μ œ +- 1번 μƒν’ˆ μ‚­μ œ +- user_id = 1 (본인 μƒν’ˆλ§Œ) +*/ +DELETE FROM products +WHERE id = 1 AND user_id = 1; + + +/* +11️⃣ μƒν’ˆ μ’‹μ•„μš” +- 1번 μœ μ €κ°€ 2번 μƒν’ˆ μ’‹μ•„μš” +*/ +INSERT INTO likes (user_id, product_id) +VALUES (1, 2) +ON CONFLICT (user_id, product_id) DO NOTHING; + + +/* +12️⃣ μƒν’ˆ μ’‹μ•„μš” μ·¨μ†Œ +- 1번 μœ μ €κ°€ 2번 μƒν’ˆ μ’‹μ•„μš” μ·¨μ†Œ +*/ +DELETE FROM likes +WHERE user_id = 1 AND product_id = 2; + + +/* +13️⃣ μƒν’ˆ λŒ“κΈ€ μž‘μ„± +- 1번 μœ μ €κ°€ 2번 μƒν’ˆμ— λŒ“κΈ€ μž‘μ„± +*/ +INSERT INTO comments (user_id, product_id, content) +VALUES (1, 2, '쒋은 μƒν’ˆμ΄λ„€μš”!'); + + +/* +14️⃣ μƒν’ˆ λŒ“κΈ€ 쑰회 +- 1번 μƒν’ˆμ— 달린 λŒ“κΈ€ λͺ©λ‘ +- μ΅œμ‹  순 +- λŒ“κΈ€ λ‚ μ§œ 2025-03-25 이전 데이터 10개 +*/ +SELECT + c.id, + u.nickname AS author, + c.content, + c.created_at +FROM comments c +JOIN users u ON u.id = c.user_id +WHERE c.product_id = 1 + AND c.created_at < '2025-03-25' +ORDER BY c.created_at DESC +LIMIT 10; diff --git a/schema.sql b/schema.sql new file mode 100644 index 000000000..abd40a311 --- /dev/null +++ b/schema.sql @@ -0,0 +1,198 @@ + +CREATE OR REPLACE FUNCTION update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- =========================================== +-- USERS +-- =========================================== +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + email VARCHAR(50) UNIQUE NOT NULL, + nickname VARCHAR(255) NOT NULL, + password VARCHAR(100) NOT NULL, + image VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +CREATE TRIGGER trg_users_updated_at +BEFORE UPDATE ON users +FOR EACH ROW +EXECUTE FUNCTION update_updated_at_column(); + +-- =========================================== +-- PRODUCTS +-- =========================================== +CREATE TABLE products ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + name VARCHAR(50) NOT NULL, + description VARCHAR(255), + price INT NOT NULL CHECK (price >= 0), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + CONSTRAINT fk_products_user FOREIGN KEY (user_id) + REFERENCES users(id) ON DELETE CASCADE +); + +CREATE INDEX idx_products_user_id ON products(user_id); +CREATE INDEX idx_products_created_at ON products(created_at); + +CREATE TRIGGER trg_products_updated_at +BEFORE UPDATE ON products +FOR EACH ROW +EXECUTE FUNCTION update_updated_at_column(); + +-- =========================================== +-- IMAGES (μƒν’ˆ 1:N) +-- =========================================== +CREATE TABLE images ( + id SERIAL PRIMARY KEY, + product_id INT NOT NULL, + image_url VARCHAR(255) NOT NULL, + is_main BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + CONSTRAINT fk_images_product FOREIGN KEY (product_id) + REFERENCES products(id) ON DELETE CASCADE +); + +CREATE INDEX idx_images_product_id ON images(product_id); + +CREATE TRIGGER trg_images_updated_at +BEFORE UPDATE ON images +FOR EACH ROW +EXECUTE FUNCTION update_updated_at_column(); + +-- =========================================== +-- ARTICLES (κ²Œμ‹œνŒ) +-- =========================================== +CREATE TABLE articles ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + title VARCHAR(100) NOT NULL, + content TEXT NOT NULL, + image VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + CONSTRAINT fk_articles_user FOREIGN KEY (user_id) + REFERENCES users(id) ON DELETE CASCADE +); + +CREATE INDEX idx_articles_user_id ON articles(user_id); +CREATE INDEX idx_articles_created_at ON articles(created_at); + +CREATE TRIGGER trg_articles_updated_at +BEFORE UPDATE ON articles +FOR EACH ROW +EXECUTE FUNCTION update_updated_at_column(); + +-- =========================================== +-- COMMENTS (μƒν’ˆ/κ²Œμ‹œκΈ€ λ‘˜ λ‹€ κ°€λŠ₯) +-- =========================================== +CREATE TABLE comments ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + product_id INT, + article_id INT, + content VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + CONSTRAINT fk_comments_user FOREIGN KEY (user_id) + REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT fk_comments_product FOREIGN KEY (product_id) + REFERENCES products(id) ON DELETE CASCADE, + CONSTRAINT fk_comments_article FOREIGN KEY (article_id) + REFERENCES articles(id) ON DELETE CASCADE, + CONSTRAINT chk_comment_target CHECK ( + (product_id IS NOT NULL AND article_id IS NULL) OR + (product_id IS NULL AND article_id IS NOT NULL) + ) +); + +CREATE INDEX idx_comments_user_id ON comments(user_id); +CREATE INDEX idx_comments_product_id ON comments(product_id); +CREATE INDEX idx_comments_article_id ON comments(article_id); + +CREATE TRIGGER trg_comments_updated_at +BEFORE UPDATE ON comments +FOR EACH ROW +EXECUTE FUNCTION update_updated_at_column(); + +-- =========================================== +-- LIKES (μƒν’ˆ/κ²Œμ‹œκΈ€ μ’‹μ•„μš”) +-- =========================================== +CREATE TABLE likes ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + product_id INT, + article_id INT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + CONSTRAINT fk_likes_user FOREIGN KEY (user_id) + REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT fk_likes_product FOREIGN KEY (product_id) + REFERENCES products(id) ON DELETE CASCADE, + CONSTRAINT fk_likes_article FOREIGN KEY (article_id) + REFERENCES articles(id) ON DELETE CASCADE, + CONSTRAINT uq_like_product UNIQUE (user_id, product_id), + CONSTRAINT uq_like_article UNIQUE (user_id, article_id) +); + +CREATE INDEX idx_likes_user_id ON likes(user_id); + +CREATE TRIGGER trg_likes_updated_at +BEFORE UPDATE ON likes +FOR EACH ROW +EXECUTE FUNCTION update_updated_at_column(); + +-- =========================================== +-- FAVORITES +-- =========================================== +CREATE TABLE favorites ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + product_id INT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + CONSTRAINT fk_favorites_user FOREIGN KEY (user_id) + REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT fk_favorites_product FOREIGN KEY (product_id) + REFERENCES products(id) ON DELETE CASCADE, + CONSTRAINT uq_favorites UNIQUE (user_id, product_id) +); + +CREATE INDEX idx_favorites_user_id ON favorites(user_id); +CREATE INDEX idx_favorites_product_id ON favorites(product_id); + +CREATE TRIGGER trg_favorites_updated_at +BEFORE UPDATE ON favorites +FOR EACH ROW +EXECUTE FUNCTION update_updated_at_column(); + +-- =========================================== +-- TAGS / PRODUCT_TAGS +-- =========================================== +CREATE TABLE tags ( + id SERIAL PRIMARY KEY, + name VARCHAR(50) UNIQUE NOT NULL +); + +CREATE TABLE product_tags ( + id SERIAL PRIMARY KEY, + product_id INT NOT NULL, + tag_id INT NOT NULL, + CONSTRAINT fk_product_tags_product FOREIGN KEY (product_id) + REFERENCES products(id) ON DELETE CASCADE, + CONSTRAINT fk_product_tags_tag FOREIGN KEY (tag_id) + REFERENCES tags(id) ON DELETE CASCADE, + CONSTRAINT uq_product_tags UNIQUE (product_id, tag_id) +); + +CREATE INDEX idx_product_tags_product_id ON product_tags(product_id); +CREATE INDEX idx_product_tags_tag_id ON product_tags(tag_id);