diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4353022..afaa8bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,65 @@
# SQL2Excel Version History
-## v1.3.3 - Docs Sync & Version Bump (2025-10-31)
+## v2.1.5 - DynamicVar DB Routing & XML Validation Update (2025-11-15)
+
+### โจ New/Changed
+- Dynamic variable DB routing
+ - XML `dynamicVar` supports `db` (alias of `database`) attribute
+ - Each dynamic variable is executed using the adapter for its specified DB key
+ - Fallback to default DB when not specified
+- XML validation update
+ - `queryDef` now allows `db` attribute during structure validation (for documentation/future use). Execution DB remains sheet-level `db` or global default
+
+### ๐ง Code Changes
+- `src/query-parser.js`
+ - Allow `db` attribute on `dynamicVar`; parse `database || db`
+ - Allow `db` attribute on `queryDef` in validation
+- `src/variable-processor.js`
+ - Execute dynamic variables on their own DB adapters (`dbAdapters[targetDbKey]`)
+- `src/index.js`
+ - Pass `dbAdapters` and `defaultDbKey` to dynamic variable processor
+
+### ๐ Documentation
+- README/README_KR: Added v2.1.5 highlights, dynamicVar `db`/`database` usage notes and examples
+- USER_MANUAL/USER_MANUAL_KR: Documented dynamicVar attributes and per-variable DB routing
+- CHANGELOG/CHANGELOG_KR: Added v2.1.5 entries
+
+## v2.1.4 - DB Adapter Test Query & Schema Alignment (2025-11-08)
+
+### โจ New/Changed
+- Adapter-level connection test SQL
+ - Added `getTestQuery()` to all DB adapters
+ - MSSQL: `SELECT 1 as test`
+ - MySQL/MariaDB: `SELECT 1 as test`
+ - PostgreSQL: `SELECT 1`
+ - SQLite: `SELECT 1`
+ - Oracle: `SELECT 1 FROM dual`
+ - `excel-cli.js` uses `adapter.getTestQuery()` for connection validation
+
+- Sample schema alignment for cross-DB consistency (Orders)
+ - PostgreSQL: added `SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID`
+ - MySQL: added `SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID`
+ - Purpose: match sample data columns and improve parity with MSSQL schema
+
+### ๐ Fixes
+- Oracle connection validation fixed during `list-dbs`/validation flows
+ - Replaced hardcoded `SELECT 1 as test` with adapter-provided query
+- `excel-cli.js`: fixed broken `catch` in `loadDatabaseConfig()` and improved error message (`configFileLoadFailed`)
+
+### ๐ง Code Changes
+- `src/database/OracleAdapter.js`: add `getTestQuery()`
+- `src/database/MSSQLAdapter.js`: add `getTestQuery()`
+- `src/database/MySQLAdapter.js`: add `getTestQuery()`
+- `src/database/PostgreSQLAdapter.js`: add `getTestQuery()`
+- `src/database/SQLiteAdapter.js`: add `getTestQuery()`
+- `src/excel-cli.js`: use `adapter.getTestQuery()`; fix `loadDatabaseConfig()` catch block
+- `resources/create_sample_tables_postgresql.sql`: add Orders columns (`SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID`)
+- `resources/create_sample_tables_mysql.sql`: add Orders columns (`SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID`)
+
+### ๐ Notes
+- These changes ensure sample data (PostgreSQL) loads cleanly across DBs when schemas are applied accordingly.
+
+## v2.1.4(v1.3.3) - Docs Sync & Version Bump (2025-10-31)
### โจ New/Changed
- Added `exceptColumns` attribute to exclude specific columns from sheet output
@@ -18,7 +77,7 @@
### ๐ Documentation
- README/README_KR, USER_MANUAL/USER_MANUAL_KR, CHANGELOG/CHANGELOG_KR updated accordingly
-## v1.3.2 - CSV/TXT Formatting & Directory Naming (2025-10-31)
+## v2.1.2(v1.3.2) - CSV/TXT Formatting & Directory Naming (2025-10-31)
### โจ New/Changed
- Per-sheet export directory naming simplified
@@ -43,7 +102,7 @@
- USER_MANUAL/USER_MANUAL_KR: Updated per-sheet export section to reflect new directory naming and formatting rules
- CHANGELOG/CHANGELOG_KR: Added v1.3.2 entry
-## v1.3.1 - Filename Variables and DATE Fixes (2025-10-30)
+## v2.1.1-beta (v1.3.1) - Filename Variables and DATE Fixes (2025-10-30)
### โจ New/Changed
- Output filename variable enhancements
@@ -66,7 +125,7 @@
- USER_MANUAL/USER_MANUAL_KR: Documented filename variables (`DB_NAME`, DATE), lowercase tokens, and local-time behavior
- CHANGELOG/CHANGELOG_KR: Added v1.3.1 entry
-## v1.3.0 - Per-sheet Export for CSV/TXT and Routing Rules (2025-10-29)
+## v2.1.0-beta (v1.3.0) - Per-sheet Export for CSV/TXT and Routing Rules (2025-10-29)
### โจ New/Changed
- Export routing based on output extension
@@ -93,7 +152,7 @@
- USER_MANUAL/USER_MANUAL_KR: Added section describing routing, directory/filename rules, and defaults
- CHANGELOG/CHANGELOG_KR: Added v1.3.0 entry
-## v1.2.11 - TOC Original Name & Sheet Name Length Warning (2025-10-29)
+## v2.0.2-beta (v1.2.11) - TOC Original Name & Sheet Name Length Warning (2025-10-29)
### โจ New/Changed
- Sheet name length > 31 characters is now treated as a warning during validation (no failure)
@@ -111,7 +170,7 @@
- README/README_KR: Updated highlights to v1.2.11, described changes
- CHANGELOG: Added v1.2.11 entry
-## v1.2.10 - Non-interactive CLI & Docs (2025-10-29)
+## v2.0.1-beta (v1.2.10) - Non-interactive CLI & Docs (2025-10-29)
### โจ New Features
@@ -124,6 +183,95 @@
- README.md / README_KR.md: Added "Non-interactive CLI" usage and examples
- Updated highlights to v1.2.10
+## v2.0.0-beta - Multi-Database Support (2025-10-22)
+
+### โจ New Features
+- **Multi-Database Support**: Support for multiple database types beyond MSSQL
+ - **Supported Databases**: MSSQL, MySQL, MariaDB
+ - **Unified Interface**: Consistent API across all database types
+ - **Database Factory Pattern**: Automatic adapter selection based on database type
+ - **Backward Compatibility**: Existing MSSQL configurations work without changes
+
+### ๐ง Configuration
+```json
+{
+ "sampleDB": {
+ "type": "mssql", // Optional, defaults to "mssql" if not specified
+ "server": "localhost",
+ "port": 1433,
+ "database": "SampleDB",
+ "user": "sa",
+ "password": "password"
+ },
+ "mysqlDB": {
+ "type": "mysql", // New: MySQL support
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password"
+ },
+ "mariaDB": {
+ "type": "mariadb", // New: MariaDB support
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password"
+ }
+}
+```
+
+### ๐ฆ Technical Changes
+- **New Architecture**:
+ - `src/database/DatabaseFactory.js`: Factory for creating database adapters
+ - `src/database/MSSQLAdapter.js`: MSSQL implementation (refactored from mssql-helper.js)
+ - `src/database/MySQLAdapter.js`: MySQL/MariaDB implementation
+
+- **Updated Files**:
+ - `src/index.js`: Uses DatabaseFactory instead of direct MSSQLHelper
+ - `src/variable-processor.js`: Database-agnostic implementation
+ - `package.json`: Added `mysql2` dependency
+
+### ๐ Database-Specific Features
+- **MSSQL**:
+ - Uses `TOP N` for row limiting
+ - Supports `GETDATE()` function
+
+- **MySQL/MariaDB**:
+ - Uses `LIMIT N` for row limiting
+ - Converts `GETDATE()` to `NOW()` automatically
+ - Connection pooling with automatic reconnection
+
+### ๐ก Usage Examples
+```xml
+
+
+
+ SELECT * FROM users
+
+
+
+ SELECT * FROM orders
+
+
+
+ SELECT * FROM products
+
+
+```
+
+### โ ๏ธ Notes
+- **Type Field**: The `type` field in database configuration is optional. If omitted, defaults to `mssql` for backward compatibility.
+- **Port Numbers**: Default ports are used if not specified (MSSQL: 1433, MySQL/MariaDB: 3306)
+- **Connection Pooling**: All database types use connection pooling for optimal performance
+
+### ๐ Dependencies
+- Added: `mysql2@^3.6.0` - MySQL/MariaDB driver with promise support
+- Maintained: `mssql@^10.0.0` - Microsoft SQL Server driver
+
+---
+
## v1.2.9 - Global Timezone System & Local Time Support (2025-10-21)
### โจ New Features
diff --git a/CHANGELOG_KR.md b/CHANGELOG_KR.md
index f151491..1a932ce 100644
--- a/CHANGELOG_KR.md
+++ b/CHANGELOG_KR.md
@@ -1,6 +1,65 @@
# SQL2Excel ๋ฒ์ ํ์คํ ๋ฆฌ
-## v1.3.3 - ๋ฌธ์ ๋๊ธฐํ ๋ฐ ๋ฒ์ ์ฌ๋ฆผ (2025-10-31)
+## v2.1.5 - ๋์ ๋ณ์ DB ๋ผ์ฐํ
& XML ๊ฒ์ฆ ์
๋ฐ์ดํธ (2025-11-15)
+
+### โจ ๋ณ๊ฒฝ ์ฌํญ
+- ๋์ ๋ณ์ DB ๋ผ์ฐํ
+ - XML `dynamicVar`์์ `db` ์์ฑ ์ง์ (`database`์ ๋ณ์นญ)
+ - ๊ฐ ๋์ ๋ณ์๋ ์ง์ ๋ DB ํค์ ์ด๋ํฐ๋ก ์คํ
+ - ๋ฏธ์ง์ ์ ์ ์ญ ๊ธฐ๋ณธ DB๋ก ํด๋ฐฑ
+- XML ๊ฒ์ฆ ์
๋ฐ์ดํธ
+ - XML ๊ตฌ์กฐ ๊ฒ์ฆ์์ `queryDef`์ `db` ์์ฑ์ ํ์ฉ (๋ฌธ์/ํฅํ ํ์ฅ์ฉ). ์ค์ ์คํ DB๋ ์ํธ์ `db` ๋๋ ์ ์ญ ๊ธฐ๋ณธ DB๊ฐ ์ ์ฉ๋จ
+
+### ๐ง ์ฝ๋ ๋ณ๊ฒฝ
+- `src/query-parser.js`
+ - `dynamicVar`์ `db` ์์ฑ ํ์ฉ; ํ์ฑ ์ `database || db` ์ฒ๋ฆฌ
+ - XML ๊ฒ์ฆ์์ `queryDef`์ `db` ์์ฑ ํ์ฉ
+- `src/variable-processor.js`
+ - ๋์ ๋ณ์๋ฅผ ํด๋น DB ์ด๋ํฐ๋ก ์คํ (`dbAdapters[targetDbKey]`)
+- `src/index.js`
+ - ๋์ ๋ณ์ ์ฒ๋ฆฌ์ `dbAdapters`์ `defaultDbKey` ์ ๋ฌ
+
+### ๐ ๋ฌธ์
+- README/README_KR: v2.1.5 ํ์ด๋ผ์ดํธ, `dynamicVar`์ `db`/`database` ์์ฑ ์ฌ์ฉ ๋
ธํธ ๋ฐ ์์ ์ถ๊ฐ
+- USER_MANUAL/USER_MANUAL_KR: ๋์ ๋ณ์ ์์ฑ ๋ฐ ๋ณ์๋ณ DB ๋ผ์ฐํ
๋์ ๋ฌธ์ํ
+- CHANGELOG/CHANGELOG_KR: v2.1.5 ํญ๋ชฉ ์ถ๊ฐ
+
+## v2.1.4 - DB ์ด๋ํฐ ํ
์คํธ ์ฟผ๋ฆฌ ๋์
๋ฐ ์คํค๋ง ์ ํฉ์ฑ (2025-11-08)
+
+### โจ ๋ณ๊ฒฝ ์ฌํญ
+- ์ด๋ํฐ ๋จ์ ์ฐ๊ฒฐ ํ
์คํธ SQL ๋์
+ - ๋ชจ๋ DB ์ด๋ํฐ์ `getTestQuery()` ์ถ๊ฐ
+ - MSSQL: `SELECT 1 as test`
+ - MySQL/MariaDB: `SELECT 1 as test`
+ - PostgreSQL: `SELECT 1`
+ - SQLite: `SELECT 1`
+ - Oracle: `SELECT 1 FROM dual`
+ - `excel-cli.js`๋ ์ฐ๊ฒฐ ๊ฒ์ฆ ์ ์ด๋ํฐ์ `getTestQuery()`๋ฅผ ์ฌ์ฉ
+
+- ์ํ ์คํค๋ง ์ ํฉ์ฑ(Orders)
+ - PostgreSQL: `SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID` ์ถ๊ฐ
+ - MySQL: `SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID` ์ถ๊ฐ
+ - ๋ชฉ์ : ์ํ ๋ฐ์ดํฐ ์ปฌ๋ผ๊ณผ ์ผ์น ๋ฐ MSSQL ์คํค๋ง์์ ์ ํฉ์ฑ ํฅ์
+
+### ๐ ๋ฒ๊ทธ ์์
+- Oracle ์ฐ๊ฒฐ ๊ฒ์ฆ ์ค๋ฅ ์์ (`list-dbs`/๊ฒ์ฆ ํ๋ก์ฐ)
+ - ํ๋์ฝ๋ฉ๋ `SELECT 1 as test` โ ์ด๋ํฐ ์ ๊ณต ํ
์คํธ ์ฟผ๋ฆฌ๋ก ๋์ฒด
+- `excel-cli.js`: `loadDatabaseConfig()`์ ๊นจ์ง `catch` ๋ธ๋ก ์์ ๋ฐ ์ค๋ฅ ๋ฉ์์ง ๊ฐ์ (`configFileLoadFailed`)
+
+### ๐ง ์ฝ๋ ๋ณ๊ฒฝ
+- `src/database/OracleAdapter.js`: `getTestQuery()` ์ถ๊ฐ
+- `src/database/MSSQLAdapter.js`: `getTestQuery()` ์ถ๊ฐ
+- `src/database/MySQLAdapter.js`: `getTestQuery()` ์ถ๊ฐ
+- `src/database/PostgreSQLAdapter.js`: `getTestQuery()` ์ถ๊ฐ
+- `src/database/SQLiteAdapter.js`: `getTestQuery()` ์ถ๊ฐ
+- `src/excel-cli.js`: ์ด๋ํฐ์ ํ
์คํธ ์ฟผ๋ฆฌ ์ฌ์ฉ; `loadDatabaseConfig()` catch ๋ธ๋ก ์์
+- `resources/create_sample_tables_postgresql.sql`: Orders ์ปฌ๋ผ ์ถ๊ฐ (`SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID`)
+- `resources/create_sample_tables_mysql.sql`: Orders ์ปฌ๋ผ ์ถ๊ฐ (`SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID`)
+
+### ๐ ๋น๊ณ
+- ๋ณธ ๋ณ๊ฒฝ์ผ๋ก ์ํ ๋ฐ์ดํฐ(PostgreSQL)๊ฐ ๊ฐ DB ์คํค๋ง ์ ์ฉ ์ ์ํํ ์ ์ฌ๋๋๋ก ์ ํฉ์ฑ์ด ๊ฐ์ ๋์์ต๋๋ค.
+
+## v2.1.3-beta (v1.3.3) - ๋ฌธ์ ๋๊ธฐํ ๋ฐ ๋ฒ์ ์ฌ๋ฆผ (2025-10-31)
### โจ ๋ณ๊ฒฝ ์ฌํญ
- ์ํธ์์ ํน์ ์ปฌ๋ผ์ ๊ฒฐ๊ณผ์์ ์ ์ธํ๋ `exceptColumns` ์์ฑ ์ถ๊ฐ
@@ -23,7 +82,7 @@
- KR/EN ๋ฌธ์(README, USER_MANUAL, CHANGELOG) ๋๊ธฐํ
- ํจํค์ง ๋ฒ์ ์ 1.3.3์ผ๋ก ์
๋ฐ์ดํธ
-## v1.3.1 - ํ์ผ๋ช
๋ณ์ ๋ฐ DATE ๊ฐ์ (2025-10-30)
+## v2.1.1-beta (v1.3.1) - ํ์ผ๋ช
๋ณ์ ๋ฐ DATE ๊ฐ์ (2025-10-30)
### โจ ๋ณ๊ฒฝ ์ฌํญ
- ์ถ๋ ฅ ํ์ผ๋ช
๋ณ์ ๊ธฐ๋ฅ ๊ฐํ
@@ -46,7 +105,7 @@
- USER_MANUAL/USER_MANUAL_KR: ํ์ผ๋ช
๋ณ์(`DB_NAME`, DATE), ์๋ฌธ์ ํ ํฐ, ๋ก์ปฌ ์๊ฐ ๋์ ์ค๋ช
์ถ๊ฐ
- CHANGELOG/CHANGELOG_KR: v1.3.1 ํญ๋ชฉ ์ถ๊ฐ
-## v1.3.0 - CSV/TXT ์ํธ๋ณ ๋ด๋ณด๋ด๊ธฐ ๋ฐ ๋ผ์ฐํ
๊ท์น (2025-10-29)
+## v2.1.0-beta (v1.3.0) - CSV/TXT ์ํธ๋ณ ๋ด๋ณด๋ด๊ธฐ ๋ฐ ๋ผ์ฐํ
๊ท์น (2025-10-29)
### โจ ๋ณ๊ฒฝ ์ฌํญ
- ์ถ๋ ฅ ํ์ฅ์์ ๋ฐ๋ฅธ ๋ผ์ฐํ
@@ -73,7 +132,7 @@
- USER_MANUAL/USER_MANUAL_KR: ๋ผ์ฐํ
, ๋๋ ํ ๋ฆฌ/ํ์ผ๋ช
๊ท์น, ๊ธฐ๋ณธ๊ฐ ์น์
์ถ๊ฐ
- CHANGELOG/CHANGELOG_KR: v1.3.0 ํญ๋ชฉ ์ถ๊ฐ
-## v1.2.11 - TOC ์๋ณธ ์ํธ๋ช
์ปฌ๋ผ ๋ฐ ์ํธ๋ช
๊ธธ์ด ๊ฒฝ๊ณ (2025-10-29)
+## v2.0.2-beta (v1.2.11) - TOC ์๋ณธ ์ํธ๋ช
์ปฌ๋ผ ๋ฐ ์ํธ๋ช
๊ธธ์ด ๊ฒฝ๊ณ (2025-10-29)
### โจ ๋ณ๊ฒฝ ์ฌํญ
- ์ํธ๋ช
๊ธธ์ด 31์ ์ด๊ณผ ์ ์ด์ ๊ฒ์ฆ์์ ์ค๋ฅ๊ฐ ์๋ ๊ฒฝ๊ณ ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
@@ -91,7 +150,7 @@
- README/README_KR: v1.2.11 ํ์ด๋ผ์ดํธ ๋ฐ ๋ณ๊ฒฝ์ ๋ฐ์
- CHANGELOG/CHANGELOG_KR: v1.2.11 ํญ๋ชฉ ์ถ๊ฐ
-## v1.2.10 - ๋น๋ํ์ CLI ๋ฐ ๋ฌธ์ ์
๋ฐ์ดํธ (2025-10-29)
+## v2.0.1-beta (v1.2.10) - ๋น๋ํ์ CLI ๋ฐ ๋ฌธ์ ์
๋ฐ์ดํธ (2025-10-29)
### โจ ์๋ก์ด ๊ธฐ๋ฅ
@@ -102,7 +161,96 @@
### ๐ ๋ฌธ์
- README.md / README_KR.md: "๋น๋ํ์ CLI" ์ฌ์ฉ๋ฒ๊ณผ ์์ ์ถ๊ฐ
-- ํ์ด๋ผ์ดํธ๋ฅผ v1.3.0์ผ๋ก ์
๋ฐ์ดํธ
+
+
+## v2.0.0-beta - ๋ค์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ง์ (2025-10-22)
+
+### โจ ์๋ก์ด ๊ธฐ๋ฅ
+- **๋ค์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ง์**: MSSQL ์ธ ๋ค์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
์ง์
+ - **์ง์ ๋ฐ์ดํฐ๋ฒ ์ด์ค**: MSSQL, MySQL, MariaDB
+ - **ํตํฉ ์ธํฐํ์ด์ค**: ๋ชจ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
์ ๋ํ ์ผ๊ด๋ API
+ - **๋ฐ์ดํฐ๋ฒ ์ด์ค ํฉํ ๋ฆฌ ํจํด**: ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
์ ๋ฐ๋ผ ์๋์ผ๋ก ์ ์ ํ ์ด๋ํฐ ์ ํ
+ - **ํ์ ํธํ์ฑ**: ๊ธฐ์กด MSSQL ์ค์ ์ ๋ณ๊ฒฝ ์์ด ๊ทธ๋๋ก ์๋
+
+### ๐ง ์ค์
+```json
+{
+ "sampleDB": {
+ "type": "mssql", // ์ ํ์ฌํญ, ์ง์ ํ์ง ์์ผ๋ฉด "mssql" ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ
+ "server": "localhost",
+ "port": 1433,
+ "database": "SampleDB",
+ "user": "sa",
+ "password": "password"
+ },
+ "mysqlDB": {
+ "type": "mysql", // ์ ๊ท: MySQL ์ง์
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password"
+ },
+ "mariaDB": {
+ "type": "mariadb", // ์ ๊ท: MariaDB ์ง์
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password"
+ }
+}
+```
+
+### ๐ฆ ๊ธฐ์ ์ ๋ณ๊ฒฝ์ฌํญ
+- **์๋ก์ด ์ํคํ
์ฒ**:
+ - `src/database/DatabaseFactory.js`: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๋ํฐ ์์ฑ ํฉํ ๋ฆฌ
+ - `src/database/MSSQLAdapter.js`: MSSQL ๊ตฌํ (mssql-helper.js์์ ๋ฆฌํฉํ ๋ง)
+ - `src/database/MySQLAdapter.js`: MySQL/MariaDB ๊ตฌํ
+
+- **์
๋ฐ์ดํธ๋ ํ์ผ**:
+ - `src/index.js`: MSSQLHelper ๋์ DatabaseFactory ์ฌ์ฉ
+ - `src/variable-processor.js`: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋
๋ฆฝ์ ์ธ ๊ตฌํ
+ - `package.json`: `mysql2` ์์กด์ฑ ์ถ๊ฐ
+
+### ๐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ณ ํน์ง
+- **MSSQL**:
+ - ํ ์ ํ์ `TOP N` ์ฌ์ฉ
+ - `GETDATE()` ํจ์ ์ง์
+
+- **MySQL/MariaDB**:
+ - ํ ์ ํ์ `LIMIT N` ์ฌ์ฉ
+ - `GETDATE()`๋ฅผ `NOW()`๋ก ์๋ ๋ณํ
+ - ์๋ ์ฌ์ฐ๊ฒฐ ๊ธฐ๋ฅ์ด ์๋ ์ฐ๊ฒฐ ํ๋ง
+
+### ๐ก ์ฌ์ฉ ์์
+```xml
+
+
+
+ SELECT * FROM users
+
+
+
+ SELECT * FROM orders
+
+
+
+ SELECT * FROM products
+
+
+```
+
+### โ ๏ธ ์ฐธ๊ณ ์ฌํญ
+- **Type ํ๋**: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์ ์ `type` ํ๋๋ ์ ํ์ฌํญ์
๋๋ค. ์๋ตํ๋ฉด ํ์ ํธํ์ฑ์ ์ํด `mssql`์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ฌ์ฉํฉ๋๋ค.
+- **ํฌํธ ๋ฒํธ**: ์ง์ ํ์ง ์์ผ๋ฉด ๊ธฐ๋ณธ ํฌํธ๋ฅผ ์ฌ์ฉํฉ๋๋ค (MSSQL: 1433, MySQL/MariaDB: 3306)
+- **์ฐ๊ฒฐ ํ๋ง**: ๋ชจ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
์ ์ต์ ์ ์ฑ๋ฅ์ ์ํด ์ฐ๊ฒฐ ํ๋ง์ ์ฌ์ฉํฉ๋๋ค
+
+### ๐ ์์กด์ฑ
+- ์ถ๊ฐ: `mysql2@^3.6.0` - Promise ์ง์์ด ํฌํจ๋ MySQL/MariaDB ๋๋ผ์ด๋ฒ
+- ์ ์ง: `mssql@^10.0.0` - Microsoft SQL Server ๋๋ผ์ด๋ฒ
+
+---
## v1.2.9 - ๊ธ๋ก๋ฒ ํ์์กด ์์คํ
๋ฐ ๋ก์ปฌ ์๊ฐ ์ง์ (2025-10-21)
diff --git a/README.md b/README.md
index 30029d9..2c40aea 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,25 @@
A Node.js-based tool for generating Excel files from SQL query results.
-## v1.3.3 Highlights
+## v2.1.5 Highlights
+
+- DynamicVar DB routing
+ - `dynamicVar` now supports `db` (alias of `database`) attribute in XML.
+ - Each dynamic variable executes on its specified DB adapter (falls back to default DB when omitted).
+- XML validation update
+ - `queryDef` now allows `db` attribute in XML schema validation. Note: current execution still uses sheet-level `db` or global default; `queryDef.db` is for future use/documentation.
+
+## v2.1.4 Highlights
+
+- Adapter-level DB connection test queries
+ - Added `getTestQuery()` to all DB adapters
+ - MSSQL: `SELECT 1 as test`, MySQL/MariaDB: `SELECT 1 as test`, PostgreSQL: `SELECT 1`, SQLite: `SELECT 1`, Oracle: `SELECT 1 FROM dual`
+ - `excel-cli.js` now uses the adapterโs test query for connection validation (fixes Oracle validation)
+- Sample schema alignment (Orders)
+ - PostgreSQL/MySQL: added `SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID`
+ - Aligns with sample data and improves parity with MSSQL schema
+
+## v2.1.3-beta (v1.3.3) Highlights
- Documentation synchronization (KR/EN) and minor updates
- Package version updated to 1.3.3
@@ -11,6 +29,7 @@ A Node.js-based tool for generating Excel files from SQL query results.
- ๐ **Multi-Sheet Support**: Save multiple SQL query results in separate sheets within one Excel file
- ๐จ **Template Style System**: Pre-defined Excel styling templates for consistent design with 7 built-in styles
- ๐ **Multiple DB Connections**: Use different database connections for each sheet
+- ๐๏ธ **Multi-Database Support (v2.0.0-beta+)**: Support for MSSQL, MySQL, and MariaDB with unified interface
- ๐ **Variable System**: Use variables in queries for dynamic query generation
- ๐ **Enhanced Dynamic Variables**: Extract values from database in real-time with advanced processing
- ๐ **Query Reuse**: Define common queries and reuse them across multiple sheets
@@ -31,7 +50,19 @@ A Node.js-based tool for generating Excel files from SQL query results.
- ๐ง **Input Validation**: Automatic whitespace trimming for file path inputs
- ๐๏ธ **Filename Variables**: Use `${DATE:...}`, `${DATE.TZ:...}`, and `${DB_NAME}` in `excel.output` (also supports custom `$(DB_NAME}`)
-## v1.3.2 Highlights
+## ๐ Multi-Database Usage
+
+- **Supported drivers**: MSSQL (`mssql`), MySQL (`mysql2`), MariaDB (`mysql2`), PostgreSQL (`pg`), SQLite (`better-sqlite3`), Oracle (`oracledb`)
+- **Configuration**: `config/dbinfo.json` with per-DB keys and `type` (optional for MSSQL default)
+- **Runtime DB selection precedence**
+ - Default DB key: `--db` (CLI) > `excel.db` (XML/JSON)
+ - Sheet: `sheet.db` > Default DB
+ - Dynamic variables: `dynamicVar.database` or `dynamicVar.db` > Default DB
+- **Connection test**
+ - Dev: `npm run list-dbs`
+ - EXE: `sql2excel.exe list-dbs`
+
+## v2.1.2(v1.3.2) Highlights
- Per-sheet export directory naming simplified
- Directory is now `` (extension suffix removed)
@@ -43,7 +74,7 @@ A Node.js-based tool for generating Excel files from SQL query results.
- Record separators remain CRLF; headers included
- Date values are serialized as `yyyy-MM-dd HH:mm:ss` (24-hour) in CSV/TXT and SQL literals
-## v1.3.1 Highlights
+## v2.1.1-beta (v1.3.1) Highlights
- Filename variables in output path
- Support `${DB_NAME}` (current DB key), custom syntax `$(DB_NAME}` normalized automatically
@@ -51,7 +82,7 @@ A Node.js-based tool for generating Excel files from SQL query results.
- Lowercase date tokens supported: `yyyy, yy, dd, d, hh, h, sss`
- Removed auto `_yyyymmddhhmmss` suffix; control naming via DATE variables
-## v1.3.0 Highlights
+## v2.1.0-beta (v1.3.0) Highlights
- **Per-sheet export routing by extension**
- `.xlsx` / `.xls` โ Generate a single Excel workbook (existing behavior)
@@ -99,6 +130,10 @@ node app.js --mode=export --query=./queries/sample-queries.json
node app.js --mode=help
```
+Notes:
+- Attributes supported on `dynamicVar`: `name`, `description`, `type`, `db`, `database` (`db` is an alias). When both are present, `database` takes precedence.
+- `queryDef` accepts `db` for validation purposes; execution DB is determined by sheet's `db` or the global default DB.
+
#### Standalone EXE
```bash
sql2excel.exe --mode=validate --xml=./queries/sample-queries.xml
@@ -115,12 +150,12 @@ sql2excel.exe --mode=help
#### For Development/Source Code Usage
- Node.js 16.0 or higher
-- SQL Server 2012 or higher
+- Database Server (MSSQL 2012+, MySQL 5.7+, or MariaDB 10.2+)
- Appropriate database permissions
#### For Standalone Executable Usage
- Windows 10 or higher (64-bit)
-- SQL Server 2012 or higher
+- Database Server (MSSQL 2012+, MySQL 5.7+, or MariaDB 10.2+)
- Appropriate database permissions
- **No Node.js installation required**
@@ -147,33 +182,47 @@ npm run build
Create `config/dbinfo.json` file:
```json
{
- "dbs": {
- "sampleDB": {
- "server": "localhost",
- "port": 1433,
- "database": "SampleDB",
- "user": "sa",
- "password": "yourpassword",
- "options": {
- "encrypt": false,
- "trustServerCertificate": true
- }
- },
- "erpDB": {
- "server": "erp-server.com",
- "port": 1433,
- "database": "ERP_Database",
- "user": "erp_user",
- "password": "erp_password",
- "options": {
- "encrypt": true,
- "trustServerCertificate": false
- }
+ "sampleDB": {
+ "type": "mssql",
+ "server": "localhost",
+ "port": 1433,
+ "database": "SampleDB",
+ "user": "sa",
+ "password": "yourpassword",
+ "options": {
+ "encrypt": false,
+ "trustServerCertificate": true
+ }
+ },
+ "mysqlDB": {
+ "type": "mysql",
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password",
+ "options": {
+ "connectionTimeout": 30000
+ }
+ },
+ "mariaDB": {
+ "type": "mariadb",
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password",
+ "options": {
+ "connectionTimeout": 30000
}
}
}
```
+**Note:**
+- `type` field is optional. If not specified, defaults to `mssql` for backward compatibility.
+- Supported types: `mssql`, `mysql`, `mariadb`
+
## ๐ Basic Usage
### Language Settings
@@ -366,7 +415,7 @@ The tool supports dynamic variables that can extract data at runtime and use it
```xml
-
+
@@ -374,7 +423,7 @@ The tool supports dynamic variables that can extract data at runtime and use it
-
+
diff --git a/README_KR.md b/README_KR.md
index 3227b09..16528b2 100644
--- a/README_KR.md
+++ b/README_KR.md
@@ -2,7 +2,25 @@
SQL ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์์
ํ์ผ๋ก ์์ฑํ๋ Node.js ๊ธฐ๋ฐ ๋๊ตฌ์
๋๋ค.
-## v1.3.3 ํ์ด๋ผ์ดํธ
+## v2.1.5 ํ์ด๋ผ์ดํธ
+
+- ๋์ ๋ณ์ DB ๋ผ์ฐํ
+ - XML์ `dynamicVar`์์ `db`(= `database` ๋ณ์นญ) ์์ฑ ์ง์.
+ - ๊ฐ ๋์ ๋ณ์๋ ์ง์ ํ DB ์ด๋ํฐ์์ ์คํ๋๋ฉฐ, ๋ฏธ์ง์ ์ ๊ธฐ๋ณธ DB ์ฌ์ฉ.
+- XML ๊ฒ์ฆ ์
๋ฐ์ดํธ
+ - XML ์คํค๋ง ๊ฒ์ฆ์์ `queryDef`์ `db` ์์ฑ์ ํ์ฉ. ์ฃผ์: ํ์ฌ ์คํ DB๋ ์ํธ์ `db` ๋๋ ์ ์ญ ๊ธฐ๋ณธ DB๊ฐ ์ฌ์ฉ๋๋ฉฐ, `queryDef.db`๋ ํฅํ ๋ฌธ์/ํ์ฅ์ฉ.
+
+## v2.1.4 ํ์ด๋ผ์ดํธ
+
+- ์ด๋ํฐ๋ณ DB ์ฐ๊ฒฐ ํ
์คํธ ์ฟผ๋ฆฌ ๋์
+ - ๋ชจ๋ DB ์ด๋ํฐ์ `getTestQuery()` ์ถ๊ฐ
+ - MSSQL/MySQL/MariaDB: `SELECT 1 as test`, PostgreSQL/SQLite: `SELECT 1`, Oracle: `SELECT 1 FROM dual`
+ - `excel-cli.js`๊ฐ ์ด๋ํฐ์ ํ
์คํธ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ฐ๊ฒฐ ๊ฒ์ฆ ์ํ (Oracle ๊ฒ์ฆ ์ด์ ํด๊ฒฐ)
+- ์ํ ์คํค๋ง ์ ํฉ์ฑ(Orders)
+ - PostgreSQL/MySQL: `SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID` ์ถ๊ฐ
+ - ์ํ ๋ฐ์ดํฐ์ ์ปฌ๋ผ ์ผ์น, MSSQL ์คํค๋ง์์ ์ ํฉ์ฑ ํฅ์
+
+## v2.1.3-beta (v1.3.3) ํ์ด๋ผ์ดํธ
- ๋ฌธ์ ๋๊ธฐํ(KR/EN) ๋ฐ ์๊ท๋ชจ ์ ๋ฆฌ
- ํจํค์ง ๋ฒ์ ์ 1.3.3์ผ๋ก ์
๋ฐ์ดํธ
@@ -11,6 +29,7 @@ SQL ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์์
ํ์ผ๋ก ์์ฑํ๋ Node.js ๊ธฐ๋ฐ ๋๊ตฌ์
๋
- ๐ **๋ค์ค ์ํธ ์ง์**: ํ๋์ ์์
ํ์ผ ๋ด์์ ์ฌ๋ฌ SQL ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ณ๋์ ์ํธ์ ์ ์ฅ
- ๐จ **ํ
ํ๋ฆฟ ์คํ์ผ ์์คํ
**: ์ผ๊ด๋ ๋์์ธ์ ์ํ ์ฌ์ ์ ์๋ ์์
์คํ์ผ๋ง ํ
ํ๋ฆฟ (7๊ฐ์ง ๋ด์ฅ ์คํ์ผ)
- ๐ **๋ค์ค DB ์ฐ๊ฒฐ**: ๊ฐ ์ํธ๋ง๋ค ๋ค๋ฅธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ฌ์ฉ ๊ฐ๋ฅ
+- ๐๏ธ **๋ค์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ง์ (v2.0.0-beta+)**: MSSQL, MySQL, MariaDB๋ฅผ ํตํฉ ์ธํฐํ์ด์ค๋ก ์ง์
- ๐ **๋ณ์ ์์คํ
**: ๋์ ์ฟผ๋ฆฌ ์์ฑ์ ์ํ ๋ณ์ ์ฌ์ฉ
- ๐ **ํฅ์๋ ๋์ ๋ณ์**: ์ค์๊ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ์ถ์ถํ์ฌ ๊ณ ๊ธ ์ฒ๋ฆฌ
- ๐ **์ฟผ๋ฆฌ ์ฌ์ฌ์ฉ**: ๊ณตํต ์ฟผ๋ฆฌ๋ฅผ ์ ์ํ๊ณ ์ฌ๋ฌ ์ํธ์์ ์ฌ์ฌ์ฉ
@@ -31,7 +50,19 @@ SQL ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์์
ํ์ผ๋ก ์์ฑํ๋ Node.js ๊ธฐ๋ฐ ๋๊ตฌ์
๋
- ๐ง **์
๋ ฅ ์ ํจ์ฑ ๊ฒ์ฆ**: ํ์ผ ๊ฒฝ๋ก ์
๋ ฅ์ ๋ํ ์๋ ๊ณต๋ฐฑ ์ ๊ฑฐ
- ๐๏ธ **ํ์ผ๋ช
๋ณ์**: `excel.output`์์ `${DATE:...}`, `${DATE.TZ:...}`, `${DB_NAME}` ์ฌ์ฉ ๊ฐ๋ฅ (์ปค์คํ
`$(DB_NAME}`๋ ์ง์)
-## v1.3.2 ํ์ด๋ผ์ดํธ
+## ๐ ๋ค์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ฉ
+
+- **์ง์ ๋๋ผ์ด๋ฒ**: MSSQL(`mssql`), MySQL(`mysql2`), MariaDB(`mysql2`), PostgreSQL(`pg`), SQLite(`better-sqlite3`), Oracle(`oracledb`)
+- **์ค์ ํ์ผ**: `config/dbinfo.json`์ DB ํค๋ณ ์ ์ ์ ๋ณด์ `type` ์ง์ (MSSQL์ ์๋ต ์ ๊ธฐ๋ณธ๊ฐ)
+- **๋ฐํ์ DB ์ ํ ์ฐ์ ์์**
+ - ๊ธฐ๋ณธ DB ํค: `--db`(CLI) > `excel.db`(XML/JSON)
+ - ์ํธ: `sheet.db` > ๊ธฐ๋ณธ DB
+ - ๋์ ๋ณ์: `dynamicVar.database` ๋๋ `dynamicVar.db` > ๊ธฐ๋ณธ DB
+- **์ฐ๊ฒฐ ํ
์คํธ**
+ - ๊ฐ๋ฐ: `npm run list-dbs`
+ - EXE: `sql2excel.exe list-dbs`
+
+## v2.1.2(v1.3.2) ํ์ด๋ผ์ดํธ
- ์ํธ๋ณ ๋ด๋ณด๋ด๊ธฐ ๋๋ ํ ๋ฆฌ ๋ช
๋ช
๋จ์ํ
- ๋๋ ํ ๋ฆฌ๋ ์ด์ `<์ถ๋ ฅํ์ผ๋ฒ ์ด์ค>` (ํ์ฅ์ ์ ๋ฏธ์ฌ ์ ๊ฑฐ)
@@ -43,7 +74,7 @@ SQL ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์์
ํ์ผ๋ก ์์ฑํ๋ Node.js ๊ธฐ๋ฐ ๋๊ตฌ์
๋
- ๋ ์ฝ๋ ๊ตฌ๋ถ ๊ฐํ์ CRLF ์ ์ง, ํค๋ ํฌํจ
- ๋ ์ง ๊ฐ์ CSV/TXT ๋ฐ SQL ๋ฆฌํฐ๋ด์์ `yyyy-MM-dd HH:mm:ss`(24์๊ฐ) ํ์์ผ๋ก ์ง๋ ฌํ
-## v1.3.1 ํ์ด๋ผ์ดํธ
+## v2.1.1-beta (v1.3.1) ํ์ด๋ผ์ดํธ
- ์ถ๋ ฅ ๊ฒฝ๋ก์์ ํ์ผ๋ช
๋ณ์ ์ง์ ๊ฐํ
- `${DB_NAME}` ์ง์ (ํ์ฌ ๊ธฐ๋ณธ DB ํค). ์ปค์คํ
๋ฌธ๋ฒ `$(DB_NAME}`๋ ์๋์ผ๋ก `${DB_NAME}`๋ก ์ ๊ทํ
@@ -51,7 +82,7 @@ SQL ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์์
ํ์ผ๋ก ์์ฑํ๋ Node.js ๊ธฐ๋ฐ ๋๊ตฌ์
๋
- ์๋ฌธ์ ๋ ์ง ํ ํฐ ์ง์: `yyyy, yy, dd, d, hh, h, sss`
- ์๋ `_yyyymmddhhmmss` ์ ๋ฏธ์ฌ ์ ๊ฑฐ โ DATE ๋ณ์๋ก ์ง์ ์ ์ด
-## v1.3.0 ํ์ด๋ผ์ดํธ
+## v2.1.0-beta (v1.3.0) ํ์ด๋ผ์ดํธ
- **ํ์ฅ์ ๊ธฐ๋ฐ ์ํธ๋ณ ๋ด๋ณด๋ด๊ธฐ ๋ผ์ฐํ
**
- `.xlsx` / `.xls` โ ๋จ์ผ ์์
ํตํฉ๋ฌธ์ ์์ฑ (๊ธฐ์กด ๋์)
@@ -99,6 +130,10 @@ node app.js --mode=export --query=./queries/sample-queries.json
node app.js --mode=help
```
+์ฐธ๊ณ :
+- `dynamicVar`์์ ์ง์ํ๋ ์์ฑ: `name`, `description`, `type`, `db`, `database` (`db`๋ ๋ณ์นญ). ๋ ๋ค ์์ผ๋ฉด `database`๊ฐ ์ฐ์ ํฉ๋๋ค.
+- `queryDef`๋ ๊ฒ์ฆ ๋ชฉ์ ์ `db`๋ฅผ ํ์ฉํฉ๋๋ค. ์ค์ ์คํ DB๋ ์ํธ์ `db` ํน์ ์ ์ญ ๊ธฐ๋ณธ DB๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
+
#### ๋
๋ฆฝ ์คํ ํ์ผ(EXE)
```bash
sql2excel.exe --mode=validate --xml=./queries/sample-queries.xml
@@ -115,12 +150,12 @@ sql2excel.exe --mode=help
#### ๊ฐ๋ฐ/์์ค ์ฝ๋ ์ฌ์ฉ ์
- Node.js 16.0 ์ด์
-- SQL Server 2012 ์ด์
+- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฒ (MSSQL 2012+, MySQL 5.7+, ๋๋ MariaDB 10.2+)
- ์ ์ ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ถํ
#### ๋
๋ฆฝ ์คํ ํ์ผ ์ฌ์ฉ ์
- Windows 10 ์ด์ (64๋นํธ)
-- SQL Server 2012 ์ด์
+- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฒ (MSSQL 2012+, MySQL 5.7+, ๋๋ MariaDB 10.2+)
- ์ ์ ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ถํ
- **Node.js ์ค์น ๋ถํ์**
@@ -147,33 +182,47 @@ npm run build
`config/dbinfo.json` ํ์ผ์ ์์ฑํ์ธ์:
```json
{
- "dbs": {
- "sampleDB": {
- "server": "localhost",
- "port": 1433,
- "database": "SampleDB",
- "user": "sa",
- "password": "yourpassword",
- "options": {
- "encrypt": false,
- "trustServerCertificate": true
- }
- },
- "erpDB": {
- "server": "erp-server.com",
- "port": 1433,
- "database": "ERP_Database",
- "user": "erp_user",
- "password": "erp_password",
- "options": {
- "encrypt": true,
- "trustServerCertificate": false
- }
+ "sampleDB": {
+ "type": "mssql",
+ "server": "localhost",
+ "port": 1433,
+ "database": "SampleDB",
+ "user": "sa",
+ "password": "yourpassword",
+ "options": {
+ "encrypt": false,
+ "trustServerCertificate": true
+ }
+ },
+ "mysqlDB": {
+ "type": "mysql",
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password",
+ "options": {
+ "connectionTimeout": 30000
+ }
+ },
+ "mariaDB": {
+ "type": "mariadb",
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password",
+ "options": {
+ "connectionTimeout": 30000
}
}
}
```
+**์ฐธ๊ณ :**
+- `type` ํ๋๋ ์ ํ์ฌํญ์
๋๋ค. ์ง์ ํ์ง ์์ผ๋ฉด ํ์ ํธํ์ฑ์ ์ํด ๊ธฐ๋ณธ๊ฐ `mssql`์ ์ฌ์ฉํฉ๋๋ค.
+- ์ง์ ํ์
: `mssql`, `mysql`, `mariadb`
+
## ๐ ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
### ์ธ์ด ์ค์
@@ -366,7 +415,7 @@ node src/excel-cli.js export --xml ./queries/sales-report.xml \
```xml
-
+
@@ -374,7 +423,7 @@ node src/excel-cli.js export --xml ./queries/sales-report.xml \
-
+
diff --git a/USER_MANUAL.md b/USER_MANUAL.md
index 2a8a133..c9a0ca8 100644
--- a/USER_MANUAL.md
+++ b/USER_MANUAL.md
@@ -19,7 +19,25 @@
SQL2Excel is a powerful Node.js-based tool for generating Excel files from SQL query results with advanced styling, template support, and standalone executable distribution.
-### What's New (v1.3.3)
+### What's New (v2.1.5, v1.3.5)
+
+- DynamicVar DB routing
+ - XML `dynamicVar` supports `db` (alias of `database`) attribute
+ - Each dynamic variable runs on its specified DB adapter; falls back to default DB if omitted
+- XML validation update
+ - `queryDef` accepts `db` in schema validation (execution DB is still determined by sheet `db` or global default)
+
+### What's New (v2.1.4, v1.3.4)
+
+- Adapter-level DB connection test queries
+ - Added `getTestQuery()` to all DB adapters
+ - MSSQL: `SELECT 1 as test`, MySQL/MariaDB: `SELECT 1 as test`, PostgreSQL: `SELECT 1`, SQLite: `SELECT 1`, Oracle: `SELECT 1 FROM dual`
+ - `excel-cli.js` now uses the adapterโs test query for connection validation (fixes Oracle validation)
+- Sample schema alignment (Orders)
+ - PostgreSQL/MySQL: added `SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID`
+ - Aligns with sample data and improves parity with MSSQL schema
+
+### What's New (v2.1.3, v1.3.3)
- Added `exceptColumns` sheet option (XML/JSON) to exclude specific columns from outputs
- Documentation synchronization across KR/EN
@@ -29,6 +47,7 @@ SQL2Excel is a powerful Node.js-based tool for generating Excel files from SQL q
- ๐ **Multi-Sheet Support**: Save multiple SQL query results in separate sheets within one Excel file
- ๐จ **Template Style System**: Pre-defined Excel styling templates for consistent design with 7 built-in styles
- ๐ **Multiple DB Connections**: Use different database connections for each sheet
+- ๐๏ธ **Multi-Database Support (v2.0.0-beta+)**: Support for MSSQL, MySQL, and MariaDB with unified interface
- ๐ **Variable System**: Use variables in queries for dynamic query generation
- ๐ **Enhanced Dynamic Variables**: Extract values from database in real-time with advanced processing
- ๐ **Query Reuse**: Define common queries and reuse them across multiple sheets
@@ -44,11 +63,11 @@ SQL2Excel is a powerful Node.js-based tool for generating Excel files from SQL q
- ๐ **Multi-language Support**: Korean and English release packages
- ๐ง **Release Automation**: Automated release package generation with proper documentation
- ๐ **Creation Timestamp**: Display creation timestamp on each Excel sheet
-- โฐ **Enhanced DateTime Variables**: 20+ automatic datetime variables for real-time timestamp generation
+- โฐ **Enhanced DateTime Variables**: 22 timezones worldwide with custom format support
- ๐ **SQL Query Formatting**: Preserve original SQL formatting with line breaks in Table of Contents
- ๐ง **Input Validation**: Automatic whitespace trimming for file path inputs
-### What's New (v1.3.2)
+### What's New (v2.1.2, v1.3.2)
- Per-sheet export routing by extension
- `.xlsx` / `.xls` โ Generate a single Excel workbook (existing behavior)
@@ -72,12 +91,12 @@ Previously in v1.2.11
#### For Development/Source Code Usage
- Node.js 16.0 or higher
-- SQL Server 2012 or higher
+- Database Server (MSSQL 2012+, MySQL 5.7+, or MariaDB 10.2+)
- Appropriate database permissions
#### For Standalone Executable Usage
- Windows 10 or higher (64-bit)
-- SQL Server 2012 or higher
+- Database Server (MSSQL 2012+, MySQL 5.7+, or MariaDB 10.2+)
- Appropriate database permissions
- **No Node.js installation required**
@@ -102,26 +121,47 @@ npm run build
### 3. Database Connection Setup
Create `config/dbinfo.json` file:
+
+#### Multi-Database Support (v2.0.0-beta+)
```json
{
- "dbs": {
- "sampleDB": {
- "server": "localhost",
- "port": 1433,
- "database": "SampleDB",
- "user": "sa",
- "password": "yourpassword",
- "options": {
- "encrypt": false,
- "trustServerCertificate": true
- }
- },
- "erpDB": {
- "server": "erp-server.com",
- "port": 1433,
- "database": "ERP_Database",
- "user": "erp_user",
- "password": "erp_password",
+ "sampleDB": {
+ "type": "mssql", // Optional: "mssql" (default), "mysql", or "mariadb"
+ "server": "localhost",
+ "port": 1433,
+ "database": "SampleDB",
+ "user": "sa",
+ "password": "yourpassword",
+ "options": {
+ "encrypt": false,
+ "trustServerCertificate": true
+ }
+ },
+ "mysqlDB": {
+ "type": "mysql", // MySQL database
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password",
+ "options": {
+ "connectionTimeout": 30000
+ }
+ },
+ "mariaDB": {
+ "type": "mariadb", // MariaDB database
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password"
+ },
+ "erpDB": {
+ "server": "erp-server.com",
+ "port": 1433,
+ "database": "ERP_Database",
+ "user": "erp_user",
+ "password": "erp_password",
"options": {
"encrypt": true,
"trustServerCertificate": false
@@ -414,6 +454,20 @@ sql2excel.exe --mode=help
}
```
+## ๐ Multi-Database
+
+This tool supports multiple databases with unified adapters and flexible routing.
+
+- **Supported drivers**: MSSQL (`mssql`), MySQL (`mysql2`), MariaDB (`mysql2`), PostgreSQL (`pg`), SQLite (`better-sqlite3`), Oracle (`oracledb`)
+- **Configuration**: Define multiple DB keys in `config/dbinfo.json` with optional `type` (defaults to `mssql`) and connection fields. See examples above and in โMulti-Database Support (v2.0.0-beta+)โ.
+- **Runtime DB selection precedence (v2.1.5+)**
+ - Default DB key: CLI `--db` > `excel.db`
+ - Per sheet: `sheet.db` overrides default DB
+ - Dynamic variables: `dynamicVar.database` or `dynamicVar.db` overrides default DB
+- **Mixed-DB usage**: You can query different DBs within a single export. See โMulti-Database Support (v2.0.0-beta+)โ for XML/JSON examples.
+- **Connection test**: Validate connectivity before export with `node src/excel-cli.js list-dbs` (dev) or `sql2excel.exe list-dbs` (EXE).
+- **Adapter behavior**: Row limiting and functions are adapted per DB (e.g., MSSQL uses TOP, MySQL/MariaDB use LIMIT).
+
## ๐จ Template Style System
SQL2Excel includes a comprehensive template style system with pre-defined Excel styling templates.
@@ -495,15 +549,15 @@ The tool supports advanced dynamic variables that can extract data at runtime an
#### XML Configuration
```xml
-
-
+
+
-
-
+
+
@@ -527,6 +581,10 @@ WHERE CustomerID IN (${customerData.CustomerID})
4. **Performance**: Variables are executed once and cached for the entire export
5. **Debug Mode**: Enable with `DEBUG_VARIABLES=true` for detailed variable substitution
+Notes:
+- Supported `dynamicVar` attributes: `name`, `description`, `type`, `db`, `database` (`db` is an alias). If both are present, `database` takes precedence.
+- `queryDef` accepts `db` in XML validation only; runtime execution DB is selected from the sheet's `db` or the global default DB.
+
## ๐ Custom Date/Time Variables
SQL2Excel provides a powerful custom date variable system that allows you to display current date and time in any format you want. These variables can be used in queries, file names, and any text content.
@@ -780,7 +838,137 @@ This feature works automatically - no configuration required!
## ๐จ Advanced Features
-### 1. Excel Styling
+### 1. Multi-Database Support (v2.0.0-beta+)
+
+SQL2Excel now supports multiple database types with a unified interface:
+
+#### Supported Databases
+- **MSSQL** (SQL Server 2012+)
+- **MySQL** (5.7+)
+- **MariaDB** (10.2+)
+
+#### Configuration
+Add the `type` field to your database connection in `config/dbinfo.json`:
+
+```json
+{
+ "mssqlDB": {
+ "type": "mssql", // or omit for backward compatibility
+ "server": "localhost",
+ "port": 1433,
+ "database": "SampleDB",
+ "user": "sa",
+ "password": "password",
+ "options": {
+ "encrypt": false,
+ "trustServerCertificate": true
+ }
+ },
+ "mysqlDB": {
+ "type": "mysql",
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password"
+ },
+ "mariaDB": {
+ "type": "mariadb",
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password"
+ }
+}
+```
+
+#### Mixed Database Queries
+You can query different database types in a single Excel file:
+
+**XML Example:**
+```xml
+
+
+
+
+
+
+
+
+ SELECT TOP 10 * FROM Customers
+
+
+
+
+
+
+ SELECT * FROM products LIMIT 10
+
+
+
+
+
+
+ SELECT * FROM orders
+ WHERE order_date >= '2024-01-01'
+ LIMIT 20
+
+
+
+```
+
+**JSON Example:**
+```json
+{
+ "excel": {
+ "output": "output/mixed_report.xlsx",
+ "db": "mssqlDB",
+ "style": "modern"
+ },
+ "sheets": [
+ {
+ "name": "MSSQL Data",
+ "db": "mssqlDB",
+ "query": "SELECT TOP 10 * FROM Customers"
+ },
+ {
+ "name": "MySQL Data",
+ "db": "mysqlDB",
+ "query": "SELECT * FROM products LIMIT 10"
+ },
+ {
+ "name": "MariaDB Data",
+ "db": "mariaDB",
+ "query": "SELECT * FROM orders WHERE order_date >= '2024-01-01' LIMIT 20"
+ }
+ ]
+}
+```
+
+#### Database-Specific Features
+- **MSSQL**: Supports `TOP N` clause, `GETDATE()` function
+- **MySQL/MariaDB**: Supports `LIMIT N` clause, `NOW()` function
+- **All**: Automatic syntax handling for each database type
+
+#### Testing Database Connections
+Test all configured database connections:
+```bash
+# Development
+node src/excel-cli.js list-dbs
+
+# Standalone executable
+sql2excel-v1.3.0.exe list-dbs
+```
+
+#### Runtime DB selection precedence (v2.1.5+)
+
+- Default DB key: CLI `--db` > `excel.db`
+- Per sheet: `sheet.db` overrides default DB
+- Dynamic variables: `dynamicVar.database` or `dynamicVar.db` overrides default DB
+- Tip: Use `list-dbs` (above) to verify connections before export
+
+### 2. Excel Styling
#### Font Styling
```xml
diff --git a/USER_MANUAL_KR.md b/USER_MANUAL_KR.md
index 4045368..5bfe185 100644
--- a/USER_MANUAL_KR.md
+++ b/USER_MANUAL_KR.md
@@ -19,7 +19,25 @@
SQL2Excel์ ๊ณ ๊ธ ์คํ์ผ๋ง, ํ
ํ๋ฆฟ ์ง์, ๋
๋ฆฝ ์คํ ํ์ผ ๋ฐฐํฌ ๊ธฐ๋ฅ์ ๊ฐ์ถ SQL ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ก ์์
ํ์ผ์ ์์ฑํ๋ ๊ฐ๋ ฅํ Node.js ๊ธฐ๋ฐ ๋๊ตฌ์
๋๋ค.
-### v1.3.3 ์ฃผ์ ๋ณ๊ฒฝ
+### v2.1.5 ์ฃผ์ ๋ณ๊ฒฝ
+
+- ๋์ ๋ณ์ DB ๋ผ์ฐํ
+ - XML `dynamicVar`์์ `db` ์์ฑ ์ง์ (`database`์ ๋ณ์นญ)
+ - ๊ฐ ๋์ ๋ณ์๋ ์ง์ ๋ DB ์ด๋ํฐ์์ ์คํ, ๋ฏธ์ง์ ์ ์ ์ญ ๊ธฐ๋ณธ DB ์ฌ์ฉ
+- XML ๊ฒ์ฆ ์
๋ฐ์ดํธ
+ - XML ์คํค๋ง ๊ฒ์ฆ์์ `queryDef`์ `db` ์์ฑ ํ์ฉ (์คํ DB๋ ์ฌ์ ํ ์ํธ `db` ๋๋ ์ ์ญ ๊ธฐ๋ณธ DB๋ก ๊ฒฐ์ )
+
+### v2.1.4 ์ฃผ์ ๋ณ๊ฒฝ
+
+- ์ด๋ํฐ๋ณ DB ์ฐ๊ฒฐ ํ
์คํธ ์ฟผ๋ฆฌ ๋์
+ - ๋ชจ๋ DB ์ด๋ํฐ์ `getTestQuery()` ์ถ๊ฐ
+ - MSSQL: `SELECT 1 as test`, MySQL/MariaDB: `SELECT 1 as test`, PostgreSQL: `SELECT 1`, SQLite: `SELECT 1`, Oracle: `SELECT 1 FROM dual`
+ - `excel-cli.js`๊ฐ ์ด๋ํฐ์ ํ
์คํธ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ฐ๊ฒฐ ๊ฒ์ฆ ์ํ (Oracle ๊ฒ์ฆ ์ด์ ํด๊ฒฐ)
+- ์ํ ์คํค๋ง ์ ํฉ์ฑ(Orders)
+ - PostgreSQL/MySQL: `SubTotal`, `PaymentMethod`, `PaymentStatus`, `EmployeeID` ์ถ๊ฐ
+ - ์ํ ๋ฐ์ดํฐ์ ์ปฌ๋ผ ์ผ์น, MSSQL ์คํค๋ง์์ ์ ํฉ์ฑ ํฅ์
+
+### v2.1.3-beta (v1.3.3) ์ฃผ์ ๋ณ๊ฒฝ
- ์ํธ์์ ํน์ ์ปฌ๋ผ ์ ์ธ๋ฅผ ์ํ `exceptColumns` ์์ฑ ์ถ๊ฐ (XML/JSON)
@@ -27,6 +45,7 @@ SQL2Excel์ ๊ณ ๊ธ ์คํ์ผ๋ง, ํ
ํ๋ฆฟ ์ง์, ๋
๋ฆฝ ์คํ ํ์ผ ๋ฐฐํฌ
- ๐ **๋ค์ค ์ํธ ์ง์**: ํ๋์ ์์
ํ์ผ ๋ด์์ ์ฌ๋ฌ SQL ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ณ๋์ ์ํธ์ ์ ์ฅ
- ๐จ **ํ
ํ๋ฆฟ ์คํ์ผ ์์คํ
**: ์ผ๊ด๋ ๋์์ธ์ ์ํ ์ฌ์ ์ ์๋ ์์
์คํ์ผ๋ง ํ
ํ๋ฆฟ (7๊ฐ์ง ๋ด์ฅ ์คํ์ผ)
- ๐ **๋ค์ค DB ์ฐ๊ฒฐ**: ๊ฐ ์ํธ๋ง๋ค ๋ค๋ฅธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ฌ์ฉ ๊ฐ๋ฅ
+- ๐๏ธ **๋ค์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ง์ (v2.0.0-beta+)**: MSSQL, MySQL, MariaDB๋ฅผ ํตํฉ ์ธํฐํ์ด์ค๋ก ์ง์
- ๐ **๋ณ์ ์์คํ
**: ๋์ ์ฟผ๋ฆฌ ์์ฑ์ ์ํ ๋ณ์ ์ฌ์ฉ
- ๐ **ํฅ์๋ ๋์ ๋ณ์**: ์ค์๊ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ์ถ์ถํ์ฌ ๊ณ ๊ธ ์ฒ๋ฆฌ
- ๐ **์ฟผ๋ฆฌ ์ฌ์ฌ์ฉ**: ๊ณตํต ์ฟผ๋ฆฌ๋ฅผ ์ ์ํ๊ณ ์ฌ๋ฌ ์ํธ์์ ์ฌ์ฌ์ฉ
@@ -42,11 +61,11 @@ SQL2Excel์ ๊ณ ๊ธ ์คํ์ผ๋ง, ํ
ํ๋ฆฟ ์ง์, ๋
๋ฆฝ ์คํ ํ์ผ ๋ฐฐํฌ
- ๐ **๋ค๊ตญ์ด ์ง์**: ํ๊ตญ์ด ๋ฐ ์์ด ๋ฆด๋ฆฌ์ค ํจํค์ง
- ๐ง **๋ฆด๋ฆฌ์ค ์๋ํ**: ์ ์ ํ ๋ฌธ์์ ํจ๊ป ์๋ ๋ฆด๋ฆฌ์ค ํจํค์ง ์์ฑ
- ๐ **์์ฑ ํ์์คํฌํ**: ๊ฐ ์์
์ํธ์ ์์ฑ ํ์์คํฌํ ํ์
-- โฐ **ํฅ์๋ DateTime ๋ณ์**: ์ค์๊ฐ ํ์์คํฌํ ์์ฑ์ ์ํ 20๊ฐ ์ด์์ ์๋ datetime ๋ณ์
+- โฐ **ํฅ์๋ DateTime ๋ณ์**: ์ ์ธ๊ณ 22๊ฐ ํ์์กด ์ง์ ๋ฐ ์ปค์คํ
ํฌ๋งท
- ๐ **SQL ์ฟผ๋ฆฌ ํฌ๋งทํ
**: ๋ชฉ์ฐจ์์ ์ค๋ฐ๊ฟ์ ํฌํจํ ์๋ณธ SQL ํฌ๋งท ์ ์ง
- ๐ง **์
๋ ฅ ์ ํจ์ฑ ๊ฒ์ฆ**: ํ์ผ ๊ฒฝ๋ก ์
๋ ฅ์ ๋ํ ์๋ ๊ณต๋ฐฑ ์ ๊ฑฐ
-### What's New (v1.3.2)
+### v2.1.2(v1.3.2) What's New
- ํ์ฅ์ ๊ธฐ๋ฐ ์ํธ๋ณ ๋ด๋ณด๋ด๊ธฐ ๋ผ์ฐํ
(์ ์ง)
- `.xlsx` / `.xls` โ ๋จ์ผ ์์
ํตํฉ๋ฌธ์ ์์ฑ (๊ธฐ์กด ๋์)
@@ -71,12 +90,12 @@ SQL2Excel์ ๊ณ ๊ธ ์คํ์ผ๋ง, ํ
ํ๋ฆฟ ์ง์, ๋
๋ฆฝ ์คํ ํ์ผ ๋ฐฐํฌ
#### ๊ฐ๋ฐ/์์ค ์ฝ๋ ์ฌ์ฉ ์
- Node.js 16.0 ์ด์
-- SQL Server 2012 ์ด์
+- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฒ (MSSQL 2012+, MySQL 5.7+, ๋๋ MariaDB 10.2+)
- ์ ์ ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ถํ
#### ๋
๋ฆฝ ์คํ ํ์ผ ์ฌ์ฉ ์
- Windows 10 ์ด์ (64๋นํธ)
-- SQL Server 2012 ์ด์
+- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฒ (MSSQL 2012+, MySQL 5.7+, ๋๋ MariaDB 10.2+)
- ์ ์ ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ถํ
- **Node.js ์ค์น ๋ถํ์**
@@ -101,26 +120,47 @@ npm run build
### 3. ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ค์
`config/dbinfo.json` ํ์ผ์ ์์ฑํ์ธ์:
+
+#### ๋ค์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ง์ (v2.0.0-beta+)
```json
{
- "dbs": {
- "sampleDB": {
- "server": "localhost",
- "port": 1433,
- "database": "SampleDB",
- "user": "sa",
- "password": "yourpassword",
- "options": {
- "encrypt": false,
- "trustServerCertificate": true
- }
- },
- "erpDB": {
- "server": "erp-server.com",
- "port": 1433,
- "database": "ERP_Database",
- "user": "erp_user",
- "password": "erp_password",
+ "sampleDB": {
+ "type": "mssql", // ์ ํ์ฌํญ: "mssql" (๊ธฐ๋ณธ๊ฐ), "mysql", ๋๋ "mariadb"
+ "server": "localhost",
+ "port": 1433,
+ "database": "SampleDB",
+ "user": "sa",
+ "password": "yourpassword",
+ "options": {
+ "encrypt": false,
+ "trustServerCertificate": true
+ }
+ },
+ "mysqlDB": {
+ "type": "mysql", // MySQL ๋ฐ์ดํฐ๋ฒ ์ด์ค
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password",
+ "options": {
+ "connectionTimeout": 30000
+ }
+ },
+ "mariaDB": {
+ "type": "mariadb", // MariaDB ๋ฐ์ดํฐ๋ฒ ์ด์ค
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password"
+ },
+ "erpDB": {
+ "server": "erp-server.com",
+ "port": 1433,
+ "database": "ERP_Database",
+ "user": "erp_user",
+ "password": "erp_password",
"options": {
"encrypt": true,
"trustServerCertificate": false
@@ -426,12 +466,21 @@ sql2excel.exe --mode=help
- ๋ด๋ถ ์ค๋ฐ๊ฟ(\r/\n)์ CSV/TXT ๋ชจ๋ ๊ณต๋ฐฑ์ผ๋ก ์ ๊ทํ
- ๋ ์ง ์ง๋ ฌํ: `yyyy-MM-dd HH:mm:ss` (24์๊ฐ)
-## ๐จ ํ
ํ๋ฆฟ ์คํ์ผ ์์คํ
+## ๐ ๋ค์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค
-SQL2Excel์ ์ฌ์ ์ ์๋ ์์
์คํ์ผ๋ง ํ
ํ๋ฆฟ์ ํฌํจํ ํฌ๊ด์ ์ธ ํ
ํ๋ฆฟ ์คํ์ผ ์์คํ
์ ์ ๊ณตํฉ๋๋ค.
+ํตํฉ ์ด๋ํฐ์ ์ ์ฐํ ๋ผ์ฐํ
์ผ๋ก ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ง์ํฉ๋๋ค.
-### ์ฌ์ฉ ๊ฐ๋ฅํ ํ
ํ๋ฆฟ ์คํ์ผ
+- **์ง์ ๋๋ผ์ด๋ฒ**: MSSQL(`mssql`), MySQL(`mysql2`), MariaDB(`mysql2`), PostgreSQL(`pg`), SQLite(`better-sqlite3`), Oracle(`oracledb`)
+- **์ค์ **: `config/dbinfo.json`์ ๋ค์์ DB ํค๋ฅผ ์ ์ํ๊ณ `type`(๋ฏธ์ง์ ์ `mssql`)๊ณผ ์ ์ ์ ๋ณด๋ฅผ ์ค์ ํ์ธ์. ์์ธ ์์๋ ์ โ๋ค์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ง์ (v2.0.0-beta+)โ ์น์
์ฐธ๊ณ .
+- **๋ฐํ์ DB ์ ํ ์ฐ์ ์์ (v2.1.5+)
+ - ๊ธฐ๋ณธ DB ํค: CLI `--db` > `excel.db`
+ - ์ํธ๋ณ: `sheet.db`๊ฐ ๊ธฐ๋ณธ DB๋ฅผ ์ค๋ฒ๋ผ์ด๋
+ - ๋์ ๋ณ์: `dynamicVar.database` ๋๋ `dynamicVar.db`๊ฐ ๊ธฐ๋ณธ DB๋ฅผ ์ค๋ฒ๋ผ์ด๋
+- **ํผํฉ ์ฌ์ฉ**: ํ๋์ ๋ด๋ณด๋ด๊ธฐ์์ ์๋ก ๋ค๋ฅธ DB๋ฅผ ๋์์ ์ฌ์ฉํ ์ ์์ต๋๋ค. XML/JSON ์์๋ โ๋ค์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ง์ (v2.0.0-beta+)โ ์น์
์ ์ฐธ๊ณ ํ์ธ์.
+- **์ฐ๊ฒฐ ํ
์คํธ**: ๋ด๋ณด๋ด๊ธฐ ์ `node src/excel-cli.js list-dbs`(๊ฐ๋ฐ) ๋๋ `sql2excel.exe list-dbs`(EXE)๋ก ํ์ธํ์ธ์.
+- **์ด๋ํฐ ๋์**: ํ ์ ํ๊ณผ ํจ์๊ฐ DB๋ณ๋ก ์๋ ์กฐ์ ๋ฉ๋๋ค (์: MSSQL=TOP, MySQL/MariaDB=LIMIT).
+## ๐จ ํ
ํ๋ฆฟ ์คํ์ผ ์์คํ
| ์คํ์ผ ID | ์ด๋ฆ | ์ค๋ช
|
|----------|------|------|
| `default` | ๊ธฐ๋ณธ ์คํ์ผ | ๊ธฐ๋ณธ ์์
์คํ์ผ |
@@ -507,15 +556,15 @@ node src/excel-cli.js export --xml queries.xml --style modern
#### XML ๊ตฌ์ฑ
```xml
-
-
+
+
-
-
+
+
@@ -539,6 +588,10 @@ WHERE CustomerID IN (${customerData.CustomerID})
4. **์ฑ๋ฅ**: ๋ณ์๋ ํ ๋ฒ ์คํ๋๊ณ ์ ์ฒด ๋ด๋ณด๋ด๊ธฐ์ ๋ํด ์บ์๋ฉ๋๋ค
5. **๋๋ฒ๊ทธ ๋ชจ๋**: ์์ธํ ๋ณ์ ์นํ์ ์ํด `DEBUG_VARIABLES=true`๋ก ํ์ฑํ
+์ฐธ๊ณ :
+- `dynamicVar`์์ ์ง์ํ๋ ์์ฑ: `name`, `description`, `type`, `db`, `database` (`db`๋ ๋ณ์นญ). ๋ ๋ค ์์ผ๋ฉด `database`๊ฐ ์ฐ์ ํฉ๋๋ค.
+- `queryDef`๋ XML ๊ฒ์ฆ์์ `db` ์์ฑ์ ํ์ฉํฉ๋๋ค. ์คํ ์์ ์ DB๋ ์ํธ์ `db` ๋๋ ์ ์ญ ๊ธฐ๋ณธ DB๋ก ๊ฒฐ์ ๋ฉ๋๋ค.
+
## ๐ ์ปค์คํ
๋ ์ง/์๊ฐ ๋ณ์
SQL2Excel์ ์ํ๋ ํ์์ผ๋ก ํ์ฌ ๋ ์ง์ ์๊ฐ์ ํ์ํ ์ ์๋ ๊ฐ๋ ฅํ ์ปค์คํ
๋ ์ง ๋ณ์ ์์คํ
์ ์ ๊ณตํฉ๋๋ค. ์ด๋ฌํ ๋ณ์๋ ์ฟผ๋ฆฌ, ํ์ผ ์ด๋ฆ ๋ฐ ๋ชจ๋ ํ
์คํธ ์ฝํ
์ธ ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
@@ -792,7 +845,137 @@ SQL2Excel์ ์์ฑ๋ ๊ฐ ์์
์ํธ์ ์๋์ผ๋ก ์์ฑ ํ์์คํฌํ
## ๐จ ๊ณ ๊ธ ๊ธฐ๋ฅ
-### 1. ์์
์คํ์ผ๋ง
+### 1. ๋ค์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ง์ (v2.0.0-beta+)
+
+SQL2Excel์ ์ด์ ํตํฉ ์ธํฐํ์ด์ค๋ก ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
์ ์ง์ํฉ๋๋ค:
+
+#### ์ง์ ๋ฐ์ดํฐ๋ฒ ์ด์ค
+- **MSSQL** (SQL Server 2012+)
+- **MySQL** (5.7+)
+- **MariaDB** (10.2+)
+
+#### ์ค์
+`config/dbinfo.json`์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ `type` ํ๋๋ฅผ ์ถ๊ฐํ์ธ์:
+
+```json
+{
+ "mssqlDB": {
+ "type": "mssql", // ๋๋ ํ์ ํธํ์ฑ์ ์ํด ์๋ต ๊ฐ๋ฅ
+ "server": "localhost",
+ "port": 1433,
+ "database": "SampleDB",
+ "user": "sa",
+ "password": "password",
+ "options": {
+ "encrypt": false,
+ "trustServerCertificate": true
+ }
+ },
+ "mysqlDB": {
+ "type": "mysql",
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password"
+ },
+ "mariaDB": {
+ "type": "mariadb",
+ "server": "localhost",
+ "port": 3306,
+ "database": "mydb",
+ "user": "root",
+ "password": "password"
+ }
+}
+```
+
+#### ํผํฉ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ
+๋จ์ผ Excel ํ์ผ์์ ๋ค์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
์ ์ฟผ๋ฆฌํ ์ ์์ต๋๋ค:
+
+**XML ์์ :**
+```xml
+
+
+
+
+
+
+
+
+ SELECT TOP 10 * FROM Customers
+
+
+
+
+
+
+ SELECT * FROM products LIMIT 10
+
+
+
+
+
+
+ SELECT * FROM orders
+ WHERE order_date >= '2024-01-01'
+ LIMIT 20
+
+
+
+```
+
+**JSON ์์ :**
+```json
+{
+ "excel": {
+ "output": "output/mixed_report.xlsx",
+ "db": "mssqlDB",
+ "style": "modern"
+ },
+ "sheets": [
+ {
+ "name": "MSSQL ๋ฐ์ดํฐ",
+ "db": "mssqlDB",
+ "query": "SELECT TOP 10 * FROM Customers"
+ },
+ {
+ "name": "MySQL ๋ฐ์ดํฐ",
+ "db": "mysqlDB",
+ "query": "SELECT * FROM products LIMIT 10"
+ },
+ {
+ "name": "MariaDB ๋ฐ์ดํฐ",
+ "db": "mariaDB",
+ "query": "SELECT * FROM orders WHERE order_date >= '2024-01-01' LIMIT 20"
+ }
+ ]
+}
+```
+
+#### ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ณ ๊ธฐ๋ฅ
+- **MSSQL**: `TOP N` ์ , `GETDATE()` ํจ์ ์ง์
+- **MySQL/MariaDB**: `LIMIT N` ์ , `NOW()` ํจ์ ์ง์
+- **์ ์ฒด**: ๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
์ ๋ํ ์๋ ๊ตฌ๋ฌธ ์ฒ๋ฆฌ
+
+#### ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํ
์คํธ
+๊ตฌ์ฑ๋ ๋ชจ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ํ
์คํธํ์ธ์:
+```bash
+# ๊ฐ๋ฐ ํ๊ฒฝ
+node src/excel-cli.js list-dbs
+
+# ๋
๋ฆฝ ์คํ ํ์ผ
+sql2excel-v1.3.0.exe list-dbs
+```
+
+#### ๋ฐํ์ DB ์ ํ ์ฐ์ ์์ (v2.1.5+)
+
+- ๊ธฐ๋ณธ DB ํค: CLI `--db` > `excel.db`
+- ์ํธ๋ณ: `sheet.db`๊ฐ ๊ธฐ๋ณธ DB๋ฅผ ์ค๋ฒ๋ผ์ด๋
+- ๋์ ๋ณ์: `dynamicVar.database` ๋๋ `dynamicVar.db`๊ฐ ๊ธฐ๋ณธ DB๋ฅผ ์ค๋ฒ๋ผ์ด๋
+- ํ: ๋ด๋ณด๋ด๊ธฐ ์ ์ ์ `list-dbs`๋ก ์ฐ๊ฒฐ ์ํ๋ฅผ ์ ๊ฒํ์ธ์
+
+### 2. ์์
์คํ์ผ๋ง
#### ๊ธ๊ผด ์คํ์ผ๋ง
```xml
diff --git a/config/dbinfo.json b/config/dbinfo.json
index cb5b822..4ae2e6d 100644
--- a/config/dbinfo.json
+++ b/config/dbinfo.json
@@ -1,5 +1,6 @@
{
"sampleDB": {
+ "type": "mssql",
"user": "sample",
"password": "sample1234!",
"server": "localhost",
@@ -8,14 +9,38 @@
"options": { "encrypt": true, "trustServerCertificate": true }
},
"erpDB": {
- "user": "erp",
- "password": "erp1234!",
+ "type": "mssql",
+ "user": "sample",
+ "password": "sample1234!",
"server": "localhost",
"database": "ERP_DB",
"port": 1433,
"options": { "encrypt": true, "trustServerCertificate": true }
},
+ "mysql": {
+ "type": "mysql",
+ "user": "sahara",
+ "password": "1111",
+ "server": "localhost",
+ "database": "etcdb",
+ "port": 3306,
+ "options": {
+ "connectionTimeout": 30000
+ }
+ },
+ "mariaDB": {
+ "type": "mariadb",
+ "user": "sahara",
+ "password": "1111",
+ "server": "localhost",
+ "database": "sampledb",
+ "port": 3306,
+ "options": {
+ "connectionTimeout": 3000
+ }
+ },
"sourceDB": {
+ "type": "mssql",
"user": "sample",
"password": "sample1234!",
"server": "localhost",
@@ -27,8 +52,43 @@
"encrypt": true,
"trustServerCertificate": true,
"enableArithAbort": true,
- "requestTimeout": 300000,
- "connectionTimeout": 30000
+ "requestTimeout": 3000,
+ "connectionTimeout": 3000
+ }
+ },
+ "postgresDB": {
+ "type": "postgresql",
+ "user": "postgres",
+ "password": "1111",
+ "server": "localhost",
+ "database": "postgres",
+ "port": 5432,
+ "options": {
+ "max": 10,
+ "idleTimeoutMillis": 3000,
+ "connectionTimeout": 3000
+ }
+ },
+ "sqliteDB": {
+ "type": "sqlite",
+ "database": "./data/mydb.sqlite",
+ "options": {
+ "mode": null
+ }
+ },
+ "oracleDB": {
+ "type": "oracle",
+ "user": "c##scott",
+ "password": "tiger",
+ "server": "localhost",
+ "port": 1521,
+ "serviceName": "xe",
+ "options": {
+ "poolMin": 0,
+ "poolMax": 4,
+ "poolIncrement": 1,
+ "queueTimeout": 3000,
+ "stmtCacheSize": 30
}
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index a61ae5d..260d39d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,21 +1,25 @@
{
"name": "sql2excel",
- "version": "1.2.5",
+ "version": "2.1.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sql2excel",
- "version": "1.2.5",
+ "version": "2.1.5",
"dependencies": {
+ "better-sqlite3": "^11.10.0",
"exceljs": "^4.3.0",
"json5": "^2.2.3",
"mssql": "^10.0.0",
+ "mysql2": "^3.4.0",
+ "oracledb": "^6.6.0",
+ "pg": "^8.16.3",
"xml2js": "^0.6.2",
"yargs": "^17.7.2"
},
"bin": {
- "sql2excel": "src/excel-cli.js"
+ "sql2excel": "app.js"
},
"devDependencies": {
"pkg": "^5.8.1"
@@ -807,6 +811,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/aws-ssl-profiles": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
+ "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -833,6 +846,17 @@
],
"license": "MIT"
},
+ "node_modules/better-sqlite3": {
+ "version": "11.10.0",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",
+ "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "prebuild-install": "^7.1.1"
+ }
+ },
"node_modules/big-integer": {
"version": "1.6.52",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
@@ -855,6 +879,15 @@
"node": "*"
}
},
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "license": "MIT",
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@@ -1031,7 +1064,6 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
- "dev": true,
"license": "ISC"
},
"node_modules/cliui": {
@@ -1205,7 +1237,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
@@ -1221,7 +1252,6 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=4.0.0"
@@ -1270,11 +1300,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/denque": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
"node_modules/detect-libc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
- "dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
@@ -1579,7 +1617,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
- "dev": true,
"license": "(MIT OR WTFPL)",
"engines": {
"node": ">=6"
@@ -1625,6 +1662,12 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "license": "MIT"
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -1786,6 +1829,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/generate-function": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+ "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-property": "^1.0.2"
+ }
+ },
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -1853,7 +1905,6 @@
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
- "dev": true,
"license": "MIT"
},
"node_modules/glob": {
@@ -2138,7 +2189,6 @@
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "dev": true,
"license": "ISC"
},
"node_modules/internal-slot": {
@@ -2427,6 +2477,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-property": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
+ "license": "MIT"
+ },
"node_modules/is-regex": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
@@ -2915,6 +2971,36 @@
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
"license": "MIT"
},
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/lru.min": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz",
+ "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==",
+ "license": "MIT",
+ "engines": {
+ "bun": ">=1.0.0",
+ "deno": ">=1.30.0",
+ "node": ">=8.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wellwelwel"
+ }
+ },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -2952,7 +3038,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -2998,7 +3083,6 @@
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
- "dev": true,
"license": "MIT"
},
"node_modules/ms": {
@@ -3052,11 +3136,58 @@
"readable-stream": "^3.6.0"
}
},
+ "node_modules/mysql2": {
+ "version": "3.15.3",
+ "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz",
+ "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==",
+ "license": "MIT",
+ "dependencies": {
+ "aws-ssl-profiles": "^1.1.1",
+ "denque": "^2.1.0",
+ "generate-function": "^2.3.1",
+ "iconv-lite": "^0.7.0",
+ "long": "^5.2.1",
+ "lru.min": "^1.0.0",
+ "named-placeholders": "^1.1.3",
+ "seq-queue": "^0.0.5",
+ "sqlstring": "^2.3.2"
+ },
+ "engines": {
+ "node": ">= 8.0"
+ }
+ },
+ "node_modules/mysql2/node_modules/iconv-lite": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
+ "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/named-placeholders": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
+ "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
+ "license": "MIT",
+ "dependencies": {
+ "lru-cache": "^7.14.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/napi-build-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
- "dev": true,
"license": "MIT"
},
"node_modules/native-duplexpair": {
@@ -3069,7 +3200,6 @@
"version": "3.75.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz",
"integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"semver": "^7.3.5"
@@ -3181,6 +3311,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/oracledb": {
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/oracledb/-/oracledb-6.10.0.tgz",
+ "integrity": "sha512-kGUumXmrEWbSpBuKJyb9Ip3rXcNgKK6grunI3/cLPzrRvboZ6ZoLi9JQ+z6M/RIG924tY8BLflihL4CKKQAYMA==",
+ "hasInstallScript": true,
+ "license": "(Apache-2.0 OR UPL-1.0)",
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
"node_modules/own-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
@@ -3240,6 +3380,95 @@
"node": ">=8"
}
},
+ "node_modules/pg": {
+ "version": "8.16.3",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
+ "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-connection-string": "^2.9.1",
+ "pg-pool": "^3.10.1",
+ "pg-protocol": "^1.10.3",
+ "pg-types": "2.2.0",
+ "pgpass": "1.0.5"
+ },
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "optionalDependencies": {
+ "pg-cloudflare": "^1.2.7"
+ },
+ "peerDependencies": {
+ "pg-native": ">=3.0.1"
+ },
+ "peerDependenciesMeta": {
+ "pg-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pg-cloudflare": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
+ "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/pg-connection-string": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
+ "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==",
+ "license": "MIT"
+ },
+ "node_modules/pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/pg-pool": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
+ "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "pg": ">=8.0"
+ }
+ },
+ "node_modules/pg-protocol": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
+ "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==",
+ "license": "MIT"
+ },
+ "node_modules/pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.1.0"
+ }
+ },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -3384,11 +3613,49 @@
"node": ">= 0.4"
}
},
+ "node_modules/postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postgres-bytea": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
+ "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/prebuild-install": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
"integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^2.0.0",
@@ -3440,7 +3707,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
@@ -3472,7 +3738,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "dev": true,
"license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
"dependencies": {
"deep-extend": "^0.6.0",
@@ -3778,6 +4043,11 @@
"node": ">=10"
}
},
+ "node_modules/seq-queue": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+ "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
+ },
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -3906,7 +4176,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -3927,7 +4196,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -3959,12 +4227,30 @@
"node": ">=8"
}
},
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"license": "BSD-3-Clause"
},
+ "node_modules/sqlstring": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
+ "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/stop-iteration-iterator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
@@ -4133,7 +4419,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -4169,7 +4454,6 @@
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"chownr": "^1.1.1",
@@ -4335,7 +4619,6 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
- "dev": true,
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
@@ -4669,6 +4952,15 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"license": "MIT"
},
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
diff --git a/package.json b/package.json
index 2c24272..ba56e4c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "sql2excel",
- "version": "1.3.3",
+ "version": "2.1.5",
"description": "SQL ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์์
ํ์ผ๋ก ์ ์ฅํ๋ ๋๊ตฌ (์ํธ๋ณ ์ฟผ๋ฆฌ, ๋ณ์, XML/JSON ์ง์)",
"main": "app.js",
"scripts": {
@@ -28,7 +28,11 @@
"templates/**/*.xml",
".env",
"node_modules/mssql/**/*",
- "node_modules/tedious/**/*"
+ "node_modules/tedious/**/*",
+ "node_modules/mysql2/**/*",
+ "node_modules/pg/**/*",
+ "node_modules/better-sqlite3/**/*",
+ "node_modules/oracledb/**/*"
],
"targets": [
"node18-win-x64"
@@ -36,11 +40,15 @@
"outputPath": "dist"
},
"dependencies": {
- "mssql": "^10.0.0",
+ "better-sqlite3": "^11.10.0",
"exceljs": "^4.3.0",
"json5": "^2.2.3",
- "yargs": "^17.7.2",
- "xml2js": "^0.6.2"
+ "mssql": "^10.0.0",
+ "mysql2": "^3.4.0",
+ "pg": "^8.16.3",
+ "oracledb": "^6.6.0",
+ "xml2js": "^0.6.2",
+ "yargs": "^17.7.2"
},
"devDependencies": {
"pkg": "^5.8.1"
diff --git a/queries/backup-data-test.xml b/queries/backup-data-test.xml
index 493607d..a1fbdf1 100644
--- a/queries/backup-data-test.xml
+++ b/queries/backup-data-test.xml
@@ -116,4 +116,135 @@
]]>
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/queries/mariadb-test.xml b/queries/mariadb-test.xml
new file mode 100644
index 0000000..5b459e3
--- /dev/null
+++ b/queries/mariadb-test.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+ SELECT
+ '์์ธ' as city,
+ '${DATE.KST:YYYY๋
MM์ DD์ผ HH์ mm๋ถ ss์ด}' as time
+ UNION ALL
+ SELECT '๋ด์', '${DATE.EST:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT '๋ฐ๋', '${DATE.GMT:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT '๋์ฟ', '${DATE.JST:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT '์๋๋', '${DATE.AEST:YYYY-MM-DD HH:mm:ss}'
+
+
+
+
+
+ SELECT
+ SCHEMA_NAME as '๋ฐ์ดํฐ๋ฒ ์ด์ค๋ช
',
+ DEFAULT_CHARACTER_SET_NAME as '๋ฌธ์์
',
+ DEFAULT_COLLATION_NAME as '์ฝ๋ ์ด์
'
+ FROM information_schema.SCHEMATA
+ WHERE SCHEMA_NAME NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')
+ ORDER BY SCHEMA_NAME
+
+
+
+
+
+ SELECT
+ TABLE_SCHEMA as '๋ฐ์ดํฐ๋ฒ ์ด์ค',
+ TABLE_NAME as 'ํ
์ด๋ธ๋ช
',
+ TABLE_ROWS as 'ํ์',
+ ROUND(DATA_LENGTH / 1024 / 1024, 2) as '๋ฐ์ดํฐํฌ๊ธฐ_MB',
+ ROUND(INDEX_LENGTH / 1024 / 1024, 2) as '์ธ๋ฑ์คํฌ๊ธฐ_MB',
+ ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) as '์ดํฌ๊ธฐ_MB'
+ FROM information_schema.TABLES
+ WHERE TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')
+ ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC
+ LIMIT 50
+
+
+
+
+
+ SELECT
+ VARIABLE_NAME as '๋ณ์๋ช
',
+ VARIABLE_VALUE as '๊ฐ'
+ FROM information_schema.GLOBAL_VARIABLES
+ WHERE VARIABLE_NAME LIKE '%version%'
+ OR VARIABLE_NAME LIKE '%character%'
+ OR VARIABLE_NAME LIKE '%collation%'
+ ORDER BY VARIABLE_NAME
+ LIMIT 30
+
+
+
+
+
diff --git a/queries/mysql-simple-test.json b/queries/mysql-simple-test.json
new file mode 100644
index 0000000..fcf5054
--- /dev/null
+++ b/queries/mysql-simple-test.json
@@ -0,0 +1,34 @@
+{
+ "excel": {
+ "output": "output/mysql_simple_${DATE.KST:YYYYMMDD}.xlsx",
+ "db": "mysql",
+ "style": "default"
+ },
+ "globalVars": {
+ "reportTitle": "MySQL Database Report",
+ "reportDate": "2024"
+ },
+ "sheets": [
+ {
+ "name": "Database Info",
+ "db": "mysql",
+ "query": "SELECT SCHEMA_NAME as db_name, DEFAULT_CHARACTER_SET_NAME as charset, DEFAULT_COLLATION_NAME as collation FROM information_schema.SCHEMATA ORDER BY SCHEMA_NAME LIMIT 20"
+ },
+ {
+ "name": "MySQL Version",
+ "db": "mysql",
+ "query": "SELECT @@version as version, @@version_comment as comment, NOW() as `current_time`"
+ },
+ {
+ "name": "Table Statistics",
+ "db": "mysql",
+ "query": "SELECT TABLE_SCHEMA as db_name, COUNT(*) as table_count, SUM(TABLE_ROWS) as total_rows FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys') GROUP BY TABLE_SCHEMA"
+ },
+ {
+ "name": "MSSQL Test",
+ "db": "sampleDB",
+ "query": "SELECT TOP 5 name, database_id, create_date FROM sys.databases ORDER BY name"
+ }
+ ]
+}
+
diff --git a/queries/mysql-test-queries.xml b/queries/mysql-test-queries.xml
new file mode 100644
index 0000000..23890d1
--- /dev/null
+++ b/queries/mysql-test-queries.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+ SELECT
+ SCHEMA_NAME as database_name,
+ DEFAULT_CHARACTER_SET_NAME as charset,
+ DEFAULT_COLLATION_NAME as collation,
+ '${DATE.KST:YYYY-MM-DD HH:mm:ss}' as report_date
+ FROM information_schema.SCHEMATA
+ ORDER BY SCHEMA_NAME
+
+
+
+
+
+
+ SELECT
+ TABLE_SCHEMA as database_name,
+ TABLE_NAME as table_name,
+ TABLE_TYPE as table_type,
+ ENGINE as engine,
+ TABLE_ROWS as row_count,
+ ROUND(DATA_LENGTH / 1024 / 1024, 2) as data_size_mb,
+ ROUND(INDEX_LENGTH / 1024 / 1024, 2) as index_size_mb,
+ CREATE_TIME as created_at
+ FROM information_schema.TABLES
+ WHERE TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')
+ ORDER BY TABLE_SCHEMA, TABLE_NAME
+ LIMIT 100
+
+
+
+
+
+
+ SELECT
+ User as username,
+ Host as host,
+ '${DATE.UTC:YYYY-MM-DD}' as report_date_utc,
+ '${DATE.KST:YYYY-MM-DD}' as report_date_kst
+ FROM mysql.user
+ ORDER BY User, Host
+
+
+
+
+
+
+ SELECT
+ VARIABLE_NAME as variable_name,
+ VARIABLE_VALUE as value
+ FROM information_schema.GLOBAL_VARIABLES
+ WHERE VARIABLE_NAME IN (
+ 'version',
+ 'version_comment',
+ 'max_connections',
+ 'character_set_server',
+ 'collation_server',
+ 'innodb_buffer_pool_size',
+ 'datadir'
+ )
+ ORDER BY VARIABLE_NAME
+
+
+
+
+
+
+ SELECT
+ TABLE_SCHEMA as database_name,
+ COUNT(*) as table_count,
+ SUM(TABLE_ROWS) as total_rows,
+ ROUND(SUM(DATA_LENGTH) / 1024 / 1024, 2) as total_data_mb,
+ '${DATE:YYYY๋
MM์ DD์ผ HH์ mm๋ถ}' as report_time
+ FROM information_schema.TABLES
+ WHERE TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')
+ GROUP BY TABLE_SCHEMA
+ ORDER BY total_data_mb DESC
+
+
+
+
+
+
+ SELECT TOP 10
+ name as database_name,
+ database_id,
+ create_date,
+ '${DATE.KST:YYYY-MM-DD HH:mm:ss}' as report_date
+ FROM sys.databases
+ ORDER BY name
+
+
+
+
+
+
+ SELECT
+ 'Seoul (KST)' as timezone,
+ '${DATE.KST:YYYY-MM-DD HH:mm:ss}' as datetime
+ UNION ALL
+ SELECT
+ 'UTC',
+ '${DATE.UTC:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT
+ 'New York (EST)',
+ '${DATE.EST:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT
+ 'Tokyo (JST)',
+ '${DATE.JST:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT
+ 'Local Time',
+ '${DATE:YYYY-MM-DD HH:mm:ss}'
+
+
+
+
+
diff --git a/queries/oracle-test.xml b/queries/oracle-test.xml
new file mode 100644
index 0000000..2293e88
--- /dev/null
+++ b/queries/oracle-test.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+ SELECT USER as username, SYS_CONTEXT('USERENV','DB_NAME') as db_name, SYSDATE as sysdate FROM dual
+
+
+
+
+
+ SELECT
+ owner,
+ table_name,
+ num_rows,
+ blocks,
+ to_char(last_analyzed,'YYYY-MM-DD HH24:MI:SS') as last_analyzed
+ FROM all_tables
+ WHERE owner = USER
+ ORDER BY table_name
+ FETCH FIRST 50 ROWS ONLY
+
+
+
+
+
+ SELECT
+ c.CustomerID,
+ c.CustomerCode,
+ c.CustomerName,
+ c.City,
+ c.Region,
+ c.Country,
+ c.CreditLimit,
+ c.IsActive,
+ to_char(c.CreatedDate, 'YYYY-MM-DD HH24:MI:SS') as CreatedDate
+ FROM Customers c
+ ORDER BY c.CustomerID
+ FETCH FIRST 100 ROWS ONLY
+
+
+
diff --git a/queries/postgresql-test.xml b/queries/postgresql-test.xml
new file mode 100644
index 0000000..71fc088
--- /dev/null
+++ b/queries/postgresql-test.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+ SELECT
+ version() as postgres_version,
+ current_database() as database_name,
+ current_user as current_user,
+ '${DATE.KST:YYYY-MM-DD HH:mm:ss}' as report_date
+
+
+
+
+
+
+ SELECT
+ schemaname as schema_name,
+ tablename as table_name,
+ '${DATE.UTC:YYYY-MM-DD HH:mm:ss}' as report_date_utc
+ FROM pg_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
+ ORDER BY schemaname, tablename
+ LIMIT 50
+
+
+
+
+
+
+ SELECT
+ '์์ธ (KST)' as timezone,
+ '${DATE.KST:YYYY-MM-DD HH:mm:ss}' as datetime
+ UNION ALL
+ SELECT
+ 'UTC',
+ '${DATE.UTC:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT
+ '๋ด์ (EST)',
+ '${DATE.EST:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT
+ '๋์ฟ (JST)',
+ '${DATE.JST:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT
+ 'Local Time',
+ '${DATE:YYYY-MM-DD HH:mm:ss}'
+
+
+
+
+
+
+ SELECT
+ datname as database_name,
+ pg_size_pretty(pg_database_size(datname)) as size
+ FROM pg_database
+ WHERE datistemplate = false
+ ORDER BY pg_database_size(datname) DESC
+ LIMIT 20
+
+
+
+
+
+
+ SELECT
+ pid as process_id,
+ usename as username,
+ application_name,
+ client_addr as client_address,
+ state,
+ query_start,
+ state_change
+ FROM pg_stat_activity
+ WHERE pid != pg_backend_pid()
+ ORDER BY query_start DESC
+ LIMIT 30
+
+
+
+
+
diff --git a/queries/queries-sample-orders.json b/queries/queries-sample-orders.json
index 7801c33..0030409 100644
--- a/queries/queries-sample-orders.json
+++ b/queries/queries-sample-orders.json
@@ -1,6 +1,6 @@
{
"excel": {
- "db": "sampleDB",
+ "db": "oracleDB",
"output": "output/sales_summary_2024.xlsx",
"maxRows": 1500,
"header": {
diff --git a/queries/queries-sample-orders.xml b/queries/queries-sample-orders.xml
index 66a011b..1ab8229 100644
--- a/queries/queries-sample-orders.xml
+++ b/queries/queries-sample-orders.xml
@@ -1,5 +1,5 @@
-
+
@@ -9,62 +9,62 @@
2024-01-01
2024-06-30
["Seoul", "Busan"]
- ["ACTIVE", "PENDING", "COMPLETED"]
+ ["Pending", "Shipped", "Delivered"]
[1, 2, 3, 5]
1000
-
+
= '${startDate}' AND OrderDate <= '${endDate}'
ORDER BY OrderDate DESC
]]>
-
+
-
+
= '${startDate}' AND o.OrderDate <= '${endDate}'
ORDER BY o.OrderDate DESC
]]>
-
+
= '${startDate}' AND o.OrderDate <= '${endDate}'
ORDER BY o.OrderDate DESC
]]>
-
+
= '${startDate}' AND o.OrderDate <= '${endDate}'
ORDER BY o.OrderDate DESC
]]>
@@ -72,21 +72,11 @@
= '${startDate}'
AND OrderDate <= '${endDate}'
AND OrderStatus IN (${statusList})
ORDER BY OrderDate DESC
]]>
-
-
-
\ No newline at end of file
diff --git a/queries/queries-with-dynamic-variables.xml b/queries/queries-with-dynamic-variables.xml
index b69acff..fdc80d5 100644
--- a/queries/queries-with-dynamic-variables.xml
+++ b/queries/queries-with-dynamic-variables.xml
@@ -1,5 +1,5 @@
-
+
@@ -11,7 +11,7 @@
2024-01-01
2024-06-30
["Seoul", "Busan"]
- ["ACTIVE", "PENDING", "COMPLETED"]
+ ["Pending", "Shipped"]
[1, 2, 3, 5]
1000
@@ -19,9 +19,9 @@
-
+
-
-
+
+
-
+
= '${startDate}'
- AND OrderDate <= '${endDate}'
- AND OrderDate >= DATEADD(day, -30, '${CURRENT_DATE}')
- ORDER BY OrderDate DESC
+ SELECT distinct OrderID, OrderNumber, OrderDate
+ FROM Orders
]]>
@@ -70,9 +65,8 @@
c.City as City,
c.Region as Region,
c.CustomerType as CustomerType
- FROM SampleDB.dbo.Customers c
- WHERE c.CustomerID IN (${customerData.CustomerID})
- AND c.Region IN (${regionList})
+ FROM Customers c
+ WHERE c.Region IN (${regionList})
ORDER BY c.CustomerID
]]>
@@ -80,63 +74,58 @@
-
+
- ["Seoul", "Busan"]
+ ["์์ธ", "๋ถ์ฐ"]
-
-
- ["Busan"]
-
-
-
+
-
+
-
+
= '${startDate}'
+ AND o.OrderID IN (${activeOrders.OrderID})
+ AND o.OrderDate >= '${startDate}'
AND o.OrderDate <= '${endDate}'
GROUP BY c.CustomerName, c.Region
ORDER BY TotalOrderAmount DESC
diff --git a/queries/sqlite-test.xml b/queries/sqlite-test.xml
new file mode 100644
index 0000000..6c98dc0
--- /dev/null
+++ b/queries/sqlite-test.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+ SELECT
+ sqlite_version() as sqlite_version,
+ '${DATE.KST:YYYY๋
MM์ DD์ผ HH์ mm๋ถ}' as report_date_kst,
+ '${DATE:YYYY-MM-DD HH:mm:ss}' as report_date_local
+
+
+
+
+
+
+ SELECT
+ name as table_name,
+ type as object_type,
+ sql as create_sql
+ FROM sqlite_master
+ WHERE type IN ('table', 'view')
+ AND name NOT LIKE 'sqlite_%'
+ ORDER BY type, name
+ LIMIT 50
+
+
+
+
+
+
+ SELECT
+ 'Seoul (KST)' as timezone,
+ '${DATE.KST:YYYY-MM-DD HH:mm:ss}' as datetime
+ UNION ALL
+ SELECT
+ 'UTC',
+ '${DATE.UTC:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT
+ 'Tokyo (JST)',
+ '${DATE.JST:YYYY-MM-DD HH:mm:ss}'
+ UNION ALL
+ SELECT
+ 'Local Time',
+ '${DATE:YYYY-MM-DD HH:mm:ss}'
+
+
+
+
+
+
+ SELECT
+ 1 as id,
+ 'Test 1' as name,
+ datetime('now') as created_at
+ UNION ALL
+ SELECT
+ 2,
+ 'Test 2',
+ datetime('now', '-1 day')
+ UNION ALL
+ SELECT
+ 3,
+ 'Test 3',
+ datetime('now', '-7 day')
+
+
+
+
+
diff --git a/resources/README_100_RECORDS.md b/resources/README_100_RECORDS.md
new file mode 100644
index 0000000..83cd734
--- /dev/null
+++ b/resources/README_100_RECORDS.md
@@ -0,0 +1,169 @@
+# 100๊ฑด ์ํ ๋ฐ์ดํฐ ์์ฑ ์๋ฃ ์๋ด
+
+## ๐ ์์ฑ๋ ํ์ผ ํํฉ
+
+### โ
์๋ฃ๋ ์์
+1. โ
`generate_sample_data.py` - Python ๋ฐ์ดํฐ ์์ฑ ์คํฌ๋ฆฝํธ (100๊ฑด ์ง์)
+2. โ
๊ฐ DB๋ณ ๊ธฐ๋ณธ ์คํฌ๋ฆฝํธ (10-15๊ฑด)
+ - `insert_sample_data.sql` (MSSQL)
+ - `insert_sample_data_mysql.sql` (MySQL/MariaDB)
+ - `insert_sample_data_postgresql.sql` (PostgreSQL)
+ - `insert_sample_data_sqlite.sql` (SQLite)
+
+### ๐ 100๊ฑด ๋ฐ์ดํฐ ๊ตฌ์ฑ
+
+**ํ๊ธ 50๊ฑด + ์์ด 50๊ฑด = ์ด 100๊ฑด**
+
+- **Customers (๊ณ ๊ฐ)**: 100๊ฑด
+ - ํ๊ธ: CUST001~CUST050 (ํ๊ตญ์ ์, ์์ธ๋ฌด์ญ, ๋ถ์ฐ์ฐ์
, ...)
+ - ์์ด: CUST051~CUST100 (Tech Solutions Inc, Global Trading Co, ...)
+
+- **Products (์ ํ)**: 100๊ฑด
+ - ํ๊ธ: PROD001~PROD050 (๋
ธํธ๋ถ, ๋ง์ฐ์ค, ํค๋ณด๋, ...)
+ - ์์ด: PROD051~PROD100 (Laptop, Mouse, Keyboard, ...)
+
+- **Employees (์ง์)**: 50๊ฑด
+ - ํ๊ธ: EMP001~EMP025 (๊น์ฒ ์, ์ด์ํฌ, ...)
+ - ์์ด: EMP026~EMP050 (John Smith, Emily Johnson, ...)
+
+- **Orders (์ฃผ๋ฌธ)**: 100๊ฑด
+ - ORD-2024-001 ~ ORD-2024-100
+
+- **OrderDetails (์ฃผ๋ฌธ์์ธ)**: 200~300๊ฑด
+ - ๊ฐ ์ฃผ๋ฌธ๋น 2-3๊ฐ ํ๋ชฉ
+
+## ๐ 100๊ฑด ๋ฐ์ดํฐ ์์ฑ ๋ฐฉ๋ฒ
+
+### ์ต์
1: Python ์คํฌ๋ฆฝํธ ์ฌ์ฉ (๊ฐ์ฅ ๊ฐ๋จ)
+
+```bash
+# ์ค์น ํ์ธ
+python --version
+
+# ์คํ (์์ง ๋ฏธ์์ฑ - ํ์ฅ ํ์)
+python resources/generate_sample_data.py
+```
+
+### ์ต์
2: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ด์์ ์์ฑ (์ถ์ฒ)
+
+#### MSSQL
+```sql
+-- 1. ๊ธฐ๋ณธ ์ํ ๋ฐ์ดํฐ ์
๋ ฅ (10๊ฑด)
+:r insert_sample_data.sql
+
+-- 2. ์ถ๊ฐ 90๊ฑด ์์ฑ
+-- Customers ์ถ๊ฐ
+DECLARE @i INT = 11;
+WHILE @i <= 100
+BEGIN
+ INSERT INTO dbo.Customers (CustomerCode, CustomerName, ContactName, Email, ...)
+ SELECT
+ 'CUST' + RIGHT('000' + CAST(@i AS VARCHAR), 3),
+ CASE WHEN @i <= 50
+ THEN N'ํ๊ตญํ์ฌ' + CAST(@i AS NVARCHAR)
+ ELSE 'Company ' + CAST(@i AS VARCHAR)
+ END,
+ ...
+ SET @i = @i + 1;
+END
+```
+
+#### MySQL/MariaDB
+```sql
+-- 1. ๊ธฐ๋ณธ ๋ฐ์ดํฐ
+SOURCE insert_sample_data_mysql.sql;
+
+-- 2. ์ฌ๊ท CTE๋ก ์ถ๊ฐ ์์ฑ
+INSERT INTO Customers (CustomerCode, CustomerName, ContactName, Email, Phone, City, Region, Country, CustomerType, CreditLimit, IsActive)
+WITH RECURSIVE Numbers AS (
+ SELECT 11 AS N
+ UNION ALL
+ SELECT N + 1 FROM Numbers WHERE N < 100
+)
+SELECT
+ CONCAT('CUST', LPAD(N, 3, '0')),
+ IF(N <= 50,
+ CONCAT('ํ๊ตญํ์ฌ', N),
+ CONCAT('Company ', N)
+ ),
+ IF(N <= 50,
+ CONCAT('๋ด๋น์', N),
+ CONCAT('Contact ', N)
+ ),
+ CONCAT('user', N, '@company.com'),
+ CONCAT('+82-', FLOOR(10 + RAND() * 90), '-', FLOOR(1000 + RAND() * 9000), '-', FLOOR(1000 + RAND() * 9000)),
+ IF(N <= 50, 'Seoul', 'New York'),
+ IF(N <= 50, 'Seoul', 'NY'),
+ IF(N <= 50, '๋ํ๋ฏผ๊ตญ', 'USA'),
+ ELT(MOD(N, 3) + 1, 'Regular', 'Premium', 'VIP'),
+ (FLOOR(10 + RAND() * 90) * 1000000),
+ 1
+FROM Numbers;
+```
+
+#### PostgreSQL
+```sql
+-- generate_series ํ์ฉ
+INSERT INTO Customers (CustomerCode, CustomerName, ContactName, Email, Phone, City, Region, Country, CustomerType, CreditLimit, IsActive)
+SELECT
+ 'CUST' || LPAD(N::TEXT, 3, '0'),
+ CASE WHEN N <= 50
+ THEN 'ํ๊ตญํ์ฌ' || N::TEXT
+ ELSE 'Company ' || N::TEXT
+ END,
+ CASE WHEN N <= 50
+ THEN '๋ด๋น์' || N::TEXT
+ ELSE 'Contact ' || N::TEXT
+ END,
+ 'user' || N::TEXT || '@company.com',
+ '+82-10-' || (1000 + FLOOR(RANDOM() * 9000))::TEXT,
+ CASE WHEN N <= 50 THEN 'Seoul' ELSE 'New York' END,
+ CASE WHEN N <= 50 THEN 'Seoul' ELSE 'NY' END,
+ CASE WHEN N <= 50 THEN '๋ํ๋ฏผ๊ตญ' ELSE 'USA' END,
+ (ARRAY['Regular', 'Premium', 'VIP'])[1 + MOD(N, 3)],
+ (10 + FLOOR(RANDOM() * 90)) * 1000000,
+ TRUE
+FROM generate_series(11, 100) AS N;
+```
+
+### ์ต์
3: ์๋ SQL ์์ฑ
+
+`insert_sample_data.sql` ํ์ผ์ ์ด์ด์ ๊ธฐ์กด ํจํด์ ๋ณต์ฌํ์ฌ ํ์ฅ:
+
+```sql
+-- ๊ธฐ์กด CUST001-CUST010์ ๋ณต์ฌํ์ฌ
+-- CUST011-CUST050 (ํ๊ธ) ์์ฑ
+-- CUST051-CUST100 (์์ด) ์์ฑ
+```
+
+## ๐ ๊ถ์ฅ ์ฌํญ
+
+### ํ
์คํธ์ฉ (10-20๊ฑด)
+- ๋น ๋ฅธ ํ
์คํธ์ ์ ํฉ
+- ํ์ฌ ์ ๊ณต๋๋ ๊ธฐ๋ณธ ์คํฌ๋ฆฝํธ ์ฌ์ฉ
+
+### ๊ฐ๋ฐ์ฉ (50-100๊ฑด)
+- ์ค์ ๊ฐ๋ฐ ํ๊ฒฝ ์๋ฎฌ๋ ์ด์
+- ์ต์
2 (DB ๋ด ์์ฑ) ์ถ์ฒ
+
+### ๋ถํ ํ
์คํธ์ฉ (1000๊ฑด+)
+- ์ฑ๋ฅ ํ
์คํธ
+- Python ์คํฌ๋ฆฝํธ ๋๋ ์ ์ฉ ๋๊ตฌ ์ฌ์ฉ
+
+## ๐ก ๋ค์ ๋จ๊ณ
+
+1. **์ฆ์ ์ฌ์ฉ**: ํ์ฌ ๊ธฐ๋ณธ ์คํฌ๋ฆฝํธ (10-15๊ฑด) ์ฌ์ฉ
+2. **ํ์ฅ ํ์**: ์์ SQL ํจํด์ผ๋ก DB์์ ์ง์ ์์ฑ
+3. **์๋ํ ํ์**: Python ์คํฌ๋ฆฝํธ ์์ฑ (ํ์ฌ ๋ถ๋ถ ๊ตฌํ)
+
+## ๐ ๊ด๋ จ ํ์ผ
+
+- `README_DATABASE_SCRIPTS.md` - ์ ์ฒด ์คํฌ๋ฆฝํธ ๊ฐ์ด๋
+- `README_SAMPLE_DATA.md` - ๋ฐ์ดํฐ ์์ฑ ์์ธ ๊ฐ์ด๋
+- `generate_sample_data.py` - Python ์์ฑ ์คํฌ๋ฆฝํธ
+
+---
+
+**์ฐธ๊ณ **: ์ค์ ํ๋ก๋์
์์๋ 10-20๊ฑด์ ์ํ ๋ฐ์ดํฐ๋ก๋ ์ถฉ๋ถํ ํ
์คํธ ๊ฐ๋ฅํฉ๋๋ค.
+100๊ฑด ์ด์์ด ํ์ํ ๊ฒฝ์ฐ ์์ SQL ํจํด์ ์ฌ์ฉํ์๋ฉด ์ฝ๊ฒ ์์ฑํ ์ ์์ต๋๋ค.
+
diff --git a/resources/README_DATABASE_SCRIPTS.md b/resources/README_DATABASE_SCRIPTS.md
new file mode 100644
index 0000000..f387d6e
--- /dev/null
+++ b/resources/README_DATABASE_SCRIPTS.md
@@ -0,0 +1,259 @@
+# Database Sample Scripts Guide
+
+๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ณ ์ํ ํ
์ด๋ธ ์์ฑ ๋ฐ ๋ฐ์ดํฐ ์
๋ ฅ ์คํฌ๋ฆฝํธ์
๋๋ค.
+
+## ๐ ํ์ผ ๊ตฌ์กฐ
+
+```
+resources/
+โโโ README_DATABASE_SCRIPTS.md (์ด ํ์ผ)
+โ
+โโโ MSSQL (SQL Server)
+โ โโโ create_sample_tables.sql ํ
์ด๋ธ ์์ฑ
+โ โโโ insert_sample_data.sql ์ํ ๋ฐ์ดํฐ ์
๋ ฅ
+โ โโโ drop_sample_tables.sql ํ
์ด๋ธ ์ญ์
+โ
+โโโ MySQL / MariaDB
+โ โโโ create_sample_tables_mysql.sql ํ
์ด๋ธ ์์ฑ
+โ โโโ insert_sample_data_mysql.sql ์ํ ๋ฐ์ดํฐ ์
๋ ฅ
+โ โโโ drop_sample_tables_mysql.sql ํ
์ด๋ธ ์ญ์
+โ
+โโโ PostgreSQL
+โ โโโ create_sample_tables_postgresql.sql ํ
์ด๋ธ ์์ฑ
+โ โโโ insert_sample_data_postgresql.sql ์ํ ๋ฐ์ดํฐ ์
๋ ฅ
+โ โโโ drop_sample_tables_postgresql.sql ํ
์ด๋ธ ์ญ์
+โ
+โโโ SQLite
+ โโโ create_sample_tables_sqlite.sql ํ
์ด๋ธ ์์ฑ
+ โโโ insert_sample_data_sqlite.sql ์ํ ๋ฐ์ดํฐ ์
๋ ฅ
+ โโโ drop_sample_tables_sqlite.sql ํ
์ด๋ธ ์ญ์
+```
+
+## ๐๏ธ ํ
์ด๋ธ ๊ตฌ์กฐ
+
+๋ชจ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋์ผํ 5๊ฐ ํ
์ด๋ธ์ด ์์ฑ๋ฉ๋๋ค:
+
+1. **Customers** - ๊ณ ๊ฐ ์ ๋ณด (10๊ฑด)
+2. **Products** - ์ ํ ์ ๋ณด (15๊ฑด)
+3. **Employees** - ์ง์ ์ ๋ณด (10๊ฑด)
+4. **Orders** - ์ฃผ๋ฌธ ์ ๋ณด (10๊ฑด)
+5. **OrderDetails** - ์ฃผ๋ฌธ ์์ธ (22๊ฑด)
+
+## ๐ ์ฌ์ฉ ๋ฐฉ๋ฒ
+
+### 1. MSSQL (SQL Server)
+
+#### SSMS (SQL Server Management Studio) ์ฌ์ฉ:
+```sql
+-- 1. ํ
์ด๋ธ ์์ฑ
+:r create_sample_tables.sql
+
+-- 2. ๋ฐ์ดํฐ ์
๋ ฅ
+:r insert_sample_data.sql
+
+-- 3. ํ
์ด๋ธ ์ญ์ (ํ์์)
+:r drop_sample_tables.sql
+```
+
+#### sqlcmd ๋ช
๋ น์ค ์ฌ์ฉ:
+```bash
+# ํ
์ด๋ธ ์์ฑ
+sqlcmd -S localhost -U sa -P yourpassword -i create_sample_tables.sql
+
+# ๋ฐ์ดํฐ ์
๋ ฅ
+sqlcmd -S localhost -U sa -P yourpassword -i insert_sample_data.sql
+
+# ํ
์ด๋ธ ์ญ์
+sqlcmd -S localhost -U sa -P yourpassword -i drop_sample_tables.sql
+```
+
+---
+
+### 2. MySQL / MariaDB
+
+#### MySQL Workbench ๋๋ ๋ช
๋ น์ค ์ฌ์ฉ:
+```bash
+# ํ
์ด๋ธ ์์ฑ
+mysql -u root -p < create_sample_tables_mysql.sql
+
+# ๋ฐ์ดํฐ ์
๋ ฅ
+mysql -u root -p < insert_sample_data_mysql.sql
+
+# ํ
์ด๋ธ ์ญ์
+mysql -u root -p < drop_sample_tables_mysql.sql
+```
+
+#### MySQL ํด๋ผ์ด์ธํธ ๋ด๋ถ์์:
+```sql
+SOURCE create_sample_tables_mysql.sql;
+SOURCE insert_sample_data_mysql.sql;
+SOURCE drop_sample_tables_mysql.sql;
+```
+
+---
+
+### 3. PostgreSQL
+
+#### psql ์ฌ์ฉ:
+```bash
+# ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ (์ฒ์ ํ ๋ฒ๋ง)
+createdb sampledb
+
+# psql ์ ์
+psql -U postgres -d sampledb
+
+# psql ๋ด๋ถ์์ ์คํ
+\i create_sample_tables_postgresql.sql
+\i insert_sample_data_postgresql.sql
+\i drop_sample_tables_postgresql.sql
+```
+
+#### ๋ช
๋ น์ค์์ ์ง์ ์คํ:
+```bash
+# ํ
์ด๋ธ ์์ฑ
+psql -U postgres -d sampledb -f create_sample_tables_postgresql.sql
+
+# ๋ฐ์ดํฐ ์
๋ ฅ
+psql -U postgres -d sampledb -f insert_sample_data_postgresql.sql
+
+# ํ
์ด๋ธ ์ญ์
+psql -U postgres -d sampledb -f drop_sample_tables_postgresql.sql
+```
+
+---
+
+### 4. SQLite
+
+#### SQLite ๋ช
๋ น์ค ์ฌ์ฉ:
+```bash
+# ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์ผ ์์ฑ ๋ฐ ํ
์ด๋ธ ์์ฑ
+sqlite3 sampledb.sqlite < create_sample_tables_sqlite.sql
+
+# ๋ฐ์ดํฐ ์
๋ ฅ
+sqlite3 sampledb.sqlite < insert_sample_data_sqlite.sql
+
+# ํ
์ด๋ธ ์ญ์
+sqlite3 sampledb.sqlite < drop_sample_tables_sqlite.sql
+```
+
+#### SQLite ํด๋ผ์ด์ธํธ ๋ด๋ถ์์:
+```bash
+# SQLite ์คํ
+sqlite3 sampledb.sqlite
+
+# ๋ด๋ถ์์ ์คํ
+.read create_sample_tables_sqlite.sql
+.read insert_sample_data_sqlite.sql
+.read drop_sample_tables_sqlite.sql
+```
+
+---
+
+## ๐ ์ธ๋ ํค ๊ด๊ณ
+
+```
+Customers (1) -----> (N) Orders
+ |
+ | (1)
+ |
+ v
+ (N) OrderDetails (N) <----- (1) Products
+
+Employees (1) -----> (N) Employees (ReportsTo - ์๊ธฐ์ฐธ์กฐ)
+```
+
+## ๐ ์ํ ๋ฐ์ดํฐ ํต๊ณ
+
+| ํ
์ด๋ธ | ๋ ์ฝ๋ ์ | ์ค๋ช
|
+|--------|----------|------|
+| Customers | 10 | ๋ค์ํ ์ง์ญ์ ๊ณ ๊ฐ |
+| Products | 15 | ์ ์์ ํ, ์ฌ๋ฌด๊ธฐ๊ธฐ, ๊ฐ๊ตฌ ๋ฑ |
+| Employees | 10 | ์กฐ์ง ๊ณ์ธต ๊ตฌ์กฐ ํฌํจ |
+| Orders | 10 | 2024๋
1-2์ ์ฃผ๋ฌธ |
+| OrderDetails | 22 | ์ฃผ๋ฌธ๋ณ ์์ธ ๋ด์ญ |
+
+## ๐ฏ SQL2Excel ํ
์คํธ ์ฟผ๋ฆฌ ์์
+
+### ๊ณ ๊ฐ๋ณ ์ฃผ๋ฌธ ํต๊ณ
+```sql
+-- MySQL/MariaDB/PostgreSQL
+SELECT
+ c.CustomerName,
+ c.City,
+ COUNT(o.OrderID) as OrderCount,
+ SUM(o.NetAmount) as TotalAmount
+FROM Customers c
+LEFT JOIN Orders o ON c.CustomerID = o.CustomerID
+GROUP BY c.CustomerID, c.CustomerName, c.City
+ORDER BY TotalAmount DESC;
+
+-- MSSQL
+SELECT TOP 10
+ c.CustomerName,
+ c.City,
+ COUNT(o.OrderID) as OrderCount,
+ SUM(o.NetAmount) as TotalAmount
+FROM Customers c
+LEFT JOIN Orders o ON c.CustomerID = o.CustomerID
+GROUP BY c.CustomerID, c.CustomerName, c.City
+ORDER BY TotalAmount DESC;
+```
+
+### ์ ํ๋ณ ํ๋งค ํต๊ณ
+```sql
+SELECT
+ p.ProductName,
+ p.Category,
+ SUM(od.Quantity) as TotalSold,
+ SUM(od.LineTotal) as TotalRevenue
+FROM Products p
+LEFT JOIN OrderDetails od ON p.ProductID = od.ProductID
+GROUP BY p.ProductID, p.ProductName, p.Category
+ORDER BY TotalRevenue DESC
+LIMIT 10; -- MySQL/MariaDB/PostgreSQL/SQLite
+```
+
+### ๋ถ์๋ณ ์ง์ ํต๊ณ
+```sql
+SELECT
+ Department,
+ COUNT(*) as EmployeeCount,
+ AVG(Salary) as AvgSalary,
+ MIN(Salary) as MinSalary,
+ MAX(Salary) as MaxSalary
+FROM Employees
+WHERE IsActive = TRUE -- PostgreSQL/MySQL/MariaDB
+-- WHERE IsActive = 1 -- MSSQL/SQLite
+GROUP BY Department
+ORDER BY EmployeeCount DESC;
+```
+
+## ๐ก Tips
+
+1. **์ด๊ธฐํ ์์**: DROP โ CREATE โ INSERT
+2. **์ธ๋ ํค**: ์ญ์ ์ OrderDetails โ Orders โ Customers ์์ ์ ์ง
+3. **์ธ์ฝ๋ฉ**: UTF-8 ์ฌ์ฉ ๊ถ์ฅ
+4. **๋ ์ง ํ์**:
+ - MSSQL: `datetime2(7)`
+ - MySQL/MariaDB: `DATETIME`
+ - PostgreSQL: `TIMESTAMP`
+ - SQLite: `TEXT` (ISO format)
+
+## ๐ ๋น ๋ฅธ ์์
+
+```bash
+# 1. MySQL ์์
+mysql -u root -p < create_sample_tables_mysql.sql
+mysql -u root -p < insert_sample_data_mysql.sql
+
+# 2. SQL2Excel๋ก ํ
์คํธ
+node src/excel-cli.js export --xml ./queries/mysql-test-queries.xml
+
+# 3. ์ ๋ฆฌ
+mysql -u root -p < drop_sample_tables_mysql.sql
+```
+
+---
+
+**Note**: ๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ค์ ํ์ผ(`config/dbinfo.json`)๋ ํจ๊ป ํ์ธํ์ธ์!
+
diff --git a/resources/README_SAMPLE_DATA.md b/resources/README_SAMPLE_DATA.md
new file mode 100644
index 0000000..6ad58f3
--- /dev/null
+++ b/resources/README_SAMPLE_DATA.md
@@ -0,0 +1,172 @@
+# ์ํ ๋ฐ์ดํฐ ์์ฑ ๊ฐ์ด๋
+
+## ๐ ํ์ฌ ์ํ ๋ฐ์ดํฐ
+
+๊ธฐ๋ณธ ์ ๊ณต๋๋ ์ํ ๋ฐ์ดํฐ:
+- **Customers**: 10๊ฑด (ํ๊ธ)
+- **Products**: 15๊ฑด (ํ๊ธ)
+- **Employees**: 10๊ฑด (ํ๊ธ)
+- **Orders**: 10๊ฑด
+- **OrderDetails**: 22๊ฑด
+
+## ๐ 100๊ฑด ์ํ ๋ฐ์ดํฐ ์์ฑ ๋ฐฉ๋ฒ
+
+### ๋ฐฉ๋ฒ 1: Python ์คํฌ๋ฆฝํธ ์ฌ์ฉ (์ถ์ฒ)
+
+```bash
+# Python ์คํฌ๋ฆฝํธ ์คํ
+python resources/generate_sample_data.py > resources/insert_sample_data_100.sql
+
+# ์์ฑ๋ SQL ํ์ผ ์คํ
+# MSSQL
+sqlcmd -S localhost -U sa -P yourpassword -i resources/insert_sample_data_100.sql
+
+# MySQL
+mysql -u root -p < resources/insert_sample_data_100_mysql.sql
+
+# PostgreSQL
+psql -U postgres -d sampledb -f resources/insert_sample_data_100_postgresql.sql
+```
+
+### ๋ฐฉ๋ฒ 2: ์ง์ SQL ์์ฑ
+
+์ํ ๋ฐ์ดํฐ ํจํด์ ์ฐธ๊ณ ํ์ฌ ์ง์ ์์ฑ:
+
+#### ํ๊ธ ๋ฐ์ดํฐ (50๊ฑด)
+```sql
+-- ๊ณ ๊ฐ
+('CUST001', '(์ฃผ)ํ๊ตญ์ ์', '๊น์ฒ ์', ...),
+('CUST002', '์์ธ๋ฌด์ญ์์ฌ', '์ด์ํฌ', ...),
+...
+('CUST050', '์ธ์ฒ์ค๋งํธ์ํฐ', 'ํ์์ง', ...)
+```
+
+#### ์์ด ๋ฐ์ดํฐ (50๊ฑด)
+```sql
+-- ๊ณ ๊ฐ
+('CUST051', 'Tech Solutions Inc', 'John Smith', ...),
+('CUST052', 'Global Trading Co', 'Emily Johnson', ...),
+...
+('CUST100', 'Defense Systems', 'Nora Bell', ...)
+```
+
+### ๋ฐฉ๋ฒ 3: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ด ์์ฑ
+
+#### MSSQL
+```sql
+-- Numbers ํ
์ด๋ธ ์์ฑ (1-100)
+WITH Numbers AS (
+ SELECT TOP 100 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS N
+ FROM sys.objects, sys.columns
+)
+INSERT INTO Customers (CustomerCode, CustomerName, ContactName, Email, ...)
+SELECT
+ 'CUST' + RIGHT('000' + CAST(N AS VARCHAR), 3),
+ CASE WHEN N <= 50
+ THEN 'ํ๊ตญํ์ฌ' + CAST(N AS VARCHAR)
+ ELSE 'Company ' + CAST(N AS VARCHAR)
+ END,
+ ...
+FROM Numbers
+```
+
+#### MySQL/MariaDB
+```sql
+-- ์ฌ๊ท CTE ์ฌ์ฉ
+WITH RECURSIVE Numbers AS (
+ SELECT 1 AS N
+ UNION ALL
+ SELECT N + 1 FROM Numbers WHERE N < 100
+)
+INSERT INTO Customers (CustomerCode, CustomerName, ...)
+SELECT
+ CONCAT('CUST', LPAD(N, 3, '0')),
+ IF(N <= 50,
+ CONCAT('ํ๊ตญํ์ฌ', N),
+ CONCAT('Company ', N)
+ ),
+ ...
+FROM Numbers;
+```
+
+#### PostgreSQL
+```sql
+-- generate_series ์ฌ์ฉ
+INSERT INTO Customers (CustomerCode, CustomerName, ContactName, ...)
+SELECT
+ 'CUST' || LPAD(N::TEXT, 3, '0'),
+ CASE WHEN N <= 50
+ THEN 'ํ๊ตญํ์ฌ' || N::TEXT
+ ELSE 'Company ' || N::TEXT
+ END,
+ ...
+FROM generate_series(1, 100) AS N;
+```
+
+## ๐ Python ์คํฌ๋ฆฝํธ ์์ธ
+
+`generate_sample_data.py` ์คํฌ๋ฆฝํธ๋ ๋ค์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค:
+
+### ๊ธฐ๋ฅ
+1. **100๊ฑด ๊ณ ๊ฐ ๋ฐ์ดํฐ** (ํ๊ธ 50 + ์์ด 50)
+2. **100๊ฑด ์ ํ ๋ฐ์ดํฐ** (ํ๊ธ 50 + ์์ด 50)
+3. **50๊ฑด ์ง์ ๋ฐ์ดํฐ** (ํ๊ธ 25 + ์์ด 25)
+4. **100๊ฑด ์ฃผ๋ฌธ ๋ฐ์ดํฐ** (๋ ์ง ๋ถ์ฐ)
+5. **200-300๊ฑด ์ฃผ๋ฌธ ์์ธ** (์ฃผ๋ฌธ๋น 2-3๊ฐ)
+
+### ์ฌ์ฉ ์์
+```bash
+# ์ ์ฒด ๋ฐ์ดํฐ ์์ฑ
+python resources/generate_sample_data.py --all --output mssql
+
+# ๊ณ ๊ฐ๋ง ์์ฑ
+python resources/generate_sample_data.py --customers 100 --lang mixed
+
+# ํน์ DB์ฉ ์์ฑ
+python resources/generate_sample_data.py --db mysql --count 100
+
+# ํ๊ธ๋ง ๋๋ ์์ด๋ง
+python resources/generate_sample_data.py --customers 100 --lang korean
+python resources/generate_sample_data.py --customers 100 --lang english
+```
+
+## ๐ฏ ๋ฐ์ดํฐ ์์ฑ ํจํด
+
+### ํ๊ธ ๋ฐ์ดํฐ (1-50)
+- **ํ์ฌ๋ช
**: ํ๊ตญ์ ์, ์์ธ๋ฌด์ญ, ๋ถ์ฐ์ฐ์
, ...
+- **๋ด๋น์**: ๊น์ฒ ์, ์ด์ํฌ, ๋ฐ๋ฏผ์, ...
+- **์ง์ญ**: ์์ธ, ๋ถ์ฐ, ๋๊ตฌ, ์ธ์ฒ, ๊ด์ฃผ, ๋์ , ์ธ์ฐ, ์ ์ฃผ
+- **์ ํ**: 02-XXXX-XXXX, 051-XXXX-XXXX
+
+### ์์ด ๋ฐ์ดํฐ (51-100)
+- **Company**: Tech Solutions Inc, Global Trading Co, ...
+- **Contact**: John Smith, Emily Johnson, ...
+- **Location**: San Francisco, New York, London, ...
+- **Phone**: +1-555-XXXX, +44-20-XXXX
+
+## ๐ก Tips
+
+1. **๋๋ ๋ฐ์ดํฐ ์์ฑ ์ ์ฑ๋ฅ**
+ - BULK INSERT ์ฌ์ฉ
+ - ์ธ๋ฑ์ค ๋นํ์ฑํ ํ ๋ฐ์ดํฐ ์
๋ ฅ
+ - ํธ๋์ญ์
๋ฐฐ์น ์ฒ๋ฆฌ
+
+2. **ํ์ค์ ์ธ ๋ฐ์ดํฐ**
+ - ๋ ์ง ๋ถ์ฐ (์ต๊ทผ 6๊ฐ์)
+ - ์ง์ญ๋ณ ๋ถํฌ ๊ณ ๋ ค
+ - ๊ณ ๊ฐ ๋ฑ๊ธ ๋น์จ (VIP 10%, Premium 30%, Regular 60%)
+
+3. **์ฐธ์กฐ ๋ฌด๊ฒฐ์ฑ**
+ - ์ธ๋ ํค ๊ด๊ณ ์ ์ง
+ - ์์: Customers โ Products โ Orders โ OrderDetails
+
+## ๐ ์ฐธ๊ณ
+
+- ๊ธฐ๋ณธ ์ํ ๋ฐ์ดํฐ: `insert_sample_data.sql` (๊ฐ DB๋ณ)
+- ํ
์ด๋ธ ์คํค๋ง: `create_sample_tables.sql` (๊ฐ DB๋ณ)
+- ๋ฐ์ดํฐ ์ญ์ : `drop_sample_tables.sql` (๊ฐ DB๋ณ)
+
+---
+
+**Note**: Python ์คํฌ๋ฆฝํธ๋ Python 3.6 ์ด์์ด ํ์ํฉ๋๋ค.
+
diff --git a/resources/create_sample_tables.sql b/resources/create_sample_tables.sql
deleted file mode 100644
index 7a8f34c..0000000
--- a/resources/create_sample_tables.sql
+++ /dev/null
@@ -1,182 +0,0 @@
--- ========================================
--- ์ํ ํ
์ด๋ธ ์์ฑ ์คํฌ๋ฆฝํธ
--- Microsoft SQL Server์ฉ
--- ========================================
---
--- ์๊ฐ ์ค์ ์ฐธ๊ณ :
--- SQL Server์์ GETDATE()๋ ์๋ฒ์ ํ์ง ์๊ฐ์ ๋ฐํํฉ๋๋ค.
--- ํ๊ตญ ํ์ค์(KST)๋ก ์ค์ ํ๋ ค๋ฉด ์๋ฒ ํ์์กด์ ํ์ธํ์ธ์.
---
--- ========================================
-
-USE [master]
-GO
-
--- ์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ (์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ)
-IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'SampleDB')
-BEGIN
- CREATE DATABASE [SampleDB]
-END
-GO
-
-USE [SampleDB]
-GO
-
--- ========================================
--- 1. Customers ํ
์ด๋ธ ์์ฑ
--- ========================================
-
--- ๊ธฐ์กด ํ
์ด๋ธ์ด ์์ผ๋ฉด ์ญ์
-IF OBJECT_ID('dbo.Customers', 'U') IS NOT NULL
- DROP TABLE dbo.Customers
-GO
-
-CREATE TABLE dbo.Customers (
- CustomerID int IDENTITY(1,1) NOT NULL,
- CustomerCode nvarchar(20) NOT NULL,
- CustomerName nvarchar(100) NOT NULL,
- ContactName nvarchar(50) NULL,
- Email nvarchar(100) NULL,
- Phone nvarchar(20) NULL,
- Address nvarchar(200) NULL,
- City nvarchar(50) NULL,
- Region nvarchar(50) NULL,
- PostalCode nvarchar(10) NULL,
- Country nvarchar(50) NULL,
- CustomerType nvarchar(20) DEFAULT 'Regular',
- CreditLimit decimal(15,2) DEFAULT 0,
- IsActive bit DEFAULT 1,
- CreatedDate datetime2(7) DEFAULT GETDATE(),
- LastUpdated datetime2(7) DEFAULT GETDATE(),
-
- CONSTRAINT PK_Customers PRIMARY KEY CLUSTERED (CustomerID ASC),
- CONSTRAINT UK_Customers_Code UNIQUE (CustomerCode)
-)
-GO
-
--- ์ธ๋ฑ์ค ์์ฑ
-CREATE NONCLUSTERED INDEX IX_Customers_Name ON dbo.Customers (CustomerName)
-CREATE NONCLUSTERED INDEX IX_Customers_City ON dbo.Customers (City)
-CREATE NONCLUSTERED INDEX IX_Customers_Region ON dbo.Customers (Region)
-CREATE NONCLUSTERED INDEX IX_Customers_Type ON dbo.Customers (CustomerType)
-GO
-
--- ========================================
--- 2. Orders ํ
์ด๋ธ ์์ฑ
--- ========================================
-
--- ๊ธฐ์กด ํ
์ด๋ธ์ด ์์ผ๋ฉด ์ญ์
-IF OBJECT_ID('dbo.Orders', 'U') IS NOT NULL
- DROP TABLE dbo.Orders
-GO
-
-CREATE TABLE dbo.Orders (
- OrderID int IDENTITY(1,1) NOT NULL,
- OrderNumber nvarchar(30) NOT NULL,
- CustomerID int NOT NULL,
- OrderDate datetime2(7) NOT NULL,
- RequiredDate datetime2(7) NULL,
- ShippedDate datetime2(7) NULL,
- OrderStatus nvarchar(20) DEFAULT 'Pending',
- ShipVia nvarchar(50) NULL,
- Freight decimal(10,2) DEFAULT 0,
- ShipName nvarchar(100) NULL,
- ShipAddress nvarchar(200) NULL,
- ShipCity nvarchar(50) NULL,
- ShipRegion nvarchar(50) NULL,
- ShipPostalCode nvarchar(10) NULL,
- ShipCountry nvarchar(50) NULL,
- SubTotal decimal(15,2) DEFAULT 0,
- TaxAmount decimal(10,2) DEFAULT 0,
- TotalAmount decimal(15,2) DEFAULT 0,
- PaymentMethod nvarchar(30) NULL,
- PaymentStatus nvarchar(20) DEFAULT 'Unpaid',
- EmployeeID int NULL,
- Notes nvarchar(500) NULL,
- CreatedDate datetime2(7) DEFAULT GETDATE(),
- LastUpdated datetime2(7) DEFAULT GETDATE(),
-
- CONSTRAINT PK_Orders PRIMARY KEY CLUSTERED (OrderID ASC),
- CONSTRAINT UK_Orders_Number UNIQUE (OrderNumber),
- CONSTRAINT FK_Orders_Customers FOREIGN KEY (CustomerID)
- REFERENCES dbo.Customers (CustomerID)
-)
-GO
-
--- ์ธ๋ฑ์ค ์์ฑ
-CREATE NONCLUSTERED INDEX IX_Orders_CustomerID ON dbo.Orders (CustomerID)
-CREATE NONCLUSTERED INDEX IX_Orders_OrderDate ON dbo.Orders (OrderDate)
-CREATE NONCLUSTERED INDEX IX_Orders_Status ON dbo.Orders (OrderStatus)
-CREATE NONCLUSTERED INDEX IX_Orders_PaymentStatus ON dbo.Orders (PaymentStatus)
-CREATE NONCLUSTERED INDEX IX_Orders_ShippedDate ON dbo.Orders (ShippedDate)
-GO
-
--- ========================================
--- 3. OrderDetails ํ
์ด๋ธ ์์ฑ (์ถ๊ฐ)
--- ========================================
-
--- ๊ธฐ์กด ํ
์ด๋ธ์ด ์์ผ๋ฉด ์ญ์
-IF OBJECT_ID('dbo.OrderDetails', 'U') IS NOT NULL
- DROP TABLE dbo.OrderDetails
-GO
-
-CREATE TABLE dbo.OrderDetails (
- OrderDetailID int IDENTITY(1,1) NOT NULL,
- OrderID int NOT NULL,
- ProductCode nvarchar(30) NOT NULL,
- ProductName nvarchar(100) NOT NULL,
- UnitPrice decimal(10,2) NOT NULL,
- Quantity int NOT NULL,
- Discount decimal(5,2) DEFAULT 0,
- LineTotal AS (UnitPrice * Quantity * (1 - Discount/100)) PERSISTED,
-
- CONSTRAINT PK_OrderDetails PRIMARY KEY CLUSTERED (OrderDetailID ASC),
- CONSTRAINT FK_OrderDetails_Orders FOREIGN KEY (OrderID)
- REFERENCES dbo.Orders (OrderID) ON DELETE CASCADE
-)
-GO
-
--- ์ธ๋ฑ์ค ์์ฑ
-CREATE NONCLUSTERED INDEX IX_OrderDetails_OrderID ON dbo.OrderDetails (OrderID)
-CREATE NONCLUSTERED INDEX IX_OrderDetails_ProductCode ON dbo.OrderDetails (ProductCode)
-GO
-
--- ========================================
--- 4. ๋ทฐ ์์ฑ (์ฃผ๋ฌธ ์์ฝ)
--- ========================================
-
-IF OBJECT_ID('dbo.vw_OrderSummary', 'V') IS NOT NULL
- DROP VIEW dbo.vw_OrderSummary
-GO
-
-CREATE VIEW dbo.vw_OrderSummary
-AS
-SELECT
- o.OrderID,
- o.OrderNumber,
- o.OrderDate,
- c.CustomerCode,
- c.CustomerName,
- c.City as CustomerCity,
- c.Region as CustomerRegion,
- o.OrderStatus,
- o.PaymentStatus,
- o.TotalAmount,
- COUNT(od.OrderDetailID) as ItemCount,
- SUM(od.Quantity) as TotalQuantity,
- o.CreatedDate
-FROM dbo.Orders o
- INNER JOIN dbo.Customers c ON o.CustomerID = c.CustomerID
- LEFT JOIN dbo.OrderDetails od ON o.OrderID = od.OrderID
-GROUP BY
- o.OrderID, o.OrderNumber, o.OrderDate,
- c.CustomerCode, c.CustomerName, c.City, c.Region,
- o.OrderStatus, o.PaymentStatus, o.TotalAmount, o.CreatedDate
-GO
-
-PRINT 'ํ
์ด๋ธ ์์ฑ์ด ์๋ฃ๋์์ต๋๋ค.'
-PRINT '- Customers ํ
์ด๋ธ'
-PRINT '- Orders ํ
์ด๋ธ'
-PRINT '- OrderDetails ํ
์ด๋ธ'
-PRINT '- vw_OrderSummary ๋ทฐ'
-GO
\ No newline at end of file
diff --git a/resources/create_sample_tables_mssql.sql b/resources/create_sample_tables_mssql.sql
new file mode 100644
index 0000000..0a2e2e6
--- /dev/null
+++ b/resources/create_sample_tables_mssql.sql
@@ -0,0 +1,242 @@
+-- ========================================
+-- ์ํ ํ
์ด๋ธ ์์ฑ ์คํฌ๋ฆฝํธ
+-- Microsoft SQL Server ์ฉ (SQL Server 2012+ ๊ถ์ฅ)
+-- ========================================
+
+SET NOCOUNT ON;
+
+-- ๊ธฐ์กด ๊ฐ์ฒด ์ญ์ (์กด์ฌ ์)
+IF OBJECT_ID('dbo.vw_OrderSummary', 'V') IS NOT NULL DROP VIEW dbo.vw_OrderSummary;
+IF OBJECT_ID('dbo.OrderDetails', 'U') IS NOT NULL DROP TABLE dbo.OrderDetails;
+IF OBJECT_ID('dbo.Orders', 'U') IS NOT NULL DROP TABLE dbo.Orders;
+IF OBJECT_ID('dbo.Products', 'U') IS NOT NULL DROP TABLE dbo.Products;
+IF OBJECT_ID('dbo.Employees', 'U') IS NOT NULL DROP TABLE dbo.Employees;
+IF OBJECT_ID('dbo.Customers', 'U') IS NOT NULL DROP TABLE dbo.Customers;
+GO
+
+-- ========================================
+-- 1. Customers ํ
์ด๋ธ
+-- ========================================
+CREATE TABLE dbo.Customers (
+ CustomerID INT IDENTITY(1,1) NOT NULL,
+ CustomerCode VARCHAR(20) NOT NULL,
+ CustomerName VARCHAR(100) NOT NULL,
+ ContactName VARCHAR(50) NULL,
+ Email VARCHAR(100) NULL,
+ Phone VARCHAR(20) NULL,
+ Address VARCHAR(200) NULL,
+ City VARCHAR(50) NULL,
+ Region VARCHAR(50) NULL,
+ PostalCode VARCHAR(10) NULL,
+ Country VARCHAR(50) NULL,
+ CustomerType VARCHAR(20) CONSTRAINT DF_Customers_CustomerType DEFAULT ('Regular'),
+ CreditLimit DECIMAL(15,2) CONSTRAINT DF_Customers_CreditLimit DEFAULT (0),
+ IsActive BIT CONSTRAINT DF_Customers_IsActive DEFAULT (1),
+ CreatedDate DATETIME2 CONSTRAINT DF_Customers_CreatedDate DEFAULT (SYSDATETIME()),
+ LastUpdated DATETIME2 CONSTRAINT DF_Customers_LastUpdated DEFAULT (SYSDATETIME()),
+ CONSTRAINT PK_Customers PRIMARY KEY (CustomerID),
+ CONSTRAINT UK_Customers_Code UNIQUE (CustomerCode)
+);
+GO
+
+CREATE INDEX IX_Customers_Name ON dbo.Customers (CustomerName);
+CREATE INDEX IX_Customers_City ON dbo.Customers (City);
+CREATE INDEX IX_Customers_Region ON dbo.Customers (Region);
+CREATE INDEX IX_Customers_Type ON dbo.Customers (CustomerType);
+GO
+
+-- LastUpdated ์๋ ์
๋ฐ์ดํธ ํธ๋ฆฌ๊ฑฐ
+IF OBJECT_ID('dbo.trg_customers_modtime', 'TR') IS NOT NULL DROP TRIGGER dbo.trg_customers_modtime;
+GO
+CREATE TRIGGER dbo.trg_customers_modtime
+ON dbo.Customers
+AFTER UPDATE
+AS
+BEGIN
+ SET NOCOUNT ON;
+ UPDATE c
+ SET LastUpdated = SYSDATETIME()
+ FROM dbo.Customers c
+ INNER JOIN inserted i ON c.CustomerID = i.CustomerID;
+END;
+GO
+
+-- ========================================
+-- 2. Orders ํ
์ด๋ธ
+-- ========================================
+CREATE TABLE dbo.Orders (
+ OrderID INT IDENTITY(1,1) NOT NULL,
+ OrderNumber VARCHAR(30) NOT NULL,
+ CustomerID INT NOT NULL,
+ OrderDate DATETIME2 NOT NULL,
+ RequiredDate DATETIME2 NULL,
+ ShippedDate DATETIME2 NULL,
+ OrderStatus VARCHAR(20) CONSTRAINT DF_Orders_OrderStatus DEFAULT ('Pending'),
+ SubTotal DECIMAL(15,2) CONSTRAINT DF_Orders_SubTotal DEFAULT (0),
+ TaxAmount DECIMAL(15,2) CONSTRAINT DF_Orders_TaxAmount DEFAULT (0),
+ TotalAmount DECIMAL(15,2) CONSTRAINT DF_Orders_TotalAmount DEFAULT (0),
+ PaymentMethod VARCHAR(30) NULL,
+ PaymentStatus VARCHAR(20) CONSTRAINT DF_Orders_PaymentStatus DEFAULT ('Unpaid'),
+ EmployeeID INT NULL,
+ Notes VARCHAR(500) NULL,
+ CreatedDate DATETIME2 CONSTRAINT DF_Orders_CreatedDate DEFAULT (SYSDATETIME()),
+ LastUpdated DATETIME2 CONSTRAINT DF_Orders_LastUpdated DEFAULT (SYSDATETIME()),
+ CONSTRAINT PK_Orders PRIMARY KEY (OrderID),
+ CONSTRAINT UK_Orders_Number UNIQUE (OrderNumber),
+ CONSTRAINT FK_Orders_Customers FOREIGN KEY (CustomerID) REFERENCES dbo.Customers(CustomerID)
+);
+GO
+
+CREATE INDEX IX_Orders_CustomerID ON dbo.Orders (CustomerID);
+CREATE INDEX IX_Orders_OrderDate ON dbo.Orders (OrderDate);
+CREATE INDEX IX_Orders_Status ON dbo.Orders (OrderStatus);
+CREATE INDEX IX_Orders_PaymentStatus ON dbo.Orders (PaymentStatus);
+CREATE INDEX IX_Orders_ShippedDate ON dbo.Orders (ShippedDate);
+GO
+
+IF OBJECT_ID('dbo.trg_orders_modtime', 'TR') IS NOT NULL DROP TRIGGER dbo.trg_orders_modtime;
+GO
+CREATE TRIGGER dbo.trg_orders_modtime
+ON dbo.Orders
+AFTER UPDATE
+AS
+BEGIN
+ SET NOCOUNT ON;
+ UPDATE o
+ SET LastUpdated = SYSDATETIME()
+ FROM dbo.Orders o
+ INNER JOIN inserted i ON o.OrderID = i.OrderID;
+END;
+GO
+
+-- ========================================
+-- 3. Products ํ
์ด๋ธ
+-- ========================================
+CREATE TABLE dbo.Products (
+ ProductID INT IDENTITY(1,1) NOT NULL,
+ ProductCode VARCHAR(20) NOT NULL,
+ ProductName VARCHAR(100) NOT NULL,
+ Category VARCHAR(50) NULL,
+ UnitPrice DECIMAL(15,2) CONSTRAINT DF_Products_UnitPrice DEFAULT (0),
+ UnitsInStock INT CONSTRAINT DF_Products_UnitsInStock DEFAULT (0),
+ UnitsOnOrder INT CONSTRAINT DF_Products_UnitsOnOrder DEFAULT (0),
+ ReorderLevel INT CONSTRAINT DF_Products_ReorderLevel DEFAULT (0),
+ Discontinued BIT CONSTRAINT DF_Products_Discontinued DEFAULT (0),
+ Description NVARCHAR(MAX) NULL,
+ CreatedDate DATETIME2 CONSTRAINT DF_Products_CreatedDate DEFAULT (SYSDATETIME()),
+ LastUpdated DATETIME2 CONSTRAINT DF_Products_LastUpdated DEFAULT (SYSDATETIME()),
+ CONSTRAINT PK_Products PRIMARY KEY (ProductID),
+ CONSTRAINT UK_Products_Code UNIQUE (ProductCode)
+);
+GO
+
+CREATE INDEX IX_Products_Name ON dbo.Products (ProductName);
+CREATE INDEX IX_Products_Category ON dbo.Products (Category);
+CREATE INDEX IX_Products_Price ON dbo.Products (UnitPrice);
+GO
+
+IF OBJECT_ID('dbo.trg_products_modtime', 'TR') IS NOT NULL DROP TRIGGER dbo.trg_products_modtime;
+GO
+CREATE TRIGGER dbo.trg_products_modtime
+ON dbo.Products
+AFTER UPDATE
+AS
+BEGIN
+ SET NOCOUNT ON;
+ UPDATE p
+ SET LastUpdated = SYSDATETIME()
+ FROM dbo.Products p
+ INNER JOIN inserted i ON p.ProductID = i.ProductID;
+END;
+GO
+
+-- ========================================
+-- 4. OrderDetails ํ
์ด๋ธ
+-- ========================================
+CREATE TABLE dbo.OrderDetails (
+ OrderDetailID INT IDENTITY(1,1) NOT NULL,
+ OrderID INT NOT NULL,
+ ProductID INT NOT NULL,
+ UnitPrice DECIMAL(15,2) NOT NULL,
+ Quantity INT NOT NULL,
+ Discount DECIMAL(5,2) CONSTRAINT DF_OrderDetails_Discount DEFAULT (0),
+ LineTotal AS (UnitPrice * Quantity * (1 - (Discount/100.0))) PERSISTED,
+ CreatedDate DATETIME2 CONSTRAINT DF_OrderDetails_CreatedDate DEFAULT (SYSDATETIME()),
+ CONSTRAINT PK_OrderDetails PRIMARY KEY (OrderDetailID),
+ CONSTRAINT FK_OrderDetails_Orders FOREIGN KEY (OrderID) REFERENCES dbo.Orders(OrderID) ON DELETE CASCADE,
+ CONSTRAINT FK_OrderDetails_Products FOREIGN KEY (ProductID) REFERENCES dbo.Products(ProductID)
+);
+GO
+
+CREATE INDEX IX_OrderDetails_OrderID ON dbo.OrderDetails (OrderID);
+CREATE INDEX IX_OrderDetails_ProductID ON dbo.OrderDetails (ProductID);
+GO
+
+-- ========================================
+-- 5. Employees ํ
์ด๋ธ
+-- ========================================
+CREATE TABLE dbo.Employees (
+ EmployeeID INT IDENTITY(1,1) NOT NULL,
+ EmployeeCode VARCHAR(20) NOT NULL,
+ FirstName VARCHAR(50) NOT NULL,
+ LastName VARCHAR(50) NOT NULL,
+ Title VARCHAR(50) NULL,
+ BirthDate DATE NULL,
+ HireDate DATE NULL,
+ Email VARCHAR(100) NULL,
+ Phone VARCHAR(20) NULL,
+ Department VARCHAR(50) NULL,
+ Salary DECIMAL(15,2) NULL,
+ ReportsTo INT NULL,
+ IsActive BIT CONSTRAINT DF_Employees_IsActive DEFAULT (1),
+ CreatedDate DATETIME2 CONSTRAINT DF_Employees_CreatedDate DEFAULT (SYSDATETIME()),
+ LastUpdated DATETIME2 CONSTRAINT DF_Employees_LastUpdated DEFAULT (SYSDATETIME()),
+ CONSTRAINT PK_Employees PRIMARY KEY (EmployeeID),
+ CONSTRAINT UK_Employees_Code UNIQUE (EmployeeCode),
+ CONSTRAINT FK_Employees_ReportsTo FOREIGN KEY (ReportsTo) REFERENCES dbo.Employees(EmployeeID)
+);
+GO
+
+CREATE INDEX IX_Employees_Name ON dbo.Employees (LastName, FirstName);
+CREATE INDEX IX_Employees_Department ON dbo.Employees (Department);
+CREATE INDEX IX_Employees_ReportsTo ON dbo.Employees (ReportsTo);
+GO
+
+IF OBJECT_ID('dbo.trg_employees_modtime', 'TR') IS NOT NULL DROP TRIGGER dbo.trg_employees_modtime;
+GO
+CREATE TRIGGER dbo.trg_employees_modtime
+ON dbo.Employees
+AFTER UPDATE
+AS
+BEGIN
+ SET NOCOUNT ON;
+ UPDATE e
+ SET LastUpdated = SYSDATETIME()
+ FROM dbo.Employees e
+ INNER JOIN inserted i ON e.EmployeeID = i.EmployeeID;
+END;
+GO
+
+-- ========================================
+-- 6. ์ฃผ๋ฌธ ์์ฝ ๋ทฐ
+-- ========================================
+CREATE VIEW dbo.vw_OrderSummary AS
+SELECT
+ o.OrderID,
+ o.OrderNumber,
+ o.OrderDate,
+ c.CustomerCode,
+ c.CustomerName,
+ c.City AS CustomerCity,
+ c.Region AS CustomerRegion,
+ o.OrderStatus,
+ o.PaymentStatus,
+ o.TotalAmount,
+ (SELECT COUNT(*) FROM dbo.OrderDetails od WHERE od.OrderID = o.OrderID) AS ItemCount,
+ (SELECT ISNULL(SUM(od.Quantity),0) FROM dbo.OrderDetails od WHERE od.OrderID = o.OrderID) AS TotalQuantity,
+ o.CreatedDate
+FROM dbo.Orders o
+JOIN dbo.Customers c ON o.CustomerID = c.CustomerID;
+GO
+
+PRINT 'MSSQL์ฉ ์ํ ํ
์ด๋ธ ์์ฑ ์๋ฃ';
diff --git a/resources/create_sample_tables_mysql.sql b/resources/create_sample_tables_mysql.sql
new file mode 100644
index 0000000..e3e4471
--- /dev/null
+++ b/resources/create_sample_tables_mysql.sql
@@ -0,0 +1,186 @@
+-- ========================================
+-- ์ํ ํ
์ด๋ธ ์์ฑ ์คํฌ๋ฆฝํธ
+-- MySQL / MariaDB์ฉ
+-- ========================================
+
+-- ์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ (์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ)
+CREATE DATABASE IF NOT EXISTS sampledb
+ DEFAULT CHARACTER SET utf8mb4
+ DEFAULT COLLATE utf8mb4_unicode_ci;
+
+USE sampledb;
+
+-- ========================================
+-- 1. Customers ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Customers;
+
+CREATE TABLE Customers (
+ CustomerID INT AUTO_INCREMENT NOT NULL,
+ CustomerCode VARCHAR(20) NOT NULL,
+ CustomerName VARCHAR(100) NOT NULL,
+ ContactName VARCHAR(50) NULL,
+ Email VARCHAR(100) NULL,
+ Phone VARCHAR(20) NULL,
+ Address VARCHAR(200) NULL,
+ City VARCHAR(50) NULL,
+ Region VARCHAR(50) NULL,
+ PostalCode VARCHAR(10) NULL,
+ Country VARCHAR(50) NULL,
+ CustomerType VARCHAR(20) DEFAULT 'Regular',
+ CreditLimit DECIMAL(15,2) DEFAULT 0,
+ IsActive BOOLEAN DEFAULT TRUE,
+ CreatedDate DATETIME DEFAULT CURRENT_TIMESTAMP,
+ LastUpdated DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+
+ PRIMARY KEY (CustomerID),
+ UNIQUE KEY UK_Customers_Code (CustomerCode),
+ INDEX IX_Customers_Name (CustomerName),
+ INDEX IX_Customers_City (City),
+ INDEX IX_Customers_Region (Region),
+ INDEX IX_Customers_Type (CustomerType)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- ========================================
+-- 2. Orders ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Orders;
+
+CREATE TABLE Orders (
+ OrderID INT AUTO_INCREMENT NOT NULL,
+ OrderNumber VARCHAR(30) NOT NULL,
+ CustomerID INT NOT NULL,
+ OrderDate DATETIME NOT NULL,
+ RequiredDate DATETIME NULL,
+ ShippedDate DATETIME NULL,
+ OrderStatus VARCHAR(20) DEFAULT 'Pending',
+ SubTotal DECIMAL(15,2) DEFAULT 0,
+ TotalAmount DECIMAL(15,2) DEFAULT 0,
+ DiscountAmount DECIMAL(15,2) DEFAULT 0,
+ TaxAmount DECIMAL(15,2) DEFAULT 0,
+ NetAmount DECIMAL(15,2) DEFAULT 0,
+ ShippingAddress VARCHAR(200) NULL,
+ PaymentMethod VARCHAR(30) NULL,
+ PaymentStatus VARCHAR(20) DEFAULT 'Unpaid',
+ EmployeeID INT NULL,
+ Notes TEXT NULL,
+ CreatedDate DATETIME DEFAULT CURRENT_TIMESTAMP,
+ LastUpdated DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+
+ PRIMARY KEY (OrderID),
+ UNIQUE KEY UK_Orders_Number (OrderNumber),
+ INDEX IX_Orders_Customer (CustomerID),
+ INDEX IX_Orders_Date (OrderDate),
+ INDEX IX_Orders_Status (OrderStatus),
+
+ CONSTRAINT FK_Orders_Customers
+ FOREIGN KEY (CustomerID)
+ REFERENCES Customers(CustomerID)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- ========================================
+-- 3. Products ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Products;
+
+CREATE TABLE Products (
+ ProductID INT AUTO_INCREMENT NOT NULL,
+ ProductCode VARCHAR(20) NOT NULL,
+ ProductName VARCHAR(100) NOT NULL,
+ Category VARCHAR(50) NULL,
+ UnitPrice DECIMAL(15,2) DEFAULT 0,
+ UnitsInStock INT DEFAULT 0,
+ UnitsOnOrder INT DEFAULT 0,
+ ReorderLevel INT DEFAULT 0,
+ Discontinued BOOLEAN DEFAULT FALSE,
+ Description TEXT NULL,
+ CreatedDate DATETIME DEFAULT CURRENT_TIMESTAMP,
+ LastUpdated DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+
+ PRIMARY KEY (ProductID),
+ UNIQUE KEY UK_Products_Code (ProductCode),
+ INDEX IX_Products_Name (ProductName),
+ INDEX IX_Products_Category (Category),
+ INDEX IX_Products_Price (UnitPrice)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- ========================================
+-- 4. OrderDetails ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS OrderDetails;
+
+CREATE TABLE OrderDetails (
+ OrderDetailID INT AUTO_INCREMENT NOT NULL,
+ OrderID INT NOT NULL,
+ ProductID INT NOT NULL,
+ UnitPrice DECIMAL(15,2) NOT NULL,
+ Quantity INT NOT NULL DEFAULT 1,
+ Discount DECIMAL(5,2) DEFAULT 0,
+ LineTotal DECIMAL(15,2) GENERATED ALWAYS AS (UnitPrice * Quantity * (1 - Discount / 100)) STORED,
+ CreatedDate DATETIME DEFAULT CURRENT_TIMESTAMP,
+
+ PRIMARY KEY (OrderDetailID),
+ INDEX IX_OrderDetails_Order (OrderID),
+ INDEX IX_OrderDetails_Product (ProductID),
+
+ CONSTRAINT FK_OrderDetails_Orders
+ FOREIGN KEY (OrderID)
+ REFERENCES Orders(OrderID)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE,
+
+ CONSTRAINT FK_OrderDetails_Products
+ FOREIGN KEY (ProductID)
+ REFERENCES Products(ProductID)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- ========================================
+-- 5. Employees ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Employees;
+
+CREATE TABLE Employees (
+ EmployeeID INT AUTO_INCREMENT NOT NULL,
+ EmployeeCode VARCHAR(20) NOT NULL,
+ FirstName VARCHAR(50) NOT NULL,
+ LastName VARCHAR(50) NOT NULL,
+ Title VARCHAR(50) NULL,
+ BirthDate DATE NULL,
+ HireDate DATE NULL,
+ Email VARCHAR(100) NULL,
+ Phone VARCHAR(20) NULL,
+ Department VARCHAR(50) NULL,
+ Salary DECIMAL(15,2) NULL,
+ ReportsTo INT NULL,
+ IsActive BOOLEAN DEFAULT TRUE,
+ CreatedDate DATETIME DEFAULT CURRENT_TIMESTAMP,
+ LastUpdated DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+
+ PRIMARY KEY (EmployeeID),
+ UNIQUE KEY UK_Employees_Code (EmployeeCode),
+ INDEX IX_Employees_Name (LastName, FirstName),
+ INDEX IX_Employees_Department (Department),
+ INDEX IX_Employees_ReportsTo (ReportsTo),
+
+ CONSTRAINT FK_Employees_ReportsTo
+ FOREIGN KEY (ReportsTo)
+ REFERENCES Employees(EmployeeID)
+ ON DELETE SET NULL
+ ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- ========================================
+-- ์๋ฃ ๋ฉ์์ง
+-- ========================================
+
+SELECT 'Sample tables created successfully for MySQL/MariaDB!' AS Message;
+
diff --git a/resources/create_sample_tables_oracle.sql b/resources/create_sample_tables_oracle.sql
new file mode 100644
index 0000000..90ccbb3
--- /dev/null
+++ b/resources/create_sample_tables_oracle.sql
@@ -0,0 +1,209 @@
+-- ========================================
+-- ์ํ ํ
์ด๋ธ ์์ฑ ์คํฌ๋ฆฝํธ
+-- Oracle Database์ฉ (12c ์ด์ ๊ถ์ฅ)
+-- ========================================
+
+-- ์ฃผ์: ์คํค๋ง๋ ํ์ฌ ์ ์ ์ฌ์ฉ์๋ก ์์ฑ๋ฉ๋๋ค. ํ์ ์ ๋ค๋ฅธ ์คํค๋ง๋ก ๋ณ๊ฒฝํ์ธ์.
+
+-- ๊ธฐ์กด ๊ฐ์ฒด ์ญ์ (์กด์ฌ ์)
+BEGIN EXECUTE IMMEDIATE 'DROP VIEW vw_OrderSummary'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+BEGIN EXECUTE IMMEDIATE 'DROP TABLE OrderDetails CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+BEGIN EXECUTE IMMEDIATE 'DROP TABLE Orders CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+BEGIN EXECUTE IMMEDIATE 'DROP TABLE Products CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+BEGIN EXECUTE IMMEDIATE 'DROP TABLE Employees CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+BEGIN EXECUTE IMMEDIATE 'DROP TABLE Customers CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+
+-- ========================================
+-- 1. Customers ํ
์ด๋ธ
+-- ========================================
+CREATE TABLE Customers (
+ CustomerID NUMBER GENERATED BY DEFAULT AS IDENTITY,
+ CustomerCode VARCHAR2(20) NOT NULL,
+ CustomerName VARCHAR2(100) NOT NULL,
+ ContactName VARCHAR2(50),
+ Email VARCHAR2(100),
+ Phone VARCHAR2(20),
+ Address VARCHAR2(200),
+ City VARCHAR2(50),
+ Region VARCHAR2(50),
+ PostalCode VARCHAR2(10),
+ Country VARCHAR2(50),
+ CustomerType VARCHAR2(20) DEFAULT 'Regular',
+ CreditLimit NUMBER(15,2) DEFAULT 0,
+ IsActive NUMBER(1) DEFAULT 1,
+ CreatedDate TIMESTAMP DEFAULT SYSTIMESTAMP,
+ LastUpdated TIMESTAMP DEFAULT SYSTIMESTAMP,
+ CONSTRAINT PK_Customers PRIMARY KEY (CustomerID),
+ CONSTRAINT UK_Customers_Code UNIQUE (CustomerCode)
+);
+
+CREATE INDEX IX_Customers_Name ON Customers (CustomerName);
+CREATE INDEX IX_Customers_City ON Customers (City);
+CREATE INDEX IX_Customers_Region ON Customers (Region);
+CREATE INDEX IX_Customers_Type ON Customers (CustomerType);
+
+-- LastUpdated ์๋ ์
๋ฐ์ดํธ ํธ๋ฆฌ๊ฑฐ
+CREATE OR REPLACE TRIGGER trg_customers_modtime
+BEFORE UPDATE ON Customers
+FOR EACH ROW
+BEGIN
+ :NEW.LastUpdated := SYSTIMESTAMP;
+END;
+/
+
+-- ========================================
+-- 2. Orders ํ
์ด๋ธ
+-- ========================================
+CREATE TABLE Orders (
+ OrderID NUMBER GENERATED BY DEFAULT AS IDENTITY,
+ OrderNumber VARCHAR2(30) NOT NULL,
+ CustomerID NUMBER NOT NULL,
+ OrderDate TIMESTAMP NOT NULL,
+ RequiredDate TIMESTAMP,
+ ShippedDate TIMESTAMP,
+ OrderStatus VARCHAR2(20) DEFAULT 'Pending',
+ SubTotal NUMBER(15,2) DEFAULT 0,
+ TaxAmount NUMBER(15,2) DEFAULT 0,
+ TotalAmount NUMBER(15,2) DEFAULT 0,
+ PaymentMethod VARCHAR2(30),
+ PaymentStatus VARCHAR2(20) DEFAULT 'Unpaid',
+ EmployeeID NUMBER,
+ Notes VARCHAR2(500),
+ CreatedDate TIMESTAMP DEFAULT SYSTIMESTAMP,
+ LastUpdated TIMESTAMP DEFAULT SYSTIMESTAMP,
+ CONSTRAINT PK_Orders PRIMARY KEY (OrderID),
+ CONSTRAINT UK_Orders_Number UNIQUE (OrderNumber),
+ CONSTRAINT FK_Orders_Customers FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
+);
+
+CREATE INDEX IX_Orders_CustomerID ON Orders (CustomerID);
+CREATE INDEX IX_Orders_OrderDate ON Orders (OrderDate);
+CREATE INDEX IX_Orders_Status ON Orders (OrderStatus);
+CREATE INDEX IX_Orders_PaymentStatus ON Orders (PaymentStatus);
+CREATE INDEX IX_Orders_ShippedDate ON Orders (ShippedDate);
+
+CREATE OR REPLACE TRIGGER trg_orders_modtime
+BEFORE UPDATE ON Orders
+FOR EACH ROW
+BEGIN
+ :NEW.LastUpdated := SYSTIMESTAMP;
+END;
+/
+
+-- ========================================
+-- 3. Products ํ
์ด๋ธ
+-- ========================================
+CREATE TABLE Products (
+ ProductID NUMBER GENERATED BY DEFAULT AS IDENTITY,
+ ProductCode VARCHAR2(20) NOT NULL,
+ ProductName VARCHAR2(100) NOT NULL,
+ Category VARCHAR2(50),
+ UnitPrice NUMBER(15,2) DEFAULT 0,
+ UnitsInStock NUMBER DEFAULT 0,
+ UnitsOnOrder NUMBER DEFAULT 0,
+ ReorderLevel NUMBER DEFAULT 0,
+ Discontinued NUMBER(1) DEFAULT 0,
+ Description CLOB,
+ CreatedDate TIMESTAMP DEFAULT SYSTIMESTAMP,
+ LastUpdated TIMESTAMP DEFAULT SYSTIMESTAMP,
+ CONSTRAINT PK_Products PRIMARY KEY (ProductID),
+ CONSTRAINT UK_Products_Code UNIQUE (ProductCode)
+);
+
+CREATE INDEX IX_Products_Name ON Products (ProductName);
+CREATE INDEX IX_Products_Category ON Products (Category);
+CREATE INDEX IX_Products_Price ON Products (UnitPrice);
+
+CREATE OR REPLACE TRIGGER trg_products_modtime
+BEFORE UPDATE ON Products
+FOR EACH ROW
+BEGIN
+ :NEW.LastUpdated := SYSTIMESTAMP;
+END;
+/
+
+-- ========================================
+-- 4. OrderDetails ํ
์ด๋ธ
+-- ========================================
+CREATE TABLE OrderDetails (
+ OrderDetailID NUMBER GENERATED BY DEFAULT AS IDENTITY,
+ OrderID NUMBER NOT NULL,
+ ProductID NUMBER NOT NULL,
+ UnitPrice NUMBER(15,2) NOT NULL,
+ Quantity NUMBER NOT NULL,
+ Discount NUMBER(5,2) DEFAULT 0,
+ LineTotal NUMBER(15,2) GENERATED ALWAYS AS (UnitPrice * Quantity * (1 - Discount/100)) VIRTUAL,
+ CreatedDate TIMESTAMP DEFAULT SYSTIMESTAMP,
+ CONSTRAINT PK_OrderDetails PRIMARY KEY (OrderDetailID),
+ CONSTRAINT FK_OrderDetails_Orders FOREIGN KEY (OrderID) REFERENCES Orders(OrderID) ON DELETE CASCADE,
+ CONSTRAINT FK_OrderDetails_Products FOREIGN KEY (ProductID) REFERENCES Products(ProductID)
+);
+
+CREATE INDEX IX_OrderDetails_OrderID ON OrderDetails (OrderID);
+CREATE INDEX IX_OrderDetails_ProductID ON OrderDetails (ProductID);
+
+-- ========================================
+-- 5. Employees ํ
์ด๋ธ
+-- ========================================
+CREATE TABLE Employees (
+ EmployeeID NUMBER GENERATED BY DEFAULT AS IDENTITY,
+ EmployeeCode VARCHAR2(20) NOT NULL,
+ FirstName VARCHAR2(50) NOT NULL,
+ LastName VARCHAR2(50) NOT NULL,
+ Title VARCHAR2(50),
+ BirthDate DATE,
+ HireDate DATE,
+ Email VARCHAR2(100),
+ Phone VARCHAR2(20),
+ Department VARCHAR2(50),
+ Salary NUMBER(15,2),
+ ReportsTo NUMBER,
+ IsActive NUMBER(1) DEFAULT 1,
+ CreatedDate TIMESTAMP DEFAULT SYSTIMESTAMP,
+ LastUpdated TIMESTAMP DEFAULT SYSTIMESTAMP,
+ CONSTRAINT PK_Employees PRIMARY KEY (EmployeeID),
+ CONSTRAINT UK_Employees_Code UNIQUE (EmployeeCode),
+ CONSTRAINT FK_Employees_ReportsTo FOREIGN KEY (ReportsTo) REFERENCES Employees(EmployeeID)
+);
+
+CREATE INDEX IX_Employees_Name ON Employees (LastName, FirstName);
+CREATE INDEX IX_Employees_Department ON Employees (Department);
+CREATE INDEX IX_Employees_ReportsTo ON Employees (ReportsTo);
+
+CREATE OR REPLACE TRIGGER trg_employees_modtime
+BEFORE UPDATE ON Employees
+FOR EACH ROW
+BEGIN
+ :NEW.LastUpdated := SYSTIMESTAMP;
+END;
+/
+
+-- ========================================
+-- 6. ์ฃผ๋ฌธ ์์ฝ ๋ทฐ
+-- ========================================
+CREATE OR REPLACE VIEW vw_OrderSummary AS
+SELECT
+ o.OrderID,
+ o.OrderNumber,
+ o.OrderDate,
+ c.CustomerCode,
+ c.CustomerName,
+ c.City AS CustomerCity,
+ c.Region AS CustomerRegion,
+ o.OrderStatus,
+ o.PaymentStatus,
+ o.TotalAmount,
+ (SELECT COUNT(*) FROM OrderDetails od WHERE od.OrderID = o.OrderID) AS ItemCount,
+ (SELECT NVL(SUM(od.Quantity),0) FROM OrderDetails od WHERE od.OrderID = o.OrderID) AS TotalQuantity,
+ o.CreatedDate
+FROM Orders o
+JOIN Customers c ON o.CustomerID = c.CustomerID;
+
+-- ์๋ฃ ๋ฉ์์ง
+PROMPT 'Oracle์ฉ ์ํ ํ
์ด๋ธ ์์ฑ ์๋ฃ'
diff --git a/resources/create_sample_tables_postgresql.sql b/resources/create_sample_tables_postgresql.sql
new file mode 100644
index 0000000..de4ad7d
--- /dev/null
+++ b/resources/create_sample_tables_postgresql.sql
@@ -0,0 +1,216 @@
+-- ========================================
+-- ์ํ ํ
์ด๋ธ ์์ฑ ์คํฌ๋ฆฝํธ
+-- PostgreSQL์ฉ
+-- ========================================
+
+-- ์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ (์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ)
+-- Note: ์ด ๋ถ๋ถ์ psql์์ ์คํํ๊ฑฐ๋, ์ด๋ฏธ sampledb๊ฐ ์์ฑ๋์ด ์์ด์ผ ํฉ๋๋ค.
+-- CREATE DATABASE sampledb ENCODING 'UTF8';
+
+-- \c sampledb
+
+-- ========================================
+-- 1. Customers ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Customers CASCADE;
+
+CREATE TABLE Customers (
+ CustomerID SERIAL NOT NULL,
+ CustomerCode VARCHAR(20) NOT NULL,
+ CustomerName VARCHAR(100) NOT NULL,
+ ContactName VARCHAR(50) NULL,
+ Email VARCHAR(100) NULL,
+ Phone VARCHAR(20) NULL,
+ Address VARCHAR(200) NULL,
+ City VARCHAR(50) NULL,
+ Region VARCHAR(50) NULL,
+ PostalCode VARCHAR(10) NULL,
+ Country VARCHAR(50) NULL,
+ CustomerType VARCHAR(20) DEFAULT 'Regular',
+ CreditLimit DECIMAL(15,2) DEFAULT 0,
+ IsActive BOOLEAN DEFAULT TRUE,
+ CreatedDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ LastUpdated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+
+ CONSTRAINT PK_Customers PRIMARY KEY (CustomerID),
+ CONSTRAINT UK_Customers_Code UNIQUE (CustomerCode)
+);
+
+-- ์ธ๋ฑ์ค ์์ฑ
+CREATE INDEX IX_Customers_Name ON Customers (CustomerName);
+CREATE INDEX IX_Customers_City ON Customers (City);
+CREATE INDEX IX_Customers_Region ON Customers (Region);
+CREATE INDEX IX_Customers_Type ON Customers (CustomerType);
+
+-- LastUpdated ์๋ ์
๋ฐ์ดํธ๋ฅผ ์ํ ํธ๋ฆฌ๊ฑฐ ํจ์
+CREATE OR REPLACE FUNCTION update_modified_column()
+RETURNS TRIGGER AS $$
+BEGIN
+ NEW.LastUpdated = CURRENT_TIMESTAMP;
+ RETURN NEW;
+END;
+$$ language 'plpgsql';
+
+CREATE TRIGGER update_customers_modtime
+ BEFORE UPDATE ON Customers
+ FOR EACH ROW
+ EXECUTE FUNCTION update_modified_column();
+
+-- ========================================
+-- 2. Orders ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Orders CASCADE;
+
+CREATE TABLE Orders (
+ OrderID SERIAL NOT NULL,
+ OrderNumber VARCHAR(30) NOT NULL,
+ CustomerID INTEGER NOT NULL,
+ OrderDate TIMESTAMP NOT NULL,
+ RequiredDate TIMESTAMP NULL,
+ ShippedDate TIMESTAMP NULL,
+ OrderStatus VARCHAR(20) DEFAULT 'Pending',
+ SubTotal DECIMAL(15,2) DEFAULT 0,
+ TotalAmount DECIMAL(15,2) DEFAULT 0,
+ DiscountAmount DECIMAL(15,2) DEFAULT 0,
+ TaxAmount DECIMAL(15,2) DEFAULT 0,
+ NetAmount DECIMAL(15,2) DEFAULT 0,
+ ShippingAddress VARCHAR(200) NULL,
+ PaymentMethod VARCHAR(30) NULL,
+ PaymentStatus VARCHAR(20) DEFAULT 'Unpaid',
+ EmployeeID INTEGER NULL,
+ Notes TEXT NULL,
+ CreatedDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ LastUpdated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+
+ CONSTRAINT PK_Orders PRIMARY KEY (OrderID),
+ CONSTRAINT UK_Orders_Number UNIQUE (OrderNumber),
+ CONSTRAINT FK_Orders_Customers
+ FOREIGN KEY (CustomerID)
+ REFERENCES Customers(CustomerID)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE
+);
+
+CREATE INDEX IX_Orders_Customer ON Orders (CustomerID);
+CREATE INDEX IX_Orders_Date ON Orders (OrderDate);
+CREATE INDEX IX_Orders_Status ON Orders (OrderStatus);
+
+CREATE TRIGGER update_orders_modtime
+ BEFORE UPDATE ON Orders
+ FOR EACH ROW
+ EXECUTE FUNCTION update_modified_column();
+
+-- ========================================
+-- 3. Products ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Products CASCADE;
+
+CREATE TABLE Products (
+ ProductID SERIAL NOT NULL,
+ ProductCode VARCHAR(20) NOT NULL,
+ ProductName VARCHAR(100) NOT NULL,
+ Category VARCHAR(50) NULL,
+ UnitPrice DECIMAL(15,2) DEFAULT 0,
+ UnitsInStock INTEGER DEFAULT 0,
+ UnitsOnOrder INTEGER DEFAULT 0,
+ ReorderLevel INTEGER DEFAULT 0,
+ Discontinued BOOLEAN DEFAULT FALSE,
+ Description TEXT NULL,
+ CreatedDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ LastUpdated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+
+ CONSTRAINT PK_Products PRIMARY KEY (ProductID),
+ CONSTRAINT UK_Products_Code UNIQUE (ProductCode)
+);
+
+CREATE INDEX IX_Products_Name ON Products (ProductName);
+CREATE INDEX IX_Products_Category ON Products (Category);
+CREATE INDEX IX_Products_Price ON Products (UnitPrice);
+
+CREATE TRIGGER update_products_modtime
+ BEFORE UPDATE ON Products
+ FOR EACH ROW
+ EXECUTE FUNCTION update_modified_column();
+
+-- ========================================
+-- 4. OrderDetails ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS OrderDetails CASCADE;
+
+CREATE TABLE OrderDetails (
+ OrderDetailID SERIAL NOT NULL,
+ OrderID INTEGER NOT NULL,
+ ProductID INTEGER NOT NULL,
+ UnitPrice DECIMAL(15,2) NOT NULL,
+ Quantity INTEGER NOT NULL DEFAULT 1,
+ Discount DECIMAL(5,2) DEFAULT 0,
+ LineTotal DECIMAL(15,2) GENERATED ALWAYS AS (UnitPrice * Quantity * (1 - Discount / 100)) STORED,
+ CreatedDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+
+ CONSTRAINT PK_OrderDetails PRIMARY KEY (OrderDetailID),
+ CONSTRAINT FK_OrderDetails_Orders
+ FOREIGN KEY (OrderID)
+ REFERENCES Orders(OrderID)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE,
+ CONSTRAINT FK_OrderDetails_Products
+ FOREIGN KEY (ProductID)
+ REFERENCES Products(ProductID)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE
+);
+
+CREATE INDEX IX_OrderDetails_Order ON OrderDetails (OrderID);
+CREATE INDEX IX_OrderDetails_Product ON OrderDetails (ProductID);
+
+-- ========================================
+-- 5. Employees ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Employees CASCADE;
+
+CREATE TABLE Employees (
+ EmployeeID SERIAL NOT NULL,
+ EmployeeCode VARCHAR(20) NOT NULL,
+ FirstName VARCHAR(50) NOT NULL,
+ LastName VARCHAR(50) NOT NULL,
+ Title VARCHAR(50) NULL,
+ BirthDate DATE NULL,
+ HireDate DATE NULL,
+ Email VARCHAR(100) NULL,
+ Phone VARCHAR(20) NULL,
+ Department VARCHAR(50) NULL,
+ Salary DECIMAL(15,2) NULL,
+ ReportsTo INTEGER NULL,
+ IsActive BOOLEAN DEFAULT TRUE,
+ CreatedDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ LastUpdated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+
+ CONSTRAINT PK_Employees PRIMARY KEY (EmployeeID),
+ CONSTRAINT UK_Employees_Code UNIQUE (EmployeeCode),
+ CONSTRAINT FK_Employees_ReportsTo
+ FOREIGN KEY (ReportsTo)
+ REFERENCES Employees(EmployeeID)
+ ON DELETE SET NULL
+ ON UPDATE CASCADE
+);
+
+CREATE INDEX IX_Employees_Name ON Employees (LastName, FirstName);
+CREATE INDEX IX_Employees_Department ON Employees (Department);
+CREATE INDEX IX_Employees_ReportsTo ON Employees (ReportsTo);
+
+CREATE TRIGGER update_employees_modtime
+ BEFORE UPDATE ON Employees
+ FOR EACH ROW
+ EXECUTE FUNCTION update_modified_column();
+
+-- ========================================
+-- ์๋ฃ ๋ฉ์์ง
+-- ========================================
+
+SELECT 'Sample tables created successfully for PostgreSQL!' AS Message;
+
diff --git a/resources/create_sample_tables_sqlite.sql b/resources/create_sample_tables_sqlite.sql
new file mode 100644
index 0000000..7b45ed0
--- /dev/null
+++ b/resources/create_sample_tables_sqlite.sql
@@ -0,0 +1,198 @@
+-- ========================================
+-- ์ํ ํ
์ด๋ธ ์์ฑ ์คํฌ๋ฆฝํธ
+-- SQLite์ฉ
+-- ========================================
+
+-- SQLite๋ ํ์ผ ๊ธฐ๋ฐ์ด๋ฏ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ ๋ถํ์
+-- ํ์ผ๋ช
: sampledb.sqlite
+
+-- ========================================
+-- 1. Customers ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Customers;
+
+CREATE TABLE Customers (
+ CustomerID INTEGER PRIMARY KEY AUTOINCREMENT,
+ CustomerCode TEXT NOT NULL UNIQUE,
+ CustomerName TEXT NOT NULL,
+ ContactName TEXT,
+ Email TEXT,
+ Phone TEXT,
+ Address TEXT,
+ City TEXT,
+ Region TEXT,
+ PostalCode TEXT,
+ Country TEXT,
+ CustomerType TEXT DEFAULT 'Regular',
+ CreditLimit REAL DEFAULT 0,
+ IsActive INTEGER DEFAULT 1, -- SQLite uses INTEGER for BOOLEAN (0=false, 1=true)
+ CreatedDate TEXT DEFAULT (datetime('now', 'localtime')),
+ LastUpdated TEXT DEFAULT (datetime('now', 'localtime'))
+);
+
+-- ์ธ๋ฑ์ค ์์ฑ
+CREATE INDEX IX_Customers_Name ON Customers (CustomerName);
+CREATE INDEX IX_Customers_City ON Customers (City);
+CREATE INDEX IX_Customers_Region ON Customers (Region);
+CREATE INDEX IX_Customers_Type ON Customers (CustomerType);
+
+-- LastUpdated ์๋ ์
๋ฐ์ดํธ๋ฅผ ์ํ ํธ๋ฆฌ๊ฑฐ
+CREATE TRIGGER update_customers_timestamp
+AFTER UPDATE ON Customers
+FOR EACH ROW
+BEGIN
+ UPDATE Customers
+ SET LastUpdated = datetime('now', 'localtime')
+ WHERE CustomerID = NEW.CustomerID;
+END;
+
+-- ========================================
+-- 2. Orders ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Orders;
+
+CREATE TABLE Orders (
+ OrderID INTEGER PRIMARY KEY AUTOINCREMENT,
+ OrderNumber TEXT NOT NULL UNIQUE,
+ CustomerID INTEGER NOT NULL,
+ OrderDate TEXT NOT NULL,
+ RequiredDate TEXT,
+ ShippedDate TEXT,
+ OrderStatus TEXT DEFAULT 'Pending',
+ TotalAmount REAL DEFAULT 0,
+ DiscountAmount REAL DEFAULT 0,
+ TaxAmount REAL DEFAULT 0,
+ NetAmount REAL DEFAULT 0,
+ ShippingAddress TEXT,
+ Notes TEXT,
+ CreatedDate TEXT DEFAULT (datetime('now', 'localtime')),
+ LastUpdated TEXT DEFAULT (datetime('now', 'localtime')),
+
+ FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE
+);
+
+CREATE INDEX IX_Orders_Customer ON Orders (CustomerID);
+CREATE INDEX IX_Orders_Date ON Orders (OrderDate);
+CREATE INDEX IX_Orders_Status ON Orders (OrderStatus);
+
+CREATE TRIGGER update_orders_timestamp
+AFTER UPDATE ON Orders
+FOR EACH ROW
+BEGIN
+ UPDATE Orders
+ SET LastUpdated = datetime('now', 'localtime')
+ WHERE OrderID = NEW.OrderID;
+END;
+
+-- ========================================
+-- 3. Products ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Products;
+
+CREATE TABLE Products (
+ ProductID INTEGER PRIMARY KEY AUTOINCREMENT,
+ ProductCode TEXT NOT NULL UNIQUE,
+ ProductName TEXT NOT NULL,
+ Category TEXT,
+ UnitPrice REAL DEFAULT 0,
+ UnitsInStock INTEGER DEFAULT 0,
+ UnitsOnOrder INTEGER DEFAULT 0,
+ ReorderLevel INTEGER DEFAULT 0,
+ Discontinued INTEGER DEFAULT 0,
+ Description TEXT,
+ CreatedDate TEXT DEFAULT (datetime('now', 'localtime')),
+ LastUpdated TEXT DEFAULT (datetime('now', 'localtime'))
+);
+
+CREATE INDEX IX_Products_Name ON Products (ProductName);
+CREATE INDEX IX_Products_Category ON Products (Category);
+CREATE INDEX IX_Products_Price ON Products (UnitPrice);
+
+CREATE TRIGGER update_products_timestamp
+AFTER UPDATE ON Products
+FOR EACH ROW
+BEGIN
+ UPDATE Products
+ SET LastUpdated = datetime('now', 'localtime')
+ WHERE ProductID = NEW.ProductID;
+END;
+
+-- ========================================
+-- 4. OrderDetails ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS OrderDetails;
+
+CREATE TABLE OrderDetails (
+ OrderDetailID INTEGER PRIMARY KEY AUTOINCREMENT,
+ OrderID INTEGER NOT NULL,
+ ProductID INTEGER NOT NULL,
+ UnitPrice REAL NOT NULL,
+ Quantity INTEGER NOT NULL DEFAULT 1,
+ Discount REAL DEFAULT 0,
+ LineTotal REAL GENERATED ALWAYS AS (UnitPrice * Quantity * (1 - Discount / 100)) STORED,
+ CreatedDate TEXT DEFAULT (datetime('now', 'localtime')),
+
+ FOREIGN KEY (OrderID) REFERENCES Orders(OrderID)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE,
+ FOREIGN KEY (ProductID) REFERENCES Products(ProductID)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE
+);
+
+CREATE INDEX IX_OrderDetails_Order ON OrderDetails (OrderID);
+CREATE INDEX IX_OrderDetails_Product ON OrderDetails (ProductID);
+
+-- ========================================
+-- 5. Employees ํ
์ด๋ธ ์์ฑ
+-- ========================================
+
+DROP TABLE IF EXISTS Employees;
+
+CREATE TABLE Employees (
+ EmployeeID INTEGER PRIMARY KEY AUTOINCREMENT,
+ EmployeeCode TEXT NOT NULL UNIQUE,
+ FirstName TEXT NOT NULL,
+ LastName TEXT NOT NULL,
+ Title TEXT,
+ BirthDate TEXT, -- SQLite stores dates as TEXT in ISO format
+ HireDate TEXT,
+ Email TEXT,
+ Phone TEXT,
+ Department TEXT,
+ Salary REAL,
+ ReportsTo INTEGER,
+ IsActive INTEGER DEFAULT 1,
+ CreatedDate TEXT DEFAULT (datetime('now', 'localtime')),
+ LastUpdated TEXT DEFAULT (datetime('now', 'localtime')),
+
+ FOREIGN KEY (ReportsTo) REFERENCES Employees(EmployeeID)
+ ON DELETE SET NULL
+ ON UPDATE CASCADE
+);
+
+CREATE INDEX IX_Employees_Name ON Employees (LastName, FirstName);
+CREATE INDEX IX_Employees_Department ON Employees (Department);
+CREATE INDEX IX_Employees_ReportsTo ON Employees (ReportsTo);
+
+CREATE TRIGGER update_employees_timestamp
+AFTER UPDATE ON Employees
+FOR EACH ROW
+BEGIN
+ UPDATE Employees
+ SET LastUpdated = datetime('now', 'localtime')
+ WHERE EmployeeID = NEW.EmployeeID;
+END;
+
+-- ========================================
+-- ์๋ฃ ๋ฉ์์ง
+-- ========================================
+
+SELECT 'Sample tables created successfully for SQLite!' AS Message;
+
diff --git a/resources/drop_sample_tables.sql b/resources/drop_sample_tables.sql
deleted file mode 100644
index a32baed..0000000
--- a/resources/drop_sample_tables.sql
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
------------------------------------------------------------
--- ์ธ๋ํค ์ญ์
------------------------------------------------------------
-ALTER TABLE OrderDetails DROP CONSTRAINT FK_OrderDetails_Orders;
-ALTER TABLE Orders DROP CONSTRAINT FK_Orders_Customers;
-
-
-
------------------------------------------------------------
--- ํ
์ด๋ธ ์ญ์
------------------------------------------------------------
-DROP TABLE OrderDetails;
-DROP TABLE Orders;
-DROP TABLE Customers;
-
-
-
-
diff --git a/resources/drop_sample_tables_mssql.sql b/resources/drop_sample_tables_mssql.sql
new file mode 100644
index 0000000..2c8a02b
--- /dev/null
+++ b/resources/drop_sample_tables_mssql.sql
@@ -0,0 +1,34 @@
+-- ========================================
+-- ์ํ ํ
์ด๋ธ/๋ทฐ ์ญ์ ์คํฌ๋ฆฝํธ (MSSQL)
+-- ์์กด์ฑ ์์์ ๋ง์ถฐ ์์ ํ๊ฒ ๋๋กญ
+-- ========================================
+
+SET NOCOUNT ON;
+
+-- 1) ๋ทฐ ๋๋กญ
+IF OBJECT_ID('dbo.vw_OrderSummary', 'V') IS NOT NULL DROP VIEW dbo.vw_OrderSummary;
+GO
+
+-- 2) ํธ๋ฆฌ๊ฑฐ ๋๋กญ (์กด์ฌ ์)
+IF OBJECT_ID('dbo.trg_customers_modtime', 'TR') IS NOT NULL DROP TRIGGER dbo.trg_customers_modtime;
+IF OBJECT_ID('dbo.trg_orders_modtime', 'TR') IS NOT NULL DROP TRIGGER dbo.trg_orders_modtime;
+IF OBJECT_ID('dbo.trg_products_modtime', 'TR') IS NOT NULL DROP TRIGGER dbo.trg_products_modtime;
+IF OBJECT_ID('dbo.trg_employees_modtime', 'TR') IS NOT NULL DROP TRIGGER dbo.trg_employees_modtime;
+GO
+
+-- 3) ์ธ๋ํค ์ ์ฝ ํด์ (์กด์ฌ ์)
+IF OBJECT_ID('dbo.FK_OrderDetails_Orders', 'F') IS NOT NULL ALTER TABLE dbo.OrderDetails DROP CONSTRAINT FK_OrderDetails_Orders;
+IF OBJECT_ID('dbo.FK_OrderDetails_Products', 'F') IS NOT NULL ALTER TABLE dbo.OrderDetails DROP CONSTRAINT FK_OrderDetails_Products;
+IF OBJECT_ID('dbo.FK_Employees_ReportsTo', 'F') IS NOT NULL ALTER TABLE dbo.Employees DROP CONSTRAINT FK_Employees_ReportsTo;
+IF OBJECT_ID('dbo.FK_Orders_Customers', 'F') IS NOT NULL ALTER TABLE dbo.Orders DROP CONSTRAINT FK_Orders_Customers;
+GO
+
+-- 4) ํ
์ด๋ธ ๋๋กญ (์์ โ ๋ถ๋ชจ ์์)
+IF OBJECT_ID('dbo.OrderDetails', 'U') IS NOT NULL DROP TABLE dbo.OrderDetails;
+IF OBJECT_ID('dbo.Orders', 'U') IS NOT NULL DROP TABLE dbo.Orders;
+IF OBJECT_ID('dbo.Products', 'U') IS NOT NULL DROP TABLE dbo.Products;
+IF OBJECT_ID('dbo.Employees', 'U') IS NOT NULL DROP TABLE dbo.Employees;
+IF OBJECT_ID('dbo.Customers', 'U') IS NOT NULL DROP TABLE dbo.Customers;
+GO
+
+PRINT 'MSSQL ์ํ ๊ฐ์ฒด ์ญ์ ์๋ฃ';
diff --git a/resources/drop_sample_tables_mysql.sql b/resources/drop_sample_tables_mysql.sql
new file mode 100644
index 0000000..7e05931
--- /dev/null
+++ b/resources/drop_sample_tables_mysql.sql
@@ -0,0 +1,16 @@
+-- ========================================
+-- ์ํ ํ
์ด๋ธ ์ญ์ ์คํฌ๋ฆฝํธ
+-- MySQL / MariaDB์ฉ
+-- ========================================
+
+USE sampledb;
+
+-- ์ธ๋ ํค ์ ์ฝ ์กฐ๊ฑด ๋๋ฌธ์ ์ญ์์ผ๋ก ์ญ์
+DROP TABLE IF EXISTS OrderDetails;
+DROP TABLE IF EXISTS Orders;
+DROP TABLE IF EXISTS Products;
+DROP TABLE IF EXISTS Employees;
+DROP TABLE IF EXISTS Customers;
+
+SELECT 'Sample tables dropped successfully for MySQL/MariaDB!' AS Message;
+
diff --git a/resources/drop_sample_tables_oracle.sql b/resources/drop_sample_tables_oracle.sql
new file mode 100644
index 0000000..1aa10f6
--- /dev/null
+++ b/resources/drop_sample_tables_oracle.sql
@@ -0,0 +1,16 @@
+-- ========================================
+-- ์ํ ํ
์ด๋ธ/๋ทฐ ์ญ์ ์คํฌ๋ฆฝํธ (Oracle)
+-- ========================================
+BEGIN EXECUTE IMMEDIATE 'DROP VIEW vw_OrderSummary'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+BEGIN EXECUTE IMMEDIATE 'DROP TABLE OrderDetails CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+BEGIN EXECUTE IMMEDIATE 'DROP TABLE Orders CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+BEGIN EXECUTE IMMEDIATE 'DROP TABLE Products CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+BEGIN EXECUTE IMMEDIATE 'DROP TABLE Employees CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+BEGIN EXECUTE IMMEDIATE 'DROP TABLE Customers CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
+/
+PROMPT 'Oracle์ฉ ์ํ ๊ฐ์ฒด ์ญ์ ์๋ฃ'
diff --git a/resources/drop_sample_tables_postgresql.sql b/resources/drop_sample_tables_postgresql.sql
new file mode 100644
index 0000000..ded1dab
--- /dev/null
+++ b/resources/drop_sample_tables_postgresql.sql
@@ -0,0 +1,17 @@
+-- ========================================
+-- ์ํ ํ
์ด๋ธ ์ญ์ ์คํฌ๋ฆฝํธ
+-- PostgreSQL์ฉ
+-- ========================================
+
+-- CASCADE ์ต์
์ผ๋ก ์ธ๋ ํค ์ ์ฝ ์กฐ๊ฑด ๋ฌด์ํ๊ณ ์ญ์
+DROP TABLE IF EXISTS OrderDetails CASCADE;
+DROP TABLE IF EXISTS Orders CASCADE;
+DROP TABLE IF EXISTS Products CASCADE;
+DROP TABLE IF EXISTS Employees CASCADE;
+DROP TABLE IF EXISTS Customers CASCADE;
+
+-- ํธ๋ฆฌ๊ฑฐ ํจ์ ์ญ์
+DROP FUNCTION IF EXISTS update_modified_column() CASCADE;
+
+SELECT 'Sample tables dropped successfully for PostgreSQL!' AS Message;
+
diff --git a/resources/drop_sample_tables_sqlite.sql b/resources/drop_sample_tables_sqlite.sql
new file mode 100644
index 0000000..aeb2f91
--- /dev/null
+++ b/resources/drop_sample_tables_sqlite.sql
@@ -0,0 +1,15 @@
+-- ========================================
+-- ์ํ ํ
์ด๋ธ ์ญ์ ์คํฌ๋ฆฝํธ
+-- SQLite์ฉ
+-- ========================================
+
+-- SQLite๋ IF EXISTS๋ฅผ ์ง์ํ๋ฏ๋ก ์์ ํ๊ฒ ์ญ์ ๊ฐ๋ฅ
+-- ์ธ๋ ํค ์ ์ฝ ์กฐ๊ฑด ๋๋ฌธ์ ์ญ์์ผ๋ก ์ญ์
+DROP TABLE IF EXISTS OrderDetails;
+DROP TABLE IF EXISTS Orders;
+DROP TABLE IF EXISTS Products;
+DROP TABLE IF EXISTS Employees;
+DROP TABLE IF EXISTS Customers;
+
+SELECT 'Sample tables dropped successfully for SQLite!' AS Message;
+
diff --git a/resources/generate_sample_data.py b/resources/generate_sample_data.py
new file mode 100644
index 0000000..004e116
--- /dev/null
+++ b/resources/generate_sample_data.py
@@ -0,0 +1,357 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+์ํ ๋ฐ์ดํฐ ์์ฑ ์คํฌ๋ฆฝํธ
+100๊ฑด์ ์ํ ๋ฐ์ดํฐ ์์ฑ (ํ๊ธ 50๊ฑด + ์์ด 50๊ฑด)
+"""
+
+import random
+from datetime import datetime, timedelta
+import argparse
+import os
+import sys
+
+# ํ๊ธ ๋ฐ์ดํฐ
+korean_companies = [
+ 'ํ๊ตญ์ ์', '์์ธ๋ฌด์ญ์์ฌ', '๋ถ์ฐ์ฐ์
', '๋๊ตฌ์ฌ์ ', '์ธ์ฒ๋ฌผ๋ฅ์ผํฐ',
+ '๊ด์ฃผ์ํ์ ํต', '์ ์ฃผํน์ฐ๋ฌผ', '์ธ์ฐํํ๊ณต์
', '๋์ ๊ธฐ์ ์ฐ๊ตฌ์', '๊ฒฝ๊ธฐํต์',
+ '์์ธ๊ฐ๊ตฌ', '๋ถ์ฐ์์ฐ', '๋๊ตฌํ๋ฐฉ์ฝํ', '์ธ์ฒํด์ด', '๊ด์ฃผ์๋์ฐจ๋ถํ',
+ '์ ์ฃผ๊ด๊ด๊ฐ๋ฐ', '์ธ์ฐ์กฐ์ ', '๋์ ๋ฐ์ด์ค', '๊ฒฝ๊ธฐ๋ฐ๋์ฒด', '์์ธํจ์
',
+ '๋ถ์ฐ์ฒ ๊ฐ', '๋๊ตฌ์ฌ์ ๊ณต์
', '์ธ์ฒ์ ๋ฆฌ', '๊ด์ฃผ์ ์๋ถํ', '์ ์ฃผ์ํ',
+ '์ธ์ฐ์ ์ ', '๋์ ์ ์', '๊ฒฝ๊ธฐํํ', '์์ธ๊ฑด์ค', '๋ถ์ฐ๊ฑด์ค์์ฌ',
+ '๋๊ตฌ๊ธฐ๊ณ', '์ธ์ฒํญ๋ง', '๊ด์ฃผ๊ด์ฐ', '์ ์ฃผ์๋์ง', '์ธ์ฐ์๋์ฐจ',
+ '๋์ ์ํํธ์จ์ด', '๊ฒฝ๊ธฐ๋ฌผ๋ฅ', '์์ธ์๋ฃ๊ธฐ๊ธฐ', '๋ถ์ฐ๋ฐ์ด์ค', '๋๊ตฌIT',
+ '์ธ์ฒ์ ์ฌ์์๋์ง', '๊ด์ฃผ์ค๋งํธํฉํ ๋ฆฌ', '์ ์ฃผ๋ฐ์ดํฐ์ผํฐ', '์ธ์ฐ๋์คํ๋ ์ด', '๋์ ์ฐ์ฃผํญ๊ณต',
+ '๊ฒฝ๊ธฐAI์ฐ์
', '์์ธํํ
ํฌ', '๋ถ์ฐ๋ก๋ด์ฐ์
', '๋๊ตฌ๋๋ก ', '์ธ์ฒ์ค๋งํธ์ํฐ'
+]
+
+korean_names = [
+ '๊น์ฒ ์', '์ด์ํฌ', '๋ฐ๋ฏผ์', '์ต์ง์', '์ ํ์ฐ',
+ '๊ฐ์์ง', '์ค์์ฐ', '์๋ํ', '์ก๋ฏธ๋', 'ํ์งํ',
+ '๊น์์', '์ด์ํฌ', '๋ฐ๊ฑดํธ', '์ต๋ฏธ์', '์ ํ์ค',
+ '๊ฐ๋ฏผ์', '์คํ์ง', '์์ฌํ', '์กํ์ฃผ', 'ํ์์ฐ',
+ '๊น๋ํ', '์ด์ง์', '๋ฐ์์ฒ ', '์ต์ค์ ', '์ ์ํ',
+ '๊ฐํ์', '์ค๋ฏธ์ ', '์๊ฒฝํธ', '์ก์ฌ๋ฏผ', 'ํ์๋ฏธ',
+ '๊น๋ฏผ์ฌ', '์ด์ํ', '๋ฐ์ ์', '์ต์นํ', '์ ์ ์ง',
+ '๊ฐ๋ํ', '์ค์์ค', '์ํ์', '์ก์ง์ฐ', 'ํ์น์ฐ',
+ '๊น์์ฐ', '์ด์์', '๋ฐํ์ค', '์ต์์ค', '์ ๋ฏผ์ฌ',
+ '๊ฐ์ ๋', '์ค๋์ค', '์์์ค', '์ก์ง์', 'ํ์์ง'
+]
+
+korean_cities = ['์์ธ', '๋ถ์ฐ', '๋๊ตฌ', '์ธ์ฒ', '๊ด์ฃผ', '๋์ ', '์ธ์ฐ', '์ ์ฃผ', '์์', '์ฑ๋จ']
+korean_regions = ['์์ธ', '๋ถ์ฐ', '๋๊ตฌ', '์ธ์ฒ', '๊ด์ฃผ', '๋์ ', '์ธ์ฐ', '์ ์ฃผ', '๊ฒฝ๊ธฐ', '๊ฐ์']
+
+# ์์ด ๋ฐ์ดํฐ
+english_companies = [
+ 'Tech Solutions Inc', 'Global Trading Co', 'Digital Innovations', 'Advanced Manufacturing', 'Pacific Logistics',
+ 'Euro Electronics', 'Asia Pacific Trade', 'Northern Industries', 'Smart Tech Corp', 'International Supply',
+ 'Future Systems', 'Green Energy Ltd', 'Advanced Materials', 'Ocean Freight Co', 'Digital Commerce',
+ 'Biotech Research', 'Automotive Parts Ltd', 'Cloud Services Inc', 'Pharma Solutions', 'Robotics International',
+ 'Financial Systems', 'Renewable Power', 'Aerospace Tech', 'Smart Agriculture', 'Quantum Computing',
+ 'Marine Solutions', 'AI Development', 'Logistics Solutions', 'Construction Tech', 'Food Processing',
+ 'Textile Manufacturing', 'Chemical Industries', 'Mining Corporation', 'Healthcare Systems', 'Education Technology',
+ 'Entertainment Media', 'Security Solutions', 'Environmental Tech', 'Transportation Systems', 'Telecommunications',
+ 'Water Treatment', 'Fashion Design', 'Gaming Studios', 'Space Technology', 'Furniture Design',
+ 'Printing Services', 'Packaging Solutions', 'Laboratory Equipment', 'Sports Equipment', 'Defense Systems'
+]
+
+english_names = [
+ 'John Smith', 'Emily Johnson', 'Michael Brown', 'Sarah Davis', 'David Wilson',
+ 'Sophie Martin', 'James Lee', 'Emma Anderson', 'Oliver Taylor', 'Isabella Thomas',
+ 'William Jackson', 'Olivia White', 'Ethan Harris', 'Ava Martinez', 'Noah Robinson',
+ 'Mia Clark', 'Lucas Rodriguez', 'Charlotte Lewis', 'Benjamin Walker', 'Amelia Hall',
+ 'Henry Allen', 'Harper Young', 'Alexander King', 'Evelyn Wright', 'Sebastian Lopez',
+ 'Ella Hill', 'Jack Scott', 'Aria Green', 'Mason Adams', 'Luna Baker',
+ 'Logan Nelson', 'Layla Carter', 'Elijah Mitchell', 'Chloe Perez', 'Matthew Roberts',
+ 'Avery Turner', 'Daniel Phillips', 'Sofia Campbell', 'Joseph Parker', 'Victoria Evans',
+ 'David Edwards', 'Grace Collins', 'Samuel Stewart', 'Zoe Sanchez', 'Ryan Morris',
+ 'Penelope Rogers', 'Isaac Reed', 'Stella Cook', 'Christian Morgan', 'Nora Bell'
+]
+
+english_cities = [
+ 'San Francisco', 'New York', 'London', 'Berlin', 'Sydney',
+ 'Paris', 'Singapore', 'Stockholm', 'Austin', 'Toronto',
+ 'Tokyo', 'Manchester', 'Munich', 'Amsterdam', 'Seattle',
+ 'Zurich', 'Milan', 'Dublin', 'Brussels', 'Seoul',
+ 'Chicago', 'Copenhagen', 'Houston', 'Rotterdam', 'Boston',
+ 'Oslo', 'San Jose', 'Singapore', 'Melbourne', 'Madrid',
+ 'Mumbai', 'Sรฃo Paulo', 'Johannesburg', 'Philadelphia', 'Edinburgh',
+ 'Los Angeles', 'Tel Aviv', 'Helsinki', 'Vienna', 'Hong Kong',
+ 'Dubai', 'Paris', 'San Francisco', 'Cape Canaveral', 'Gothenburg',
+ 'Hamburg', 'Atlanta', 'Basel', 'Denver', 'Arlington'
+]
+
+# ์ ํ ์นดํ
๊ณ ๋ฆฌ (ํ๊ธ/์์ด)
+korean_products = [
+ ('๋
ธํธ๋ถ', '์ ์์ ํ'), ('๋ง์ฐ์ค', '์ ์์ ํ'), ('ํค๋ณด๋', '์ ์์ ํ'), ('๋ชจ๋ํฐ', '์ ์์ ํ'), ('์ธ์ฅ SSD', '์ ์ฅ์ฅ์น'),
+ ('USB ๋ฉ๋ชจ๋ฆฌ', '์ ์ฅ์ฅ์น'), ('์ด์ดํฐ', '์ค๋์ค'), ('์คํผ์ปค', '์ค๋์ค'), ('์น์บ ', '์ ์์ ํ'), ('ํ๋ธ๋ฆฟ', '์ ์์ ํ'),
+ ('ํ๋ฆฐํฐ', '์ฌ๋ฌด๊ธฐ๊ธฐ'), ('๋ณตํฉ๊ธฐ', '์ฌ๋ฌด๊ธฐ๊ธฐ'), ('์คํ ๋', '์ฌ๋ฌด์ฉํ'), ('์์', '๊ฐ๊ตฌ'), ('์บ๋น๋', '๊ฐ๊ตฌ'),
+ ('์ฑ
์', '๊ฐ๊ตฌ'), ('๋จํ', '์ฌ๋ฌด์ฉํ'), ('์ถฉ์ ๊ธฐ', '์ ์์ ํ'), ('์ผ์ด๋ธ', '์ ์์ ํ'), ('ํ๋ธ', '์ ์์ ํ'),
+ ('ํค๋์
', '์ค๋์ค'), ('๋ง์ดํฌ', '์ค๋์ค'), ('์ค์บ๋', '์ฌ๋ฌด๊ธฐ๊ธฐ'), ('๋ผ๋ฒจ๊ธฐ', '์ฌ๋ฌด๊ธฐ๊ธฐ'), ('๊ณ์ฐ๊ธฐ', '์ฌ๋ฌด์ฉํ'),
+ ('ํ์ดํธ๋ณด๋', '์ฌ๋ฌด์ฉํ'), ('๋ณต์ฌ๊ธฐ', '์ฌ๋ฌด๊ธฐ๊ธฐ'), ('ํ์๊ธฐ', '์ฌ๋ฌด๊ธฐ๊ธฐ'), ('๋ฐ์ธ๋', '์ฌ๋ฌด์ฉํ'), ('ํ๊ธฐ๊ตฌ', '์ฌ๋ฌด์ฉํ'),
+ ('๋
ธํธ', '์ฌ๋ฌด์ฉํ'), ('ํ์ผ', '์ฌ๋ฌด์ฉํ'), ('ํด๋ฆฝ', '์ฌ๋ฌด์ฉํ'), ('ํ
์ดํ', '์ฌ๋ฌด์ฉํ'), ('๊ฐ์', '์ฌ๋ฌด์ฉํ'),
+ ('์คํ
์ดํ๋ฌ', '์ฌ๋ฌด์ฉํ'), ('ํ์น', '์ฌ๋ฌด์ฉํ'), ('์', '์ฌ๋ฌด์ฉํ'), ('ํ๊ดํ', '์ฌ๋ฌด์ฉํ'), ('์ง์ฐ๊ฐ', '์ฌ๋ฌด์ฉํ'),
+ ('์์ ํ
์ดํ', '์ฌ๋ฌด์ฉํ'), ('ํฌ์คํธ์', '์ฌ๋ฌด์ฉํ'), ('๋ฌ๋ ฅ', '์ฌ๋ฌด์ฉํ'), ('ํ๋๋', '์ฌ๋ฌด์ฉํ'), ('์๊ณ', '์ฌ๋ฌด์ฉํ'),
+ ('์ฐ๋ ๊ธฐํต', '์ฌ๋ฌด์ฉํ'), ('์ฐ์ฐ๊ฝ์ด', '์ฌ๋ฌด์ฉํ'), ('์ ๋ฐ์ฅ', '๊ฐ๊ตฌ'), ('์ฌ๋ฌผํจ', '๊ฐ๊ตฌ'), ('์นธ๋ง์ด', '๊ฐ๊ตฌ')
+]
+
+english_products = [
+ ('Laptop Computer', 'Electronics'), ('Wireless Mouse', 'Electronics'), ('Mechanical Keyboard', 'Electronics'),
+ ('Monitor 27inch', 'Electronics'), ('External SSD', 'Storage'), ('USB Flash Drive', 'Storage'),
+ ('Wireless Earbuds', 'Audio'), ('Bluetooth Speaker', 'Audio'), ('HD Webcam', 'Electronics'),
+ ('Tablet Device', 'Electronics'), ('Laser Printer', 'Office Equipment'), ('Multifunction Printer', 'Office Equipment'),
+ ('Desk Lamp', 'Office Supplies'), ('Office Chair', 'Furniture'), ('File Cabinet', 'Furniture'),
+ ('Standing Desk', 'Furniture'), ('LED Lamp', 'Office Supplies'), ('Power Adapter', 'Electronics'),
+ ('USB Cable', 'Electronics'), ('USB Hub', 'Electronics'), ('Gaming Headset', 'Audio'),
+ ('Studio Microphone', 'Audio'), ('Document Scanner', 'Office Equipment'), ('Label Printer', 'Office Equipment'),
+ ('Calculator', 'Office Supplies'), ('Whiteboard', 'Office Supplies'), ('Copier', 'Office Equipment'),
+ ('Paper Shredder', 'Office Equipment'), ('Ring Binder', 'Office Supplies'), ('Pen Set', 'Office Supplies'),
+ ('Notebook', 'Office Supplies'), ('File Folder', 'Office Supplies'), ('Paper Clips', 'Office Supplies'),
+ ('Adhesive Tape', 'Office Supplies'), ('Scissors', 'Office Supplies'), ('Stapler', 'Office Supplies'),
+ ('Hole Punch', 'Office Supplies'), ('Ruler Set', 'Office Supplies'), ('Highlighter', 'Office Supplies'),
+ ('Eraser', 'Office Supplies'), ('Correction Tape', 'Office Supplies'), ('Sticky Notes', 'Office Supplies'),
+ ('Wall Calendar', 'Office Supplies'), ('Daily Planner', 'Office Supplies'), ('Wall Clock', 'Office Supplies'),
+ ('Waste Bin', 'Office Supplies'), ('Umbrella Stand', 'Office Supplies'), ('Shoe Rack', 'Furniture'),
+ ('Storage Locker', 'Furniture'), ('Privacy Screen', 'Furniture')
+]
+
+def _fmt_str(s):
+ return "'" + str(s).replace("'", "''") + "'"
+
+def _fmt_bool(dialect, v):
+ if dialect == 'postgresql':
+ return 'TRUE' if v else 'FALSE'
+ # others accept 1/0, Oracle uses NUMBER(1)
+ return '1' if v else '0'
+
+def _fmt_dt_str(dt):
+ return dt.strftime('%Y-%m-%d %H:%M:%S')
+
+def _fmt_datetime(dialect, dt):
+ s = _fmt_dt_str(dt)
+ if dialect == 'oracle':
+ return f"TO_TIMESTAMP({_fmt_str(s)}, 'YYYY-MM-DD HH24:MI:SS')"
+ return _fmt_str(s)
+
+def _now():
+ return datetime.utcnow()
+
+def gen_customers(rows):
+ data = []
+ for i in range(rows // 2):
+ data.append({
+ 'code': f"CUST{i+1:03d}",
+ 'name': ("(์ฃผ)" if i < 10 else "") + korean_companies[i % len(korean_companies)],
+ 'contact': korean_names[i % len(korean_names)],
+ 'email': f"{korean_names[i % len(korean_names)].replace(' ', '').lower()}@{korean_companies[i % len(korean_companies)].replace(' ', '').lower()}.co.kr",
+ 'phone': f"02-{random.randint(1000,9999)}-{random.randint(1000,9999)}",
+ 'address': '์์ธ์ ๊ฐ๋จ๊ตฌ',
+ 'city': korean_cities[i % len(korean_cities)],
+ 'region': korean_regions[i % len(korean_regions)],
+ 'country': '๋ํ๋ฏผ๊ตญ',
+ 'ctype': random.choice(['Premium','Regular','VIP']),
+ 'credit': float(random.randint(150,2000) * 100000),
+ 'active': True
+ })
+ for i in range(rows - len(data)):
+ data.append({
+ 'code': f"CUST{i+1+len(data):03d}",
+ 'name': english_companies[i % len(english_companies)],
+ 'contact': english_names[i % len(english_names)],
+ 'email': f"{english_names[i % len(english_names)].split()[0].lower()}@{english_companies[i % len(english_companies)].split()[0].lower()}.com",
+ 'phone': f"+1-555-{random.randint(1000,9999)}",
+ 'address': 'Address',
+ 'city': english_cities[i % len(english_cities)],
+ 'region': 'State',
+ 'country': 'USA',
+ 'ctype': random.choice(['Premium','Regular','VIP']),
+ 'credit': float(random.randint(200,2500) * 100000),
+ 'active': True
+ })
+ return data
+
+def gen_products(rows):
+ data = []
+ for i in range(rows // 2):
+ name, cat = korean_products[i % len(korean_products)]
+ data.append({
+ 'code': f"P-{100+i}", 'name': name, 'cat': cat,
+ 'price': round(random.uniform(10, 2000), 2),
+ 'stock': random.randint(0, 500), 'onorder': random.randint(0, 200),
+ 'reorder': random.randint(0, 50), 'disc': False,
+ 'desc': None
+ })
+ for i in range(rows - len(data)):
+ name, cat = english_products[i % len(english_products)]
+ data.append({
+ 'code': f"P-{200+i}", 'name': name, 'cat': cat,
+ 'price': round(random.uniform(10, 2000), 2),
+ 'stock': random.randint(0, 500), 'onorder': random.randint(0, 200),
+ 'reorder': random.randint(0, 50), 'disc': False,
+ 'desc': None
+ })
+ return data
+
+def gen_employees(rows):
+ data = []
+ base = datetime(1980, 1, 1)
+ for i in range(rows):
+ first = random.choice(['Alice','Brian','Cathy','David','Evan','Fiona','George','Hanna','Ian','Julia'])
+ last = random.choice(['Kim','Lee','Park','Choi','Jung','Kang','Yoon','Lim','Song','Han'])
+ hire = _now() - timedelta(days=random.randint(0, 3650))
+ birth = datetime(1970,1,1) + timedelta(days=random.randint(0, 20000))
+ data.append({
+ 'code': f"E-{i+1:03d}", 'first': first, 'last': last,
+ 'title': random.choice(['Manager','Engineer','Analyst','Assistant','Director']),
+ 'birth': birth.date(), 'hire': hire.date(),
+ 'email': f"{first.lower()}.{last.lower()}@example.com",
+ 'phone': f"010-{random.randint(1000,9999)}-{random.randint(1000,9999)}",
+ 'dept': random.choice(['Sales','IT','Finance','HR','Marketing']),
+ 'salary': round(random.uniform(3000,9000),2),
+ 'reports': None if i==0 else random.randint(1, i),
+ 'active': True
+ })
+ return data
+
+def gen_orders(rows, customers_count, employees_count):
+ data = []
+ start = datetime(2024,1,1)
+ for i in range(rows):
+ odt = start + timedelta(days=random.randint(0,60), hours=random.randint(0,23), minutes=random.randint(0,59))
+ ship = None if random.random() < 0.3 else (odt + timedelta(days=random.randint(1,7)))
+ req = odt + timedelta(days=random.randint(1,10))
+ subtotal = round(random.uniform(50, 5000), 2)
+ tax = round(subtotal * 0.1, 2)
+ total = round(subtotal + tax, 2)
+ data.append({
+ 'number': f"SO-2024{i+1:05d}",
+ 'customer_id': random.randint(1, customers_count),
+ 'order_date': odt,
+ 'required_date': req,
+ 'shipped_date': ship,
+ 'status': random.choice(['Pending','Shipped','Delivered']),
+ 'subtotal': subtotal,
+ 'tax': tax,
+ 'total': total,
+ 'pay_method': random.choice(['Card','Wire','Cash']),
+ 'pay_status': random.choice(['Unpaid','Paid']),
+ 'emp_id': random.randint(1, max(1, employees_count)),
+ 'notes': None
+ })
+ return data
+
+def gen_order_details(rows, orders_count, products_count):
+ data = []
+ for _ in range(rows):
+ qty = random.randint(1,5)
+ price = round(random.uniform(5, 2000),2)
+ data.append({
+ 'order_id': random.randint(1, orders_count),
+ 'product_id': random.randint(1, products_count),
+ 'unit_price': price,
+ 'qty': qty,
+ 'discount': round(random.choice([0,0,0,5,10]),2)
+ })
+ return data
+
+def render_inserts(dialect, table, rows):
+ lines = []
+ if table == 'customers':
+ cols = '(CustomerCode, CustomerName, ContactName, Email, Phone, Address, City, Region, Country, CustomerType, CreditLimit, IsActive)'
+ for r in rows:
+ vals = [
+ _fmt_str(r['code']), _fmt_str(r['name']), _fmt_str(r['contact']), _fmt_str(r['email']), _fmt_str(r['phone']),
+ _fmt_str(r['address']), _fmt_str(r['city']), _fmt_str(r['region']), _fmt_str(r['country']), _fmt_str(r['ctype']),
+ f"{r['credit']:.2f}", _fmt_bool(dialect, r['active'])
+ ]
+ lines.append(f"INSERT INTO Customers {cols} VALUES (" + ", ".join(vals) + ");")
+ elif table == 'products':
+ cols = '(ProductCode, ProductName, Category, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, Description)'
+ for r in rows:
+ vals = [
+ _fmt_str(r['code']), _fmt_str(r['name']), _fmt_str(r['cat']), f"{r['price']:.2f}",
+ str(r['stock']), str(r['onorder']), str(r['reorder']), _fmt_bool(dialect, r['disc']), 'NULL' if r['desc'] is None else _fmt_str(r['desc'])
+ ]
+ lines.append(f"INSERT INTO Products {cols} VALUES (" + ", ".join(vals) + ");")
+ elif table == 'employees':
+ cols = '(EmployeeCode, FirstName, LastName, Title, BirthDate, HireDate, Email, Phone, Department, Salary, ReportsTo, IsActive)'
+ for r in rows:
+ birth = _fmt_str(r['birth'].strftime('%Y-%m-%d')) if dialect != 'oracle' else f"TO_DATE({_fmt_str(r['birth'].strftime('%Y-%m-%d'))}, 'YYYY-MM-DD')"
+ hire = _fmt_str(r['hire'].strftime('%Y-%m-%d')) if dialect != 'oracle' else f"TO_DATE({_fmt_str(r['hire'].strftime('%Y-%m-%d'))}, 'YYYY-MM-DD')"
+ vals = [
+ _fmt_str(r['code']), _fmt_str(r['first']), _fmt_str(r['last']), _fmt_str(r['title']),
+ birth, hire, _fmt_str(r['email']), _fmt_str(r['phone']), _fmt_str(r['dept']), f"{r['salary']:.2f}",
+ 'NULL' if r['reports'] is None else str(r['reports']), _fmt_bool(dialect, r['active'])
+ ]
+ lines.append(f"INSERT INTO Employees {cols} VALUES (" + ", ".join(vals) + ");")
+ elif table == 'orders':
+ cols = '(OrderNumber, CustomerID, OrderDate, RequiredDate, ShippedDate, OrderStatus, SubTotal, TaxAmount, TotalAmount, PaymentMethod, PaymentStatus, EmployeeID, Notes)'
+ for r in rows:
+ order_dt = _fmt_datetime(dialect, r['order_date'])
+ req_dt = _fmt_datetime(dialect, r['required_date'])
+ ship_dt = 'NULL' if r['shipped_date'] is None else _fmt_datetime(dialect, r['shipped_date'])
+ vals = [
+ _fmt_str(r['number']), str(r['customer_id']), order_dt, req_dt, ship_dt,
+ _fmt_str(r['status']), f"{r['subtotal']:.2f}", f"{r['tax']:.2f}", f"{r['total']:.2f}",
+ _fmt_str(r['pay_method']), _fmt_str(r['pay_status']), str(r['emp_id']), 'NULL'
+ ]
+ lines.append(f"INSERT INTO Orders {cols} VALUES (" + ", ".join(vals) + ");")
+ elif table == 'orderdetails':
+ cols = '(OrderID, ProductID, UnitPrice, Quantity, Discount)'
+ for r in rows:
+ vals = [str(r['order_id']), str(r['product_id']), f"{r['unit_price']:.2f}", str(r['qty']), f"{r['discount']:.2f}"]
+ lines.append(f"INSERT INTO OrderDetails {cols} VALUES (" + ", ".join(vals) + ");")
+ return lines
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--dialect', '--db', dest='dialect', choices=['mssql','mysql','postgresql','oracle','sqlite'], default='mssql')
+ parser.add_argument('--tables', default='customers,products,employees,orders,orderdetails')
+ parser.add_argument('--rows', type=int, default=100)
+ parser.add_argument('--output', default='')
+ args = parser.parse_args()
+
+ # Normalize dialect (accept common synonyms via --db/--dialect)
+ alias_map = {
+ 'postgres': 'postgresql', 'pg': 'postgresql',
+ 'maria': 'mysql', 'mariadb': 'mysql'
+ }
+ dialect = alias_map.get(args.dialect.lower(), args.dialect.lower())
+ tables = [t.strip().lower() for t in args.tables.split(',') if t.strip()]
+ rows = args.rows
+
+ customers = gen_customers(rows)
+ products = gen_products(rows)
+ employees = gen_employees(min(100, max(10, rows//2)))
+ orders = gen_orders(min(100, rows), len(customers), len(employees))
+ orderdetails = gen_order_details(rows*2, len(orders), len(products))
+
+ all_lines = []
+ if 'customers' in tables:
+ all_lines.append(f"-- Customers ({len(customers)} records)")
+ all_lines.extend(render_inserts(dialect, 'customers', customers))
+ if 'products' in tables:
+ all_lines.append(f"-- Products ({len(products)} records)")
+ all_lines.extend(render_inserts(dialect, 'products', products))
+ if 'employees' in tables:
+ all_lines.append(f"-- Employees ({len(employees)} records)")
+ all_lines.extend(render_inserts(dialect, 'employees', employees))
+ if 'orders' in tables:
+ all_lines.append(f"-- Orders ({len(orders)} records)")
+ all_lines.extend(render_inserts(dialect, 'orders', orders))
+ if 'orderdetails' in tables:
+ all_lines.append(f"-- OrderDetails ({len(orderdetails)} records)")
+ all_lines.extend(render_inserts(dialect, 'orderdetails', orderdetails))
+
+ output = "\n".join(all_lines) + "\n"
+ out_path = args.output
+ if not out_path:
+ script_dir = os.path.dirname(__file__)
+ out_path = os.path.join(script_dir, f"sample_data_{dialect}.sql")
+ with open(out_path, 'w', encoding='utf-8') as f:
+ f.write(output)
+ sys.stdout.write(out_path + "\n")
+
+if __name__ == "__main__":
+ main()
+
diff --git a/resources/insert_sample_data.sql b/resources/insert_sample_data.sql
deleted file mode 100644
index d36590f..0000000
--- a/resources/insert_sample_data.sql
+++ /dev/null
@@ -1,277 +0,0 @@
--- ========================================
--- ์ํ ๋ฐ์ดํฐ ์
๋ ฅ ์คํฌ๋ฆฝํธ
--- Microsoft SQL Server์ฉ
--- ========================================
-
-USE [SampleDB]
-GO
-
--- ========================================
--- 1. Customers ์ํ ๋ฐ์ดํฐ ์
๋ ฅ
--- ========================================
-
-PRINT '๊ณ ๊ฐ ๋ฐ์ดํฐ ์
๋ ฅ ์ค...'
-
--- ๊ธฐ์กด ๋ฐ์ดํฐ ์ญ์ (์ฐธ์กฐ๋ฌด๊ฒฐ์ฑ ๋๋ฌธ์ ์ญ์์ผ๋ก)
-DELETE FROM dbo.OrderDetails
-DELETE FROM dbo.Orders
-DELETE FROM dbo.Customers
-GO
-
--- IDENTITY ์๋ ๋ฆฌ์
-DBCC CHECKIDENT ('dbo.Customers', RESEED, 0)
-DBCC CHECKIDENT ('dbo.Orders', RESEED, 0)
-DBCC CHECKIDENT ('dbo.OrderDetails', RESEED, 0)
-GO
-
--- ๊ณ ๊ฐ ๋ฐ์ดํฐ ์
๋ ฅ
-INSERT INTO dbo.Customers (
- CustomerCode, CustomerName, ContactName, Email, Phone,
- Address, City, Region, PostalCode, Country,
- CustomerType, CreditLimit, IsActive, CreatedDate
-) VALUES
--- ์์ธ ์ง์ญ ๊ณ ๊ฐ
-('CUST001', '์ผ์ฑ์ ์(์ฃผ)', '๊น์ฒ ์', 'kim@samsung.com', '02-1234-5678',
- '์์ธํน๋ณ์ ์์ด๊ตฌ ์์ด๋๋ก 1321', '์์ธ', '์์ธ', '06765', '๋ํ๋ฏผ๊ตญ', 'VIP', 50000000, 1, '2024-01-15'),
-
-('CUST002', 'LG์ ์(์ฃผ)', '๋ฐ์ํฌ', 'park@lg.com', '02-2345-6789',
- '์์ธํน๋ณ์ ์๋ฑํฌ๊ตฌ ์ฌ์๋๋ก 128', '์์ธ', '์์ธ', '07336', '๋ํ๋ฏผ๊ตญ', 'VIP', 30000000, 1, '2024-01-20'),
-
-('CUST003', 'ํ๋์๋์ฐจ(์ฃผ)', '์ด๋ฏผ์', 'lee@hyundai.com', '02-3456-7890',
- '์์ธํน๋ณ์ ์ข
๋ก๊ตฌ ์จ๊ณก๋ก 75', '์์ธ', '์์ธ', '03045', '๋ํ๋ฏผ๊ตญ', 'Premium', 40000000, 1, '2024-02-01'),
-
-('CUST004', '์ ์ธ๊ณ๋ฐฑํ์ ', '์ ์์ง', 'jung@shinsegae.com', '02-4567-8901',
- '์์ธํน๋ณ์ ์ค๊ตฌ ์๊ณต๋ก 63', '์์ธ', '์์ธ', '04530', '๋ํ๋ฏผ๊ตญ', 'Regular', 10000000, 1, '2024-02-10'),
-
--- ๋ถ์ฐ ์ง์ญ ๊ณ ๊ฐ
-('CUST005', '๋ถ์ฐํญ๋ง๊ณต์ฌ', '๊น๋ถ์ฐ', 'kim@busanport.com', '051-999-1234',
- '๋ถ์ฐ๊ด์ญ์ ์ค๊ตฌ ํด๊ด๋ก 1', '๋ถ์ฐ', '๋ถ์ฐ', '48943', '๋ํ๋ฏผ๊ตญ', 'Premium', 20000000, 1, '2024-02-15'),
-
-('CUST006', '๋กฏ๋ฐ๋ฐฑํ์ ์ผํ
์ํฐ์ ', '์ต๋กฏ๋ฐ', 'choi@lotte.com', '051-888-2345',
- '๋ถ์ฐ๊ด์ญ์ ํด์ด๋๊ตฌ ์ผํ
๋จ๋๋ก 35', '๋ถ์ฐ', '๋ถ์ฐ', '48058', '๋ํ๋ฏผ๊ตญ', 'Regular', 8000000, 1, '2024-03-01'),
-
--- ๋๊ตฌ ์ง์ญ ๊ณ ๊ฐ
-('CUST007', '๋๊ตฌ์ํ', '์๋๊ตฌ', 'seo@dgb.co.kr', '053-777-3456',
- '๋๊ตฌ๊ด์ญ์ ์ค๊ตฌ ๋ฌ๊ตฌ๋ฒ๋๋ก 2077', '๋๊ตฌ', '๊ฒฝ๋ถ', '41911', '๋ํ๋ฏผ๊ตญ', 'Premium', 15000000, 1, '2024-03-05'),
-
--- ์ธ์ฒ ์ง์ญ ๊ณ ๊ฐ
-('CUST008', '์ธ์ฒ๊ตญ์ ๊ณตํญ๊ณต์ฌ', '์์ธ์ฒ', 'ahn@airport.kr', '032-666-4567',
- '์ธ์ฒ๊ด์ญ์ ์ค๊ตฌ ๊ณตํญ๋ก 272', '์ธ์ฒ', '์ธ์ฒ', '22382', '๋ํ๋ฏผ๊ตญ', 'VIP', 25000000, 1, '2024-03-10'),
-
--- ๊ด์ฃผ ์ง์ญ ๊ณ ๊ฐ
-('CUST009', '๊ธฐ์์๋์ฐจ ๊ด์ฃผ๊ณต์ฅ', '์กฐ๊ด์ฃผ', 'cho@kia.com', '062-555-5678',
- '๊ด์ฃผ๊ด์ญ์ ๊ด์ฐ๊ตฌ ์์ด๋ก 60', '๊ด์ฃผ', '์ ๋จ', '62034', '๋ํ๋ฏผ๊ตญ', 'Premium', 18000000, 1, '2024-03-15'),
-
--- ๋์ ์ง์ญ ๊ณ ๊ฐ
-('CUST010', 'KAIST', '๋ฌธ๋์ ', 'moon@kaist.ac.kr', '042-444-6789',
- '๋์ ๊ด์ญ์ ์ ์ฑ๊ตฌ ๋ํ๋ก 291', '๋์ ', '๋์ ', '34141', '๋ํ๋ฏผ๊ตญ', 'Regular', 5000000, 1, '2024-03-20'),
-
--- ํด์ธ ๊ณ ๊ฐ
-('CUST011', 'Sony Corporation', 'Tanaka Hiroshi', 'tanaka@sony.co.jp', '+81-3-1234-5678',
- '1-7-1 Konan, Minato-ku', 'Tokyo', 'Tokyo', '108-0075', 'Japan', 'VIP', 60000000, 1, '2024-04-01'),
-
-('CUST012', 'Apple Inc.', 'John Smith', 'john.smith@apple.com', '+1-408-996-1010',
- 'One Apple Park Way', 'Cupertino', 'California', '95014', 'USA', 'VIP', 80000000, 1, '2024-04-05'),
-
--- ๋นํ์ฑ ๊ณ ๊ฐ
-('CUST013', '๊ตฌ)๋์ฐ์ ์', '๊น๋์ฐ', 'kim@daewoo.com', '02-9999-0000',
- '์์ธํน๋ณ์ ์ค๊ตฌ ์ฒญ๊ณ์ฒ๋ก 100', '์์ธ', '์์ธ', '04517', '๋ํ๋ฏผ๊ตญ', 'Regular', 0, 0, '2023-12-01')
-
-PRINT '๊ณ ๊ฐ ๋ฐ์ดํฐ ์
๋ ฅ ์๋ฃ (13๊ฐ)'
-GO
-
--- ========================================
--- 2. Orders ์ํ ๋ฐ์ดํฐ ์
๋ ฅ
--- ========================================
-
-PRINT '์ฃผ๋ฌธ ๋ฐ์ดํฐ ์
๋ ฅ ์ค...'
-
-INSERT INTO dbo.Orders (
- OrderNumber, CustomerID, OrderDate, RequiredDate, ShippedDate,
- OrderStatus, ShipVia, Freight, ShipName, ShipAddress, ShipCity, ShipRegion, ShipCountry,
- SubTotal, TaxAmount, TotalAmount, PaymentMethod, PaymentStatus, EmployeeID, Notes
-) VALUES
--- 2024๋
1์ ์ฃผ๋ฌธ
-('ORD-2024-0001', 1, '2024-01-16', '2024-01-26', '2024-01-18',
- 'Shipped', 'ํ์งํ๋ฐฐ', 5000, '์ผ์ฑ์ ์(์ฃผ)', '์์ธํน๋ณ์ ์์ด๊ตฌ ์์ด๋๋ก 1321', '์์ธ', '์์ธ', '๋ํ๋ฏผ๊ตญ',
- 950000, 95000, 1050000, '์ ์ฉ์นด๋', 'Paid', 101, '๊ธด๊ธ๋ฐฐ์ก ์์ฒญ'),
-
-('ORD-2024-0002', 2, '2024-01-22', '2024-02-01', '2024-01-25',
- 'Shipped', 'CJ๋ํํต์ด', 3000, 'LG์ ์(์ฃผ)', '์์ธํน๋ณ์ ์๋ฑํฌ๊ตฌ ์ฌ์๋๋ก 128', '์์ธ', '์์ธ', '๋ํ๋ฏผ๊ตญ',
- 1200000, 120000, 1323000, '๊ณ์ข์ด์ฒด', 'Paid', 102, NULL),
-
--- 2024๋
2์ ์ฃผ๋ฌธ
-('ORD-2024-0003', 3, '2024-02-05', '2024-02-15', '2024-02-07',
- 'Shipped', '๋ก์ ํ๋ฐฐ', 7000, 'ํ๋์๋์ฐจ(์ฃผ)', '์์ธํน๋ณ์ ์ข
๋ก๊ตฌ ์จ๊ณก๋ก 75', '์์ธ', '์์ธ', '๋ํ๋ฏผ๊ตญ',
- 2500000, 250000, 2757000, '์ ์ฉ์นด๋', 'Paid', 103, '๋๋์ฃผ๋ฌธ'),
-
-('ORD-2024-0004', 4, '2024-02-12', '2024-02-22', '2024-02-14',
- 'Shipped', 'ํ์งํ๋ฐฐ', 2000, '์ ์ธ๊ณ๋ฐฑํ์ ', '์์ธํน๋ณ์ ์ค๊ตฌ ์๊ณต๋ก 63', '์์ธ', '์์ธ', '๋ํ๋ฏผ๊ตญ',
- 800000, 80000, 882000, '์ ์ฉ์นด๋', 'Paid', 101, NULL),
-
-('ORD-2024-0005', 5, '2024-02-18', '2024-02-28', '2024-02-20',
- 'Shipped', '๋ถ์ฐํ๋ฐฐ', 4000, '๋ถ์ฐํญ๋ง๊ณต์ฌ', '๋ถ์ฐ๊ด์ญ์ ์ค๊ตฌ ํด๊ด๋ก 1', '๋ถ์ฐ', '๋ถ์ฐ', '๋ํ๋ฏผ๊ตญ',
- 1500000, 150000, 1654000, '๊ณ์ข์ด์ฒด', 'Paid', 104, '๋ถ์ฐ์ง์ญ ๋ฐฐ์ก'),
-
--- 2024๋
3์ ์ฃผ๋ฌธ
-('ORD-2024-0006', 6, '2024-03-03', '2024-03-13', '2024-03-05',
- 'Shipped', 'CJ๋ํํต์ด', 3500, '๋กฏ๋ฐ๋ฐฑํ์ ์ผํ
์ํฐ์ ', '๋ถ์ฐ๊ด์ญ์ ํด์ด๋๊ตฌ ์ผํ
๋จ๋๋ก 35', '๋ถ์ฐ', '๋ถ์ฐ', '๋ํ๋ฏผ๊ตญ',
- 600000, 60000, 663500, '์ ์ฉ์นด๋', 'Paid', 105, NULL),
-
-('ORD-2024-0007', 7, '2024-03-08', '2024-03-18', '2024-03-10',
- 'Shipped', '๋๊ตฌํ๋ฐฐ', 2500, '๋๊ตฌ์ํ', '๋๊ตฌ๊ด์ญ์ ์ค๊ตฌ ๋ฌ๊ตฌ๋ฒ๋๋ก 2077', '๋๊ตฌ', '๊ฒฝ๋ถ', '๋ํ๋ฏผ๊ตญ',
- 1100000, 110000, 1212500, '๊ณ์ข์ด์ฒด', 'Paid', 106, NULL),
-
-('ORD-2024-0008', 8, '2024-03-12', '2024-03-22', '2024-03-14',
- 'Shipped', '์ธ์ฒํ๋ฐฐ', 6000, '์ธ์ฒ๊ตญ์ ๊ณตํญ๊ณต์ฌ', '์ธ์ฒ๊ด์ญ์ ์ค๊ตฌ ๊ณตํญ๋ก 272', '์ธ์ฒ', '์ธ์ฒ', '๋ํ๋ฏผ๊ตญ',
- 3200000, 320000, 3526000, '์ ์ฉ์นด๋', 'Paid', 107, '๊ณตํญ ์ง๋ฐฐ์ก'),
-
--- 2024๋
4์ ์ฃผ๋ฌธ (์ผ๋ถ ๋ฏธ์ฒ๋ฆฌ)
-('ORD-2024-0009', 9, '2024-04-02', '2024-04-12', '2024-04-04',
- 'Shipped', '๊ด์ฃผํ๋ฐฐ', 4500, '๊ธฐ์์๋์ฐจ ๊ด์ฃผ๊ณต์ฅ', '๊ด์ฃผ๊ด์ญ์ ๊ด์ฐ๊ตฌ ์์ด๋ก 60', '๊ด์ฃผ', '์ ๋จ', '๋ํ๋ฏผ๊ตญ',
- 1800000, 180000, 1984500, '๊ณ์ข์ด์ฒด', 'Paid', 108, NULL),
-
-('ORD-2024-0010', 10, '2024-04-08', '2024-04-18', NULL,
- 'Processing', NULL, 0, 'KAIST', '๋์ ๊ด์ญ์ ์ ์ฑ๊ตฌ ๋ํ๋ก 291', '๋์ ', '๋์ ', '๋ํ๋ฏผ๊ตญ',
- 500000, 50000, 550000, '๊ณ์ข์ด์ฒด', 'Pending', 109, '์ฐ๊ตฌ์ฉ ์ฅ๋น'),
-
-('ORD-2024-0011', 11, '2024-04-15', '2024-04-25', NULL,
- 'Processing', NULL, 0, 'Sony Corporation', '1-7-1 Konan, Minato-ku', 'Tokyo', 'Tokyo', 'Japan',
- 5000000, 0, 5000000, '์ ์ฉ์นด๋', 'Pending', 110, 'ํด์ธ๋ฐฐ์ก'),
-
-('ORD-2024-0012', 12, '2024-04-20', '2024-04-30', NULL,
- 'Pending', NULL, 0, 'Apple Inc.', 'One Apple Park Way', 'Cupertino', 'California', 'USA',
- 8000000, 0, 8000000, '๊ณ์ข์ด์ฒด', 'Pending', 111, '๋๋ ํด์ธ์ฃผ๋ฌธ'),
-
--- ์ทจ์๋ ์ฃผ๋ฌธ
-('ORD-2024-0013', 1, '2024-04-25', '2024-05-05', NULL,
- 'Cancelled', NULL, 0, '์ผ์ฑ์ ์(์ฃผ)', '์์ธํน๋ณ์ ์์ด๊ตฌ ์์ด๋๋ก 1321', '์์ธ', '์์ธ', '๋ํ๋ฏผ๊ตญ',
- 300000, 30000, 330000, NULL, 'Cancelled', 101, '๊ณ ๊ฐ ์์ฒญ์ผ๋ก ์ทจ์')
-
-PRINT '์ฃผ๋ฌธ ๋ฐ์ดํฐ ์
๋ ฅ ์๋ฃ (13๊ฐ)'
-GO
-
--- ========================================
--- 3. OrderDetails ์ํ ๋ฐ์ดํฐ ์
๋ ฅ
--- ========================================
-
-PRINT '์ฃผ๋ฌธ์์ธ ๋ฐ์ดํฐ ์
๋ ฅ ์ค...'
-
-INSERT INTO dbo.OrderDetails (
- OrderID, ProductCode, ProductName, UnitPrice, Quantity, Discount
-) VALUES
--- ์ฃผ๋ฌธ 1๋ฒ ์์ธ (์ผ์ฑ์ ์)
-(1, 'PROD-001', '๊ฐค๋ญ์ S24 Ultra', 1300000, 1, 0),
-(1, 'PROD-002', '๊ฐค๋ญ์ ์์น6', 350000, 1, 10),
-
--- ์ฃผ๋ฌธ 2๋ฒ ์์ธ (LG์ ์)
-(2, 'PROD-011', 'LG OLED TV 65์ธ์น', 2200000, 1, 0),
-(2, 'PROD-012', 'LG ์ฌ์ด๋๋ฐ', 450000, 1, 5),
-
--- ์ฃผ๋ฌธ 3๋ฒ ์์ธ (ํ๋์๋์ฐจ)
-(3, 'PROD-021', 'ํ๋ ์ ๋ค์์ค G90 ๋ถํ์ธํธ', 5000000, 1, 20),
-
--- ์ฃผ๋ฌธ 4๋ฒ ์์ธ (์ ์ธ๊ณ๋ฐฑํ์ )
-(4, 'PROD-031', '๋ช
ํ ํธ๋๋ฐฑ ์ปฌ๋ ์
', 1200000, 1, 15),
-(4, 'PROD-032', '๋ธ๋๋ ํฅ์ ์ธํธ', 800000, 1, 20),
-
--- ์ฃผ๋ฌธ 5๋ฒ ์์ธ (๋ถ์ฐํญ๋ง๊ณต์ฌ)
-(5, 'PROD-041', 'ํญ๋ง ๊ด๋ฆฌ ์์คํ
', 2500000, 1, 10),
-(5, 'PROD-042', '๋ณด์ ์นด๋ฉ๋ผ ์ธํธ', 800000, 1, 5),
-
--- ์ฃผ๋ฌธ 6๋ฒ ์์ธ (๋กฏ๋ฐ๋ฐฑํ์ )
-(6, 'PROD-051', '๋งค์ฅ ๋์คํ๋ ์ด ์์คํ
', 1500000, 1, 0),
-
--- ์ฃผ๋ฌธ 7๋ฒ ์์ธ (๋๊ตฌ์ํ)
-(7, 'PROD-061', '์ํ ๋ณด์ ์๋ฃจ์
', 2000000, 1, 5),
-(7, 'PROD-062', 'ATM ๊ด๋ฆฌ ์์คํ
', 800000, 1, 0),
-
--- ์ฃผ๋ฌธ 8๋ฒ ์์ธ (์ธ์ฒ๊ณตํญ)
-(8, 'PROD-071', '๊ณตํญ ๋ณด์ ์์คํ
', 4000000, 1, 0),
-(8, 'PROD-072', '์น๊ฐ ์๋ด ์์คํ
', 2500000, 1, 10),
-
--- ์ฃผ๋ฌธ 9๋ฒ ์์ธ (๊ธฐ์์๋์ฐจ)
-(9, 'PROD-081', '์๋์ฐจ ์์ฐ๋ผ์ธ ๋ถํ', 3000000, 1, 10),
-(9, 'PROD-082', 'ํ์ง๊ด๋ฆฌ ์ฅ๋น', 1200000, 1, 5),
-
--- ์ฃผ๋ฌธ 10๋ฒ ์์ธ (KAIST)
-(10, 'PROD-091', '์ฐ๊ตฌ์ฉ ์ปดํจํฐ ์์คํ
', 800000, 1, 0),
-(10, 'PROD-092', '์คํ์ค ์ธก์ ์ฅ๋น', 400000, 1, 5),
-
--- ์ฃผ๋ฌธ 11๋ฒ ์์ธ (Sony)
-(11, 'PROD-101', '๊ณ ๊ธ ์นด๋ฉ๋ผ ์์คํ
', 8000000, 1, 15),
-(11, 'PROD-102', 'ํ๋ก ์ค๋์ค ์ฅ๋น', 3000000, 1, 10),
-
--- ์ฃผ๋ฌธ 12๋ฒ ์์ธ (Apple)
-(12, 'PROD-111', '๊ธฐ์
์ฉ MacBook Pro ์ธํธ', 12000000, 1, 20),
-(12, 'PROD-112', '์์ดํจ๋ ํ๋ก ๋๋ ๊ตฌ๋งค', 8000000, 1, 15)
-
--- ์ฃผ๋ฌธ 13๋ฒ์ ์ทจ์๋์ด ์์ธ๋ด์ญ ์์
-
-PRINT '์ฃผ๋ฌธ์์ธ ๋ฐ์ดํฐ ์
๋ ฅ ์๋ฃ (18๊ฐ)'
-GO
-
--- ========================================
--- 4. Orders ํ
์ด๋ธ์ SubTotal, TotalAmount ์
๋ฐ์ดํธ
--- ========================================
-
-PRINT '์ฃผ๋ฌธ ๊ธ์ก ์ฌ๊ณ์ฐ ์ค...'
-
-UPDATE o
-SET
- SubTotal = ISNULL(detail_sum.SubTotal, 0),
- TotalAmount = ISNULL(detail_sum.SubTotal, 0) + o.TaxAmount + o.Freight
-FROM dbo.Orders o
-LEFT JOIN (
- SELECT
- OrderID,
- SUM(LineTotal) as SubTotal
- FROM dbo.OrderDetails
- GROUP BY OrderID
-) detail_sum ON o.OrderID = detail_sum.OrderID
-
-PRINT '์ฃผ๋ฌธ ๊ธ์ก ์ฌ๊ณ์ฐ ์๋ฃ'
-GO
-
--- ========================================
--- 5. ๋ฐ์ดํฐ ์
๋ ฅ ๊ฒฐ๊ณผ ํ์ธ
--- ========================================
-
-PRINT ''
-PRINT '=== ๋ฐ์ดํฐ ์
๋ ฅ ์๋ฃ ==='
-PRINT ''
-
--- ํ
์ด๋ธ๋ณ ๋ฐ์ดํฐ ๊ฐ์ ํ์ธ
-SELECT 'Customers' as TableName, COUNT(*) as RecordCount FROM dbo.Customers
-UNION ALL
-SELECT 'Orders' as TableName, COUNT(*) as RecordCount FROM dbo.Orders
-UNION ALL
-SELECT 'OrderDetails' as TableName, COUNT(*) as RecordCount FROM dbo.OrderDetails
-
-PRINT ''
-PRINT '=== ์ฃผ๋ฌธ ์ํ๋ณ ์ง๊ณ ==='
-
--- ์ฃผ๋ฌธ ์ํ๋ณ ์ง๊ณ
-SELECT
- OrderStatus,
- COUNT(*) as OrderCount,
- SUM(TotalAmount) as TotalAmount
-FROM dbo.Orders
-GROUP BY OrderStatus
-ORDER BY OrderCount DESC
-
-PRINT ''
-PRINT '=== ์ง์ญ๋ณ ๊ณ ๊ฐ ์ ==='
-
--- ์ง์ญ๋ณ ๊ณ ๊ฐ ์
-SELECT
- Region,
- COUNT(*) as CustomerCount
-FROM dbo.Customers
-WHERE IsActive = 1
-GROUP BY Region
-ORDER BY CustomerCount DESC
-
-PRINT ''
-PRINT '์ํ ๋ฐ์ดํฐ ์
๋ ฅ์ด ๋ชจ๋ ์๋ฃ๋์์ต๋๋ค!'
-GO
\ No newline at end of file
diff --git a/src/database/DatabaseFactory.js b/src/database/DatabaseFactory.js
new file mode 100644
index 0000000..2f3ddb7
--- /dev/null
+++ b/src/database/DatabaseFactory.js
@@ -0,0 +1,116 @@
+const MSSQLAdapter = require('./MSSQLAdapter');
+const MySQLAdapter = require('./MySQLAdapter');
+const PostgreSQLAdapter = require('./PostgreSQLAdapter');
+const SQLiteAdapter = require('./SQLiteAdapter');
+const OracleAdapter = require('./OracleAdapter');
+const { getMessages } = require('../utils/messages');
+
+/**
+ * DatabaseFactory - ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
์ ๋ฐ๋ผ ์ ์ ํ ์ด๋ํฐ๋ฅผ ์์ฑ
+ */
+class DatabaseFactory {
+ /**
+ * ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ด๋ํฐ ์์ฑ
+ * @param {string} dbType - ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
(mssql, mysql, mariadb, postgresql, sqlite)
+ * @param {Object} config - ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ค์
+ * @param {string} language - ์ธ์ด ์ค์ (en/kr)
+ * @returns {Object} ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๋ํฐ ์ธ์คํด์ค
+ */
+ static createAdapter(dbType, config, language = 'en') {
+ const msg = getMessages('database', language);
+ const normalizedType = (dbType || 'mssql').toLowerCase();
+
+ switch (normalizedType) {
+ case 'mssql':
+ case 'sqlserver':
+ return new MSSQLAdapter(config, language);
+
+ case 'mysql':
+ case 'mariadb':
+ return new MySQLAdapter(config, language);
+
+ case 'postgresql':
+ case 'postgres':
+ case 'pg':
+ return new PostgreSQLAdapter(config, language);
+
+ case 'sqlite':
+ case 'sqlite3':
+ return new SQLiteAdapter(config, language);
+
+ case 'oracle':
+ case 'oracledb':
+ case 'oci':
+ return new OracleAdapter(config, language);
+
+ default:
+ throw new Error(`${msg.unsupportedDbType} ${dbType}`);
+ }
+ }
+
+ /**
+ * ์ง์ํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
๋ชฉ๋ก
+ * @returns {Array} ์ง์ DB ํ์
์ ๋ณด
+ */
+ static getSupportedTypes() {
+ return [
+ { type: 'mssql', name: 'Microsoft SQL Server', defaultPort: 1433 },
+ { type: 'mysql', name: 'MySQL', defaultPort: 3306 },
+ { type: 'mariadb', name: 'MariaDB', defaultPort: 3306 },
+ { type: 'postgresql', name: 'PostgreSQL', defaultPort: 5432 },
+ { type: 'sqlite', name: 'SQLite', defaultPort: null },
+ { type: 'oracle', name: 'Oracle Database', defaultPort: 1521 }
+ ];
+ }
+
+ /**
+ * ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
๋ณ ๊ธฐ๋ณธ ํฌํธ ๋ฒํธ
+ * @param {string} dbType - ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
+ * @returns {number|null} ๊ธฐ๋ณธ ํฌํธ ๋ฒํธ
+ */
+ static getDefaultPort(dbType) {
+ const typeInfo = this.getSupportedTypes().find(t => t.type === dbType.toLowerCase());
+ return typeInfo ? typeInfo.defaultPort : null;
+ }
+
+ /**
+ * ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์ ์ ํจ์ฑ ๊ฒ์ฆ
+ * @param {string} dbType - ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์
+ * @param {Object} config - ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์
+ * @param {string} language - ์ธ์ด ์ค์
+ * @returns {boolean} ์ ํจ์ฑ ๊ฒ์ฆ ๊ฒฐ๊ณผ
+ */
+ static validateConfig(dbType, config, language = 'en') {
+ const msg = getMessages('database', language);
+ const normalizedType = (dbType || 'mssql').toLowerCase();
+
+ // SQLite๋ ํ์ผ ๊ธฐ๋ฐ์ด๋ฏ๋ก database ๋๋ server ํ๋๋ง ํ์
+ if (normalizedType === 'sqlite' || normalizedType === 'sqlite3') {
+ if (!config.database && !config.server) {
+ throw new Error(`${msg.requiredConfigMissing} database or server (file path)`);
+ }
+ return true;
+ }
+
+ // ๋ค๋ฅธ DB๋ค์ server, database, user, password ํ์
+ const requiredFields = ['server', 'database', 'user', 'password'];
+ const missingFields = requiredFields.filter(field => !config[field]);
+
+ if (missingFields.length > 0) {
+ throw new Error(`${msg.requiredConfigMissing} ${missingFields.join(', ')}`);
+ }
+
+ // Port number validation (optional, use default if not provided)
+ if (config.port) {
+ const port = parseInt(config.port, 10);
+ if (isNaN(port) || port < 1 || port > 65535) {
+ throw new Error(msg.portInvalid);
+ }
+ }
+
+ return true;
+ }
+}
+
+module.exports = DatabaseFactory;
+
diff --git a/src/database/MSSQLAdapter.js b/src/database/MSSQLAdapter.js
new file mode 100644
index 0000000..10becdd
--- /dev/null
+++ b/src/database/MSSQLAdapter.js
@@ -0,0 +1,192 @@
+const mssql = require('mssql');
+const { getMessages } = require('../utils/messages');
+const { formatDate, createInClause } = require('../utils/date-utils');
+
+/**
+ * MSSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๋ํฐ
+ */
+class MSSQLAdapter {
+ constructor(config, language = 'en') {
+ this.config = config;
+ this.msg = getMessages('database', language);
+ this.dbPools = {};
+ this.dbType = 'mssql';
+ }
+
+ /**
+ * MSSQL ์ฐ๊ฒฐ ํ ์์ฑ
+ * @param {Object} config - MSSQL ์ฐ๊ฒฐ ์ค์
+ * @param {string} dbKey - ๋ฐ์ดํฐ๋ฒ ์ด์ค ํค
+ * @returns {Promise} ์ฐ๊ฒฐ ํ
+ */
+ async createConnectionPool(config, dbKey) {
+ if (!this.dbPools[dbKey]) {
+ if (!config) {
+ throw new Error(`${this.msg.dbIdNotFound} ${dbKey}`);
+ }
+
+ console.log(`[DB] ${dbKey} ${this.msg.dbConnecting}`);
+ const pool = new mssql.ConnectionPool(config);
+ await pool.connect();
+ this.dbPools[dbKey] = pool;
+ console.log(`[DB] ${dbKey} ${this.msg.dbConnected}`);
+ }
+ return this.dbPools[dbKey];
+ }
+
+ /**
+ * MSSQL ์ฟผ๋ฆฌ ์คํ
+ * @param {mssql.ConnectionPool} pool - ์ฐ๊ฒฐ ํ
+ * @param {string} sql - SQL ์ฟผ๋ฆฌ
+ * @returns {Promise