Puri๋ ํ์
์์ ํ ์ฟผ๋ฆฌ ๋น๋์ด์ง๋ง, ๋ณต์กํ SQL ํํ์์ด ํ์ํ ๋๋ Raw SQL์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Raw ํจ์ ๊ฐ์
Raw ํ์
ํจ์ ํ์
๋ณ Raw ํจ์ rawString, rawNumber
WHERE Raw ๋ณต์กํ ์กฐ๊ฑด๋ฌธ whereRaw
CASE WHEN ์กฐ๊ฑด๋ถ ๊ฐ ์ ํ CASE ํํ์
์๋ธ์ฟผ๋ฆฌ ์ค์ฒฉ ์ฟผ๋ฆฌ ์์ฑ Subquery
Raw ํ์
ํจ์
rawString - ๋ฌธ์์ด ๋ฐํ
const results = await db . table ( "users" ). select ({
id: "id" ,
fullName: Puri . rawString ( "CONCAT(first_name, ' ', last_name)" ),
upperName: Puri . upper ( "username" ),
lowerEmail: Puri . lower ( "email" ),
});
// ํ์
: { id: number; fullName: string; upperName: string; lowerEmail: string; }[]
rawNumber - ์ซ์ ๋ฐํ
const results = await db . table ( "employees" ). select ({
id: "id" ,
salary: "salary" ,
yearsSince: Puri . rawNumber ( "EXTRACT(YEAR FROM AGE(NOW(), hire_date))" ),
roundedSalary: Puri . rawNumber ( "ROUND(salary, -3)" ),
});
// ํ์
: { id: number; salary: string; yearsSince: number; roundedSalary: number; }[]
rawBoolean - ๋ถ๋ฆฐ ๋ฐํ
const results = await db . table ( "users" ). select ({
id: "id" ,
isActive: "is_active" ,
isAdmin: Puri . rawBoolean ( "role = 'admin'" ),
hasEmail: Puri . rawBoolean ( "email IS NOT NULL" ),
});
// ํ์
: { id: number; isActive: boolean; isAdmin: boolean; hasEmail: boolean; }[]
rawDate - ๋ ์ง ๋ฐํ
const results = await db . table ( "users" ). select ({
id: "id" ,
createdAt: "created_at" ,
nextWeek: Puri . rawDate ( "created_at + INTERVAL '7 days'" ),
});
// ํ์
: { id: number; createdAt: Date; nextWeek: Date; }[]
rawStringArray - ๋ฌธ์์ด ๋ฐฐ์ด ๋ฐํ
const results = await db
. table ( "projects" )
. join ( "projects__employees" , "projects.id" , "projects__employees.project_id" )
. join ( "users" , "projects__employees.employee_id" , "users.id" )
. select ({
projectId: "projects.id" ,
memberNames: Puri . rawStringArray ( "ARRAY_AGG(users.username)" ),
})
. groupBy ( "projects.id" );
// ํ์
: { projectId: number; memberNames: string[]; }[]
Static SQL ํจ์
Puri๊ฐ ์ ๊ณตํ๋ ๋ด์ฅ SQL ํจ์๋ค์
๋๋ค.
๋ฌธ์์ด ํจ์
const results = await db
. table ( "users" )
. select ({
fullName: Puri . concat ( "first_name" , "' '" , "last_name" ),
});
์ง๊ณ ํจ์
const results = await db . table ( "employees" ). select ({
total: Puri . count ( "id" ),
totalSalary: Puri . sum ( "salary" ),
avgSalary: Puri . avg ( "salary" ),
maxSalary: Puri . max ( "salary" ),
minSalary: Puri . min ( "salary" ),
});
WHERE Raw
๋ณต์กํ WHERE ์กฐ๊ฑด์ ์ง์ ์์ฑํ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ WHERE Raw
const results = await db
. table ( "employees" )
. select ({ id: "id" , salary: "salary" })
. whereRaw ( "salary > ?" , [ 50000 ])
. whereRaw ( "EXTRACT(YEAR FROM hire_date) = ?" , [ 2023 ]);
SQL Injection ์ฃผ์ : whereRaw์์๋ ๋ฐ๋์ ๋ฐ์ธ๋ฉ(?)์ ์ฌ์ฉํ์ธ์.
์ฌ์ฉ์ ์
๋ ฅ์ ์ง์ ๋ฌธ์์ด์ ๋ฃ์ผ๋ฉด ์ ๋ฉ๋๋ค.
๋ณต์กํ ์กฐ๊ฑด
const results = await db
. table ( "employees" )
. select ({ id: "id" , name: "username" })
. whereRaw (
`
(department_id = ? AND salary > ?)
OR (department_id = ? AND salary > ?)
` ,
[ 1 , 60000 , 2 , 70000 ]
);
๋ ์ง ํจ์
// ์ต๊ทผ 30์ผ ๋ฐ์ดํฐ
const results = await db
. table ( "users" )
. select ({ id: "id" })
. whereRaw ( "created_at > NOW() - INTERVAL '30 days'" );
// ํน์ ์ฐ๋
const results = await db
. table ( "users" )
. select ({ id: "id" })
. whereRaw ( "EXTRACT(YEAR FROM created_at) = ?" , [ 2024 ]);
CASE WHEN - ์กฐ๊ฑด๋ถ ๊ฐ
CASE WHEN ํํ์์ผ๋ก ์กฐ๊ฑด์ ๋ฐ๋ผ ๋ค๋ฅธ ๊ฐ์ ๋ฐํํ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ CASE WHEN
const results = await db . table ( "employees" ). select ({
id: "id" ,
name: "username" ,
salaryLevel: Puri . rawString ( `
CASE
WHEN salary < 50000 THEN 'Junior'
WHEN salary < 70000 THEN 'Mid'
ELSE 'Senior'
END
` ),
});
์ซ์ ๊ณ์ฐ
const results = await db . table ( "products" ). select ({
id: "id" ,
name: "name" ,
price: "price" ,
discountedPrice: Puri . rawNumber ( `
CASE
WHEN category = 'sale' THEN price * 0.8
WHEN category = 'clearance' THEN price * 0.5
ELSE price
END
` ),
});
Boolean ๊ฒฐ๊ณผ
const results = await db . table ( "users" ). select ({
id: "id" ,
name: "username" ,
isPremium: Puri . rawBoolean ( `
CASE
WHEN subscription_tier IN ('gold', 'platinum') THEN TRUE
ELSE FALSE
END
` ),
});
์๋ธ์ฟผ๋ฆฌ์ Raw SQL
Scalar ์๋ธ์ฟผ๋ฆฌ
const results = await db . table ( "users" ). select ({
id: "id" ,
name: "username" ,
postCount: Puri . rawNumber ( `
(SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id)
` ),
});
COALESCE - NULL ์ฒ๋ฆฌ
const results = await db . table ( "employees" ). select ({
id: "id" ,
departmentName: Puri . rawString ( `
COALESCE(
(SELECT name FROM departments WHERE id = employees.department_id),
'No Department'
)
` ),
});
์ค์ ์์
์ฌ์ฉ์ ํต๊ณ ๋์๋ณด๋
async getUserStats ( userId : number ) {
const stats = await this . getPuri ( "r" )
. table ( "users" )
. select ({
userId: "users.id" ,
username: "users.username" ,
// ๊ฒ์๊ธ ํต๊ณ
totalPosts: Puri . rawNumber ( `
(SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id)
` ),
recentPosts: Puri . rawNumber ( `
(SELECT COUNT(*)
FROM posts
WHERE posts.user_id = users.id
AND posts.created_at > NOW() - INTERVAL '30 days')
` ),
// ํ๋ ๋ ๋ฒจ
activityLevel: Puri . rawString ( `
CASE
WHEN (SELECT COUNT(*) FROM posts WHERE user_id = users.id) > 100 THEN 'High'
WHEN (SELECT COUNT(*) FROM posts WHERE user_id = users.id) > 10 THEN 'Medium'
ELSE 'Low'
END
` ),
// ๊ฐ์
๊ฒฝ๊ณผ ์ผ์
daysSinceJoined: Puri . rawNumber ( `
EXTRACT(DAY FROM AGE(NOW(), users.created_at))
` ),
})
. where ( "users.id" , userId )
. first ();
return stats ;
}
์๊ฐ๋๋ณ ์ง๊ณ
async getHourlyStats ( date : string ) {
const stats = await this . getPuri ( "r" )
. table ( "events" )
. select ({
hour: Puri . rawNumber ( "EXTRACT(HOUR FROM created_at)" ),
date: Puri . rawDate ( "DATE(created_at)" ),
eventCount: Puri . count ( "id" ),
uniqueUsers: Puri . rawNumber ( "COUNT(DISTINCT user_id)" ),
})
. whereRaw ( "DATE(created_at) = ?" , [ date ])
. groupBy ( "hour" , "date" )
. orderBy ( "hour" , "asc" );
return stats ;
}
์์ ๊ณ์ฐ
async getTopUsers () {
const results = await this . getPuri ( "r" )
. table ( "users" )
. select ({
userId: "users.id" ,
username: "users.username" ,
postCount: Puri . rawNumber ( `
(SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id)
` ),
rank: Puri . rawNumber ( `
RANK() OVER (ORDER BY
(SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id) DESC
)
` ),
})
. whereRaw ( `
(SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id) > 0
` )
. orderBy ( "rank" , "asc" )
. limit ( 10 );
return results ;
}
์๋์ฐ ํจ์
ROW_NUMBER
const results = await db . table ( "employees" ). select ({
id: "id" ,
name: "username" ,
salary: "salary" ,
rowNumber: Puri . rawNumber ( `
ROW_NUMBER() OVER (ORDER BY salary DESC)
` ),
});
RANK / DENSE_RANK
const results = await db . table ( "employees" ). select ({
id: "id" ,
departmentId: "department_id" ,
salary: "salary" ,
rankInDept: Puri . rawNumber ( `
RANK() OVER (PARTITION BY department_id ORDER BY salary DESC)
` ),
});
LAG / LEAD - ์ด์ /๋ค์ ํ
const results = await db
. table ( "sales" )
. select ({
id: "id" ,
month: "month" ,
amount: "amount" ,
previousMonth: Puri . rawNumber ( `
LAG(amount, 1) OVER (ORDER BY month)
` ),
nextMonth: Puri . rawNumber ( `
LEAD(amount, 1) OVER (ORDER BY month)
` ),
})
. orderBy ( "month" , "asc" );
JSON ํจ์ (PostgreSQL)
JSON ํ๋ ์ถ์ถ
const results = await db . table ( "users" ). select ({
id: "id" ,
city: Puri . rawString ( "metadata->>'city'" ),
age: Puri . rawNumber ( "(metadata->>'age')::integer" ),
tags: Puri . rawStringArray (
"ARRAY(SELECT jsonb_array_elements_text(metadata->'tags'))"
),
});
JSON ์ง๊ณ
const results = await db
. table ( "employees" )
. select ({
departmentId: "department_id" ,
employees: Puri . rawString ( `
JSON_AGG(JSON_BUILD_OBJECT(
'id', id,
'name', username,
'salary', salary
))
` ),
})
. groupBy ( "department_id" );
์ฑ๋ฅ ์ต์ ํ
EXPLAIN ์ฌ์ฉ
// ์ฟผ๋ฆฌ ์คํ ๊ณํ ํ์ธ
const plan = await db
. table ( "employees" )
. select ({ id: "id" })
. where ( "department_id" , 1 )
. rawQuery ()
. explain ();
console . log ( plan );
์ธ๋ฑ์ค ํํธ (PostgreSQL์ ์ง์ ์ํจ)
PostgreSQL์ ์ตํฐ๋ง์ด์ ๊ฐ ์๋์ผ๋ก ์ธ๋ฑ์ค๋ฅผ ์ ํํฉ๋๋ค. ๋์ ํต๊ณ ์
๋ฐ์ดํธ:
ํ์
์์ ์ฑ
Raw ํจ์๋ค์ ๋ฐํ ํ์
์ ๋ช
์ํฉ๋๋ค.
const results = await db . table ( "users" ). select ({
stringValue: Puri . rawString ( "'test'" ), // string
numberValue: Puri . rawNumber ( "123" ), // number
boolValue: Puri . rawBoolean ( "TRUE" ), // boolean
dateValue: Puri . rawDate ( "NOW()" ), // Date
arrayValue: Puri . rawStringArray ( "'{}'" ), // string[]
});
// ํ์
์ด ์๋์ผ๋ก ์ถ๋ก ๋จ
results [ 0 ]. stringValue ; // string
results [ 0 ]. numberValue ; // number
results [ 0 ]. boolValue ; // boolean
๋ค์ ๋จ๊ณ