دوال النصوص String Functions
تُعد دوال النصوص من أهم الأدوات في SQL للتعامل مع البيانات النصية. سنتعلم كيفية دمج النصوص، استخراج أجزاء منها، البحث والاستبدال، وتنسيق البيانات النصية بطرق متعددة.
مقدمة في دوال النصوص
البيانات النصية موجودة في كل مكان - الأسماء، العناوين، الأوصاف، والمزيد. SQL يوفر مجموعة غنية من الدوال للتعامل مع هذه البيانات:
- دوال الدمج: CONCAT, CONCAT_WS
- دوال الاستخراج: SUBSTRING, LEFT, RIGHT
- دوال البحث: LOCATE, POSITION, INSTR
- دوال التحويل: UPPER, LOWER, INITCAP
- دوال التنظيف: TRIM, LTRIM, RTRIM
- دوال الاستبدال: REPLACE, TRANSLATE
- دوال القياس: LENGTH, CHAR_LENGTH
دمج النصوص CONCAT
دالة CONCAT تُستخدم لدمج نصين أو أكثر معاً في نص واحد.
الصيغة الأساسية
-- MySQL, PostgreSQL, SQL Server
SELECT CONCAT(string1, string2, ...) AS result;
-- Oracle (استخدام ||)
SELECT string1 || string2 AS result FROM dual;
أمثلة عملية
-- جدول الموظفين
SELECT
first_name,
last_name,
CONCAT(first_name, ' ', last_name) AS full_name
FROM employees;
-- النتيجة:
-- first_name | last_name | full_name
-- أحمد | محمد | أحمد محمد
-- سارة | علي | سارة علي
-- خالد | عبدالله | خالد عبدالله
CONCAT_WS - الدمج مع فاصل
CONCAT_WS (With Separator) تدمج النصوص مع فاصل محدد بينها:
-- بناء عنوان كامل
SELECT
CONCAT_WS(', ',
street,
city,
country
) AS full_address
FROM customers;
-- النتيجة:
-- شارع الملك فهد, الرياض, السعودية
-- دمج رقم الهاتف مع رمز الدولة
SELECT
CONCAT_WS('-',
country_code,
area_code,
phone_number
) AS formatted_phone
FROM contacts;
-- النتيجة:
-- 966-11-1234567
في معظم قواعد البيانات، إذا كان أي جزء من CONCAT يحتوي على NULL، تكون النتيجة NULL. لكن CONCAT_WS تتجاهل القيم الفارغة:
SELECT CONCAT('Hello', NULL, 'World'); -- NULL في MySQL
SELECT CONCAT_WS('-', 'A', NULL, 'B'); -- A-B (تتجاهل NULL)
استخراج النصوص SUBSTRING
دوال الاستخراج تسمح لك بأخذ جزء من النص.
دالة SUBSTRING
-- الصيغة: SUBSTRING(string, start, length)
-- الترقيم يبدأ من 1
SELECT SUBSTRING('Hello World', 1, 5); -- 'Hello'
SELECT SUBSTRING('Hello World', 7, 5); -- 'World'
SELECT SUBSTRING('Hello World', 7); -- 'World' (حتى النهاية)
-- استخراج السنة من تاريخ نصي
SELECT SUBSTRING('2025-01-15', 1, 4) AS year; -- '2025'
-- استخراج الشهر
SELECT SUBSTRING('2025-01-15', 6, 2) AS month; -- '01'
دوال LEFT و RIGHT
-- LEFT: أول n حرف
SELECT LEFT('Hello World', 5); -- 'Hello'
-- RIGHT: آخر n حرف
SELECT RIGHT('Hello World', 5); -- 'World'
-- استخراج رمز الدولة من رقم الهاتف
SELECT
phone_number,
LEFT(phone_number, 3) AS country_code
FROM customers;
-- استخراج امتداد الملف
SELECT
filename,
RIGHT(filename, 3) AS extension
FROM files;
-- 'document.pdf' → 'pdf'
-- 'image.jpg' → 'jpg'
مثال عملي: تنسيق رقم البطاقة
-- إظهار آخر 4 أرقام فقط
SELECT
card_number,
CONCAT('****-****-****-', RIGHT(card_number, 4)) AS masked_card
FROM payments;
-- النتيجة:
-- card_number | masked_card
-- 4532123456789012 | ****-****-****-9012
دوال البحث في النصوص
دوال البحث تساعدك في العثور على موقع نص داخل نص آخر.
دالة LOCATE / POSITION / INSTR
-- MySQL: LOCATE(substring, string)
SELECT LOCATE('World', 'Hello World'); -- 7
-- PostgreSQL: POSITION(substring IN string)
SELECT POSITION('World' IN 'Hello World'); -- 7
-- Oracle: INSTR(string, substring)
SELECT INSTR('Hello World', 'World') FROM dual; -- 7
-- البحث مع موقع البداية
SELECT LOCATE('o', 'Hello World', 5); -- 8 (يبدأ البحث من الموقع 5)
-- إذا لم يُوجد يرجع 0
SELECT LOCATE('xyz', 'Hello World'); -- 0
استخدام البحث مع SUBSTRING
-- استخراج النطاق من البريد الإلكتروني
SELECT
email,
SUBSTRING(email, LOCATE('@', email) + 1) AS domain
FROM users;
-- النتيجة:
-- email | domain
-- ahmed@gmail.com | gmail.com
-- sara@company.com | company.com
-- استخراج اسم المستخدم
SELECT
email,
LEFT(email, LOCATE('@', email) - 1) AS username
FROM users;
-- النتيجة:
-- email | username
-- ahmed@gmail.com | ahmed
تحويل حالة الأحرف
هذه الدوال مفيدة للغة الإنجليزية وبعض اللغات الأخرى.
-- تحويل لأحرف كبيرة
SELECT UPPER('hello world'); -- 'HELLO WORLD'
-- تحويل لأحرف صغيرة
SELECT LOWER('HELLO WORLD'); -- 'hello world'
-- أول حرف كبير (PostgreSQL, Oracle)
SELECT INITCAP('hello world'); -- 'Hello World'
-- تطبيق عملي: البحث بدون حساسية للحالة
SELECT *
FROM products
WHERE LOWER(product_name) = LOWER('IPhone');
-- تنسيق الأسماء
SELECT
CONCAT(
UPPER(LEFT(first_name, 1)),
LOWER(SUBSTRING(first_name, 2))
) AS formatted_name
FROM employees;
للبحث بدون حساسية للحالة، يمكن استخدام COLLATE أو ILIKE في PostgreSQL بدلاً من تحويل الحالة في كل مرة.
تنظيف النصوص TRIM
دوال TRIM تزيل الفراغات أو أحرف محددة من بداية و/أو نهاية النص.
-- إزالة الفراغات من الطرفين
SELECT TRIM(' Hello World '); -- 'Hello World'
-- إزالة من اليسار فقط
SELECT LTRIM(' Hello World '); -- 'Hello World '
-- إزالة من اليمين فقط
SELECT RTRIM(' Hello World '); -- ' Hello World'
-- إزالة أحرف محددة (MySQL, PostgreSQL)
SELECT TRIM(BOTH '-' FROM '---Hello---'); -- 'Hello'
SELECT TRIM(LEADING '0' FROM '00012345'); -- '12345'
SELECT TRIM(TRAILING '.' FROM 'Hello...'); -- 'Hello'
مثال عملي: تنظيف البيانات المستوردة
-- تنظيف وتوحيد البيانات
UPDATE customers
SET
first_name = TRIM(first_name),
last_name = TRIM(last_name),
email = LOWER(TRIM(email)),
phone = TRIM(REPLACE(phone, ' ', ''))
WHERE
first_name LIKE ' %'
OR first_name LIKE '% '
OR email != LOWER(email);
الاستبدال REPLACE
دالة REPLACE تستبدل كل ظهور لنص بنص آخر.
-- الصيغة: REPLACE(string, old_text, new_text)
SELECT REPLACE('Hello World', 'World', 'SQL');
-- 'Hello SQL'
-- استبدال متعدد
SELECT REPLACE(REPLACE(phone, '-', ''), ' ', '') AS clean_phone
FROM customers;
-- '966-11-123 4567' → '966111234567'
-- تحديث روابط قديمة
UPDATE articles
SET content = REPLACE(content, 'http://', 'https://')
WHERE content LIKE '%http://%';
-- إزالة حرف بالاستبدال بفراغ
SELECT REPLACE('A-B-C-D', '-', ''); -- 'ABCD'
TRANSLATE - استبدال متعدد
-- استبدال حرف بحرف
-- TRANSLATE(string, from_chars, to_chars)
SELECT TRANSLATE('Hello', 'elo', 'aio');
-- 'Haiii' (e→a, l→i, o→i)
-- إزالة أحرف خاصة
SELECT TRANSLATE(
phone_number,
'()-. ',
''
) AS digits_only
FROM contacts;
-- '(966) 11-123.4567' → '966111234567'
قياس طول النص
-- عدد الأحرف
SELECT LENGTH('Hello'); -- 5 (MySQL)
SELECT CHAR_LENGTH('Hello'); -- 5
SELECT LEN('Hello'); -- 5 (SQL Server)
-- ملاحظة مهمة للعربية:
SELECT LENGTH('مرحبا'); -- قد يُرجع عدد البايتات
SELECT CHAR_LENGTH('مرحبا'); -- 5 (عدد الأحرف الصحيح)
-- التحقق من طول كلمة المرور
SELECT
username,
CASE
WHEN CHAR_LENGTH(password_hash) < 8 THEN 'ضعيفة'
WHEN CHAR_LENGTH(password_hash) < 12 THEN 'متوسطة'
ELSE 'قوية'
END AS password_strength
FROM users;
استخدامات عملية
-- العثور على سجلات بأسماء قصيرة جداً
SELECT * FROM customers
WHERE CHAR_LENGTH(TRIM(first_name)) < 2;
-- إحصائيات عن طول الأوصاف
SELECT
MIN(CHAR_LENGTH(description)) AS min_length,
MAX(CHAR_LENGTH(description)) AS max_length,
AVG(CHAR_LENGTH(description)) AS avg_length
FROM products;
-- اقتطاع الأوصاف الطويلة
SELECT
product_name,
CASE
WHEN CHAR_LENGTH(description) > 100
THEN CONCAT(LEFT(description, 97), '...')
ELSE description
END AS short_description
FROM products;
إضافة حشو LPAD/RPAD
دوال الحشو تضيف أحرفاً لتصل النص لطول معين.
-- LPAD: إضافة من اليسار
SELECT LPAD('123', 6, '0'); -- '000123'
SELECT LPAD('42', 5, '*'); -- '***42'
-- RPAD: إضافة من اليمين
SELECT RPAD('Hello', 10, '.'); -- 'Hello.....'
SELECT RPAD('99', 5, '0'); -- '99000'
-- تنسيق أرقام الفواتير
SELECT
CONCAT('INV-', LPAD(invoice_id, 8, '0')) AS invoice_number
FROM invoices;
-- INV-00001234
-- تنسيق تقرير
SELECT
RPAD(product_name, 30, '.'),
LPAD(FORMAT(price, 2), 10, ' ')
FROM products;
-- Output:
-- iPhone 15 Pro............ 1299.00
-- MacBook Air.............. 999.00
أمثلة متقدمة
تحليل الروابط URL
-- استخراج أجزاء الرابط
WITH url_parts AS (
SELECT 'https://www.example.com/products/123?ref=home' AS url
)
SELECT
url,
-- استخراج البروتوكول
LEFT(url, LOCATE('://', url) - 1) AS protocol,
-- استخراج النطاق
SUBSTRING_INDEX(
SUBSTRING_INDEX(url, '://', -1),
'/',
1
) AS domain,
-- استخراج المسار
SUBSTRING(
url,
LOCATE('/', url, LOCATE('://', url) + 3)
) AS path
FROM url_parts;
تنسيق الأسماء
-- تحويل الاسم للاختصار
SELECT
first_name,
last_name,
CONCAT(
UPPER(LEFT(first_name, 1)),
'.',
UPPER(LEFT(last_name, 1)),
'.'
) AS initials
FROM employees;
-- النتيجة:
-- first_name | last_name | initials
-- Ahmed | Mohamed | A.M.
-- Sara | Ali | S.A.
-- توليد اسم مستخدم فريد
SELECT
LOWER(
CONCAT(
LEFT(first_name, 1),
last_name,
FLOOR(RAND() * 100)
)
) AS username
FROM employees;
-- 'amohamed42', 'sali78'
البحث المتقدم
-- بحث في عدة حقول
SELECT *
FROM products
WHERE
LOWER(CONCAT_WS(' ',
product_name,
description,
category
)) LIKE LOWER('%laptop%');
-- البحث مع تسليط الضوء على النتائج
SELECT
product_name,
REPLACE(
description,
'laptop',
'**laptop**'
) AS highlighted_description
FROM products
WHERE description LIKE '%laptop%';
مقارنة بين قواعد البيانات
| الدالة | MySQL | PostgreSQL | SQL Server | Oracle |
|---|---|---|---|---|
| الدمج | CONCAT() | CONCAT() أو || | CONCAT() أو + | || أو CONCAT() |
| الطول | LENGTH() | LENGTH() | LEN() | LENGTH() |
| الاستخراج | SUBSTRING() | SUBSTRING() | SUBSTRING() | SUBSTR() |
| البحث | LOCATE() | POSITION() | CHARINDEX() | INSTR() |
| أول حرف كبير | غير موجود | INITCAP() | غير موجود | INITCAP() |
اختبر معلوماتك
السؤال 1: ما نتيجة CONCAT('Hello', ' ', 'World')؟
السؤال 2: ما نتيجة SUBSTRING('Database', 5, 4)؟
السؤال 3: ما الدالة المستخدمة لإزالة الفراغات من طرفي النص؟
السؤال 4: ما نتيجة LPAD('42', 5, '0')؟
تمرين عملي
تحدي: معالجة بيانات العملاء
اكتب استعلام SQL لتنسيق بيانات العملاء:
- دمج الاسم الأول والأخير مع مسافة
- استخراج النطاق من البريد الإلكتروني
- تنسيق رقم الهاتف بإضافة أصفار في البداية ليصبح 10 أرقام
الجدول: customers (first_name, last_name, email, phone)