[শুরুর তারিখ; শেষ তারিখ] সপ্তাহের দিনের তালিকার অন্তর অন্তর


18

আমার দুটি সিস্টেমের মধ্যে ডেটা রূপান্তর করতে হবে।

প্রথম সিস্টেম তারিখের একটি সরল তালিকা হিসাবে সময়সূচী সংরক্ষণ করে। তফসিলের অন্তর্ভুক্ত প্রতিটি তারিখ এক সারি। তারিখের ক্রমটিতে বিভিন্ন ফাঁক থাকতে পারে (সাপ্তাহিক ছুটি, পাবলিক ছুটির দিন এবং আরও বিরতি, সপ্তাহের কিছু দিন সময়সূচী থেকে বাদ দেওয়া যেতে পারে)। মোটেও কোনও ফাঁক থাকতে পারে না, এমনকি উইকএন্ডও অন্তর্ভুক্ত করা যায়। তফসিলটি 2 বছর পর্যন্ত দীর্ঘ হতে পারে। সাধারণত এটি কয়েক সপ্তাহ দীর্ঘ হয়।

এখানে একটি শিডিয়ুলের একটি সাধারণ উদাহরণ যা উইকএন্ড বাদে দুই সপ্তাহ বিস্তৃত রয়েছে (নীচের স্ক্রিপ্টে আরও জটিল উদাহরণ রয়েছে):

+----+------------+------------+---------+--------+
| ID | ContractID |     dt     | dowChar | dowInt |
+----+------------+------------+---------+--------+
| 10 |          1 | 2016-05-02 | Mon     |      2 |
| 11 |          1 | 2016-05-03 | Tue     |      3 |
| 12 |          1 | 2016-05-04 | Wed     |      4 |
| 13 |          1 | 2016-05-05 | Thu     |      5 |
| 14 |          1 | 2016-05-06 | Fri     |      6 |
| 15 |          1 | 2016-05-09 | Mon     |      2 |
| 16 |          1 | 2016-05-10 | Tue     |      3 |
| 17 |          1 | 2016-05-11 | Wed     |      4 |
| 18 |          1 | 2016-05-12 | Thu     |      5 |
| 19 |          1 | 2016-05-13 | Fri     |      6 |
+----+------------+------------+---------+--------+

IDস্বতন্ত্র, তবে এটি অবশ্যই ক্রমিক নয় (এটি প্রাথমিক কী)। তারিখগুলি প্রতিটি চুক্তির মধ্যে স্বতন্ত্র (এতে অনন্য সূচক থাকে (ContractID, dt))।

দ্বিতীয় সিস্টেমটি সময়সূচির অংশ হিসাবে সপ্তাহের দিনের তালিকার অন্তর হিসাবে সময়সূচী সংরক্ষণ করে। প্রতিটি বিরতি তার শুরু এবং শেষ তারিখগুলি (অন্তর্ভুক্ত) এবং সময়সূচির অন্তর্ভুক্ত সপ্তাহের দিনগুলির একটি তালিকা দ্বারা সংজ্ঞায়িত হয়। এই বিন্যাসে আপনি দক্ষতার সাথে সোম-বুড়ের মতো পুনরাবৃত্তিমূলক সাপ্তাহিক নিদর্শনগুলি সংজ্ঞায়িত করতে পারেন, তবে কোনও প্যাটার্নটি ব্যাহত হলে এটি ব্যথা হয়ে ওঠে, উদাহরণস্বরূপ প্রকাশ্য ছুটির দিন দ্বারা by

উপরের সাধারণ উদাহরণটি দেখতে কেমন হবে তা এখানে:

+------------+------------+------------+----------+----------------------+
| ContractID |  StartDT   |   EndDT    | DayCount |       WeekDays       |
+------------+------------+------------+----------+----------------------+
|          1 | 2016-05-02 | 2016-05-13 |       10 | Mon,Tue,Wed,Thu,Fri, |
+------------+------------+------------+----------+----------------------+

[StartDT;EndDT] একই চুক্তিভুক্ত অন্তরগুলি ওভারল্যাপ করা উচিত নয়।

আমার প্রথম সিস্টেমের ডেটা দ্বিতীয় সিস্টেম দ্বারা ব্যবহৃত ফর্ম্যাটে রূপান্তর করা দরকার। এই মুহুর্তে আমি একক প্রদত্ত চুক্তির জন্য C # তে ক্লায়েন্টের পক্ষে এটি সমাধান করছি, তবে আমি সার্ভারের মধ্যে বাল্ক প্রসেসিং এবং রফতানি / আমদানির জন্য সার্ভার সাইডে টি-এসকিউএল এ এটি করতে চাই। সম্ভবত, এটি সিএলআর ইউডিএফ ব্যবহার করে করা যেতে পারে, তবে এই পর্যায়ে আমি এসকিউএলসিএলআর ব্যবহার করতে পারি না।

এখানে চ্যালেঞ্জ হ'ল অন্তরগুলির তালিকা যতটা সম্ভব সংক্ষিপ্ত এবং মানব-বান্ধব করে তোলা।

উদাহরণস্বরূপ, এই সময়সূচী:

+-----+------------+------------+---------+--------+
| ID  | ContractID |     dt     | dowChar | dowInt |
+-----+------------+------------+---------+--------+
| 223 |          2 | 2016-05-05 | Thu     |      5 |
| 224 |          2 | 2016-05-06 | Fri     |      6 |
| 225 |          2 | 2016-05-09 | Mon     |      2 |
| 226 |          2 | 2016-05-10 | Tue     |      3 |
| 227 |          2 | 2016-05-11 | Wed     |      4 |
| 228 |          2 | 2016-05-12 | Thu     |      5 |
| 229 |          2 | 2016-05-13 | Fri     |      6 |
| 230 |          2 | 2016-05-16 | Mon     |      2 |
| 231 |          2 | 2016-05-17 | Tue     |      3 |
+-----+------------+------------+---------+--------+

এটি হয়ে উঠতে হবে:

+------------+------------+------------+----------+----------------------+
| ContractID |  StartDT   |   EndDT    | DayCount |       WeekDays       |
+------------+------------+------------+----------+----------------------+
|          2 | 2016-05-05 | 2016-05-17 |        9 | Mon,Tue,Wed,Thu,Fri, |
+------------+------------+------------+----------+----------------------+

,এটা না:

+------------+------------+------------+----------+----------------------+
| ContractID |  StartDT   |   EndDT    | DayCount |       WeekDays       |
+------------+------------+------------+----------+----------------------+
|          2 | 2016-05-05 | 2016-05-06 |        2 | Thu,Fri,             |
|          2 | 2016-05-09 | 2016-05-13 |        5 | Mon,Tue,Wed,Thu,Fri, |
|          2 | 2016-05-16 | 2016-05-17 |        2 | Mon,Tue,             |
+------------+------------+------------+----------+----------------------+

আমি gaps-and-islandsএই সমস্যার জন্য একটি পদ্ধতির প্রয়োগ করার চেষ্টা করেছি । আমি দুটি পাসে এটি করার চেষ্টা করেছি। প্রথম পাসে আমি টানা সহজ দিনগুলির দ্বীপগুলি খুঁজে পাই, অর্থাৎ দ্বীপের শেষ দিনগুলির ক্রমের কোনও ফাঁক, সে সপ্তাহান্তে, পাবলিক ছুটি হোক বা অন্য কিছু হোক। যেমন প্রতিটি পাওয়া দ্বীপ জন্য আমি স্বতন্ত্র একটি কমা দিয়ে পৃথক করা তালিকা তৈরী WeekDays। দ্বিতীয় পাসে আমি গ্রুপটি সপ্তাহের সংখ্যার ক্রমের ফাঁক বা the এর পরিবর্তনের দিকে তাকিয়ে আরও দ্বীপপুঞ্জ খুঁজে পেয়েছি WeekDays

এই পদ্ধতির সাথে প্রতিটি আংশিক সপ্তাহে উপরে বর্ণিত একটি অতিরিক্ত বিরতি হিসাবে শেষ হয়, কারণ সপ্তাহের সংখ্যাটি ক্রমাগত হলেও WeekDaysপরিবর্তন। এছাড়াও, এক সপ্তাহের মধ্যে নিয়মিত ফাঁক থাকতে পারে ( ContractID=3নমুনা ডেটা দেখুন, যার জন্য কেবল ডেটা রয়েছে Mon,Wed,Fri,) এবং এই পদ্ধতিটি এই জাতীয় সময়সূচীতে প্রতিটি দিনের জন্য আলাদা ব্যবধান তৈরি করতে পারে। উজ্জ্বল দিকে, সময়সূচীর কোনও ফাঁক না থাকলে এটি একটি ব্যবধান উত্পন্ন করে ( ContractID=7সপ্তাহান্তে অন্তর্ভুক্ত নমুনা ডেটাতে দেখুন) এবং সেই ক্ষেত্রে শুরু বা শেষ সপ্তাহটি আংশিক কিনা তা বিবেচ্য নয়।

আমি কী করছি তার আরও ভাল ধারণা পেতে দয়া করে নীচের স্ক্রিপ্টে অন্যান্য উদাহরণ দেখুন। আপনি দেখতে পারেন যে প্রায়শই সাপ্তাহিক ছুটির দিনগুলি বাদ থাকে তবে সপ্তাহের অন্য কোনও দিনও বাদ যায়। উদাহরণস্বরূপ কেবল 3 Mon, Wedএবং Friতফসিলের অংশ। এছাড়াও, সপ্তাহান্তে অন্তর্ভুক্ত করা যেতে পারে, উদাহরণস্বরূপ 7.. সমাধানটি সপ্তাহের সমস্ত দিনকে সমানভাবে বিবেচনা করা উচিত। সপ্তাহের যে কোনও দিন সময়সূচী থেকে অন্তর্ভুক্ত বা বাদ দেওয়া যেতে পারে।

অন্তর্ভুক্ত উত্পন্ন তালিকা প্রদত্ত সময়সূচী সঠিকভাবে বর্ণনা করে তা যাচাই করতে আপনি নিম্নলিখিত সিউডো কোডটি ব্যবহার করতে পারেন:

  • সমস্ত অন্তর মাধ্যমে লুপ
  • শুরু এবং শেষের তারিখগুলির মধ্যে (অন্তর্ভুক্ত) মধ্যবর্তী সমস্ত ক্যালেন্ডারের তারিখগুলির মধ্যবর্তী প্রতিটি ব্যবধান লুপের জন্য।
  • প্রতিটি তারিখের জন্য সপ্তাহের দিনটি তালিকাভুক্ত রয়েছে কিনা তা পরীক্ষা করে দেখুন WeekDays। যদি হ্যাঁ, তবে এই তারিখটি তফসিলের অন্তর্ভুক্ত।

আশা করা যায়, কোন ক্ষেত্রে নতুন অন্তর তৈরি করা উচিত তা এ বিষয়টি স্পষ্ট করে। উদাহরণস্বরূপ 4 এবং 5 এক সোমবার ( 2016-05-09) তফসিলের মাঝামাঝি থেকে সরানো হয় এবং এই জাতীয় সময়সূচী একটি একক বিরতি দ্বারা প্রতিনিধিত্ব করা যায় না। উদাহরণস্বরূপ 6 সময়সূচীতে একটি দীর্ঘ ব্যবধান রয়েছে, সুতরাং দুটি বিরতি প্রয়োজন।

ব্যবধানগুলি সময়সূচীতে সাপ্তাহিক নিদর্শনগুলি উপস্থাপন করে এবং যখন কোনও প্যাটার্নটি ব্যাহত / পরিবর্তিত হয় তখন নতুন বিরতি যুক্ত করতে হয়। উদাহরণস্বরূপ 11 প্রথম তিন সপ্তাহের একটি প্যাটার্ন থাকে Tue, তারপরে এই প্যাটার্নটি পরিবর্তিত হয় Thu। ফলস্বরূপ এ জাতীয় সময়সূচি বর্ণনা করতে আমাদের দুটি অন্তর প্রয়োজন।


আমি এই মুহূর্তে এসকিউএল সার্ভার 2008 ব্যবহার করছি, সুতরাং সমাধানটি এই সংস্করণে কাজ করা উচিত। যদি এসকিউএল সার্ভার ২০০৮ এর কোনও সমাধানটি পরবর্তী সংস্করণগুলির বৈশিষ্ট্যগুলি ব্যবহার করে সরল / উন্নত করা যায় তবে এটি একটি বোনাস, দয়া করে এটিও দেখান।

আমার কাছে একটি Calendarসারণী (তারিখের তালিকা) এবং Numbersটেবিল (1 থেকে শুরু হওয়া পূর্ণসংখ্যার তালিকা) রয়েছে, সুতরাং প্রয়োজনে সেগুলি ব্যবহার করা ঠিক আছে। অস্থায়ী সারণীগুলি তৈরি করা এবং বেশ কয়েকটি প্রশ্ন রয়েছে যা বেশ কয়েকটি পর্যায়ে ডেটা প্রক্রিয়া করে। একটি অ্যালগরিদম পর্যায়ে সংখ্যা ঠিক করতে হবে, কার্সার এবং সুস্পষ্ট WHILEলুপগুলি ঠিক নেই।


নমুনা ডেটা এবং প্রত্যাশিত ফলাফলের জন্য স্ক্রিপ্ট

-- @Src is sample data
-- @Dst is expected result

DECLARE @Src TABLE (ID int PRIMARY KEY, ContractID int, dt date, dowChar char(3), dowInt int);
INSERT INTO @Src (ID, ContractID, dt, dowChar, dowInt) VALUES

-- simple two weeks (without weekend)
(110, 1, '2016-05-02', 'Mon', 2),
(111, 1, '2016-05-03', 'Tue', 3),
(112, 1, '2016-05-04', 'Wed', 4),
(113, 1, '2016-05-05', 'Thu', 5),
(114, 1, '2016-05-06', 'Fri', 6),
(115, 1, '2016-05-09', 'Mon', 2),
(116, 1, '2016-05-10', 'Tue', 3),
(117, 1, '2016-05-11', 'Wed', 4),
(118, 1, '2016-05-12', 'Thu', 5),
(119, 1, '2016-05-13', 'Fri', 6),

-- a partial end of the week, the whole week, partial start of the week (without weekends)
(223, 2, '2016-05-05', 'Thu', 5),
(224, 2, '2016-05-06', 'Fri', 6),
(225, 2, '2016-05-09', 'Mon', 2),
(226, 2, '2016-05-10', 'Tue', 3),
(227, 2, '2016-05-11', 'Wed', 4),
(228, 2, '2016-05-12', 'Thu', 5),
(229, 2, '2016-05-13', 'Fri', 6),
(230, 2, '2016-05-16', 'Mon', 2),
(231, 2, '2016-05-17', 'Tue', 3),

-- only Mon, Wed, Fri are included across two weeks plus partial third week
(310, 3, '2016-05-02', 'Mon', 2),
(311, 3, '2016-05-04', 'Wed', 4),
(314, 3, '2016-05-06', 'Fri', 6),
(315, 3, '2016-05-09', 'Mon', 2),
(317, 3, '2016-05-11', 'Wed', 4),
(319, 3, '2016-05-13', 'Fri', 6),
(330, 3, '2016-05-16', 'Mon', 2),

-- a whole week (without weekend), in the second week Mon is not included
(410, 4, '2016-05-02', 'Mon', 2),
(411, 4, '2016-05-03', 'Tue', 3),
(412, 4, '2016-05-04', 'Wed', 4),
(413, 4, '2016-05-05', 'Thu', 5),
(414, 4, '2016-05-06', 'Fri', 6),
(416, 4, '2016-05-10', 'Tue', 3),
(417, 4, '2016-05-11', 'Wed', 4),
(418, 4, '2016-05-12', 'Thu', 5),
(419, 4, '2016-05-13', 'Fri', 6),

-- three weeks, but without Mon in the second week (no weekends)
(510, 5, '2016-05-02', 'Mon', 2),
(511, 5, '2016-05-03', 'Tue', 3),
(512, 5, '2016-05-04', 'Wed', 4),
(513, 5, '2016-05-05', 'Thu', 5),
(514, 5, '2016-05-06', 'Fri', 6),
(516, 5, '2016-05-10', 'Tue', 3),
(517, 5, '2016-05-11', 'Wed', 4),
(518, 5, '2016-05-12', 'Thu', 5),
(519, 5, '2016-05-13', 'Fri', 6),
(520, 5, '2016-05-16', 'Mon', 2),
(521, 5, '2016-05-17', 'Tue', 3),
(522, 5, '2016-05-18', 'Wed', 4),
(523, 5, '2016-05-19', 'Thu', 5),
(524, 5, '2016-05-20', 'Fri', 6),

-- long gap between two intervals
(623, 6, '2016-05-05', 'Thu', 5),
(624, 6, '2016-05-06', 'Fri', 6),
(625, 6, '2016-05-09', 'Mon', 2),
(626, 6, '2016-05-10', 'Tue', 3),
(627, 6, '2016-05-11', 'Wed', 4),
(628, 6, '2016-05-12', 'Thu', 5),
(629, 6, '2016-05-13', 'Fri', 6),
(630, 6, '2016-05-16', 'Mon', 2),
(631, 6, '2016-05-17', 'Tue', 3),
(645, 6, '2016-06-06', 'Mon', 2),
(646, 6, '2016-06-07', 'Tue', 3),
(647, 6, '2016-06-08', 'Wed', 4),
(648, 6, '2016-06-09', 'Thu', 5),
(649, 6, '2016-06-10', 'Fri', 6),
(655, 6, '2016-06-13', 'Mon', 2),
(656, 6, '2016-06-14', 'Tue', 3),
(657, 6, '2016-06-15', 'Wed', 4),
(658, 6, '2016-06-16', 'Thu', 5),
(659, 6, '2016-06-17', 'Fri', 6),

-- two weeks, no gaps between days at all, even weekends are included
(710, 7, '2016-05-02', 'Mon', 2),
(711, 7, '2016-05-03', 'Tue', 3),
(712, 7, '2016-05-04', 'Wed', 4),
(713, 7, '2016-05-05', 'Thu', 5),
(714, 7, '2016-05-06', 'Fri', 6),
(715, 7, '2016-05-07', 'Sat', 7),
(716, 7, '2016-05-08', 'Sun', 1),
(725, 7, '2016-05-09', 'Mon', 2),
(726, 7, '2016-05-10', 'Tue', 3),
(727, 7, '2016-05-11', 'Wed', 4),
(728, 7, '2016-05-12', 'Thu', 5),
(729, 7, '2016-05-13', 'Fri', 6),

-- no gaps between days at all, even weekends are included, with partial weeks
(805, 8, '2016-04-30', 'Sat', 7),
(806, 8, '2016-05-01', 'Sun', 1),
(810, 8, '2016-05-02', 'Mon', 2),
(811, 8, '2016-05-03', 'Tue', 3),
(812, 8, '2016-05-04', 'Wed', 4),
(813, 8, '2016-05-05', 'Thu', 5),
(814, 8, '2016-05-06', 'Fri', 6),
(815, 8, '2016-05-07', 'Sat', 7),
(816, 8, '2016-05-08', 'Sun', 1),
(825, 8, '2016-05-09', 'Mon', 2),
(826, 8, '2016-05-10', 'Tue', 3),
(827, 8, '2016-05-11', 'Wed', 4),
(828, 8, '2016-05-12', 'Thu', 5),
(829, 8, '2016-05-13', 'Fri', 6),
(830, 8, '2016-05-14', 'Sat', 7),

-- only Mon-Wed included, two weeks plus partial third week
(910, 9, '2016-05-02', 'Mon', 2),
(911, 9, '2016-05-03', 'Tue', 3),
(912, 9, '2016-05-04', 'Wed', 4),
(915, 9, '2016-05-09', 'Mon', 2),
(916, 9, '2016-05-10', 'Tue', 3),
(917, 9, '2016-05-11', 'Wed', 4),
(930, 9, '2016-05-16', 'Mon', 2),
(931, 9, '2016-05-17', 'Tue', 3),

-- only Thu-Sun included, three weeks
(1013,10,'2016-05-05', 'Thu', 5),
(1014,10,'2016-05-06', 'Fri', 6),
(1015,10,'2016-05-07', 'Sat', 7),
(1016,10,'2016-05-08', 'Sun', 1),
(1018,10,'2016-05-12', 'Thu', 5),
(1019,10,'2016-05-13', 'Fri', 6),
(1020,10,'2016-05-14', 'Sat', 7),
(1021,10,'2016-05-15', 'Sun', 1),
(1023,10,'2016-05-19', 'Thu', 5),
(1024,10,'2016-05-20', 'Fri', 6),
(1025,10,'2016-05-21', 'Sat', 7),
(1026,10,'2016-05-22', 'Sun', 1),

-- only Tue for first three weeks, then only Thu for the next three weeks
(1111,11,'2016-05-03', 'Tue', 3),
(1116,11,'2016-05-10', 'Tue', 3),
(1131,11,'2016-05-17', 'Tue', 3),
(1123,11,'2016-05-19', 'Thu', 5),
(1124,11,'2016-05-26', 'Thu', 5),
(1125,11,'2016-06-02', 'Thu', 5),

-- one week, then one week gap, then one week
(1210,12,'2016-05-02', 'Mon', 2),
(1211,12,'2016-05-03', 'Tue', 3),
(1212,12,'2016-05-04', 'Wed', 4),
(1213,12,'2016-05-05', 'Thu', 5),
(1214,12,'2016-05-06', 'Fri', 6),
(1215,12,'2016-05-16', 'Mon', 2),
(1216,12,'2016-05-17', 'Tue', 3),
(1217,12,'2016-05-18', 'Wed', 4),
(1218,12,'2016-05-19', 'Thu', 5),
(1219,12,'2016-05-20', 'Fri', 6);

SELECT ID, ContractID, dt, dowChar, dowInt
FROM @Src
ORDER BY ContractID, dt;


DECLARE @Dst TABLE (ContractID int, StartDT date, EndDT date, DayCount int, WeekDays varchar(255));
INSERT INTO @Dst (ContractID, StartDT, EndDT, DayCount, WeekDays) VALUES
(1, '2016-05-02', '2016-05-13', 10, 'Mon,Tue,Wed,Thu,Fri,'),
(2, '2016-05-05', '2016-05-17',  9, 'Mon,Tue,Wed,Thu,Fri,'),
(3, '2016-05-02', '2016-05-16',  7, 'Mon,Wed,Fri,'),
(4, '2016-05-02', '2016-05-06',  5, 'Mon,Tue,Wed,Thu,Fri,'),
(4, '2016-05-10', '2016-05-13',  4, 'Tue,Wed,Thu,Fri,'),
(5, '2016-05-02', '2016-05-06',  5, 'Mon,Tue,Wed,Thu,Fri,'),
(5, '2016-05-10', '2016-05-20',  9, 'Mon,Tue,Wed,Thu,Fri,'),
(6, '2016-05-05', '2016-05-17',  9, 'Mon,Tue,Wed,Thu,Fri,'),
(6, '2016-06-06', '2016-06-17', 10, 'Mon,Tue,Wed,Thu,Fri,'),
(7, '2016-05-02', '2016-05-13', 12, 'Sun,Mon,Tue,Wed,Thu,Fri,Sat,'),
(8, '2016-04-30', '2016-05-14', 15, 'Sun,Mon,Tue,Wed,Thu,Fri,Sat,'),
(9, '2016-05-02', '2016-05-17',  8, 'Mon,Tue,Wed,'),
(10,'2016-05-05', '2016-05-22', 12, 'Sun,Thu,Fri,Sat,'),
(11,'2016-05-03', '2016-05-17',  3, 'Tue,'),
(11,'2016-05-19', '2016-06-02',  3, 'Thu,'),
(12,'2016-05-02', '2016-05-06',  5, 'Mon,Tue,Wed,Thu,Fri,'),
(12,'2016-05-16', '2016-05-20',  5, 'Mon,Tue,Wed,Thu,Fri,');

SELECT ContractID, StartDT, EndDT, DayCount, WeekDays
FROM @Dst
ORDER BY ContractID, StartDT;

উত্তরের তুলনা

বাস্তব টেবিল @Srcরয়েছে 403,555সঙ্গে সারি 15,857স্বতন্ত্র ContractIDs। সমস্ত উত্তর সঠিক ফলাফল দেয় (কমপক্ষে আমার ডেটার জন্য) এবং এগুলির সমস্ত যুক্তিসঙ্গত দ্রুত, তবে তারা অনুকূলতার মধ্যে পৃথক। কম বিরতি উত্পন্ন, তত ভাল। আমি কৌতূহল জন্য রান সময় অন্তর্ভুক্ত। মূল ফোকাসটি সঠিক এবং সর্বোত্তম ফলাফল, গতি নয় (যদি এটি খুব বেশি সময় না নেয় - আমি 10 মিনিটের পরে জিগি ক্রুয়েল্টিফ্রি জাইটিজিস্টার দ্বারা পুনঃবিবেচনাযোগ্য জিজ্ঞাসাটি বন্ধ করে দিয়েছি)।

+--------------------------------------------------------+-----------+---------+
|                         Answer                         | Intervals | Seconds |
+--------------------------------------------------------+-----------+---------+
| Ziggy Crueltyfree Zeitgeister                          |     25751 |    7.88 |
| While loop                                             |           |         |
|                                                        |           |         |
| Ziggy Crueltyfree Zeitgeister                          |     25751 |    8.27 |
| Recursive                                              |           |         |
|                                                        |           |         |
| Michael Green                                          |     25751 |   22.63 |
| Recursive                                              |           |         |
|                                                        |           |         |
| Geoff Patterson                                        |     26670 |    4.79 |
| Weekly gaps-and-islands with merging of partial weeks  |           |         |
|                                                        |           |         |
| Vladimir Baranov                                       |     34560 |    4.03 |
| Daily, then weekly gaps-and-islands                    |           |         |
|                                                        |           |         |
| Mikael Eriksson                                        |     35840 |    0.65 |
| Weekly gaps-and-islands                                |           |         |
+--------------------------------------------------------+-----------+---------+
| Vladimir Baranov                                       |     25751 |  121.51 |
| Cursor                                                 |           |         |
+--------------------------------------------------------+-----------+---------+

(11,'2016-05-03', '2016-05-17', 3, 'Tue,'), (11,'2016-05-19', '2016-06-02', 3, 'Thu,');@ ডিস্টে এক সারি থাকা উচিত নয় Tue, Thu,?
কিন শাহ

@ কিন, উদাহরণ ১১-তে দুটি (অন্তত) দুটি অন্তর (দুটি সারি @Dst) থাকতে হবে। তফসিলের প্রথম দুই সপ্তাহের কেবলমাত্র Tueতাই আপনি WeekDays=Tue,Thu,এই সপ্তাহগুলির জন্য থাকতে পারবেন না । তফসিলের শেষ দুই সপ্তাহের মধ্যে কেবলমাত্র Thuতাই আপনি WeekDays=Tue,Thu,এই সপ্তাহগুলির জন্য আর থাকতে পারবেন না । এর জন্য উপ-অনুকূল সমাধানটি তিনটি সারি হবে: কেবল Tueপ্রথম দুই সপ্তাহের Tue,Thu,জন্য, তৃতীয় সপ্তাহে উভয়ই রয়েছে Tueএবং Thuতারপরে কেবল Thuশেষ দুই সপ্তাহের জন্য।
ভ্লাদিমির বারানভ

1
আপনি কি দয়া করে অ্যালগরিদমটি ব্যাখ্যা করতে পারেন যার মাধ্যমে চুক্তি 11 দুটি অন্তরগুলিতে "অনুকূলভাবে" বিভক্ত হয়। আপনি কি সি # অ্যাপ্লিকেশনটিতে এটি অর্জন করেছেন? কিভাবে?
মাইকেল গ্রিন

@ মিশেলগ্রিন, দুঃখিত আমি এর আগে উত্তর দিতে পারিনি। হ্যাঁ, সি # কোড চুক্তি 11টিকে দুটি বিরতিতে বিভক্ত করে। রুক্ষ অ্যালগরিদম: আমি নির্ধারিত তারিখগুলি এক-এক করে লুপ করি, বিরতি শুরুর পর থেকে সপ্তাহের কোন দিনগুলিতে আমি মুখোমুখি হয়েছি তা নোট করে এবং আমার একটি নতুন বিরতি শুরু করা উচিত কিনা তা নির্ধারণ করে: যদি ContractIDপরিবর্তন হয়, অন্তর নির্ধারিত দিনের তালিকায় কোনও ফাঁক থাকলে। দিনের বাইরে গিয়ে নতুন সপ্তাহের দিনটি আগে দেখা যায়নি।
ভ্লাদিমির বারানভ

@ মিশেলগ্রিন, আমি আমার সি # কোডটিকে কার্সার-ভিত্তিক অ্যালগরিদমতে রূপান্তর করেছি, এটি দেখতে কীভাবে এটি বাস্তব উপাত্তের অন্যান্য সমাধানগুলির সাথে তুলনা করে। আমি আমার উত্তরে উত্স কোড যুক্ত করেছি এবং প্রশ্নের সংক্ষিপ্ত সারণিতে ফলাফলগুলি।
ভ্লাদিমির বরানভ

উত্তর:


6

এটি একটি পুনরাবৃত্ত সিটিই ব্যবহার করে। এর ফলাফল প্রশ্নের উদাহরণের সাথে অভিন্ন । এটি একটি দুঃস্বপ্নের সাথে সামনে এসেছিল ... কোডটিতে এর সংশ্লেষিত যুক্তি দিয়ে স্বাচ্ছন্দ্যে মন্তব্যগুলি অন্তর্ভুক্ত করা হয়েছে।

SET DATEFIRST 1 -- Make Monday weekday=1

DECLARE @Ranked TABLE (RowID int NOT NULL IDENTITY PRIMARY KEY,                   -- Incremental uninterrupted sequence in the right order
                       ID int NOT NULL UNIQUE, ContractID int NOT NULL, dt date,  -- Original relevant values (ID is not really necessary)
                       WeekNo int NOT NULL, dowBit int NOT NULL);                 -- Useful to find gaps in days or weeks
INSERT INTO @Ranked
SELECT ID, ContractID, dt,
       DATEDIFF(WEEK, '1900-01-01', DATEADD(DAY, 1-DATEPART(dw, dt), dt)) AS WeekNo,
       POWER(2, DATEPART(dw, dt)-1) AS dowBit
FROM @Src
ORDER BY ContractID, WeekNo, dowBit

/*
Each evaluated date makes part of the carried sequence if:
  - this is not a new contract, and
    - sequence started this week, or
    - same day last week was part of the sequence, or
    - sequence started last week and today is a lower day than the accumulated weekdays list
  - and there are no sequence gaps since previous day
(otherwise it does not make part of the old sequence, so it starts a new one) */

DECLARE @RankedRanges TABLE (RowID int NOT NULL PRIMARY KEY, WeekDays int NOT NULL, StartRowID int NULL);

WITH WeeksCTE AS -- Needed for building the sequence gradually, and comparing the carried sequence (and previous day) with a current evaluated day
( 
    SELECT RowID, ContractID, dowBit, WeekNo, RowID AS StartRowID, WeekNo AS StartWN, dowBit AS WeekDays, dowBit AS StartWeekDays
    FROM @Ranked
    WHERE RowID = 1 
    UNION ALL
    SELECT RowID, ContractID, dowBit, WeekNo, StartRowID,
           CASE WHEN StartRowID IS NULL THEN StartWN ELSE WeekNo END AS WeekNo,
           CASE WHEN StartRowID IS NULL THEN WeekDays | dowBit ELSE dowBit END AS WeekDays,
           CASE WHEN StartRowID IS NOT NULL THEN dowBit WHEN WeekNo = StartWN THEN StartWeekDays | dowBit ELSE StartWeekDays END AS StartWeekDays
    FROM (
        SELECT w.*, pre.StartWN, pre.WeekDays, pre.StartWeekDays,
               CASE WHEN w.ContractID <> pre.ContractID OR     -- New contract always break the sequence
                         NOT (w.WeekNo = pre.StartWN OR        -- Same week as a new sequence always keeps the sequence
                              w.dowBit & pre.WeekDays > 0 OR   -- Days in the sequence keep the sequence (provided there are no gaps, checked later)
                              (w.WeekNo = pre.StartWN+1 AND (w.dowBit-1) & pre.StartWeekDays = 0)) OR -- Days in the second week when less than a week passed since the sequence started remain in sequence
                         (w.WeekNo > pre.StartWN AND -- look for gap after initial week
                          w.WeekNo > pre.WeekNo+1 OR -- look for full-week gaps
                          (w.WeekNo = pre.WeekNo AND                            -- when same week as previous day,
                           ((w.dowBit-1) ^ (pre.dowBit*2-1)) & pre.WeekDays > 0 -- days between this and previous weekdays, compared to current series
                          ) OR
                          (w.WeekNo > pre.WeekNo AND                                   -- when following week of previous day,
                           ((-1 ^ (pre.dowBit*2-1)) | (w.dowBit-1)) & pre.WeekDays > 0 -- days between this and previous weekdays, compared to current series
                          )) THEN w.RowID END AS StartRowID
        FROM WeeksCTE pre
        JOIN @Ranked w ON (w.RowID = pre.RowID + 1)
        ) w
) 
INSERT INTO @RankedRanges -- days sequence and starting point of each sequence
SELECT RowID, WeekDays, StartRowID
--SELECT *
FROM WeeksCTE
OPTION (MAXRECURSION 0)

--SELECT * FROM @RankedRanges

DECLARE @Ranges TABLE (RowNo int NOT NULL IDENTITY PRIMARY KEY, RowID int NOT NULL);

INSERT INTO @Ranges       -- @RankedRanges filtered only by start of each range, with numbered rows to easily find the end of each range
SELECT StartRowID
FROM @RankedRanges
WHERE StartRowID IS NOT NULL
ORDER BY 1

-- Final result putting everything together
SELECT rs.ContractID, rs.dt AS StartDT, re.dt AS EndDT, re.RowID-rs.RowID+1 AS DayCount,
       CASE WHEN rr.WeekDays & 64 > 0 THEN 'Sun,' ELSE '' END +
       CASE WHEN rr.WeekDays & 1 > 0 THEN 'Mon,' ELSE '' END +
       CASE WHEN rr.WeekDays & 2 > 0 THEN 'Tue,' ELSE '' END +
       CASE WHEN rr.WeekDays & 4 > 0 THEN 'Wed,' ELSE '' END +
       CASE WHEN rr.WeekDays & 8 > 0 THEN 'Thu,' ELSE '' END +
       CASE WHEN rr.WeekDays & 16 > 0 THEN 'Fri,' ELSE '' END +
       CASE WHEN rr.WeekDays & 32 > 0 THEN 'Sat,' ELSE '' END AS WeekDays
FROM (
    SELECT r.RowID AS StartRowID, COALESCE(pos.RowID-1, (SELECT MAX(RowID) FROM @Ranked)) AS EndRowID
    FROM @Ranges r
    LEFT JOIN @Ranges pos ON (pos.RowNo = r.RowNo + 1)
    ) g
JOIN @Ranked rs ON (rs.RowID = g.StartRowID)
JOIN @Ranked re ON (re.RowID = g.EndRowID)
JOIN @RankedRanges rr ON (rr.RowID = re.RowID)


অন্য কৌশল

এটি আগেরটির তুলনায় দ্রুততর হওয়া উচিত কারণ এটি এসকিউএল সার্ভার ২০০৮ সালে স্লো সীমিত পুনরাবৃত্ত সিটিইয়ের উপর নির্ভর করে না, যদিও এটি কম-বেশি একই কৌশল প্রয়োগ করে।

একটি WHILEলুপ রয়েছে (আমি এড়াতে কোনও উপায় অবলম্বন করতে পারিনি) তবে হ্রাস সংখ্যক পুনরাবৃত্তির জন্য যায় (যে কোনও চুক্তিতে সিকোয়েন্সগুলির সর্বাধিক সংখ্যা (বিয়োগ এক))।

এটি একটি সহজ কৌশল এবং এটি এক সপ্তাহের চেয়ে কম বা দীর্ঘতর সিক্যুয়েন্সের জন্য ব্যবহার করা যেতে পারে (অন্য কোনও সংখ্যার জন্য ধ্রুবক 7 এর কোনও সংস্থার পরিবর্তে এবং dowBitএর DayNoপরিবর্তে মোডুলাস এক্স থেকে গণনা করা হয়েছে DATEPART(wk)) এবং 32 পর্যন্ত।

SET DATEFIRST 1 -- Make Monday weekday=1

-- Get the minimum information needed to calculate sequences
DECLARE @Days TABLE (ContractID int NOT NULL, dt date, DayNo int NOT NULL, dowBit int NOT NULL, PRIMARY KEY (ContractID, DayNo));
INSERT INTO @Days
SELECT ContractID, dt, CAST(CAST(dt AS datetime) AS int) AS DayNo, POWER(2, DATEPART(dw, dt)-1) AS dowBit
FROM @Src

DECLARE @RangeStartFirstPass TABLE (ContractID int NOT NULL, DayNo int NOT NULL, PRIMARY KEY (ContractID, DayNo))

-- Calculate, from the above list, which days are not present in the previous 7
INSERT INTO @RangeStartFirstPass
SELECT r.ContractID, r.DayNo
FROM @Days r
LEFT JOIN @Days pr ON (pr.ContractID = r.ContractID AND pr.DayNo BETWEEN r.DayNo-7 AND r.DayNo-1) -- Last 7 days
GROUP BY r.ContractID, r.DayNo, r.dowBit
HAVING r.dowBit & COALESCE(SUM(pr.dowBit), 0) = 0

-- Update the previous list with all days that occur right after a missing day
INSERT INTO @RangeStartFirstPass
SELECT *
FROM (
    SELECT DISTINCT ContractID, (SELECT MIN(DayNo) FROM @Days WHERE ContractID = d.ContractID AND DayNo > d.DayNo + 7) AS DayNo
    FROM @Days d
    WHERE NOT EXISTS (SELECT 1 FROM @Days WHERE ContractID = d.ContractID AND DayNo = d.DayNo + 7)
    ) d
WHERE DayNo IS NOT NULL AND
      NOT EXISTS (SELECT 1 FROM @RangeStartFirstPass WHERE ContractID = d.ContractID AND DayNo = d.DayNo)

DECLARE @RangeStart TABLE (ContractID int NOT NULL, DayNo int NOT NULL, PRIMARY KEY (ContractID, DayNo));

-- Fetch the first sequence for each contract
INSERT INTO @RangeStart
SELECT ContractID, MIN(DayNo)
FROM @RangeStartFirstPass
GROUP BY ContractID

-- Add to the list above the next sequence for each contract, until all are added
-- (ensure no sequence is added with less than 7 days)
WHILE @@ROWCOUNT > 0
  INSERT INTO @RangeStart
  SELECT f.ContractID, MIN(f.DayNo)
  FROM (SELECT ContractID, MAX(DayNo) AS DayNo FROM @RangeStart GROUP BY ContractID) s
  JOIN @RangeStartFirstPass f ON (f.ContractID = s.ContractID AND f.DayNo > s.DayNo + 7)
  GROUP BY f.ContractID

-- Summarise results
SELECT ContractID, StartDT, EndDT, DayCount,
       CASE WHEN WeekDays & 64 > 0 THEN 'Sun,' ELSE '' END +
       CASE WHEN WeekDays & 1 > 0 THEN 'Mon,' ELSE '' END +
       CASE WHEN WeekDays & 2 > 0 THEN 'Tue,' ELSE '' END +
       CASE WHEN WeekDays & 4 > 0 THEN 'Wed,' ELSE '' END +
       CASE WHEN WeekDays & 8 > 0 THEN 'Thu,' ELSE '' END +
       CASE WHEN WeekDays & 16 > 0 THEN 'Fri,' ELSE '' END +
       CASE WHEN WeekDays & 32 > 0 THEN 'Sat,' ELSE '' END AS WeekDays
FROM (
    SELECT r.ContractID,
           MIN(d.dt) AS StartDT,
           MAX(d.dt) AS EndDT,
           COUNT(*) AS DayCount,
           SUM(DISTINCT d.dowBit) AS WeekDays
    FROM (SELECT *, COALESCE((SELECT MIN(DayNo) FROM @RangeStart WHERE ContractID = rs.ContractID AND DayNo > rs.DayNo), 999999) AS DayEnd FROM @RangeStart rs) r
    JOIN @Days d ON (d.ContractID = r.ContractID AND d.DayNo BETWEEN r.DayNo AND r.DayEnd-1)
    GROUP BY r.ContractID, r.DayNo
    ) d
ORDER BY ContractID, StartDT

@ ভ্লাদিমিরবারানোভ আমি একটি নতুন কৌশল যুক্ত করেছি, যা আরও দ্রুত হওয়া উচিত। আপনার আসল ডেটা দিয়ে এটি কীভাবে রেট দেয় তা আমাকে জানতে দিন!
জিগি ক্রুয়েলটিফ্রি জাইটিজিস্টার

2
@ জিগি ক্রুয়েল্টিফ্রিজেটজিস্টার, আমি আপনার শেষ সমাধানটি যাচাই করেছিলাম এবং এটিকে প্রশ্নের উত্তরগুলির তালিকায় যুক্ত করেছি। এটি সঠিক ফলাফল এবং পুনরাবৃত্ত সিটিই হিসাবে একই সংখ্যার অন্তর অন্তর্ভুক্ত করে এবং এর গতিও খুব কাছাকাছি। আমি যেমন বলেছি, গতি ততক্ষণ সমালোচিত নয় যতক্ষণ না এটি যুক্তিসঙ্গত। 1 সেকেন্ড বা 10 সেকেন্ড আমার পক্ষে সত্যিই গুরুত্বপূর্ণ নয়।
ভ্লাদিমির বড়ানভ

অন্যান্য উত্তরগুলিও দুর্দান্ত এবং দরকারী I আমি এই উত্তরটি বেছে নিয়েছি, কারণ আমি যখন অনুগ্রহ শুরু করেছি তখন আমি পুনরাবৃত্ত সিটিই সম্পর্কে ভাবিনি এবং এই উত্তরটিই এটিই প্রথম প্রস্তাব করেছিল এবং এর কার্যক্ষম সমাধান ছিল। কড়া কথায় বলতে গেলে, রিকার্সিভ সিটিই কোনও সেট-ভিত্তিক সমাধান নয়, তবে এটি সর্বোত্তম ফলাফল দেয় এবং যুক্তিসঙ্গত দ্রুত। @ জিফপ্যাটারসনের একটি উত্তর দুর্দান্ত, তবে কম অনুকূল ফলাফল দেয় এবং খোলামেলাভাবে বলতে গেলে, খুব জটিল।
ভ্লাদিমির বারানভ

5

আপনি যা খুঁজছেন ঠিক তা নয় তবে সম্ভবত এটি আপনার আগ্রহী হতে পারে।

ক্যোরিয়াম প্রতি সপ্তাহে ব্যবহৃত দিনগুলির জন্য কমা দ্বারা পৃথক স্ট্রিং সহ সপ্তাহগুলি তৈরি করে। তখনই পরপর সপ্তাহ দ্বীপপুঞ্জের একই প্যাটার্ন ব্যবহার করে খুঁজে বের করে Weekdays

with Weeks as
(
  select T.*,
         row_number() over(partition by T.ContractID, T.WeekDays order by T.WeekNumber) as rn
  from (
       select S1.ContractID,
              min(S1.dt) as StartDT,
              max(S1.dt) as EndDT,
              datediff(day, 0, S1.dt) / 7 as WeekNumber, -- Number of weeks since '1900-01-01 (a monday)'
              count(*) as DayCount,
              stuff((
                    select ','+S2.dowChar
                    from @Src as S2
                    where S2.ContractID = S1.ContractID and
                          S2.dt between min(S1.dt) and max(S1.dt)
                    order by S2.dt
                    for xml path('')
                    ), 1, 1, '') as WeekDays
       from @Src as S1
       group by S1.ContractID, 
                datediff(day, 0, S1.dt) / 7
       ) as T
)
select W.ContractID,
       min(W.StartDT) as StartDT,
       max(W.EndDT) as EndDT,
       count(*) * W.DayCount as DayCount,
       W.WeekDays
from Weeks as W
group by W.ContractID,
         W.WeekDays,
         W.DayCount,
         W.rn - W.WeekNumber
order by W.ContractID,
         min(W.WeekNumber);

ফলাফল:

ContractID  StartDT    EndDT      DayCount    WeekDays
----------- ---------- ---------- ----------- -----------------------------
1           2016-05-02 2016-05-13 10          Mon,Tue,Wed,Thu,Fri
2           2016-05-05 2016-05-06 2           Thu,Fri
2           2016-05-09 2016-05-13 5           Mon,Tue,Wed,Thu,Fri
2           2016-05-16 2016-05-17 2           Mon,Tue
3           2016-05-02 2016-05-13 6           Mon,Wed,Fri
3           2016-05-16 2016-05-16 1           Mon
4           2016-05-02 2016-05-06 5           Mon,Tue,Wed,Thu,Fri
4           2016-05-10 2016-05-13 4           Tue,Wed,Thu,Fri
5           2016-05-02 2016-05-06 5           Mon,Tue,Wed,Thu,Fri
5           2016-05-10 2016-05-13 4           Tue,Wed,Thu,Fri
5           2016-05-16 2016-05-20 5           Mon,Tue,Wed,Thu,Fri
6           2016-05-05 2016-05-06 2           Thu,Fri
6           2016-05-09 2016-05-13 5           Mon,Tue,Wed,Thu,Fri
6           2016-05-16 2016-05-17 2           Mon,Tue
6           2016-06-06 2016-06-17 10          Mon,Tue,Wed,Thu,Fri
7           2016-05-02 2016-05-08 7           Mon,Tue,Wed,Thu,Fri,Sat,Sun
7           2016-05-09 2016-05-13 5           Mon,Tue,Wed,Thu,Fri
8           2016-04-30 2016-05-01 2           Sat,Sun
8           2016-05-02 2016-05-08 7           Mon,Tue,Wed,Thu,Fri,Sat,Sun
8           2016-05-09 2016-05-14 6           Mon,Tue,Wed,Thu,Fri,Sat
9           2016-05-02 2016-05-11 6           Mon,Tue,Wed
9           2016-05-16 2016-05-17 2           Mon,Tue
10          2016-05-05 2016-05-22 12          Thu,Fri,Sat,Sun
11          2016-05-03 2016-05-10 2           Tue
11          2016-05-17 2016-05-19 2           Tue,Thu
11          2016-05-26 2016-06-02 2           Thu

ContractID = 2ফলাফলের মধ্যে পার্থক্যটি কী আপনি চান তার সাথে তুলনা করা দেখায়। প্রথম এবং শেষ সপ্তাহ পৃথক সময় হিসাবে আলাদা হিসাবে বিবেচনা করা হবে WeekDays


আমার এই ধারণাটি ছিল, তবে চেষ্টা করার সুযোগ হয়নি। একটি কাজের জিজ্ঞাসা সরবরাহ করার জন্য আপনাকে ধন্যবাদ। আমি পছন্দ করি কীভাবে এটি আরও কাঠামোগত ফলাফল দেয়। সপ্তাহগুলিতে ডেটা গ্রুপিংয়ের সময় নীচের দিকটি নমনীয়তা হ্রাস করা হয় (একটি সাধারণ দৈনিক ফাঁক-এবং-দ্বীপপুঞ্জের কাছে উদাহরণের উদাহরণ 7 এবং 8 এক বিরতিতে ভেঙে দেওয়া হবে), তবে এটি একই সাথে উজ্জ্বল দিক - আমরা এর জটিলতা হ্রাস করি সমস্যাটি. সুতরাং, এই পদ্ধতির সাথে সবচেয়ে বড় সমস্যাটি সময়সূচির শুরু এবং শেষের আংশিক সপ্তাহ। এই জাতীয় আংশিক সপ্তাহগুলি একটি অতিরিক্ত বিরতি জেনারেট করে ...
ভ্লাদিমির বারানভ

আপনি এই আংশিক সপ্তাহগুলিকে মূল তফসিলের সাথে যুক্ত / গোষ্ঠী / মার্জ করার কোনও উপায় সম্পর্কে ভাবতে পারেন? আমি এই পর্যায়ে শুধুমাত্র খুব অস্পষ্ট ধারণা আছে। আমরা যদি আংশিক সপ্তাহগুলিকে সঠিকভাবে মার্জ করার কোনও উপায় খুঁজে পাই তবে শেষ ফলাফলটি অনুকূলের খুব কাছাকাছি হবে।
ভ্লাদিমির বরানভ

@ ভ্লাদিমিরবারানোভ কীভাবে এটি সম্পন্ন হবে তা নিশ্চিত নয়। কিছু মনে এলে উত্তরটি আপডেট করব।
মিকেল এরিকসন

আমার অস্পষ্ট ধারণাটি হ'ল: সপ্তাহে মাত্র 7 দিন থাকে, তাই WeekDays7-বিট সংখ্যাও। শুধুমাত্র 128 টি সংমিশ্রণ। কেবলমাত্র 128 * 128 = 16384 সম্ভাব্য জোড়া রয়েছে। সমস্ত সম্ভাব্য জোড়া দিয়ে একটি টেম্প টেবিল তৈরি করুন, তারপরে একটি সেট-ভিত্তিক অ্যালগরিদম বের করুন যা চিহ্নিত করবে কোন জোড়গুলিকে একীভূত করা যেতে পারে: এক সপ্তাহের একটি প্যাটার্ন পরের সপ্তাহের একটি প্যাটার্ন দ্বারা "আচ্ছাদিত"। বর্তমান সাপ্তাহিক ফলাফলটিতে স্ব-যোগদান করুন (যেহেতু LAG২০০৮ সালে কোনও ফলাফল নেই ) এবং কোন জোড়গুলিকে একত্রীকরণের সিদ্ধান্ত নেওয়ার জন্য সেই টেম্প টেবিলটি ব্যবহার করুন ... এই ধারণার কোনও যোগ্যতা আছে কিনা তা নিশ্চিত নন।
ভ্লাদিমির বড়ানভ

5

আমি একটি পদ্ধতির সাথে শেষ করেছি যা এই ক্ষেত্রে সর্বোত্তম সমাধান দেয় এবং আমি মনে করি সাধারণভাবে ভাল করবে। সমাধানটি বেশ দীর্ঘ, তবে এটি দেখার জন্য আকর্ষণীয় হবে যে অন্য কারওর কাছে আরও সংক্ষিপ্ততর দৃষ্টিভঙ্গি রয়েছে।

এখানে এমন একটি স্ক্রিপ্ট রয়েছে যাতে সম্পূর্ণ সমাধান রয়েছে

এবং এখানে অ্যালগরিদমের একটি রূপরেখা রয়েছে:

  • পিটটি ডেটা সেট করুন যাতে প্রতি সপ্তাহে একটি একক সারি প্রতিনিধিত্ব করে
  • প্রত্যেকের মধ্যে সপ্তাহের দ্বীপগুলি গণনা করুন ContractId
  • যে কোনও সংলগ্ন সপ্তাহের মধ্যে একত্রে মিলিত হয় ContractIdএবং একই হয়WeekDays
  • যে কোনও এক সপ্তাহের জন্য (এখনও একীভূত হয়নি) যেখানে পূর্ববর্তী গ্রুপিং একই দ্বীপে রয়েছে এবং WeekDaysএকক সপ্তাহের WeekDaysপূর্ববর্তী গ্রুপিংয়ের একটি শীর্ষস্থানীয় উপসেট মেলে , সেই আগের গ্রুপিংয়ে মার্জ
  • যে কোনও এক সপ্তাহের জন্য (এখনও একীভূত হয়নি) যেখানে পরবর্তী গ্রুপিং একই দ্বীপে রয়েছে এবং WeekDaysএকক সপ্তাহের WeekDaysপরবর্তী গ্রুপিংয়ের একটি পেছনের উপসেটের সাথে মিল রয়েছে , সেই পরবর্তী গ্রুপিংয়ে মার্জ করুন
  • একই দ্বীপে যে কোনও সংলগ্ন সপ্তাহের জন্য যেখানে দু'টিই একীভূত হয়নি, তাদের একত্রে একত্রীকরণ করুন যদি উভয়ই একত্রে আংশিক সপ্তাহ হয় (যেমন, "সোম, মঙ্গল, বুধ, থু," এবং "বুধ, থু, শনি," )
  • অন্য কোনও একক সপ্তাহের জন্য (এখনও একীভূত হয়নি), যদি সম্ভব হয় তবে সপ্তাহটিকে দুটি ভাগে ভাগ করুন এবং উভয় অংশকে একীভূত করুন, প্রথম অংশটি একই দ্বীপে আগের গ্রুপিংয়ে এবং দ্বিতীয় অংশটি একই দ্বীপে নিম্নলিখিত গ্রুপিংয়ে ভাগ করুন

কাজের সমাধান উত্পাদন করতে এত বড় দৈর্ঘ্যে যাওয়ার জন্য আপনাকে ধন্যবাদ। সত্যি কথা বলতে কিছুটা অভিভূত হয়। আমি সন্দেহ করেছিলাম যে আংশিক সপ্তাহগুলিতে মার্জ করা সহজ হবে না, তবে আমি এটি এত জটিল হওয়ার আশা করতে পারি না। আমার এখনও একটি আশা রয়েছে যে এটি আরও সহজভাবে করা যেতে পারে তবে আমার কাছে এই বিষয়ে দৃ concrete় ধারণা নেই।
ভ্লাদিমির বড়ানভ

দ্রুত চেক নিশ্চিত করে যে এটি নমুনা ডেটার জন্য প্রত্যাশিত ফলাফল তৈরি করে, যা দুর্দান্ত, তবে, আমি লক্ষ্য করেছি যে নির্দিষ্ট সময়সূচী অনুকূল উপায়ে পরিচালিত হয় না। সবচেয়ে সহজ উদাহরণ: (1214,12,'2016-05-06', 'Fri', 6), (1225,12,'2016-05-09', 'Mon', 2),। এটি একটি বিরতি হিসাবে উপস্থাপন করা যেতে পারে, তবে আপনার সমাধান দুটি উত্পাদন করে। আমি স্বীকার করি, এই উদাহরণটি নমুনা ডেটাতে ছিল না এবং এটি সমালোচনাযোগ্য নয়। আমি রিয়েল ডেটাতে আপনার সমাধান চালানোর চেষ্টা করব।
ভ্লাদিমির বড়ানভ

আমি আপনার উত্তর প্রশংসা করি। আমি যখন অনুগ্রহ শুরু করি তখন আমি পুনরাবৃত্ত সিটিই সম্পর্কে ভাবিনি এবং জিগি ক্রুয়েল্টিফ্রি জাইটিজিস্টার সর্বপ্রথম এটির পরামর্শ এবং কার্যনির্বাহী সমাধান উপস্থাপন করেছিলেন। কড়া কথায় বলতে গেলে, রিকার্সিভ সিটিই কোনও সেট-ভিত্তিক সমাধান নয়, তবে এটি সর্বোত্তম ফলাফল দেয়, যুক্তিযুক্ত জটিল এবং যুক্তিসঙ্গত দ্রুত। আপনার উত্তরটি সেট-ভিত্তিক, তবে ব্যবহারিক না হওয়ার দিক থেকে খুব জটিল হতে দেখা যাচ্ছে। আমি আশা করি আমি অনুগ্রহটি বিভক্ত করতে পারি, তবে দুর্ভাগ্যক্রমে এটি অনুমোদিত নয়।
ভ্লাদিমির বারানভ

@ ভ্লাদিমিরবারানভ কোনও সমস্যা নেই, অনুগ্রহটি আপনার ইচ্ছামতো ব্যবহারের জন্য 100% আপনার। আমার অনুগ্রহমূলক প্রশ্নগুলি পছন্দ করার কারণ হ'ল প্রশ্ন জিজ্ঞাসা করা ব্যক্তিটি সাধারণত একটি সাধারণ প্রশ্নের চেয়ে অনেক বেশি ব্যস্ত থাকে। পয়েন্টগুলি সম্পর্কে খুব বেশি যত্ন করবেন না। আমি সম্পূর্ণরূপে সম্মত হই যে এই সমাধানটি আমার প্রডাকশন কোডে ব্যবহার করব এমন নয়; এটি একটি সম্ভাব্য ধারণার অন্বেষণ ছিল, তবে এটি জটিল হয়ে ওঠে।
জেফ প্যাটারসন

3

আমি ফাঁকাগুলির সাথে সপ্তাহের গ্রুপিংয়ের পিছনে যুক্তি বা সপ্তাহান্তে সপ্তাহগুলি বুঝতে পারি না (যেমন যখন সপ্তাহান্তের সাথে টানা দু'সপ্তাহ থাকে, উইকএন্ড কোন সপ্তাহে যায়?)

নিম্নলিখিত কোয়েরিটি কেবলমাত্র একটানা সপ্তাহের দিনগুলিকে এবং গ্রুপ সান-স্যাট (সোম-সনের পরিবর্তে) গোষ্ঠীগুলি ব্যতীত পছন্দসই আউটপুট তৈরি করে। আপনি যা চান ঠিক তেমন না হলেও, সম্ভবত এটি ভিন্ন কৌশলটির জন্য কিছু সংকেত সরবরাহ করতে পারে। দিনের গ্রুপিং এখান থেকে আসে । ব্যবহৃত উইন্ডোটিং ফাংশনগুলিতে এসকিউএল সার্ভার ২০০৮ এর সাথে কাজ করা উচিত, তবে আমার এটির ভার্সনটি পরীক্ষার জন্য নেই এটি আসলে হয় কিনা।

WITH 
  mysrc AS (
    SELECT *, RANK() OVER (PARTITION BY ContractID ORDER BY DT) AS rank
    FROM @Src
    ),
  prepos AS (
    SELECT s.*, pos.ID AS posid
    FROM mysrc s
    LEFT JOIN mysrc pos ON (pos.ContractID = s.ContractID AND pos.rank = s.rank+1 AND (pos.DowInt = s.DowInt+1 OR pos.DowInt = 2 AND s.DowInt=6))
    ),
  grped AS (
    SELECT TOP 100 *, (SELECT COUNT(CASE WHEN posid IS NULL THEN 1 END) FROM prepos WHERE contractid = p.contractid AND rank < p.rank) as grp
    FROM prepos p
    ORDER BY ContractID, DT
    )
SELECT ContractID, min(dt) AS StartDT, max(dt) AS EndDT, count(*) AS DayCount,
       STUFF( (SELECT ', ' + dowchar
               FROM (
                 SELECT TOP 100 dowint, dowchar 
                 FROM grped 
                 WHERE ContractID = g.ContractID AND grp = g.grp 
                 GROUP BY dowint, dowchar 
                 ORDER BY 1
                 ) a 
               FOR XML PATH(''), TYPE).value('.','varchar(max)'), 1, 2, '') AS WeekDays
FROM grped g
GROUP BY ContractID, grp
ORDER BY 1, 2

ফলাফল

+------------+------------+------------+----------+-----------------------------------+
| ContractID | StartDT    | EndDT      | DayCount | WeekDays                          |
+------------+------------+------------+----------+-----------------------------------+
| 1          | 2/05/2016  | 13/05/2016 | 10       | Mon, Tue, Wed, Thu, Fri           |
| 2          | 5/05/2016  | 17/05/2016 | 9        | Mon, Tue, Wed, Thu, Fri           |
| 3          | 2/05/2016  | 2/05/2016  | 1        | Mon                               |
| 3          | 4/05/2016  | 4/05/2016  | 1        | Wed                               |
| 3          | 6/05/2016  | 9/05/2016  | 2        | Mon, Fri                          |
| 3          | 11/05/2016 | 11/05/2016 | 1        | Wed                               |
| 3          | 13/05/2016 | 16/05/2016 | 2        | Mon, Fri                          |
| 4          | 2/05/2016  | 6/05/2016  | 5        | Mon, Tue, Wed, Thu, Fri           |
| 4          | 10/05/2016 | 13/05/2016 | 4        | Tue, Wed, Thu, Fri                |
| 5          | 2/05/2016  | 6/05/2016  | 5        | Mon, Tue, Wed, Thu, Fri           |
| 5          | 10/05/2016 | 20/05/2016 | 9        | Mon, Tue, Wed, Thu, Fri           |
| 6          | 5/05/2016  | 17/05/2016 | 9        | Mon, Tue, Wed, Thu, Fri           |
| 6          | 6/06/2016  | 17/06/2016 | 10       | Mon, Tue, Wed, Thu, Fri           |
| 7          | 2/05/2016  | 7/05/2016  | 6        | Mon, Tue, Wed, Thu, Fri, Sat      |
| 7          | 8/05/2016  | 13/05/2016 | 6        | Sun, Mon, Tue, Wed, Thu, Fri      |
| 8          | 30/04/2016 | 30/04/2016 | 1        | Sat                               |
| 8          | 1/05/2016  | 7/05/2016  | 7        | Sun, Mon, Tue, Wed, Thu, Fri, Sat |
| 8          | 8/05/2016  | 14/05/2016 | 7        | Sun, Mon, Tue, Wed, Thu, Fri, Sat |
| 9          | 2/05/2016  | 4/05/2016  | 3        | Mon, Tue, Wed                     |
| 9          | 9/05/2016  | 10/05/2016 | 2        | Mon, Tue                          |
+------------+------------+------------+----------+-----------------------------------+


3

সম্পূর্ণতার জন্য, এখানে একটি দ্বি-পাস gaps-and-islandsপদ্ধতির এই প্রশ্নটি জিজ্ঞাসা করার আগে আমি নিজেকে চেষ্টা করেছি।

যেহেতু আমি এটি সত্য ডেটাতে পরীক্ষা করছিলাম তখন আমি খুব কম কেস পেয়েছি যখন এটির ভুল ফলাফল তৈরি হয়েছিল এবং এটি ঠিক করা হয়েছিল।

এখানে আলগোরিদিম:

  • পরপর তারিখ দ্বীপপুঞ্জের জেনারেট করুন ( CTE_ContractDays, CTE_DailyRN, CTE_DailyIslands) এবং একটি দ্বীপ প্রতিটি শুরু এবং শেষ তারিখ জন্য এক সপ্তাহ সংখ্যা গণনা। এখানে সপ্তাহের সংখ্যাটি ধরে নিয়ে গণনা করা হয় যে সোমবার সপ্তাহের প্রথম দিন।
  • যদি শিডিউলে একই সপ্তাহের মধ্যে অ-অনুক্রমিক তারিখ থাকে (উদাহরণস্বরূপ 3), পূর্ববর্তী স্তরটি একই সপ্তাহের জন্য কয়েকটি সারি তৈরি করবে। গ্রুপ সারিগুলিতে প্রতি সপ্তাহে একটি করে সারি থাকবে ( CTE_Weeks)।
  • পূর্ববর্তী স্তর থেকে প্রতিটি সারির জন্য সপ্তাহের দিনগুলির ( CTE_FirstResult) কমা-বিচ্ছিন্ন তালিকা তৈরি করুন ।
  • ফাঁক-এবং-দ্বীপপুঞ্জের দ্বিতীয় পাসটি একই WeekDays( CTE_SecondRN, CTE_Schedules) দিয়ে একটানা সপ্তাহে গ্রুপে ।

সাপ্তাহিক নিদর্শনগুলিতে (1, 7, 8, 10, 12) কোনও বিঘ্ন না ঘটে যখন এটি ভাল ক্ষেত্রে পরিচালনা করে। যখন প্যাটার্নটিতে অ-অনুক্রমিক দিন থাকে (3) তখন এটি ভাল ক্ষেত্রে পরিচালনা করে।

তবে, দুর্ভাগ্যক্রমে, এটি আংশিক সপ্তাহগুলির জন্য অতিরিক্ত বিরতি উত্পন্ন করে (2, 3, 5, 6, 9, 11)

WITH
CTE_ContractDays
AS
(
    SELECT
         S.ContractID
        ,MIN(S.dt) OVER (PARTITION BY S.ContractID) AS ContractMinDT
        ,S.dt
        ,ROW_NUMBER() OVER (PARTITION BY S.ContractID ORDER BY S.dt) AS rn1
        ,DATEDIFF(day, '2001-01-01', S.dt) AS DayNumber
        ,S.dowChar
        ,S.dowInt
    FROM
        @Src AS S
)
,CTE_DailyRN
AS
(
    SELECT
        DayNumber - rn1 AS WeekGroupNumber
        ,ROW_NUMBER() OVER (
            PARTITION BY
                ContractID
                ,DayNumber - rn1
            ORDER BY dt) AS rn2
        ,ContractID
        ,ContractMinDT
        ,dt
        ,rn1
        ,DayNumber
        ,dowChar
        ,dowInt
    FROM CTE_ContractDays
)
,CTE_DailyIslands
AS
(
    SELECT
        ContractID
        ,ContractMinDT
        ,MIN(dt) AS MinDT
        ,MAX(dt) AS MaxDT
        ,COUNT(*) AS DayCount
        -- '2001-01-01' is Monday
        ,DATEDIFF(day, '2001-01-01', MIN(dt)) / 7 AS WeekNumberMin
        ,DATEDIFF(day, '2001-01-01', MAX(dt)) / 7 AS WeekNumberMax
    FROM CTE_DailyRN
    GROUP BY
        ContractID
        ,rn1-rn2
        ,ContractMinDT
)
,CTE_Weeks
AS
(
    SELECT
        ContractID
        ,ContractMinDT
        ,MIN(MinDT) AS MinDT
        ,MAX(MaxDT) AS MaxDT
        ,SUM(DayCount) AS DayCount
        ,WeekNumberMin
        ,WeekNumberMax
    FROM CTE_DailyIslands
    GROUP BY
        ContractID
        ,ContractMinDT
        ,WeekNumberMin
        ,WeekNumberMax
)
,CTE_FirstResult
AS
(
    SELECT
        ContractID
        ,ContractMinDT
        ,MinDT
        ,MaxDT
        ,DayCount
        ,CA_Data.XML_Value AS DaysOfWeek
        ,WeekNumberMin AS WeekNumber
        ,ROW_NUMBER() OVER(PARTITION BY ContractID ORDER BY MinDT) AS rn1
    FROM
        CTE_Weeks
        CROSS APPLY
        (
            SELECT CAST(CTE_ContractDays.dowChar AS varchar(8000)) + ',' AS dw
            FROM CTE_ContractDays
            WHERE
                    CTE_ContractDays.ContractID = CTE_Weeks.ContractID
                AND CTE_ContractDays.dt >= CTE_Weeks.MinDT
                AND CTE_ContractDays.dt <= CTE_Weeks.MaxDT
            GROUP BY
                CTE_ContractDays.dowChar
                ,CTE_ContractDays.dowInt
            ORDER BY CTE_ContractDays.dowInt
            FOR XML PATH(''), TYPE
        ) AS CA_XML(XML_Value)
        CROSS APPLY
        (
            SELECT CA_XML.XML_Value.value('.', 'VARCHAR(8000)')
        ) AS CA_Data(XML_Value)
)
,CTE_SecondRN
AS
(
    SELECT 
        ContractID
        ,ContractMinDT
        ,MinDT
        ,MaxDT
        ,DayCount
        ,DaysOfWeek
        ,WeekNumber
        ,rn1
        ,WeekNumber - rn1 AS SecondGroupNumber
        ,ROW_NUMBER() OVER (
            PARTITION BY
                ContractID
                ,DaysOfWeek
                ,DayCount
                ,WeekNumber - rn1
            ORDER BY MinDT) AS rn2
    FROM CTE_FirstResult
)
,CTE_Schedules
AS
(
    SELECT
        ContractID
        ,MIN(MinDT) AS StartDT
        ,MAX(MaxDT) AS EndDT
        ,SUM(DayCount) AS DayCount
        ,DaysOfWeek
    FROM CTE_SecondRN
    GROUP BY
        ContractID
        ,DaysOfWeek
        ,rn1-rn2
)
SELECT
    ContractID
    ,StartDT
    ,EndDT
    ,DayCount
    ,DaysOfWeek AS WeekDays
FROM CTE_Schedules
ORDER BY
    ContractID
    ,StartDT
;

ফলাফল

+------------+------------+------------+----------+------------------------------+
| ContractID |  StartDT   |   EndDT    | DayCount |           WeekDays           |
+------------+------------+------------+----------+------------------------------+
|          1 | 2016-05-02 | 2016-05-13 |       10 | Mon,Tue,Wed,Thu,Fri,         |
|          2 | 2016-05-05 | 2016-05-06 |        2 | Thu,Fri,                     |
|          2 | 2016-05-09 | 2016-05-13 |        5 | Mon,Tue,Wed,Thu,Fri,         |
|          2 | 2016-05-16 | 2016-05-17 |        2 | Mon,Tue,                     |
|          3 | 2016-05-02 | 2016-05-13 |        6 | Mon,Wed,Fri,                 |
|          3 | 2016-05-16 | 2016-05-16 |        1 | Mon,                         |
|          4 | 2016-05-02 | 2016-05-06 |        5 | Mon,Tue,Wed,Thu,Fri,         |
|          4 | 2016-05-10 | 2016-05-13 |        4 | Tue,Wed,Thu,Fri,             |
|          5 | 2016-05-02 | 2016-05-06 |        5 | Mon,Tue,Wed,Thu,Fri,         |
|          5 | 2016-05-10 | 2016-05-13 |        4 | Tue,Wed,Thu,Fri,             |
|          5 | 2016-05-16 | 2016-05-20 |        5 | Mon,Tue,Wed,Thu,Fri,         |
|          6 | 2016-05-05 | 2016-05-06 |        2 | Thu,Fri,                     |
|          6 | 2016-05-09 | 2016-05-13 |        5 | Mon,Tue,Wed,Thu,Fri,         |
|          6 | 2016-05-16 | 2016-05-17 |        2 | Mon,Tue,                     |
|          6 | 2016-06-06 | 2016-06-17 |       10 | Mon,Tue,Wed,Thu,Fri,         |
|          7 | 2016-05-02 | 2016-05-13 |       12 | Sun,Mon,Tue,Wed,Thu,Fri,Sat, |
|          8 | 2016-04-30 | 2016-05-14 |       15 | Sun,Mon,Tue,Wed,Thu,Fri,Sat, |
|          9 | 2016-05-02 | 2016-05-11 |        6 | Mon,Tue,Wed,                 |
|          9 | 2016-05-16 | 2016-05-17 |        2 | Mon,Tue,                     |
|         10 | 2016-05-05 | 2016-05-22 |       12 | Sun,Thu,Fri,Sat,             |
|         11 | 2016-05-03 | 2016-05-10 |        2 | Tue,                         |
|         11 | 2016-05-17 | 2016-05-19 |        2 | Tue,Thu,                     |
|         11 | 2016-05-26 | 2016-06-02 |        2 | Thu,                         |
|         12 | 2016-05-02 | 2016-05-06 |        5 | Mon,Tue,Wed,Thu,Fri,         |
|         12 | 2016-05-16 | 2016-05-20 |        5 | Mon,Tue,Wed,Thu,Fri,         |
+------------+------------+------------+----------+------------------------------+

কার্সার-ভিত্তিক সমাধান

আমি আমার সি # কোডটিকে কার্সার ভিত্তিক অ্যালগরিদমে রূপান্তর করেছি, কেবল এটি দেখতে যে কীভাবে এটি বাস্তব ডেটার অন্যান্য সমাধানগুলির সাথে তুলনা করে। এটি নিশ্চিত করে যে এটি অন্যান্য সেট-ভিত্তিক বা পুনরাবৃত্ত পদ্ধতির তুলনায় অনেক ধীর, তবে এটি একটি অনুকূল ফলাফল উত্পন্ন করে।

CREATE TABLE #Dst_V2 (ContractID bigint, StartDT date, EndDT date, DayCount int, WeekDays varchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS);

SET NOCOUNT ON;

DECLARE @VarOldDateFirst int = @@DATEFIRST;
SET DATEFIRST 7;

DECLARE @iFS int;
DECLARE @VarCursor CURSOR;
SET @VarCursor = CURSOR FAST_FORWARD
FOR
    SELECT
        ContractID
        ,dt
        ,dowChar
        ,dowInt
    FROM #Src AS S
    ;

OPEN @VarCursor;

DECLARE @CurrContractID bigint = 0;
DECLARE @Currdt date;
DECLARE @CurrdowChar char(3);
DECLARE @CurrdowInt int;


DECLARE @VarCreateNewInterval bit = 0;
DECLARE @VarTempDT date;
DECLARE @VarTempdowInt int;

DECLARE @LastContractID bigint = 0;
DECLARE @LastStartDT date;
DECLARE @LastEndDT date;
DECLARE @LastDayCount int = 0;
DECLARE @LastWeekDays varchar(255);
DECLARE @LastMonCount int;
DECLARE @LastTueCount int;
DECLARE @LastWedCount int;
DECLARE @LastThuCount int;
DECLARE @LastFriCount int;
DECLARE @LastSatCount int;
DECLARE @LastSunCount int;


FETCH NEXT FROM @VarCursor INTO @CurrContractID, @Currdt, @CurrdowChar, @CurrdowInt;
SET @iFS = @@FETCH_STATUS;
IF @iFS = 0
BEGIN
    SET @LastContractID = @CurrContractID;
    SET @LastStartDT = @Currdt;
    SET @LastEndDT = @Currdt;
    SET @LastDayCount = 1;
    SET @LastMonCount = 0;
    SET @LastTueCount = 0;
    SET @LastWedCount = 0;
    SET @LastThuCount = 0;
    SET @LastFriCount = 0;
    SET @LastSatCount = 0;
    SET @LastSunCount = 0;
    IF @CurrdowInt = 1 SET @LastSunCount = @LastSunCount + 1;
    IF @CurrdowInt = 2 SET @LastMonCount = @LastMonCount + 1;
    IF @CurrdowInt = 3 SET @LastTueCount = @LastTueCount + 1;
    IF @CurrdowInt = 4 SET @LastWedCount = @LastWedCount + 1;
    IF @CurrdowInt = 5 SET @LastThuCount = @LastThuCount + 1;
    IF @CurrdowInt = 6 SET @LastFriCount = @LastFriCount + 1;
    IF @CurrdowInt = 7 SET @LastSatCount = @LastSatCount + 1;
END;

WHILE @iFS = 0
BEGIN

    SET @VarCreateNewInterval = 0;

    -- Contract changes -> start new interval
    IF @LastContractID <> @CurrContractID
    BEGIN
        SET @VarCreateNewInterval = 1;
    END;

    IF @VarCreateNewInterval = 0
    BEGIN
        -- check days of week
        -- are we still within the first week of the interval?
        IF DATEDIFF(day, @LastStartDT, @Currdt) > 6
        BEGIN
            -- we are beyond the first week, check day of the week
            -- have we seen @CurrdowInt before?
            -- we should start a new interval if this is the new day of the week that didn't exist in the first week
            IF @CurrdowInt = 1 AND @LastSunCount = 0 SET @VarCreateNewInterval = 1;
            IF @CurrdowInt = 2 AND @LastMonCount = 0 SET @VarCreateNewInterval = 1;
            IF @CurrdowInt = 3 AND @LastTueCount = 0 SET @VarCreateNewInterval = 1;
            IF @CurrdowInt = 4 AND @LastWedCount = 0 SET @VarCreateNewInterval = 1;
            IF @CurrdowInt = 5 AND @LastThuCount = 0 SET @VarCreateNewInterval = 1;
            IF @CurrdowInt = 6 AND @LastFriCount = 0 SET @VarCreateNewInterval = 1;
            IF @CurrdowInt = 7 AND @LastSatCount = 0 SET @VarCreateNewInterval = 1;

            IF @VarCreateNewInterval = 0
            BEGIN
                -- check the gap between current day and last day of the interval
                -- if the gap between current day and last day of the interval
                -- contains a day of the week that was included in the interval before,
                -- we should create new interval
                SET @VarTempDT = DATEADD(day, 1, @LastEndDT);
                WHILE @VarTempDT < @Currdt
                BEGIN
                    SET @VarTempdowInt = DATEPART(WEEKDAY, @VarTempDT);

                    IF @VarTempdowInt = 1 AND @LastSunCount > 0 BEGIN SET @VarCreateNewInterval = 1; BREAK; END;
                    IF @VarTempdowInt = 2 AND @LastMonCount > 0 BEGIN SET @VarCreateNewInterval = 1; BREAK; END;
                    IF @VarTempdowInt = 3 AND @LastTueCount > 0 BEGIN SET @VarCreateNewInterval = 1; BREAK; END;
                    IF @VarTempdowInt = 4 AND @LastWedCount > 0 BEGIN SET @VarCreateNewInterval = 1; BREAK; END;
                    IF @VarTempdowInt = 5 AND @LastThuCount > 0 BEGIN SET @VarCreateNewInterval = 1; BREAK; END;
                    IF @VarTempdowInt = 6 AND @LastFriCount > 0 BEGIN SET @VarCreateNewInterval = 1; BREAK; END;
                    IF @VarTempdowInt = 7 AND @LastSatCount > 0 BEGIN SET @VarCreateNewInterval = 1; BREAK; END;

                    SET @VarTempDT = DATEADD(day, 1, @VarTempDT);
                END;
            END;
        END;
        -- else
        -- we are still within the first week, so we can add this day to the interval
    END;

    IF @VarCreateNewInterval = 1
    BEGIN
        -- save the new interval into the final table
        SET @LastWeekDays = '';
        IF @LastSunCount > 0 SET @LastWeekDays = @LastWeekDays + 'Sun,';
        IF @LastMonCount > 0 SET @LastWeekDays = @LastWeekDays + 'Mon,';
        IF @LastTueCount > 0 SET @LastWeekDays = @LastWeekDays + 'Tue,';
        IF @LastWedCount > 0 SET @LastWeekDays = @LastWeekDays + 'Wed,';
        IF @LastThuCount > 0 SET @LastWeekDays = @LastWeekDays + 'Thu,';
        IF @LastFriCount > 0 SET @LastWeekDays = @LastWeekDays + 'Fri,';
        IF @LastSatCount > 0 SET @LastWeekDays = @LastWeekDays + 'Sat,';

        INSERT INTO #Dst_V2 
            (ContractID
            ,StartDT
            ,EndDT
            ,DayCount
            ,WeekDays)
        VALUES
            (@LastContractID
            ,@LastStartDT
            ,@LastEndDT
            ,@LastDayCount
            ,@LastWeekDays);

        -- init the new interval
        SET @LastContractID = @CurrContractID;
        SET @LastStartDT = @Currdt;
        SET @LastEndDT = @Currdt;
        SET @LastDayCount = 1;
        SET @LastMonCount = 0;
        SET @LastTueCount = 0;
        SET @LastWedCount = 0;
        SET @LastThuCount = 0;
        SET @LastFriCount = 0;
        SET @LastSatCount = 0;
        SET @LastSunCount = 0;
        IF @CurrdowInt = 1 SET @LastSunCount = @LastSunCount + 1;
        IF @CurrdowInt = 2 SET @LastMonCount = @LastMonCount + 1;
        IF @CurrdowInt = 3 SET @LastTueCount = @LastTueCount + 1;
        IF @CurrdowInt = 4 SET @LastWedCount = @LastWedCount + 1;
        IF @CurrdowInt = 5 SET @LastThuCount = @LastThuCount + 1;
        IF @CurrdowInt = 6 SET @LastFriCount = @LastFriCount + 1;
        IF @CurrdowInt = 7 SET @LastSatCount = @LastSatCount + 1;

    END ELSE BEGIN

        -- update last interval
        SET @LastEndDT = @Currdt;
        SET @LastDayCount = @LastDayCount + 1;
        IF @CurrdowInt = 1 SET @LastSunCount = @LastSunCount + 1;
        IF @CurrdowInt = 2 SET @LastMonCount = @LastMonCount + 1;
        IF @CurrdowInt = 3 SET @LastTueCount = @LastTueCount + 1;
        IF @CurrdowInt = 4 SET @LastWedCount = @LastWedCount + 1;
        IF @CurrdowInt = 5 SET @LastThuCount = @LastThuCount + 1;
        IF @CurrdowInt = 6 SET @LastFriCount = @LastFriCount + 1;
        IF @CurrdowInt = 7 SET @LastSatCount = @LastSatCount + 1;
    END;


    FETCH NEXT FROM @VarCursor INTO @CurrContractID, @Currdt, @CurrdowChar, @CurrdowInt;
    SET @iFS = @@FETCH_STATUS;
END;

-- save the last interval into the final table
IF @LastDayCount > 0
BEGIN
    SET @LastWeekDays = '';
    IF @LastSunCount > 0 SET @LastWeekDays = @LastWeekDays + 'Sun,';
    IF @LastMonCount > 0 SET @LastWeekDays = @LastWeekDays + 'Mon,';
    IF @LastTueCount > 0 SET @LastWeekDays = @LastWeekDays + 'Tue,';
    IF @LastWedCount > 0 SET @LastWeekDays = @LastWeekDays + 'Wed,';
    IF @LastThuCount > 0 SET @LastWeekDays = @LastWeekDays + 'Thu,';
    IF @LastFriCount > 0 SET @LastWeekDays = @LastWeekDays + 'Fri,';
    IF @LastSatCount > 0 SET @LastWeekDays = @LastWeekDays + 'Sat,';

    INSERT INTO #Dst_V2
        (ContractID
        ,StartDT
        ,EndDT
        ,DayCount
        ,WeekDays)
    VALUES
        (@LastContractID
        ,@LastStartDT
        ,@LastEndDT
        ,@LastDayCount
        ,@LastWeekDays);
END;

CLOSE @VarCursor;
DEALLOCATE @VarCursor;

SET DATEFIRST @VarOldDateFirst;

DROP TABLE #Dst_V2;

2

আমি কিছুটা অবাক হয়েছিলাম যে ভ্লাদিমিরের কার্সর সমাধানটি এত ধীর ছিল, তাই আমি সেই সংস্করণটিও অনুকূলিত করার চেষ্টা করেছি। আমি নিশ্চিত করেছিলাম যে কার্সার ব্যবহার করা আমার জন্যও খুব ধীর ছিল।

যাইহোক, এসকিউএল সার্ভারে কোনও সীমাবদ্ধ প্রক্রিয়া করার সময় একটি ভেরিয়েবল সংযোজন করে অননুমোদিত কার্যকারিতা ব্যবহারের ব্যয়ে, আমি এই যুক্তির একটি সরলিকৃত সংস্করণ তৈরি করতে সক্ষম হয়েছি যা সর্বোত্তম ফলাফল দেয় এবং কার্সার এবং আমার মূল সমাধান উভয়ের চেয়ে অনেক দ্রুত সম্পাদন করে । সুতরাং আপনার নিজের ঝুঁকিতে ব্যবহার করুন, তবে আগ্রহের ক্ষেত্রে সমাধানটি উপস্থাপন করব। WHILEলুপের প্রতিটি পুনরাবৃত্তিতে পরবর্তী সারির সংখ্যাটি সন্ধান করে এক থেকে সর্বোচ্চ সারি সংখ্যায় একটি লুপ ব্যবহার করার জন্য সমাধানটি আপডেট করা সম্ভব হবে । এটি পুরোপুরি নথিভুক্ত এবং নির্ভরযোগ্য কার্যকারিতা ধরে রাখবে তবে WHILEলুপগুলিকে অনুমোদিত নয় এমন সমস্যার (কিছুটা কৃত্রিম) বর্ণিত সীমাবদ্ধতা লঙ্ঘন করবে ।

নোট করুন যে যদি এসকিউএল 2014 ব্যবহারের অনুমতি দেওয়া হয় তবে সম্ভবত এমন একটি স্থানীয়ভাবে সংকলিত সঞ্চিত পদ্ধতি যা সারি সংখ্যার উপরে চলে যায় এবং মেমরি-অনুকূলিত টেবিলের প্রতিটি সারি সংখ্যা অ্যাক্সেস করে এটি একই যুক্তির প্রয়োগ হবে যা আরও দ্রুত চালিত হবে।

ট্রায়াল ডেটা প্রায় অর্ধ মিলিয়ন সারিতে বাড়ানো সহ পুরো সমাধানটি এখানে । নতুন সমাধানটি প্রায় 3 সেকেন্ডের মধ্যে সম্পূর্ণ হয় এবং আমার মতে আমি প্রস্তাবিত আগের সমাধানের চেয়ে অনেক বেশি সংক্ষিপ্ত এবং পাঠযোগ্য। আমি এখানে জড়িত তিনটি পদক্ষেপটি ভেঙে দেব:

পদক্ষেপ 1: প্রাক প্রক্রিয়াজাতকরণ

আমরা প্রথমে ডেটা সেটে একটি সারি নম্বর যুক্ত করি, ক্রমে আমরা ডেটা প্রক্রিয়া করব। এটি করার সময় আমরা প্রতিটি ডাউনইন্টকে 2 এর শক্তিতে রূপান্তর করি যাতে কোনও বিভাজনে কোন দিনগুলি পালন করা হয়েছে তা উপস্থাপন করতে আমরা একটি বিটম্যাপ ব্যবহার করতে পারি:

IF OBJECT_ID('tempdb..#srcWithRn') IS NOT NULL
    DROP TABLE #srcWithRn
GO
SELECT rn = IDENTITY(INT, 1, 1), ContractId, dt, dowInt,
    POWER(2, dowInt) AS dowPower, dowChar
INTO #srcWithRn
FROM #src
ORDER BY ContractId, dt
GO
ALTER TABLE #srcWithRn
ADD PRIMARY KEY (rn)
GO

পদক্ষেপ 2: নতুন গোষ্ঠীকরণ শনাক্ত করার জন্য চুক্তির দিনগুলিতে লুপিং

আমরা পরের সারিতে সংখ্যার ক্রম অনুযায়ী ডাটাটি লুপ করব। আমরা কেবলমাত্র সারি সংখ্যাগুলির তালিকাটি গণনা করি যা একটি নতুন গোষ্ঠীকরণের সীমানা তৈরি করে, তারপর সেই সারি সংখ্যাগুলিকে একটি টেবিলের মধ্যে আউটপুট দেয়:

DECLARE @ContractId INT, @RnList VARCHAR(MAX), @NewGrouping BIT = 0, @DowBitmap INT = 0, @startDt DATE
SELECT TOP 1 @ContractId = ContractId, @startDt = dt, @RnList = ',' + CONVERT(VARCHAR(MAX), rn), @DowBitmap = DowPower
FROM #srcWithRn
WHERE rn = 1

SELECT 
    -- New grouping if new contract, or if we're observing a new day that we did
    -- not observe within the first 7 days of the grouping
    @NewGrouping = CASE
        WHEN ContractId <> @ContractId THEN 1
        WHEN DATEDIFF(DAY, @startDt, dt) > 6
            AND @DowBitmap & dowPower <> dowPower THEN 1
        ELSE 0
        END,
    @ContractId = ContractId,
    -- If this is a newly observed day in an existing grouping, add it to the bitmap
    @DowBitmap = CASE WHEN @NewGrouping = 0 THEN @DowBitmap | DowPower ELSE DowPower END,
    -- If this is a new grouping, reset the start date of the grouping
    @startDt = CASE WHEN @NewGrouping = 0 THEN @startDt ELSE dt END,
    -- If this is a new grouping, add this rn to the list of row numbers that delineate the boundary of a new grouping
    @RnList = CASE WHEN @NewGrouping = 0 THEN @RnList ELSE @RnList + ',' + CONVERT(VARCHAR(MAX), rn) END 
FROM #srcWithRn
WHERE rn >= 2
ORDER BY rn
OPTION (MAXDOP 1)

-- Split the list of grouping boundaries into a table
IF OBJECT_ID('tempdb..#newGroupingRns') IS NOT NULL
    DROP TABLE #newGroupingRns
SELECT splitListId AS rn
INTO #newGroupingRns
FROM dbo.f_delimitedIntListSplitter(SUBSTRING(@RnList, 2, 1000000000), DEFAULT)
GO
ALTER TABLE #newGroupingRns
ADD PRIMARY KEY (rn)
GO

পদক্ষেপ 3: প্রতিটি গ্রুপিংয়ের সীমানার সারি সংখ্যার ভিত্তিতে চূড়ান্ত ফলাফলের গণনা করা

তারপরে আমরা প্রতিটি গ্রুপিংয়ে পড়ে থাকা সমস্ত তারিখকে একত্রিত করতে উপরের লুপে চিহ্নিত সীমানা ব্যবহার করে চূড়ান্ত গোষ্ঠীগুলি গণনা করি:

IF OBJECT_ID('tempdb..#finalGroupings') IS NOT NULL
    DROP TABLE #finalGroupings
GO
SELECT MIN(s.ContractId) AS ContractId,
    MIN(dt) AS StartDT,
    MAX(dt) AS EndDT,
    COUNT(*) AS DayCount,
    CASE WHEN MAX(CASE WHEN dowChar = 'Sun' THEN 1 ELSE 0 END) = 1 THEN 'Sun,' ELSE '' END + 
    CASE WHEN MAX(CASE WHEN dowChar = 'Mon' THEN 1 ELSE 0 END) = 1 THEN 'Mon,' ELSE '' END + 
    CASE WHEN MAX(CASE WHEN dowChar = 'Tue' THEN 1 ELSE 0 END) = 1 THEN 'Tue,' ELSE '' END + 
    CASE WHEN MAX(CASE WHEN dowChar = 'Wed' THEN 1 ELSE 0 END) = 1 THEN 'Wed,' ELSE '' END + 
    CASE WHEN MAX(CASE WHEN dowChar = 'Thu' THEN 1 ELSE 0 END) = 1 THEN 'Thu,' ELSE '' END + 
    CASE WHEN MAX(CASE WHEN dowChar = 'Fri' THEN 1 ELSE 0 END) = 1 THEN 'Fri,' ELSE '' END + 
    CASE WHEN MAX(CASE WHEN dowChar = 'Sat' THEN 1 ELSE 0 END) = 1 THEN 'Sat,' ELSE '' END AS WeekDays
INTO #finalGroupings
FROM #srcWithRn s
CROSS APPLY (
    -- For any row, its grouping is the largest boundary row number that occurs at or before this row
    SELECT TOP 1 rn AS groupingRn
    FROM #newGroupingRns grp
    WHERE grp.rn <= s.rn
    ORDER BY grp.rn DESC
) g
GROUP BY g.groupingRn
ORDER BY g.groupingRn
GO

ধন্যবাদ. আমি কার্সার বা WHILEলুপগুলি ব্যবহার না করার জন্য জিজ্ঞাসা করেছি , কারণ আমি ইতিমধ্যে জানতাম কীভাবে এটি কার্সার দিয়ে সমাধান করতে হয় এবং আমি একটি সেট-ভিত্তিক সমাধান খুঁজতে চাই। তদতিরিক্ত, আমার সন্দেহ হয়েছিল যে কার্সারটি ধীর হবে (বিশেষত এটিতে নেস্টেড লুপের সাহায্যে)। এই কৌশলটি নতুন কৌশল শেখার ক্ষেত্রে অত্যন্ত আকর্ষণীয় এবং আমি আপনার প্রচেষ্টার প্রশংসা করি।
ভ্লাদিমির বারানভ

1

কোডটি অনুসরণ করবে আলোচনা।

declare @Helper table(
    rn tinyint,
    dowInt tinyint,
    dowChar char(3));
insert @Helper
values  ( 1,1,'Sun'),
        ( 2,2,'Mon'),
        ( 3,3,'Tue'),
        ( 4,4,'Wed'),
        ( 5,5,'Thu'),
        ( 6,6,'Fri'),
        ( 7,7,'Sat'),
        ( 8,1,'Sun'),
        ( 9,2,'Mon'),
        (10,3,'Tue'),
        (11,4,'Wed'),
        (12,5,'Thu'),
        (13,6,'Fri'),
        (14,7,'Sat');



with MissingDays as
(
    select
        h1.rn as rn1,
        h1.dowChar as StartDay,
        h2.rn as rn2,
        h2.dowInt as FollowingDayInt,
        h2.dowChar as FollowingDayChar
    from @Helper as h1
    inner join @Helper as h2
        on h2.rn > h1.rn
    where h1.rn < 8
    and h2.rn < h1.rn + 8
)
,Numbered as
(
    select
        a.*,
        ROW_NUMBER() over (partition by a.ContractID order by a.dt) as rn
    from #Src as a
)
,Incremented as
(
    select
        b.*,
        convert(varchar(max), b.dowChar)+',' as WeekDays,
        b.dt as IntervalStart
    from Numbered as b
    where b.rn = 1

    union all

    select
        c.*,
        case
            when
                (DATEDIFF(day, d.IntervalStart, c.dt) > 6)      -- interval goes beyond 7 days
            and (
                    (d.WeekDays not like '%'+c.dowChar+'%')     -- the new week day has not been seen before
                or 
                    (DATEDIFF(day, d.dt, c.dt) > 7)
                or 
                    (
                        (DATEDIFF(day, d.dt, c.dt) > 1)
                        and
                        (
                        exists( select
                                    e.FollowingDayChar
                                from MissingDays as e
                                where e.StartDay = d.dowChar
                                and rn2 < (select f.rn2 from MissingDays as f
                                            where f.StartDay = d.dowChar
                                            and f.FollowingDayInt = c.dowInt)
                                and d.WeekDays like '%'+e.FollowingDayChar+'%'
                            )
                        )
                    )
                )
            then convert(varchar(max),c.dowChar)+','
            else
                case
                    when d.WeekDays like '%'+c.dowChar+'%'
                    then d.WeekDays
                    else d.WeekDays+convert(varchar(max),c.dowChar)+','
                end
        end,
        case
            when
                (DATEDIFF(day, d.IntervalStart, c.dt) > 6)      -- interval goes beyond 7 days
            and (
                    (d.WeekDays not like '%'+c.dowChar+'%')     -- the new week day has not been seen before
                or
                    (DATEDIFF(day, d.dt, c.dt) > 7)             -- there is a one week gap
                or 
                    (
                        (DATEDIFF(day, d.dt, c.dt) > 1)         -- there is a gap..
                        and
                        (
                        exists( select                          -- .. and the omitted days are in the preceeding interval
                                    e.FollowingDayChar
                                from MissingDays as e
                                where e.StartDay = d.dowChar
                                and rn2 < (select f.rn2 from MissingDays as f
                                            where f.StartDay = d.dowChar
                                            and f.FollowingDayInt = c.dowInt)
                                and d.WeekDays like '%'+e.FollowingDayChar+'%'
                            )
                        )
                    )
                )
            then c.dt
            else d.IntervalStart
        end
    from Numbered as c
    inner join Incremented as d
    on d.ContractID = c.ContractID
    and d.rn = c.rn - 1
)
select
    g.ContractID,
    g.IntervalStart as StartDT,
    MAX(g.dt) as EndDT,
    COUNT(*) as DayCount,
    MAX(g.WeekDays) as WeekDays
from Incremented as g
group by
    g.ContractID,
    g.IntervalStart
order by
    ContractID,
    StartDT;

@Helper এই নিয়মটি সামলাতে হয়:

যদি ব্যবধানের বর্তমান দিন এবং শেষ দিনের মধ্যে ব্যবধানে সপ্তাহের একটি দিন থাকে যা আগে অন্তর অন্তর্ভুক্ত ছিল, আমাদের নতুন বিরতি তৈরি করা উচিত

এটি আমাকে প্রদত্ত যে কোনও দুটি দিনের মধ্যে দিনের নাম ক্রমে দিনের নাম তালিকাভুক্ত করতে দেয়। নতুন ব্যবধান শুরু করা উচিত কিনা তা সিদ্ধান্ত নেওয়ার সময় এটি ব্যবহার করা হয়। আমি সপ্তাহান্তে কোডটিকে সহজভাবে মোড়ানো করতে দুই সপ্তাহের মূল্যবান মান দিয়ে এটিকে জনপ্রিয় করি।

এটি বাস্তবায়নের জন্য আরও পরিষ্কার উপায় রয়েছে। একটি পূর্ণ "তারিখ" সারণী এক হবে। দিনের সংখ্যা এবং মডুলো পাটিগণিতের সাথে সম্ভবত একটি চতুর উপায় রয়েছে।

সিটিই MissingDaysপ্রদত্ত যে কোনও দুটি দিনের মধ্যে দিনের নামের একটি তালিকা তৈরি করতে হবে। এটি এই আঠালো উপায়ে পরিচালিত হয় কারণ পুনরাবৃত্ত সিটিই (নিম্নলিখিত) সমষ্টি, টপ () বা অন্যান্য অপারেটরদের অনুমতি দেয় না। এটি অবহেলা, কিন্তু এটি কার্যকর।

সিটিই Numberedহ'ল ডেটাতে একটি জ্ঞাত, ফাঁক-মুক্ত ক্রম প্রয়োগ করা। এটি পরে অনেক তুলনা এড়িয়ে যায়।

সিটিই Incrementedহ'ল ক্রিয়াটি যেখানে ঘটে। সংক্ষেপে আমি ডেটা নিয়ে পদক্ষেপ নিতে এবং বিধি প্রয়োগ করতে একটি পুনরাবৃত্ত সিটিই ব্যবহার করি। Numbered(উপরে) এ উত্পাদিত সারি নম্বরটি পুনরাবৃত্ত প্রসেসিং ড্রাইভ করতে ব্যবহৃত হয়।

পুনরাবৃত্ত সিটিইর বীজ প্রতিটি কন্ট্রাক্টআইডির জন্য সহজভাবে প্রথম তারিখ পায় এবং মানগুলি আরম্ভ করে যা কোনও নতুন বিরতি প্রয়োজন কিনা তা সিদ্ধান্ত নিতে ব্যবহার করা হবে।

নতুন ব্যবধানটি শুরু হওয়া উচিত কিনা তা সিদ্ধান্ত নেওয়ার জন্য বর্তমান ব্যবধানের শুরু তারিখ, দিনের তালিকা এবং ক্যালেন্ডারের তারিখগুলির মধ্যে কোনও ফাঁকের দৈর্ঘ্য প্রয়োজন। এগুলি সিদ্ধান্তের উপর নির্ভর করে পুনরায় সেট করা বা এগিয়ে নেওয়া যেতে পারে। সুতরাং পুনরাবৃত্তিমূলক অংশটি ভার্বোজ এবং কিছুটা পুনরাবৃত্ত, কারণ আমাদের সিদ্ধান্ত নিতে হবে যে একাধিক কলাম মানের জন্য একটি নতুন অন্তর শুরু করতে হবে।

কলাম WeekDaysএবং জন্য সিদ্ধান্ত যুক্তিIntervalStart একই সিদ্ধান্তের যুক্তি হওয়া উচিত - এটি তাদের মধ্যে কাট-পেস্ট করা যায়। নতুন অন্তর শুরু করার যুক্তি যদি হয় তবে এটি পরিবর্তন করার কোড। আদর্শভাবে এটি বিমূর্ত হবে, অতএব; পুনরাবৃত্তির সিটিইতে এটি করা চ্যালেঞ্জের হতে পারে।

EXISTS()দফা একটি recursive কোটে মধ্যে সমষ্টিগত ফাংশন ব্যবহার করতে পারবেন না হওয়ার জল বেরিয়ে যাওয়ার পথ নেই। এটি কেবল এটিই দেখায় যে কোনও ব্যবধানের মধ্যে পড়া দিনগুলি ইতিমধ্যে ইতিমধ্যে বর্তমান ব্যবধানে রয়েছে কিনা।

লজিক ক্লজগুলির বাসা বাঁধার বিষয়ে কোনও জাদু নেই। যদি এটি অন্য কোনও রূপে পরিষ্কার হয়, বা নেস্টেড CASEs ব্যবহার করে বলুন, এটিকে এভাবে রাখার কোনও কারণ নেই।

চূড়ান্তটি SELECTপছন্দসই বিন্যাসে আউটপুট দেওয়া হয়।

পিকে চালু Src.IDরাখা এই পদ্ধতির পক্ষে কার্যকর নয়। একটি ক্লাস্টারড সূচকটি দুর্দান্ত (ContractID,dt)হবে বলে আমি মনে করি।

কয়েকটি রুক্ষ প্রান্ত রয়েছে। দিনগুলি ডাউ সিকোয়েন্সে ফিরে আসে না, তবে ক্যালেন্ডার ক্রমগুলিতে তারা উত্স ডেটাতে উপস্থিত হয়। @ হেল্পারের সাথে করণীয় সব কিছুই ক্লুঙ্কি এবং এটি ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে আছ আমি প্রতিদিন এক বিট ব্যবহার করার পরিবর্তে এবং বাইনারি ফাংশনগুলি ব্যবহার করার ধারণাটি পছন্দ করি LIKE। কিছু সহায়ক সিটিইকে যথাযথ সূচকের সাহায্যে টেম্প টেবিলের মধ্যে পৃথক করা নিঃসন্দেহে সহায়তা করবে।

এর সাথে অন্যতম চ্যালেঞ্জ হ'ল একটি "সপ্তাহ" কোনও মানক ক্যালেন্ডারের সাথে একত্রিত হয় না, বরং ডেটা দ্বারা চালিত হয় এবং যখন নির্ধারিত হয় যে একটি নতুন বিরতি শুরু করা উচিত ts একটি "সপ্তাহ", বা কমপক্ষে একটি বিরতি, একদিন থেকে পুরো ডেটাসেট বিস্তৃত হতে পারে।


স্বার্থের জন্য, বিভিন্ন পরিবর্তনের পরে জিওফের নমুনা ডেটার (তার জন্য ধন্যবাদ!) বিপরীতে আনুমানিক ব্যয় এখানে:

                                             estimated cost

My submission as is w/ CTEs, Geoff's data:      791682
Geoff's data, cluster key on (ContractID, dt):   21156.2
Real table for MissingDays:                      21156.2
Numbered as table UCI=(ContractID, rn):             16.6115    26s elapsed.
                  UCI=(rn, ContractID):             41.9845    26s elapsed.
MissingDays as refactored to simple lookup          16.6477    22s elapsed.
Weekdays as varchar(30)                             13.4013    30s elapsed.

সারিগুলির আনুমানিক এবং প্রকৃত সংখ্যা বন্যভাবে পৃথক।

পরিকল্পনার একটি টেবিল স্পু রয়েছে, সম্ভবত পুনরাবৃত্তির সিটিইর ফলস্বরূপ। বেশিরভাগ ক্রিয়াটি একটি ওয়ার্কটেবলে চলে আসে যা:

Table 'Worktable'.   Scan count       2, logical reads 4 196 269, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'MissingDays'. Scan count 464 116, logical reads   928 232, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Numbered'.    Scan count 484 122, logical reads 1 475 467, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

পুনরাবৃত্তির যেভাবে বাস্তবায়ন হয়েছে, আমার ধারণা!


ধন্যবাদ. এটি নমুনা ডেটাতে সঠিক এবং অনুকূল ফলাফল দেয়। আমি এখন এটি বাস্তব তথ্য পরীক্ষা করব। একটি সাইড নোট: MAX(g.IntervalStart), বিজোড় বলে মনে হয় কারণ g.IntervalStartরয়েছে GROUP BY। আমি এটি একটি সিনট্যাক্স ত্রুটি দেওয়ার প্রত্যাশা করেছি, তবে এটি কার্যকর হয়। এটা ঠিক g.IntervalStart as StartDTভিতরে থাকা উচিত SELECT? অথবা g.IntervalStartমধ্যে করা উচিত হবে না GROUP BY?
ভ্লাদিমির বারানভ 11

আমি বাস্তব ডেটাতে ক্যোয়ারী চালানোর চেষ্টা করেছি এবং 10 মিনিটের পরে আমাকে এটি বন্ধ করতে হয়েছিল। এটি সম্ভবত সম্ভবত যদি সিটিই MissingDaysএবং Numberedসঠিক সূচী সহ টেম্প টেবিলগুলি প্রতিস্থাপন করা হয়, তবে এটির নৈপুণ্য কার্যকর হতে পারে have আপনি কোন সূচকগুলি সুপারিশ করবেন? আমি আগামীকাল সকালে চেষ্টা করতে পারে।
ভ্লাদিমির বারানভ

আমি মনে করি Numberedকোনও টেম্প টেবিল এবং ক্লাস্টার ইনডেক্সের সাথে প্রতিস্থাপন করা একদম (ContractID, rn)চলবে। সম্পর্কিত পরিকল্পনাটি তৈরি করতে কোনও বড় ডেটাসেট ছাড়াই এটি অনুমান করা কঠিন। Physicalising MissingDatesইনডেক্স সঙ্গে (StartDay, FollowingDayInt)ভাল, খুব হবে।
মাইকেল গ্রিন

ধন্যবাদ। আমি এখনই এটি চেষ্টা করতে পারি না, তবে আমি আগামীকাল সকালে করব।
ভ্লাদিমির বারানভ

আমি এটি অর্ধ মিলিয়ন সারি ডেটা সেটটিতে ব্যবহার করে দেখেছি (বিদ্যমান ডেটা সেটটি, বিভিন্ন কন্ট্রাক্টআইডিসহ 4,000 বার প্রতিলিপি করা হয়েছে)। এটি প্রায় 15 মিনিট ধরে চলছে এবং এখন পর্যন্ত 30 গিগাবাইট টেম্পিডবি স্থান নিয়েছে। সুতরাং আমি আরও কিছু অপ্টিমাইজেশন প্রয়োজন হতে পারে বলে মনে করি। আপনি যদি সহায়ক মনে করেন তবে এটি প্রসারিত পরীক্ষার ডেটা
জিফ প্যাটারসন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.