فصل ۲: SQL پایه — خواندن و نوشتن داده، درست و امن

توابع رشته، عدد و تاریخ، CASE و تاریخ شمسی

جعبه‌ابزار توابع داخلی

MySQL صدها تابع دارد؛ این درس آن‌هایی را پوشش می‌دهد که هر روز لازم می‌شوند، به‌همراه رفتارهایی که مخصوص متن فارسی و پول ریالی است.

رشته‌ها

SELECT
  LENGTH('کاشان')        AS bytes,       -- 10 : تعداد بایت
  CHAR_LENGTH('کاشان')   AS chars,       -- 5  : تعداد کاراکتر
  CONCAT('فرش ', NULL)   AS c1,          -- NULL !
  CONCAT_WS(' - ', 'کاشان', NULL, 'لچک‌ترنج') AS c2,  -- NULLها رد می‌شوند
  SUBSTRING('KSH-1001', 5)     AS num,   -- 1001
  LPAD(42, 6, '0')             AS code,  -- 000042
  TRIM('  فرش  ')              AS t,
  UPPER('ksh')                 AS u;

برای شمارش طول متن فارسی همیشه CHAR_LENGTH را به کار ببرید؛ LENGTH بایت می‌شمارد و برای هر حرف فارسی ۲ برمی‌گرداند.

اعداد و پول

SELECT
  ROUND(48750000, -5)            AS r,      -- 48800000 : گرد به صد هزار ریال
  TRUNCATE(123.789, 1)           AS tr,     -- 123.7
  17 DIV 5 AS q, 17 MOD 5 AS m,             -- 3 و 2
  FORMAT(480000000 / 10, 0)      AS toman;  -- '48,000,000'

تاریخ و زمان

SELECT
  NOW(), CURDATE(), CURRENT_TIME(),
  DATE_ADD(CURDATE(), INTERVAL 45 DAY)                  AS due_date,
  DATEDIFF('2025-06-01', '2025-03-21')                  AS days,
  TIMESTAMPDIFF(MONTH, '2024-03-20', CURDATE())         AS months,
  DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i')                  AS formatted,
  LAST_DAY(CURDATE())                                   AS month_end;

CASE: منطق شرطی داخل کوئری

SELECT sku,
  CASE
    WHEN reeds IS NULL THEN 'نامشخص'
    WHEN reeds >= 70   THEN 'ممتاز'
    WHEN reeds >= 50   THEN 'خوب'
    ELSE 'معمولی'
  END AS grade,
  IF(stock > 0, 'موجود', 'ناموجود') AS availability
FROM carpets
ORDER BY CASE city WHEN 'کاشان' THEN 1 WHEN 'قم' THEN 2 ELSE 3 END, sku;

CASE اولین شاخه‌ی TRUE را برمی‌گرداند، پس شرط‌ها را از خاص به عام بنویسید. ترفند ORDER BY با CASE برای ترتیب دلخواه (نه الفبایی) بسیار کاربردی است.

تاریخ شمسی

MySQL تقویم جلالی ندارد. سه راه وجود دارد:

روشمزیتعیب
تبدیل در برنامه (jdatetime در پایتون، verta در لاراول)ساده، دقیقگزارش‌گیری ماهانه‌ی شمسی در SQL سخت است
Stored Function تبدیلدر همه‌ی کوئری‌ها در دسترسکند روی میلیون‌ها ردیف، مانع ایندکس
جدول تقویم (هر روز یک ردیف با سال و ماه شمسی)سریع، قابل JOIN و ایندکسیک بار ساختن جدول

ستون‌ها را همیشه میلادی (DATE/DATETIME) ذخیره کنید و شمسی را فقط برای نمایش و گروه‌بندی بسازید. در پروژه‌ی پایانی جدول تقویم را به کار می‌گیریم.

نکته‌هایی که کمتر کسی می‌داند

  • NOW() در طول یک دستور ثابت است، اما SYSDATE() لحظه‌ی واقعی اجرای همان تابع را می‌دهد و با Replication مبتنی بر statement ناسازگار است؛ تقریباً همیشه NOW را بخواهید.
  • ROUND روی DECIMAL «نیمه به دور از صفر» گرد می‌کند اما روی FLOAT و DOUBLE به کتابخانه‌ی C وابسته است و ممکن است 2.5 را 2 کند؛ یکی دیگر از دلایل DECIMAL برای پول.
  • FORMAT() رشته برمی‌گرداند؛ مرتب‌سازی روی خروجی آن الفبایی است و '9,000' بعد از '10,000' می‌آید. فقط برای نمایش نهایی.
  • تقسیم بر صفر در SELECT فقط NULL و یک warning می‌دهد، اما در INSERT و UPDATE با sql_mode سخت‌گیر خطاست؛ برای درصدها از x / NULLIF(y, 0) استفاده کنید.
  • GROUP_CONCAT به‌طور پیش‌فرض خروجی را در 1024 بایت (حدود ۵۰۰ حرف فارسی) می‌بُرد و فقط یک warning می‌دهد؛ SET SESSION group_concat_max_len = 100000; را فراموش نکنید.

برای ذخیره‌ی پیشرفت و شرکت در آزمون، وارد شوید — رایگان است.