SQL Server 2016: نهاية الدعم الممتد في 14 يوليو 2026
تنص سياسة دورة الحياة لدى Microsoft على عشر سنوات من الدعم لكل إصدار من SQL Server: خمس سنوات دعم عادي ثم خمس سنوات دعم ممتد. بالنسبة لـ SQL Server 2016، انتهى الدعم الممتد في 14 يوليو 2026. بعد هذا التاريخ، لا تُصدر Microsoft أي تحديثات أمنية أو إصلاحات حرجة لهذا الإصدار. يتراكم على أي خادم SQL Server 2016 متصل بالشبكة بعد هذا التاريخ ثغرات أمنية غير مسدودة. تشترط المتطلبات التنظيمية — PCI-DSS وGDPR وISO 27001 — عموماً أن تتلقى مكونات البنية التحتية التحديثات الأمنية المتاحة. وبالتالي فإن الاستمرار على SQL Server 2016 بعد يوليو 2026 يُعدّ انحرافاً موثقاً قابلاً للرصد في أي تدقيق.
لماذا نختار PostgreSQL بدلاً من ترقية SQL Server أو Azure SQL
- تكلفة ترخيص صفرية: PostgreSQL صادر بموجب رخصة PostgreSQL (مرنة تشبه BSD)، دون CAL ودون إصدار Enterprise منفصل للشراء.
- تحكم كامل في البنية التحتية: على VPS مخصص، تتولى إدارة الإصدارات والإضافات وضبط الذاكرة والنسخ الاحتياطية دون الاعتماد على مزود سحابي.
- SQL Server 2022 يُعالج ثقل الإصدار لكنه يُبقي تكاليف الترخيص ويربط المؤسسة بمنظومة Microsoft.
- Azure SQL Managed Instance يبسّط الهجرة لكنه يُضيف تكلفة شهرية متكررة وتبعية لـ Azure وقيوداً إضافية على الشبكة.
- PostgreSQL يدعم الميزات المتقدمة ذاتها: دوال النوافذ التحليلية، CTE المتكررة، JSON الأصيل، تقسيم الجداول، والنسخ المتماثل المنطقي.
- منظومة الأدوات واسعة: pgAdmin وDBeaver وMetabase وPostgREST، ومعظم أطر ORM (Entity Framework وHibernate وSQLAlchemy وActiveRecord) تدعم PostgreSQL.
- توزيعات Linux الرئيسية تحزّم PostgreSQL مع تحديثات أمنية منتظمة ضمن عقد صيانة التوزيعة.
التدقيق الأولي: ما يُهاجَر بسلاسة وما يستلزم عملاً
قبل كتابة أي أمر للهجرة، لا بد من تدقيق الهيكل. العناصر التي تُهاجَر دون عائق: الجداول البسيطة ذات الأنواع المعيارية، وقيود PRIMARY KEY وFOREIGN KEY وUNIQUE وNOT NULL، والمؤشرات من نوع B-tree، والعروض الخالية من صياغة T-SQL الخاصة، والتسلسلات. العناصر التي تستلزم عملاً صريحاً: الإجراءات المخزنة ودوال T-SQL (صياغة غير متوافقة، يجب إعادة كتابتها بـ PL/pgSQL أو نقلها إلى طبقة التطبيق)، والمحفزات التي تستخدم الجداول الوهمية INSERTED وDELETED (المقابل في PostgreSQL: NEW وOLD)، وأعمدة ROWVERSION/TIMESTAMP المستخدمة لاكتشاف التزامن التفاؤلي، والأعمدة المحسوبة، وتلميحات القفل (WITH (NOLOCK) وWITH (UPDLOCK))، والجداول المؤقتة العامة (##temp). يمكن الحصول على قائمة كاملة بعناصر SQL Server عبر استعلام INFORMATION_SCHEMA وعروض النظام sys.objects وsys.procedures وsys.triggers.
مقابلة أنواع البيانات: SQL Server ← PostgreSQL
مرّر الجدول أفقيًا
| نوع SQL Server | نوع PostgreSQL | ملاحظة |
|---|---|---|
| DATETIME2 | TIMESTAMPTZ | تخزين الطوابع الزمنية مع المنطقة الزمنية؛ DATETIME2 لا يحمل منطقة زمنية، يجب تحديد اتفاقية UTC صراحةً |
| UNIQUEIDENTIFIER | UUID | PostgreSQL يدعم UUID أصلياً؛ استخدم إضافة uuid-ossp أو gen_random_uuid() للتوليد |
| NVARCHAR(MAX) / NTEXT | TEXT | PostgreSQL يخزّن جميع السلاسل بـ UTF-8؛ لا تمييز بين NVARCHAR وVARCHAR ولا حد MAX منفصل |
| BIT | BOOLEAN | تصبح القيم 0/1 قيمتَي false/true؛ انتبه لأعمدة BIT المستخدمة كأعداد صحيحة في العمليات الحسابية |
| MONEY / SMALLMONEY | NUMERIC(19,4) | MONEY في SQL Server عدد صحيح مُدرَّج، NUMERIC دقيق؛ سلوك متطابق للحسابات المالية |
| INT IDENTITY(1,1) | SERIAL أو GENERATED ALWAYS AS IDENTITY | SERIAL اختصار لتسلسل؛ GENERATED ALWAYS AS IDENTITY معيار SQL:2003، يُفضَّل للجداول الجديدة |
| ROWVERSION / TIMESTAMP | bytea + trigger أو xmin | لا مقابل مباشر؛ عمود النظام xmin يُعالج اكتشاف التزامن التفاؤلي في معظم الحالات |
تعارضات T-SQL الشائعة ومقابلاتها في PostgreSQL
ثمة بنى T-SQL شائعة لا مقابل مباشر لها في SQL المعياري وتستلزم إعادة كتابة.
TOP N بدون ORDER BY: يقبل SQL Server SELECT TOP 10 * FROM t بلا ترتيب. يستلزم PostgreSQL استخدام LIMIT 10؛ وإن كان الترتيب مهماً فأضف ORDER BY صريحاً.
ISNULL(expr, value): تقابلها الدالة المعيارية COALESCE(expr, value). السلوك متطابق للحالات الشائعة.
GETDATE() وGETUTCDATE(): تصبحان NOW() (طابع زمني مع منطقة الجلسة) وNOW() AT TIME ZONE 'UTC' أو CURRENT_TIMESTAMP على التوالي.
DATEDIFF وDATEADD: يستخدم PostgreSQL حساب الفترات: date + INTERVAL '7 days'، ويُرجع date2 - date1 قيمة INTERVAL.
تلميح WITH (NOLOCK): يطلب هذا التلميح قراءة بلا قفل مشترك. لا يدعم PostgreSQL هذا التلميح؛ يغطي مستوى عزل READ COMMITTED (الافتراضي) معظم حالات الاستخدام. لتقارير التحليل التي تتحمل بيانات قديمة قليلاً، النسخة المتماثلة للقراءة هي الحل البنيوي.
PRINT في الإجراءات: استخدم RAISE NOTICE 'message' في PL/pgSQL.
GO كفاصل دُفعات: لا وجود لهذا الفاصل في PostgreSQL؛ يجب إعادة هيكلة السكريبتات إلى معاملات أو كتل DO $$...$$ منفصلة.
خطة الهجرة في 8 خطوات
حصر الهيكل وتدقيقه: تصدير قائمة بجميع الجداول والأعمدة مع أنواعها والمؤشرات والقيود والإجراءات المخزنة والدوال والمحفزات والعروض. تحديد الكائنات التي تستلزم إعادة كتابة يدوية وترتيبها حسب الأولوية.
إعداد خادم PostgreSQL المستهدف على VPS: تثبيت PostgreSQL 16، وضبط postgresql.conf (الذاكرة المشتركة، الاتصالات، WAL)، وإنشاء مستخدم التطبيق بالحد الأدنى من الصلاحيات، وتفعيل TLS للاتصالات البعيدة.
تحويل الهيكل والبيانات باستخدام pgloader أو يدوياً: يُهاجر الأمر pgloader mssql://user:pass@host/db postgresql://user:pass@vps/db الهيكل والبيانات بأمر واحد. للهياكل المعقدة، صدّر DDL من SQL Server وكيّفه يدوياً مستعيناً بجدول مقابلة الأنواع.
التحقق من الهيكل المُهاجَر: التحقق من عدد الجداول وعدد الصفوف لكل جدول والنزاهة المرجعية (FOREIGN KEY) والمؤشرات. مقارنة الأنواع الفعلية بالأنواع المتوقعة عبر information_schema.columns.
إعادة كتابة الإجراءات المخزنة والمحفزات: يجب إعادة كتابة كل كائن T-SQL بـ PL/pgSQL أو نقل منطقه إلى طبقة التطبيق. اختبار كل إجراء بشكل مستقل بمجموعات بيانات تمثيلية.
تكييف كود التطبيق: البحث في الكود المصدري عن جميع عبارات SQL الحرفية والاستعلامات ذات المعاملات التي تستخدم بنى T-SQL الخاصة. تحديث موصّل الاتصال (التحول من mssql إلى pg أو psycopg2 أو npgsql أو الموصّل المناسب للبنية التقنية).
اختبار الحمل والتراجع: تشغيل مجموعة اختبارات التراجع الكاملة على قاعدة بيانات PostgreSQL للتجهيز. استخدام pgbench للتحقق من الأداء تحت الضغط. مقارنة خطط تنفيذ الاستعلامات الحرجة (EXPLAIN ANALYZE) بين SQL Server وPostgreSQL.
التبديل في نافذة صيانة: تحديد نافذة زمنية (ليلاً أو عطلة نهاية الأسبوع)، وضع التطبيق في وضع الصيانة، تشغيل هجرة تفاضلية نهائية للبيانات المضافة منذ الهجرة الأولية، توجيه DSN التطبيق نحو PostgreSQL، التحقق من الوظائف الحرجة، ورفع الصيانة. إبقاء SQL Server في وضع القراءة فقط 48 ساعة للسماح بتراجع سريع عند الحاجة.
ضبط PostgreSQL 16 على VPS للإنتاج
التثبيت الافتراضي لـ PostgreSQL غير مضبوط للإنتاج. المعاملات التالية في postgresql.conf هي نقطة انطلاق لـ VPS بذاكرة 4 GB.
shared_buffers: اضبطها على 25% من الذاكرة المتاحة، أي 1GB لـ 4 GB. يحدد هذا المعامل الذاكرة المخصصة للذاكرة المؤقتة الداخلية لـ PostgreSQL.
effective_cache_size: اضبطها على 75% من إجمالي الذاكرة، أي 3GB. يُعلم هذا المعامل مُخطِّط الاستعلامات بالذاكرة المتاحة لذاكرة نظام التشغيل المؤقتة.
work_mem: من 16MB إلى 64MB حسب تعقيد استعلامات الترتيب. يُطبَّق هذا المعامل لكل عملية ترتيب ولكل اتصال؛ قيمة مرتفعة جداً على خادم يحمل اتصالات متزامنة كثيرة تُنهك الذاكرة.
maintenance_work_mem: 256MB لعمليات الصيانة (VACUUM وCREATE INDEX).
max_connections: 100 كافٍ لمعظم التطبيقات التي تستخدم مجمّع اتصالات. إن كان التطبيق يفتح اتصالاً لكل طلب بلا مجمّع، استخدم PgBouncer أمامه.
في pg_hba.conf، اقصر الاتصالات البعيدة على عنوان IP خادم التطبيق واشترط TLS: hostssl all appuser 10.0.0.5/32 scram-sha-256.
فعّل TLS بتوليد شهادة (Let's Encrypt أو شهادة داخلية) وضبط ssl = on وssl_cert_file وssl_key_file في postgresql.conf.
المزالق: الإجراءات المخزنة ومحفزات T-SQL
الإجراءات المخزنة بـ T-SQL هي نقطة الاحتكاك الرئيسية في الهجرة من SQL Server إلى PostgreSQL. T-SQL لهجة خاصة بهياكل تحكم ومعالجة مؤشرات ومعالجة أخطاء (كتل TRY...CATCH) لا مقابل مباشر لها في PL/pgSQL.
مؤشرات FAST_FORWARD في SQL Server تصبح حلقات FOR في PL/pgSQL، لكن الأداء مماثل ويجب في كل الأحوال التساؤل عن ضرورة المؤشرات: الاستعلام المبني على مجموعات البيانات يحل محل المؤشر بشكل أكثر كفاءة في الغالب.
كتلة TRY...CATCH تصبح BEGIN...EXCEPTION WHEN OTHERS THEN في PL/pgSQL.
الجداول المؤقتة المحلية (#temp) تقابلها الجداول المؤقتة للجلسة في PostgreSQL المنشأة بـ CREATE TEMPORARY TABLE. الجداول المؤقتة العامة (##temp) لا مقابل مباشر لها — يجب استبدالها بجداول دائمة مع إدارة صريحة لدورة الحياة.
المحفزات التي تُشير إلى INSERTED وDELETED (الجداول الوهمية في SQL Server) يجب إعادة كتابتها لاستخدام NEW وOLD في PostgreSQL. هيكل التشغيل يختلف أيضاً: يُميز PostgreSQL بين محفزات BEFORE وAFTER، وبين FOR EACH ROW وFOR EACH STATEMENT.
نهج عملي يقضي بنقل منطق الإجراءات المخزنة إلى طبقة التطبيق أثناء الهجرة، مما يبسّط الاختبار ويقلل الارتباط بين التطبيق وقاعدة البيانات.
اختبار الحمل قبل التبديل: pgbench ومقارنة خطط الاستعلام
قبل التبديل للإنتاج، تحقق من الأداء باستخدام pgbench. تهيئة قاعدة بيانات اختبار تمثيلية: pgbench -i -s 50 db_name ينشئ هيكل معياري بخمسة ملايين صف. شغّل محاكاة حمل: pgbench -c 20 -j 4 -T 60 db_name يحاكي 20 اتصالاً متوازياً لمدة 60 ثانية ويُبلّغ بالمعاملات في الثانية. للاستعلامات الحرجة في التطبيق، قارن خطط التنفيذ بين SQL Server (SET STATISTICS IO ON; SELECT ...) وPostgreSQL (EXPLAIN (ANALYZE, BUFFERS) SELECT ...). راقب مسح الجداول الكاملة غير المتوقعة (مؤشر مفقود أو إحصائيات غير محسوبة — شغّل ANALYZE بعد الهجرة) وصلات NESTED LOOP على أحجام كبيرة ستستفيد من HASH JOIN. حدّث الإحصائيات بعد الهجرة الأولية بـ ANALYZE table_name أو VACUUM ANALYZE على قاعدة البيانات كاملة.
استكشاف الأخطاء — الأخطاء الشائعة في هجرة SQL Server إلى PostgreSQL
ثمة فئات من الأخطاء تتكرر بانتظام في الهجرات الأولى.
ERROR: column is of type uuid but expression is of type character varying: عمود UNIQUEIDENTIFIER مُهاجَر إلى UUID يتلقى سلسلة بتنسيق UUID بلا تحويل صريح. كيّف استعلام التطبيق ليُرسل نوع UUID حقيقياً، أو أضف تحويلاً ضمنياً في PostgreSQL (CREATE CAST)، أو هاجر العمود إلى TEXT إن لم تُستغل دلالات UUID.
ERROR: operator does not exist: boolean = integer: عمود BIT مُهاجَر إلى BOOLEAN يُقارَن بـ 0 أو 1. استبدل col = 1 بـ col = true أو ببساطة col في جمل WHERE.
ERROR: function isnull(character varying, text) does not exist: استبدل جميع استدعاءات ISNULL بـ COALESCE.
ERROR: syntax error at or near "TOP": صياغة SELECT TOP N غير موجودة في PostgreSQL. استبدلها بـ SELECT ... LIMIT N.
تجاوزات صامتة على أعمدة MONEY ← NUMERIC: تحقق من أن القيم القصوى في الجدول المصدر تتسع ضمن الدقة المستهدفة NUMERIC(19,4). نطاق MONEY في SQL Server هو ±922,337,203,685,477.5807؛ يغطي NUMERIC(19,4) النطاق ±999,999,999,999,999.9999، وهو كافٍ في جميع الحالات تقريباً.
فقدان بيانات على أعمدة DATETIME بلا منطقة زمنية: DATETIME وDATETIME2 في SQL Server لا يخزنان المنطقة الزمنية. إن كانت قاعدة البيانات تُغذَّى من تطبيقات في مناطق زمنية متعددة، ستُفسَّر الطوابع الزمنية المُهاجَرة إلى TIMESTAMPTZ بلا تحويل سابق على أنها UTC. حدد اتفاقية (UTC أو التوقيت المحلي) وطبّق تحويل AT TIME ZONE أثناء الهجرة إن لزم.
التحقق من الهجرة وإعادة الوصول للمستخدمين
بعد التبديل، تُقلل قائمة التحقق المنظمة من خطر إعادة المستخدمين إلى قاعدة بيانات ناقصة.
تحقق من عدد الصفوف لجميع الجداول الحرجة: استعلام على information_schema.tables مع COUNT(*) لكل جدول، مقارنةً بالأرقام المسجلة من SQL Server قبل الهجرة.
تحقق من النزاهة المرجعية: SELECT * FROM pg_constraint WHERE contype = 'f' يُدرج جميع قيود المفتاح الخارجي؛ حاول إدراجاً ينتهك قيداً للتأكد من فعاليتها.
تحقق من المؤشرات: SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' يُدرج جميع المؤشرات المنشأة. قارنها بقائمة مؤشرات SQL Server.
اختبر الوظائف الحرجة في التطبيق: مسار الشراء، والمصادقة، والتقارير، والتصدير — على الأقل المسارات التي تمس الجداول الأكثر حجماً والاستعلامات الأكثر تعقيداً.
راقب سجلات PostgreSQL خلال الـ 24 ساعة الأولى: tail -f /var/log/postgresql/postgresql-*.log. تظهر أخطاء عدم تطابق الأنواع ومهلات الاتصال وخطط الاستعلام دون المستوى الأمثل عادةً في الساعات الأولى من حركة المرور الحقيقية.
أبق SQL Server في وضع القراءة فقط 48 ساعة مع تقييد الوصول للفريق التقني. التراجع يعني توجيه DSN التطبيق مجدداً نحو SQL Server وإعادة تشغيل البيانات المكتوبة في PostgreSQL خلال نافذة التبديل.