PDA

View Full Version : ادغام دو کوئری در EF (کوئری اول شرط قبل از گروهبندی-کوئری دوم شرط بعد از گروهبندی)



mr.sirwan
پنج شنبه 22 مهر 1395, 23:11 عصر
سلام و خسته نباشید، ابتدا از مدیران عزیز عذر خواهی میکنم که این تاپیک زیاد با این تالار مرتبط نیس، ولی مجبور شدم چون دیدم تالار EF زیاد بازدید کننده نداره و قبلا واسه یه تاپیک دیگه به جواب نرسیدم
دوستان من یه همچین جدولی دارم:


142989


و میخوام دو کوئری Select زیر رو که با EF: Code-First نوشته شدن به صورت همزمان اجرا کنم و جمعا به تعداد PageSize از هر دو کوئری برام رکورد واکشی بشه:

List<Models.DupOfInLetters> InDups = new List<Models.DupOfInLetters>();
InDups = oDBC.DupOfInLetters
.Where(d => d.InReferraled.IsReadyToGo == true
&& d.InReferraled.IsEndOfWorK == false
&& d.InReferraled.IsArchived == false
&& d.InReferraled.IsSigned == true
&& (d.SendType == Models.EnumTypes.DuplicationTypes.رونوشت)
&& d.ReferraledToEmployeeCode == frmMain.CurrentUser)
.GroupBy(grouping => grouping.LetterId)
.Select(g => g.OrderByDescending(o => o.Id).FirstOrDefault())
.OrderByDescending(l => l.Id)
.Skip((PageIndex - 1) * (PageSize))
.Take(PageSize)
.ToList();


List<Models.DupOfInLetters> InDups = new List<Models.DupOfInLetters>();
InDups = oDBC.DupOfInLetters
.Where(d => d.InReferraled.IsReadyToGo == true
&& d.InReferraled.IsEndOfWorK == false
&& d.InReferraled.IsArchived == false
&& d.InReferraled.IsSigned == true
&& (d.SendType == Models.EnumTypes.DuplicationTypes.ارجاع))
.GroupBy(grouping => grouping.LetterId)
.Select(g => g.OrderByDescending(o => o.Id).FirstOrDefault())
.Where(d => d.ReferraledToEmployeeCode == frmMain.CurrentUser)
.OrderByDescending(l => l.Id)
.Skip((PageIndex - 1) * (PageSize))
.Take(PageSize)
.ToList();


دو قطعه کد بالا در دو خط با هم تفاوت دارند، قطعه کد پایینی یکی از شرطاش که با بالایی فرق داره (d.SendType == Models.EnumTypes.DuplicationTypes.ارجاع) و دیگه اینکه شرط d.ReferraledToEmployeeCode == frmMain.CurrentUser در قطعه کد دوم بعد از متد Select قرار گرفته
آیا راهی هست که این دو رو باهم ترکیب کنم که همزمان اجرا بشن و درصورت تکراری بودن رکورد های واکشی شده از این دو قطعه کد، فقط یک رکورد انتخاب بشه، اون هم از نوع ارجاع؟؟ اگه توو EF امکان پذیر نیست، با دستورات SQL چطوری همچین کوئری رو بنویسم؟
سپاس از راهنمایتون

Mahmoud.Afrad
جمعه 23 مهر 1395, 00:42 صبح
در کوئری دوم ، شرط خط 26 رو میتونید به قبل از گروهبندی منتقل کنید. در اینصورت دو کوئری شبیه هستند و میتونید به یک کوئری تبدیل کنید.

mr.sirwan
جمعه 23 مهر 1395, 01:52 صبح
در کوئری دوم ، شرط خط 26 رو میتونید به قبل از گروهبندی منتقل کنید. در اینصورت دو کوئری شبیه هستند و میتونید به یک کوئری تبدیل کنید.

ممنون از پاسخگوییتون، هدف هرکدوم از این دو کوئری متفاوته، در ادامه توضیح میدم:

کوئری اول:

ابتدا پس از بررسی چهار فیلد بولین، میاد تمامی رکوردهایی که از نوع رونوشت هستند و گیرنده شون کاربر جاری هستش رو میگیره، در متد GroupBy بر اساس آیدی نامه گروه بندی میکنه، حالا بر اساس آیدی همین رکورد ها بصورت نزولی مرتب میکنه و اولین روکورد رو Select میکنه (ممکنه یک نامه توسط چندین شخص به کاربر جاری رونوشت شده باشه، واسه همین این اعمال انجام میشه)

اینم یه نمونه از اطلاعات داخل جدول (فقط رکوردهای نوع رونوشت رو اینجا نوشتم) رنگهای قرمز اونهایی هستن که توسط کوئری اول برای گیرنده ای با کد 6 انتخاب میشن




142990



اما کوئری دوم:

پس از بررسی چهار فیلد بولین، میاد تمامی رکوردهایی که از نوع ارجاع هستند رو برمیگردونه، در متد GroupBy بر اساس آیدی نامه گروه بندی میکنه، حالا بر اساس آیدی همین رکورد ها بصورت نزولی مرتب میکنه و اولین روکورد رو Select میکنه، حالا میاد داخل where چک میکنه که این رکوردی که انتخاب شده آیا ارجاع گیرنده ش کاربر جاری هستش یا خیر، تمامی رکوردهایی که اخرین ارجاعشون مربوط به کاربر جاری هست رو برمیگردونه (به این خاطر که سیاست من در بحث ارجاعات به این صورت هست که هر نامه در هر زمان، به عنوان ارجاع فقط در دست یک نفر باشه اونم آخرین ارجاعیه که صورت گرفته)

اینم یه نمونه از اطلاعات داخل جدول (فقط رکوردهای نوع ارجاع رو اینجا نوشتم) رنگهای قرمز اونهایی هستن که توسط کوئری دوم برای ارجاع گیرنده ای با کد 9 انتخاب میشن

142991

Mahmoud.Afrad
جمعه 23 مهر 1395, 07:20 صبح
کوئری اول که بحثی نیست.
در کوئری دوم ، فرقی نمیکنه اول گروهبندی و بعد شرط اعمال بشه یا اول شرط و بعد گروهبندی و سلکت بزرگترین آیدی. مثلا برای همان کد گیرنده 9، چه اینکه اول گروهبندی و بعد کد گیرنده 9 رو سلکت کنید و چه اول رکوردهای با گیرنده 9 رو سلکت و بعد گروهبندی ... نتیجه یکسان هست.

mr.sirwan
جمعه 23 مهر 1395, 11:42 صبح
کوئری دوم به این شکلی که کدش رو گذاشتم اونطوری که انتظار دارم برام رکوردارو واکشی میکنه،
چیزی که من انتظار دارم:
اگر نامه ای قبلا به من ارجاع شده باشه و من نامه رو به یه نفر دیگه ارجاع بدم، دیگه در دفعات بعدی نامه ارجاعی در دست من نیست و در دست آخرین ارجاع گیرنده خواهد بود (با اینکه من جزوی از ارجاع گیرنده های این نامه بودم ولی دیگه نباید واسه من نشون داده بشه چون من یا هر کس دیگه ای این نامه رو به شخص دیگه ای ارجاع داده، بطور خلاصه من روش سلسله مراتبی رو پیش گرفتم در هر زمان یک نامه از نوع ارجاع فقط در دست یک نفر باشه)

یک رکورد جدید به مثال پست قبلی اضافه میکنم و بررسی میکنیم که کوئری اصلی چه رکوردهایی رو برمیگردونه و با تغییر در کوئری چه رکوردهایی واکشی میشن

142995

الان در این جدول، رکوردهایی که باید برای گیرنده ای با کد 9 واکشی بشن فقط رکود قرمز رنگ هستش که این امر توسط کوئری اصلی درست اجرا میشه، اما با تغییر کوئری به این شکل که شما فرمودین:



InDups = oDBC.DupOfInLetters
.Where(d => d.InReferraled.IsReadyToGo == true
&& d.InReferraled.IsEndOfWorK == false
&& d.InReferraled.IsArchived == false
&& d.InReferraled.IsSigned == true
&& (d.SendType == Models.EnumTypes.DuplicationTypes.ارجاع)

&& d.ReferraledToEmployeeCode == frmMain.CurrentUser)
.GroupBy(grouping => grouping.LetterId)
.Select(g => g.OrderByDescending(o => o.Id).FirstOrDefault())
.OrderByDescending(l => l.Id)
.Skip((PageIndex - 1) * (PageSize))
.Take(PageSize)
.ToList();




هم رکورد قرمز رنگ و هم رکورد نارنجی رنگ واکشی میشن، که من همچین چیزی رو نمیخوام

امیدوارم منظورمو رسونده باشم، خیلی ممنون از پاسخگوییتون جناب آفراد

Mahmoud.Afrad
جمعه 23 مهر 1395, 20:42 عصر
...

142995

الان در این جدول، رکوردهایی که باید برای گیرنده ای با کد 9 واکشی بشن فقط رکود قرمز رنگ هستش


الان سوالی که برام پیش اومده مگر براساس letterid گروهبندی نمیشه، پس خط رنگ نارنجی هم باید سلکت بشه چون این دو رکورد در یک گروه و مربوط به یک نامه نیستتند.(letterId یکسانی ندارند.)

mr.sirwan
جمعه 23 مهر 1395, 21:10 عصر
الان سوالی که برام پیش اومده مگر براساس letterid گروهبندی نمیشه، پس خط رنگ نارنجی هم باید سلکت بشه چون این دو رکورد در یک گروه و مربوط به یک نامه نیستتند.(letterId یکسانی ندارند.)

خیر، اجازه بدین مرحله به مرحله اجرای کوئری رو بررسی کنیم (کاری به فیلد های بولین نداریم):

ابتدا در اولین Where تمامی رکوردهایی که از نوع ارجاع هستند انتخاب میشن به این شکل:

143004

حالا میرسیم به GroupBy و OrderByDescending

143005

حالا میرسیم به Select.FirstOrDefault در این حال میاد اولین رکورد از هر گروه رو select میکنه که این رکوردها انتخاب میشن:

143006

حالا میرسیم به Where دوم، اینجا میاد و به ازای هر سه رکورد انتخاب شده شرط RefferraledTo==9 رو چک میکنه، که فقط در یک رکورد این شرط صدق میکنه:


143007

اگر اشتباه میکنم ممنون میشم بفرمایید

Mahmoud.Afrad
جمعه 23 مهر 1395, 22:42 عصر
خب ، میشه طور دیگری هم بیان کرد، برای نامه های نوع "ارجاع" ، به شرطی باید سلکت از گروه صورت بگیره که اون نامه برای شخص دیگری ارسال نشده باشه. یعنی گروهی که Refferraller برابر 9 داره باید کنار گذاشته بشه(یا اینکه گروهی انتخاب بشه که همه رکوردهاش refferraller مخالف 9 دارند).

کلا شرطها رو منتقل کنید بعد از GroupBy. در اینصورت دو کوئری رو میتونید ترکیب کنید
var result =
db.DupOfInLetters
.GroupBy(g => g.LetterId)
.Select(g =>
g.OrderByDescending(rec => rec.Id)
.FirstOrDefault(d =>
(d.SendType == Models.EnumTypes.DuplicationTypes.ارجاع
&& g.All(record => record.Refferraller != currentUser)
&& d.RefferralledTo == currentUser)
|| (d.SendType == Models.EnumTypes.DuplicationTypes.رونوشت && d.RefferralledTo == currentUser)))
.OrderByDescending(l => l.Id)
.Skip((PageIndex - 1) * (PageSize))
.Take(PageSize)
.ToList();

mr.sirwan
جمعه 23 مهر 1395, 23:14 عصر
بسیار سپاسگذار از راهنماییتون، کد رو امتحان کردم، شرط Where که فیلد های بولین رو چک میکرد رو قبل از groupby قرار دادم و برنامه رو اجرا کردم، ارور object reference not set to an instance of an object داد، trace کردم دیدم سه تا رکورد برگردونده، که اخرین رکورد یعنی سومین رکورد NULL هستش، دو رکورد اول درستن، میدونم که علتش متد FirstOrDefault هستش اما درک نمیکنم چرا Null برگردونده؟؟

-----------------------------------------------------------------

ویرایش:
بنده قسمت g.All(record => record.Refferraller != currentUser) رو حذف کردم و درست شد، چون با این خط رکوردای درست رو نشون نمیداد ولی الان با حذفش از کوئری دیگه مشکل برطرف شد.

جناب آفراد یه دنیا ممنون

Mahmoud.Afrad
جمعه 23 مهر 1395, 23:53 عصر
g.All(record => record.Refferraller != currentUser) رو نباید حذف کنید. این شرط میاد رکورد با رنگ نارنجی رو حذف میکنه

http://barnamenevis.org/attachment.php?attachmentid=142995&d=1476434242


ببینید چی null هست که خطا میده.

mr.sirwan
شنبه 24 مهر 1395, 00:04 صبح
g.All(record => record.Refferraller != currentUser) رو نباید حذف کنید. این شرط میاد رکورد با رنگ نارنجی رو حذف میکنه

http://barnamenevis.org/attachment.php?attachmentid=142995&d=1476434242


ببینید چی null هست که خطا میده.

حق با شماس، ولی در نظر بگیرین که یک نامه در دست دو یا چند نفر بصورت چرخشی هی ارجاع بشه، مثلا نامه 17 رو کاربر 5 به 9 ارجاع میده، کاربر 9 به 7 ارجاع میده و حالا دوباره کاربر 7 به 9 ارجاع میده، الان دقیقا همچین وضعیتی تو داده های جدولم وجود داره، که میشه آخرین ارجاع نامه 17 و ارجاع گیرنده 9 که باید برای کاربر 9 نشون داده بشه ولی با اون خط کد میاد کلا ارجاعات مربوط به نامه 17 رو نادیده میگیره، همین نامه ای که ذکر کردم واکشی شد

بازم ممنون


ویرایش:
ولی یه چیز عجیب :متفکر: اینکه شد همون کوئری که قبلا استفاده میکردم، مثل همونی که شما تو پست دوم گفتین فقط شرظ "ارجاع" و "رونوشت" رو باهم Or کن، قبلا همینکارو میکردم ولی درست کار نمیکرد پس این کوئری جدید هم اشتباه واکشی میکنه :متفکر:
جریان چیه؟؟ ای بابا :افسرده::عصبانی++:

Mahmoud.Afrad
شنبه 24 مهر 1395, 02:01 صبح
خب من هم گفتم نباید شرط رو حذف کنید.

کوئری زیر رو امتحان کنید
var result =
(
from d in db.DupOfInLetters
group d by new {d.LetterId, d.SendType}
into grouping
let referentialLetter = grouping.Where(rec => rec.SendType == 0).OrderByDescending(rec => rec.Id).FirstOrDefault()
let replicationLetter = grouping.Where(rec => rec.SendType == 1).OrderByDescending(rec => rec.Id).FirstOrDefault(rec => rec.SendType == 1 && rec.RefferralledTo == currentUser)
let isReferential = grouping.Key.SendType == 0 && referentialLetter != null && referentialLetter.RefferralledTo == currentUser
select isReferential ? referentialLetter : replicationLetter
)
.Where(rec => rec != null).ToList();
اگر جواب نداد ، تنها راهی که به نظرم میرسه اینه که دو کوئری رو با هم union کنید.

mr.sirwan
شنبه 24 مهر 1395, 10:32 صبح
با تشکر، این درست جواب میده، ولی فرض کنین یه نامه قبلا به من رونوشت شده و بعدها به بنده ارجاع هم میشه، این کدی که شما گذاشتین جفتشون رو برمیگردونه، کجای کد رو تغییر بدم تا این رو هم برطرف کنم؟ یعنی ارجاع رو برگردونه، اولویت ارجاع بیشتر از رونوشت هستش، مگر اینکه من هم نامه رو به شخص دیگه ای ارجاع بدم در اینصورت باید رونوشت واکشی بشه

یه سوال دیگه آیا میشه همین کد رو کلا به لامبدا اکسپرشن نوشت؟

خیلی ممنون بابت زحمتی که میکشین جناب آفراد

Mahmoud.Afrad
شنبه 24 مهر 1395, 23:05 عصر
خب براساس شماره نامه و کد شخص گیرنده مجددا گروهبندی کنید و اگر تعداد بیش از یک بود نوع ارجاعی و در غیراینصورت همان یک مورد را سلکت کنید.
var result =
(
from d in db.DupOfInLetters
group d by new {d.LetterId, d.SendType}
into grouping
select
grouping.Key.SendType == 0
? grouping.Where(rec => rec.SendType == 0)
.OrderByDescending(rec => rec.Id)
.Take(1)
.FirstOrDefault(d => d.RefferralledTo == currentUser)
: grouping.Where(rec => rec.SendType == 1)
.OrderByDescending(rec => rec.Id)
.FirstOrDefault(rec => rec.SendType == 1 && rec.RefferralledTo == currentUser)
)
.Where(rec => rec != null)
.GroupBy(g => new {g.LetterId, g.RefferralledTo})
.Select(g =>
g.Count() > 1
? g.FirstOrDefault(rec => rec.SendType == 0)
: g.FirstOrDefault()
)
.ToList();
این هم معادل لامبدا
var result =
db.DupOfInLetters.GroupBy(d => new {d.LetterId, d.SendType})
.Select(grouping => grouping.Key.SendType == 0
? grouping.Where(rec => rec.SendType == 0)
.OrderByDescending(rec => rec.Id)
.Take(1)
.FirstOrDefault(d => d.RefferralledTo == currentUser)
: grouping.Where(rec => rec.SendType == 1)
.OrderByDescending(rec => rec.Id)
.FirstOrDefault(rec => rec.SendType == 1 && rec.RefferralledTo == currentUser))
.Where(rec => rec != null)
.GroupBy(g => new {g.LetterId, g.RefferralledTo})
.Select(g =>
g.Count() > 1
? g.FirstOrDefault(rec => rec.SendType == 0)
: g.FirstOrDefault()
)
.ToList();

mr.sirwan
یک شنبه 25 مهر 1395, 00:23 صبح
آقای آفراد یک دنیا ممنونات فراوان :لبخند: کار میکنه مشکل ارجاع و رونوشت تکراری هم حل شد، بابت زحماتتون سپاسگذارم