আমি খুব সাধারণ ক্যালেন্ডার টেবিল রেখে এটিকে সমাধান করেছি - প্রতি বছর সমর্থিত সময় অঞ্চল অনুযায়ী এক সারি থাকে , স্ট্যান্ডার্ড অফসেট এবং ডিএসটি এবং এর অফসেটের প্রারম্ভের তারিখের সময় / শেষের তারিখ সহ (যদি সেই সময় অঞ্চলটি এটি সমর্থন করে)। তারপরে একটি ইনলাইন, স্কিমা-আবদ্ধ, টেবিল-মূল্যযুক্ত ফাংশন যা উত্স সময় নেয় (অবশ্যই ইউটিসিতে) এবং অফসেট যুক্ত / বিয়োগ করে।
আপনি ডেটা বড় অংশের বিরুদ্ধে প্রতিবেদন করতে পারলে স্পষ্টতই এটি কখনও খুব ভাল সম্পাদন করতে পারে না; বিভাজনটি আপনাকে সাহায্য করবে বলে মনে হতে পারে, তবে আপনার এখনও এমন ঘটনা ঘটতে পারে যেখানে এক বছরের শেষ কয়েক ঘন্টা বা পরের বছরের প্রথম কয়েক ঘন্টা আসলে একটি নির্দিষ্ট বছরের সাথে রূপান্তরিত হওয়ার সময় অন্য বছরের সাথে সম্পর্কিত - সুতরাং আপনি কখনই সত্যিকারের পার্টিশন পেতে পারবেন না বিচ্ছিন্নতা, যখন আপনার প্রতিবেদনের সীমা 31 ডিসেম্বর বা 1 জানুয়ারী অন্তর্ভুক্ত না করে থাকে।
বেশ কয়েকটি অদ্ভুত প্রান্তের মামলাগুলি আপনার বিবেচনা করা উচিত:
2014-11-02 05:30 ইউটিসি এবং 2014-11-02 06:30 ইউটিসি উভয়ই পূর্ব সময় অঞ্চলে 01:30 এএম রূপান্তর করে, উদাহরণস্বরূপ (প্রথমবারের মতো 01:30 স্থানীয়ভাবে একটি আঘাত হয়েছিল, এবং তারপরে একটিতে দ্বিতীয়বার যখন ঘড়ির কাঁটা দুটি সকাল 2:00 টা থেকে 1:00 এএম এ ফিরে এসেছিল, এবং আরও আধ ঘন্টা ব্যয় হয়েছে)। সুতরাং আপনাকে কীভাবে প্রতিবেদনের সেই ঘন্টাটি পরিচালনা করতে হবে তা সিদ্ধান্ত নিতে হবে - ইউটিসি অনুসারে, আপনি এই দুটি ঘন্টা একবার ডিএসটি পর্যবেক্ষণকারী টাইম জোনে এক ঘন্টার সাথে ম্যাপ করলে আপনি যা যা পরিমাপ করছেন তার দ্বিগুণ ট্র্যাফিক বা ভলিউম দেখতে হবে। এটি ইভেন্টগুলির ক্রম সহ মজাদার গেমগুলি খেলতেও পারে, যেহেতু যুক্তিযুক্তভাবে অন্য কিছু প্রদর্শিত হওয়ার পরে ঘটতে পারেসময় হওয়ার আগে একবারের পরিবর্তে দুই ঘন্টার পরিবর্তে এক ঘন্টার সাথে সামঞ্জস্য হয়। একটি চূড়ান্ত উদাহরণ একটি পৃষ্ঠাগুলি যা 05:59 ইউটিসি-তে ঘটেছিল, তারপরে 06:00 ইউটিসিতে ঘটে এমন একটি ক্লিক। ইউটিসির সময়গুলিতে এক মিনিটের ব্যবধানে এগুলি ঘটেছিল, তবে পূর্ব সময়ের রূপান্তরিত হওয়ার সময়, দৃশ্যটি 1:59 পূর্বাহ্নে ঘটেছিল এবং ক্লিকটি এক ঘন্টা আগে ঘটেছিল।
2014-03-09 02:30 মার্কিন যুক্তরাষ্ট্রে কখনই ঘটে না। এটি হ'ল সকাল 2:00 টায় আমরা ঘড়িগুলি সকাল 3:00 টায় রোল করি। ব্যবহারকারী সম্ভবত যদি এমন সময়ে প্রবেশ করে এবং আপনাকে এটিটি ইউটিসি তে রূপান্তর করতে বলেন বা আপনার ফর্মটি ডিজাইন করতে বলেছেন যাতে ব্যবহারকারীরা এমন সময় বাছতে না পারে তাই সম্ভবত আপনি কোনও ত্রুটি বাড়িয়ে তুলতে চাইবেন।
এমনকি এই প্রান্তের কেসগুলি মাথায় রেখে আমি এখনও মনে করি আপনার সঠিক পন্থা রয়েছে: ইউটিসিতে ডেটা সংরক্ষণ করুন। কিছু সময় অঞ্চল থেকে অন্য সময় অঞ্চল থেকে ইউটিসি থেকে অন্য সময় অঞ্চলগুলিতে ডেটা ম্যাপ করা অনেক সহজ, বিশেষত যখন বিভিন্ন সময় অঞ্চল বিভিন্ন তারিখে ডিএসটি শুরু / শেষ করে এবং এমনকি একই সময় অঞ্চল বিভিন্ন বছরে বিভিন্ন বিধি ব্যবহার করে স্যুইচ করতে পারে ( উদাহরণস্বরূপ, মার্কিন যুক্তরাষ্ট্র 6 বছর বা তার আগে নিয়মগুলি পরিবর্তন করেছে)।
আপনি এই সমস্তটির জন্য একটি ক্যালেন্ডার টেবিলটি ব্যবহার করতে চাইবেন, কিছুটা বড় CASE
অভিব্যক্তি নয় ( বিবৃতি নয় )। আমি এমএসএসকিউএলটিপস.কম এর জন্য এই তিনটি অংশের সিরিজটি লিখেছি ; আমি মনে করি যে তৃতীয় অংশটি আপনার পক্ষে সবচেয়ে কার্যকর হবে:
http://www.mssqltips.com/sqlservertip/3173/handle-conversion-between-time-zones-in-sql-server--part-1/
http://www.mssqltips.com/sqlservertip/3174/handle-conversion-between-time-zones-in-sql-server--part-2/
http://www.mssqltips.com/sqlservertip/3175/handle-conversion-between-time-zones-in-sql-server--part-3/
এর মধ্যে একটি বাস্তব জীবন্ত উদাহরণ
ধরা যাক আপনার একটি খুব সাধারণ ফ্যাক্ট টেবিল রয়েছে। এক্ষেত্রে আমি কেবলমাত্র যত্ন নেওয়ার বিষয়টি ইভেন্টের সময়, তবে কেবলমাত্র যত্ন নেওয়ার পক্ষে টেবিলটিকে প্রশস্ত করতে কেবল অর্থহীন জিইউডি যুক্ত করব। আবার, স্পষ্টভাবে বলতে গেলে, তথ্য সারণী কেবলমাত্র ইউটিসি সময় এবং ইউটিসি সময়গুলিতে ইভেন্টগুলি সঞ্চয় করে। এমনকি কলামটিও আমি প্রত্যাহার করে নিয়েছি _UTC
তাই কোনও বিভ্রান্তি নেই।
CREATE TABLE dbo.Fact
(
EventTime_UTC DATETIME NOT NULL,
Filler UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID()
);
GO
CREATE CLUSTERED INDEX x ON dbo.Fact(EventTime_UTC);
GO
এখন, আসুন 10,000,000 সারি দিয়ে আমাদের ফ্যাক্ট টেবিলটি লোড করুন - প্রতি রাতে 3 সেকেন্ড (প্রতি ঘন্টা 1,200 সারি) প্রতিনিধিত্ব করে 2013-12-30 থেকে মধ্যরাত ইউটিসি-এ 2014-12-12 এ 5 টা ইউটিসি-র পরে কিছুটা সময় অবধি। এটি নিশ্চিত করে যে ডেটা এক বছরের সীমানা বিভক্ত করে, পাশাপাশি ডিএসটি একসাথে এবং একাধিক সময় অঞ্চলের পিছনে থাকে। এটি সত্যিই ভীতিজনক মনে হচ্ছে, তবে আমার সিস্টেমে ~ 9 সেকেন্ড সময় নিয়েছে। টেবিলটি প্রায় 325 এমবি হওয়া উচিত।
;WITH x(c) AS
(
SELECT TOP (10000000) DATEADD(SECOND,
3*(ROW_NUMBER() OVER (ORDER BY s1.[object_id])-1),
'20131230')
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
ORDER BY s1.[object_id]
)
INSERT dbo.Fact WITH (TABLOCKX) (EventTime_UTC)
SELECT c FROM x;
এবং কেবল এই সন্ধানের 10 মিমি সারি সারণির বিপরীতে একটি সাধারণ সন্ধানের ক্যোয়ারী কেমন হবে তা দেখানোর জন্য, যদি আমি এই কোয়েরিটি চালাই:
SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, EventTime_UTC), 0),
COUNT(*)
FROM dbo.Fact
WHERE EventTime_UTC >= '20140308'
AND EventTime_UTC < '20140311'
GROUP BY DATEADD(HOUR, DATEDIFF(HOUR, 0, EventTime_UTC), 0);
আমি এই পরিকল্পনাটি পেয়েছি এবং এটি 25 মিলিসেকেন্ডে * ফিরে আসে, 358 টি পাঠ করে, 72 ঘন্টা প্রতি ঘণ্টায় মোট ফিরে আসে:
* আমাদের নিখরচায় এসকিউএল সেন্ট্রি প্ল্যান এক্সপ্লোরার দ্বারা পরিমাপকৃত সময়কাল , যা ফলাফলগুলি বাতিল করে দেয়, সুতরাং এতে ডেটা, স্থানান্তর ইত্যাদির নেটওয়ার্ক ট্রান্সফার সময় অন্তর্ভুক্ত হয় না অতিরিক্ত অস্বীকৃতি হিসাবে, আমি এসকিউএল সেন্ট্রিয়ের জন্য কাজ করি।
এটি একটু বেশি সময় নেয়, স্পষ্টতই, আমি যদি আমার পরিসরটি আরও বড় করে তুলি - এক মাসের ডেটা 258ms সময় নেয়, দু'মাসে 500 মিমি বেশি সময় নেয় এবং আরও অনেক কিছু। সমান্তরালতা এড়ায়:
আপনি এখানে অন্যান্য প্রতিবেদন জিজ্ঞাসাগুলি পূরণের আরও ভাল সমাধান সম্পর্কে চিন্তাভাবনা শুরু করেছেন এবং আপনার আউটপুটটি কোন টাইম জোনে প্রদর্শিত হবে তার সাথে কিছুই করার নেই is আমি তাতে won'tুকব না, আমি কেবল এটিই প্রদর্শন করতে চাই যে টাইম জোনের রূপান্তরটি আপনার প্রতিবেদনের অনুসন্ধানগুলি আরও অনেক বেশি চুষিয়ে তুলবে না এবং আপনি ইতিমধ্যে চওড়াতে পারেন যদি আপনি যথাযথভাবে সমর্থন করেন না এমন বড় পরিসীমা পেয়ে যাচ্ছেন proper ইনডেক্স। যুক্তিটি সঠিক কিনা তা দেখানোর জন্য আমি ছোট তারিখের রেঞ্জগুলিতে আটকে যাচ্ছি এবং আপনার পরিসর-ভিত্তিক রিপোর্টিং কোয়েরিগুলি সময় অঞ্চল রূপান্তরগুলির সাথে বা ছাড়াই পর্যাপ্ত পরিমাণে সম্পাদন করে তা নিশ্চিত করার বিষয়ে আপনাকে চিন্তিত করতে দিন।
ঠিক আছে, এখন আমাদের আমাদের সময় অঞ্চলগুলি সংরক্ষণের জন্য টেবিলগুলির প্রয়োজন (অফসেট সহ, কয়েক মিনিটের মধ্যে, যেহেতু প্রত্যেকে ইউটিসি থেকে কয়েক ঘণ্টারও দূরে থাকে না) এবং প্রতিটি সমর্থিত বছরের জন্য ডিএসটি পরিবর্তনের তারিখ রয়েছে। সরলতার জন্য, আমি উপরের ডেটার সাথে মেলে কয়েকটি সময় অঞ্চল এবং একক বছর প্রবেশ করতে যাচ্ছি।
CREATE TABLE dbo.TimeZones
(
TimeZoneID TINYINT NOT NULL PRIMARY KEY,
Name VARCHAR(9) NOT NULL,
Offset SMALLINT NOT NULL, -- minutes
DSTName VARCHAR(9) NOT NULL,
DSTOffset SMALLINT NOT NULL -- minutes
);
বৈচিত্র্যের জন্য কয়েকটি সময় অঞ্চল অন্তর্ভুক্ত করা হয়েছে, কয়েকটি অর্ধ ঘন্টা অফসেট সহ কিছু, যা ডিএসটি পালন করে না। নোট করুন যে দক্ষিণ গোলার্ধের অস্ট্রেলিয়া আমাদের শীতের সময় ডিএসটি পালন করে, তাই তাদের ঘড়িগুলি এপ্রিল মাসে ফিরে আসে এবং অক্টোবরে এগিয়ে যায়। (উপরের টেবিলটি নামগুলি উল্টে রেখেছে, তবে দক্ষিণ গোলার্ধের সময় অঞ্চলগুলির জন্য কীভাবে এটি কোনও কম বিভ্রান্তিকর করা যায় তা আমি নিশ্চিত নই।)
INSERT dbo.TimeZones VALUES
(1, 'UTC', 0, 'UTC', 0),
(2, 'GMT', 0, 'BST', 60),
-- London = UTC in winter, +1 in summer
(3, 'EST', -300, 'EDT', -240),
-- East coast US (-5 h in winter, -4 in summer)
(4, 'ACDT', 630, 'ACST', 570),
-- Adelaide (Australia) +10.5 h Oct - Apr, +9.5 Apr - Oct
(5, 'ACST', 570, 'ACST', 570);
-- Darwin (Australia) +9.5 h year round
এখন, টিজেড কখন পরিবর্তন হয় তা জানতে একটি ক্যালেন্ডার সারণী। আমি কেবল আগ্রহের সারিগুলি সন্নিবেশ করতে যাচ্ছি (উপরে প্রতিটি সময় অঞ্চল এবং 2014 এর জন্য কেবল ডিএসটি পরিবর্তন)। পিছনে পিছনে গণনার স্বাচ্ছন্দ্যের জন্য, আমি উভয় মুহূর্তটি ইউটিসি-তে সংরক্ষণ করি যেখানে একটি সময় অঞ্চল পরিবর্তন হয় এবং একই মুহূর্তটি স্থানীয় সময়। সময় অঞ্চলগুলি যা ডিএসটি পর্যবেক্ষণ করে না, এটি সারা বছর ধরে স্ট্যান্ডার্ড এবং 1 জানুয়ারি থেকে ডিএসটি "শুরু" হয়।
CREATE TABLE dbo.Calendar
(
TimeZoneID TINYINT NOT NULL FOREIGN KEY
REFERENCES dbo.TimeZones(TimeZoneID),
[Year] SMALLDATETIME NOT NULL,
UTCDSTStart SMALLDATETIME NOT NULL,
UTCDSTEnd SMALLDATETIME NOT NULL,
LocalDSTStart SMALLDATETIME NOT NULL,
LocalDSTEnd SMALLDATETIME NOT NULL,
PRIMARY KEY (TimeZoneID, [Year])
);
আপনি এটিকে অবশ্যই অ্যালগরিদম দিয়ে পপুলেট করতে পারেন (এবং আসন্ন টিপ সিরিজটি কিছু চালাক সেট-বেসড কৌশল ব্যবহার করে, যদি আমি নিজেই তাই বলে থাকি), লুপের পরিবর্তে, ম্যানুয়ালি পপুলেট করুন, আপনার কী আছে। এই উত্তরের জন্য আমি পাঁচ বারের অঞ্চলগুলির জন্য কেবল এক বছর ম্যানুয়ালি آباد করার সিদ্ধান্ত নিয়েছি এবং আমি কোনও অভিনব কৌশলকে বিরক্ত করব না।
INSERT dbo.Calendar VALUES
(1, '20140101', '20140101 00:00','20150101 00:00','20140101 00:00','20150101 00:00'),
(2, '20140101', '20140330 01:00','20141026 00:00','20140330 02:00','20141026 01:00'),
(3, '20140101', '20140309 07:00','20141102 06:00','20140309 03:00','20141102 01:00'),
(4, '20140101', '20140405 16:30','20141004 16:30','20140406 03:00','20141005 02:00'),
(5, '20140101', '20140101 00:00','20150101 00:00','20140101 00:00','20150101 00:00');
ঠিক আছে, সুতরাং আমাদের কাছে আমাদের ফ্যাক্ট ডেটা এবং আমাদের "মাত্রা" সারণী রয়েছে (যখন আমি এটি বলি তখন কুঁকড়ে যাই), সুতরাং যুক্তিটি কী? ঠিক আছে, আমি অনুমান করি আপনি ব্যবহারকারীদের তাদের সময় অঞ্চলটি নির্বাচন করতে এবং ক্যোয়ারির জন্য তারিখের সীমাটি প্রবেশ করতে চলেছেন। আমি এও ধরে নেব যে তারিখের সীমাটি তাদের নিজস্ব সময় অঞ্চলে পুরো দিন হবে; কোন আংশিক দিন, আংশিক ঘন্টা মনে করবেন না। সুতরাং তারা একটি শুরুর তারিখ, শেষের তারিখ এবং একটি সময় জোনে পাস করবে। সেখান থেকে আমরা সেই সময় অঞ্চল থেকে শুরু করে ইউটিসিতে সূচনা / শেষের তারিখ রূপান্তর করতে একটি স্কেলার ফাংশন ব্যবহার করব, যা আমাদের ইউটিসি রেঞ্জের উপর ভিত্তি করে ডেটা ফিল্টার করতে দেয়। একবার এটি হয়ে গেলে এবং এতে আমাদের সমষ্টি সম্পাদন করা হলে, আমরা তারপরে ব্যবহারকারীর কাছে প্রদর্শিত হওয়ার আগে গ্রুপযুক্ত সময়ের রূপান্তরটি উত্স সময় অঞ্চলে ফিরে প্রয়োগ করতে পারি।
স্কেলার ইউডিএফ:
CREATE FUNCTION dbo.ConvertToUTC
(
@Source SMALLDATETIME,
@SourceTZ TINYINT
)
RETURNS SMALLDATETIME
WITH SCHEMABINDING
AS
BEGIN
RETURN
(
SELECT DATEADD(MINUTE, -CASE
WHEN @Source >= src.LocalDSTStart
AND @Source < src.LocalDSTEnd THEN t.DSTOffset
WHEN @Source >= DATEADD(HOUR,-1,src.LocalDSTStart)
AND @Source < src.LocalDSTStart THEN NULL
ELSE t.Offset END, @Source)
FROM dbo.Calendar AS src
INNER JOIN dbo.TimeZones AS t
ON src.TimeZoneID = t.TimeZoneID
WHERE src.TimeZoneID = @SourceTZ
AND t.TimeZoneID = @SourceTZ
AND DATEADD(MINUTE,t.Offset,@Source) >= src.[Year]
AND DATEADD(MINUTE,t.Offset,@Source) < DATEADD(YEAR, 1, src.[Year])
);
END
GO
এবং টেবিলের মূল্যবান ফাংশন:
CREATE FUNCTION dbo.ConvertFromUTC
(
@Source SMALLDATETIME,
@SourceTZ TINYINT
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT
[Target] = DATEADD(MINUTE, CASE
WHEN @Source >= trg.UTCDSTStart
AND @Source < trg.UTCDSTEnd THEN tz.DSTOffset
ELSE tz.Offset END, @Source)
FROM dbo.Calendar AS trg
INNER JOIN dbo.TimeZones AS tz
ON trg.TimeZoneID = tz.TimeZoneID
WHERE trg.TimeZoneID = @SourceTZ
AND tz.TimeZoneID = @SourceTZ
AND @Source >= trg.[Year]
AND @Source < DATEADD(YEAR, 1, trg.[Year])
);
এবং একটি পদ্ধতি যা এটি ব্যবহার করে ( সম্পাদনা করুন : 30 মিনিটের অফসেট গ্রুপিং পরিচালনা করতে আপডেট হয়েছে):
CREATE PROCEDURE dbo.ReportOnDateRange
@Start SMALLDATETIME, -- whole dates only please!
@End SMALLDATETIME, -- whole dates only please!
@TimeZoneID TINYINT
AS
BEGIN
SET NOCOUNT ON;
SELECT @Start = dbo.ConvertToUTC(@Start, @TimeZoneID),
@End = dbo.ConvertToUTC(@End, @TimeZoneID);
;WITH x(t,c) AS
(
SELECT DATEDIFF(MINUTE, @Start, EventTime_UTC)/60,
COUNT(*)
FROM dbo.Fact
WHERE EventTime_UTC >= @Start
AND EventTime_UTC < DATEADD(DAY, 1, @End)
GROUP BY DATEDIFF(MINUTE, @Start, EventTime_UTC)/60
)
SELECT
UTC = DATEADD(MINUTE, x.t*60, @Start),
[Local] = y.[Target],
[RowCount] = x.c
FROM x OUTER APPLY
dbo.ConvertFromUTC(DATEADD(MINUTE, x.t*60, @Start), @TimeZoneID) AS y
ORDER BY UTC;
END
GO
(আপনি সেখানে সংক্ষিপ্ত সার্কিটে যেতে চান বা কোনও পৃথক স্টোরেজ পদ্ধতি ব্যবহার করতে পারেন, ব্যবহারকারী যদি ইউটিসিতে রিপোর্ট করতে চান - স্পষ্টতই ইউটিসি-তে অনুবাদ করা ব্যর্থ ব্যস্ততার কাজ হয়ে যাবে।)
নমুনা কল:
EXEC dbo.ReportOnDateRange
@Start = '20140308',
@End = '20140311',
@TimeZoneID = 3;
41ms * এ ফিরে আসে এবং এই পরিকল্পনাটি উত্পন্ন করে:
* আবার, বাতিল ফলাফল সহ।
2 মাসের জন্য, এটি 507ms এ ফিরে আসে এবং প্ল্যানটি সারি অ্যাকাউন্টগুলির তুলনায় অন্যরকম:
যদিও আরও জটিল এবং রান সময়কে কিছুটা বাড়িয়ে তুলছে, আমি যথেষ্ট আস্থাশীল যে এই ধরণের পদ্ধতির ব্রিজ টেবিল পদ্ধতির চেয়ে অনেক বেশি কার্যকর হবে। এবং এটি একটি ডিবিএ.এস উত্তরের জন্য অফ-কাফ উদাহরণ; আমি নিশ্চিত যে আমার যুক্তি এবং দক্ষতা আমার চেয়ে অনেক বেশি স্মার্ট লোকেরা উন্নত করতে পারে।
আমি যে প্রান্তের বিষয়ে কথা বলি সেগুলি দেখার জন্য আপনি ডেটা ব্যবহার করতে পারেন - ঘড়িগুলি যেদিকে এগিয়ে চলেছে এমন এক ঘণ্টার জন্য কোনও আউটপুটের সারি নেই, যেখানে তারা ঘুরেছে তার জন্য দুটি সারি (এবং সেই ঘন্টাটি দু'বার হয়েছিল)। আপনি খারাপ মান দিয়েও খেলতে পারেন; আপনি যদি 20140309 02:30 পূর্বের সময়টি পাস করেন তবে উদাহরণস্বরূপ, এটি খুব ভালভাবে কাজ করবে না।
আপনার প্রতিবেদনটি কীভাবে কাজ করবে সে সম্পর্কে আমার কাছে সমস্ত অনুমানগুলি নাও থাকতে পারে, তাই আপনাকে কিছুটা সামঞ্জস্য করতে হতে পারে। তবে আমি মনে করি এটিতে বেসিকগুলি অন্তর্ভুক্ত।