مهدی نظارت
مهدی نظارت
خواندن ۳ دقیقه·۶ ماه پیش

ساخت Tally Table در SQL

سلام به همگی بعد از مدتها با یه مطلب کاربردی خدمتتون اومدم و می خواهم در مورد جدول های شمارشی یا Tally Table با شما صحبت کنم .

ابتدا بگذارید صورت مسئله را مطرح کنم ، فرض کنیم از یک پایگاه داده دیگه ای مثل اکسل یا اکسس یا ... جدولی با نام IMP را به SQL ایمپورت کردید که دارای ستون ردیف با نام Rn هست و شما متوجه می شید که شماره ردیفها با تعداد رکوردها مطابقت نداره چرا که بعضی از ردیفها حذف شدن ! مثلا شماره ردیفها از 1 تا یک میلیون هست اما وقتی نتیجه Import را بررسی می کنید یا روی جدول مورد نظر Select Count(*) را اجرا می کنید ، می بینید که تعداد اونها 999,996 هست و چهار تا رکورد کم داره و باید شماره ردیفهایی که نیست را پیدا کنیم .

در عمل و محیط تجاری و کاری ممکنه با این مشکل در شماره سریالهای فاکتور یک شرکت که باید ردیف و پشت سرهم باشه ، اما تعدادی از اونها نیست و بخاطر ملاحظات قانونی باید پیداشون کرد ، نبودن بعضی از شماره اسناد حسابداری در جداول بایگانی یا پیدا کردن ردیفهای حذف شده یک جدول بر اساس فیلد یکتای ( Identity ) آن و ... مواجه می شید و باید مسئله را حل کنید .

خب حالا چطوری شماره ردیف هایی که نیست را پیدا کنیم ؟ جواب اینه که باید این جدول را با جدول دیگه ای که شماره ردیفهاش کامله ( اسم این جدول را می گذاریم SRC و فیلد ردیف اون را Line می نامیم ) جوین کرد تا ردیفهای مفقودی را پیدا کنیم مثلا :

Select src.Line

From SRC

Left join IMP on src.Line=IMP.RN

Where IMP.Rn is null

توضیح تکمیلی اینکه اون جدول SRC با ستون Line همان Tally Table یا جدول شمارشی هست و برای حل مسئله باید در پایگاه داده خودمون چنین جدولی داشته باشیم و حالا اگه نداریم چکار کنیم ؟

دو تا راهکار هست که مشکل گشاست

راه کار اول : با فرض اینکه تعداد ردیفهای جدول IMP کم هست مثلا زیر هزار تا و می خوایم ردیف های حذف شده را پیدا کنیم ، از یک جدول دیگه که تعداد ردیفهای مورد نظر ما را داره و با کمک تابع Row_number جدول SRC را بصورت موقت می سازیم ، مثلا من اینجا از جدول sys.sysobjects خود SQL استفاده می کنم :

Select Src.Line

From (

Select Line=Row_number()over(order by O1.Id)

From sys.sysobjects O1

) SRC

Left join IMP on IMP.Rn=Src.Line

where IMP.Rn is null

راه کار دوم : اگه تعداد ردیفهای جدول IMP زیاد بود مثلا یک میلیون و ما جدولی با تعداد رکورد بالا نداریم یا نمی شناسیم تا از اون کمک بگیریم ، Cross Join را به کمک می طلبیم :

Select Src.Line

From (

Select top 1000000

Line=Row_number()over(order by O1.Id)

From sys.sysobjects O1

Cross Join Sys.sysobjects O2

Order by O1. id

) SRC

left join IMP on IMP.Rn=Src.Line

where IMP.Rn is null

نکته اول : اگه جدول Sys.Sysobjects دارای 2000 تا رکورد باشه با اون Cross join تعداد ردیفها ضرب دکارتی می شه و می شه 4 میلیون رکورد !

نکته دوم : آیا به جای sys.sysobjects نمی تونیم از خود جدول IMP و Cross Join استفاده کنیم ؟ اوه اوه ⛔ ، مراقب cross join باشید چون اگه چنین کاری کنیم و جدول IMP شما یک میلیون رکورد داشته باشه و بخواهیم در Cross Join دوبار از این جدول استفاده کنیم، سرور SQL برای آماده سازی جدول SRC ، ابتدا باید در جدول داخلی یک تریلیون رکورد را تولید کنه ( Cross join و ضرب دکارتی )، اون تعداد رکورد را مرتب کنه (order by) ، یک میلیونیم اون را بیرون بکشه ( Top 1000000 ) و با IMP بیرونی جوین کنه و در این بین حتما سکته خواهد کرد .

نکته سوم : اگه جدول sysobjects فقط 100 تا رکورد داشت چی ؟ می تونید چند بار اون را با خودش Cross join کنید !

امیدوارم این پست مفید بوده باشه و ارزش وقت شما را داشته باشه .💛

sqltally tableجدول شمارشی
شاید از این پست‌ها خوشتان بیاید