FiveM MySQL.rawExecute คืออะไร? ต่างจาก MySQL.prepare และ MySQL.query อย่างไร
FiveM MySQL.rawExecute คือ API ของ oxmysql สำหรับ Execute SQL Query ที่ถูกเรียกบ่อย โดยรองรับ Parameter หลายชุดสำหรับ Query เดียว คล้ายกับ MySQL.prepare แต่มีความแตกต่างสำคัญเรื่อง รูปแบบข้อมูลที่คืนกลับจาก SELECT ซึ่ง rawExecute จะคืนผลเป็น Array ของ Rows อย่างสม่ำเสมอ ขณะที่ prepare สามารถคืนเป็นค่าเดียว, Row เดียว หรือ Array ตามจำนวน Columns และ Rows ที่ Query ได้.
ดังนั้น rawExecute เหมาะเมื่อ Developer ต้องการ Query ที่ถูกเรียกบ่อยและต้องการ Result Structure ที่คาดเดาง่าย โดยเฉพาะ Code ที่ต้อง Loop ผลลัพธ์หลาย Rows
จำง่ายๆ:
MySQL.query
= Query ทั่วไป
MySQL.prepare
= Query เรียกบ่อย + Result ยืดหยุ่นตามข้อมูล
MySQL.rawExecute
= Query เรียกบ่อย + SELECT คืน Array
① MySQL.rawExecute คืออะไร
รูปแบบ Lua พื้นฐาน:
local response =
MySQL.rawExecute.await(
[[
SELECT
`firstname`,
`lastname`
FROM `users`
WHERE `identifier` = ?
]],
{
identifier
}
)
oxmysql ระบุว่า rawExecute สามารถใช้ Execute Frequently-called Queries และรองรับ Parameter หลายชุดกับ Query เดียว.
② MySQL.rawExecute.await คืออะไร
.await คือ Promise/Await Style สำหรับ Lua
รูปแบบ:
local result =
MySQL.rawExecute.await(
query,
parameters
)
เมื่อ Database ส่งผลกลับ Code จึงทำงานบรรทัดถัดไป
Official API รองรับทั้ง Promise/Await และ Callback.
③ Callback ใช้ได้ไหม
ได้
ตัวอย่าง:
MySQL.rawExecute(
[[
SELECT
`firstname`,
`lastname`
FROM `users`
WHERE `identifier` = ?
]],
{
identifier
},
function(response)
print(
json.encode(response)
)
end
)
oxmysql มี Callback API ของ rawExecute โดยตรง.
④ จุดสำคัญที่สุดของ rawExecute คืออะไร
สำหรับ SELECT จุดที่ควรจำคือ:
rawExecute
↓
Array of Rows
แม้ Result จะมีเพียงหนึ่ง Rowก็ตาม Documentation ระบุความแตกต่างนี้ไว้โดยตรงเมื่อเทียบกับ prepare.
⑤ ตัวอย่าง SELECT ได้หนึ่ง Row
SQL:
SELECT
`firstname`,
`lastname`
FROM `users`
WHERE `identifier` = ?
LIMIT 1;
เมื่อใช้ rawExecute ควรคิด Result ในลักษณะ:
local rows =
MySQL.rawExecute.await(
query,
{
identifier
}
)
local row = rows[1]
if not row then
return
end
print(
row.firstname,
row.lastname
)
นี่ต่างจาก MySQL.single ซึ่ง API ถูกออกแบบให้คืน Columns ของหนึ่ง Row โดยตรง.
⑥ MySQL.rawExecute กับ MySQL.prepare ต่างกันอย่างไร
ความต่างหลักที่ Official Documentation ระบุคือ Result ของ SELECT
rawExecute
SELECT
↓
Array of Rows
prepare
SELECT
↓
Column
หรือ
Row
หรือ
Array of Rows
ขึ้นอยู่กับจำนวน Rows และ Columns.
⑦ ทำไม Array Result มีประโยชน์
เพราะ Code สามารถออกแบบให้คาดหวัง Structure เดียวได้
ตัวอย่าง:
local rows =
MySQL.rawExecute.await(
query,
params
)
for i = 1, #rows do
local row = rows[i]
-- process row
end
ไม่ต้องคาดเดาว่า Result จะเป็น Scalar, Object หรือ Array ตามจำนวนข้อมูล
⑧ rawExecute กับ MySQL.query ต่างกันอย่างไร
MySQL.query เป็น General-purpose Query API โดยเมื่อใช้ SELECT จะคืน Rows และ Columns ที่ตรงเงื่อนไขทั้งหมด.
ส่วน rawExecute ถูกระบุว่าเหมาะกับ Frequently-called Queries และรองรับ Parameter Sets หลายชุด.
จำ:
query
= ใช้งานทั่วไป อ่านง่าย
rawExecute
= Query เดิมถูกเรียกบ่อย
+ ต้องการ Array Result
⑨ rawExecute กับ MySQL.single ต่างกันอย่างไร
MySQL.single
คืน:
หนึ่ง Row
โดยตรง.
ตัวอย่าง:
local row =
MySQL.single.await(
query,
params
)
MySQL.rawExecute
คืน:
Array
ดังนั้นหนึ่ง Row จะอ่านผ่าน:
local row = rows[1]
⑩ rawExecute กับ MySQL.scalar ต่างกันอย่างไร
MySQL.scalar ถูกออกแบบให้คืน Column แรกของ Row เดียว.
ตัวอย่าง:
local count =
MySQL.scalar.await(
[[
SELECT COUNT(*)
FROM `characters`
WHERE `identifier` = ?
]],
{
identifier
}
)
ถ้าต้องการค่าเดียว scalar มักสื่อ Intent ได้ชัดกว่า rawExecute
⑪ rawExecute กับ MySQL.update ต่างกันอย่างไร
MySQL.update คืนจำนวน Rows ที่ได้รับผลจาก Query.
ตัวอย่าง:
local affectedRows =
MySQL.update.await(
[[
UPDATE `users`
SET `firstname` = ?
WHERE `identifier` = ?
]],
{
newName,
identifier
}
)
หากสิ่งที่ Resource ต้องการคือ affectedRows อย่างชัดเจน MySQL.update อ่านง่ายกว่า
⑫ rawExecute กับ MySQL.insert ต่างกันอย่างไร
MySQL.insert ถูกออกแบบให้เพิ่ม Record และคืน Insert ID เมื่อมีค่าที่ถูกต้อง.
ตัวอย่าง:
local id =
MySQL.insert.await(
[[
INSERT INTO `users`
(`identifier`, `firstname`)
VALUES (?, ?)
]],
{
identifier,
firstName
}
)
ถ้าต้องการ Insert ID โดยตรง ใช้ insert จะสื่อเจตนาได้ดีกว่า
⑬ rawExecute แทน API ทุกตัวได้ไหม
ไม่ควร
แม้ SQL หลายประเภทสามารถ Execute ผ่าน API ที่ยืดหยุ่นได้ แต่การเลือก API ตาม Intent ช่วยให้ Code อ่านง่าย
เช่น:
หลาย Rows
→ query
Row เดียว
→ single
ค่าเดียว
→ scalar
Insert ID
→ insert
Affected Rows
→ update
Repeated Query + Array Result
→ rawExecute
API แต่ละตัวมี Return Contract แตกต่างกันตาม Documentation.
⑭ rawExecute เหมาะเมื่อไหร่
Candidate ที่ดีคือ:
SQL Structure เดิม
+
ถูกเรียกบ่อย
+
Parameters เปลี่ยน
+
SELECT Result ต้องการ Array เสมอ
ตัวอย่าง:
โหลด Vehicles หลาย Record
อ่าน Character List
อ่าน Logs
อ่าน Settings หลาย Record
Query ชุดข้อมูลเดิมของผู้เล่นหลายคน
⑮ rawExecute ไม่จำเป็นเมื่อไหร่
ถ้า Query:
เรียกหนึ่งครั้ง
หรือ:
ต้องการหนึ่งค่า
หรือ:
ต้องการ Insert ID
ก็ไม่จำเป็นต้องเลือก rawExecute เพียงเพราะชื่อดู Low-level กว่า
เลือก API ให้ตรงกับข้อมูลที่ต้องการ
⑯ Value Placeholder ใช้อะไร
rawExecute รองรับ:
?
สำหรับ Value Placeholder.
ตัวอย่าง:
WHERE `identifier` = ?
และส่ง:
{
identifier
}
⑰ Column Placeholder ใช้อะไร
Official Documentation ระบุว่า rawExecute รองรับ:
??
สำหรับ Column Placeholder.
แต่ Dynamic Column SQL ควรถูกจำกัดจาก Server-side Allowlist หากมีข้อมูลจาก Network Event เข้ามา
⑱ Named Placeholder ใช้ได้ไหม
สำหรับ rawExecute Documentation ระบุว่า Named Placeholders จะ Error และรองรับเฉพาะ:
?
??
ใน API นี้.
ส่วน Placeholder Documentation ของ oxmysql ระบุ Named Placeholder แบบ @name ว่า Deprecated สำหรับ APIs ทั่วไป.
ดังนั้น Resource ใหม่ควรใช้:
WHERE `identifier` = ?
เป็นหลัก
⑲ อย่าต่อ Client Input เข้า SQL String
หลีกเลี่ยง:
local query =
"SELECT * FROM users WHERE identifier = '" ..
identifier ..
"'"
ใช้:
local rows =
MySQL.rawExecute.await(
[[
SELECT
`id`,
`firstname`
FROM `users`
WHERE `identifier` = ?
]],
{
identifier
}
)
การใช้ Parameters แยกจาก SQL Structure เป็น Pattern ที่ oxmysql Documentation รองรับ.
⑳ rawExecute กับ Prepared Statement เหมือนกันไหม
rawExecute และ prepare มีลักษณะใกล้กันตรงที่ Documentation ระบุว่าทั้งคู่เหมาะกับ Frequently-called Queries และรองรับ Parameter Sets หลายชุด.
ความต่างที่ต้องจำมากที่สุดคือ Result Shape ของ SELECT
㉑ rawExecute เร็วกว่า prepare ไหม
ไม่ควรสรุปว่าเร็วกว่าเสมอ
oxmysql ระบุว่าความเร็ว Query จริงแตกต่างตาม:
Hardware
Database Settings
Database Version
Current Workload
และแนะนำดูความเร็วจริงจาก Debug UI หรือ Server Console เมื่อเปิด mysql_debug.
ดังนั้นต้อง Benchmark บน Server จริง
㉒ rawExecute เร็วกว่า query ไหม
Documentation ระบุ rawExecute ว่าสามารถใช้กับ Frequently-called Queries ได้เร็วขึ้น แต่ไม่ได้หมายความว่า Query ทุกตัวจะได้ประโยชน์เท่ากัน.
หาก Query ถูกเรียกเพียงไม่กี่ครั้ง ความต่างอาจไม่ใช่ Bottleneck ที่สำคัญที่สุด
㉓ อย่า Optimize จากชื่อ API
อย่าคิดว่า:
rawExecute
=
เร็วที่สุด
คำถามที่สำคัญกว่าคือ:
Query ใช้เวลากี่ ms?
ถูกเรียกกี่ครั้ง?
Database ใช้ Index หรือไม่?
Result ใหญ่แค่ไหน?
Resource ทำอะไรต่อหลัง Query?
oxmysql ระบุให้วัด Query Speed จริงผ่าน Debug Tools แทนการอิง Benchmark แบบตายตัว.
㉔ mysql_debug ใช้ตรวจอย่างไร
เปิดใน Server Configuration ตามระบบ oxmysql แล้วตรวจ Query Timing ใน Server Console
oxmysql Benchmark Documentation ระบุว่า Real Query Speeds จะแสดงใน Debug UI และ Server Console เมื่อเปิด mysql_debug.
Workflow:
query
↓
วัด
เปลี่ยน API
↓
วัดอีกครั้ง
เปรียบเทียบ
㉕ Benchmark ต้องใช้ Scenario เดิม
ก่อน:
MySQL.query
หลัง:
MySQL.rawExecute
ต้องทดสอบด้วย:
ข้อมูลชุดเดิม
Player Count ใกล้เคียง
Query เดิม
Database Load ใกล้เคียง
เพราะ Performance ขึ้นกับ Current Workload ด้วย.
㉖ Parameter Sets หลายชุดคืออะไร
ทั้ง rawExecute และ prepare รองรับการใช้ Parameter Sets หลายชุดกับ Query เดียวตาม Official Documentation.
แนวคิด:
SQL เดียว
Parameters ชุดที่ 1
Parameters ชุดที่ 2
Parameters ชุดที่ 3
เหมาะกับบาง Workload ที่ Query Structure เหมือนเดิม
㉗ อย่าส่ง Parameter Sets จำนวนมากแบบไม่วัด
API รองรับไม่ได้หมายความว่า:
ส่งหลายหมื่นชุด
=
ดีที่สุด
ต้องดู:
Database CPU
Memory
Query Latency
Player Count
Total Workload
และ Benchmark จริง
㉘ ตัวอย่าง Character List
local characters =
MySQL.rawExecute.await(
[[
SELECT
`id`,
`firstname`,
`lastname`
FROM `characters`
WHERE `identifier` = ?
ORDER BY `id` ASC
]],
{
identifier
}
)
for i = 1, #characters do
local character =
characters[i]
print(
character.firstname,
character.lastname
)
end
Result แบบ Array เหมาะกับ Character List โดยธรรมชาติ
㉙ ตัวอย่าง Vehicle List
local vehicles =
MySQL.rawExecute.await(
[[
SELECT
`id`,
`model`,
`stored`
FROM `vehicles`
WHERE `owner` = ?
]],
{
identifier
}
)
for i = 1, #vehicles do
local vehicle =
vehicles[i]
print(
vehicle.id,
vehicle.model
)
end
ถ้า Query ถูกเรียกบ่อยและ Consumer ต้องการ Array ของ Rows rawExecute เป็น Candidate ที่เหมาะตาม API Design.
㉚ ถ้า Vehicle มีเพียงคันเดียวจะเกิดอะไร
ด้วย rawExecute ควรยังคิดว่า:
Array
ดังนั้น:
local vehicles =
MySQL.rawExecute.await(
query,
params
)
local vehicle =
vehicles[1]
ต่างจาก MySQL.single ซึ่งคืน Row โดยตรง.
㉛ ถ้าไม่เจอ Row ต้องตรวจอย่างไร
ใช้:
local rows =
MySQL.rawExecute.await(
query,
params
)
if not rows
or #rows == 0 then
return
end
จากนั้นจึงเข้าถึง:
rows[1]
ช่วยลด Error จากการอ่าน Row ที่ไม่มีอยู่
㉜ Date Result มีข้อควรระวัง
Official rawExecute Documentation ระบุว่า Date จะไม่คืน Date String รูปแบบที่มักใช้ใน FiveM เหมือน APIs บางประเภท.
ดังนั้น Columns เช่น:
DATE
DATETIME
TIMESTAMP
ควรทดสอบ Return Type จริงก่อนเขียน Formatter
㉝ TINYINT(1) คืน Boolean ไหม
Documentation ระบุว่า:
TINYINT 1
BIT
จะไม่คืน Boolean โดยอัตโนมัติใน rawExecute.
ดังนั้น Resource อาจ Normalize เอง:
local stored =
row.stored == 1
ตาม Schema และ Result จริง
㉞ อย่าสมมติ Type จาก Database Schema
ก่อน Production ควรทดสอบ Columns เช่น:
TINYINT
BIT
DATE
DATETIME
DECIMAL
JSON
กับ API ที่เลือกใช้จริง
เพราะ Result Representation อาจแตกต่างจากสิ่งที่ Lua Code คาดไว้
㉟ rawExecute แทน Database Index ได้ไหม
ไม่ได้
rawExecute
= Query API
Index
= Database Access Structure
ถ้า Query:
WHERE `owner` = ?
ต้อง Scan Table ใหญ่ การเปลี่ยน query เป็น rawExecute ไม่ได้สร้าง Index ให้ Database
ต้องวิเคราะห์ SQL และ Execution Plan แยกต่างหาก
㊱ rawExecute แทน Cache ได้ไหม
ไม่ได้
rawExecute ยัง Query Database
Cache อาจช่วยให้:
อ่านจาก Memory
โดยไม่ต้อง Query ซ้ำ
ถ้าข้อมูลแทบไม่เปลี่ยน:
Database
↓
Load ครั้งเดียว
↓
Cache
↓
ใช้ระหว่าง Session
อาจเหมาะกว่า Query ทุก Action
㊲ rawExecute แทน Transaction ได้ไหม
ไม่ได้
Transaction ของ oxmysql ทำให้หลาย Queries ถูก Commit เฉพาะเมื่อทั้งหมดสำเร็จ.
ส่วน rawExecute เน้น Frequently-called Query Execution
ดังนั้น:
rawExecute
= Execution Pattern
transaction
= Data Consistency
㊳ rawExecute ใช้ทุก Frame ได้ไหม
ไม่ควรใช้ Database Query ทุก Frame เพียงเพราะ API ทำงานเร็ว
ตัวอย่างที่ควรหลีกเลี่ยง:
CreateThread(function()
while true do
MySQL.rawExecute.await(
query,
params
)
Wait(0)
end
end)
หากข้อมูลต้องใช้ต่อเนื่อง ควรพิจารณา Cache หรือ Event-driven Architecture
㊴ Query Frequency สำคัญกว่า API บางครั้ง
สมมติ:
query = 4 ms
เรียก 10 ครั้ง
กับ:
rawExecute = 2 ms
เรียก 100,000 ครั้ง
Architecture หลังอาจยังสร้าง Workload มากกว่า
ดังนั้น Optimization ต้องดูทั้ง:
Query Time
×
Query Count
ไม่ใช่เวลา Query เพียงครั้งเดียว
㊵ SELECT * ยังควรหลีกเลี่ยงไหม
ถ้าใช้เพียง:
id
firstname
lastname
เขียน:
SELECT
`id`,
`firstname`,
`lastname`
FROM `characters`
WHERE `identifier` = ?;
ดีกว่า:
SELECT *
FROM `characters`
WHERE `identifier` = ?;
rawExecute ไม่ได้ลด Columns ให้เอง
㊶ LIMIT ยังสำคัญไหม
ถ้า Logic ต้องการเพียงหนึ่ง Record:
SELECT
`id`,
`firstname`
FROM `users`
WHERE `identifier` = ?
LIMIT 1;
ควรกำหนด Intent ใน SQL
แต่หากต้องการหนึ่ง Rowโดยตรง MySQL.single อาจเหมาะกว่าทาง Semantic.
㊷ อย่าใช้ rawExecute เพียงเพื่อ Result หนึ่งค่า
ถ้าต้องการ:
SELECT COUNT(*)
และต้องการผลเดียว
MySQL.scalar ถูกออกแบบให้คืน First Column ของหนึ่ง Rowโดยตรง.
Code จึงอ่านง่ายกว่า:
local count =
MySQL.scalar.await(...)
㊸ อย่าใช้ rawExecute เพียงเพื่อ Insert ID
ใช้:
MySQL.insert.await(...)
เมื่อ Intent คือ Insert และต้องการ Insert ID เพราะ oxmysql มี API เฉพาะสำหรับเรื่องนี้.
㊹ อย่าใช้ rawExecute เพียงเพื่อ affectedRows
ใช้:
MySQL.update.await(...)
เมื่อ Resource ต้องรู้จำนวน Rows ที่ถูกแก้ เพราะ update มี Return Contract นี้โดยตรง.
㊺ Database Access Layer ช่วยเลือก API ได้อย่างไร
แทนการเขียน SQL กระจายทั่ว Resource:
local DB = {}
แล้วทำ:
function DB.getVehicles(identifier)
return MySQL.rawExecute.await(
[[
SELECT
`id`,
`model`,
`stored`
FROM `vehicles`
WHERE `owner` = ?
]],
{
identifier
}
)
end
ทำให้ภายหลังเปลี่ยน API หรือ SQL ได้จากจุดเดียว
㊻ ทำไม Database Layer ช่วย Performance
เพราะ Query เดียวกันจะไม่ถูกเขียนซ้ำหลายรูปแบบทั่ว Resource
สามารถรวม:
Query
Cache
Result Normalization
Validation
Metrics
ไว้ที่เดียว
และ Benchmark ได้ง่ายขึ้น
㊼ Client ควรเรียก rawExecute โดยตรงไหม
ไม่ควรให้ Client ทำ Database Authority โดยตรง
Architecture:
Client
↓
Network Event
↓
Server Validate
↓
rawExecute
↓
Server Process Result
↓
ส่งเฉพาะข้อมูลที่จำเป็น
Cfx.re เตือนว่า Client สามารถ Trigger Network Events และแนะนำให้ตรวจข้อมูลสำคัญด้วย Server-side Methods เช่น State, Position และ Permissions.
㊽ Prepared Query ไม่ได้ทำ Event ปลอดภัย
ต่อให้ SQL ใช้ Placeholder อย่างถูกต้อง Client Request ก็ยังอาจไม่ควรได้รับอนุญาต
ต้องแยก:
SQL Safety
≠
Gameplay Authorization
Server ต้องตรวจ:
Player คือใคร
Record เป็นของใคร
Permission ถูกต้องหรือไม่
Request อยู่ใน State ที่อนุญาตหรือไม่
Cfx.re แนะนำ Server-side Validation สำหรับ Networked Events.
㊾ ตัวอย่าง Request Character List
RegisterNetEvent(
'character:requestList',
function()
local src = source
local identifier =
GetPlayerIdentifierByType(
src,
'license'
)
if not identifier then
return
end
local characters =
MySQL.rawExecute.await(
[[
SELECT
`id`,
`firstname`,
`lastname`
FROM `characters`
WHERE `identifier` = ?
ORDER BY `id` ASC
]],
{
identifier
}
)
TriggerClientEvent(
'character:receiveList',
src,
characters
)
end
)
Cfx.re ระบุว่า RegisterNetEvent ใช้เมื่อ Event ต้องเดินทางข้าม Client/Server Context และควรเพิ่ม Server-side Checks สำหรับข้อมูลสำคัญ.
㊿ อย่าให้ Client ส่ง Identifier มาเองถ้า Server หาได้
หลีกเลี่ยง:
RegisterNetEvent(
'character:requestList',
function(identifier)
-- trust identifier
end
)
ถ้า Server สามารถ Resolve Identifier จาก source ได้เอง
Cfx.re แนะนำให้ดึงค่าที่สำคัญด้วย Server-side Methods แทนปล่อยให้ Client กำหนด.
51. อย่าส่ง Database Row ทั้งหมดกลับ Client
สมมติ Table มี:
id
identifier
firstname
lastname
internal_flag
private_note
created_at
ถ้า Client ต้องใช้เพียง:
id
firstname
lastname
Query เฉพาะ:
SELECT
`id`,
`firstname`,
`lastname`
ลด Data Exposure และลด Result Size ที่ไม่จำเป็น
52. rawExecute เหมาะกับ Logs หรือไม่
อาจเหมาะถ้า:
Query Structure เดิม
+
ถูกเรียกบ่อย
+
คืนหลาย Rows
เช่น:
local logs =
MySQL.rawExecute.await(
[[
SELECT
`id`,
`event`,
`created_at`
FROM `logs`
WHERE `identifier` = ?
ORDER BY `id` DESC
LIMIT 50
]],
{
identifier
}
)
แต่ Table Logs ขนาดใหญ่ยังต้องมี Query/Index/Pagination Design ที่เหมาะสม
53. rawExecute ไม่แทน Pagination
ต่อให้ Query API เร็ว หาก Query คืน:
100,000 Rows
Resource ยังต้องรับและ Process ข้อมูลจำนวนมาก
ควรใช้:
LIMIT
Pagination
Filter
ตาม Use Case
54. rawExecute ไม่แทน EXPLAIN
ถ้า Query ช้าเพราะ Database Access Plan ไม่ดี ต้องดู:
EXPLAIN
Index
Table Size
Rows
การเปลี่ยน Wrapper Function เพียงอย่างเดียวไม่แก้ SQL Structure
55. rawExecute กับ Slow Query Warning
ถ้า rawExecute ยังช้า อย่าคิดว่า API พังทันที
ตรวจ:
Query Time
Query Count
Index
Database CPU
Server Hitch
Result Size
oxmysql ระบุว่าความเร็ว Query แตกต่างตาม Hardware, Settings, Database Version และ Current Workload.
56. FiveM Profiler ยังควรใช้ไหม
ควร หาก Server มี Hitch และไม่แน่ใจว่าปัญหาเกิดจาก Database หรือ Resource Code
เพราะ Database Query อาจเร็ว แต่ Code หลัง Query เช่น:
JSON Processing
Loop Results
Nested Tables
Events
อาจเป็น Bottleneck แทน
57. Result 1,000 Rows แล้ว Loop หนักเกิดจาก rawExecute ไหม
ไม่จำเป็น
อาจเป็น:
Database Query
= เร็ว
Lua Processing
= ช้า
ดังนั้นต้องแยก Database Timing ออกจาก Resource Processing
58. rawExecute กับ Resource Restart
rawExecute เป็น Database API
Resource Restart จะ Reset:
Lua Variables
Cache
Runtime State
แต่ Persistent Records ใน Database ยังคงอยู่ตาม Database Storage
Resource จึงควร Load Data ใหม่เมื่อเริ่มหากจำเป็น
59. rawExecute กับ State Bag ต่างกันอย่างไร
rawExecute
= อ่าน/เขียน Database
State Bag
= Runtime Network State
อย่า Query Database ทุกครั้งที่ Client ต้องรู้ Runtime State ซึ่งสามารถอยู่ใน Server Memory/State Bag ได้อย่างเหมาะสม
60. ตารางเลือก oxmysql API
| ต้องการ | API ที่ควรพิจารณา |
|---|---|
| Query ทั่วไปหลาย Rows | MySQL.query |
| Row เดียว | MySQL.single |
| ค่าเดียว | MySQL.scalar |
| Insert ID | MySQL.insert |
| affectedRows | MySQL.update |
| Query เรียกบ่อย Result ยืดหยุ่น | MySQL.prepare |
| Query เรียกบ่อย SELECT เป็น Array | MySQL.rawExecute |
| หลาย Query ต้อง Commit พร้อมกัน | MySQL.transaction |
ความแตกต่างเรื่อง Result ของ query, single, scalar, prepare และ rawExecute ถูกกำหนดไว้ใน oxmysql API Documentation.
Checklist FiveM MySQL.rawExecute
ก่อนใช้ตรวจ:
Query ถูกเรียกบ่อยจริงหรือไม่
SQL Structure เดิมหรือไม่
ต้องการ Array Result หรือไม่
MySQL.queryเพียงพอหรือไม่MySQL.singleเหมาะกว่าหรือไม่MySQL.scalarเหมาะกว่าหรือไม่ต้องการ Insert ID หรือไม่
ต้องการ affectedRows หรือไม่
ใช้
?Placeholderใช้
??เฉพาะเมื่อจำเป็นไม่ใช้ Named Placeholder
ไม่ต่อ Client Input เข้า SQL String
เลือก Columns ที่จำเป็น
ใช้ LIMIT เมื่อเหมาะสม
ใช้ Pagination เมื่อข้อมูลเยอะ
ตรวจ Result เป็น Array
ตรวจ Array ว่าง
ตรวจ Date Result Type
ตรวจ TINYINT/BIT Result
ไม่ใช้แทน Database Index
ไม่ใช้แทน Cache
ไม่ใช้แทน Transaction
ไม่ Query ทุก Frame
ตรวจ Query Count
ตรวจ Query Time
เปิด
mysql_debugเมื่อ Benchmarkตรวจ Database Workload
ตรวจ Index
ตรวจ Resource Processing หลัง Query
Validate Network Events
ดึง Identifier จาก Server เมื่อทำได้
ไม่ส่ง Private Columns กลับ Client
Test Player Disconnect
Test Resource Restart
Benchmark Before/After
FiveM MySQL.rawExecute ใช้ยังไง
ตัวอย่าง:
local rows =
MySQL.rawExecute.await(
[[
SELECT
`id`,
`firstname`,
`lastname`
FROM `characters`
WHERE `identifier` = ?
]],
{
identifier
}
)
for i = 1, #rows do
print(
rows[i].firstname,
rows[i].lastname
)
end
Official API รองรับ MySQL.rawExecute.await() และ SELECT Result เป็น Array of Rows.
FiveM rawExecute ต่างจาก prepare อย่างไร
ความต่างที่ชัดที่สุดคือ:
rawExecute
SELECT
→ Array of Rows
prepare
SELECT
→ Column / Row / Array
ตามจำนวนข้อมูลที่ Query ได้.
FiveM rawExecute ต่างจาก query อย่างไร
query เป็น General Query API ส่วน rawExecute ถูกออกแบบสำหรับ Frequently-called Queries และรองรับ Parameter Sets หลายชุด.
หาก Query ไม่ใช่ Hot Path query อาจอ่านง่ายและเพียงพอ
FiveM rawExecute เร็วกว่า prepare ไหม
ไม่มีคำตอบตายตัวจาก Environment หนึ่งไปยังอีก Environment
oxmysql ระบุว่า Query Speed ขึ้นกับ:
Hardware
Database Settings
Database Version
Current Workload
จึงควร Benchmark ด้วย mysql_debug หรือ Debug UI.
FiveM rawExecute เร็วกว่า MySQL.query ไหม
Documentation ระบุว่า rawExecute สามารถใช้สำหรับ Frequently-called Queries ได้เร็วขึ้น แต่ไม่ได้รับประกัน Performance Benefit เดียวกันสำหรับทุก Query.
Query Frequency และ SQL Design ยังสำคัญมาก
FiveM rawExecute คืน Row เดียวได้ไหม
เมื่อใช้ SELECT ให้คาดหวัง Array of Rows
ถ้าต้องการ Row แรก:
local row = rows[1]
ถ้าระบบต้องการหนึ่ง Row อยู่แล้ว MySQL.single อาจตรง Intent กว่า.
FiveM rawExecute ใช้ Placeholder อะไร
รองรับ:
?
สำหรับ Value และ:
??
สำหรับ Column
Named Placeholder ไม่รองรับใน API นี้.
FiveM rawExecute ใช้กับ Client ได้ไหม
Database Logic ควรทำฝั่ง Server และ Network Request ต้อง Validate ฝั่ง Server
Cfx.re ระบุว่า Cheats สามารถ Trigger Client-to-Server Network Events และแนะนำให้ Server ตรวจข้อมูลสำคัญด้วย Server-side Methods.
FiveM rawExecute แก้ Slow Query ได้ไหม
ไม่เสมอไป
ถ้าปัญหาคือ:
ไม่มี Index
Query Scan หนัก
Result ใหญ่
Database CPU สูง
Query ถูกเรียกมากเกินไป
ต้องแก้สาเหตุนั้นโดยตรง
rawExecute เป็นเพียงหนึ่งใน Query APIs
FiveM rawExecute ใช้แทน Cache ดีไหม
ไม่
หากข้อมูลถูกอ่านซ้ำตลอดแต่แทบไม่เปลี่ยน:
Database Load
↓
Cache
↓
ใช้งานจาก Memory
อาจลด Database Workload ได้มากกว่าการเปลี่ยน Query API อย่างเดียว
FAQ FiveM MySQL.rawExecute
MySQL.rawExecute คืออะไร
เป็น oxmysql API สำหรับ Frequently-called Queries รองรับ Parameter Sets หลายชุด และ SELECT จะคืน Array of Rows.
rawExecute.await ใช้ได้ไหม
ได้ Official Lua API รองรับ Promise/Await โดยตรง.
rawExecute ใช้ Callback ได้ไหม
ได้ โดยมี Callback API แยกเช่นกัน.
rawExecute กับ prepare ต่างกันตรงไหน
rawExecute คืน Array สำหรับ SELECT ส่วน prepare สามารถคืน Column, Row หรือ Array ตาม Result.
rawExecute ใช้ ? ได้ไหม
ได้ ใช้สำหรับ Value Placeholder.
rawExecute ใช้ ?? ได้ไหม
ได้ ใช้สำหรับ Column Placeholder.
rawExecute ใช้ Named Placeholder ได้ไหม
ไม่ได้ Documentation ระบุว่า Named Placeholder จะ Error.
rawExecute แทน query ได้ทุกกรณีไหม
ไม่จำเป็น MySQL.query เป็น API ทั่วไปที่คืน Rows/Columns ของ SELECT และอาจเหมาะกับ Query ที่ไม่ได้ถูกเรียกหนัก.
rawExecute แทน single ได้ไหม
ทำได้ในเชิงการ Execute SQL บางกรณี แต่ถ้าต้องการหนึ่ง Row MySQL.single มี Return Contract ที่ตรงกว่า.
rawExecute แทน scalar ได้ไหม
ไม่ควรหากต้องการเพียงหนึ่งค่า เพราะ MySQL.scalar ถูกออกแบบให้คืน Column แรกของหนึ่ง Row.
rawExecute เร็วที่สุดของ oxmysql ไหม
ไม่ควรสรุปเช่นนั้น Performance แตกต่างตาม Hardware, Database Configuration, Version และ Workload และควรวัดด้วย Debug Tools.
rawExecute ปลอดภัยจาก Client Cheat ไหม
API Database ไม่ได้ทำให้ Network Event ปลอดภัยโดยอัตโนมัติ Server ยังต้อง Validate Requests และดึงค่าที่สำคัญด้วย Server-side Methods.
ประเด็นสำคัญ
FiveM MySQL.rawExecute ของ oxmysql เหมาะกับ Query Structure เดิมที่ถูกเรียกบ่อย และรองรับ Parameter Sets หลายชุด โดยข้อแตกต่างสำคัญจาก MySQL.prepare คือเมื่อใช้ SELECT จะคืนข้อมูลในรูป Array of Rows ทำให้ Resource ที่ต้อง Process List สามารถใช้ Result Structure ที่คาดเดาได้ง่ายขึ้น.
แต่ไม่ควรเปลี่ยนทุก SQL เป็น rawExecute เพราะ oxmysql มี API เฉพาะสำหรับ Use Case ต่างๆ เช่น single สำหรับหนึ่ง Row, scalar สำหรับหนึ่งค่า, insert สำหรับ Insert ID และ update สำหรับ affectedRows.
และต้องจำว่า:
rawExecute
≠ Index
rawExecute
≠ Cache
rawExecute
≠ Transaction
rawExecute
≠ Server-side Security
หาก Query ช้า ให้ดู Query Time, Query Count, SQL Structure, Index และ Database Workload พร้อม Benchmark บน Environment จริง เพราะ oxmysql ระบุว่า Performance สามารถแตกต่างอย่างมากตาม Hardware, Database Settings, Database Version และ Current Workload.
สำหรับผู้อ่าน comsiam ให้จำสูตร “Query ซ้ำบ่อย + ต้องการ Array → พิจารณา rawExecute → Benchmark” และ comsiam แนะนำให้เลือก oxmysql API จาก Return Data ที่ Resource ต้องการก่อน แล้วค่อย Optimize Hot Path ที่วัดแล้วว่าเป็น Bottleneckจริง จะดีกว่าการเปลี่ยน Query ทั้ง Server เป็น API เดียวโดยไม่มีข้อมูลรองรับ
Comments
Post a Comment