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 ทั่วไปหลาย RowsMySQL.query
Row เดียวMySQL.single
ค่าเดียวMySQL.scalar
Insert IDMySQL.insert
affectedRowsMySQL.update
Query เรียกบ่อย Result ยืดหยุ่นMySQL.prepare
Query เรียกบ่อย SELECT เป็น ArrayMySQL.rawExecute
หลาย Query ต้อง Commit พร้อมกันMySQL.transaction

ความแตกต่างเรื่อง Result ของ query, single, scalar, prepare และ rawExecute ถูกกำหนดไว้ใน oxmysql API Documentation.

Checklist FiveM MySQL.rawExecute

ก่อนใช้ตรวจ:

  1. Query ถูกเรียกบ่อยจริงหรือไม่

  2. SQL Structure เดิมหรือไม่

  3. ต้องการ Array Result หรือไม่

  4. MySQL.query เพียงพอหรือไม่

  5. MySQL.single เหมาะกว่าหรือไม่

  6. MySQL.scalar เหมาะกว่าหรือไม่

  7. ต้องการ Insert ID หรือไม่

  8. ต้องการ affectedRows หรือไม่

  9. ใช้ ? Placeholder

  10. ใช้ ?? เฉพาะเมื่อจำเป็น

  11. ไม่ใช้ Named Placeholder

  12. ไม่ต่อ Client Input เข้า SQL String

  13. เลือก Columns ที่จำเป็น

  14. ใช้ LIMIT เมื่อเหมาะสม

  15. ใช้ Pagination เมื่อข้อมูลเยอะ

  16. ตรวจ Result เป็น Array

  17. ตรวจ Array ว่าง

  18. ตรวจ Date Result Type

  19. ตรวจ TINYINT/BIT Result

  20. ไม่ใช้แทน Database Index

  21. ไม่ใช้แทน Cache

  22. ไม่ใช้แทน Transaction

  23. ไม่ Query ทุก Frame

  24. ตรวจ Query Count

  25. ตรวจ Query Time

  26. เปิด mysql_debug เมื่อ Benchmark

  27. ตรวจ Database Workload

  28. ตรวจ Index

  29. ตรวจ Resource Processing หลัง Query

  30. Validate Network Events

  31. ดึง Identifier จาก Server เมื่อทำได้

  32. ไม่ส่ง Private Columns กลับ Client

  33. Test Player Disconnect

  34. Test Resource Restart

  35. 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

Popular posts from this blog

FiveM ยังน่าเล่นไหม? Enhanced เปลี่ยน FiveM แค่ไหน

FiveM คืออะไร เล่นอย่างไร สำหรับมือใหม่ เริ่มต้นตั้งแต่ศูนย์

วิธีตั้ง Admin Permission ด้วย add_ace และ add_principal FiveM แบบละเอียด