एसक्यूएल एक सेट आधारित भाषा है और लूप अंतिम उपाय होना चाहिए। तो सेट आधारित दृष्टिकोण सबसे पहले आपको आवश्यक सभी तिथियों को उत्पन्न करना होगा और एक बार में लूपिंग और डालने के बजाय उन्हें एक बार में डालना होगा। हारून बर्ट्रेंड ने बिना लूप के सेट या सीक्वेंस जेनरेट करने पर एक बेहतरीन सीरीज़ लिखी है:
- बिना लूप के एक सेट या सीक्वेंस जेनरेट करें - भाग 1
- बिना लूप के एक सेट या सीक्वेंस जेनरेट करें - भाग 2
- बिना लूप के एक सेट या सीक्वेंस जेनरेट करें - भाग 3
भाग 3 विशेष रूप से प्रासंगिक है क्योंकि यह तिथियों से संबंधित है।
मान लें कि आपके पास कैलेंडर तालिका नहीं है, तो आप अपनी प्रारंभ और समाप्ति तिथियों के बीच तिथियों की सूची बनाने के लिए स्टैक्ड सीटीई विधि का उपयोग कर सकते हैं।
DECLARE @StartDate DATE = '2015-01-01',
@EndDate DATE = GETDATE();
WITH N1 (N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2)
SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY N) - 1, @StartDate)
FROM N3;
मैंने कुछ विवरण छोड़ दिया है कि यह कैसे काम करता है क्योंकि यह लिंक किए गए आलेख में शामिल है, संक्षेप में यह 10 पंक्तियों की हार्ड कोडित तालिका से शुरू होता है, फिर इस तालिका में 100 पंक्तियां (10 x 10) प्राप्त करने के लिए स्वयं से जुड़ता है और फिर इस तालिका में शामिल हो जाता है 1000 पंक्तियों में से 10,000 पंक्तियाँ प्राप्त करने के लिए (मैं इस बिंदु पर रुक गया लेकिन यदि आपको और पंक्तियों की आवश्यकता है तो आप और जोड़ सकते हैं)।
प्रत्येक चरण में आउटपुट एक एकल कॉलम होता है जिसे N
. कहा जाता है 1 के मान के साथ (चीजों को सरल रखने के लिए)। एक ही समय में 10,000 पंक्तियों को कैसे उत्पन्न किया जाए, यह परिभाषित करते हुए, मैं वास्तव में SQL सर्वर को केवल TOP
का उपयोग करके आवश्यक संख्या उत्पन्न करने के लिए कहता हूं। और आपकी आरंभ और समाप्ति तिथि के बीच का अंतर - TOP(DATEDIFF(DAY, @StartDate, @EndDate) + 1)
. इससे अनावश्यक काम से बचा जा सकता है। दोनों तिथियों को शामिल करने के लिए मुझे अंतर में 1 जोड़ना पड़ा।
रैंकिंग फ़ंक्शन का उपयोग करना ROW_NUMBER()
मैं जेनरेट की गई प्रत्येक पंक्ति में एक वृद्धिशील संख्या जोड़ता हूं, फिर मैं तिथियों की सूची प्राप्त करने के लिए इस वृद्धिशील संख्या को आपकी प्रारंभ तिथि में जोड़ता हूं। चूंकि ROW_NUMBER()
1 से शुरू होता है, मुझे इसमें से 1 घटाना होगा ताकि यह सुनिश्चित हो सके कि प्रारंभ तिथि शामिल है।
तब यह केवल उन तारीखों को बाहर करने का मामला होगा जो पहले से मौजूद हैं NOT EXISTS
. का उपयोग करके . मैंने उपरोक्त क्वेरी के परिणामों को उनके अपने सीटीई में संलग्न किया है जिसे dates
. कहा जाता है :
DECLARE @StartDate DATE = '2015-01-01',
@EndDate DATE = GETDATE();
WITH N1 (N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Dates AS
( SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY N) - 1, @StartDate)
FROM N3
)
INSERT INTO MyTable ([TimeStamp])
SELECT Date
FROM Dates AS d
WHERE NOT EXISTS (SELECT 1 FROM MyTable AS t WHERE d.Date = t.[TimeStamp])
यदि आप एक कैलेंडर तालिका बनाना चाहते हैं (जैसा कि लिंक किए गए लेखों में वर्णित है) तो इन अतिरिक्त पंक्तियों को सम्मिलित करना आवश्यक नहीं हो सकता है, आप बस अपना परिणाम सेट फ्लाई पर उत्पन्न कर सकते हैं, जैसे कुछ:
SELECT [Timestamp] = c.Date,
t.[FruitType],
t.[NumOffered],
t.[NumTaken],
t.[NumAbandoned],
t.[NumSpoiled]
FROM dbo.Calendar AS c
LEFT JOIN dbo.MyTable AS t
ON t.[Timestamp] = c.[Date]
WHERE c.Date >= @StartDate
AND c.Date < @EndDate;
अतिरिक्त
आपके वास्तविक प्रश्न का उत्तर देने के लिए आपका लूप इस प्रकार लिखा जाएगा:
DECLARE @StartDate AS DATETIME
DECLARE @EndDate AS DATETIME
DECLARE @CurrentDate AS DATETIME
SET @StartDate = '2015-01-01'
SET @EndDate = GETDATE()
SET @CurrentDate = @StartDate
WHILE (@CurrentDate < @EndDate)
BEGIN
IF NOT EXISTS (SELECT 1 FROM myTable WHERE myTable.Timestamp = @CurrentDate)
BEGIN
INSERT INTO MyTable ([Timestamp])
VALUES (@CurrentDate);
END
SET @CurrentDate = DATEADD(DAY, 1, @CurrentDate); /*increment current date*/
END
SQL Fiddle पर उदाहरण
मैं इस दृष्टिकोण की वकालत नहीं करता, सिर्फ इसलिए कि कुछ केवल एक बार किया जा रहा है इसका मतलब यह नहीं है कि मुझे इसे करने का सही तरीका नहीं दिखाना चाहिए।
आगे की व्याख्या
चूंकि स्टैक्ड सीटीई पद्धति सेट आधारित दृष्टिकोण को अधिक जटिल बना सकती है, इसलिए मैं इसे अनियंत्रित सिस्टम टेबल master..spt_values
का उपयोग करके सरल बनाऊंगा। . यदि आप दौड़ते हैं:
SELECT Number
FROM master..spt_values
WHERE Type = 'P';
आप देखेंगे कि आपको 0 -2047 से सभी नंबर मिल गए हैं।
अब अगर आप दौड़ते हैं:
DECLARE @StartDate DATE = '2015-01-01',
@EndDate DATE = GETDATE();
SELECT Date = DATEADD(DAY, number, @StartDate)
FROM master..spt_values
WHERE type = 'P';
आपको अपनी आरंभ तिथि से लेकर भविष्य में 2047 दिनों तक की सभी तिथियां मिलती हैं। यदि आप एक और जहां क्लॉज जोड़ते हैं तो आप इसे अपनी समाप्ति तिथि से पहले की तारीखों तक सीमित कर सकते हैं:
DECLARE @StartDate DATE = '2015-01-01',
@EndDate DATE = GETDATE();
SELECT Date = DATEADD(DAY, number, @StartDate)
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY, number, @StartDate) <= @EndDate;
अब आपके पास एक सेट आधारित क्वेरी में आवश्यक सभी तिथियां हैं, आप NOT EXISTS
का उपयोग करके अपनी तालिका में पहले से मौजूद पंक्तियों को समाप्त कर सकते हैं
DECLARE @StartDate DATE = '2015-01-01',
@EndDate DATE = GETDATE();
SELECT Date = DATEADD(DAY, number, @StartDate)
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY, number, @StartDate) <= @EndDate
AND NOT EXISTS (SELECT 1 FROM MyTable AS t WHERE t.[Timestamp] = DATEADD(DAY, number, @StartDate));
अंत में आप INSERT
. का उपयोग करके इन तिथियों को अपनी तालिका में सम्मिलित कर सकते हैं
DECLARE @StartDate DATE = '2015-01-01',
@EndDate DATE = GETDATE();
INSERT YourTable ([Timestamp])
SELECT Date = DATEADD(DAY, number, @StartDate)
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY, number, @StartDate) <= @EndDate
AND NOT EXISTS (SELECT 1 FROM MyTable AS t WHERE t.[Timestamp] = DATEADD(DAY, number, @StartDate));
उम्मीद है कि यह किसी तरह यह दिखाने के लिए जाता है कि सेट आधारित दृष्टिकोण न केवल अधिक कुशल है बल्कि सरल भी है।