PostgreSQL: أنواع البيانات في PostgreSQL: الدليل الكامل
آخر تحديث: 2026-08-26
اختيار نوع البيانات المناسب هو أساس تصميم قاعدة البيانات — فهو يحدد كفاءة التخزين وأداء الاستعلام ودقة البيانات.
1. ما ستتعلمه
- أنواع الأعداد الصحيحة (SMALLINT / INTEGER / BIGINT)
- التسلسلات التلقائية (SERIAL / BIGSERIAL / IDENTITY)
- الأعداد العشرية والدقيقة (REAL / DOUBLE / DECIMAL / NUMERIC)
- أنواع السلاسل (CHAR / VARCHAR / TEXT)
- أنواع التاريخ/الوقت (DATE / TIME / TIMESTAMP / TIMESTAMPTZ / INTERVAL)
- الأنواع الخاصة (UUID / INET / BOOLEAN / ENUM / JSONB)
2. قصة حقيقية لمطور
(1) المشكلة: محتار بشأن أنواع البيانات
واجه Bob سلسلة من الاختيارات الصعبة عند تصميم جداول قاعدة بيانات للتجارة الإلكترونية:
- هل يجب أن يكون معرف المستخدم INTEGER أم BIGINT؟ ماذا لو تجاوز عدد المستخدمين 2.1 مليار؟
- هل يجب استخدام REAL أم DECIMAL لأسعار المنتجات؟ يقال إن الأعداد العشرية العائمة فيها مشاكل دقة.
- هل يجب استخدام VARCHAR(5000) أم TEXT لوصف المنتج؟
- هل يجب استخدام TIMESTAMP أم TIMESTAMPTZ للطوابع الزمنية؟ وماذا عن المستخدمين العالميين؟
- هل يجب تخزين عناوين IP كـ VARCHAR، أم يوجد نوع مخصص؟
(2) الحل: اخيار دقيق للنوع
يقدم PostgreSQL مجموعة غنية من أنواع البيانات المخصصة؛ الاختيار الدقيق يوفر التخزين ويضمن الدقة:
-- اختيارات الأنواع الصحيحة لقاعدة بيانات التجارة الإلكترونية
CREATE TABLE smart_products (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- معرف مقاوم للمستقبل
name VARCHAR(200) NOT NULL, -- سلسلة محدودة
description TEXT, -- نص غير محدود
price DECIMAL(10, 2) NOT NULL, -- مال دقيق
weight_kg REAL, -- تقريبي مقبول
is_available BOOLEAN DEFAULT true, -- علم نعم/لا
created_at TIMESTAMPTZ DEFAULT NOW(), -- واع بالمنطقة الزمنية
source_ip INET, -- نوع عنوان IP
attributes JSONB DEFAULT '{}'::jsonb -- هيكل مرن
);
(3) النتيجة
- DECIMAL يضمن صفر فقدان دقة للمال (89.99 يبقى دائمًا 89.99، وليس 89.9899999 أبدًا)
- TIMESTAMPTZ يتعامل مع المناطق الزمنية تلقائيًا، لذا يرى المستخدمون العالميون وقتهم المحلي
- INET يدعم استعلاما�� نطاق IP، أكثر كفاءة بعشر مرات من تخزين IP كـ VARCHAR
- JSONB يخزن السمات الديناميكية بمرونة، متجنبًا تغييرات ALTER TABLE المتكررة
3. تدفق قرار نوع البيانات
graph TB
START[ما نوع البيانات؟] --> NUM{رقمي؟}
NUM -->|نعم| INT{تحتاج دقة<br/>دقيقة؟}
INT -->|نعم، مال/نسب| DEC[DECIMAL / NUMERIC]
INT -->|لا، قياسات| FLOAT[REAL / DOUBLE PRECISION]
NUM -->|أعداد صحيحة| RANGE{نطاق القيمة؟}
RANGE -->|-32768 إلى 32767| SMALL[SMALLINT]
RANGE -->|-2.1 مليار إلى 2.1 مليار| INT2[INTEGER]
RANGE -->|أكبر| BIG[BIGINT]
NUM -->|معرف تلقائي| AUTO[SERIAL / IDENTITY]
START --> STR{نص؟}
STR -->|طول ثابت| CHAR[CHAR]
STR -->|متغير محدود| VAR[VARCHAR n]
STR -->|غير محدود| TEXT2[TEXT]
START --> TIME{تاريخ/وقت؟}
TIME -->|تاريخ فقط| DATE2[DATE]
TIME -->|وقت فقط| TIME2[TIME]
TIME -->|طابع زمني| TZ{منطقة زمنية؟}
TZ -->|نعم، تطبيق عالمي| TSTZ[TIMESTAMPTZ]
TZ -->|لا، محلي فقط| TS[TIMESTAMP]
TIME -->|مدة| IV[INTERVAL]
START --> SPEC{خاص؟}
SPEC -->|نعم/لا| BOOL[BOOLEAN]
SPEC -->|UUID| UUID2[UUID]
SPEC -->|عنوان IP| IP[INET / CIDR]
SPEC -->|قائمة تعداد| ENUM2[ENUM]
SPEC -->|مستند JSON| JSON[JSONB]
4. أنواع الأعداد الصحيحة
| النوع | التخزين | النطاق | الاستخدام النموذجي |
|---|---|---|---|
SMALLINT |
2 بايت | -32,768 ~ 32,767 | العمر، التقييم، رمز الحالة |
INTEGER (INT) |
4 بايت | -2,147,483,648 ~ 2,147,483,647 | أعداد صحيحة عامة، معرفات المفاتيح الأساسية |
BIGINT |
8 بايت | ±9,223,372,036,854,775,807 | معرفات الجداول الكبيرة، المبالغ (بالسنت) |
(1) ▶ مثال
-- SMALLINT للقيم ذات النطاق الصغير
CREATE TABLE ratings (
user_id INTEGER REFERENCES users(id),
product_id INTEGER REFERENCES products(id),
score SMALLINT CHECK (score BETWEEN 1 AND 5), -- 1-5 ضمن نطاق SMALLINT تمامًا
PRIMARY KEY (user_id, product_id)
);
-- INTEGER لمعظم المع��فات
CREATE TABLE categories (
id SERIAL PRIMARY KEY, -- SERIAL = INTEGER + تسلسل تلقائي
name VARCHAR(100) NOT NULL
);
-- BIGINT للجداول عالية النمو
CREATE TABLE audit_log (
id BIGSERIAL PRIMARY KEY, -- BIGSERIAL = BIGINT + تلقائي
action VARCHAR(50),
details JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
Output:
CREATE TABLE
5. التسلسلات التلقائية
(1) SERIAL مقابل IDENTITY
| الطريقة | الصيغة | قياسي | إدراج يدوي؟ | موصى به |
|---|---|---|---|---|
SERIAL |
id SERIAL PRIMARY KEY |
خاص بـ PG | نعم | توافق قديم |
BIGSERIAL |
id BIGSERIAL PRIMARY KEY |
خاص بـ PG | نعم | توافق قديم |
IDENTITY |
id INT GENERATED ALWAYS AS IDENTITY |
معيار SQL | يحتاج OVERRIDING | ✅ مشاريع جديدة |
(2) ▶ مثال
-- عمود هوية بمعيار SQL (PostgreSQL 10+)
CREATE TABLE orders_v2 (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
total DECIMAL(12, 2) DEFAULT 0
);
-- إدراج بدون تحديد id (توليد تلقائي)
INSERT INTO orders_v2 (user_id, total) VALUES (1, 99.99);
-- محاولة إدراج id يدويًا (ستفشل مع GENERATED ALWAYS)
-- INSERT INTO orders_v2 (id, user_id, total) VALUES (100, 1, 50.00);
-- استخدم GENERATED BY DEFAULT إذا كنت بحاجة لتجاوز يدوي أحيانًا
CREATE TABLE logs (
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
message TEXT
);
Output:
INSERT 0 1
6. الأعداد العشرية والدق��قة
(1) REAL / DOUBLE مقابل DECIMAL
| النوع | التخزين | الدقة | الأفضل لـ |
|---|---|---|---|
REAL |
4 بايت | 6 أرقام معنوية | الحوسبة العلمية، المستشعرات |
DOUBLE PRECISION |
8 بايت | 15 رقمًا معنويًا | الحوسبة العلمية، الإحصاء |
DECIMAL(p,s) |
متغير | دقيق | المال، النسب (موصى به) |
NUMERIC(p,s) |
متغير | دقيق | نفس DECIMAL |
(3) ▶ مثال
-- فخ دقة العائمة: 0.1 + 0.2 != 0.3
SELECT 0.1::REAL + 0.2::REAL = 0.3::REAL AS float_equal;
-- الناتج: f (خطأ!)
-- DECIMAL لا فقدان دقة
SELECT 0.1::DECIMAL + 0.2::DECIMAL = 0.3::DECIMAL AS decimal_equal;
-- الناتج: t (صحيح!)
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(4) ▶ مثال
-- DECIMAL(الدقة, المقياس)
-- الدقة = إجمالي الأرقام، المقياس = الأرقام بعد الفاصلة العشرية
-- DECIMAL(10,2) = حتى 99,999,999.99
CREATE TABLE products_precise (
id SERIAL PRIMARY KEY,
name VARCHAR(200),
price DECIMAL(10, 2) NOT NULL, -- الحد الأقصى 99,999,999.99
tax_rate DECIMAL(5, 4) DEFAULT 0.0875, -- 0.0875 = 8.75%
discount NUMERIC(5, 2) DEFAULT 0.00 -- الحد الأقصى 999.99%
);
-- حسابات المال دقيقة
SELECT name,
price,
price * tax_rate AS tax_amount,
price + (price * tax_rate) AS price_with_tax
FROM products_precise;
Output:
count
-------
5
(1 row)
| اختيار الدقة | معاملات DECIMAL | القيمة القصوى | الأفضل لـ |
|---|---|---|---|
| تجارة إلكترونية صغيرة | DECIMAL(8,2) |
999,999.99 | حجم يومي < 1M |
| تجارة إلكترونية كبيرة | DECIMAL(12,2) |
99,999,999,999.99 | تجارة إلكترونية عالمية |
| عملات رقمية | DECIMAL(20,8) |
كبير جدًا | BTC دقة 8 منازل عشرية |
7. أنواع السلاسل
| النوع | التخزين | الحد الأقصى للطول | الأفضل لـ |
|---|---|---|---|
CHAR(n) |
طول ثابت، مبطن بمسافات | n | التجزئات، رموز ISO |
VARCHAR(n) |
طول متغير | n | نص مع حد طول |
TEXT |
طول متغير | غير محدود | نص بدون حد طول |
(5) ▶ مثال
-- CHAR: طول ثابت (مبطن بمسافات)
SELECT LENGTH('abc'::CHAR(5));
-- الناتج: 5 (مبطن إلى 5 أحرف)
-- VARCHAR: طول متغير مع حد
SELECT LENGTH('abc'::VARCHAR(5));
-- الناتج: 3 (مخزن كما هو، الحد الأقصى 5)
-- TEXT: طول متغير، بلا حد
SELECT LENGTH('abc'::TEXT);
-- الناتج: 3 (بلا حد)
-- اختبار الأداء: VARCHAR و TEXT متطابقان في PG
-- (على عكس MySQL حيث VARCHAR أسرع من TEXT)
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
8. أنواع التاريخ والوقت
| النوع | التخزين | النطاق | الأفضل لـ |
|---|---|---|---|
DATE |
4 بايت | 4713 ق.م ~ 5874897 م | تاريخ فقط (عيد ميلاد، عطلة) |
TIME |
8 بايت | 00:00:00 ~ 24:00:00 | وقت فقط (ساعات العمل) |
TIMESTAMP |
8 بايت | 4713 ق.م ~ 294276 م | تاريخ+وقت (بدون منطقة زمنية) |
TIMESTAMPTZ |
8 بايت | نفس أعلاه | تاريخ+وقت+منطقة زمنية (✅ موصى به) |
INTERVAL |
16 بايت | ±178000000 سنة | فترة زمنية |
(6) ▶ مثال
-- تعيين المنطقة الزمنية إلى UTC
SET timezone = 'UTC';
-- إدراج نفس اللحظة بأنواع مختلفة
INSERT INTO test_times (ts_no_tz, ts_with_tz) VALUES
('2026-07-13 10:00:00', '2026-07-13 10:00:00+00');
-- التغيير إلى منطقة طوكيو الزمنية
SET timezone = 'Asia/Tokyo';
-- TIMESTAMP (بدون منطقة زمنية) يظهر نفس القيمة الحرفية
SELECT ts_no_tz FROM test_times;
-- الناتج: 2026-07-13 10:00:00 (بدون تغيير، يمكن أن يكون أي منطقة زمنية!)
-- TIMESTAMPTZ يحول إلى المنطقة الزمنية المحلية
SELECT ts_with_tz FROM test_times;
-- الناتج: 2026-07-13 19:00:00+09 (10:00 UTC = 19:00 طوكيو)
Output:
INSERT 0 1
(7) ▶ مثال
-- التاريخ والوقت الحالي
SELECT NOW(); -- 2026-07-13 10:30:00.123456+00
SELECT CURRENT_DATE; -- 2026-07-13
SELECT CURRENT_TIMESTAMP; -- نفس NOW()
-- حساب التاريخ مع INTERVAL
SELECT NOW() + INTERVAL '7 days'; -- بعد أسبوع من الآن
SELECT NOW() - INTERVAL '3 months'; -- قبل 3 أشهر
-- حساب العمر
SELECT AGE(TIMESTAMP '1990-05-15'); -- 36 سنة 1 شهر 28 يوم
-- استخراج الأجزاء
SELECT EXTRACT(YEAR FROM NOW()); -- 2026
SELECT EXTRACT(MONTH FROM NOW()); -- 7
SELECT EXTRACT(DOW FROM NOW()); -- 1 (الاثنين، 0=الأحد)
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
9. الأنواع الخاصة
(1) UUID
-- تمكين إضافة uuid-ossp
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- توليد إصدارات UUID مختلفة
SELECT uuid_generate_v4(); -- UUID عشوائي (الأكثر شيوعًا)
SELECT uuid_generate_v1(); -- UUID معتمد على الوقت
| إصدار UUID | التوليد | الأفضل لـ |
|---|---|---|
| v1 | طابع زمني + عنوان MAC | مرتب زمنيًا |
| v4 | عشوائي | معظم السيناريوهات (موصى به) |
| v7 | طابع زمني + عشوائي (معيار جديد) | مرتب زمنيًا + عشوائي |
(2) INET / CIDR (عناوين الشبكة)
(8) ▶ مثال
-- INET: IP مفرد أو نطاق IP
CREATE TABLE access_log (
id BIGSERIAL PRIMARY KEY,
source_ip INET NOT NULL,
access_time TIMESTAMPTZ DEFAULT NOW()
);
INSERT INTO access_log (source_ip) VALUES
('192.168.1.100'),
('10.0.0.5'),
('2001:db8::1');
-- استعلام: العثور على جميع IP في شبكة فرعية (مستحيل مع VARCHAR!)
SELECT source_ip FROM access_log
WHERE source_ip << '192.168.1.0/24'::INET;
-- << تعني "محتوى داخل"
Output:
INSERT 0 1
| العامل | المعنى | مثال |
|---|---|---|
<< |
محتوى في | 192.168.1.5' << '192.168.1.0/24' = true |
>> |
يحتوي | '192.168.1.0/24' >> '192.168.1.5' = true |
= |
يساوي | '192.168.1.5'::INET = '192.168.1.5'::INET |
(3) BOOLEAN
(9) ▶ مثال
-- Boolean يقبل تمثيلات متعددة
SELECT true, 't', 'true', 'yes', 'on', '1'; -- الكل = TRUE
SELECT false, 'f', 'false', 'no', 'off', '0'; -- الكل = FALSE
-- Boolean في عبارة WHERE
SELECT name FROM products WHERE is_available IS TRUE;
SELECT name FROM products WHERE is_available IS NOT FALSE;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(4) ENUM
(10) ▶ مثال
-- إنشاء نوع تعداد (مرة واحدة، قابل لإعادة الاستخدام عبر الجداول)
CREATE TYPE order_status AS ENUM (
'pending', 'paid', 'shipped', 'delivered', 'cancelled'
);
-- استخدام في تعريف الجدول
CREATE TABLE orders_enum (
id SERIAL PRIMARY KEY,
status order_status DEFAULT 'pending'
);
-- قيم التعداد يتم التحقق منها تلقائيًا
INSERT INTO orders_enum (status) VALUES ('pending'); -- موافق
-- INSERT INTO orders_enum (status) VALUES ('unknown'); -- خطأ!
Output:
INSERT 0 1
ALTER TYPE ... ADD VALUE (مسموح داخل معاملة منذ PG 9.1). إذا كانت القيم تتغير كثيرًا، VARCHAR + قيد CHECK أكثر مرونة.
10. جدول مقارنة أنواع البيانات
| الحاجة | ❌ غير موصى به | ✅ موصى به | السبب |
|---|---|---|---|
| المال | REAL / FLOAT | DECIMAL(p,s) | الأعداد العشرية العائمة تفقد الدقة |
| الطابع الزمني | TIMESTAMP | TIMESTAMPTZ | التطبيقات العالمية تحتاج مناطق زمنية |
| عنوان IP | VARCHAR | INET | INET يدعم استعلامات النطاق |
| نعم/لا | INTEGER (0/1) | BOOLEAN | دلالة أوضح |
| نص طويل | VARCHAR(9999) | TEXT | TEXT بلا حد ونفس الأداء |
| تعداد الحالة | VARCHAR + CHECK | ENUM أو VARCHAR+CHECK | ENUM إذا كان مستقرًا، CHECK إذا كان متغيرًا |
| سمات مرنة | أعمدة NULL كثيرة | JSONB | عمود واحد للحقول الديناميكية |
| معرف تلقائي | SERIAL | IDENTITY | صيغة معيار SQL |
11. مثال كامل: جدول يستخدم أنواعًا متعددة
-- ============================================
-- مثال كامل: جدول بجميع الأنواع الرئيسية
-- نظام جمع بيانات المستشعرات
-- ============================================
-- إنشاء نوع تعداد
CREATE TYPE sensor_status AS ENUM ('active', 'inactive', 'maintenance');
-- إنشاء جدول بأنواع بيانات متنوعة
CREATE TABLE sensors (
-- الهوية
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
uuid UUID DEFAULT uuid_generate_v4() UNIQUE,
-- النص
name VARCHAR(100) NOT NULL,
description TEXT,
location_code CHAR(10),
-- الرقمي
latitude DECIMAL(9, 6) CHECK (latitude BETWEEN -90 AND 90),
longitude DECIMAL(9, 6) CHECK (longitude BETWEEN -180 AND 180),
altitude REAL,
-- الشبكة
ip_address INET,
subnet CIDR,
-- الحالة
status sensor_status DEFAULT 'active',
is_online BOOLEAN DEFAULT false,
-- الوقت
installed_at DATE,
last_reading_at TIMESTAMPTZ,
reading_interval INTERVAL DEFAULT INTERVAL '5 minutes',
-- بيانات مرنة
metadata JSONB DEFAULT '{}'::jsonb,
-- التدقيق
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- إدراج بيانات نموذجية
INSERT INTO sensors (name, latitude, longitude, ip_address, status, installed_at, metadata)
VALUES (
'Temperature Sensor A1',
35.6762, 139.6503,
'192.168.1.50',
'active',
'2025-01-15',
'{"model": "TX-200", "unit": "celsius", "range_min": -40, "range_max": 85}'::jsonb
);
-- استعلام بعوامل خاصة بالنوع
SELECT name, ip_address, metadata->>'model' AS model
FROM sensors
WHERE ip_address << '192.168.1.0/24'::INET
AND status = 'active';
❓ أسئلة شائعة
📖 ملخص
- اختيار الأعداد الصحيحة: SMALLINT (نطاق صغير) / INTEGER (افتراضي) / BIGINT (معرفات الجداول الكبيرة)؛ فضل IDENTITY للتلقائي
- المال يجب استخدام DECIMAL؛ الأنواع العشرية العائمة تفقد الدقة
- السلاسل: VARCHAR (محدود الطول) / TEXT (غير محدود)، بأداء متطابق
- الوقت: TIMESTAMPTZ (واعي بالمنطقة الزمنية) موصى به؛ INTERVAL للحسابات الزمنية
- الأنواع الخاصة: UUID (معرفات موزعة) / INET (عناوين IP) / BOOLEAN (نعم/لا) / ENUM (تعدادات ثابتة) / JSONB (سمات مرنة)
- المبدأ الأساسي: اختر النوع الأكثر دقة؛ عندما تكون في شك، أخطئ في الجانب الأكبر (BIGINT يكلف 4 بايت إضافية لكل صف، لكن إعادة كتابة جدول كبير عبر ALTER TABLE تكلف أكثر بكثير)
📝 تمارين
-
أساسي (★): أنشئ جدول
countriesمع: id (SERIAL مفتاح أساسي)، name (VARCHAR(100))، iso_code (CHAR(2))، population (BIGINT)، gdp DECIMAL(15,2)، is_developed (BOOLEAN). أدرج 3 صفوف اختبار. -
متوسط (★★): أنشئ جدول
server_logsمع: id (BIGINT IDENTITY مفتاح أساسي)، source_ip (INET)، request_time (TIMESTAMPTZ)، response_time_ms (INTEGER)، is_error (BOOLEAN)، metadata (JSONB). أدرج سجلي سجل، ثم استعلم عن جميع السجلات من شبكة10.0.0.0/8. -
تحدي (★★★): صمم جدول
financial_transactionsيتعامل مع عملات متعددة (USD/EUR/JPY/CNY) بدقات مبالغ مختلفة (USD/EUR: منزلتان عشريتان، JPY: 0 منازل عشرية، CNY: منزلتان عشريتان). نفذه باستخدام DECIMAL + قيود CHECK بحيث تتطلب العملات المختلفة مقاييس عشرية مختلفة. اكتب CREATE TABLE SQL بالإضافة إلى 3 أمثلة INSERT لعملات مختلفة.