دليل postgresql العملي أنواع بيانات خاصة في قواعد بيانات Postgres


مصطفى عطا العايش

تتميز Postgres بإضافة عدة أنواع بيانات مميزة، نتحدث عنها في هذا الفصل، وهي المصفوفات Arrays والنوع Hstore والنوع JSONB وهي تساهم بشكل أساسي بالسماح بتخزين هيكل بيانات أكبر من مجرد قيمة واحدة في العمود في الجدول، كما سنستخدم الأنواع التعدادية ENUM لتحديد قيم مخصصة في أعمدة جداولنا.

المصفوفات (Arrays)

تتيح Postgres تخزين بيانات على شكل مصفوفات متغيرة الطول ضمن عمود واحد، حيث يمكن أن يكون نوع المصفوفات من الأنواع الأساسية، أو نوعًا جديدًا يحدده المستخدم أو من الأنواع التعدادية (enumerated).

لتحديد عمود ما لتخزين مصفوفة، نقوم بوضع قوسين [] بعد اسم النوع كما يلي:

hsoubguide=# CREATE TABLE hsoub_team
hsoubguide-# (
hsoubguide(# team_name text,
hsoubguide(# team_members text[]
hsoubguide(# );

CREATE TABLE

يُنشئ الأمر السابق جدولًا اسمه hsoub_team له عمودان، أحدهما نص نخزّن فيه اسم الفريق، وعمود آخر team_members يخزن مصفوفة أحادية البعد لتخزين أسماء أعضاء الفريق.

إدخال قيم المصفوفات

hsoubguide=# INSERT INTO hsoub_team
hsoubguide-# VALUES
hsoubguide-# ('postgres_team',
hsoubguide(# '{"mostafa","jamil","abood"}'
hsoubguide(# );

INSERT 0 1

لاحظ أن السلاسل النصية بداخل المصفوفة تكون محصورة بعلامات تنصيص مزدوجة، وذلك لأن قيمة المصفوفة كاملة هي التي تُكتب بين علامات تنصيص مفردة.

سيُظهر استعلام الجدول ما يلي:

hsoubguide=# SELECT * FROM hsoub_team ;

   team_name   |     team_members      
---------------+-----------------------
 postgres_team | {mostafa,jamil,abood}
(1 row)

كما يمكن بناء المصفوفات بطريقة ثانية، عن طريق استخدام باني المصفوفات (Constructor) كما يلي:

hsoubguide=# INSERT INTO hsoub_team
hsoubguide-# VALUES
hsoubguide-# ('C++ team',
hsoubguide(# ARRAY['mostafa','yougharta']
hsoubguide(# );

INSERT 0 1

عند استخدام باني المصفوفات يتم حصر السلاسل النصية بعلامات تنصيص مفردة، ولا شك أن هذه الطريقة أكثر وضوحًا وأسهل مقروئيّة.

يُعطي استعلام الجدول الآن ما يلي:

hsoubguide=# SELECT * FROM hsoub_team ;

   team_name   |     team_members      
---------------+-----------------------
 postgres_team | {mostafa,jamil,abood}
 C++ team      | {mostafa,yougharta}
(2 rows)

ولكن لماذا نستخدم المصفوفات؟ ألم يكن بإمكاننا تخزين محتويات المصفوفة كلها كنص عادي؟ إن ميزة المصفوفات تأتي من القدرة على اختيار عنصر محدد من المصفوفة والوصول إليه، كما سنرى في الفقرة التالية.

الوصول إلى عناصر من المصفوفة

يمكن اختيار عناصر المصفوفة عن طريق رقم العنصر للوصول إلى عنصر مفرد، أو باستخدام المجال (من العنصر: إلى العنصر) كما يلي:

hsoubguide=# SELECT * FROM hsoub_team WHERE team_members[2] = 'jamil';

   team_name   |     team_members      
---------------+-----------------------
 postgres_team | {mostafa,jamil,abood}
(1 row)
hsoubguide=# SELECT * FROM hsoub_team WHERE team_members[2:3] = ARRAY['jamil','abood'];

   team_name   |     team_members      
---------------+-----------------------
 postgres_team | {mostafa,jamil,abood}
(1 row)
hsoubguide=# SELECT team_members[2:3] FROM hsoub_team ;

 team_members  
---------------
 {jamil,abood}
 {yougharta}
(2 rows)

ملاحظة: يجب الانتباه إلى أن ترقيم عناصر المصفوفة يبدأ من 1 وليس من 0 كما في بعض لغات البرمجة.

تعديل قيم عناصر المصفوفات

يمكن تعديل قيمة عنصر واحد في المصفوفة، كما يمكن تعديل المصفوفة كاملةً، أو مجالًا محدّدًا منها.

فلتعديل عنصر واحد في المصفوفة، نستخدم الوصول إلى العنصر المُراد تعديله كما يلي:

hsoubguide=# UPDATE hsoub_team SET team_members[3]='new_member';

UPDATE 2
hsoubguide=# SELECT * FROM hsoub_team ;

   team_name   |          team_members          
---------------+--------------------------------
 postgres_team | {mostafa,jamil,new_member}
 C++ team      | {mostafa,yougharta,new_member}
(2 rows)

يمكن الوصول إلى عنصر خارج حدود المصفوفة كما في المثال السابق، ولكن يمكن كذلك تجاوز هذا الحد لتعديل العنصر رقم 5 مثلًا في مصفوفة من 3 عناصر فقط، عندها يتم ملء العناصر الفارغة بقيمة NULL كما في المثال التالي:

hsoubguide=# UPDATE hsoub_team SET team_members[5]='new_member2';

UPDATE 2
hsoubguide=# SELECT * FROM hsoub_team ;

   team_name   |                  team_members                  
---------------+------------------------------------------------
 postgres_team | {mostafa,jamil,new_member,NULL,new_member2}
 C++ team      | {mostafa,yougharta,new_member,NULL,new_member2}
(2 rows)

أما لتعديل المصفوفة كاملةً فلا نستخدم الوصول إلى عنصر مفرد، بل نقوم بتغيير قيمة العمود كاملةً:

hsoubguide=# UPDATE hsoub_team SET team_members = ARRAY ['a','b'] WHERE team_name ='C++ team';

UPDATE 1
hsoubguide=# SELECT * FROM hsoub_team ;

   team_name   |                team_members                
---------------+--------------------------------------------
 postgres_team | {mostafa,jamil,new_member,NULL,new_member}
 C++ team      | {a,b}
(2 rows)

كما يمكن تغيير قيمة مجالٍ من المصفوفة كما يلي:

hsoubguide=# UPDATE hsoub_team SET team_members[2:3] = ARRAY['x','y'] WHERE team_name LIKE 'postgres%';

UPDATE 1
hsoubguide=# SELECT * FROM hsoub_team ;

   team_name   |         team_members          
---------------+-------------------------------
 C++ team      | {a,b}
 postgres_team | {mostafa,x,y,NULL,new_member}
(2 rows)

ملاحظة: يجب أن يكون حجم المجال المُستبدل مساويًا أو أصغر من طول المصفوفة الجديدة، فلو حاولنا استبدال مجالٍ بمجالٍ أصغر منها سيظهر الخطأ التالي:

hsoubguide=# UPDATE hsoub_team SET team_members[2:4] = ARRAY['one_member']  WHERE team_name LIKE 'postgres%';

ERROR:  source array too small

أما إن كان حجم المصفوفة الجديدة أكبر من الأصلية، فيتم أخذ عدد من العناصر مساوٍ للمجال الأصلي، كما يلي:

hsoubguide=# UPDATE hsoub_team SET team_members[2:4] = ARRAY['one_member','a','b','c','d','e']  WHERE team_name LIKE 'postgres%';

UPDATE 1
hsoubguide=# SELECT * FROM hsoub_team ;

   team_name   |            team_members             
---------------+-------------------------------------
 C++ team      | {a,b}
 postgres_team | {mostafa,one_member,a,b,new_member}
(2 rows)

البحث ضمن المصفوفات

للبحث عن عنصر معين ضمن المصفوفة نستخدم الكلمة المفتاحية ANY كما يوضح المثال التالي:

hsoubguide=# SELECT * FROM hsoub_team WHERE 'mostafa' = ANY(team_members);

   team_name   |            team_members             
---------------+-------------------------------------
 postgres_team | {mostafa,one_member,a,b,new_member}
(1 row)

ويمكن البحث للتحقق من كون كل قيم المصفوفة تطابق قيمة معينة باستخدام الكلمة ALL.

hsoubguide=# INSERT INTO hsoub_team
hsoubguide-# VALUES
hsoubguide-# ('team1',
hsoubguide(# ARRAY['programmer1','programmer1','programmer1']
hsoubguide(# );

INSERT 0 1
hsoubguide=# SELECT * FROM hsoub_team WHERE 'programmer1' = ALL(team_members);
 team_name |             team_members              
-----------+---------------------------------------
 team1     | {programmer1,programmer1,programmer1}
(1 row)

كما يمكن استخدام ALL مع تحديد المجال ضمن المصفوفة كما يلي:

hsoubguide=# INSERT INTO hsoub_team
VALUES
('team7',
ARRAY['programmer1','programmer1','another_programmer']
);

INSERT 0 1
hsoubguide=# SELECT * FROM hsoub_team WHERE 'programmer1' = ALL(team_members[1:2]);

 team_name |                 team_members                 
-----------+----------------------------------------------
 team1     | {programmer1,programmer1,programmer1}
 team7     | {programmer1,programmer1,another_programmer}
(2 rows)

أنواع البيانات التعدادية (Enumerated Data Types)

توفر Postgres نوع بيانات تعدادية enums تُستخدم لحصر قيم عمود ما في مجموعة قيم محددة مسبقًا من القيم.

سنقوم في المثال التالي بحصر قيم العمود contact_method بمجموعة القيم Email و SMS و Phone، وذلك عن طريق تعريف التعداد كما يلي:

hsoubguide=# CREATE TYPE e_contact_method AS ENUM (
hsoubguide(# 'Email',
hsoubguide(# 'Sms',
hsoubguide(# 'Phone');

CREATE TYPE

ومن ثم نرفق نوع التعداد الجديد بالعمود الذي نريد حصر قيمه كما يلي:

hsoubguide=# CREATE TABLE contact_method_info (
hsoubguide(# contact_name text,
hsoubguide(# contact_method e_contact_method,
hsoubguide(# value text
hsoubguide(# );

CREATE TABLE

استخدام الأنواع التعدادية

سنحاول في هذا المثال إدخال قيم في العمود الذي يستخدم النوع التعدادي، لنرى ما يحصل عند الخروج عن القيم المحددة مسبقًا:

hsoubguide=# INSERT INTO contact_method_info
hsoubguide-# VALUES ('Jamil', 'Email', 'jamil@mail.com');
INSERT 0 1
hsoubguide=# SELECT * FROM contact_method_info ;
 contact_name | contact_method |     value      
--------------+----------------+----------------
 Jamil        | Email          | jamil@mail.com
(1 row)

لا يمكن إدراج قيمة للعمود contact_method غير موجودة سلفًا ضمن التعداد e_contact_method وسيظهر خطأ كما في المثال التالي:

hsoubguide=# INSERT INTO contact_method_info VALUES ('Jamil', 'Fax', '4563456');

ERROR:  invalid input value for enum e_contact_method: "Fax"
LINE 1: INSERT INTO contact_method_info VALUES ('Jamil', 'Fax', '456...

عرض وتعديل قيم التعداد

يمكننا عرض قائمة القيم في التعداد بالاستعانة بالجداول pg_type و pg_enum التي تُخزّن إعدادات الأنواع والتعدادات، وذلك كما يلي:

hsoubguide=# SELECT pg_type.typname, pg_enum.enumlabel
hsoubguide-# FROM pg_type,pg_enum
hsoubguide-# WHERE pg_type.oid = pg_enum.enumtypid;

     typname      | enumlabel 
------------------+-----------
 e_contact_method | Email
 e_contact_method | Sms
 e_contact_method | Phone
(3 rows)

كما يمكن إضافة قيم للتعدادات الموجودة مسبقًا كما يلي:

hsoubguide=# ALTER TYPE e_contact_method
hsoubguide-# ADD VALUE 'Facebook' AFTER 'Phone';

ALTER TYPE
hsoubguide=# SELECT pg_type.typname, pg_enum.enumlabel
hsoubguide-# FROM pg_type,pg_enum
hsoubguide-# WHERE pg_type.oid = pg_enum.enumtypid;

     typname      | enumlabel 
------------------+-----------
 e_contact_method | Email
 e_contact_method | Sms
 e_contact_method | Phone
 e_contact_method | Facebook
(4 rows)

يتم حفظ ترتيب القيم داخل التعدادات بنفس الترتيب الذي تم إدخال القيم به، ولكن يمكن إدخال قيم جديدة وتحديد مكانها قبل قيمة معينة أو بعدها، كما في المثال التالي:

hsoubguide=# ALTER TYPE e_contact_method
hsoubguide-# ADD VALUE 'Twitter' BEFORE 'Sms';

ALTER TYPE
hsoubguide=# SELECT pg_type.typname,pg_enum.enumlabel,pg_enum.enumsortorder
hsoubguide-# FROM pg_type, pg_enum
hsoubguide-# WHERE pg_type.oid = pg_enum.enumtypid
hsoubguide-# ORDER BY pg_enum.enumsortorder;

     typname      | enumlabel | enumsortorder 
------------------+-----------+---------------
 e_contact_method | Email     |             1
 e_contact_method | Twitter   |           1.5
 e_contact_method | Sms       |             2
 e_contact_method | Phone     |             3
 e_contact_method | Facebook  |             4
(5 rows)

لا تسمح Postgres بإزالة قيم من التعدادات ولا بتغيير ترتيبها، وللقيام بذلك علينا حذف التعداد عن طريق التعليمة DROP TYPE، ولكن انتبه إلى أنه لا يمكنك حذف التعداد إذا كان هناك أعمدة تستخدم هذا النوع، لكن يمكنك حذف التعداد مع جميع الأعمدة المرتبطة به باستخدام الكلمة المفتاحية CASCADE، ولتوضيح ذلك لدينا المثال التالي:

hsoubguide=# DROP TYPE e_contact_method CASCADE;

NOTICE:  drop cascades to column contact_method of table contact_method_info
DROP TYPE
hsoubguide=# SELECT pg_type.typname, pg_enum.enumlabel
FROM pg_type,pg_enum                     
WHERE pg_type.oid = pg_enum.enumtypid;

 typname | enumlabel 
---------+-----------
(0 rows)
hsoubguide=# SELECT * FROM contact_method_info ;

 contact_name |     value      
--------------+----------------
 Jamil        | jamil@mail.com
(1 row)

نوع البيانات HStore

HStore هو أسلوب تخزين (مفتاح، قيمة) ضمن Postgres يُستخدم مثل القاموس، لكنه مخصص لعمود في سطر ما.

تفعيل HStore

لتفعيل HStore في قاعدة البيانات قم بتنفيذ الأمر التالي:

hsoubguide=# CREATE EXTENSION hstore;

CREATE EXTENSION

إنشاء عمود HStore

لإنشاء حقل في جدول ذو نوع بيانات HStore استخدم HStore كنوع للعمود ببساطة كما يلي:

hsoubguide=# CREATE TABLE students (
hsoubguide-# id serial PRIMARY KEY,
hsoubguide-# name varchar,
hsoubguide-# attributes hstore
hsoubguide-# );

CREATE TABLE

إدخال بيانات من نوع HStore

لإدخال البيانات عليك كتابتها ضمن علامات تنصيص مفردة. الفرق في HStore هو بنية إضافية لتوضيح كيفية إنشاء القاموس:

hsoubguide=# INSERT INTO students (name,attributes) VALUES(
hsoubguide(# 'mostafa',
hsoubguide(# 'nickname => mayesh,
hsoubguide'# grade => 12,
hsoubguide'# school => "Hsoub Academy",
hsoubguide'# weight => 82'
hsoubguide(# );

INSERT 0 1

إن استخدام النوع HStore كان من أوائل المحاولات في الخروج عن هيكلة قواعد البيانات، ومن ثم فهو من أوائل أنواع NoSQL التي ظهرت، فليس هناك محددات للعناصر التي يمكننا تخزينها في HStore.

الوصول إلى بيانات من نوع HStore

يمكننا استخدام العملية <- للوصول إلى عناصر في داخل العنصر من النوع Hstore، وذلك بتحديد اسم المفتاح كما يلي:

hsoubguide=# SELECT name,attributes->'school'
hsoubguide=# FROM students
hsoubguide=# WHERE attributes->'nickname' = 'mayesh';

  name   |   ?column?    
---------+---------------
 mostafa | Hsoub Academy
(1 row)

لتغيير اسم العمود بدلًا من ?column? استخدم AS كما يلي:

hsoubguide=# SELECT name,attributes->'school' AS school
FROM students
WHERE attributes->'nickname' = 'mayesh';

  name   |    school     
---------+---------------
 mostafa | Hsoub Academy
(1 row)

بما أنه لا توجد قيود على المفاتيح المخزنة بداخل النوع Hstore فإنه من الممكن أن يوجد مفتاحُ ما في سطر ما، ولا يوجد في سطر آخر، وعندها يُتعامل معه على أنه موجود بقيمة خالية، كما في المثال التالي:

hsoubguide=# INSERT INTO students (name,attributes) VALUES(
hsoubguide(# 'Jamil',
hsoubguide(# 'grade => 13
hsoubguide'# ,
hsoubguide'# weight => 72');

INSERT 0 1
hsoubguide=# SELECT * FROM students;

 id |  name   |                                   attributes                                   
----+---------+--------------------------------------------------------------------------------
  1 | mostafa | "grade"=>"12", "school"=>"Hsoub Academy", "weight"=>"82", "nickname"=>"mayesh"
  2 | Jamil   | "grade"=>"13", "weight"=>"72"
(2 rows)
hsoubguide=# SELECT name,attributes->'school' AS school FROM students;

  name   |    school     
---------+---------------
 mostafa | Hsoub Academy
 Jamil   | 
(2 rows)

يتيح استخدام Hstore مرونة عالية في قاعدة البيانات، ولكنها أصبحت تقنية قديمة مقارنةً بتقنية النوع JSONB التي سنتحدث عنها في الفقرة التالية.

بيانات بصيغة JSON

ظهر استخدام JSON في postgres بدءًا من الإصدار 9.2، لكن الإصدار الحقيقي ظهر باسم JSONB في postgres 9.4.

JSNOB هي الصيغة الثنائية لتعابير JSON للتخزين الدائم، فهي أكثر كفاءة في التخزين والفهرسة.

إنشاء أعمدة JSONB

لإنشاء أعمدة من النوع JSONB حدد النوع JSONB ضمن تعليمة CREATE TABLE كما يلي:

hsoubguide=# CREATE TABLE school (
hsoubguide(#     id serial PRIMARY KEY,
hsoubguide(#     name varchar,
hsoubguide(#     attributes JSONB
hsoubguide(# );

CREATE TABLE

إدخال البيانات من النوع JSONB

يُفترض أن يكون إدخال عمود يحتوي على صيغة JSON سهلًا ومباشرًا، ونوضّحه بالمثال التالي:

hsoubguide=# INSERT INTO school (name,attributes) VALUES (
hsoubguide(#     'Hsoub', '{
hsoubguide'#     "manager" : "Agha",
hsoubguide'#     "classes" : 7,
hsoubguide'#     "teachers": 12}'
hsoubguide(# );

INSERT 0 1
hsoubguide=# SELECT * FROM school;

 id | name  |                    attributes                     
----+-------+---------------------------------------------------
  1 | Hsoub | {"classes": 7, "manager": "Agha", "teachers": 12}
(1 row)

الوصول إلى قيم المفاتيح في JSONB

يمكننا استخدام العملية <- للوصول إلى القيم عن طريق أسماء مفاتيحها، كما يلي:

hsoubguide=# SELECT name,attributes->'manager' AS manager FROM school;

 name  | manager 
-------+---------
 Hsoub | "Agha"
(1 row)

ويمكننا استخدامها داخل شروط التصفية كذلك:

hsoubguide=# SELECT * FROM school WHERE attributes->'classes' = '7';

 id | name  |                    attributes                     
----+-------+---------------------------------------------------
  1 | Hsoub | {"classes": 7, "manager": "Agha", "teachers": 12}
(1 row)

هناك العديد من الأمور المتقدمة التي يمكن القيام بها في JSONB، ولكن يجب أن نعرف أنها مخصصة لتخزين العناصر والوصول إليها، ولكنها لم تصمم لتعديل العناصر بعد تخزينها.

التعامل مع التاريخ والوقت

يمكن تخزين التاريخ والوقت في Postgres باستخدام عدة أنواع، كما تتيح العديد من الطرق للتعامل المرن مع التواريخ والأوقات، النوع الأساسي لتخزين التاريخ هو DATE ولتخزين الوقت TIME، ويمكننا حفظ التاريخ مع الوقت باستخدام النوع TIMESTAMP.

سننشئ جدولًا جديدًا لتعلم التعامل مع هذه الأنواع الجديدة:

hsoubguide=# CREATE TABLE date_example(
hsoubguide(#     mydate DATE NOT NULL DEFAULT CURRENT_DATE,
hsoubguide(#     mytime TIME NOT NULL DEFAULT CURRENT_TIME,
hsoubguide(#     mytimestamp TIMESTAMP NOT NULL DEFAULT NOW()
hsoubguide(# );

CREATE TABLE
hsoubguide=# SELECT * FROM date_example;
 mydate | mytime | mytimestamp 
--------+--------+-------------
(0 rows)

أنشأنا في الجدول السابق ثلاث أعمدة، لكل منها قمنا بمنع تخزين القيمة الخالية عن طريق NOT NULL، واستخدمنا القيم الافتراضية التالية:

  • التاريخ الحالي، باستخدام CURRENT_DATE
  • الوقت الحالي، باستخدام CURRENT_TIME
  • التاريخ والوقت الحالي، باستخدام التابع NOW()‎

سندخل الآن سطرًا مميزًا، حيث لن نحدد فيه أي قيمة، بل سنستخدم التوجيه DEFAULT VALUES كي يتم إدخال جميع القيم الافتراضية كما يلي:

hsoubguide=# INSERT INTO date_example DEFAULT VALUES;

INSERT 0 1
hsoubguide=# SELECT * FROM date_example;
   mydate   |     mytime      |        mytimestamp         
------------+-----------------+----------------------------
 2020-08-22 | 01:24:08.241482 | 2020-08-22 01:24:08.241482
(1 row)

كما يظهر استعلام الجدول السابق، يتم إظهار التاريخ بالتنسيق yyyy-mm-dd.

استخدام لتابع NOW()‎

يمكننا استخدام التابع NOW()‎ للاستعلام عن التاريخ والوقت كما يلي:

hsoubguide=# SELECT NOW();

              now              
-------------------------------
 2020-08-22 01:26:48.875054+03
(1 row)
hsoubguide=# SELECT NOW()::DATE;

    now     
------------
 2020-08-22
(1 row)
hsoubguide=# SELECT NOW()::TIME;

       now       
-----------------
 01:26:55.072126
(1 row)

استخدام التابع TO_CHAR()‎

كما يمكننا تحديد النسق الذي نرغب بعرض التاريخ فيه من خلال التابع TO_CHAR()‎ كما يلي:

hsoubguide=# SELECT TO_CHAR(NOW()::DATE,'dd ++ mm ++ yyyy');

     to_char      
------------------
 22 ++ 08 ++ 2020
(1 row)
hsoubguide=# SELECT TO_CHAR(NOW()::DATE,'Month dd/mm/yyyy');

       to_char        
----------------------
 August    22/08/2020
(1 row)

يمكننا استخدام التابع AGE()‎ لحساب فارق التاريخ وإظهاره بنفس تنسيق التاريخ كما يلي:

hsoubguide=# SELECT AGE(CURRENT_DATE,'25-07-1993');

       age        
------------------
 27 years 28 days
(1 row)

كما يمكننا استخدام عملية الطرح للتواريخ - للحصول على الفرق بالأيام كما يلي:

hsoubguide=# SELECT CURRENT_DATE-'25-07-1993' AS days;

 days 
------
 9890
(1 row)

للحصول على فارق الوقت كذلك علينا تحديد نوع البيانات التي نقوم بطرحها على أنها بيانات وقت:

hsoubguide=# SELECT time '10:57:18' - time '02:17:17' AS result;

  result  
----------
 08:40:01
(1 row)

ملاحظة: انتبه إلى إضافة النوع time قبل الوقت المطروح، وإلا سيظهر الخطأ التالي:

hsoubguide=# SELECT '10:57:18' - '02:17:17' AS result;

ERROR:  operator is not unique: unknown - unknown
LINE 1: SELECT '10:57:18' - '02:17:17' AS result;
                          ^
HINT:  Could not choose a best candidate operator. You might need to add explicit type casts.

استخدام التابع EXTRACT()‎

لعل أحد أكثر التوابع فائدةً هو التابع EXTRACT()‎ كما يمكننا استخلاص السنة والشهر واليوم من التاريخ كما يلي:

hsoubguide=# SELECT EXTRACT(YEAR FROM TIMESTAMP '2016-12-31 13:30:15');

 date_part 
-----------
      2016
(1 row)
hsoubguide=# SELECT EXTRACT(MONTH FROM TIMESTAMP '2016-12-31 13:30:15');

 date_part 
-----------
        12
(1 row)
hsoubguide=# SELECT EXTRACT(DAY FROM TIMESTAMP '2016-12-31 13:30:15');

 date_part 
-----------
        31
(1 row)
hsoubguide=# SELECT EXTRACT(HOUR FROM TIMESTAMP '2016-12-31 13:30:15');

 date_part 
-----------
        13
(1 row)
hsoubguide=# SELECT EXTRACT(MINUTES FROM TIMESTAMP '2016-12-31 13:30:15');

 date_part 
-----------
        30
(1 row)
hsoubguide=# SELECT EXTRACT(SECONDS FROM TIMESTAMP '2016-12-31 13:30:15');

 date_part 
-----------
        15
(1 row)

تخزين المدد الزمنية INTERVAL

تتيح كذلك Postgres استخدام نوع مميز لتخزين المدة الزمنية INTERVAL، وسننشئ جدولًا صغيرًا لتعلّم كيفية استخدامه:

hsoubguide=# CREATE TABLE date_example2(
hsoubguide(#     myinterval INTERVAL
hsoubguide(# );

CREATE TABLE

يمكننا تخزين مدة زمنية كسنوات كما يلي:

hsoubguide=# INSERT INTO date_example2(myinterval) VALUES ('2 years');

INSERT 0 1

أو كسنوات وأشهر:

hsoubguide=# INSERT INTO date_example2(myinterval) VALUES ('2 years 3 months');

INSERT 0 1

كما يمكن تخزين مدة زمنية في الماضي باستخدام الكلمة ago:

hsoubguide=# INSERT INTO date_example2(myinterval) VALUES ('2 years 3 months ago');

INSERT 0 1

يمكننا كذلك تخزين التاريخ مع الوقت كمدة زمنية:

hsoubguide=# INSERT INTO date_example2(myinterval) VALUES ('2 years 3 months 5 days 33 minutes');

INSERT 0 1
hsoubguide=# INSERT INTO date_example2(myinterval) VALUES ('2 years 3 months 5 days 33 minutes ago');

INSERT 0 1

وعند الاستعلام عن كل هذه المدد الزمنية سنجد أنها مخزنة كما أدخلناها، باستثناء المدد التي أضفنا لها كلمة ago قد أضيف لها إشارة السالب:

hsoubguide=# SELECT * FROM date_example2;

             myinterval             
------------------------------------
 2 years
 2 years 3 mons
 -2 years -3 mons
 2 years 3 mons 5 days 00:33:00
 -2 years -3 mons -5 days -00:33:00
(5 rows)

تكمن فائدة استخدام المدد الزمنية عند الحاجة إلى الرجوع بتاريخ معين إلى مدة زمنية محددة كما يلي:

hsoubguide=# SELECT NOW() - INTERVAL '1 year 2 months 3 days 4 hours 5 minutes 6 seconds' AS "1 year,2 months,3 days,04h:05m:06s ago";

 1 year,2 months,3 days,04h:05m:06s ago 
----------------------------------------
 2019-06-18 22:36:02.945923+03
(1 row)

خلاصة

تتميز Postgres عن الأصل SQL بوجود أنواع البيانات الخاصة Arrays و JSONB و HSTORE التي تسمح بتخزين مجموعات مرتبة من البيانات ضمن عمود واحد، وقد تعرفنا إلى كل منها في هذا الفصل، مما يعطينا القدرة على تخزين البيانات بطريقة أكثر احترافية باستخدام Postgres، وقد تعرفنا كذلك في هذا الفصل إلى النوع ENUM الذي يحدد القيمة المخزنة ضمن العمود بقيم معرفة مسبقًا.

اقرأ أيضًا





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن