Hike News
Hike News

Basic Database System Part 4 - Normalization Process , 1NF , 2NF , 3NF, BCNF

Basic Database System Part 4 - Normalization Process , 1NF , 2NF , 3NF, BCNF

ในตอนที่แล้วเราได้รู้ว่า Normalization คืออะไรไปแล้ว ตอนนี้เรามาดูว่า Normalization มีขั้นตอนอะไรบ้าง จากนั้นเราไปรู้จัก 1NF , 2NF , 3NF, BCNF ว่ามันคืออะไร

Normalization Process

กระบวนการทำ Normalization มีขั้นตอนง่ายๆดังต่อไปนี้

  1. ตรวจสอบว่า Table ที่ทำการตรวจสอบนั้นอยู่ใน 1NF ไหม ถ้าไม่อยู่ในรูปแบบ 1NF ต้องทำให้อยู่ในรูปแบบ 1NF
  2. ทำการตรวจสอบว่า Table นั้นอยู่ใน NF ชั้นที่ต้องการแล้วรึยัง ถ้าไม่อยู่ให้ทำการแยก Table ตามหลักการของแต่ NF โดยทั่วไปแล้วเราจะทำจนกว่าจะถึงชั้น 5NF (ใครบอกถึง 3NF ถือว่าผิดนะครับ ต้องทำถึง 5NF)
  3. กลับทำข้อ 2 ไปเรื่อยๆจนกว่าทุก Table ที่เรามีจะอยู่ใน NF ที่เราต้องการ ( โดยทั่วไปคือต้องถึง 5NF )

อ่านถึงตรงนี้แล้วอาจจะงงเพราะติดศัพท์พวก NF คืออะไร 1NF คืออะไร 5 NF คืออะไร เดี๋ยวเรามาค่อยๆดูกันครับว่า NF คืออะไร ไล่ไปเรื่อยๆจนถึง BCNF แล้วไปต่อ 4NF , 5NF ในตอนต่อไป

Normal form (NF)

Normal form คือ Table (Relation) ที่อยู่ในรูปแบบที่ดี (Good form) โดยแต่ละ Normal form นั้นจะแก้ปัญหาเกี่ยวกับ INSERT UPDATE DELETE ในกรณีต่างๆ ซึ่งแต่ละ Normal form นั้นระบุเงื่อนไขไว้ว่าต้องมีคุณสมบัติอะไรจึงจะผ่าน Normal form นั้น ในกรณีไม่ผ่านก็จะมีวิธีแก้ไขบอกว่าต้องทำอย่างไร

1NF

A relation table structure T is in the first normal form (1NF) , if and only if all of its attributes contain atomic values only , and each attribute belongs to only one domain

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

Table ที่จะเป็น 1NF ได้นั้น Column (Attribute) ทั้งหมดของ Table นั้นจะต้องมีค่าเป็น atomic value และ Column เหล่านั้นต้องมีค่ามาจาก Domain เดียวกัน

อ่านแบบนี้คงจะไม่เข้าใจเรามาลองดูตัวอย่าง Table ที่ไม่ผ่าน 1NF กันดีกว่า

All of its attributes contain atomic values only : Column (Attribute) ทั้งหมดของ Table นั้นจะต้องมีค่าเป็น atomic value

เรามาดูตัวอย่าง Table ที่ Column (Attribute) มีค่าไม่เป็น atomic value

Column : MOBILE_NO ไม่เป็น Atomic value

จากตัวอย่าง Table ในภาพจะเห็นว่า Column “MOBILE_NO” นั้นไม่เป็น Automic value เพราะมีค่าเป็น List ของเบอร์มือถือที่ User คนนั้นมี ถามว่าปัญหาที่จะเกิดขึ้นจาก Table ที่เก็บค่าแบบนี้คือ เวลาคุณอยากเพิ่มเบอร์โทรขึ้นมาคุณจะสั่งยังไง มันจะไม่ใช่การสั่งแบบ INSERT เบอร์โทรศัพท์ใหม่เข้าไป แต่ต้องไปดูก่อนว่ามี Row ของ User นี้อยู่ใน Database ไหม ถ้าไม่มีจะ INSERT แต่ถ้ามีต้องเป็น UPDATE ค่าที่ Row เก่า ซึ่งคุณจะต้องทำการ Substring ตัดต่อ String เบอร์โทรศัพท์ เช่นกันถ้าต้องการลบข้อมูลโทรศัพท์คุณจะไม่ได้สั่ง DELETE ROW แต่จะกลายเป็นต้องเข้าไปหา ROW ของ USER นั้นจากนั้นดึงค่าจากนั้นต้อง Replace string แล้วเอาค่าใหม่ใส่เข้าไปแทน

อีกทั้งถ้าคุณจำได้ Relation คือ Subset of the Cartesian product of domains ผลลัพธ์ของ Cartesian product of domains ที่ออกมาใน attribute ต่างๆจะไม่มีทางเป็น Set หรือ List ดังนั้น Table ที่เราเห็นนี้ไม่ใช่ Relation ด้วยซ้ำไป

วิธีแก้ไข

วิธีแก้ไข Table นี้ให้อยู่ในรูปแบบ 1NF ก็คือทำให้มันเป็น Automic value คือให้ 1 row มี 1 ค่าไป ซึ่งจะได้ผลลัพธ์ออกมาดังภาพด้านล่าง

Table ที่ถูกแก้ให้เป็น 1NF

Each attribute belongs to only one domain : แต่ละ Column (Attribute) ต้องอยู่ภายใน 1 Domain เท่านั้น

เรามาดูตัวอย่าง Table ที่ไม่ถูกกฎนี้กัน

Column : VALUES มีค่ามาจากหลากหลาย Domain

จากตัวอย่าง Table จะเห็นว่า Column : VALUES นั้นมีค่ามาจากหลาย Domain คือจะเห็นว่าเป็นค่าที่มา Domain : เบอร์มือถือ , Domain : email ปัญหาที่จะเกิดกับ Table นี้คือ ถ้าคุณอยากหาแสดงข้อมูล USERNAME , EMAIL , MOBILE_NO, NICKNAME ของ USER ทั้งหมดที่มี EMAIL = ‘surapong007@hotmail.com’ คุณจะเขียน SQL ยังไงออกมา (เขียนได้นะครับแต่เขียนยากในระดับหนึ่งเลย)

อีกทั้ง Table นี้ไม่จัดเป็น Relation เพราะ ผลลัพธ์ของ Cartesian product of domains ที่ออกมาใน attribute ต่างๆต้องมาจาก Domain เดียวกันไม่ใช่มาจากหลากหลาย Domain

วิธีแก้ไข

วิธีแก้ไขให้ Table นี้อยู่ในรูปแบบ 1NF คือแยก Column ที่มาจากหลาย Domain ให้เป็น Column ใหม่ไปเลยดังภาพด้านล่าง

Table ที่ถูกแก้ให้เป็น 1NF

จะเห็นว่า Table ถูกเพิ่ม Column : EMAIL , MOBILE_NO, NICKNAME เข้าไป

ลองตรวจสอบกับ TABLE : CURRENTCY_TRADING_SYSTEM ว่าเป็น 1NF ไหม

TABLE : CURRENTCY_TRADING_SYSTEM

เราลองตรวจ TABLE : CURRENTCY_TRADING_SYSTEM ดูว่าเป็น 1NF ไหมโดยดูตามกฏของ 1NF ที่ว่า

  • All of its attributes contain atomic values only
    ข้อนี้ผ่านเพราะทุกค่าของแต่ละ Column เป็น Atomic value
  • Each attribute belongs to only one domain
    ข้อนี้ผ่านเพราะทุกค่าของแต่ละ Column มาจาก Domain เดียวกัน

ดังนั้น TABLE : CURRENTCY_TRADING_SYSTEM จึงเป็น 1NF

2NF

A relation table structure T is in the second normal form (2NF) if and only if the relationship between primary key and every nonkey attribute is in full functional dependence

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

Table ที่จะเป็น 2NF ได้นั้น ความสัมพันธ์ระหว่าง Primary key กับ nonkey attribute ทุกตัวต้องเป็น Full functional dependence

ผมเชื่อว่าอ่านแล้วอาจจะไม่เข้าใจเพราะติดศัพท์ทางเทคนิค เรามาดูศัพท์เทคนิคทีละตัวครับ

Nonkey attribute

คือ Column หรือกลุ่มของ Column ที่ไม่ได้เป็น Candidate key (จริงๆ Column มันคือ Attribute นั่นแหละ ถ้าตามทฤษฎีเราต้องเรียก Column ว่า Attribute เรียก Row ว่า Tuple แต่ขอละเป็น Column ละกันนะ)

Functional dependence (FD)

คือความสัมพันธ์แบบ Function อ่านแล้วงงใช่ไหมครับ คิดง่ายๆครับว่าถ้า A มีความสัมพันธ์แบบ Function กับ B ( A -> B ) แปลว่า ถ้าเรากำหนดค่า A เป็นค่าหนึ่ง จะได้ค่า B เป็นค่าหนึ่งเสมอ ซึ่งผมว่าก็ไม่เข้าใจอีกแหละ เราลองมาดูตัวอย่างอย่างกัน

 ความสัมพันธ์ระหว่าง USERNAME กับ EMAIL

คุณลองดู COLUMN USERNAME กับ EMAIL ครับ คุณจะเห็นว่าถ้าเรากำหนดค่า USERNAME เป็น sarun เราจะได้ email เป็น fax_inwza007@hotmail.com เสมอ ซึ่งไม่ได้แค่ USERNAME เป็น sarun USERNAME อื่นๆก็เป็นแบบนี้ ความสัมพันธ์แบบนี้คือ Funcational dependence ครับ ถามว่าแล้วเราจะรู้ได้ไงว่ามันเป็น ต้องมานั่งไล่ดูค่าทุกค่าเลยเหรอ จริงๆคือไม่ใช่ครับ เราดูค่าเบื้องต้นเพื่อคาดเดาว่าอาจจะใช่ การจะบอกว่าใช่หรือไม่ใช่นั้นต้องไปดูความสัมพันธ์ตามงานจริงครับ คือในงานนี้ USERNAME กับ EMAIL เป็น Functional dependence ( USERNAME -> EMAIL ) เพราะ USERNAME มี EMAIL ได้แค่ EMAIL เดียว ดังนั้นถ้ากำหนดค่า USERNAME อะไรก็จะอ้างถึง EMAIL ได้ EMAIL เดียวเสมอ มันจะแตกต่างกับความสัมพันธ์ระหว่าง USERNAME กับ CURRENCY_CODE ครับที่ 1 USERNAME สามารถมีได้หลาย CURRENCY_CODE

ความสัมพันธ์ระหว่าง USERNAME กับ CURRENCY_CODE

Full Functional dependence

ในส่วนที่แล้วเรารู้ว่า Functional dependence (FD) แต่ Full Functional dependence คืออะไรล่ะ อธิบายยากคุณลองดูตัวอย่างดีกว่า คุณสังเกตุดูจะเห็นว่า { USERNAME , CURRENCY_CODE } มีความสัมพันธ์ FD กับ EMAIL

คุณลองดูได้เลยครับว่าจริงไหม มันจริงแน่นอนครับเพราะ USERNAME มันเป็น FD กับ EMAIL ดังนั้นจะเอา USERNAME ไปรวมกับ Column ไหนมันก็เป็น FD หมดแหละ แต่สิ่งที่เราต้องการจริงๆคือความสัมพันธ์แบบ USERNAME -> EMAIL ไม่ใช่ { USERNAME , CURRENCY_CODE } -> EMAIL ดังนั้นจะเป็น Full Functional dependence ก็ต่อเมื่อ EMAIL นั้นขึ้นอยู่กับค่า USERNAME และ CURRENCY_CODE ทั้งคู่ ซึ่งจะเห็นว่าไม่ใช่เพราะ EMAIL ขึ้นอยู่กับค่า USERNAME ค่าเดียว

  • { USERNAME , CURRENCY_CODE } -> EMAIL
  • USERNAME -> EMAIL

หรือว่าง่ายๆถ้าหาค่าฝั่งซ้ายที่เป็น Subset ได้ก็แปลว่าไม่เป็น Full FD ละ จากตัวอย่าง { USERNAME , CURRENCY_CODE } -> EMAIL ไม่เป็น Full FD เพราะเราเจอ FD : USERNAME -> EMAIL ซึ่งจะเห็น USERNAME เป็น Subset ของ { USERNAME , CURRENCY_CODE }

ลองตรวจสอบกับ TABLE : CURRENTCY_TRADING_SYSTEM ว่าเป็น 2NF ไหม

เรามา List แต่ละส่วนประกอบที่เราต้องใช้ตรวจสอบ

  1. Primary key : { USERNAME , CURRENCY_CODE }

  2. Nonkey attribute มีดังต่อไปนี้

    • EMAIL
    • USER_TYPE
    • PRIORITY
    • CURRENCY_NAME
    • COUNTRY
    • AMOUNT
  3. Full Functional Dependency ของ Column (Attribute ) ใน Table นี้

    • USERNAME -> EMAIL
    • USERNAME -> USER_TYPE
    • USERNAME -> PRIORITY
    • CURRENCY_CODE -> CURRENCY_NAME
    • CURRENCY_CODE -> COUNTRY
    • USER_TYPE -> PRIORITY
    • { USERNAME , CURRENCY_CODE } -> AMOUNT

จากนิยาม

A relation table structure T is in the second normal form (2NF) if and only if the relationship between primary key and every nonkey attribute is in full functional dependence

Table ที่จะเป็น 2NF ได้นั้น ความสัมพันธ์ระหว่าง Primary key กับ nonkey attribute ทุกตัวต้องเป็น Full functional dependence

จะเห็นว่า TABLE : CURRENTCY_TRADING_SYSTEM ไม่เป็น 2NF เพราะ ความสัมพันธ์ระหว่าง Primary key กับ nonkey attribute ไม่ได้เป็น Full functional dependence ดูได้จาก { USERNAME , CURRENCY_CODE } ไม่ได้เป็น Full FD กับ EMAIL , USER_TYPE , PRIORITY , CURRENCY_NAME , COUNTRY

วิธีแก้ไข

วิธีแก้ทำให้ Table นี้เป็น 2NF ก็คือแยก Table ออกไปโดยเราจะแยก Table ตาม FD ที่เป็น Subset ของ Primary เลยครับ จากนั้นก็ตรวจสอบอีกครั้งว่าเป็น 2NF ไหม โดยจากตัวอย่างเราจะแยกได้ 3 Table คือ

Table : USER

TABLE : USER

ตรวจสอบว่า Table นี้เป็น 2NF ไหม

  1. Primary key : USERNAME

  2. Nonkey attribute มีดังต่อไปนี้

    • USER_TYPE

    • PRIORITY

      คุณอาจจะสงสัยว่าทำไม EMAIL หายไปไหน ที่มันหายไปเพราะ EMAIL เป็น Candidate key นะครับ

  3. Full Functional Dependency ของ Column (Attribute ) ใน Table นี้

    • USERNAME -> USER_TYPE
    • USERNAME -> PRIORITY
    • USER_TYPE -> PRIORITY

Table นี้เป็น 2NF เพราะ ความสัมพันธ์ระหว่าง Primary key กับ nonkey attribute เป็น Full FD ทั้งหมด

Table : CURRENCY

Table : CURRENCY

ตรวจสอบว่า Table นี้เป็น 2NF ไหม

  1. Primary key : CURRENCY_CODE

  2. Nonkey attribute มีดังต่อไปนี้

    • COUNTRY

      คุณอาจจะสงสัยว่าทำไม CURRENCY_NAME หายไปไหน ที่มันหายไปเพราะ CURRENCY_NAME เป็น Candidate key นะครับ ทำไม Country ไม่เป็น ที่ไม่เป็นก็เพราะบางประเทศมีหลายสกุลเงินนะครับ

  3. Full Functional Dependency ของ Column (Attribute ) ใน Table นี้

    • CURRENCY_CODE -> COUNTRY

Table : USER_CURRENCY

Table : USER_CURRENCY

ตรวจสอบว่า Table นี้เป็น 2NF ไหม

  1. Primary key : { USERNAME , CURRENCY_CODE }

  2. Nonkey attribute มีดังต่อไปนี้

    • COUNTRY

      คุณอาจจะสงสัยว่าทำไม CURRENCY_NAME หายไปไหน ที่มันหายไปเพราะ CURRENCY_NAME เป็น Candidate key นะครับ ทำไม Country ไม่เป็นล่ะ ที่ไม่เป็นก็เพราะบางประเทศมีหลายสกุลเงินครับ

  3. Full Functional Dependency ของ Column (Attribute ) ใน Table นี้

    • { USERNAME , CURRENCY_CODE } -> AMOUNT

Table นี้ 2NF เพราะ ความสัมพันธ์ระหว่าง Primary key กับ nonkey attribute เป็น Full FD ทั้งหมด

3NF

A relation table structure T is in the third normal form (3NF) if and only if T is in 2NF and there are no funcational dependencies between nonkey attributes of T

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

Table จะเป็น 3NF ก็ต่อเมื่อ Table นั้นเป็น 2NF และ Table นั้นไม่มี funcational dependence ระหว่าง nonkey attributes ของ Table นั้น

สำหรับขั้นนี้นั้นเราไม่ติดศัพท์อะไรแล้ว เรามาตรวจสอบ Table ที่เราได้จากขั้นตอนที่แล้วกันเลยว่าจะสามารถเป็น 3NF ได้หรือไม่

Table : USER_CURRENCY

Table : USER_CURRENCY

ตรวจสอบว่า Table นี้เป็น 3NF ไหม

  1. Nonkey attribute มีดังต่อไปนี้

    • COUNTRY
  2. FD ระหว่าง Nonkey attribute

    ไม่มีเพราะมี Nonkey attribute ตัวเดียว

เนื่องจาก Table นี้ไม่มี funcational dependence ระหว่าง nonkey attributes และ Table นี้ก็เป็น 2NF ดังนั้น Table จึงเป็น 3NF

Table : CURRENCY

Table : CURRENCY

ตรวจสอบว่า Table นี้เป็น 3NF ไหม

  1. Nonkey attribute มีดังต่อไปนี้

    • COUNTRY
  2. FD ระหว่าง Nonkey attribute

    ไม่มีเพราะมี Nonkey attribute ตัวเดียว

เนื่องจาก Table นี้ไม่มี funcational dependence ระหว่าง nonkey attributes และ Table นี้ก็เป็น 2NF ดังนั้น Table จึงเป็น 3NF

Table : USER

Table : USER

ตรวจสอบว่า Table นี้เป็น 3NF ไหม

  1. Nonkey attribute มีดังต่อไปนี้

    • USER_TYPE
    • PRIORITY
  2. FD ระหว่าง Nonkey attribute

    • USER_TYPE -> PRIORITY

Table นี้ไม่เป็น 3NF เพราะเราตรวจพบ FD คือ USER_TYPE -> PRIORITY ซึ่งเป็น Nonkey attribute

วิธีแก้ไข

ให้ทำการแยกตารางออกไปตาม FD ของ Nonkey attribute ครับ ส่วน Table เดิมเก็บ Column ที่เป็นตัวกำหนดไว้ ส่วน Column ที่มีค่าตามให้ลบออกจาก Table โดยจากตัวอย่างเราจะแก้ไข Table : USER ลบ Column : PRIORITY และ สร้าง Table USER_TYPE ขึ้นมาใหม่

Table : USER

Table : USER

ตรวจสอบว่า Table นี้เป็น 3NF ไหม

  1. Nonkey attribute มีดังต่อไปนี้

    • USER_TYPE
  2. FD ระหว่าง Nonkey attribute

    ไม่มีเพราะมี Nonkey attribute ตัวเดียว

เนื่องจาก Table นี้ไม่มี funcational dependence ระหว่าง nonkey attributes และ Table นี้ก็เป็น 2NF ดังนั้น Table จึงเป็น 3NF ตามนิยาม

Table : USER_TYPE

Table : USER_TYPE

ตรวจสอบว่า Table นี้เป็น 3NF ไหม

  1. Nonkey attribute มีดังต่อไปนี้

    • PRIORITY
  2. FD ระหว่าง Nonkey attribute

    ไม่มีเพราะมี Nonkey attribute ตัวเดียว

เนื่องจาก Table นี้ไม่มี funcational dependence ระหว่าง nonkey attributes และ Table นี้ก็เป็น 2NF ดังนั้น Table จึงเป็น 3NF ตามนิยาม

Table ที่เป็น 3NF ที่มีปัญหา INSERT , DELETE , UPDATE

Table ที่เป็น 3NF และมีปัญหา INSERT , DELETE , UPDATE

เรามาดูรายละเอียดของ TABLE นี้เพื่อนำไปใช้ในการตรวจสอบว่า Table นี้เป็น 3NF ไหม

  1. Primary key : { USERNAME , CURRENCY_CODE }
  2. Candidate key : { USERNAME , CURRENCY_CODE } , { EMAIL, CURRENCY_CODE }
  3. Nonkey attribute มีดังต่อไปนี้
    • AMOUNT
      EMAIL ไม่เป็น Nonkey attribute เพราะมันเป็น 1 ใน Candidate key
  4. FD ใน Table
    • { USERNAME , CURRENCY_CODE } -> AMOUNT
    • { EMAIL , CURRENCY_CODE } -> AMOUNT
    • USERNAME -> EMAIL
    • EMAIL -> USERNAME

ตรวจสอบ NF แต่ละขั้น

  • 1NF
    เป็น 1NF เพราะทุก Column ( Attribute ) เป็น Atomic และ แต่ละ Column มีค่ามาจาก Domain เดียว
  • 2NF
    เป็น 2NF เพราะ ความสัมพันธ์ระหว่าง Primary key กับ nonkey attribute ทุกตัวเป็น Full functional dependence ดูได้จาก { USERNAME , CURRENCY_CODE } เป็น Full FD กับ AMOUNT ซึ่งเป็น nonkey attribute ตัวเดียว
  • 3NF
    เป็น 3NF เพราะ Table นี้เป็น 2NF และ Table นี้ไม่มี funcational dependence ระหว่าง nonkey attributes ดูได้จาก Nonkey attribute มีตัวเดียว

ปัญหา Insert ข้อมูลไม่ถูกต้องเข้าไปได้

ตัวอย่างปัญหา Insert ข้อมูลไม่ถูกต้องเข้าไปได้

Table นี้จะมีปัญหาการ Insert ข้อมูลที่ไม่ถูกต้องเข้าไปได้ดูได้จากเราสามารถ Insert ข้อมูล EMAIL ของ USERNAME : apirat เป็น champ007@gmail.com ได้ ซึ่งนั่นทำให้เกิดปัญหา Data inconsistency

ปัญหา Insert ข้อมูลเข้าไปไม่ได้

จากตัวอย่างเราไม่สามารถ Insert ข้อมูลของ User ใหม่เข้าไปได้เลย เช่น ถ้าผมต้องการ Insert ข้อมูล User : wasinee เข้าไปผมจะไม่สามารถเข้าไปได้เพราะติดกฎ Primary key must not be null

ตัวอย่างปัญหา Insert ข้อมูลเข้าไปไม่ได้

ปัญหา Delete ข้อมูล

จากตัวอย่างถ้าเราลบข้อมูลการ Row ที่ผมล้อมกรอบสีแดงไว้ ข้อมูลของ User : apirat จะหายไปเลย ทั้งๆที่เราอยากลบข้อมูลการที่เขามีเงินสกุล THB ไปเท่านั้น

ตัวอย่างปัญหา Delete ข้อมูล

ซึ่งจากป้ญหานี้จึงต้องทำ Normalization ขั้นตอนต่อไป

BCNF

A relation table structure T is in the Boyce/Codd normal form (BCNF) if and only determinant in T is a candidate key of T.

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

Table จะเป็น BCNF ได้นั้น Determinant ทั้งหมดใน Table จะต้องเป็น Candidate key ของ Table

ผมว่าคุณน่าจะไม่เข้าใจเพราะติดศัพท์เทคนิค Determinant ว่าคืออะไร

Determinant

ถ้าเราบอกว่า A กับ B มีความสัมพันธ์แบบ Functional dependence โดยค่าของ A เป็นตัวกำหนดว่าค่าของ B เราจะสามารถซึ่งจะเขียนอธิบายแบบนี้ A -> B ซึ่ง A จะเป็น Determinant ดังนั้นถ้ามีความสัมพันธ์ FD ที่ USERNAME -> EMAIL ก็แปลว่า USERNAME เป็น Determinant

ตรวจสอบ Table ที่เป็น 3NF ที่มีปัญหาว่าเป็น BCNF ไหม

รายละเอียดของ Table

  1. Candidate key : { USERNAME , CURRENCY_CODE } , { EMAIL, CURRENCY_CODE }
  2. FD ใน Table
    • { USERNAME , CURRENCY_CODE } -> AMOUNT
    • { EMAIL , CURRENCY_CODE } -> AMOUNT
    • USERNAME -> EMAIL
    • EMAIL -> USERNAME

เนื่องจาก Table นี้มี Determinant ที่ไม่เป็น Candidate key คือ USERNAME และ EMAIL ดังนั้น Table นี้ไม่ได้เป็น BCNF

วิธีแก้ไข

วิธีแก้ไขให้ Table นี้ให้เป็น BCNF นั้นสามารถทำได้โดยแยกเอา COLUMN ที่เป็น Determinant ที่ไม่ได้เป็น Candidate key ออกไปโดยผมจะเลือกเอา Column EMAIL ออกไปซึ่งจะทำให้เราจะได้ตารางใหม่สองตารางเป็น

TABLE : BCNF_USER_CURRENCY

TABLE : BCNF_USER_CURRENCY

ตรวจสอบว่า Table นี้เป็น BCNF ไหม

  1. Candidate key : { USERNAME , CURRENCY_CODE }
  2. FD ใน Table
    • { USERNAME , CURRENCY_CODE } -> AMOUNT

เนื่องจาก Determinant ของทุก FD เป็น Candidate key ดังนั้น Table นี้จึงเป็น BCNF

TABLE : BCNF_USER

TABLE : BCNF_USER

ตรวจสอบว่า Table นี้เป็น BCNF ไหม

  1. Candidate key : USERNAME , EMAIL
  2. FD ใน Table
    • USERNAME -> EMAIL
    • EMAIL -> USERNAME

เนื่องจาก Determinant ของทุก FD เป็น Candidate key ดังนั้น Table นี้จึงเป็น BCNF

เกี่ยวกับ BCNF

โดยปกติแล้ว Table ที่เป็น 3NF ส่วนใหญ่นั้นจะไม่มีปัญหา INSERT , DELETE , UPDATE ที่มาจากความสัมพันธ์ FD แล้ว แต่จะมีบางกรณีแบบในตัวอย่างที่เป็น 3NF แล้วแต่ยังมีปัญหา ดังนั้นจึงมีการคิดค้น BCNF เพื่อตรวจสอบและจัดการกับกรณีนี้

สรุป

สำหรับตอนนี้เราได้เรียนรู้เกี่ยวกับ Normalization Process ว่ามีกระบวนการอย่างไร Normal Form ตั้งแต่ขึ้น 1NF ถึง BCNF ได้รู้ว่า Functional Dependency คืออะไร เราได้ลองทำ Normalization Process กับ Table : CURRENTCY_TRADING_SYSTEM ไปจนถึงขั้น BCNF แล้ว (ตัวอย่างผมทำให้ดูถึง 3NF คุณลองไปทำต่อดูว่า Table เหล่านั้นเป็น BCNF ได้ยังไง) ซึ่งเราจะได้ Table ทั้งหมดตามภาพด้านล่าง

Table ทั้งหมดที่เป็น BCNF

ซึ่งถ้าคุณอ่านถึงตอนที่แล้วคุณจะรู้ว่า Table เหล่านี้ไม่มีปัญหา INSERT , DELETE , UPDATE แล้ว ซึ่งนั่นทำให้เกิดความเข้าใจผิดๆว่า Normalization Process นั้นทำถึงแค่ BCNF ก็พอ (มีแย่กว่านั้นที่บอกว่า 3NF ก็พอ) จริงๆถ้าทำถึงขั้น BCNF แล้ว Table ทั้งหมดที่ได้จะไม่เกิดปัญหา INSERT , DELETE , UPDATE ที่มาจากความสัมพันธ์ระหว่าง Column ( Attribute ) ที่เป็น FD แต่ความสัมพันธ์นั้นไม่ได้มีแค่ FD ครับ ยังมีความสัมพันธ์อื่นๆอีก ดังนั้นตอนต่อไปเราจะมาทำ Normalization Process ในขั้น 4NF ,5NF กันต่อครับ

Ref

  • กระบวนการ Normalization Process และนิยามที่เป็นภาษาอังกฤษทั้งหมดเอามาจาก หนังสือ Relational database systems : language, conceptual modeling and design for engineers

Basic Database System Part 3 - Normalization

Basic Database System Part 3 - Normalization

สำหรับตอนนี้เราจะมาพูดถึง Normalization ว่ามันคืออะไร จากนั้นเราจะมาดูตัวอย่างของปัญหาจนทำให้เราต้องมาทำ Normalization กันครับ แล้วก็ตามที่ผมบอกไปนะครับที่ผมเขียนตรงนี้อาจไม่ถูกต้องตามทฤษฎี 100% คุณสามารถอ่านมันเป็นแนวทางเริ่มต้นครับ จากนั้นพอเข้าใจแล้วลองไปหาหนังสือเกี่ยวกับ Database จริงๆมาอ่าน หรือถ้าได้มีโอกาสเรียนกับอาจารย์ก็ขอให้ตั้งใจเรียนและถามสิ่งที่สงสัยกับอาจารย์ที่สอนนะครับ

Normalization

จริงๆผมอยากเอาปัญหาตั้งก่อนแล้วค่อยบอกว่า Normalization แต่คิดไปคิดมาอธิบายก่อนเลยดีกว่าว่ามันคืออะไรเพราะมันสั้นมากๆ โดย Normalization คือกระบวนการในการออกแบบ Database เพื่อไม่ให้เกิดปัญหาเกี่ยวกับ INSERT , UPDATE , DELETE และทำให้ข้อมูลที่เราเก็บนั้นไม่มีความซ้ำซ้อนของข้อมูล

ปัญหา INSERT , UPDATE , DELETE

ส่วนนี้เรามาดูว่า Table (Relation) ที่เราคิดออกแบบขึ้นมาแล้วยังไม่ผ่านกระบวนการ Normalization จะมีปัญหาอะไรบ้าง

Table : CURRENCY_TRADING_SYSTEM

Table :  CURRENCY_TRADING_SYSTEM

สมมุติว่าผมต้องการทำ Application แลกเปลี่ยนเงินตราระหว่างประเทศขึ้นมา เรา Design Table : CURRENCY_TRADING_SYSTEM ขึ้นมาเพื่อเก็บข้อมูลว่า User ไหนเก็บเงินสกุลไหนไว้บ้าง ซึ่งผมก็ออกแบบ Table ไว้เก็บข้อมูลดังภาพ โดยแต่ละ Column จะเก็บข้อมูล

  • USERNAME : เก็บ USERNAME ของผู้ใช้
  • EMAIL : เก็บ EMAIL ของผู้ใช้
  • USER_TYPE : เก็บว่าผู้ใช้นี้เป็น TYPE ไหน
  • PRIORITY : ค่าความสำคัญของ USER โดยความสำคัญจะขึ้นอยู่กับว่าเป็น USER TYPE ไหน โดยค่ายิ่งมากยิ่งสำคัญ
  • CURRENCY_CODE : CODE ของสกุลเงินนั้น
  • CURRENCY_NAME : ชื่อของสกุลเงินนั้น
  • COUNTRY : ชื่อประเทศเจ้าของสกุลเงินนั้น
  • AMOUNT : ผู้ใช้มีเงินสกุลนั้นเท่าไหร่

Table นี้มี Primary key คือ USERNAME , CURRENTCY_CODE ซึ่งคุณสามารถตรวจสอบได้โดยเอา USERNAME + CURRENCY_CODE ไปทำการค้นหาคุณจะเจอแค่ 1 ROW เสมอ และมันก็ตรงตาม Business Logic คือ USER หนึ่งคนก็ควรมีข้อมูลการถือเงินสกุลใดสกุลหนึ่งแค่ 1 ข้อมูลเท่านั้น มันคงจะแปลกๆถ้าบอกว่า USER : surapong มีเงิน THB 2 จำนวน

ปัญหาการ Insert

ตอนเราเห็น Table นี้เราอาจจะไม่เห็นปัญหาของมัน แต่เมื่อเราเริ่มใช้งานเนี่ยเราจะเริ่มเจอปัญหา ปัญหาแรกที่เราจะเจอมี 2 ปัญหาคือ

  1. INSERT ข้อมูลไม่ได้
  2. INSERT ข้อมูลที่ไม่ถูกต้องเข้าไปได้

เรามาดูทีละปัญหา

INSERT ข้อมูลไม่ได้

ลองนึกภาพตามนะครับว่า ถ้าคุณอยากจะ Add User ใหม่เข้ามาในระบบ แต่ User นั้นยังไม่ได้โอนเงินเข้ามาในระบบคุณเลยสักสกุลเงิน ดังนั้นข้อมูลที่คุณจะ Insert เข้าไปก็จะมีหน้าตาประมาณนี้

ข้อมูล User ที่ต้องการ Insert

ซึ่งมันเหมือนจะ Insert ได้ใช่ไหมครับ แต่จริงๆมัน Insert ไม่ได้ครับ เพราะกฎของ Relational Database Model ที่ตั้งไว้ว่า Primary key must not be null พอตัว CURRENCY_CODE มีค่าเป็น NULL ข้อมูลนี้จึงไม่สามารถ INSERT ได้

ในทางกลับกันถ้าคุณอยากเพิ่มสกุลเงินชนิดใหม่เข้าไปเก็บใน Database ซึ่งเงินพอเป็นเงินสกุลใหม่ก็แปลว่าไม่มีคนเคยซื้อเงินสกุลนี้เลย ข้อมูลที่ Insert เข้าไปก็จะเป็น

ข้อมูล Currency ที่ต้องการ Insert

ซึ่งจะเห็นว่า USERNAME = NULL ก็แปลว่าไม่สามารถ INSERT ข้อมูลได้ครับ

INSERT ข้อมูลที่ไม่ถูกต้องเข้าไปได้

ถ้าสมมุติคุณอยากจะเพิ่มข้อมูลการซื้อสกุลเงินใหม่ของ USER : surapong เข้าไป แต่ตอนบันทึกนั้น เขาใส่ข้อมูล email ผิดลงไปดังตัวอย่าง

ข้อมูล User ที่ต้องการ Insert ที่มีข้อมูล Email ไม่ถูกต้อง

ซึ่งจากตัวอย่างนั้นข้อมูลสามารถเพิ่มลงไปใน Database ได้ แต่ปัญหาที่เกิดขึ้นคือความถูกต้องของข้อมูล คุณจะเห็นได้ว่าตอนนี้ USER : surapong เนี่ยมี EMAIL ที่ 2 EMAIL คือ surapong_inwza007@hotmail.com , surapong_007inwza@hotmail.com ซึ่งคำถามคือตกลง EMAIL ไหนเป็น EMAIL ที่ถูก สมมติถ้าต้องส่ง EMAIL ไปให้ user คนนั้นเราจะต้องใช้ EMAIL ไหนล่ะ ปัญหาข้อมูลไม่ถูกต้องไม่สัมพันธ์กันนี้มีชื่อเรียกว่า Data inconsistency

ปัญหาการ Delete

ปัญหาการ Delete ที่เราจะเจอนั้นคือปัญหาการลบข้อมูลที่ไม่ได้อยากลบให้หายไปด้วย อ่านแล้วอาจจะไม่เข้าใจ เรามาลองดูตัวอย่างครับ

ข้อมูลที่ต้องการ Delete

จากภาพเราต้องการลบ Row ที่ล้อมกรอบสีแดงไว้ ซึ่งก็เป็นเรื่องปกติที่จะลบข้อมูลออกเพราะ User ทำการถอนเงินออกไปแล้ว แต่ปัญหาอยู่ที่เมื่อเราลบข้อมูล Row นี้ทิ้งไป ข้อมูลเกี่ยวกับสกุลเงิน JPY (Yen) จะหายไปจาก Database ด้วย ซึ่งนั่นเป็นสิ่งที่เราไม่ต้องการ

ปัญหาการ Update

ตัวอย่างข้อมูลที่ซ้ำซ้อน

ปัญหานี้เกิดจากที่มีข้อมูลซ้ำซ้อนอยู่ใน Table จากตัวอย่างคุณจะเห็นว่าข้อมูล email ของ USER นั้นแสดงซ้ำ คราวนี้เวลาจะต้องแก้ไขข้อมูล email เช่น ต้องการเปลี่ยน email ของ USER : sarun เป็น sarun@gmail.com ก็ต้องทำการ UPDATE ทุก Row ที่ USERNAME = sarun ซึ่งถ้าเป็นคำสั่ง SQL ก็จะเป็นแบบนี้

1
2
3
UPDATE CURRENCY_TRADING_SYSTEM
SET EMAIL = 'sarun@gmail.com'
WHERE USERNAME = 'sarun'

ถามว่ามันเป็นปัญหายังไง ปัญหาคือเมื่อมันมีหลาย ROW ถ้าระหว่างที่กำลัง UPDATE แต่ละ ROW อยู่นั้นตัว DBMS นั้นเกิดปัญหา ก็แปลว่า ข้อมูล email ของ USER : sarun นั่นเองจะมี email เก่ากับ email ที่ถูกแก้ไขไปแล้วอยู่ ซึ่งนั่นก่อให้เกิดปัญหา Data inconsistency ครับ แต่น้่นเป็นเรื่องเมื่ออดีตครับ ในปัจจุบันนั้น DBMS นั้นมี TRANSACTION PROCESS ซึ่งนั่นก็แปลว่า ถ้าเกิดปัญหาระหว่างการ UPDATE ตัวระบบก็จะ ROLLBACK ข้อมูลกลับไปเหมือนว่าไม่เคยสั่ง UPDATE ดังนั้นปัญหาเรื่อง Data inconsistency จากการ UPDATE จะไม่เกิดขึ้นครับ

แล้วเราจะแก้ปัญหาพวกนี้ยังไง

วิธีแก้ปัญหา INSERT , UPDATE , DELETE ก็คือการทำ Normalization ครับ โดยกระบวนการทำนั้นผมจะยังไม่อธิบายในตอนนี้ เพราะถ้าอธิบายในตอนนี้มันจะยาวมากเพราะต้องยกตัวอย่างให้เห็น ดังนั้นเราแสดงผลลัพธ์จากการทำ Normalization ก่อนเพื่อให้เห็นว่ามันสามารถแก้ปัญหา INSERT , UPDATE , DELETE ได้จริง

ผลลัพธ์จากการทำ Normalization

Table ที่เกิดจากการทำ Normalization

ผลลัพธ์จากการทำ Normalization นั้นจะเปลี่ยนจาก Table : CURRENCY_TRADING_SYSTEM มาเป็น 4 Table คือ

  • USER : Table นี้เก็บข้อมูลของ User
  • USER_TYPE : Table นี้เก็บข้อมูลของ USER TYPE
  • CURRENCY : Table นี้เก็บข้อมูลของ CURRENCY
  • USER_CURRENCY : Table นี้เก็บข้อมูลว่า USER นั้นมี CURRENCY ใดอยู่บ้าง

ทดสอบกับปัญหาการ Insert

ทดสอบกับปัญหา Insert ไม่ได้

หากเราต้องการ Insert User ใหม่เข้าไปในระบบเราสามารถ Insert ข้อมูลไปที่ Table : User ได้เลยเพราะไม่ติดปัญเรื่อง Primary key must not be null แล้ว

ข้อมูล User ใหม่ที่ต้องการ Insert

ทดสอบปัญหา INSERT ข้อมูลที่ไม่ถูกต้องเข้าไปได้

ปัญหานี้จะถูกแก้เพราะเมื่อเราต้องการ Insert ข้อมูว่า User : surapong มีสกุลเงินใหม่เพิ่มเข้าไปนั้นจะทำการเพิ่มที่ Table : USER_CURRENCY ซึ่ง Table นี้จะทำการกรอกแค่ USERNAME , CURRENCY , AMOUNT เท่านั้น ดังนั้นจะไม่มีทางกรอกข้อมูลที่ไม่ถูกต้องไม่สอดคล้องลงไปได้

ข้อมูล USER_CURRENCY ใหม่ที่ต้องการ Insert

ทดสอบกับปัญหาการ Delete

ปัญหาการ Delete แล้วลบข้อมูลที่ไม่อยากลบจะไม่เกิดขึ้นเพราะเวลาลบข้อมูลเราลบที่ Table : USER_CURRENCY ซึ่งเมื่อลบไปแล้วข้อมูลของ CURRENCY ก็ยังคงอยู่ไม่ได้หายไป

ข้อมูลที่ต้องการลบ

อยากแสดงข้อมูลเหมือน Table : CURRENCY_TRADING_SYSTEM จะทำยังไง

ถ้าเราอยากแสดงข้อมูลเหมือนกับ Table : CURRENCY_TRADING_SYSTEM นั้นสามารถทำได้โดยการ SELECT แล้วในคำสั่ง SELECT เราสั่งให้ทำการ JOIN TABLE ที่เกี่ยวข้องเข้าด้วยกัน โดยจากตัวอย่างเราจะได้คำสั่ง SQL แบบนี้

1
2
3
4
5
SELECT *
FROM USER_CURRENCY t1
INNER JOIN USER t2 ON t1.USERNAME = t2.USERNAME
INNER JOIN CURRENCY t3. ON t1.CURRENCY_CODE = t3.CURRENCY_CODE
INNER JOIN USER_TYPE t4 ON t2.USER_TYPE = t4.USER_TYPE

ปัญหาใหม่

เมื่อทำ Normalization เพื่อออกแบบ Database ให้ไม่ให้เกิดปัญหาการ INSERT , DELETE , UPDATE ซึ่งผลลัพธ์ที่ได้คือเปลี่ยนจาก 1 Table กลายเป็น 4 Table ปัญหาที่ตามมาก็คือเมื่อต้องการแสดงข้อมูลให้เหมือนกับ Table : CURRENCY_TRADING_SYSTEM นั้นจะต้องทำการ JOIN ซึ่งเมื่อทำการ JOIN ก็จะเกิดปัญหาเรื่องต้องใช้ Resource มาทำการ JOIN ซึ่งเป็นปัญหาด้าน Performance

พอเป็นแบบนี้คุณก็จะบอกว่า อ้าวแล้วทำไปทำไมวะ คำตอบก็คือ ปัญหาเรื่อง Resource ที่ใช้ในการ JOIN ซึ่งเป็นปัญหาเรื่อง Performance นั้นสามารถแก้ไขได้ด้วยการเพิ่ม Hardware หรือใช้วิธีการ Tuning DBMS ให้สามารถ JOIN ได้เร็วขึ้นอาจจะทำ INDEX แบ่ง Partition data ต่างๆนาๆ แต่ปัญหา INSERT , UPDATE , DELETE และปัญหา Data inconsistency นั้นไม่สามารถแก้ได้ด้วย Hardware หรือการ Tuning DBMS แก้ได้ วิธีแก้คือต้องไปไล่แก้ข้อมูลให้ถูกต้องหรือไปเพิ่มเงื่อนไขในการทำงานเช่น ถ้าสมัคร User ใหม่ต้องทำการซื้อสกุลเงินหนึ่งที่มีในระบบเลย หรือถ้าต้องการเพิ่มสกุลเงินใหม่ก็ต้องมีคนซื้อสกุลเงินนั้นอย่างน้อยหนึ่งคน ซึ่งอันนี้ยังพอเป็นไปได้ แต่ถ้าเรื่อง Data inconsistency คุณจะทำยังไงในเมื่อข้อมูลมันไม่ถูกต้องสอดคล้องกัน คุณจะโทรไปหา User แต่ละคนเพื่อถามว่า EMAIL ของคุณคืออันไหนกันแน่เหรอ หรือถ้าไม่โทรคุณจะเอาอะไรตัดสินใจว่าอันไหนถูก หากลองคิดแบบนี้แล้วคุณคิดว่าเราควรทำหรือไม่ควรทำ Normalization ล่ะ เพราะปัญหาหนึ่งแก้ได้ด้วยเงินกับความรู้เทคนิค อีกปัญหาหนึ่งต้องสร้างเงื่อนไขที่ทำให้การดำเนินธุรกิจมีความยากขึ้น

สรุป

สำหรับตอนนี้เราได้รู้แล้วว่า Normalization คืออะไร มันมีไว้เพื่อแก้ปัญหาอะไร แล้วมันจำเป็นต้องทำไหม เมื่อเราเข้าใจที่มาที่ไปและเหตุผลแล้ว ในตอนต่อไปเราจะไปดูว่าการทำ Normalization นั้นทำอย่างไรกันครับ

Basic Database System Part 2 - Relational Database Model

Basic Database System Part 2 - Relational Database Model

ในตอนที่แล้วเราได้รู้ว่า Database , Database Model , DBMS คืออะไรกันไปแล้ว ในตอนนี้เราจะมาลงรายละเอียดเกี่ยวกับ Relational Database Model กัน

Relation คืออะไร

ถ้าคุณไปถามคนในวงการคอมพิวเตอร์ว่า Relation คืออะไรในเนื้อหาเกี่ยวกับ Database บางคนอาจจะบอกว่ามันคือความสัมพันธ์ ซึ่งอยากจะบอกว่ามันไม่ใช่นะครับ ตามนิยามแล้ว Relation คือ Subset ของ Cartesian product ของ Domain ( Subset of the Cartesian product of domains ) ซึ่งผมเชื่อว่าหลายคนอ่านแล้วอาจจะไม่เข้าใจ ตอนผมเรียนตอนปี 3 ผมฟังครั้งแรกก็ไม่เข้าใจเหมือนกันครับ ดังนั้นเรามาดูตัวอย่างเพื่อทำความเข้าใจกันดีกว่า

กำหนดให้มี Domain ดังต่อไปนี้ (จะมองว่า Domain คือ Column ก็ได้นะครับ)

Domain : Name มีค่าที่เป็นไปได้คือ : { Wasinee , Sunisa , Thanaporn }

Domain : Subject มีค่าที่เป็นไปได้คือ : { Math , Physic }

Domain : Status มีค่าที่เป็นไปได้คือ : { PASS , FAIL }

Cartesian product ของ Domain ก็คือเอาทุกค่าของแต่ละ Domain มารวมกันในทุกความเป็นไปได้ ซึ่งจะเห็นว่ามีทั้งหมด 12 แบบซึ่งเกิดจาก 3 x 2 x 2 ดังภาพ

Cartesian product ของ Domain

ถ้าสังเกตจะเห็นว่าค่ามันอาจจะดูแปลกๆในความเป็นจริงที่ Wasinee จะสอบผ่านและสอบตกวิชา Physic พร้อมกัน อีกทั้งคนหนึ่งคนไม่จำเป็นต้องสอบทุกวิชาหรือบางคนอาจไม่สอบสักวิชาเลยก็ได้ ซึ่งข้อมูลที่เกิดขึ้นจริงที่จะคือ

ข้อมูลที่เกิดขึ้นจริง

ซึ่งจะเห็นว่าข้อมูลที่เกิดขึ้นจริงนั้นเป็น Subset ของ Cartesian product ของ Domain (คุ้นๆไหม) ดังนั้นข้อมูลที่เกิดขึ้นจริงก็คือ Relation นั่นเอง

คราวนี้คุณก็เข้าใจแล้วนะครับว่า Relation คืออะไร ทำไมต้องเป็น Subset ของ Cartesian product ของ Domain

( อันนี้เรื่องตลกนะครับรู้ไว้เอาฮาไม่รู้ก็ไม่เป็นไร เวลารุ่นพี่รุ่นน้องเด็กวิศวคอมลาดกระบังเจอกันแล้วไม่รู้จักกัน คำถามหนึ่งที่สามารถถามกันเพื่อเช็คว่าเฮ้ยเอ็งใช่รุ่นพี่หรือรุ่นน้องจริงรึเปล่าวะคำถามหนึ่งคือ Relation คืออะไร ซึ่งเด็กวิศวคอมลาดกระบังที่เรียน Database แล้วจะตอบแนวๆนี้หมดครับ )

Relational Database Table

เรารู้แล้วว่า Relation คืออะไร แต่เราจะแสดง Relation ให้คนดูยังไงให้เข้าใจ วิธีที่จะแสดง Relation ให้คนเข้าใจก็คือเอาไปแสดงเป็น Table นั่นเอง จริงๆ Relation มันจะไม่มีการบอกว่าค่าแต่ล่ะค่ามาจาก Domain (Column) อะไร ตัวอย่าง Relation แบบไม่แสดงในตารางจะเป็นแบบด้านล่าง (จริงๆ Relation แสดงได้ด้วยสมการทางคณิตศาสตร์เลยนะครับ)

1
2
3
4
5
6
Relation = { 
(Wasinee, Math, PASS) ,
(Thanaporn, Math, PASS) ,
(Thanaporn, Physic, FAIL)
}

ดังนั้นเพื่อให้คนทั่วไปเข้าใจ Relation ได้ง่ายขึ้นเขาจึงทำให้มันอยู่ในรูปแบบของ Table ดังภาพที่คุณเห็นตัวอย่างที่ผมอธิบายที่มีหัวตารางเป็นการบอกว่าค่านี้มาจาก Domain (Column) ไหนเพื่อให้เข้าใจง่ายขึ้น คราวนี้ Table ที่แสดง Relation ได้นั้นจะต้องมีคุณสมบัติดังต่อไปนี้

  1. Row ต้องไม่ซ้ำ

  2. ลำดับของ Row ไม่มีความสำคัญ ว่าง่ายๆคือ Row 1 กับ Row 2 สลับที่กันก็ไม่มีความสำคัญ

  3. ลำดับของ Column ไม่มีความสำคัญ ว่าง่ายๆคือ จะสลับ Column 1 กับ Column 2 กันก็ไม่มีผลอะไร

  4. ค่าใน Column นั้นต้องเป็นค่าที่อยู่ใน Domain เดียวกัน ไม่ได้มาจากหลากหลาย Domain

  5. ค่าใน Column ต้องเป็นค่าค่าเดียวไม่ใช่ Set หรือ List

กฎของ Relational Database

ในส่วนที่แล้วเรารู้แล้วว่า Relation คืออะไร หน้าตาเป็นอย่างไร ซึ่งเป็นหนึ่งในส่วนประกอบของ Database model ส่วนต่อมาที่เราต้องรู้คือกฎของ Database นั่นเอง

The Entity Integrity : Primary key must not be NULL

ก็ตามนั้นเลยครับ Primary key ต้องไม่มีค่าเป็น NULL คราวนี้คุณอาจจะสงสัยว่า Primary key คืออะไร NULL คืออะไร เรามาทำความเข้าใจกันทีละตัวครับ เริ่มจาก NULL ก่อน

NULL

สำหรับ NULL นั้นแปลว่าไม่มีค่าหรืออาจจะแปลว่าไม่รู้ก็ได้ครับ ตัวอย่างเช่น Relation ดังภาพด้านล่าง

ตัวอย่างการเก็บค่า NULL

จะเห็นว่า Row : Wasinee นั้นมีค่า Salary เป็น NULL ซึ่งแปลว่าเราไม่รู้ว่าค่า Salary ของ Wasinee เป็นเท่าไหร่

Primary key

ก่อนรู้จัก Primary key เรามารู้จัก Super key ก่อน Super key คือกลุ่มของ Column ที่ใช้เป็นใช้อ้างถึง Row นั้นได้เพียง Row เดียว (ภาษาไทยอธิบายยาก ภาษาอังกฤษเขานิยามไว้แบบนี้น่าจะเข้าใจมากกว่า : A super key is a set of attribute whose value uniquely identifies a tuple ) ผมว่าอ่านที่ผมอธิบายแล้วน่าจะไม่เข้าใจ ดังนั้นดูตัวอย่างน่าจะเข้าใจง่ายกว่า

ตัวอย่าง Table : USER

จากภาพด้านบนเราจะเห็นว่า Table (ขอเรียก Relation เป็น Table เลยนะครับ) นี้มี 3 Column คือ USERNAME , EMAIL , DATE OF BIRTH ถ้าดูตอนนี้เราจะพบว่ามี Super key ทั้งหมดคือ

  1. USERNAME , EMAIL , DATE OF BIRTH

  2. USERNAME , EMAIL

  3. USERNAME , DATE OF BIRTH

  4. EMAIL , DATE OF BIRTH

  5. USERNAME

  6. EMAIL

วิธีพิสูจน์ว่าจริงไหมให้คุณเอาค่าจากกลุ่ม Column เหล่านี้ไปหาใน Table ครับ เวลาเอาค่าไปหามันจะอ้างถึงแค่ Row เดียวเสมอ ซึ่งจะเห็นว่า DATE OF BIRTH Column เดียวไม่สามารถอ้างถึง Row ได้แค่ Row เดียวดูได้จากถ้าอ้างด้วย 1990-05-16 จะอ้างถึงได้ 2 Row

ต่อไปเรามารู้จัก Candidate Key ซึ่งก็คือ Super key ที่ไม่มี Super key อื่นเป็น Subset (นิยามภาษาอังกฤษน่าจะเข้าใจง่ายกว่า : A candidate key is defined as a superkey which has no other superkeys as a subset (or has no superkeys as its proper subset) ) ก็ตามเดิมครับอ่านแล้วไม่เข้าใจมาดูตัวอย่างกันดีกว่า เรามาไล่พิจารณาจาก Super key ที่เราหาได้จากตัวอย่างที่แล้วว่ามีอันไหนเป็น Candidate Key ได้บ้าง

  1. USERNAME , EMAIL , DATE OF BIRTH

    ไม่เป็น Candidate key เพราะมี Super key อื่นเป็น Subset ดูได้จาก Super key { USERNAME , EMAIL } ที่เป็น Subset

  2. USERNAME , EMAIL

    ไม่เป็น Candidate key เพราะมี Super key อื่นเป็น Subset ดูได้จาก Super key { USERNAME } ที่เป็น Subset

  3. USERNAME , DATE OF BIRTH

    ไม่เป็น Candidate key เพราะมี Super key อื่นเป็น Subset ดูได้จาก Super key { USERNAME } ที่เป็น Subset

  4. EMAIL , DATE OF BIRTH

    ไม่เป็น Candidate key เพราะมี Super key อื่นเป็น Subset ดูได้จาก Super key { EMAIL } ที่เป็น Subset

  5. USERNAME

    เป็น Candidate key เพราะไม่มี Super key อื่นเป็น Subset

  6. EMAIL

    เป็น Candidate key เพราะไม่มี Super key อื่นเป็น Subset

ทั้งหมดที่อธิบายนั้นปูทางมาเพื่ออธิบายว่า Primary key คือตัวหลักที่ใช้อ้างอิงถึง Row นั้นโดยเราจะเลือกตัวอ้างอิงหลักนี้มาจาก Candidate key ( การแปลเป็นไทยมันยากมาก ผมว่าอ่านนิยามภาษาอังกฤษเข้าใจง่ายกว่า The primary key is the principle identifier of the relation; and is simply defined as a selected by candidate key ) ถ้าจากตัวอย่างด้านบนเราสามารถกำหนด Primary key ได้จาก USERNAME หรือ EMAIL ซึ่งเป็น Candidate key ครับ

สมมติถ้าเราเลือก USENAME เป็น Primary key Column USERNAME จะต้องไม่มีค่าเป็น NULL ครับ นี่คือ กฎที่ว่า Primary key must not be NULL

ตรงนี้อาจจะมีคนสงสัยว่าทำไมต้องห้าม Primary key มีค่าเป็น NULL ลองอ่านกฎข้อต่อไปครับจะเข้าใจ

The Referential Integrity : Foreign key values must match primary key values or be null

Foreign key is defined as nonkey attribute(s) in a relation which is the primary key of other relation or the same relation. A non key attribute is define as an attribute which is not a candidate key.

ผมว่าเริ่มด้วยนิยาม Foreign key แบบภาษาอังกฤษเลยดีกว่าถ้าเข้าใจแล้วจะได้ไม่ต้องเสียเวลามาอ่านภาษาไทยที่ผมพยายามจะอธิบาย หากใครอ่านไม่เข้าใจลองมาอ่านที่ผมอธิบายดูครับ

เริ่มจาก non key attribute คือ Column หรือกลุ่มของ Column ที่ไม่ได้เป็น Candidate key (จริงๆ Column มันคือ Attribute นั่นแหละ ถ้าตามทฤษฎีเราต้องเรียก Column ว่า Attribute เรียก Row ว่า Tuple แต่ขอละเป็น Column ละกันนะ)

Foreign key คือ Column หรือ กลุ่มของ Column ที่ไม่ได้เป็น Candidate key ของ Relation ปัจจุบัน (ที่ Foreign key นี้อยู่ ) ซึ่ง Column หรือกลุ่มของ Column นั้นต้องอ้างอิงถึง Primary key ของ Relation อื่นหรือ Relation เดียวกัน อ่านแล้วอาจจะงง (แน่นอนผมก็งง) ลองไปดูตัวอย่างดีกว่าครับ ของแบบนี้ดูตัวอย่างน่าจะเข้าใจง่ายกว่า

ตัวอย่าง Foreign key

จากภาพด้านบนเราจะเห็นว่ามี Table : Book ที่เก็บข้อมูลเกี่ยวกับการยืมหนังสือ คราวนี้สังเกตที่ Column : Borrow By ที่เป็น Column ที่บอกว่าใครเป็นคนยืม โดยตัว Column นี้ต้องการอ้างอิงถึง Primary key ของ Table : User ที่เก็บข้อมูลผู้ใช้ ดังนั้นเมื่อต้องการอ้างอิงแบบนี้ตัว Column : Borrow By จะกลายเป็น Foreign key

คราวนี้เรามาลองตรวจดูว่า Column : Borrow By เป็น Foreign key ได้หรือไม่

  1. Column หรือ กลุ่มของ Column ไม่ได้เป็น Candidate key

    จากตัวอย่างจะเห็นว่า Column : Borrow By นี้ไม่ได้เป็น Candidate key (ดูได้จากถ้าหา Row ด้วยค่าของ Column นี้จะผลลัพธ์ออกมามากกว่า 1 Row )

  2. อ้างอิงถึง Primary key ของ Relation อื่นหรือ Relation ตัวเอง

    จากตัวอย่าง Column : BORROW BY อ้างอิงถึง Column : USERNAME ซึ่งเป็น Primary key ของ Table : USER

ถ้าเราเลือก Column : Borrow By จะเป็น Foreign key ดังนั้นค่าที่อยู่ใน Column : Borrow By จะต้องมีค่าจริงๆอยู่ใน Table : USER Columnn : USERNAME หรือไม่ก็มีค่าเป็น NULL ซึ่งจากในตัวอย่างจะเห็นว่าค่าใน Column : Borrow By มีค่าตรงกับ Column : USERNAME หรือไม่ก็มีค่า NULL นี่แหละครับ นี่คือตัวอย่างของกฎที่ว่า Foreign key values must match primary key values or be null

สำหรับใครที่สงสัยว่าทำไมกฎข้อแรกบอกว่า Primary key ห้ามมีค่าเป็น NULL ตรงนี้น่าจะเข้าใจแล้วนะครับ เพราะถ้าเรายอมให้ค่า Primary key มีค่าเป็น NULL ได้ เวลามี Row นั้นไม่มีได้ต้องการอ้างอิงถึงอีก Table เช่นถ้าจากตัวอย่างคือหนังสือเล่มนี้ไม่ได้ถูกยืม พอไม่ถูกยืมเลยจะกำหนดค่า BORROW BY เป็น NULL คราวนี้ถ้าเรายอมให้ Primary key มีค่าเป็น NULL ได้มันก็กลายเป็นว่าจะอ้างอิงถึง Row ที่ Primary key มีค่าเป็นค่า NULL ซึ่งนั่นไม่ใช่สิ่งที่เราต้องการ เราต้องการบอกว่าหนังสือไม่มีคนยืมไม่ใช่อ้างอิงถึงหนังสือที่ Primary key มีค่าเป็น NULL ดังนั้นจึงต้องมีการกำหนดให้ Primary key ต้องไม่มีค่าเป็น NULL

ภาษาที่ใช้กับ Relational Database Model

สำหรับภาษาที่นิยมนำมาใช้กับ Relational Database Model นั้นคือภาษา Structured Query Language (SQL) โดยเราสามารถแบ่งภาษา SQL ออกเป็นประเภทตามการใช้งานได้ดังต่อไปนี้

  1. Data Definition Language (DDL)

    คำสั่งประเภทนี้จะเกี่ยวข้องกับการกำหนดโครงสร้างต่างๆของ Database เช่น ทำการสร้าง Database , สร้าง Table แก้ไข Table ลบ Table เป็นต้น โดยคำสั่งพวกนี้จะมี Key word ประมาณ CREATE , ALTER , DROP เรามาดูตัวอย่างคำสั่งเหล่านี้กันครับ

    ตัวอย่างการสร้าง Database

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    CREATE DATABASE PRESIDENT;
    ```

    ตัวอย่างการสร้าง Table

    ```sql
    CREATE TABLE IF NOT EXISTS PRESIDENT (
    `PRES_NAME` VARCHAR(128) ,
    `BIRTH_YR` INT,
    `YRS_SERV` INT,
    `DEATH_AGE` INT ,
    `PARTY` VARCHAR(128) ,
    `STATE_BORN` VARCHAR(128)
    );
  2. Data Manipulation Language (DML)

    คำสั่งเหล่านี้จะเป็นคำสั่งเกี่ยวกับการ ค้นหา เพิ่ม ลบ แก้ไข ข้อมูล เช่น ต้องการค้นหาข้อมูลใน Table ภายใต้เงื่อนไขต่างๆ หรือ ต้องการเพิ่มข้อมูลเข้าไปใน Table โดยคำสั่งประเภทนี้จะมี Key word ประมาณ SELECT , UPDATE , DELETE , INSERT

    ตัวอย่าง คำสั่ง INSERT ที่ต้องการเพิ่มข้อมูล Table PRESIDENT

    1
    2
    3
    4
    INSERT INTO PRESIDENT VALUES
    ('Washington G',1732,7,'67','Federalist','Virginia'),
    ('Adams J',1735,4,'90','Federalist','Massachusetts'),
    ('Jefferson T',1743,8,'83','Demo-Rep','Virginia');

    ตัวอย่าง คำสั่ง SELECT ที่ทำการค้นหาข้อมูลที่ใน Table : PRESIDENT ภายใต้เงื่อนไขที่ว่า PARTY = ‘Federalist’

    1
    2
    3
    SELECT *
    FROM PRESIDENT
    WHERE PARTY = 'Federalist';

    ตัวอย่าง คำสั่ง UPDATE ทำการแก้ไขข้อมูล YRS_SERV ที่ ROW ที่มีค่า PRES_NAME = ‘Washington G’

    1
    2
    3
    UPDATE PRESIDENT 
    SET YRS_SERV = 5
    WHERE PRES_NAME = 'Washington G';

    ตัวอย่าง คำสั่ง DELETE ลบข้อมูล ROW ที่มีค่า PRES_NAME = ‘Washington G’

    1
    2
    DELETE FROM PRESIDENT
    WHERE PRES_NAME = 'Washington G';

    สำหรับใครอยากเรียนเกี่ยวกับคำสั่ง SQL ประเภท Data Manipulation Language ในส่วนของการ SELECT ผมมีทำคลิปสอนไว้ คุณสามารถไปดูได้ตาม Link นี้เลยครับ

  3. Data Control Language (DCL)

    คำสั่งจำพวกนี้จะเป็นคำสั่งเกี่ยวกับการกำหนดสิทธิ์ เช่น กำหนดสิทธิ์ให้สามารถทำอะไรกับ Database ได้บ้าง เช่น SELECT ข้อมูลใน Table ได้ไหม DELETE ข้อมูลใน Table ได้ โดย Key word ของคำสั่งประเภทนี้จะเป็น GRANT , REVOKE

  4. Transaction Control Language (TCL)

    คำสั่งประเภทนี้เป็นคำสั่งที่เกี่ยวกับ Transaction ซึ่งจะเกี่ยวกับการสั่งให้ตัว Database บันทึกข้อมูลทั้งหมดที่ได้ทำไป หรือ ทำการคืนค่าทั้งหมดที่เคยกลับไปเหมือนไม่มีอะไรเกิดขึ้น (นิยามของ Transaction ไม่ใช่ที่ผมพูด ผมแค่ยกตัวอย่างว่ามันเอาไปใช้งานยังไง นิยาม Transaction จริงๆเดี๋ยวจะอธิบายในตอนอื่นครับ) โดยคำสั่งประเภทนี้จะมี Keyword ประมาณ COMMIT , ROLLBACK , SET AUTO COMMIT OFF เรามาดูตัวอย่างกันดีกว่า

    ถ้าสมมติเราต้องการแก้ไขค่าข้อมูลของ Table : PRESIDENT โดยทำการแก้ไขข้อมูลโดยต้องการทำการเปลี่ยน YRS_SERV ของ Washington G เป็น 4 และ Jefferson T เป็น 5 เราจะได้คำสั่ง SQL ประมาณนี้

    1
    2
    3
    4
    5
    6
    7
    8

    UPDATE PRESIDENT
    SET YRS_SERV = 4
    WHERE PRES_NAME = 'Washington G';

    UPDATE PRESIDENT
    SET YRS_SERV = 5
    WHERE PRES_NAME = 'Jefferson T';

    ถ้าเราสั่งคำสั่ง SQL แบบนี้ทุกครั้งจะทำการบันทึกทันทีหลังทำการสั่ง คราวนี้คนที่สั่งเราพึ่งค้นพบว่าเขาบอกข้อมูลผิดแล้วอยากให้เราแก้กลับเป็นค่าเดิมก่อนแก้ไข ซึ่งถ้าเราต้องการอะไรแบบนี้เราสามารถทำได้โดยสั่ง SET AUTO COMMIT OFF ตรงนี้จะเป็นการสั่งว่า คำสั่งที่สั่งไปจะยังไม่ถูกบันทึกลง Database แบบสมบูรณ์ (จริงๆมันบันทึกครับแต่บันทึกให้สามารถย้อนกลับได้) จนกว่าจะสั่ง COMMIT ดังนั้นถ้าได้รับคำสั่งให้แก้ไขข้อมูลแล้วเผื่อให้ย้อนกลับได้ถ้ายังไม่ COMMIT เราสามารถสั่งแบบนี้

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SET AUTO COMMIT OFF;

    UPDATE PRESIDENT
    SET YRS_SERV = 4
    WHERE PRES_NAME = 'Washington G';

    UPDATE PRESIDENT
    SET YRS_SERV = 5
    WHERE PRES_NAME = 'Jefferson T';

    คราวนี้เมื่อสั่งเสร็จคุณอาจจะรอ Confirm กับคนสั่งอีกรอบ ถ้าคนสั่งตรวจทานแล้วข้อมูลถูกต้องแน่นอนคุณก็แค่สั่ง COMMIT ข้อมูลจะถูกบันทึกลง Database แบบสมบูรณ์

    1
    COMMIT;

    แต่ถ้าคนสั่งเขาตรวจอีกรอบแล้วข้อมูลไม่ถูกต้องเขาบอกให้เราแก้ไขข้อมูลกลับไปก่อนมีการแก้ไขเราก็แค่สั่ง ROLLBACK ข้อมูลจะถูกย้อนกลับไปตอนหลังสั่ง SET AUTO COMMIT OFF;

    1
    ROLLBACK;

สรุป

สำหรับตอนนี้เราได้ลงรายละเอียดของ Relational Database Model ตั้งแต่ Relation คืออะไร เราจะเห็นข้อมูลของ Relation ในรูปแบบไหน กฎของ Relational Database model ว่ามีอะไรบ้าง Primary key คืออะไร Foreign key และสุดท้ายมาจบด้วยภาษา SQL ที่ใช้ทำงานกับ Relational Database Model ในตอนถัดไปเรามาพูดถึงเรื่อง Normalization ว่ามันคืออะไร สำคัญอย่างไรกันครับ

Ref

  • นิยามที่เป็นภาษาอังกฤษทั้งหมดเอามาจาก หนังสือ Relational database systems : language, conceptual modeling and design for engineers ผมแนะนำเป็นอย่างยิ่งให้ไปหาเล่มเต็มมาอ่าน อธิบายดีกว่าที่ผมเขียนเยอะมากครับ

Basic Database System Part 1 - Database , Database model , DBMS คืออะไร

Basic Database System Part 1 - Database , Database model , DBMS คืออะไร

เกริ่นก่อนเข้าเรื่อง

ก่อนจะเริ่มอ่านสิ่งที่ผมเขียนในตอนนี้และตอนต่อๆไป ผมอยากออกตัวก่อนเลยว่าสิ่งที่ผมเขียนอาจจะไม่ถูกต้อง 100% ตามทฤษฎี เพราะผมก็ไม่ใช่อาจารย์ที่สอนวิชานี้โดยตรง (ผมเป็นแค่โปรแกรมเมอร์ธรรมดา) แต่สิ่งที่ผมสอนผมอ้างอิงจากหนังสือกับความรู้ที่เคยเรียนกับอาจารย์ตั้งแต่อยู่สมัยปี 3 แต่ความรู้ที่อาจารย์สอนตอนนั้นเป็นความรู้ที่สามารถเอามาใช้ได้ถึงปัจจุบัน

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

Database คืออะไร

ถ้าพูดถึงนิยาม Database นี่ยากเลยครับ แต่ถ้าตามหนังสือที่ผมอ่าน Database นั้นคือที่ที่เก็บข้อมูล โดยข้อมูลที่เก็บนั้นจะเป็นความจริง ความจริงในที่นี้หมายถึงสิ่งที่เราคิดว่ามันจริง เช่น เราเก็บข้อมูลวันเกิดของ User ที่กรอก เราไม่รู้ว่ามันจริงแบบจริงไหม แต่ถ้าเก็บลง Database เราเชื่อว่าจริง ( กรอกข้อมูลผิดมาเราก็เชื่อว่าจริง App แปลงค่าผิดเราก็คิดว่ามันจริง ) ถามว่าทำไมต้องเก็บแต่ความจริง คือถ้าเราเก็บเรื่องไม่จริงเนี่ยมันจะเก็บได้ไม่มีวันหมดครับ เช่น ถ้าเราเก็บเรื่องไม่จริงของวันเกิดผมเนี่ย คุณจะสามารถใส่ตั้งแต่ 1 January 1970 ไปจนจักรวาลแตกดับได้เลยซึ่งแน่นอนว่ามันไม่มีทางเก็บได้และที่สำคัญจะเก็บไปทำไม

เมื่อเราเก็บข้อมูลแล้ว คำถามคือเราจะให้ข้อมูลนั้นแสดงให้เราเห็นแบบไหนและเราจะใช้งานมันและปรับเปลี่ยนแก้ไขมันยังไง ลองนึกภาพตามนะครับ ถ้าคุณบอกคุณเก็บข้อมูลวันเกิดผมไว้คำถามคือ ตอนที่คุณเรียกมันมาดูมันจะแสดงเป็นแบบไหน เป็นกราฟ เป็น text ยาวๆ เป็นไฟล์ คราวนี้ถ้าอยากแก้ไขข้อมูลวันเกิดผมคุณจะแก้มันยังไง แก้ตรงไหน หากคุณคิดตามคุณจะเห็นสิ่งที่เกี่ยวข้องกับ Database อย่างแรกคือ 1. เราเห็นข้อมูลยังไง หน้าตาแบบไหน ภาษาทางเทคนิคจะเรียกมันว่า Database model 2. เวลาเราจะจัดการกับ Database เราจะสั่งมันยังไงหรือเรียกง่ายๆว่าภาษาในการคุยกับ Database 3. กฏของ Database model นั้น แต่อธิบายจะเข้าใจยากดูเอาในตัวอย่างดีกว่าครับ

เรามายกตัวอย่าง Database ที่มี Database model ชนิดต่างๆกัน

  1. Hierarchy database

    ภาพจาก theintactone.com

    Database ประเภทนี้จะแสดงข้อมูลเป็นแบบลำดับชั้น อย่างตัวอย่างในภาพเนี่ยจะมีชั้น College ซึ่ง College ก็จะเก็บข้อมูลของ Department ตัว Department ก็จะเก็บข้อมูล Course , Teacher , Students ซ้อนไปเป็นลำดับชั้น เวลาจะหาข้อมูลก็ต้องเข้าไปหาผ่านแต่ละชั้นลงไปเช่น ถ้าอยากจะหาข้อมูล Students ก็ต้องไล่หาจาก College -> Department -> Students ส่วนภาษาที่ใช้กับ Hierarchy database จะเป็นการสั่ง traverse ไปหาข้อมูลตามชั้น ส่วนกฏของ Database model นี้คือ ตัวชั้นลูกจะไม่สามารถมาเป็นชั้นพ่อของชั้นที่อยู่เหนือกว่าได้ตัวอย่างเช่น ชั้น Students จะไม่สามารถมาเป็นพ่อของชั้น Department ได้

  1. Graph Database

    ภาพจาก neo4j.com

    Database ประเภทนี้จะแสดงข้อมูลให้เราเห็นเป็น Graph ซึ่งก็คือจะมี Node ซึ่งจะมี Attribute ต่างๆ แล้วแต่ละ Node สัมพันธ์กันยังไงดูได้จากภาพที่มี Node ที่เป็น Person : Dan , Person : Ann , Car : Volvo มีความสัมพันธ์ Loves กัน คราวนี้ถ้าต้องการหาข้อมูลเช่น อยากหาว่า Person : Dan เนี่ยมีความสัมพันธ์แบบ Loves กับ Node ไหน เราก็มีภาษาในการเข้าถึงข้อมูลประมาณภาพด้านล่างซึ่งเป็นภาษาชื่อ Cypher (โดยหากท่านใดสนใจลงรายละเอียดเกี่ยวกับภาษานี้สามารถกด Link นี้เพื่อไปดูรายละเอียดเพิ่มเติม )

    ภาพจาก neo4j.com

    กฎของ Database model นี้คือทุกความสัมพันธ์จะต้องมีความสัมพันธ์กับ Node ที่มีอยู่จริงจะไม่สามารถให้ Node มีความสัมพันธ์กับ Node ที่ไม่มีจริงได้ เช่น Node Person : DAN เวลาบอกว่ามีความสัมพันธ์แบบ Loves กับ Node ที่มีอยู่จริงเช่น Node Person : Ann จะไม่สามารถบอกว่ามีความสัมพันธ์แบบ Loves กับ Node Person : Wasinee ที่ไม่มีอยู่ใน Database ได้

  2. Relational Database

    ภาพจาก codecademy.com

    Database model นี้จะแสดงข้อมูลให้เราเห็นเป็น Relation ต่างๆ หรือจะให้เข้าใจง่ายๆคือเห็นเป็น “ตาราง” “Table” ( เดี๋ยวเราค่อยอธิบายว่า Relation ในตอนต่อๆไปว่ามันคืออะไร ตอนนี้เข้าใจว่าเป็นตารางไปก่อน ) ดังภาพ จะเห็นว่ามี Table : Product ซึ่งเราดูก็เข้าใจได้เลยว่า Table นี้เก็บข้อมูลของ Product โดยข้อมูลของ Product ประกอบไปด้วย ModelNumber , ProductName , ProductPrice , UnitCost ซึ่งก็คือ Column ส่วน Row ก็คือข้อมูลจริงๆที่ถูกเก็บไว้

    ส่วนภาษาที่เราใช้กับ Database ชนิดนี้คือภาษา SQL ( Structured Query Language ) โดยถ้าเราอยากหาข้อมูล Product ที่มี ProductPrice น้อยกว่า 200.00 ก็จะสามารถเขียนได้เป็นแบบนี้

    1
    2
    3
    SELECT *
    FROM Product
    WHERE ProductPrice < 200.00

    กฏของ Database model นี้คือ Primary key จะต้องไม่มีค่าเป็น NULL ( NULL ในที่นี้คือค่าชนิดหนึ่งที่บอกว่าไม่มีค่า ) หรือว่าง่ายๆคือ Primary key จะต้องมีค่า และ Primary key ต้องไม่มีค่าซ้ำ ส่วน Primary key คืออะไร Primary key คือค่าที่บอกว่า Row นี้แตกต่างจาก Row อื่นและเป็นคนละ Row กัน ถ้าเทียบกับในชีวิตจริงที่เห็นได้ง่ายๆคือ เลขประจำตัวประชาชน 13 หลักที่เราใช้กันนั่นแหละ กฏอีกข้อคือ Foreign Key ที่เชื่อมกันระหว่างตารางนั้นถ้ามีค่าจะต้องเป็นค่าที่มีอยู่จริง ถ้าไม่มีค่าจะต้องเป็นค่า NULL ตัวอย่างในภาพ Table : Sales มี Foreign Key ที่ Column : ModelNumber กับ Column : ModelNumber ที่ Table : Product ดังนั้น ค่า Column : ModelNumber ของ Table : Sales จะต้องมีค่าตรงกับ Column : ModelNumber ที่ Table : Product หรือไม่ก็มีค่า NULL ซึ่งจากภาพตัวอย่างจะเห็นว่าค่าตรงกันหมด

Database model ยังมีอีกมายมาย ไม่ว่าจะเป็นแบบ Object , Key-Value , Document ซึ่งแต่ละชนิดก็มีกฎและภาษาที่ใช้ต่างกัน อีกทั้ง Database model เดียวกันนั้นสามารถเข้ามีภาษาเข้าถึงได้หลายภาษา เช่น Relational Database สามารถใช้ภาษา SQL ในการเข้าถึงข้อมูล แต่จริงๆแล้วเราสามารถใช้ Relational Algebra ในการเข้าถึงข้อมูลก็ได้ แล้วก็สิ่งสำคัญที่สุดก็คือ ไม่มี Database model ไหนดีที่สุดนะครับ แต่ละ Database model ต่างมีข้อดีข้อเสียและเหมาะสมในการใช้งานในงานที่แตกต่างกันไป เช่น ถ้าคุณต้องการทำงานกับที่แสดงผลแบบ Graph แล้วเข้าใจง่าย หรืองานที่เวลาจะหาข้อมูลก็ก็ต้องการหาประมาณว่าต้องการหา Node ที่มีความสัมพันธ์ A กับ Node ที่มีความสัมพันธ์ B ที่มีความสัมพันธ์ C อย่างน้อย 3 Node ถ้าเป็นความต้องการประมาณนี้ Graph Database น่าจะตอบโจทย์กว่า ดังนั้นจะเลือกใช้งาน Database model ไหนก็ขอให้ดูความเหมาะสมและข้อจำกดต่างๆในการตัดสินใจ อย่าไปเชื่อใครที่มาบอกคุณว่า Database Model แบบ Document ดีที่สุด แบบ Graph ดีที่สุด แบบ Relational ดีที่สุด (เพราะส่วนใหญ่จะมาขายของ)

อีกเรื่องที่อยากให้รู้ไว้คือ Database model ที่เราเห็นนั้นไม่จำเป็นต้องเก็บแบบที่เราเห็นจริงๆนะครับ ในความเป็นจริงตัว DBMS จะแปลงตัว Database model ที่เราเห็นไปเก็บในรูปแบบอีกรูปแบบหนึ่งที่มีประสิทธิภาพในการ เพิ่ม ลบ แก้ไข และ ดึง อย่างรวดเร็ว ดังนั้นอย่าเข้าใจผิดว่าการลบข้อมูล Node ใน Graph Database นั้นจะต้องค่อยๆวิ่งไปใน Graph แล้วหา Node ที่ตรงเงื่อนไขแล้วลบ ในความเป็นจริง DBMS อาจจะมี Hash map ที่รู้อยู่แล้วว่า Node ที่ต้องการลบอยู่ไหนแล้วทำการลบเลยก็ได้

DBMS : Database Management System คืออะไร

ในส่วนที่แล้วเราพูดถึงว่า Database คืออะไร Database model คืออะไร คราวนี้เราสมมติว่าเราเขียนโปรแกรมเก่งมาก เราสามารถเขียนโปรแกรมที่ให้เก็บข้อมูลและแสดงผล Database model ได้ตามที่ต้องการ ทุกอย่างเหมือนจะเรียบร้อยแต่จริงๆแล้วมันยังไม่จบครับ เพราะจะมีความต้องการอื่นๆเพิ่มขึ้นมาเช่น ต้องรองรับการใช้งานจากผู้ใช้หลายคน แล้วถ้ามี User หลายคนก็ต้องเพิ่มการ Authentication เข้าไป ทีนี้แต่ละ User ก็อาจเห็นไม่เหมือนกัน ดังนั้นก็ต้องแบ่งสิทธิ์ให้เห็นไม่เท่ากันอีก คราวนี้ถ้ามีคนใช้งานเยอะๆใช้งาน Database พร้อมๆกันก็จะเกิดปัญหาเรื่องใช้การใช้งานพร้อมกัน คนนึงสั่ง Delete คนนึงสั่ง Update ตกลงจะให้อันไหนทำงาน คุณก็ต้องเขียนส่วนที่มาจัดการเรื่องนี้อีก

คราวนี้ลองคิดว่าถ้าคุณจะเขียน Application อะไรสักอย่างขึ้นมา คุณต้องมาเขียนส่วนที่เกี่ยวกับ Database แบบนี้ตลอดทุกครั้งมันก็ดูจะเป็นเรื่องที่ดูแปลกๆที่ต้องมาเขียนอะไรซ้ำๆแบบนี้ทุกครั้ง ดังนั้นเขาเลยแยกส่วนที่เกี่ยวกับการจัดการ Database ออกมาเป็น Application โดยเฉพาะ ดังนั้น Database Management System (DBMS) ก็คือ Application ที่ทำหน้าที่บริหารจัดการเกี่ยวกับ Database คราวนี้พอจะเรียกใช้งานอะไร Database ก็มาเรียกที่ DBMS แทนแล้ว DBMS ไปจัดการต่อให้

โดยหน้าที่หลักๆของ DBMS ส่วนใหญ่จะมีดังต่อไปนี้

  1. บริหารจัดการเก็บข้อมูลและแสดงผลข้อมูลตาม Database model ที่กำหนด เช่น DBMS ที่ใช้ Database model เป็น Relational Database model ก็ต้องรองรับการสั่งผ่านภาษา SQL ได้ เมื่อเรียกให้แสดงข้อมูลก็ต้องแสดงออกมาแบบที่เราสั่งเก็บไปจากตัวอย่างก็ต้องแสดงข้อมูลใน Table ออกมาให้ดูได้

  2. จัดการเรื่องสิทธิ์ของการเข้าถึงข้อมูลต่างๆใน Database ของ User ซึ่งแต่ละ User อาจจะมีสิทธิ์ไม่เท่ากัน เช่น User : Shopping Application อาจจะเข้าถึง Table ที่เกี่ยวกับสินค้าได้เท่านั้นไม่สามารถเข้าถึง Table ที่เกี่ยวกับการจ่ายเงินได้ ส่วน User : Payment application อาจจะเห็นข้อมูลเกี่ยวกับ Table : Payment เท่านั้น หรือ User บางพวกมีสิทธิ์ SELECT ดูข้อมูลอย่างเดียวไม่มีสิทธิ์แก้ไขเป็นต้น

  3. จัดการปัญหาการใช้งานข้อมูลเดียวกันพร้อมๆกันหลายคนได้ว่าจะให้คนเรียกคนไหนทำงานสำเร็จไม่สำเร็จ ถ้าถูกเรียกระหว่างการแก้ไขข้อมูลจะเห็นข้อมูลแบบไหน ในส่วนนี้ทางเทคนิคจะเรียกว่า Transaction Processing (เรื่องนี้คุยกันยาวมากครับ เดี๋ยวผมเขียนเป็นตอนใหม่อธิบาย)

  4. ทำ Query optimization ได้ ตรงนี้เราต้องมาทำความเข้าใจก่อนว่าภาษาที่สั่งให้ Database ไปทำงานนั้นบางภาษานั้นเป็นการสั่งแบบ Declarative คือสั่งว่าอยากได้อะไร ไม่ได้สั่งว่าให้ทำแบบไหน ตัวอย่างเช่น ภาษา SQL ดังตัวอย่าง

    1
    2
    3
    SELECT *
    FROM Product
    WHERE ProductPrice < 200.00

    จะเห็นว่าเป็นการสั่งให้ไปเอาข้อมูลที่มีค่า ProductPrice < 200.00 กลับมา ไม่ได้บอกว่าให้ไปเอามาอย่างไร ดังนั้นตัว DBMS จึงมีหน้าที่ไปทำให้การเข้าถึงข้อมูลได้ดีที่สุดโดยที่ผู้ใช้ไม่ได้ต้องรู้ (เรื่องนี้ก็คุยได้ยาวเหมือนกันเดี๋ยวผมเขียนแยกเป็นอีกตอนครับ)

  5. สามารถตั้งกฎและบังคับให้เก็บข้อมูลตามกฎที่กำหนดไว้ได้ ตัวอย่างเช่น ถ้าคุณตั้งกฎไว้ว่า field นี้จะต้องมีค่าไม่ซ้ำกันเลยตัว DBMS จะทำการ Check ให้ว่า field นั้นจะต้องไม่ซ้ำกันเลยใน Database หรือ ถ้าตั้งกฎว่าค่า field ที่เก็บเกี่ยวกับเงินจะต้องมีค่ามากกว่าหรือเท่ากับ 0 ตัว DBMS ก็จะทำการ Check ให้

DBMS ที่มีให้ใช้กันนั้นมีมากมายแต่ละตัวก็จะบอกว่า DBMS ของตนนั้นมี Database Model อะไร ดังนั้นเมื่อคุณคุยกันทางเทคนิคคุณอาจจะต้องลงรายละเอียดกับคนที่คุณคุยด้วยว่าเขาใช้ DBMS อะไร

ตัวอย่างอย่าง DBMS ที่มี Database model เป็นแบบ Relational Database

  • MySQL
  • MariaDB
  • DB2
  • PostgresSQL
  • Oracle Database

สรุป

สำหรับตอนนี้เราได้รู้ว่า Database , Database Model , DBMS คืออะไร ซึ่งมันจะเป็นพื้นฐานทำให้เข้าใจว่าสิ่งนี้มีไปทำไม มีไปเพื่ออะไร ในตอนถัดไปเราจะไปศึกษาเกี่ยวกับ Relational Database ซึ่งถือว่าเป็น Database model พื้นฐานที่โปรแกรมเมอร์ทุกคนควรรู้เพราะถ้าเป็นงานทั่วๆไปก็มักจะใช้ Database model ชนิดนี้

แนะนำหนังสือ

Relational database systems : language, conceptual modeling and design for engineers

หากท่านหาหนังสือเกี่ยวกับ Database ตั้งแต่เริ่มต้นมาอ่าน ผมขอแนะนำหนังสือเล่มนี้เลยครับในหนังสือจะอธิบายว่าทำไมเพราะอะไรถึงเป็นแบบนั้น สอนการใช้งานภาษา SQL ตั้งแต่พื้นฐานจนสามารถ Query ได้ทุกรูปแบบ (ผมใช้โจทย์จากหนังสือเล่มนี้สามารถ Query ได้เกือบทุกอย่างตาม Standard SQL92) สอนออกแบบ Database ด้วยวิธีต่างๆ สอนการทำ Normalization บอกเลยว่าอ่านจบก็สามารถนำความรู้ที่ได้ไปใช้ทำงานได้เลยครับ

ผัวเดียวเมียเดียว - อาณานิคมครอบครัวในสยาม

ผัวเดียวเมียเดียว - อาณานิคมครอบครัวในสยาม

ผัวเดียวเมียเดียว - อาณานิคมครอบครัวในสยาม

ตอนเด็กผมมักนั่งดูละครกับที่บ้าน ละครหลายๆเรื่องที่ผมดูมักจะพูดถึงความรักของตัวพระเอกนางเอกที่รักกันไม่เปลี่ยนใจจากกันแต่ที่สำคัญที่สุดของทุกเรื่องคือทั้งสองฝ่ายจะต้องรักเดียวใจเดียว ในบางเรื่องจะมีตัวละครที่จะเป็นคนที่ไม่ได้รักเดียวใจเดียวและพยายามมีแฟนหลายคนหรือมีภรรยาหลายคน ซึ่งตัวละครเหล่านี้นั้นมีจุดจบไม่ค่อยดีสักเท่าไหร่บางทีก็ตายไม่ก็หมดตัวไม่เหลืออะไร

ตอนเด็กผมก็ไม่คิดอะไรผมก็จดจำเหมือนโดนสอนว่าถ้าอยากมีชีวิตดีๆจะต้องเป็นคนรักเดียวใจเดียวอย่าคิดจะมีมากกว่า 1 ทันที แต่เมื่อโตขึ้นผมก็เริ่มเกิดคำถามว่า “ทำไมคนที่เขามีภรรยาหลายคนนี่มันเป็นคนเลว คนชั่ว เพียงเพราะมีภรรยาหลายคนจริงเหรอ” เมื่อมีคำถามผมก็เริ่มหาว่ามันมีใครที่มีภรรยาหลายคนบ้างไหม ซึ่งผมก็เห็นว่าบนโลกนี้มันมีคนที่มีภรรยาหลายคนครับ และคนที่มีภรรยาหลายคนเขาก็ไม่ได้เป็นอาชญากร ไม่ได้จับภรรยามาทรมาน ข่มขืน ถ้าคุณหาไม่เจอคุณลองศึกษาศาสนาอิสลามก็ได้ครับ ศาสนานี้อนุญาตให้มีภรรยาได้ 4 คน แต่การจะมีได้นั้นผู้ชายจะต้องเลี้ยงดูภรรยาทุกคนให้เท่าเทียมกัน อีกทั้งการจะมีคนที่ 2 ได้นั้น ภรรยาคนแรกต้องยินยอม ถ้าคุณอ่านถึงตรงนี้คุณเริ่มเห็นอะไรไหมครับ การมีภรรยาหลายคนไม่ได้ตัวบ่งชี้ว่าคนคนนั้นเป็นคนไม่ดี และ การที่จะมีภรรยาหลายคนสามารถมีได้ถ้าทั้งหมดตกลงกันได้ (คุณจะเห็นได้ว่าศาสนาอิสลามบังคับว่าต้องเลี้ยงดูให้เท่าเทียม และ ภรรยาที่มีอยู่ก่อนต้องอนุญาต)

อีกคำถามที่น่าสนใจคือ ศีลธรรม(ค่านิยม) ที่ให้การมีภรรยานั้นถูกต้องมาตั้งแต่แรกหรือมันพึ่งเกิดขึ้นขึ้นมา ถ้ามันไม่ใช่สิ่งที่มีมาแต่แรก มันเริ่มตอนไหนและทำไมคนส่วนใหญ่ในสังคมถึงนำมันมาใช้แทนค่านิยมเก่า

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

ผมเก็บความสงสัยนี้มาเรื่อยๆจนวันหนึ่งตอนไปเดินห้องสมุด TK park ผมเหลือบไปเห็นหนังสือเล่มนี้เข้า เฮ้ยหนังสืออะไรวะชื่อ “ผัวเดียวเมียเดียว” ด้วยความสงสัยผมจึงหยิบมาอ่านซึ่งขอบอกเลยว่าผมได้เจอคำตอบของคำถามที่ผมสงสัยมาหลายปีในหนังสือเล่มนี้

หนังสือเล่มถูกแก้ไขจากวิทยานิพนธ์

ผู้เขียนบอกว่าหนังสือเล่มนี้นั้นถูกแก้ไขจากวิทยานิพนธ์ของเขาซึ่งได้รวบรวมข้อมูลเนื้อหาจากแหล่งข้อมูลที่น่าเชื่อถือต่างๆมาสรุป ซึ่งเมื่อมันเป็นงานวิทยานิพนธ์มันจึงเป็นงานเขียนที่เป็นกลางไม่เอาอารมณ์ผู้แต่งมาเป็นตัวตัดสิน ซึ่งหาได้ยากมากในการอ่านหนังสือประวัติศาสตร์หรือเล่าเรื่อง เพราะส่วนใหญ่ผู้เขียนจะชอบเอาตัวเองมาตัดสินว่ามันถูกมันผิด หลักฐานจะเอนเอียงจะเข้าข้างสิ่งที่ตัวเองเชื่อ แต่หนังสือเล่มนี้ไม่ใช่อย่างนั้นครับ หนังสือเล่มนี้เขียนเป็นกลางเอามากๆ ไม่ได้บอกว่า “ผัวเดียวหลายเมีย” เป็นเรื่องแย่ หรือ “ผัวเดียวเมียเดียว” เป็นเรื่องดี หนังสือเล่มนี้หาหลักฐานที่มาของการเปลี่ยนแปลงว่าทำไม เหตุใด อะไรเป็นตัวสนับสนุน ด้วยวิธีการใดและมีการตอบโต้ระหว่างสองฝั่งอย่างไร ซึ่งผมรู้สึกชอบมากเพราะมันปล่อยให้คุณได้ทำความเข้าใจ ไม่มีการเอาสีขาวสีดำมาป้ายว่ามันถูกหรือผิด (แล้วพอเป็นวิทยานิพนธ์มันต้องมีหลักฐานอ้างอิงซึ่งเขามีหลักฐานอ้างอิงให้คุณไปหาอ่านต่อได้ ไม่ได้มาลอยๆแบบหนังสือบางเล่ม)

ประเทศเราในอดีตไม่ได้มีค่านิยมผัวเดียวเมียเดียว

คุณอ่านไม่ผิดครับ (ไม่ต้องทำท่าขยะแขยงครับ หลายๆประเทศที่เจริญแล้วก็เป็น “ญี่ปุ่น” เป็นต้น ) ประเทศไทยของเราไม่ได้มีค่านิยมที่ผู้ชายจะต้องมีผัวเดียวเมียเดียว ในอดีตประเทศเราอนุญาตให้ฝ่ายชายมีเมียกี่คนก็ได้ครับ ในหนังสือเล่าถึงหลักฐานมากมายที่บอกว่า ผู้ดีมีเงินมีอันจะกินนั้นจะมีเมียมากกว่า 1 คน ซึ่งเหตุผลที่มีนั้นไม่ได้มาจากความมักมากในกามอารมณ์เป็นที่ตั้ง แต่มาจากการแสดงความมีหน้ามีตาในสังคม อีกทั้งการได้ผู้หญิงมาเป็นเมียนั้นก็ไม่ได้มาจากการปล้นฆ่าแย่งมา บางส่วนสมัครใจมาเป็นเมียเอง (คุณอ่านไม่ผิดครับ เขายินยอมมาเป็นภรรยาที่ 2 3 4 5 เอง) แต่ในทางที่เลวร้ายหน่อยคือพ่อแม่ขายลูกสาวเพื่อแลกเงิน อันนี้คือเรื่องผัวเมียในประชาชนคนธรรมดา

ส่วนในระดับผู้ปกครองประเทศนั้นการมีภรรยาหลายคนนั้นมาจากการที่ต้องการสร้างความสัมพันธ์ทางการเมืองของคนในชนชั้นผู้ปกครอง มองง่ายๆ ถ้าคุณเป็นญาติทางฝ่ายภรรยาผม ผมจะกล้าทำอะไรที่ไม่ดีไหม เช่นกัน ถ้าผมเป็นสามีพี่สาวคุณ คุณจะกล้าทำเรื่องไม่ดีกับผมเหรอมันก็จะออกมาประมาณนี้ครับ

อีกเรื่องที่คุณควรรู้นะครับ ถ้าใครบอกว่าศาสาพุทธมีภรรยาได้คนเดียวขอบอกเลยว่า “ผิด” นะครับ และคนนั้นควรโดนด่าด้วยว่าโคตรมั่ว ในหนังสือมีหลักฐานว่า ศาสนา “พุทธ” ไม่เคยห้ามให้มี ผัวเดียวเมียเดียว คุณจะมีหลายเมียก็ได้ไม่ผิด (คุณไม่เชื่อไปหามาอ่านได้เลยครับ)

การเปลี่ยนแปลงเป็นค่านิยมผัวเดียวเมียเดียว

หนังสืออธิบายว่าการเปลี่ยนค่านิยมเป็นผัวเดียวเมียเดียวนั้นไม่ได้มาจากปัจจัยเดียว แต่มันเกิดจากหลายๆปัจจัยร่วมกันซึ่งมีหลายอย่างแต่ตรงนี้ผมจะอธิบายแค่ไม่กี่ปัจจัยเพื่อให้คุณไปหาอ่านเอาเองว่ามีอะไรบ้าง

การเข้ามาของมหาอำนาจตะวันตก

เมื่อประเทศมหาอำนาจตะวันตกไม่ว่าจะเป็นอังกฤษ ฝรั่งเศส ได้เข้ามาที่ไทย ซึ่งก็แน่นอนประเทศเหล่านี้ย่อมพาวัฒนธรรม ความเชื่อ ค่านิยมมาเผยแพร่ในประเทศไทยด้วย ซึ่งค่านิยมของประเทศเหล่านี้คือ “ผัวเดียวเมียเดียว” ซึ่งเมื่อค่านิยมไม่เหมือนกัน ประเทศมหาอำนาจเหล่านี้ก็ย่อมจะต้องชี้ให้เห็นว่าทำไมค่านิยม “ผัวเดียวเมียเดียว” ของเขาดีกว่าอย่างไร พร้อมกับชี้ให้เห็นโทษหรือเรื่องไม่ดีของ “ผัวเดียวหลายเมีย” ในมุมมองของเขา อีกทั้งด้วยความที่เขามีวิทยาการด้านทางทหารสูงกว่าเขาจึงใช้อำนาจนั้นทำให้ประเทศไทยต้องทำสนธิสัญญาที่ไทยเสียเปรียบ และจะยอมยกเลิกสนธิสัญญาก็ต่อเมื่อทำตามเงื่อนไขบางอย่างตามที่ประเทศเหล่านั้นกำหนด ซึ่งหนึ่งในเงื่อนไขนั้นคือต้องเขียนกฎหมายว่าด้วยเรื่องครอบครัวให้เสร็จ ซึ่งนั่นเป็นอีกหนึ่งตัวเร่งที่ทำให้ไทยต้องเขียนกฎหมายครอบครัวซึ่งนั่นเป็นการรีบให้ประเทศเราต้องตัดสินใจว่าจะเลือก “ผัวเดียวเมียเดียว” หรือ “ผัวเดียวหลายเมีย”

สื่อสิ่งพิมพ์

ในยุคนั้นเทคโนโลยีการพิมพ์เริ่มเป็นที่แพร่หลายและมันก็กลายเป็นสื่อในการเผยแพร่ความรู้ แนวคิด ความเชื่อต่างๆให้กับผู้อ่าน ซึ่งไม่นานมันก็ถูกใช้เป็นตัวปลูกฝังค่านิยม ซึ่งดูได้จากนิยายที่มีในสมัยนั้น เริ่มแต่งให้คนที่มีคนจำพวก “ผัวเดียวหลายเมีย” มีการใช้ชีวิตหรือจุดจบในนิยายเป็นไปในด้านไม่ดี และแต่งให้คนที่มีค่านิยมแบบผัวเดียวมีเดียวมีชีวิตที่สุข (คุณเห็นความคล้ายอะไรไหมครับและคุณเริ่มเข้าใจรึยังว่าทำไมถึงมีการแบนสิ่งพิมพ์ ทำ List หนังสือต้องห้าม)

อ่านแล้วได้อะไร

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

Node-RED - ตอนที่ 3 - Connect MySQL Database และตัวอย่างการใช้งาน

Node-RED - ตอนที่ 3 - Connect MySQL Database และตัวอย่างการใช้งาน

ตอนนี้เรามาทำให้ Node-RED ติดต่อกับ Database ได้กัน ถ้าถามว่าทำไมต้องต่อ Database ก็เพราะในบางงานเราอยากจำข้อมูลบางอย่างที่ยิงมาที่ Server เช่น เราอยากให้ Return Error : Duplicate กับ Transaction ที่มีเลข Reference ซ้ำเช่นตัวอย่างด้านล่าง

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

การส่งครั้งแรก

# Request

{
"transactionRef" : "TX00001"
}

# Response
{
"status" : "SUCCESS"
}


การส่งครั้งที่สอง

# Request

{
"transactionRef" : "TX00001"
}

# Response
{
"status" : "DUPLICATE"
}

ซึ่งถ้าเราไม่เก็บข้อมูลไว้ว่า transactionRef ไหนใช้ไปแล้วบ้างเราจะไม่สามารถ Return Error Duplicate กลับไปให้ได้เลย ดังนั้นเราเลยต้องทำตัวที่เก็บข้อมูลซึ่งตัวเก็บข้อมูลที่เป็นที่นิยมก็คือ MySql นั่นเอง

ลง Node ที่ใช้เชื่อมต่อกับ Database

สามารถทำตามภาพด้านล่างได้เลย

ทำการ set ค่าให้ node-mysql

ส่วนนี้จะเป็นการทำการ config ให้ node สามารถเชื่อมต่อกับ Database ได้ โดยมีขั้นตอนดังต่อไปนี้

ในส่วนนี้คือการ Config เกี่ยวกับ Database

Host : ที่อยู่ของ database ของผมคือ 192.168.56.101

Port : Port ของ Database ของผมคือ 3306

User : User ที่ใช้ Login เข้า Database

Pass : Pass ที่ใช้ Login เข้า Database

Database : Database ที่ต้องการใช้งาน ของผมคือ Database ที่ชื่อ Node red

จากนั้นกด Deploy ถ้าเชื่อมต่อสำเร็จตัว node จะขึ้นคำว่า connected

สร้าง Table : payment_transaction เพื่อเก็บข้อมูล

ให้นำ sql script ที่สร้าง table ด้านล่างไปสร้าง table

1
2
3
4
5
CREATE TABLE `payment_transaction` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`transactionRef` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

ส่ง Query ไปให้ Database ทำงาน

สั่ง Insert

ตัว node mysql นั้นจะรับคำสั่ง SQL เพื่อส่งไป Database ผ่าน msg.topic โดยถ้าสมมติจะ INSERT ข้อมูลลง Database ด้วยคำสั่ง

1
INSERT INTO payment_transaction VALUES(NULL, 'TX00001');

เราก็สามารถ set ค่า msg.topic ด้วย SQL ด้านบนได้เลย โดยเราจะใช้ Node : Inject ในการส่งค่า msg.topic เข้าไปตามภาพ

  1. ทำการเลือก Node : Inject (Node Injdect นั้นจะทำการสร้าง object : msg ตามที่เรากำหนด ว่าง่ายๆ มีไว้สร้าง msg และส่งไปให้ node ต่อไป ส่วนใหญ่ inject จะมีไว้ใช้ทดลองว่าถ้าได้รับ msg แบบนี้ ตัว flow ของเรานั้นทำงานถูกต้องหรือไม่ )

  2. ลาก Node : Inject เข้าไปใน Flow

  3. ทำการเปิด Node : Inject และกำหนดค่าในส่วนของ Topic ด้วยคำสั่ง SQL ที่เราต้องการ

  4. ทำการกด Done

  1. ทำการลากเส้นเชื่อมระหว่าง inject กับ mysql

  2. ทำการกด Deploy เพื่อ update ตัว node-RED

  3. ทำการกดปุ่มที่ node inject เพื่อให้ inject ส่งค่า

จากนั้นลองไปดูที่ Database ก็จะพบว่าข้อมูลที่เราสั่ง Insert ไป

สั่ง Select

ในส่วนที่แล้วเราสามารถสั่งให้ Insert ข้อมูลได้แล้ว สิ่งที่ขาดอยู่คือการ select เพื่อหาข้อมูล ซึ่งก็ไม่ยากเลยเพราะเรารู้แล้วว่าตัว Node mysql รับคำสั่ง sql ผ่าน msg.topic เราก็แค่เปลี่ยนคำสั่ง INSERT เป็นคำสั่ง SELECT ก็สามารถทำการ SELECT ข้อมูลได้แล้ว โดยเราจะ SELECT ข้อมูลง่ายๆแบบ

1
SELECT * FROM payment_transaction;

  1. ทำการแก้ไข msg.topic ของ node inject เป็น sql ที่เราต้องการ

  2. ทำการกด Done

  1. ทำการนำ Node : debug เข้ามาใน Flow จากนั้นลากเส้นเชื่อมระหว่าง node mysql กับ debug

  2. กด Deploy เพื่อ Update ตัว NodeRed

  1. ทำการกดปุ่มที่ Node inject เพื่อส่งค่าเข้าไป

  2. ลองดูผลลัพธ์จากการ Debug

จากดูที่ช่อง Debug เราจะพบว่าตัว Node sql หลังจากได้รับคำสั่ง SELECT จากจะ Return ผลลัพธ์ SELECT ออกมาเป็น array ของ object ซึ่งแต่ละ object จะมีค่า key ตามการ select ซึ่งอันนี้เรา select * มันเลยได้ key เป็นชื่อ column

จากภาพจะเห็นว่าผลลัพธ์ที่ออกมาเป็น array ที่มี object เดียว เพราะใน database เรามี row อยู่ row เดียว

แล้วจะ Dynamic Query ยังไง

จากความรู้ที่จากหัวข้อที่แล้วพอจะคิดคร่าวได้ประมาณว่า รับ http request มาจากนั้นทำ Query select เพื่อหาข้อมูลว่ามี transactionRef ใน DB ไหม ถ้าไม่มีทำการ insert ข้อมูลลง DB และทำการ Return SUCCESS แต่ถ้ามีแล้วทำการ Return DUPLICATE ปัญหาคือเราจะทำให้คำสั่ง SQL ของเรา Dynamic ได้ยังไง

ภาพร่างคร่าวๆของ flow ที่จะทำ

คราวนี้เราจะ dynamic query ยังไง ตรงส่วนนี้เราทำได้ 2 วิธี

  1. เราใช้ node : function เพื่อทำการสร้าง msg.topic ด้วย code ที่เราต้องการ เช่น
1
2
msg.topic = `SELECT * FROM payment_transaction  WHERE transactionRef = '${msg.payload.transactionRef}'`
return msg;

ซึ่งเมื่อเราต่อ httpin เข้า function แล้วลอง debug จะเห็นว่าคำสั่ง sql ที่ msg.topic จะได้ค่าตาม transactionRef ที่รับมา

ซึ่งถ้าเราเอาลากเส้นต่อเข้าไปที่ node mysql เราก็จะสามารถ select ได้

  1. ใช้ binding param ของ node mysql

ตัว node mysql นั้นมี feaure binding param ให้เราโดยในส่วนของ msg.topic เรากำหนดสร้าง param ขึ้นมาเพื่อจะนำค่าไปแทนที่ได้เช่น

1
INSERT INTO payment_transaction VALUES(NULL, :transactionRef);

ซึ่งถ้าเรา set แบบนี้ตัว transaction จะกลายเป็น param เพื่อเอาค่าไปแปะ ซึ่งคำถามคือจะเอาค่าจากไหนไปแปะ คำตอบคือตัว node mysql จะเอาค่าจาก msg.payload ไปแปะให้ตัวอย่างเช่น ถ้า payload คือ

1
2
3
{
"transactionRef" : "Tx000WASINEE"
}

sql ที่จะได้คือ

1
INSERT INTO payment_transaction VALUES(NULL, 'Tx000WASINEE');

โดยเราสามารถใช้ Node inject ทดสอบดูว่าสามารถทำได้จริงหรือไม่โดยทำการ Set ดังภาพ

ซึ่งเมื่อเราสั่งให้ Inject ทำงานและเข้าไปดูใน Database เราจะพบข้อมูล Tx000WASINEE อยู่ใน Database

เรามาสร้าง Flow ทั้งหมดกัน

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

ภาพรวมของ Flow

Flow ของผมหน้าตาประมาณนี้เดี๋ยวผมจะลงรายละเอียดบางจุดเท่านั้น ส่วนจุดอื่นๆที่ผมอธิบายไปแล้วหรือคิดว่าง่ายนั้นผมจะข้ามไปเลย หากใครสนใจทั้ง Flow สามารถ Download ได้จากตรงนี้เลย Link อย่าลืมเปลี่ยน config ตรง node mysql ด้วยนะครับ

เตรียมค่าสำหรับ Select

ตรงนี้ผมจะเขียนเป็น Function โดยการทำการ copy ค่า payload ไปใส่ใน copyPayload ซึ่งที่ต้องทำแบบนี้นั้นต้องทำเพราะเวลาเราทำการ Select ผ่าน Node mysql แล้วจะทำให้ค่า payload นั้นหายไป ดังนั้นเราต้องทำการเก็บค่า payload ไว้เพื่อใช้ในการ Insert

1
msg.copyPayload = msg.payload

เช็คว่ามีค่าใน DB ไหม

อันนี้ตามภาพเลยว่ามีข้อมูลไหม ถ้ามีไปทางด้านบน ถ้าไม่มีก็ไปทางด้านล่าง

ย้ายค่า copyPayload ไปเป็น payload

มาลอง Test กันดูว่าใช้งานได้จริงไหม

เราจะทดลองยิงกันโดยเราจะยิงโดยใช้ Body ด้านล่างโดยจะลองยิงสองครั้งเพื่อดูว่าได้ Response แบบที่คิดไหมและใน DB ได้เก็บข้อมูลลงไปไหม

1
2
3
{
"transactionRef" : "TX-SUNISA"
}

ลองยิงครั้งแรก

จากการยิงครั้งแรกจะเห็นว่ายิงแล้วได้ SUCCESS

ลองยิงครั้งที่สอง

จากการยิงครั้งนี้จะเห็นว่ายิงแล้วได้ DUPLICATE

ตรวจสอบ Database ว่ามีข้อมูลไหม

จากภาพจะเห็นว่ามีข้อมูลอยู่ใน Database จริงๆ

สรุป

สำหรับตอนนี้เราได้เรียนรู้วิธีที่จะให้ Node-RED ต่อกับ Database แล้ว โดยในตัวอย่างเราใช้ทั้ง SELECT และ INSERT ส่วน UPDATE นั้นก็ไม่น่าจะยากครับ ผมเชื่อว่าถ้าคุณทดลองทำตามที่ผมทำในตอนนี้ผมเชื่อว่าคุณจะสามารถทำการ UPDATE ได้แน่นอนครับ สำหรับตอนนี้ขอจบเพียงเท่านี้สวัสดีครับ

ปรัชญาแมว ปรัชญาเหมียว - แมวและความหมายของชีวิต

ปรัชญาแมว ปรัชญาเหมียว - แมวและความหมายของชีวิต

ปรัชญาแมว ปรัชญาเหมียว

ก่อนที่จะเข้าเรื่องผมขอออกตัวก่อนเลยว่า ผมไม่ใช่คนรักแมว ไม่ได้เป็นทาสแมว ไม่ได้นั่งสังเกตุแมว ไม่เคยได้เลี้ยงแมวจริงๆจังๆ จะเคยเลี้ยงก็แต่แมวจรที่มาทำท่าทางน่าสงสารจนใจอ่อนให้ข้าวและสุดท้ายก็มานั่งรอกินข้าวทุกวัน ดังนั้นผมอาจจะไม่ค่อยเข้าใจความเป็นแมวที่หนังสือพยายามสื่อให้เห็น ดังนั้นผมเลยอาจจะไม่เข้าใจความสุดยอดของหนังสือเล่มนี้อย่างลึกซึ้ง

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

การรู้ว่าตัวเองคือตัวเอง

การรู้ว่าตัวเองคือตัวเองเป็นความสามารถที่สัตว์จำนวนหนึ่งมี ตัวอย่างเช่น มนุษย์ ลิง ซึ่งนักวิทยาศาสตร์สามารถทดลองว่าสัตว์มีความสามารถเหล่านี้หรือไม่ได้ด้วยการให้มันส่องกระจก ถ้าสัตว์พวกนั้นรู้ว่าสิ่งที่อยู่ในกระจกคือตัวเอง พวกมันจะเริ่มลองจับสิ่งนั้นสิ่งนี้ของตัวเอง อย่างลิงเนี่ยมันจะมีการจัดขนตัวเองเลียขนตัวเองให้ดูดี อ่านถึงตรงนี้คงสงสัยว่าแล้วมันเกี่ยวกับปรัชญายังไง การที่เรารู้ว่าตัวเองคือตัวเองเป็นจุดเริ่มต้นของการรู้ว่าเรามีตัวตน เราต่างจากคนอื่น จากนั้นเราเริ่มมีความรู้สึกว่าเราอยากเป็นที่ตัวตนที่ดีขึ้น ดีกว่าเดิม หรืออย่างน้อยก็รักษาสิ่งที่ตัวเองคิดว่าดีไว้ ซึ่งนี่เป็นหนึ่งปัจจัยจากอีกหลายๆปัจจัยที่ทำให้ ศาสนา ค่านิยม ศีลธรรม ต่างๆนาๆในสังคมทีมักจะบอกว่าให้ทำสิ่งนั้นสิ่งนี้เพื่อให้เป็นสิ่งที่ดีกว่าหรือคงความดีไว้ (ผมใช้คำว่าเป็นปัจจัยหนึ่งเพราะมันไม่ใช่มีแค่การรู้ตัวเองแล้วจะทำให้เกิดศาสนา ค่านิยม ศีลธรรมขึ้นมา มันมีอีกหลายๆปัจจัยร่วมกันจึงทำให้เกิด) ซึ่งนั่นก็มีผลเสียสร้างความวุ่นวายปั่นป่วนให้กับมนุษย์อย่างเราให้ต้องขวนขวายให้ได้มาซึ่งการเป็นสิ่งที่ดีกว่าและรักษามันไว้ ในหนังสือเปรียบเทียบว่าแมวไม่รู้ว่าตัวเองคือตัวเอง (แต่เขาไม่กล้าฟันธงนะเขาบอกว่ามันอาจรู้แต่ไม่สนใจ ผมเลยสงสัยว่า เอ้า คุณยังไม่มั่นใจมันเลยแล้วจะมายกย่องมันได้อย่างไร) ดังนั้นมันจึงไม่ต้องขวนขวายอะไร มันใช้ชีวิตไปเรื่อยๆ ตามสัญชาตญาณ เหตุกาณณ์ฉพาะหน้า อยากทำอะไรก็ทำ มันไม่สนใจว่ามันควรจะทำให้ตัวเองดีขึ้นหรือคงความดีไว้ไหม

ความหมายของชีวิต

ความตายเป็นสิ่งที่น่ากลัว (แต่บางคนก็ไม่กลัว) ทำไมมันถึงน่ากลัวก็คงมีหลายเหตุผลแต่เหตุผลที่หนังสืออธิบายไว้คือการที่คุณจะหายไปจากโลกนี้และถูกลืมเลือน ซึ่งแน่นอนว่าหลายๆคนคงไม่ชอบ (ผมคือหนึ่งในนั้น) ดังนั้นหลายๆคนจึงพยายามทำบางสิ่งบางอย่างทิ้งไว้เพื่อให้เหมือนตัวเองไม่ได้หายไปจากโลก ตัวอย่างง่ายๆที่เราเห็นคงเป็นการทำป้ายหลุมศพ การเอาชื่อตัวเองไปเป็นสถานที่ ใส่ชื่อตัวเองเป็นผู้ร่วมสร้างอะไรบางอย่าง หรือบางคนทำให้ตัวเองเป็นที่โด่งดังเพื่อเป็นที่จดจำด้วยวิธีการต่างๆ ส่วนอีกเหตุผลหนึ่งคือเมื่อเรารู้ว่าเราต้องตายแล้วการมีชีวิตอยู่นี้คืออะไร มันมีความหมายอะไร ถ้าเราคิดแบบง่ายๆว่าแบบวิชาชีววิทยาว่า “เราก็แค่เกิดมามีชีวิตสืบพันธุ์แล้วก็ตาย” ซึ่งมันก็อาจจะดูไม่มีความหมายสูงส่งอะไร (หากคุณไม่ให้ค่าการทำให้เผ่าพันธุ์ของเราไม่สูญพันธุ์) ด้วยความที่มันดูไร้ค่าแบบนี้คนเราจึงพยายามหาความหมายของชีวิตให้กับตนเอง ไม่ว่าจะเป็นต้องเป็นคนดี ต้องช่วยเหลือคนอื่น ต้องเป็นที่หนึ่ง ต้องแต่งเติมเรื่องราวต่างๆให้กับชีวิตตนเองเพื่อให้ชีวิตนั้นมีความหมาย ซึ่งสิ่งนี้ต่างจากแมวที่ผู้เขียนอ้างว่าแมวไม่ได้สนใจความหมายของชีวิตมันจึงไม่ต้องมาทนทุกข์ในการพยายามหาความหมายให้กับชีวิตดังนั้นแมวจึงไม่ต้องวุ่นวายและน่าจะมีความสุขและอิสรภาพมากกว่าคน (แต่เขาถ้าเขาคิดสักนิดว่าถ้าความสุขของคนคนหนึ่งคือการหาความหมายให้ชีวิตเขาอาจจะมีความสุขเท่ากับหรือมากกว่าแมวก็ได้)

สรุป

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

ก็สำหรับใครที่ชื่นชอบเกี่ยวกับแมวแล้วอยากได้ความรู้เรื่องปรัชญาผมก็แนะนำให้อ่านเลยครับ รับรองว่าคุณจะรู้สึกเพลิดเพลินไปกับมันอย่างแน่นอน (ยกยอแมวซะขนาดนั้น) แต่หากใครสนใจปรัชญาเพียวๆหรือชอบการอ่านอะไรที่มีเหตุผลหนักแน่นผมก็ไม่ค่อยแนะนำให้อ่านครับ

Node-RED - ตอนที่ 2 - สร้าง Response ตามเงื่อนไขที่ต้องการและสร้าง webhook เพื่อใช้ในการ Test

Node-RED - ตอนที่ 2 - สร้าง Response ตามเงื่อนไขที่ต้องการและสร้าง webhook เพื่อใช้ในการ Test

ในตอนที่แล้วเราทำการสร้าง Mock server ด้วย Node-RED โดยตัวอย่างที่แล้วจะเป็นการสร้าง Transaction Reference และ Response กลับไปให้เพื่อใช้ในการ Test แต่ในการ Test นั้นมีหลายกรณี เช่น กรณีที่ถูกต้อง กรณีที่เกิดปัญหา หรือกรณีต่างๆตามเงื่อนไขที่ Application ต้องการ อีกทั้งใน Appllication บางประเภทนั้นไม่ได้มีแค่การ Response ข้อมูล แต่จะมีการยิง Http request ไปหา URL ที่ตกลงกันไว้หรือที่เรียกกันว่า Webhook ดังนั้นตอนนี้เราจะมาทำสิ่งที่ว่ามาด้วย Node-RED กัน

สิ่งที่เราจะทำกัน

สมมติว่าเราจะเขียน Application ขายของที่เมื่อลูกค้าเลือกของเรียบร้อยแล้วจะส่งไปให้ Payment application (ซึ่งก็คือ Mock server ที่จะใช้ Node-RED สร้าง) ที่ทำหน้าหักเงินจากบัญชีลูกค้าแล้วจากนั้นตัว Payment application จะยิง Request กลับไปหา Server ของเราโดยมี Flow ตามนี้

โดย Request ที่ต้องการหักเงินลูกค้า (1) ของเราจะมีหน้าตาแบบนี้

1
2
3
4
5
6
{
"transactionId" : "4d74cb8f-b3a6-42ba-8383-4f60339fd84b",
"accountNo" : "10110",
"ref" : "ABCDEFG",
"amount" : 300
}

ส่วน Response ที่กลับมาจาก Payment Application (2) จะมีหน้าตาแบบนี้

1
2
3
4
{
"transactionId" : "4d74cb8f-b3a6-42ba-8383-4f60339fd84b",
"status" : "PROCESSING"
}

ส่วน Request ที่ยิงกลับไปที่ Webhook (3) จะมีหน้าตาแบบนี้

1
2
3
4
5
6
{
"transactionId" : "4d74cb8f-b3a6-42ba-8383-4f60339fd84b",
"status" : "SUCCESS",
"amount" : 300,
"errorMessage" : null
}

สร้างกรณีสำเร็จและหาบัญชีไม่เจอ

ในการทำงานของ Application นั้นไม่ได้เขียนรองรับกรณีที่สำเร็จอย่างเดียว เราจะต้องเขียนกรณีที่เกิดปัญหาด้วยซึ่งเมื่อต้องการ Test ว่าเมื่อเกิดกรณีที่เกิดปัญหาแล้ว Application ของเราจะทำงานได้อย่างที่คิดไหม ดังนั้นเราจึงต้อง Mock ตัว Payment Application ส่ง Response ที่มีปัญหากลับมาคือหาบัญชีไม่เจอ โดยจะให้กรณีนี้เกิดขึ้นเมื่อส่งค่า accountNo เป็น UNKNOWN โดยตัว Payment application จะตอบ Http response status เป็น 404 กลับมา ดังนั้นเมื่อจะทำการ Test กรณีนี้ก็แค่ตอนส่งค่าไปก็ set ค่า accountNo เป็น UNKNOWN

ตัวอย่าง Http request ที่ยิงไปแล้วจะเกิดปัญหา

1
2
3
4
5
6
{
"transactionId" : "4d74cb8f-b3a6-42ba-8383-4f60339fd84b",
"accountNo" : "UNKNOWN",
"ref" : "ABCDEFG",
"amount" : 300
}

สร้าง Flow Node-Red สำหรับกรณีทั่วไป

  • ในขั้นนี้ไม่ทำอะไรมากครับเราจะสร้าง component http in เข้าไปโดยกำหนด path เป็น /payment

  • ในส่วนถัดไปเราจะทำการกำหนด Response เพื่อตอบกลับโดยในตอนที่แล้วเราใช้ component : function สร้าง แต่ตอนนี้เราจะลองใช้ Component : change ในการกำหนดค่า Response โดยมีขั้นตอนดังต่อไปนี้

    1. ทำการลาก Component : change ลงไปใน Flow

    2. ทำการเชื่อม Http in กับ Change เข้าด้วยกัน

    3. ทำการคลิกที่ Component : change จากนั้นดูที่ด้านขวาในส่วนนี้จะเป็นการบอกว่าเราจะทำการเปลี่ยนค่า msg อะไรบ้าง โดยเราจะแก้ไขข้อมูล msg.payload.status เป็น PROCESSING เพื่อให้ตรงกับ Response ที่เราต้องการ เราจึงเลือก type ของการเปลี่ยนเป็น set จากนั้นเลือกค่าเป็น PROCESSING

    4. ทำการกด Add เพื่อเพิ่มการแก้ไขข้อมูลของ msg

    5. ทำการเลือก type เป็น Delete เพื่อลบ accountNo, ref, amount

    6. กด done

  • ในส่วนถัดไปการลาก Component : http response จากนั้นลากเชื่อมระหว่าง change เข้ากับ http response

  • เปิด Postman เพื่อทดลอง

    เลือก set endpoint ให้ยิงไปที่ http://your-nodered-endpoint/payment ( Node-RED ของผมคือ http://192.168.56.101:1880) และ payload เป็น

    1
    2
    3
    4
    5
    6
    {
    "transactionId" : "4d74cb8f-b3a6-42ba-8383-4f60339fd84b",
    "accountNo" : "10110",
    "ref" : "ABCDEFG",
    "amount" : 300
    }

    จากนั้นลองยิงซึ่งจะได้ผลลัพธ์ดังภาพ

จากการ setting ทั้งหมดเราจะได้ mcok server ที่ตอบกลับในกรณีที่สำเร็จแล้ว

สร้าง Flow Node-RED ในกรณีไม่สำเร็จ

ในขั้นตอนที่แล้วเราได้กรณีที่สำเร็จแล้ว ในตอนนี้เราจะสร้างกรณีที่ไม่สำเร็จโดยมีขั้นตอนดังต่อไปนี้

  • ทำการสร้างเงื่อนไขให้ Flow ด้วย Component : change

    1. ทำการดึง Component : change เข้ามาใน Flow

    2. ดูตรงส่วน Property ตรงส่วนนี้จะเป็นการบอกว่าให้ทำการตรวจสอบ property ไหนโดยจากที่เราจะทำกันคือเราจะตรวจสอย property : accountNo ที่อยู่ใน request ดังนั้นค่าจึงเป็น msg.payload.request (payload คือ request ที่ยิงเข้ามา)

    3. ส่วนนี้เป็นเงื่อนไขที่เราจะตรวจสอบโดยที่เราจะทำกันจะเช็คว่าหากค่า accountNo จะเข้าเงื่อนไข error

    4. ส่วนอันนี้เป็นเงื่อนไขกรณีที่ไม่ตรงกับเงื่อนไขอื่นๆเราจะให้ทำอะไร

  • ทำการสร้าง component : Http respones และกำหนดให้ return http status เป็น 404 เพื่อใช้เป็นการตอบกลับกรณีไม่สำเร็จ

  • ทำการแก้ไขการเชื่อมต่อ

    1. ทำการลากเส้นจาก switch ที่เป็นเงื่อนไขแรกไปที่ component : http response ตัวใหม่ เพื่อให้เวลาเจอกรณีไม่สำเร็จให้ไปออก response 404

    2. ทำการลากเส้นจาก switch ที่เป็นเงื่อนไขที่ 2 ไปที่ component เดิม เพื่อให้กรณีสำเร็จไปส่งไปที่เงื่อนไขเดิม

  • ทดสอบยิงด้วย Postman

    จากภาพจะเห็นว่าถ้า accountNo เป็น UNKNOWN http status ที่กลับมาจะเป็น 404

    แต่กรณีที่ accountNo ไม่ใช่ UNKNOWN ข้อมูลจะตอบกลับมาปกติ

สร้างกรณียิง Http request ไปที่ webhook

ในส่วนนี้เราจะทำการจำลองให้ตัว payment application ยิง Request กลับไปที่ webhook ที่เรากำหนด โดยการยิงเนี่ยเราจะทำการหน่วงเวลาเพื่อให้สมจริงคือจะหน่วงเวลาทิ้งไว้สัก 5 วินาทีจากนั้นค่อยยิงไป โดยเรามีขั้นตอนการทำดังต่อไปนี้

  • ทำการสร้างตัวหน่วงเวลาโดยการดึง component : delay

  • ทำการสร้าง Component : change และกำหนดค่าเพื่อให้ได้ response ตามที่เราต้องการคือให้ status เป็น success เพื่อ field errorMessage และลบ field อื่นที่ไม่ต้องการออก

  • ทำการส่ง Http request ไปที่ url ที่กำหนด

    1. เนื่องจากเราไม่ได้เขียน My application จึงไม่มีที่ให้ตัว Node-RED ยิงไปหาดังนั้นเราจะใช้ตัว web hook online แทน โดยเราจะทำการเข้า web https://webhook.site/ ซึ่งเมื่อเข้าแล้วให้เรา copy ตรงส่วน url ไว้ และเปิดหน้านี้ทิ้งไว้

    2. ทำการสร้าง Component : http reqeust จากนั้น set ค่า Method เป็น POST และ set ค่า URL เป็น URL ที่เราทำการ Copy จากส่วนขั้นตอนที่แล้ว

  • ทำการเชื่อม flow ดังภาพ

    1. เชื่อมให้ข้อมูลที่มาจากกรณีสำเร็จไหลไปเข้า component : delay เพื่อทำการหน่วงเวลา

    2. เชื่อมข้อมูลจาก component : delay ไปหา component : change เพื่อทำการแก้ไขค่าให้เป็นไปตาม Response ที่เราต้องการ

    3. เชื่อมข้อมูลจาก component : change ไปที่ http request เพื่อยิง http request ไปที่ url ที่ต้องการ

  • ทำการทดลองส่งข้อมูลด้วย Postman เพื่อทดสอบว่าใช้งานได้จริงหรือไม่

  1. เลือก set endpoint ให้ยิงไปที่ http://your-nodered-endpoint/payment ( Node-RED ของผมคือ http://192.168.56.101:1880) และ payload เป็น

    1
    2
    3
    4
    5
    6
    {
    "transactionId" : "4d74cb8f-b3a6-42ba-8383-4f60339fd84b",
    "accountNo" : "THANAPORN",
    "ref" : "ABCDEFG",
    "amount" : 300
    }

  1. จากนั้นเข้าไปดูที่ webhook.site ที่เปิดไว้ในขั้นตอนก่อนหน้านี้จะเห็นว่ามี Request เข้ามาแล้วซึ่งนั่นหมายความว่าตัว Node-RED ของเรายิง http request ออกมาแล้ว

สรุป

สำหรับตอนนี้เราได้ทำการสร้าง Flow ของ Node-RED ให้สามารถ Response ได้ตามเงื่อนไขที่เราต้องการและทำตัวยิง Http request ไปยัง endpoint ที่เราต้องการได้ ซึ่งทั้งสองเป็นสิ่งที่เป็นเรื่องพื้นฐานที่เราจะเจอเวลาที่ต้องทำการ Test ให้ครบ Loop

ความทรงจำปี 2022

ปี 2022 ที่ผ่านมา

ตอนมาเขียนสรุปปี 2022 เนี่ยยังนึกถึงตอนที่ยังนอนป่วยอยู่โรงพยาบาลเมื่อปีที่แล้วอยู่เลย ไม่ทันไรก็ได้ก็สิ้นปีแล้วซึ่งมองย้อนกลับไปแล้วรู้สึกว่าปีนี้นั้นเป็นปีที่ว่างเปล่าเสียเหลือเกิน เหมือนว่าเราไม่ได้โตขึ้นหรือเก่งขึ้นเลย

งาน

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

ในส่วนของเนื้องานปีนี้ได้ทำ Application ที่เป็นระดับที่ผู้ใช้ทั่วไปที่ไม่ใช่ Enterprise มาใช้แล้ว ซึ่งถามว่าภูมิใจไหมก็บอกเลยว่า “ไม่” เพราะมันน่าจะดีได้มากกว่านี้ เร็วได้มากกว่านี้ (ถ้าผมเขียนโปรแกรมได้ดีกว่านี้) ทำเงินได้มากกว่านี้ แต่อย่างว่าแค่แม่งขึ้น Production แล้วขายของได้เงินเกือบ 20 ล้าน โดยที่ไม่เคยมีใครมาให้ Requirement (จริงๆแม่งมาให้ 1 สัปดาห์ก่อนขึ้น Production มึงจะมาทำควยอะไร แถมมาก็ติ ไม่บอกว่าดีคืออะไรด้วย ถ้ากูอยู่วันนั้นแม่งน่าจะโดนกูยัดด้วยคำถามแล้วต้อนแม่งให้จนแบบที่โสคราตีสทำแน่) แม่งก็ปาฏิหารย์ขนาดไหนละ

ในด้านเทคนิคปีนี้ได้ k8s แบบขึ้น Production สักที เรียกว่าเริ่มจาก Beginer ที่เรียนคอร์สแค่คอร์สเดียวแล้วลุยเดี่ยวกับ Vendor ที่แม่งตอบแบบ เฮ้ย เอาจริงดิ มันใช่จริงเหรอวะ กูทำตาม Manual ที่มึงบอกแล้วมันไม่ได้นะ ถึงขนาดที่ต้องไปแกะ template config ของแม่งจนรู้ว่า set ผิดแล้วต้องมา override ทับแม่ง ซึ่งถ้ามองว่าเป็นข้อดีก็ดีแหละจาก k8s ประมาณ 10/100 ตอนนี้น่าจะได้สักประมาณ 40/100 ละ

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

ความรัก

สำหรับปีนี้เรื่องความรักก็ยังวนอยู่ที่เดิม ในเรื่องของตัวบุคคลก็คือกลับไปคุยกับคนที่เคยจีบไปเมื่อสองสามปีที่แล้ว ซึ่งต้นเหตุที่กลับไปคุยกันก็เพราะเราดันไปเจอป้ายโฆษณาวงเกาหลี ก็เลยถ่ายส่งไปให้เขาดูว่ามันมีป้ายอย่างใหญ่เลยนะน้องมาถ่ายรึยัง จากเหตุการณ์นั้นก็เลยลองกลับไปคุยกันอีกรอบ ได้ไปดูหนังได้ไปกินข้าวด้วยกัน (แต่เราไม่เคยถ่ายรูปคู่ด้วยกันสักครั้งเลย) แต่ทุกอย่างมันก็จบแบบเดิมคือเหมือนลักษณะการใช้ชีวิตของสองคนไม่เหมือนกัน ชอบคนละอย่าง ใช้ชีวิตคนละแบบ อีกทั้งน้องเขาเองก็มีตัวเลือกที่น่าสนใจกว่าผมด้วย ส่วนตัวผมเองก็ยังไม่สามารถก้าวข้ามเส้นการตัดสินใจเรื่องความรักได้ ผมไม่สามารถนิยามได้ว่าสิ่งที่ผมรู้สึกมันคือความรักแบบที่ควรจะมี หรือเป็นแค่ความต้องการทางของร่างกายเฉยๆ ซึ่งด้วยทั้งหมดทั้งมวลมันก็มาจบที่น้องเขาก็เลือกตัวเลือกที่ถูกใจกว่า
ส่วนผมก็ยอมรับการตัดสินใจและอยู่กับคำถามเรื่องความรักของผมต่อไป ส่วนในเรื่องของทัศนคติก็เป็นเหมือนที่ได้อ่านไป ผมยังคงวนเวียนอยู่กับการนิยามคำว่ารัก รักที่ถูกที่ควรมันคืออะไร ซึ่งจริงๆมันอาจจะไม่ยุ่งยากถ้าผมไปให้สุดไม่ต้องสนใจอะไร หาผู้หญิงสักคนคุยด้วยพอถูกคอแล้วก็ขอเป็นแฟนไป ได้ก็ดีถือว่าได้ลอง ไม่ได้ก็ช่างแม่ง แต่ก็เสือกคิดเยอะ คิดว่ามันเหมาะมันควรแล้วเหรอที่จะใช้คำว่ารักโดยที่ไม่รู้ว่ามันคืออะไร หรือเราบอกว่ารักโดยที่อีกฝ่ายไม่รู้ว่ารักของเรากับรักของเขาตรงกันไหม แล้วพาทั้งเขาและเราไปเสียเวลาในความรักที่ทั้งคู่เห็นไม่ตรงกัน

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

อารมณ์

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

สุขภาพ

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

ตามศิลปิน

ปีนี้ก็ยังตามศิลปินอยู่เหมือนเดิม ตอนแรกว่าจะน้อยลงแต่ดันไปเจอศิลปินที่ชอบคนใหม่ๆขึ้นมาอีก แต่ก็มีหลายคนที่เลิกเป็นศิลปินไป บางคนเลิกแล้วก็หายไปเลย (ซึ่งก็หาเจอแต่เห็นแล้วรู้ว่าเขาต้องการความส่วนตัวมากๆ) บางคนก็ย้ายไปทำอย่างอื่น ปีนี้ก็น่าจะยังตามอยู่แต่คิดว่าไม่น่าจะหาเพิ่มละคงเป็นศิลปินคนเก่าๆที่ได้ตาม

อ่านหนังสือ

หลังจากเริ่มอ่านหนังสือพวกปรัชญา นิยาย มาได้หลายปีก็ได้เจอปัญหาหนังสือล้นบ้าน แม่เริ่มบ่นว่าห้องรกไม่มีที่จะวางของ ซึ่งทำให้ต้องหาวิธีหาหนังสือมาอ่านแทนการซื้อ ซึ่งวิธีแก้ปัญหาก็คือการไปสมัครสมาชิกห้องสมุดนั่นเอง ซึ่งผมไปสมัครเป็นสมาชิกรายปีกับห้องสมุด TK Park ซึ่งถือว่าเป็นการตอบโจทย์ดีเกินคาด เพราะผมได้อ่านหนังสือเยอะขึ้นโดยไม่ต้องเสียเงินซื้อหนังสือ แถมยังสามารถอ่านหนังสือบางเล่มที่ไม่ได้ตีพิมพ์อีกแล้ว ซึ่งผมสามารถยืมหนังสือได้หลากหลายประเภทมากๆไม่ว่าจะเป็นปรัชญา วรรณกรรมคลาสิคของประเทศต่างๆ ซึ่งพอได้อ่านแล้วรู้สึกได้เห็นโลกในมุมมองใหม่ๆ ได้เห็นเทคนิคการเขียน การเล่าเรื่อง ความรู้ในศาสตร์ต่างๆที่แตกต่างออกไป สำหรับใครที่ชอบอ่านหนังสือและอยู่ในกรุงเทพ ผมแนะนำให้ลองสมัครสมาชิก TK Park ดูครับรับรองว่าคุ้มแน่นอน

งานอดิเรก

ปีนี้ยังคงทำช่อง Youtube กับ Blog เหมือนเดิม โดยช่องจะเพิ่มเนื้อหาสอนทำโจทย์จากเว็บ programming.in.th ส่วน podcast ที่ว่าจะทำสัปดาห์ละตอน แต่พอทำไปเรื่อยๆเหมือนจะหมดเรื่องพูดเลยหยุดทำไปนานพอสมควร ก็เลยเปลี่ยนเป็นอยากพูดเมื่อไหร่ค่อยทำ ส่วน Blog ก็ลงเกี่ยวกับการเขียนโปรแกรมบ้าง แต่ปีนี้จะลงเกี่ยวกับหนังสือซะส่วนใหญ่เพราะอยาก Review หนังสือที่คิดว่าดีให้คนอื่นไปลองอ่านบ้างบวกกับอยากทำเป็น Gallery บันทึกว่าตัวเองเคยอ่านอะไรไปบ้าง

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

เป้าหมายเมื่อปีที่แล้ว

  • ทำ podcast ทุกสัปดาห์

    อันนี้คือไม่ได้ทำเพราะหมดเรื่องพูด ตอนนี้เป็นทำเมื่ออยากทำแทนละ

  • อ่านหนังสือทุกวัน

    อันนี้ทำได้และเหมือนจะกลายเป็นนิสัยไปละคือถ้าไม่ได้อ่านจะเหมือนชีวิตขาดอะไรไป

  • เล่นกีต้าร์

    อันนี้เหมือนเดิมเลยคือเอากีต้าร์ไว้บริษัท พอ Work From Home ก็เลยไม่ได้จับเลย

  • หาอาชีพเสริมที่ได้เงินแบบจริงๆจังๆ

    อันนี้กลับไปเขียนนิยายแล้วก็เขียนเรื่องสั้นละ แต่ดูเหมือนจะไม่ตอบโจทย์สักเท่าไหร่ อาจจะต้องไปหัดเขียนนิยายแบบที่คนปกติเขาชอบกันบ้าง

  • ลองเล่น IOT

    อันนี้ไม่ได้เล่นเลยเพราะไม่ใส่ใจเองแล้วก็ไม่รู้จะเอาไปประยุกต์กับอะไรด้วย คงจะต้องอยากลองทำอะไรที่เป็น Hareware จริงๆดูถึงจะได้ลองทำ

ปี 2023 ทำอะไรดีล่ะ

  • หาอาชีพเสริม

    เรื่องนี้ก็ยังเป็นเรื่องหลักที่อยากทำ อย่างแรกคงจะลองเขียนเรื่องสั้นต่อไปสัก 10 - 20 ตอน แล้วเปลี่ยนไปเขียนนิยายดูสักเรื่องสัก 10 - 20 ตอน หรือไม่ก็อาจจะลองเป็นติวเตอร์สอนเขียนโปรแกรมดูสักครั้ง

  • ทำช่อง Youtube เป็นช่องสอนเขียน Code

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

  • ออกกำลังกาย

    ปีหน้าจะออกกำลังกายเป็นประจำทุกวัน อาจจะเป็นการออกกำลังกายเล็กๆน้อยๆแบบ วิดพื้น ซิตอัพ เล่นดัมเบล เล่นโยคะแบบง่ายๆเพื่อบิดตัว ยืดร่างกาย โดยหวังว่าน่าจะทำให้ร่างกายยังแข็งแรงเหมือนเดิม

ถึงปีนี้จะเป็นปีที่ว่างเปล่าแต่มันก็เป็นปีที่เต็มไปด้วยเรื่องราวต่างๆไม่ว่าจะเรื่องดี เรื่องร้าย ซึ่งทุกอย่างมันผ่านไปแล้วไม่สามารถกลับไปแก้ไขอะไรได้ แต่มันจะเป็นความทรงจำบทเรียนที่จะอยู่กับผมไปตลอดไป และสุดท้ายหวังว่า “เราคงจะได้พบกันอีก”

เพลงที่ได้ฟังในปีนี้

Nobody knows
ถ้าเธอ
Not Ready to Lose You
สมมติว่าเรา
คืนวันศุกร์
ชอบอยู่คนเดียว
705
Call Center
เหตุผลง่ายๆ
เฮอร์ไมโอน้อง ในจักรวาลอนิเมะ
 I Ain’t Worried
Top Gun Anthem
24/7 You
แก้ตัว
ไปต่อไม่ไหว
SIGH
Soudane
ファンサ(Fansa) cover by Baifern
BFF
คนหรือไมโครเวฟ (Microwave)

Node-RED - ตอนที่ 1 - ทำ Mock Server ด้วย Node-RED

Node-RED - ทำ Mock Application Server ด้วย Node-RED

ปัญหาในการใช้งาน Application Server ที่ต้องทำงานด้วย

ปัญหาหนึ่งที่เจอในเวลาเขียน Program ก็คือเราต้องการใช้งาน Application Server ที่ต้องทำงานด้วย แต่เราไม่สามารถติดต่อมันได้ด้วยเหตุผลบางอย่างเช่น

  • Application Server นั้นเป็นของ Third party เขาไม่เปิดให้ใช้งานจนกว่าจะทำข้อตกลงทางกฎหมายหรือต้องตกลงบางอย่างให้เสร็จก่อน
  • Application Server นั้นยังพัฒนาไม่เสร็จ จะเสร็จก่อนกำหนดเวลา 1 สัปดาห์
  • การเรียกใช้งานนั้นต้องเสียเงิน ตัวอย่างเช่น เรียกใช้งาน SMS Provider ที่ใช้ส่ง OTP ให้ลูกค้า

ซึ่งด้วยปัญหาต่างๆเหล่านี้มักทำให้เราไม่สามารถเขียนโปรแกรมต่อได้ ทางเหล่าผู้พัฒนาก็เลยแก้ปัญหากันโดยกำหนด Spec ในการใช้งานตัว Application Server ขึ้นมา ตัวอย่างเช่น API ใช้งาน Board Trello , CAT FACE เพื่อบอกให้รู้ว่า Application Server มี Request Response อย่างไร เพื่อให้ทีมพัฒนาที่จะมาใช้ด้วยเนี่ยสามารถไปเขียนโปรแกรมก่อนได้

แต่ปัญหามันไม่ได้ถูกแก้เพราะต่อให้เราได้ spec ของ Application Server ที่เราต้องติดต่อด้วยมาแล้ว แต่เราก็ไม่สามารถยิงไปหามันจริงๆได้ ทำให้เราไม่สามารถ Test code ที่เราเขียนขึ้นมาได้ตาม Flow ตัวอย่างเช่น ถ้าคุณต้องเขียน App ซื้อของออนไลน์ Flow การทำงานตั้งแต่ต้นจนจบคือเลือกสินค้าและจ่ายเงิน ในส่วนการเลือกสินค้านั้นเป็นส่วนของเราที่เขียน แต่ส่วนที่เป็นส่วนจ่ายเงินนั้นเป็นส่วนที่คนอื่นเขียนและเราไม่สามารถใช้งานได้ พอไม่สามารถใช้งานได้เราก็ Test ทั้ง Flow ไม่ได้ วิธีแก้ปัญหาที่ไม่ค่อยดีอย่างหนึ่งที่ Developer จำนวนหนึ่งมักใช้คือทำการแก้ Code ตรงที่เรียกให้มันผ่านตลอดหรือมีผลตามที่อยากได้ ซึ่งมันก็ตอบโจทย์การ Test ให้ผ่านไปได้ แต่ปัญหาคือ Developer มักจะลืมกลับไปแก้ Code ตรงนั้นหลังจาก Test เสร็จ ซึ่งมันก็จะลืมไปจนขึ้น Production และเกิดปัญหานั่นเอง

ดังนั้นมันจะมีดีกว่าไหมถ้าเราสามารถติดต่อไป Application Server ที่เราสามารถกำหนด Response ที่ Application Server นั้นตอบกลับได้อย่างที่เราต้องการ โดยไม่ต้องใส่ Logic ให้เหมือนจริง ซึ่งก็ขอดีใจด้วยครับ ชาว Developer ได้สร้างสิ่งนี้ขึ้นมาให้เราแล้วมากมายมันเรียก Mock Server ซึ่งมีอยู่หลายเจ้า โดยเจ้าที่ผมจะเอามาสอนวิธีใช้คือ Node-RED

จริงๆ Node-RED สามารถทำได้มากกว่า Mock server นะครับ มันสามารถทำได้หลายอย่างมากมีหลายคนเอาไปทำเป็น Server ส่งข้อมูลระหว่าง IOT บางคนก็เอาไปทำเป็น Application Server ที่มีหน้าที่ประมวลผลเลยก็มี

Start Node-RED ด้วย Docker

เนื่องจากเข้าใจว่าคนที่มาอ่านบทความนี้คงจะเป็น Dev QA หรือผู้ที่น่าจะเคยใช้งาน Docker มาแล้วดังนั้นจึงขอใช้ Node-RED ในรูปแบบของ Docker เพราะมันสะดวกไม่ต้อง config อะไรให้ยุ่งยาก

1
2
3
4
5
6
7
8
9
10
11

# ตัวอย่างการเรียกใช้งาน
# path/to/persistent/data ให้เปลี่ยนเป็น path ที่คุณอยากจะ mount data กับ Node-RED
# โดยถ้าไม่ทำการ map ไว้อะไรที่เคยทำไว้จะหายไปทั้งหมด

# กรณีเป็น Linux ให้ run
sudo chown -R 1000:1000 path/to/persistent/data


docker run -d -p 1880:1880 -v path/to/persistent/data:/data --name mock-server nodered/node-red:latest

โดยเมื่อ Run command ด้านบนเสร็จให้ลองเปิด Browser แล้วใส่ url

1
2
3
4
5
6
http://[ip ของเครื่องที่ Run docker]:8080

# ตัวอย่างคือ เครื่องที่ Run docker มี IP : 192.168.56.101 url ที่ได้จะเป็น

http://192.168.56.101:1880/

ซึ่งเมื่อเปิดแล้วจะได้ภาพแบบด้านล่าง ถ้าทำไม่ผิด

Mock Server

หากทาง Spec ที่ตกลงกันนั้นมี Request กับ Response ดังต่อไปนี้

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

ENDPOINT : /api/data
HTTP METHOD : POST

# HTTP REQUEST
{
"ref" : "string"
}

# HTTP RESPONSE

{
"transactionId" : "string", // unique string
"ref" : "string" // ref from request
}


โดยเรายิง request ที่มี ref ไปทาง server จะ return response กลับไปให้เป็น transactionId ที่สร้างขึ้นมาโดยต้อง unique และต้องเอา ref ส่งคืนกลับไปให้ใน response ด้วย

ด้วยความต้องการด้านบนเราสามารถใช้ Node-red ทำการ Mock server ได้โดยทำตามขั้นตอนดังต่อไปนี้

สร้างขารับ Http request ด้วย http in

  1. เลือก Component ชื่อ http in แล้วลากลงไปใน Flow
  2. ทำการกำหนด Method เป็น POST
  3. ทำการใส่ค่า URL เป็น /api/data
  4. กดปุ่ม Done

สร้างขาตอบกลับ Http response ด้วย

  1. เลือก Component ชื่อ http_response แล้วลากลงไปใน Flow
  2. ทำการลากเส้นจาก http in ไปหา http_response
  3. กด Deploy ด้านขวาบน

ลองทดสอบ Mock Server

  1. ทำการเปิด PostMan จากนั้นตั้งค่า Method เป็น POST

  2. ทำการใส่ url เป็น http://[ip เครื่องที่ run docker]:1880/api/data (ของผมเป็น VM เครื่อง 192.168.56.101)

  3. จากนั้นเลือก Body เลือกชนิดเป็น raw และเปลี่ยน type ของ raw เป็น Json จากนั้นกำหนด body เป็น

    1
    2
    3
    {
    "ref" : "TX1803"
    }

    จากนั้นกด Send

  4. ดู Response ที่กลับมา

    จาก Response จะเห็นว่า response นั้นเหมือนกับ request เลย เพื่อพิสูจน์ว่าเป็นจริงไหมให้ลองแก้ request เป็นข้อมูลแบบอื่นดู เช่น

    1
    2
    3
    4
    {
    "ref" : "TX1803",
    "testInput" : "TEST NEW INPUT"
    }

    จากนั้นยิงใหม่จะเห็นว่าข้อมูล Response ก็เปลี่ยนไปตาม Request

ทำการแก้ไข Response ที่จะตอบกลับ

  1. ทำลาก component : function ลงไปใน flow

  2. ทำการเพิ่ม

    1
    msg.payload.transactionId = "1234567890123";
  3. กด Done

  4. จากนั้นลากเส้นเชื่อมระหว่าง http in <–> function <–> http response

  1. กด Deploy

  2. จากนั้นเปิด Postman และยิง Request เหมือนในข้อก่อนหน้านี้และดูผลลัพธ์ที่ได้

ซึ่งจะเห็นว่ามีฟิลล์ชื่อ transactionId กลับมาใน response ด้วยซึ่งเป็นค่าเดียวกันกับที่เรา ตั้งค่าไปในขั้นตอนที่ 2 ซึ่งจะเห็นว่าเราสามารถตั้งค่าให้ Response ที่จะตอบกลับไปเป็นอะไรก็ได้ผ่านการ set ค่าที่ field msg.payload โดยถ้าเราเปลี่ยนข้อมูลตรง function เป็น

1
2
3
4
msg.payload = {
"field1" : "12345689",
"name" : "nico"
};

จากนั้น Deploy และใช้ Postman ยิงไปใหม่จะได้ข้อมูลดังภาพ

จะเห็นว่า Response ที่ตอบกลับมานั้นจะมีค่าเหมือนกับที่ set ค่าให้กับ msg.payload

  1. ทำการตั้งค่า function เป็นอย่างด้านล่างและกด Deploy

    1
    2
    3
    // ส่วนนี้จะทำการเอาค่า unix timestamp มาเป็น transactionId ซึ่งถ้าไม่ยิงมาพร้อมกันจริงๆเลขจะไม่ซ้ำกัน
    var transactionId = String(Date.now());
    msg.payload.transactionId = transactionId;

  2. ลองยิงด้วย Postman จะเห็นว่าได้ข้อมูลตาม spec api ที่ต้องการ

Export Flow และ Import Flow

Export Flow

เราสามารถ Export flow ที่เราพึ่งเขียนไปออกมาเก็บไว้ที่เครื่องตัวเองหรือจะส่งต่อให้ Dev คนอื่นๆที่ทำงานร่วมกับเราก็ได้โดยทำตามภาพ โดยไฟล์ที่ได้จะเป็นไฟล์ .json

Import Flow

เราสามารถ Import flow ได้โดยกดเลือก Import ตามภาพ

สรุป

ในตอนนี้เราสามารถทำการ Mock Http Response ให้สามารถตอบกลับตาม spec ที่เราต้องการได้แล้ว ในส่วนของ Component http in , http response ต่างๆนั้นสามารถทำอะไรได้อีกหลายๆอย่าง โดยสามารถไปอ่านเพิ่มได้ที่ https://cookbook.nodered.org/http/#http-endpoints