Google is committed to advancing racial equity for Black communities. See how.
หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

ใช้เงื่อนไขในกฎฐานข้อมูลแบบเรียลไทม์

คู่มือนี้สร้างจากการ เรียนรู้หลักภาษาของกฎความปลอดภัย Firebase เพื่อแสดงวิธีเพิ่มเงื่อนไขให้กับกฎการรักษาความปลอดภัยฐานข้อมูลแบบเรียลไทม์ของ Firebase

ส่วนประกอบหลักของกฎการรักษาความปลอดภัยฐานข้อมูลแบบเรียลไทม์คือ เงื่อนไข เงื่อนไขคือนิพจน์บูลีนที่กำหนดว่าการดำเนินการเฉพาะควรได้รับอนุญาตหรือปฏิเสธ สำหรับกฎพื้นฐานการใช้ตัวอักษร true และ false เนื่องจากเงื่อนไขทำงานได้ดีในระดับจังหวัด แต่ภาษากฎการรักษาความปลอดภัยฐานข้อมูลแบบเรียลไทม์ช่วยให้คุณสามารถเขียนเงื่อนไขที่ซับซ้อนมากขึ้นซึ่งสามารถ:

  • ตรวจสอบการรับรองความถูกต้องของผู้ใช้
  • ประเมินข้อมูลที่มีอยู่เทียบกับข้อมูลที่ส่งใหม่
  • เข้าถึงและเปรียบเทียบส่วนต่างๆของฐานข้อมูลของคุณ
  • ตรวจสอบข้อมูลขาเข้า
  • ใช้โครงสร้างของแบบสอบถามขาเข้าสำหรับตรรกะความปลอดภัย

การใช้ $ Variables เพื่อจับภาพกลุ่มเส้นทาง

คุณสามารถจับภาพบางส่วนของเส้นทางสำหรับการอ่านหรือเขียนโดยการประกาศตัวแปรการดักจับด้วยคำนำหน้า $ สิ่งนี้ทำหน้าที่เป็นตัวแทนและเก็บค่าของคีย์นั้นเพื่อใช้ภายในเงื่อนไขกฎ:

{
  "rules": {
    "rooms": {
      // this rule applies to any child of /rooms/, the key for each room id
      // is stored inside $room_id variable for reference
      "$room_id": {
        "topic": {
          // the room's topic can be changed if the room id has "public" in it
          ".write": "$room_id.contains('public')"
        }
      }
    }
  }
}

ตัวแปร $ ไดนามิกยังสามารถใช้ควบคู่กับชื่อพา ธ คงที่ ในตัวอย่างนี้เรากำลังใช้ $other ตัวแปรประกาศ .validate กฎ ที่ทำให้มั่นใจว่า widget มีเด็กคนอื่น ๆ กว่าไม่มี title และ color การเขียนใด ๆ ที่จะส่งผลให้มีการสร้างเด็กเพิ่มเติมจะล้มเหลว

{
  "rules": {
    "widget": {
      // a widget can have a title or color attribute
      "title": { ".validate": true },
      "color": { ".validate": true },

      // but no other child paths are allowed
      // in this case, $other means any key excluding "title" and "color"
      "$other": { ".validate": false }
    }
  }
}

การรับรองความถูกต้อง

หนึ่งในรูปแบบกฎความปลอดภัยที่พบบ่อยที่สุดคือการควบคุมการเข้าถึงตามสถานะการพิสูจน์ตัวตนของผู้ใช้ ตัวอย่างเช่นแอปของคุณอาจต้องการอนุญาตให้เฉพาะผู้ใช้ที่ลงชื่อเข้าใช้เขียนข้อมูล

หากแอปของคุณใช้ Firebase Authentication ตัวแปร request.auth จะมีข้อมูลการตรวจสอบสิทธิ์สำหรับไคลเอ็นต์ที่ร้องขอข้อมูล สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ request.auth โปรดดู เอกสารอ้างอิง

การตรวจสอบสิทธิ์ Firebase ผสานรวมกับฐานข้อมูลเรียลไทม์ของ Firebase เพื่อให้คุณควบคุมการเข้าถึงข้อมูลตามผู้ใช้แต่ละรายโดยใช้เงื่อนไข เมื่อผู้ใช้ตรวจสอบสิทธิ์แล้วตัวแปร auth ในกฎ Realtime Database Security Rules จะถูกเติมด้วยข้อมูลของผู้ใช้ ข้อมูลนี้รวมถึงตัวระบุเฉพาะ ( uid ) ตลอดจนข้อมูลบัญชีที่เชื่อมโยงเช่นรหัส Facebook หรือที่อยู่อีเมลและข้อมูลอื่น ๆ หากคุณใช้ผู้ให้บริการรับรองความถูกต้องที่กำหนดเองคุณสามารถเพิ่มฟิลด์ของคุณเองลงในเพย์โหลดการตรวจสอบสิทธิ์ของผู้ใช้ของคุณ

ส่วนนี้จะอธิบายวิธีการรวมภาษา Firebase Realtime Database Security กับข้อมูลการตรวจสอบสิทธิ์เกี่ยวกับผู้ใช้ของคุณ ด้วยการรวมแนวคิดทั้งสองนี้เข้าด้วยกันคุณสามารถควบคุมการเข้าถึงข้อมูลตามข้อมูลประจำตัวของผู้ใช้

ตัวแปรการ auth

กำหนดไว้ล่วงหน้า auth ตัวแปรในกฎระเบียบเป็นโมฆะก่อนที่จะรับรองความถูกต้องเกิดขึ้น

เมื่อตรวจสอบผู้ใช้ด้วย Firebase Authentication แล้วจะมีแอตทริบิวต์ต่อไปนี้:

ผู้ให้บริการ วิธีการตรวจสอบสิทธิ์ที่ใช้ ("รหัสผ่าน", "ไม่ระบุชื่อ", "facebook", "github", "google" หรือ "twitter")
เอ่อ รหัสผู้ใช้ที่ไม่ซ้ำกันรับประกันว่าจะไม่ซ้ำกันในผู้ให้บริการทั้งหมด
โทเค็น เนื้อหาของโทเค็น Firebase Auth ID ดูเอกสารอ้างอิงสำหรับ auth.token สำหรับรายละเอียดเพิ่มเติม

นี่คือตัวอย่างกฎที่ใช้ตัวแปร auth เพื่อให้แน่ใจว่าผู้ใช้แต่ละคนสามารถเขียนไปยังเส้นทางเฉพาะผู้ใช้เท่านั้น:

{
  "rules": {
    "users": {
      "$user_id": {
        // grants write access to the owner of this user account
        // whose uid must exactly match the key ($user_id)
        ".write": "$user_id === auth.uid"
      }
    }
  }
}

การจัดโครงสร้างฐานข้อมูลของคุณเพื่อรองรับเงื่อนไขการพิสูจน์ตัวตน

โดยปกติจะมีประโยชน์ในการจัดโครงสร้างฐานข้อมูลของคุณในลักษณะที่ทำให้การเขียนกฎง่ายขึ้น รูปแบบทั่วไปอย่างหนึ่งในการจัดเก็บข้อมูลผู้ใช้ในฐานข้อมูลแบบเรียลไทม์คือการจัดเก็บผู้ใช้ทั้งหมดของคุณในโหนด users เดียวซึ่งมีลูกเป็นค่า uid สำหรับผู้ใช้ทุกคน หากคุณต้องการ จำกัด การเข้าถึงข้อมูลนี้เพื่อให้มีเพียงผู้ใช้ที่ลงชื่อเข้าใช้เท่านั้นที่สามารถดูข้อมูลของตนเองได้กฎของคุณจะมีลักษณะดังนี้

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid"
      }
    }
  }
}

ทำงานกับ Authentication Custom Claims

สำหรับแอปที่ต้องการการควบคุมการเข้าถึงแบบกำหนดเองสำหรับผู้ใช้ที่แตกต่างกัน Firebase Authentication ช่วยให้นักพัฒนาสามารถ ตั้งค่าการอ้างสิทธิ์ของผู้ใช้ Firebase ได้ การอ้างสิทธิ์เหล่านี้สามารถ auth.token ในตัวแปร auth.token ในกฎของคุณ นี่คือตัวอย่างของกฎที่ใช้ประโยชน์จากการอ้างสิทธิ์ที่กำหนดเอง hasEmergencyTowel :

{
  "rules": {
    "frood": {
      // A towel is about the most massively useful thing an interstellar
      // hitchhiker can have
      ".read": "auth.token.hasEmergencyTowel === true"
    }
  }
}

นักพัฒนาที่สร้าง โทเค็นการตรวจสอบสิทธิ์แบบกำหนดเอง สามารถเลือกที่จะเพิ่มการอ้างสิทธิ์ให้กับโทเค็นเหล่านี้ได้ การอ้างสิทธิ์เหล่านี้สามารถใช้ได้กับตัวแปร auth.token ในกฎของคุณ

ข้อมูลที่มีอยู่กับข้อมูลใหม่

ตัวแปร data กำหนดไว้ล่วงหน้าถูกใช้เพื่ออ้างถึงข้อมูลก่อนที่จะมีการดำเนินการเขียน ในทางกลับกันตัวแปร newData มีข้อมูลใหม่ที่จะมีอยู่หากการดำเนินการเขียนสำเร็จ newData แสดงถึงผลลัพธ์ที่ผสานของข้อมูลใหม่ที่ถูกเขียนและข้อมูลที่มีอยู่

เพื่อเป็นตัวอย่างกฎนี้จะช่วยให้เราสามารถสร้างระเบียนใหม่หรือลบบันทึกที่มีอยู่ได้ แต่ไม่สามารถเปลี่ยนแปลงข้อมูลที่ไม่เป็นค่าว่างที่มีอยู่ได้:

// we can write as long as old data or new data does not exist
// in other words, if this is a delete or a create, but not an update
".write": "!data.exists() || !newData.exists()"

การอ้างอิงข้อมูลในเส้นทางอื่น

ข้อมูลใด ๆ ที่สามารถใช้เป็นเกณฑ์สำหรับกฎ การใช้ root data และ newData ตัวแปรที่กำหนดไว้ล่วงหน้าเราสามารถเข้าถึงเส้นทางใดก็ได้ตามที่มีอยู่ก่อนหรือหลังเหตุการณ์การเขียน

ลองพิจารณาตัวอย่างนี้ซึ่งอนุญาตให้เขียนได้ตราบเท่าที่ค่าของ /allow_writes/ node เป็น true โหนดพาเรนต์ไม่มีชุดแฟ readOnly และมีชายด์ชื่อ foo ในข้อมูลที่เขียนใหม่:

".write": "root.child('allow_writes').val() === true &&
          !data.parent().child('readOnly').exists() &&
          newData.child('foo').exists()"

การตรวจสอบข้อมูล

การบังคับใช้โครงสร้างข้อมูลและการตรวจสอบความถูกต้องของรูปแบบและเนื้อหาของข้อมูลควรทำโดยใช้. .validate กฎซึ่งจะรันหลังจากกฎ . .write สำเร็จเพื่อให้สิทธิ์การเข้าถึงเท่านั้น ด้านล่างนี้คือตัวอย่างตรวจ .validate ความถูกต้องของกฎซึ่งอนุญาตเฉพาะวันที่ในรูปแบบปปปป - ดด - ววระหว่างปี 1900-2099 ซึ่งตรวจสอบโดยใช้นิพจน์ทั่วไป

".validate": "newData.isString() &&
              newData.val().matches(/^(19|20)[0-9][0-9][-\\/. ](0[1-9]|1[012])[-\\/. ](0[1-9]|[12][0-9]|3[01])$/)"

.validate กฎเป็นชนิดเดียวของกฎการรักษาความปลอดภัยซึ่งไม่ได้มีน้ำตก หากกฎการตรวจสอบความถูกต้องล้มเหลวในเร็กคอร์ดย่อยใด ๆ การดำเนินการเขียนทั้งหมดจะถูกปฏิเสธ นอกจากนี้นิยามการตรวจสอบความถูกต้องจะถูกละเว้นเมื่อข้อมูลถูกลบ (นั่นคือเมื่อค่าใหม่ที่เขียนเป็น null )

สิ่งเหล่านี้อาจดูเหมือนเป็นเรื่องเล็กน้อย แต่ในความเป็นจริงเป็นคุณสมบัติที่สำคัญสำหรับการเขียนกฎความปลอดภัยฐานข้อมูลแบบเรียลไทม์ของ Firebase ที่มีประสิทธิภาพ พิจารณากฎต่อไปนี้:

{
  "rules": {
    // write is allowed for all paths
    ".write": true,
    "widget": {
      // a valid widget must have attributes "color" and "size"
      // allows deleting widgets (since .validate is not applied to delete rules)
      ".validate": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99
        ".validate": "newData.isNumber() &&
                      newData.val() >= 0 &&
                      newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical
        // /valid_colors/ index
        ".validate": "root.child('valid_colors/' + newData.val()).exists()"
      }
    }
  }
}

เมื่อคำนึงถึงตัวแปรนี้ให้ดูที่ผลลัพธ์สำหรับการดำเนินการเขียนต่อไปนี้:

JavaScript
var ref = db.ref("/widget");

// PERMISSION_DENIED: does not have children color and size
ref.set('foo');

// PERMISSION DENIED: does not have child color
ref.set({size: 22});

// PERMISSION_DENIED: size is not a number
ref.set({ size: 'foo', color: 'red' });

// SUCCESS (assuming 'blue' appears in our colors list)
ref.set({ size: 21, color: 'blue'});

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child('size').set(99);
วัตถุประสงค์ -C
FIRDatabaseReference *ref = [[[FIRDatabase database] reference] child: @"widget"];

// PERMISSION_DENIED: does not have children color and size
[ref setValue: @"foo"];

// PERMISSION DENIED: does not have child color
[ref setValue: @{ @"size": @"foo" }];

// PERMISSION_DENIED: size is not a number
[ref setValue: @{ @"size": @"foo", @"color": @"red" }];

// SUCCESS (assuming 'blue' appears in our colors list)
[ref setValue: @{ @"size": @21, @"color": @"blue" }];

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
[[ref child:@"size"] setValue: @99];
รวดเร็ว
var ref = FIRDatabase.database().reference().child("widget")

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo")

// PERMISSION DENIED: does not have child color
ref.setValue(["size": "foo"])

// PERMISSION_DENIED: size is not a number
ref.setValue(["size": "foo", "color": "red"])

// SUCCESS (assuming 'blue' appears in our colors list)
ref.setValue(["size": 21, "color": "blue"])

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("widget");

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo");

// PERMISSION DENIED: does not have child color
ref.child("size").setValue(22);

// PERMISSION_DENIED: size is not a number
Map<String,Object> map = new HashMap<String, Object>();
map.put("size","foo");
map.put("color","red");
ref.setValue(map);

// SUCCESS (assuming 'blue' appears in our colors list)
map = new HashMap<String, Object>();
map.put("size", 21);
map.put("color","blue");
ref.setValue(map);

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
ส่วนที่เหลือ
# PERMISSION_DENIED: does not have children color and size
curl -X PUT -d 'foo' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION DENIED: does not have child color
curl -X PUT -d '{"size": 22}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION_DENIED: size is not a number
curl -X PUT -d '{"size": "foo", "color": "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# SUCCESS (assuming 'blue' appears in our colors list)
curl -X PUT -d '{"size": 21, "color": "blue"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# If the record already exists and has a color, this will
# succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
# will fail to validate
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

ตอนนี้เรามาดูโครงสร้างเดียวกัน แต่ใช้กฎ. .write แทน . .validate :

{
  "rules": {
    // this variant will NOT allow deleting records (since .write would be disallowed)
    "widget": {
      // a widget must have 'color' and 'size' in order to be written to this path
      ".write": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99, ONLY IF WE WRITE DIRECTLY TO SIZE
        ".write": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical valid_colors/ index
        // BUT ONLY IF WE WRITE DIRECTLY TO COLOR
        ".write": "root.child('valid_colors/'+newData.val()).exists()"
      }
    }
  }
}

ในตัวแปรนี้การดำเนินการใด ๆ ต่อไปนี้จะสำเร็จ:

JavaScript
var ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.set({size: 99999, color: 'red'});

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child('size').set(99);
วัตถุประสงค์ -C
Firebase *ref = [[Firebase alloc] initWithUrl:URL];

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
[ref setValue: @{ @"size": @9999, @"color": @"red" }];

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
[[ref childByAppendingPath:@"size"] setValue: @99];
รวดเร็ว
var ref = Firebase(url:URL)

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.setValue(["size": 9999, "color": "red"])

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.childByAppendingPath("size").setValue(99)
Java
Firebase ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
Map<String,Object> map = new HashMap<String, Object>();
map.put("size", 99999);
map.put("color", "red");
ref.setValue(map);

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child("size").setValue(99);
ส่วนที่เหลือ
# ALLOWED? Even though size is invalid, widget has children color and size,
# so write is allowed and the .write rule under color is ignored
curl -X PUT -d '{size: 99999, color: "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# ALLOWED? Works even if widget does not exist, allowing us to create a widget
# which is invalid and does not have a valid color.
# (allowed by the write rule under "color")
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

สิ่งนี้แสดงให้เห็นความแตกต่างระหว่างกฎ . .write และ. .validate ตามที่แสดงให้เห็นแล้วกฎทั้งหมดนี้ควรเขียนโดยใช้. .validate โดยมีข้อยกเว้นที่เป็นไปได้ของกฎ newData.hasChildren() ซึ่งจะขึ้นอยู่กับว่าควรอนุญาตให้ลบได้หรือไม่

กฎตามแบบสอบถาม

แม้ว่าคุณ จะไม่สามารถใช้กฎเป็นตัวกรองได้ แต่คุณสามารถ จำกัด การเข้าถึงข้อมูลส่วนย่อยได้โดยใช้พารามิเตอร์การค้นหาในกฎของคุณ ใช้ query. นิพจน์ในกฎของคุณเพื่อให้สิทธิ์ในการอ่านหรือเขียนตามพารามิเตอร์การสืบค้น

ตัวอย่างเช่นกฎตามแบบสอบถามต่อไปนี้ใช้กฎ การรักษาความปลอดภัยที่อิงตามผู้ใช้ และ กฎ ตามคิวรีเพื่อ จำกัด การเข้าถึงข้อมูลในคอลเลกชัน baskets เฉพาะตะกร้าสินค้าที่ผู้ใช้ที่ใช้งานอยู่เป็นเจ้าของ:

"baskets": {
  ".read": "auth.uid != null &&
            query.orderByChild == 'owner' &&
            query.equalTo == auth.uid" // restrict basket access to owner of basket
}

การสืบค้นต่อไปนี้ซึ่งรวมถึงพารามิเตอร์การค้นหาในกฎจะประสบความสำเร็จ:

db.ref("baskets").orderByChild("owner")
                 .equalTo(auth.currentUser.uid)
                 .on("value", cb)                 // Would succeed

อย่างไรก็ตามการสืบค้นที่ไม่มีพารามิเตอร์ในกฎจะล้มเหลวด้วยข้อผิดพลาด PermissionDenied :

db.ref("baskets").on("value", cb)                 // Would fail with PermissionDenied

คุณยังสามารถใช้กฎที่ใช้แบบสอบถามเพื่อ จำกัด ปริมาณข้อมูลที่ไคลเอ็นต์ดาวน์โหลดผ่านการดำเนินการอ่าน

ตัวอย่างเช่นกฎต่อไปนี้ จำกัด การเข้าถึงการอ่านเฉพาะผลลัพธ์ 1,000 รายการแรกของแบบสอบถามตามลำดับความสำคัญ:

messages: {
  ".read": "query.orderByKey &&
            query.limitToFirst <= 1000"
}

// Example queries:

db.ref("messages").on("value", cb)                // Would fail with PermissionDenied

db.ref("messages").limitToFirst(1000)
                  .on("value", cb)                // Would succeed (default order by key)

query. ต่อไปนี้ นิพจน์มีอยู่ในกฎความปลอดภัยของฐานข้อมูลแบบเรียลไทม์

นิพจน์กฎที่อิงการสืบค้น
นิพจน์ ประเภท คำอธิบาย
query.orderByKey
query.orderByPriority
query.orderByValue
บูลีน True สำหรับการสืบค้นที่เรียงตามคีย์ลำดับความสำคัญหรือค่า เป็นอย่างอื่นเท็จ
query.orderByChild สตริง
โมฆะ
ใช้สตริงเพื่อแทนพา ธ สัมพัทธ์ไปยังโหนดลูก ตัวอย่างเช่น query.orderByChild == "address/zip" ถ้าโหนดลูกไม่ได้เรียงลำดับคิวรีค่านี้จะเป็นโมฆะ
query.startAt
query.endAt
query.equalTo
สตริง
จำนวน
บูลีน
โมฆะ
ดึงข้อมูลขอบเขตของคิวรีที่ดำเนินการหรือส่งคืนค่าว่างหากไม่มีชุดที่ถูกผูกไว้
query.limitToFirst
query.limitToLast
จำนวน
โมฆะ
ดึงขีด จำกัด ของคิวรีที่ดำเนินการหรือส่งคืนค่าว่างหากไม่มีการกำหนดขีด จำกัด

ขั้นตอนถัดไป

หลังจากการอภิปรายเงื่อนไขนี้คุณมีความเข้าใจที่ซับซ้อนมากขึ้นเกี่ยวกับกฎและพร้อมที่จะ:

เรียนรู้วิธีจัดการกรณีการใช้งานหลักและเรียนรู้ขั้นตอนการทำงานสำหรับการพัฒนาทดสอบและปรับใช้กฎ:

เรียนรู้คุณสมบัติของกฎที่เฉพาะเจาะจงสำหรับฐานข้อมูลเรียลไทม์: