একটি সঞ্চিত পদ্ধতি ব্যবহার করে কিভাবে একটি তারিখের পরিসরে প্রতিদিন সারি তৈরি করা যায়?


11

আমি একটি সঞ্চিত পদ্ধতি তৈরি করতে চাই যা একটি নির্দিষ্ট তারিখের সীমাতে প্রতিদিনের জন্য একটি সারণীতে একটি সারি তৈরি করে। সঞ্চিত পদ্ধতি দুটি ইনপুট গ্রহণ করে - ব্যবহারকারীর পছন্দসই তারিখের একটি শুরু তারিখ এবং সমাপ্তির তারিখ।

সুতরাং, আসুন আমি বলি যে আমার মতো একটি টেবিল রয়েছে:

SELECT Day, Currency
FROM ConversionTable

দিন একটি তারিখটাইম, এবং মুদ্রা কেবল একটি পূর্ণসংখ্যা।

জিনিসগুলি সহজ রাখার জন্য, কেবলমাত্র আমি বলি যে আমি সর্বদা এই সন্নিবেশকৃত সারিগুলির জন্য মুদ্রা কলামটি 1 করে রাখতে চাই। সুতরাং, যদি কেউ 'মার্চ 5, 2017' শুরুর তারিখ হিসাবে এবং 'এপ্রিল 11, 2017' শেষের তারিখ হিসাবে ইনপুট করে তবে আমি নীচের সারিগুলি তৈরি করতে চাই:

2017-03-05 00:00:00, 1
2017-03-06 00:00:00, 1
...
2017-04-11 00:00:00, 1

এটি করার জন্য সঞ্চিত প্রক্রিয়াটি কোড করার সর্বোত্তম উপায় কী? আমি আমার পরীক্ষার পরিবেশে এসকিউএল সার্ভার ২০০৮ আর 2 ব্যবহার করছি, তবে আমাদের আসল পরিবেশটি এসকিউএল সার্ভার ২০১২ ব্যবহার করে, তাই যদি 2012 এ নতুন কার্যকারিতা চালু হয় যা এই কাজটিকে আরও সহজ করে তোলে আমি যদি আমার পরীক্ষা মেশিনটি আপগ্রেড করতে পারি।

উত্তর:


15

একটি বিকল্প একটি পুনরাবৃত্তি সিটিই:

DECLARE @StartDate datetime = '2017-03-05'
       ,@EndDate   datetime = '2017-04-11'
;

WITH theDates AS
     (SELECT @StartDate as theDate
      UNION ALL
      SELECT DATEADD(day, 1, theDate)
        FROM theDates
       WHERE DATEADD(day, 1, theDate) <= @EndDate
     )
SELECT theDate, 1 as theValue
  FROM theDates
OPTION (MAXRECURSION 0)
;

( MAXRECURSIONনীচে স্কট হজিনের মন্তব্যে ইঙ্গিতটি যুক্ত করা হয়েছে।)


হ্যাঁ! আমি "সংখ্যার" টেবিল পদ্ধতির (স্কট হডগিনের উত্তর) চিন্তা করেছিলাম। খুব বড় সংখ্যক দিনের জন্য, এটি আরও ভাল পারফর্ম করতে পারে। এবং একটি দুর্দান্ত পুনঃব্যবহারযোগ্য পন্থা (জন সি এর উত্তর) সর্বদা ভাল। এটি আমিই ভাবতে পারি কেবল সহজ এবং সর্বাধিক সোজা-এগিয়ে উত্তর।
আরডিফোজ

1
শুধু মনে রাখবেন - আপনি যখন MAXRECURSION নির্দিষ্ট করবেন না তখন পুনরাবৃত্তির একটি ডিফল্ট সীমা 100 থাকে - যখন বিস্তৃত তারিখের সীমাটি উত্তীর্ণ হয় তখন আপনার এসপিকে ফুঁকতে আমি ঘৃণা করি :)
স্কট হডগিন

2
@ স্কট হজগিন আপনি ঠিক বলেছেন সমাধানের MAXRECURSIONজন্য প্রশ্নের সাথে ইঙ্গিতটি যুক্ত করা হয়েছে।
আরডিফোজ

এই পদ্ধতির ইচ্ছাকৃত বা অনিচ্ছাকৃত আচরণের শেষ তারিখের পরবর্তী দিনটি অন্তর্ভুক্ত করার একটি সময় অন্তর্ভুক্ত রয়েছে। যদি এই আচরণটি অনাকাঙ্ক্ষিত হয় তবে WHEE থেকে DATEADD (DAY, 1, তারিখ) তে পরিবর্তন করুন <@ শেষ তারিখ
ক্রিস পোর্টার

1
@ ক্রিস্পোর্টার - দুর্দান্ত পয়েন্ট! যাইহোক, আপনি যদি এটি তৈরি করেন তবে DATEADD(DAY, 1, theDate) < @EndDateউভয় ডেটটাইম মানগুলির একই সময়ের উপাদান থাকা সীমাটি আপনি শেষ করতে পারবেন না। আমি উত্তরটি যথাযথভাবে সংশোধন করেছি, তবে ব্যবহার করছি <= @EndDate। আপনি না থাকলে চান পরিসীমা মান শেষে নির্বিশেষে অন্তর্ভুক্ত, তারপর < @EndDateপ্রকৃতপক্ষে সঠিক হবে।
আরডিফোজ

6

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

উদাহরণ স্বরূপ

Select * from [dbo].[udf-Range-Date]('2017-03-05','2017-04-11','DD',1) 

রিটার্নস

RetSeq  RetVal
1   2017-03-05 00:00:00.000
2   2017-03-06 00:00:00.000
3   2017-03-07 00:00:00.000
4   2017-03-08 00:00:00.000
5   2017-03-09 00:00:00.000
...
36  2017-04-09 00:00:00.000
37  2017-04-10 00:00:00.000
38  2017-04-11 00:00:00.000

আগ্রহী হলে ইউডিএফ

CREATE FUNCTION [dbo].[udf-Range-Date] (@R1 datetime,@R2 datetime,@Part varchar(10),@Incr int)
Returns Table
Return (
    with cte0(M)   As (Select 1+Case @Part When 'YY' then DateDiff(YY,@R1,@R2)/@Incr When 'QQ' then DateDiff(QQ,@R1,@R2)/@Incr When 'MM' then DateDiff(MM,@R1,@R2)/@Incr When 'WK' then DateDiff(WK,@R1,@R2)/@Incr When 'DD' then DateDiff(DD,@R1,@R2)/@Incr When 'HH' then DateDiff(HH,@R1,@R2)/@Incr When 'MI' then DateDiff(MI,@R1,@R2)/@Incr When 'SS' then DateDiff(SS,@R1,@R2)/@Incr End),
         cte1(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
         cte2(N)   As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
         cte3(N,D) As (Select 0,@R1 Union All Select N,Case @Part When 'YY' then DateAdd(YY, N*@Incr, @R1) When 'QQ' then DateAdd(QQ, N*@Incr, @R1) When 'MM' then DateAdd(MM, N*@Incr, @R1) When 'WK' then DateAdd(WK, N*@Incr, @R1) When 'DD' then DateAdd(DD, N*@Incr, @R1) When 'HH' then DateAdd(HH, N*@Incr, @R1) When 'MI' then DateAdd(MI, N*@Incr, @R1) When 'SS' then DateAdd(SS, N*@Incr, @R1) End From cte2 )

    Select RetSeq = N+1
          ,RetVal = D 
     From  cte3,cte0 
     Where D<=@R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1) 
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1) 
*/

@ রবভি খুব সত্য। অনেক দিন আগে শিখেছি একটি সত্য উত্তর নেই।
জন ক্যাপেলটি

জবাবের জন্য ধন্যবাদ. দেখে মনে হচ্ছে এটি করার বিভিন্ন উপায় রয়েছে :) অন্য লোকটি আমার প্রশ্নের উত্তরটি এমনভাবে দিয়েছে যাতে ডাকা পছন্দসই আউটপুটটি সুনির্দিষ্টভাবে সমাধান করা হয়েছিল, তবে আপনি ঠিক বলেছেন যে এটি আরও নমনীয়।
রব ভি

@ জনক্যাপেল্টিটি আমার যা করা দরকার তার জন্য এটি নিখুঁত, তবে উইকেন্ডস এবং হলিডেগুলি অপসারণ করার জন্য আমার এটির প্রয়োজন ছিল ... আমি শেষ লাইনটি নীচের দিকে পরিবর্তন করে করেছি যেখানে ডি <= @ আর 2 এবং তারিখ পার্ট (সাপ্তাহিক, ডি) নেই (1,7) এবং এতে নেই (ছুটির দিন থেকে ছুটির দিন নির্বাচন করুন)। ছুটির দিনগুলি একটি টেবিল যা ছুটির তারিখগুলি ধারণ করে।
মেটে

@ ম্যাট ভাল হয়েছে! সাপ্তাহিক ছুটির দিন এবং ছুটির দিনগুলি বাদ দিতে আমিও একই কাজ করতাম। :)
জন ক্যাপেল্টি

3

উদাহরণস্বরূপ তারিখের মাত্রা সারণীটি কীভাবে তৈরি করা যায় তার বিষয়ে অ্যারন বার্ট্র্যান্ডের পোস্ট ব্যবহার করে আমি এগুলি নিয়ে এসেছি:

DECLARE @StartDate DATE ='2017-03-05 00:00:00'
DECLARE @EndDate DATE ='2017-04-11 00:00:00'

Declare @DateTable table ([date]       DATE PRIMARY KEY);

-- use the catalog views to generate as many rows as we need

INSERT @DateTable ([date])
SELECT d
FROM (
    SELECT d = DATEADD(DAY, rn - 1, @StartDate)
    FROM (
        SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate)) rn = ROW_NUMBER() OVER (
                ORDER BY s1.[object_id]
                )
        FROM sys.all_objects AS s1
        CROSS JOIN sys.all_objects AS s2
        -- on my system this would support > 5 million days
        ORDER BY s1.[object_id]
        ) AS x
    ) AS y;

SELECT *
FROM @DateTable
ORDER BY [date]

আপনার সঞ্চিত পদ্ধতিতে আপনার এই ধরণের যুক্তি রাখতে এবং আপনার যা প্রয়োজন তা যুক্ত করতে সক্ষম হওয়া উচিত।


2

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

with hours as
   (select 0 clockhour union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 10 union select 11 union select 12 
    union select 13 union select 14 union select 15 union select 16 union select 17 union select 18 union select 19 union select 20 union select 21 union select 22 union select 23)
, days as
   (select *
    from 
       (select to_number(n0.number || n1.number, '99') daynum
        from
           (select 0 as number union select 1 union select 2 union select 3) as n0
           cross join
           (select 1 as number union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0) as n1)
    where daynum between 1 and 31)
, months as
   (select 1 as monthnum, 'jan' as themonth, 31 as numdays union select 2, 'feb', 28 union select 3, 'mar', 31 union select 4, 'apr', 30 union select 5, 'may', 31 union select 6, 'jun', 30 
    union select 7, 'jul', 31 union select 8, 'aug', 31 union select 9, 'sep', 30 union select 10, 'oct', 31 union select 11, 'nov', 30 union select 12, 'dec', 31)
, years as
   (select century || decade || yr as yr
    from 
       (select 19 century union select 20) 
    cross join
       (select 0 decade union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) 
    cross join
       (select 0 yr union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9))
select cast(daynum || '-' || themonth || '-' || yr || ' ' || clockhour || ':00:00' as timestamp) dayhour
from hours
cross join days
cross join months
cross join years
where cast(daynum || '-' || themonth || '-' || yr || ' ' || clockhour || ':00:00' as timestamp) 
between date_trunc('month', dateadd('month', -$MONTHS_AGO, getdate()))
and     date_trunc('month', dateadd('month', $MONTHS_AHEAD, getdate()))
and   daynum <= numdays
order by yr, monthnum, daynum, clockhour;
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.