Table, Data Type និង Constraint

មាតិកា

  1. CREATE TABLE ដំបូងរបស់អ្នក
  2. ការជ្រើសរើស Data Type
  3. ហេតុអ្វីលុយមិនដែលប្រើ Float
  4. Identity Column និង Primary Key
  5. NULL៖ តម្លៃដែលមានន័យថា "មិនស្គាល់"
  6. Constraint៖ ច្បាប់ដែល Database អនុវត្ត
  7. Table របស់ shopdb៖ customers និង products
  8. មើលច្បាប់ដំណើរការ៖ សារកំហុសពិតប្រាកដ
  9. ការផ្លាស់ប្តូរ និងការលុប Table
  10. រូបមន្ត៖ រក្សាទុក schema.sql
  11. សាកល្បងដោយខ្លួនឯង

1. CREATE TABLE ដំបូងរបស់អ្នក

ក្នុង ជំពូកមុន យើងបានបង្កើត database ទទេមួយ។ ឥឡូវយើងផ្តល់រចនាសម្ព័ន្ធឲ្យវា។ Statement ដែលបង្កើត table គឺ CREATE TABLE ហើយវាជា DDL៖ វាកំណត់រចនាសម្ព័ន្ធ មិនមែនទិន្នន័យទេ។

តភ្ជាប់ទៅ shopdb៖

docker exec -it shopdb-pg psql -U postgres -d shopdb

នេះគឺជា table តូចមួយដើម្បីរៀនរូបរាង។ យើងនឹងបោះវាចោលក្នុងពេលឆាប់ៗនេះ៖

CREATE TABLE pets (
    name text,
    age  integer
);

Postgres ឆ្លើយថា CREATE TABLE។ លំនាំគឺ៖

CREATE TABLE table_name (
    column_name  data_type  [rules...],
    column_name  data_type  [rules...]
);

Column នីមួយៗមាន ឈ្មោះ, data type និងជាជម្រើសមាន constraint (ច្បាប់) ខ្លះ។ Column ត្រូវបានញែកដោយសញ្ញាក្បៀស ហើយគ្មានសញ្ញាក្បៀសបន្ទាប់ពីមួយចុងក្រោយទេ។ មើលវាដោយ \d៖

shopdb=# \d pets
                Table "public.pets"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 name   | text    |           |          |
 age    | integer |           |          |

ចំណាំ public.pets៖ public គឺជា schema លំនាំដើម ដែលជា namespace នៅក្នុង database។ នៅទីនេះ "schema" មានន័យថាថតនៃ table ដែលជាអត្ថន័យខុសពី "ការរចនានៃ database" ដែលយើងប្រើពីមុន។ អ្នកអាចមិនអើពើនឹងវាសម្រាប់ពេលនេះ។ លុប table ហាត់នោះចេញ៖

DROP TABLE pets;

ប្រើឈ្មោះដែលជាអក្សរតូច ដោយភ្ជាប់ពាក្យដោយសញ្ញា underscore៖ created_at, order_items។ រចនាប័ទ្មនេះ ហៅថា snake_case ជួយអ្នកកុំឲ្យត្រូវដាក់ quote ឈ្មោះនៅពេលក្រោយ។ ចៀសវាងដកឃ្លា និងអក្សរធំក្នុងឈ្មោះ។

2. ការជ្រើសរើស Data Type

Column នីមួយៗមាន type តែមួយ ហើយ type សម្រេចថាតម្លៃអ្វីខ្លះត្រូវបានអនុញ្ញាត និងរបៀបដែលវាដំណើរការ។ ទាំងនេះគឺជា type ដែលអ្នកនឹងប្រើញឹកញាប់បំផុតក្នុង PostgreSQL៖

Typeរក្សាទុកតម្លៃឧទាហរណ៍
integerចំនួនគត់រហូតដល់ប្រហែល 2.1 ពាន់លាន42, -7, 0
bigintចំនួនគត់ធំជាង (ប្រហែល 9 quintillion)9000000000
numeric(p, s)ទសភាគច្បាស់លាស់៖ p ខ្ទង់សរុប s ខ្ទង់ក្រោយចុច15.00, 5.50
real, double precisionទសភាគប្រហាក់ប្រហែល (floating point)3.14159
textអក្សរប្រវែងណាក៏បាន'Sokha Chan'
varchar(n)អក្សររហូតដល់ n តួអក្សរ'KH'
booleanពិត ឬមិនពិតtrue, false
dateកាលបរិច្ឆេទតាមប្រតិទិន'2026-01-05'
timestamptzកាលបរិច្ឆេទ និងម៉ោងជាមួយ time zone'2026-01-05 09:30:00+07'
uuidលេខសម្គាល់តែមួយគត់ 128 bit'3f2a9c1d-...'

ដំបូន្មានខ្លះអំពីការជ្រើសរើស៖

Text។ ក្នុង Postgres text និង varchar ដំណើរការដូចគ្នា។ ប្រើ text លុះត្រាតែអ្នកមានហេតុផលពិតប្រាកដក្នុងការកំណត់ប្រវែង ហើយអនុវត្តដែនកំណត់ដោយច្បាប់ CHECK (ផ្នែកទី 6)។

ចំនួនគត់។ integer គឺជាជម្រើសធម្មតា។ ប្រើ bigint នៅពេលអ្នករំពឹងថាមាន row ឬតម្លៃលើសពី 2 ពាន់លាន ឧទាហរណ៍ ID នៃ table ដែលកត់ត្រារាល់ការចុចលើគេហទំព័រធំមួយ។

កាលបរិច្ឆេទ និងម៉ោង។ ប្រើ date នៅពេលអ្នកត្រូវការតែថ្ងៃ (ថ្ងៃកំណើត)។ ប្រើ timestamptz ("timestamp with time zone") សម្រាប់ពេលវេលាជាក់លាក់ ដូចជានៅពេលការបញ្ជាទិញត្រូវបានធ្វើ។ ទោះឈ្មោះយ៉ាងនេះក៏ដោយ វាមិនរក្សាទុកអក្សរតំបន់ទេ។ វារក្សាទុកពេលវេលាជាក់លាក់ក្នុង UTC ហើយបំប្លែងទៅ time zone នៃ session របស់អ្នកនៅពេលបង្ហាញ។ នោះធ្វើឲ្យវាមានសុវត្ថិភាពនៅពេលអ្នកប្រើរបស់អ្នកនៅប្រទេសផ្សេងៗ។ ចូលចិត្តវាជាងប្រើ type timestamp ធម្មតា ដែលលុបព័ត៌មានតំបន់ចោលដោយស្ងាត់ៗ ហើយបង្កឲ្យមាន bug ដែលគួរឲ្យច្របូកច្របល់។

uuid។ uuid គឺជាលេខសម្គាល់វែងដែលមើលទៅដូចចៃដន្យ ដូចជា 3f2a9c1d-7b44-4e0c-9a21-5d6b8e0f1a77។ វាមានប្រយោជន៍នៅពេល record ត្រូវបានបង្កើតនៅកន្លែងច្រើន (server ឬឧបករណ៍ច្រើន) ហើយអ្នកមិនអាចប្រើ counter រួមបាន។ យើងប្រើ ID ជាចំនួនគត់ធម្មតាក្នុង shopdb ព្រោះវាសាមញ្ញជាង និងតូចជាងក្នុងការអាន។ UUID គឺជាជម្រើសនៃការរចនាដែលអ្នកអាចត្រឡប់មកពិនិត្យឡើងវិញនៅពេលក្រោយ។

Boolean។ Column boolean ផ្ទុក true, false ឬ NULL (មិនស្គាល់)។ ដាក់ឈ្មោះវាដូចសំណួរ៖ is_active, has_paid។

Postgres ពិនិត្យ type នៅពេលអ្នក insert។ ការដាក់អក្សរក្នុង column លេខបរាជ័យភ្លាមៗ ដូចដែលអ្នកនឹងឃើញក្នុងផ្នែកទី 8។

3. ហេតុអ្វីលុយមិនដែលប្រើ Float

នេះគឺជាការសម្រេចចិត្តអំពី type សំខាន់បំផុតក្នុងជំពូកនេះ។ ដំណើរការ query ពីរនេះ (:: បំប្លែងតម្លៃទៅជា type មួយ)៖

SELECT 0.1::double precision + 0.2::double precision AS float_sum;
SELECT 0.1::numeric + 0.2::numeric AS numeric_sum;
      float_sum
----------------------
 0.30000000000000004
(1 row)

 numeric_sum
-------------
         0.3
(1 row)

Type floating-point (real, double precision) រក្សាទុកលេខជា binary ហើយមិនអាចតំណាងតម្លៃដូចជា 0.1 បានច្បាស់លាស់ទេ។ កំហុសតូចនោះគ្មានគ្រោះថ្នាក់សម្រាប់ការវាស់វែងដូចជាសីតុណ្ហភាព ប៉ុន្តែនៅពេលអ្នកបូកតម្លៃរាប់លាន កំហុសនោះកកកុញ ហើយអ្នកបាត់បង់ ឬបង្កើតសេនបន្ថែម។ ប្រសិនបើអ្នកបានអាន គណិតវិទ្យាសម្រាប់អ្នកសរសេរកម្មវិធី នេះគឺជាឥទ្ធិពលដូចគ្នាដែលអ្នកបានឃើញក្នុង JavaScript។

numeric រក្សាទុកខ្ទង់ទសភាគបានច្បាស់លាស់។ អ្នកប្រាប់ថាចង់បានប៉ុន្មានខ្ទង់៖ numeric(10, 2) មានន័យថារហូតដល់ 10 ខ្ទង់សរុប ដែលមាន 2 ខ្ទង់ក្រោយចុចទសភាគ។ ដូច្នេះតម្លៃធំបំផុតគឺ 99999999.99។ វាជា type ត្រឹមត្រូវសម្រាប់តម្លៃ សមតុល្យ និងតម្លៃណាក៏ដោយដែលត្រូវតែបូកបានត្រឹមត្រូវ។

SELECT 15.00::numeric(10,2) AS price,
       (15.00 * 3)::numeric(10,2) AS total;
 price | total
-------+-------
 15.00 |  45.00
(1 row)

វិធីទូទៅទីពីរគឺរក្សាទុកលុយជាចំនួនគត់នៃឯកតាតូចបំផុត (សេន ឬរៀល) ហើយបំប្លែងសម្រាប់ការបង្ហាញ។ វាក៏ច្បាស់លាស់ដែរ។ យើងប្រើ numeric(10, 2) ក្នុង shopdb ព្រោះវាអានបានធម្មជាតិ។

4. Identity Column និង Primary Key

គ្រប់ table ត្រូវការ column ដែលកំណត់អត្តសញ្ញាណ row នីមួយៗ។ សម្រាប់អតិថិជន ឈ្មោះមិនល្អគ្រប់គ្រាន់ទេ ព្រោះមនុស្សពីរនាក់អាចមានឈ្មោះដូចគ្នា។ អ៊ីមែលអាចប្តូរ។ ដូច្នេះយើងបន្ថែមលេខដែលមានតែដើម្បីកំណត់អត្តសញ្ញាណ row ហៅថា surrogate key ហើយឲ្យ database បង្កើតវា៖

id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY

បីផ្នែក៖

  • integer៖ ចំនួនគត់។
  • GENERATED ALWAYS AS IDENTITY៖ Postgres បំពេញ column ដោយស្វ័យប្រវត្តិជា 1, 2, 3 ហើយបន្តបន្ទាប់ទៀត។ អ្នកមិនដែលជ្រើសរើសតម្លៃទេ។ នេះគឺជាវិធីទំនើបដែលជាស្តង់ដារ។ អ្នកអាចឃើញមេរៀនចាស់ៗប្រើ serial ដែលធ្វើការងារស្រដៀងគ្នា។
  • PRIMARY KEY៖ ច្បាប់ដែលថាតម្លៃត្រូវតែមានតែមួយគត់ និងមិនដែល NULL ហើយ column គឺជាអត្តសញ្ញាណផ្លូវការរបស់ row។

Primary key ផ្តល់ឲ្យអ្នកនូវដៃចាប់ដែលអាចទុកចិត្តបាន៖ "អតិថិជន 3" មានន័យថាមនុស្សម្នាក់យ៉ាងពិតប្រាកដ។ Table ផ្សេងទៀតនឹងប្រើវាដើម្បីចង្អុលទៅអតិថិជន (ជំពូកទី 6)។

ដោយសារ column គឺ GENERATED ALWAYS Postgres បដិសេធមិនឲ្យអ្នកផ្តល់តម្លៃផ្ទាល់ខ្លួនដោយចៃដន្យ៖

ERROR:  cannot insert a non-DEFAULT value into column "id"
DETAIL:  Column "id" is an identity column defined as GENERATED ALWAYS.
HINT:  Use OVERRIDING SYSTEM VALUE to override.

រឿងមួយទៀតដែលគួរដឹង៖ counter មិនដែលថយក្រោយទេ។ ប្រសិនបើ insert បរាជ័យ ឬប្រសិនបើអ្នកលុប row 3 row បន្ទាប់នៅតែទទួលលេខបន្ទាប់ មិនមែនលេខដែលត្រូវបានដោះលែងទេ។ ដូច្នេះ ID អាចមានចន្លោះ (1, 2, 4, 5)។ នេះជារឿងធម្មតា។ កុំប្រើ ID ជា "ចំនួន row" ហើយកុំព្យាយាមដាក់លេខឡើងវិញ។

5. NULL៖ តម្លៃដែលមានន័យថា "មិនស្គាល់"

NULL គឺជាសញ្ញាពិសេសដែលមានន័យថា "គ្មានតម្លៃនៅទីនេះ"។ ប្រហែលតម្លៃមិនស្គាល់ ប្រហែលវាមិនអនុវត្ត។ ឧទាហរណ៍ អតិថិជនម្នាក់ប្រហែលមិនបានប្រាប់យើងពីទីក្រុងរបស់ពួកគេ។ city របស់ពួកគេគឺ NULL។

បីរឿងដែលត្រូវរៀនឲ្យឆាប់៖

  1. NULL មិនមែនសូន្យ ហើយមិនមែនជា string ទទេទេ។ ផលិតផលដែលមាន stock = 0 គឺអស់ស្តុក។ ផលិតផលដែលមាន stock = NULL មានន័យថាយើងមិនដឹង។
  2. NULL មិនអាចប្រៀបធៀបដោយ = បានទេ។ ការពិនិត្យ city = NULL មិនដែលពិតទេ សូម្បីតែពេល city ជា NULL។ អ្នកត្រូវសរសេរ city IS NULL។ ជំពូកទី 5 ស្រាវជ្រាវរឿងនេះឲ្យស៊ីជម្រៅ។
  3. នព្វន្តណាក៏ដោយជាមួយ NULL ផ្តល់ NULL៖ 5 + NULL គឺ NULL។
SELECT 5 + NULL AS result, NULL = NULL AS same, NULL IS NULL AS is_null;
 result | same | is_null
--------+------+---------
        |      | t
(1 row)

Cell ទទេក្នុង psql គឺ NULL។ តាមលំនាំដើម column ទទួលយក NULL។ សម្រាប់អ្វីដែលតម្រូវ យើងបន្ថែម NOT NULL ដែលជា constraint ដំបូងដែលយើងជួបបន្ទាប់។

6. Constraint៖ ច្បាប់ដែល Database អនុវត្ត

Constraint គឺជាច្បាប់ដែលភ្ជាប់នឹង column ឬ table។ Database ពិនិត្យវាគ្រប់ពេល insert និង update ហើយបដិសេធ row ណាដែលបំពានវា។ គិតអំពី constraint ដូចអ្នកយាមមាត់ទ្វារ៖ ទិន្នន័យអាក្រក់មិនដែលចូលបានទេ មិនថាកម្មវិធី ឬមនុស្សណាព្យាយាមបន្ថែមវាក៏ដោយ។

Constraintអត្ថន័យ
NOT NULLតម្រូវឲ្យមានតម្លៃ
UNIQUEគ្មាន row ពីរណាអាចមានតម្លៃដូចគ្នាក្នុង column នេះទេ
CHECK (condition)លក្ខខណ្ឌត្រូវតែពិតសម្រាប់គ្រប់ row
DEFAULT valueប្រើតម្លៃនេះនៅពេលមិនបានផ្តល់
PRIMARY KEYNOT NULL បូក UNIQUE ហើយវាដាក់ឈ្មោះ row
REFERENCESforeign key ទៅ table មួយទៀត (ជំពូកទី 6)

នេះគឺពួកវាក្នុង table សាកល្បងមួយ ដើម្បីអ្នកអាចឃើញប្រភេទនីមួយៗ៖

CREATE TABLE demo (
    id         integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    name       text NOT NULL,
    email      text NOT NULL UNIQUE,
    age        integer CHECK (age >= 0),
    is_active  boolean NOT NULL DEFAULT true,
    created_at timestamptz NOT NULL DEFAULT now()
);

អានវាម្តងមួយបន្ទាត់៖

  • name text NOT NULL៖ តម្រូវឲ្យមានឈ្មោះ។
  • email text NOT NULL UNIQUE៖ តម្រូវឲ្យមាន ហើយគ្មានការស្ទួន។
  • age integer CHECK (age >= 0)៖ ប្រសិនបើបានផ្តល់ វាមិនត្រូវអវិជ្ជមានទេ។ ចំណាំថា CHECK ឆ្លងកាត់នៅពេលតម្លៃជា NULL ព្រោះ "មិនស្គាល់" មិនមែនជាការបំពានទេ។ ផ្សំ CHECK ជាមួយ NOT NULL នៅពេលតម្រូវឲ្យមានតម្លៃ។
  • is_active ... DEFAULT true៖ ប្រសិនបើអ្នកមិនផ្តល់តម្លៃ វាគឺ true។
  • created_at ... DEFAULT now()៖ បំពេញដោយម៉ោងបច្ចុប្បន្ន។ នេះគឺជារបៀបដែលអ្នកទទួលបាន timestamp ស្វ័យប្រវត្តិ។

តោះ insert row មួយដោយផ្តល់តែ column ដែលតម្រូវ៖

INSERT INTO demo (name, email) VALUES ('Test User', 'test@example.com');
SELECT * FROM demo;
 id |   name    |      email       | age | is_active |          created_at
----+-----------+------------------+-----+-----------+-------------------------------
  1 | Test User | test@example.com |     | t         | 2026-10-03 10:12:09.511203+00
(1 row)

យើងបានផ្តល់ពីរតម្លៃ។ Postgres បានបំពេញ id, is_active និង created_at ពី identity និង default ហើយទុក age ជា NULL។ យើងគ្របដណ្តប់ INSERT ឲ្យពេញលេញក្នុងជំពូកបន្ទាប់។ លុប table សាកល្បងនោះនៅពេលអ្នករួចរាល់៖

DROP TABLE demo;

7. Table របស់ shopdb៖ customers និង products

ឥឡូវជារបស់ពិត។ យើងចាប់ផ្តើមជាមួយ table ពីរដែលឈរដោយខ្លួនឯង ដែលមិនចង្អុលទៅ table ផ្សេងទេ៖ customers និង products។ បើក shopdb/schema.sql ហើយសរសេរ៖

-- shopdb/schema.sql
-- រចនាសម្ព័ន្ធនៃ database shopdb។

CREATE TABLE customers (
    id         integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    name       text NOT NULL,
    email      text NOT NULL UNIQUE,
    city       text,
    created_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE products (
    id       integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    name     text NOT NULL,
    price    numeric(10, 2) NOT NULL CHECK (price >= 0),
    stock    integer NOT NULL DEFAULT 0 CHECK (stock >= 0),
    category text
);

តោះពន្យល់ហេតុផលនៃជម្រើសរចនា៖

Columnការសម្រេចចិត្តហេតុអ្វី
customers.emailNOT NULL UNIQUEយើងកំណត់អត្តសញ្ញាណមនុស្សដោយអ៊ីមែលពេលពួកគេចូលគណនី។ គណនីពីរមិនត្រូវចែករំលែកអ៊ីមែលតែមួយទេ។
customers.cityអនុញ្ញាត NULLមិនមែនអតិថិជនគ្រប់គ្នាប្រាប់ទីក្រុងរបស់ពួកគេទេ។
customers.created_atDEFAULT now()អតិថិជនគ្រប់គ្នាទទួលបានម៉ោងចុះឈ្មោះដោយស្វ័យប្រវត្តិ។
products.pricenumeric(10, 2), CHECK (price >= 0)លុយច្បាស់លាស់ និងគ្មានតម្លៃអវិជ្ជមាន។
products.stockDEFAULT 0, CHECK (stock >= 0)ផលិតផលថ្មីចាប់ផ្តើមដោយស្តុកសូន្យ ហើយយើងមិនអាចលក់អ្វីដែលយើងគ្មានបានទេ។

មានរឿងមួយក្នុង products ដែលអ្នកគួរមើលដោយប្រុងប្រយ័ត្ន៖ column category គឺជា text ធម្មតា។ វានឹងផ្ទុកតម្លៃដូចជា 'Electronics'។ នេះគឺជាការរចនា បណ្តោះអាសន្ន។ ការធ្វើឡើងវិញនូវឈ្មោះប្រភេទជាអក្សរ បណ្តាលឲ្យមានការវាយខុស (Electronic, electronics) ហើយធ្វើឲ្យការប្តូរឈ្មោះប្រភេទពិបាក។ ក្នុងជំពូកទី 6 យើងនឹងជួសជុលវាដោយ table categories និង foreign key។ ការចាប់ផ្តើមដោយកំណែសាមញ្ញអនុញ្ញាតឲ្យអ្នកដឹងពីបញ្ហាមុនពេលជួសជុល។

ដំណើរការឯកសារនេះលើ database ពី terminal ធម្មតារបស់អ្នក៖

docker exec -i shopdb-pg psql -U postgres -d shopdb < schema.sql
CREATE TABLE
CREATE TABLE

ឥឡូវពិនិត្យអ្វីដែលអ្នកបានសាងសង់៖

shopdb=# \dt
          List of relations
 Schema |   Name    | Type  |  Owner
--------+-----------+-------+----------
 public | customers | table | postgres
 public | products  | table | postgres
(2 rows)

និងលម្អិតនៃ table មួយ៖

shopdb=# \d customers
                                  Table "public.customers"
   Column   |           Type           | Collation | Nullable |           Default
------------+--------------------------+-----------+----------+------------------------------
 id         | integer                  |           | not null | generated always as identity
 name       | text                     |           | not null |
 email      | text                     |           | not null |
 city       | text                     |           |          |
 created_at | timestamp with time zone |           | not null | now()
Indexes:
    "customers_pkey" PRIMARY KEY, btree (id)
    "customers_email_key" UNIQUE CONSTRAINT, btree (email)

Postgres បានបង្កើត index ពីរឲ្យយើង ដើម្បីអនុវត្ត primary key និងច្បាប់ unique។ ឈ្មោះរបស់ពួកវា customers_pkey និង customers_email_key នឹងលេចឡើងក្នុងសារកំហុស។ យើងសិក្សា index ក្នុងជំពូកទី 11។

8. មើលច្បាប់ដំណើរការ៖ សារកំហុសពិតប្រាកដ

ច្បាប់ដែលអ្នកមិនដែលឃើញវាដំណើរការ គឺជាច្បាប់ដែលអ្នកមិនទុកចិត្ត។ តោះបំពានច្បាប់នីមួយៗដោយចេតនា។ Statement ទាំងនេះសុទ្ធតែបរាជ័យ ដូច្នេះវាមិនផ្លាស់ប្តូរអ្វីទេ។ (អក្សរនៃកំហុសអាចខុសគ្នាបន្តិចរវាងកំណែ ប៉ុន្តែទាំងនេះគឺជាសារធម្មតា។)

បាត់តម្លៃដែលតម្រូវ (NOT NULL)៖

INSERT INTO customers (email) VALUES ('nobody@example.com');
ERROR:  null value in column "name" of relation "customers" violates not-null constraint
DETAIL:  Failing row contains (1, null, nobody@example.com, null, 2026-10-03 10:20:41.002931+00).

Type ខុស៖

INSERT INTO products (name, price, stock)
VALUES ('Mystery Box', 9.99, 'lots');
ERROR:  invalid input syntax for type integer: "lots"
LINE 2: VALUES ('Mystery Box', 9.99, 'lots');
                                     ^

បំពានច្បាប់ (CHECK)៖

INSERT INTO products (name, price) VALUES ('Free Money', -5);
ERROR:  new row for relation "products" violates check constraint "products_price_check"
DETAIL:  Failing row contains (1, Free Money, -5.00, 0, null).

ស្ទួន (UNIQUE)។ ដំបូងបន្ថែមអតិថិជនពិតមួយនាក់ បន្ទាប់មកព្យាយាមបន្ថែមអ៊ីមែលដដែលម្តងទៀត៖

INSERT INTO customers (name, email)
VALUES ('Sokha Chan', 'sokha@example.com');

INSERT INTO customers (name, email)
VALUES ('Another Sokha', 'sokha@example.com');
INSERT 0 1
ERROR:  duplicate key value violates unique constraint "customers_email_key"
DETAIL:  Key (email)=(sokha@example.com) already exists.

Insert ទីមួយជោគជ័យ (INSERT 0 1 មានន័យថា insert មួយ row) ហើយទីពីរបរាជ័យ។

ជ្រើសរើស ID ដែលបង្កើតដោយស្វ័យប្រវត្តិដោយខ្លួនឯង៖

INSERT INTO customers (id, name, email)
VALUES (99, 'Manual Id', 'manual@example.com');
ERROR:  cannot insert a non-DEFAULT value into column "id"
DETAIL:  Column "id" is an identity column defined as GENERATED ALWAYS.
HINT:  Use OVERRIDING SYSTEM VALUE to override.

មើលថាសារទាំងនោះមានប្រយោជន៍យ៉ាងណា។ ពួកវាដាក់ឈ្មោះ column, table និង constraint ហើយ DETAIL បង្ហាញ row ដែលមានបញ្ហា។ នៅពេលអ្នកឃើញកំហុសក្នុងការងាររបស់អ្នកផ្ទាល់ សូមអានទាំងបីបន្ទាត់មុនពេលស្វែងរកអ្វីផ្សេង។

ចូរយកចិត្តទុកដាក់ចំពោះអ្វីដែលច្បាប់ទាំងនេះផ្តល់ឲ្យអ្នក។ កូដកម្មវិធីរបស់អ្នកអាចមាន bug ឬមិត្តរួមការងារអាចដំណើរការ script ដែលមិនប្រុងប្រយ័ត្ន ហើយទិន្នន័យនៅតែត្រឹមត្រូវ ព្រោះ database បដិសេធ។ ច្បាប់ក្នុង database គឺជាខ្សែការពារចុងក្រោយ ហើយមិនដូចការពិនិត្យក្នុងកូដកម្មវិធីទេ ពួកវាមិនអាចរំលងបានឡើយ។

ឥឡូវសម្អាត row មួយដែលយើងបានបន្ថែម ដើម្បីឲ្យជំពូកទី 4 ចាប់ផ្តើមស្អាត។ យើងនឹងរៀន DELETE ឲ្យបានត្រឹមត្រូវក្នុងជំពូកបន្ទាប់ ប៉ុន្តែនេះគឺជាបន្ទាត់នោះ៖

DELETE FROM customers WHERE email = 'sokha@example.com';
DELETE 1

ចងចាំចំណាំអំពី "ចន្លោះ"៖ row នោះបានប្រើ ID 1 ហើយ insert ដែលបរាជ័យបានប្រើបន្ថែមខ្លះទៀត។ Identity counter មិនកំណត់ឡើងវិញទេ។ នៅពេលអ្នកដំណើរការ seed.sql ក្នុងជំពូកទី 4 ID នឹងចាប់ផ្តើមខ្ពស់ជាង 1។ ដើម្បីទទួលបាន ID ដូចដែលបានបង្ហាញក្នុងស៊េរីនេះបេះបិទ សូមសាងសង់ឡើងវិញដោយប្រើរូបមន្តពីជំពូកទី 2 (drop database, create database, ដំណើរការ schema.sql) មុនពេលផ្ទុកទិន្នន័យ seed។ យើងនឹងរំលឹកអ្នក។

9. ការផ្លាស់ប្តូរ និងការលុប Table

ការរចនាផ្លាស់ប្តូរ។ ALTER TABLE កែ table ដែលមានរួចហើយ។ នេះគឺជាទម្រង់ទូទៅ ដោយប្រើ table បោះចោល៖

CREATE TABLE notes (id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY);

-- បន្ថែម column
ALTER TABLE notes ADD COLUMN body text;

-- បន្ថែមច្បាប់ទៅ column ដែលមានស្រាប់
ALTER TABLE notes ALTER COLUMN body SET NOT NULL;

-- ដកច្បាប់នោះចេញ
ALTER TABLE notes ALTER COLUMN body DROP NOT NULL;

-- កំណត់ default
ALTER TABLE notes ALTER COLUMN body SET DEFAULT '';

-- ប្តូរឈ្មោះ column
ALTER TABLE notes RENAME COLUMN body TO content;

-- ប្តូរ type របស់ column
ALTER TABLE notes ALTER COLUMN content TYPE varchar(500);

-- ដក column ចេញ
ALTER TABLE notes DROP COLUMN content;

ម្នាក់ៗឆ្លើយថា ALTER TABLE។ ចុងក្រោយ លុប table នោះ៖

DROP TABLE notes;

DROP TABLE លុប table និងទិន្នន័យទាំងអស់របស់វាភ្លាមៗ។ គ្មាន undo ហើយគ្មានការបញ្ជាក់ទេ។ ទម្លាប់សុវត្ថិភាពពីរ៖

  • DROP TABLE IF EXISTS notes; មិនធ្វើអ្វីជំនួសឲ្យការបរាជ័យ នៅពេល table បាត់ ដែលងាយស្រួលក្នុង script។
  • ពិនិត្យឈ្មោះ table ពីរដង។ ក្នុងជំពូកទី 12 អ្នកនឹងឃើញថា transaction អនុញ្ញាតឲ្យអ្នកសាកល្បងការផ្លាស់ប្តូរដែលមានហានិភ័យ ហើយ roll back វាវិញបាន។

ការប្រុងប្រយ័ត្នពីរអំពី ALTER TABLE លើប្រព័ន្ធពិត៖

  1. ការប្តូរ type របស់ column ឬការបន្ថែមច្បាប់ដូចជា NOT NULL ធ្វើឲ្យ Postgres ពិនិត្យគ្រប់ row ដែលមានស្រាប់។ លើ table ដ៏ធំ នោះអាចយឺត ហើយអាចចាក់សោ table។ លើ table ដែលមានទិន្នន័យអាក្រក់ វាបរាជ័យ។
  2. ដោយសារតែបែបនោះ វាល្អជាងច្រើនក្នុងការរចនាឲ្យបានត្រឹមត្រូវសមរម្យតាំងពីដំបូង។ ជំពូកទី 10 ត្រឡប់មករឿងនេះ។ យើងក៏នឹងឃើញក្នុងជំពូកទី 10 ថាក្រុមការងាររក្សាទុកការផ្លាស់ប្តូរជាឯកសារដែលមានលេខរៀង ហៅថា migration។

10. រូបមន្ត៖ រក្សាទុក schema.sql

រូបមន្តនេះធានាថា project របស់អ្នកត្រូវនឹងជំពូកបន្ទាប់។

  1. បើក shopdb/schema.sql ហើយប្រាកដថាវាមានតែ CREATE TABLE statement ពីរពីផ្នែកទី 7 ប៉ុណ្ណោះ។ ដក table សាកល្បង (pets, demo, notes) ចេញពីឯកសារ។ ពួកវាសម្រាប់ហាត់។

  2. សាងសង់ database ឡើងវិញពីសូន្យ ដើម្បីកុំឲ្យមានអ្វីសល់ គ្មាន row សល់ ហើយ ID counter ថ្មី៖

    docker exec -i shopdb-pg psql -U postgres -c "DROP DATABASE IF EXISTS shopdb;"
    docker exec -i shopdb-pg psql -U postgres -c "CREATE DATABASE shopdb;"
    docker exec -i shopdb-pg psql -U postgres -d shopdb < schema.sql
    
  3. ពិនិត្យលទ្ធផលដោយ \dt និង \d products៖

    shopdb=# \d products
                                       Table "public.products"
      Column  |     Type      | Collation | Nullable |           Default
    ----------+---------------+-----------+----------+------------------------------
     id       | integer       |           | not null | generated always as identity
     name     | text          |           | not null |
     price    | numeric(10,2) |           | not null |
     stock    | integer       |           | not null | 0
     category | text          |           |          |
    Indexes:
        "products_pkey" PRIMARY KEY, btree (id)
    Check constraints:
        "products_price_check" CHECK (price >= 0)
        "products_stock_check" CHECK (stock >= 0)
    
  4. ប្រសិនបើអ្នកប្រើ Git សូម commit ដោយសារដូចជា "Add customers and products tables"។

ប្រសិនបើ DROP DATABASE ត្អូញត្អែរថា database កំពុងត្រូវបានចូលប្រើដោយអ្នកប្រើផ្សេង នោះអ្នកនៅតែមាន session psql បើកលើ shopdb។ វាយ \q ក្នុងវា ហើយសាកល្បងម្តងទៀត។

11. សាកល្បងដោយខ្លួនឯង

  1. គំនិតសម្រាប់ products ធំជាងមុន។ បន្ថែម column is_active boolean NOT NULL DEFAULT true ទៅ products ដោយ ALTER TABLE។ ពិនិត្យវាដោយ \d products។ បន្ទាប់មកដកវាចេញម្តងទៀត ដើម្បីឲ្យ schema.sql របស់អ្នកនៅតែត្រូវនឹងរូបមន្ត។
  2. រចនា table reviews។ លើក្រដាស ឬក្នុងឯកសារសាកល្បង សរសេរ CREATE TABLE reviews ដែលមាន៖ identity primary key, rating ដែលតម្រូវឲ្យមាន និងត្រូវតែនៅចន្លោះ 1 និង 5 (ប្រើ CHECK (rating BETWEEN 1 AND 5)), comment ជាជម្រើស និង created_at ដែលមាន default ជាម៉ោងឥឡូវ។ ដំណើរការវា បន្ទាប់មកព្យាយាម insert rating 6 ហើយអានកំហុស។ លុប table នោះក្រោយមក។
  3. Float ទល់នឹង numeric។ ដំណើរការ SELECT 0.1::double precision * 3; និង SELECT 0.1::numeric * 3;។ ប្រៀបធៀបលទ្ធផល។ បន្ទាប់មកដំណើរការ SELECT (0.1::double precision + 0.2::double precision) = 0.3::double precision; និងដូចគ្នាជាមួយ numeric។ ការប្រៀបធៀបមួយណាជា true?
  4. បំពានច្បាប់នីមួយៗ។ ផ្ទុក table ទាំងពីររបស់អ្នកឡើងវិញ ហើយបង្កឲ្យមានកំហុស NOT NULL, UNIQUE, CHECK និង type ខុស លើ customers និង products។ សម្រាប់កំហុសនីមួយៗ សរសេរចុះថាបន្ទាត់ណានៃសារដាក់ឈ្មោះ constraint។
  5. តើអ្នកនឹងជ្រើសរើសអ្វី? សម្រាប់តម្លៃនីមួយៗ ជ្រើសរើស data type ហើយបញ្ជាក់ហេតុផល៖ ថ្ងៃកំណើតរបស់មនុស្សម្នាក់, លេខទូរស័ព្ទដូចជា +855 12 345 678, ពេលវេលាជាក់លាក់ដែលការទូទាត់បានកើតឡើង, ថាតើទំនិញត្រូវបានវេចខ្ចប់ជាកាដូឬអត់ និងទម្ងន់ផលិតផលគិតជាគីឡូក្រាម។ (ព័ត៌មានជំនួយ៖ គិតថាតើអ្នកនឹងធ្វើគណិតលើលេខទូរស័ព្ទឬទេ។)
  6. ល្បែងផ្គុំ NULL។ ទាយលទ្ធផលនៃ SELECT 10 + NULL, 'abc' || NULL, NULL IS NULL, NULL = NULL; ហើយបន្ទាប់មកដំណើរការវា។ តើអ្វីដែលគួរឲ្យភ្ញាក់ផ្អើល?

ទៅទីណាបន្ត។ ឥឡូវអ្នកអាចពិពណ៌នា table ឲ្យច្បាស់លាស់ ហើយឲ្យ database អនុវត្តច្បាប់របស់អ្នក។ shopdb មានពីរ table ប៉ុន្តែពួកវាទទេ។ ក្នុងជំពូកបន្ទាប់ ការ Insert, Update និង Delete ទិន្នន័យ អ្នកបំពេញពួកវាដោយ INSERT ផ្លាស់ប្តូរពួកវាដោយ UPDATE លុប row ដោយ DELETE ហើយរៀនថាហេតុអ្វីបានជា WHERE clause សមនឹងទទួលបានការគោរពពេញលេញរបស់អ្នក។

ទិញកាហ្វេជូនខ្ញុំ

ចូលចិត្តមេរៀននេះមែនទេ? សូមពិចារណាទិញកាហ្វេជូនខ្ញុំមួយកែវ ដើម្បីគាំទ្រការបង្កើតមាតិកាឥតគិតថ្លៃបន្ថែមទៀត!