چرا نباید با شماره ردیف (Ordinal) در SQL Server مرتبسازی کنید؟ | پرهیز از ORDER BY Ordinal
در دنیای پایگاه دادههای رابطهای، مرتبسازی نتایج یک عملیات رایج و ضروری است. برای مرتبسازی دادهها در SQL Server، از دستور `ORDER BY` استفاده میکنیم که میتواند بر اساس نام ستون، شماره ردیف (ordinal) یا یک عبارت عمل کند. با این حال، استفاده از شماره ردیف برای مرتبسازی، یک “بدترین عملکرد” (worst practice) تلقی میشود که میتواند منجر به مشکلات نگهداری، خوانایی و حتی خطاهای پنهان در کوئریهای SQL شما شود. این مقاله به تفصیل توضیح میدهد که چرا باید از این روش پرهیز کرد و همواره از نام ستونها برای مرتبسازی استفاده نمود.
برای شروع، اجازه دهید یک کوئری ساده برای مشاهده ستونهای جدول `sys.objects` در پایگاه داده `master` را اجرا کنیم:
SELECT c.name
FROM sys.columns c
WHERE c.object_id = OBJECT_ID('sys.objects');
این کوئری لیست نام ستونها را به ما میدهد. حالا فرض کنید میخواهیم نتایج جدول `sys.objects` را مرتب کنیم. یک راه، استفاده از شماره ردیف ستونها در دستور `SELECT` است. به عنوان مثال، اگر ستون `type` در خروجی `SELECT` ستون دوم باشد، میتوانیم به این شکل مرتبسازی کنیم:
SELECT so.name, so.type, so.type_desc
FROM sys.objects so
ORDER BY 2;
در این مثال، `ORDER BY 2` به SQL Server میگوید که نتایج را بر اساس ستون دوم در لیست `SELECT` مرتب کن، که در اینجا `so.type` است. این روش در نگاه اول ممکن است سریع و مختصر به نظر برسد، اما مشکلات جدی به همراه دارد.
چرا مرتبسازی با شماره ردیف (Ordinal) یک مشکل است؟
1. خوانایی پایین و پیچیدگی نگهداری:
مهمترین دلیل برای پرهیز از مرتبسازی با شماره ردیف، کاهش شدید خوانایی کد و افزایش پیچیدگی نگهداری آن است. هنگامی که یک توسعهدهنده دیگر (یا خود شما پس از مدتی) به این کوئری نگاه میکند، بدون مراجعه به لیست `SELECT` نمیتواند بفهمد `ORDER BY 2` به چه ستونی اشاره دارد. این امر مستندسازی کد را دشوار میکند و میتواند به اشتباهات تفسیری منجر شود. به عبارت دیگر، کد شما خود-مستندساز نیست.
یک اصطلاح دیگر برای شماره ترتیبی ستون، `column_position` است.
column_position
این قابلیت (مرتبسازی با `column_position`) در SQL Server برای راحتی در برخی سناریوها مانند ابزارهای BI قدیمی یا گزارشگیری سریع وجود دارد، اما برای کوئریهای تولیدی و کدبلندمدت توصیه نمیشود.
2. آسیبپذیری در برابر تغییرات:
کوئریهایی که با شماره ردیف مرتبسازی میشوند، به شدت نسبت به تغییرات در لیست `SELECT` آسیبپذیر هستند. تصور کنید که یک ستون جدید به لیست `SELECT` اضافه میشود. اگر این ستون قبل از ستون مرتبسازی شده فعلی قرار گیرد، شماره ردیف ستون مرتبسازی شما تغییر خواهد کرد و کوئری نتایج را به اشتباه مرتب میکند، بدون اینکه هیچ خطایی را گزارش دهد! این نوع خطاهای پنهان، یافتن و رفع کردنشان بسیار دشوار است.
به این مثال توجه کنید. ما کوئری قبلی را داریم که بر اساس ستون دوم (`so.type`) مرتب شده بود:
SELECT so.name, so.type, so.type_desc
FROM sys.objects so
ORDER BY 2; -- مرتبسازی بر اساس so.type
حالا اگر ستون `so.object_id` را به عنوان ستون دوم اضافه کنیم:
SELECT so.name, so.object_id, so.type, so.type_desc
FROM sys.objects so
ORDER BY 2; -- حالا مرتبسازی بر اساس so.object_id است!
همانطور که میبینید، تنها با اضافه کردن یک ستون، عملکرد مرتبسازی ما به طور کامل و بیصدا تغییر کرده است. این میتواند منجر به دادههای نادرست در گزارشها یا برنامههای کاربردی شود که تشخیص آن بسیار دشوار است.
3. تداخل با `TOP` و `DISTINCT`:
در برخی موارد، استفاده از `TOP` یا `DISTINCT` در کوئری میتواند بر شماره ردیف ستونها تأثیر بگذارد و منجر به رفتار غیرمنتظره در مرتبسازی شود، حتی اگر لیست `SELECT` به ظاهر ثابت بماند.
راه حل صحیح: همیشه از نام ستونها استفاده کنید.
برای جلوگیری از تمامی مشکلات فوق، راه حل ساده و قطعی این است که همیشه از نام ستونها در دستور `ORDER BY` استفاده کنید. این روش کد شما را خوانا، قابل نگهداری و مقاوم در برابر تغییرات میسازد.
به جای `ORDER BY 2`، از `ORDER BY so.type` استفاده کنید:
SELECT so.name, so.type, so.type_desc
FROM sys.objects so
ORDER BY so.type;
با این کار، حتی اگر ستونهای دیگری به لیست `SELECT` اضافه شوند، مرتبسازی شما همچنان به درستی بر اساس ستون `so.type` انجام خواهد شد.
مسائل عملکردی:
برخی ممکن است نگران باشند که استفاده از نام ستونها به جای شماره ردیف، تأثیری بر عملکرد کوئریهای SQL Server داشته باشد. اما در SQL Serverهای مدرن (از SQL Server 2005 به بعد)، موتور پایگاه داده به اندازه کافی هوشمند است که تفاوت عملکردی قابل توجهی بین این دو روش (در بیشتر سناریوها) ایجاد نمیکند. بهینهساز کوئری (Query Optimizer) به خوبی نام ستونها را به موقعیتهای داخلی ترجمه میکند. بنابراین، ملاحظات خوانایی و نگهداری، وزن بسیار بیشتری نسبت به ملاحظات عملکردی جزئی (که معمولاً وجود ندارند) دارند.
نتیجهگیری
استفاده از شماره ردیف (ordinal) برای مرتبسازی نتایج در دستور `ORDER BY` یک “بدترین عملکرد” است که باید از آن پرهیز شود. این روش باعث کاهش خوانایی، افزایش دشواری نگهداری و ایجاد آسیبپذیری در برابر خطاهای پنهان در کوئریهای شما میشود. برای اطمینان از صحت، پایداری و خوانایی کد SQL خود، همیشه نتایج را با استفاده از نام کامل ستونها مرتب کنید. با این کار، نه تنها کد خود را برای آینده مقاوم میکنید، بلکه همکاری و اشکالزدایی را نیز برای خود و همکارانتان آسانتر میسازید.