2012-06-21 19 views
7

में तारीखों की एक श्रृंखला उत्पन्न करने के लिए कैसे शीर्षक मेरा मतलब है कि कब्जा नहीं करता है, और यह एक डुप्लिकेट हो सकता है।एसक्यूएल सर्वर

यहां लंबा संस्करण है: अतिथि का नाम, उनकी पंजीकरण तिथि और उनकी चेकआउट तिथि दी गई है, मैं प्रत्येक अतिथि के लिए एक पंक्ति कैसे बना सकता हूं कि वे अतिथि थे?

पूर्व: बॉब 7/14 में चेक करता है और 7/17 छोड़ देता है। मैं चाहता हूं कि

('Bob', 7/14), ('Bob', 7/15), ('Bob', 7/16), ('Bob', 7/17) 

मेरे परिणाम के रूप में।

धन्यवाद!

+0

एक नज़र यहाँ है http://stackoverflow.com/questions/1478951/tsql-generate-a-resultset-of-incrementing-dates [1]: http: // stackoverflow ।कॉम/प्रश्न/1478951/tsql-gener-a-resultet-of-incrementing-date – StoicFnord

+3

आम तौर पर, आप नहीं करते हैं। आपके पास एक लुक-अप टेबल है और उन्हें वहां से बाहर निकालें। 'Calendar.date> = user.start_date और calendar.date <= user.leave_date' आप * लूप, या रिकर्सिव क्वेरी का उपयोग करके सेट जेनरेट कर सकते हैं, लेकिन वे लुक-अप टेबल का उपयोग करने के जितना तेज़ नहीं होते हैं। – MatBailie

+0

मैंने एक बहुत ही समान सवाल पूछा, लेकिन मेरा समय घंटों तक नहीं था। आप अपनी ज़रूरत को आसानी से फिट करने के लिए बदल सकते हैं। http://stackoverflow.com/questions/10986344/get-every-hour-for-a-time-range – Limey

उत्तर

23

मैं तर्क दूंगा कि इस विशिष्ट उद्देश्य के लिए नीचे दी गई क्वेरी एक समर्पित लुकअप तालिका का उपयोग करने के रूप में कुशल है।

DECLARE @start DATE, @end DATE; 
SELECT @start = '20110714', @end = '20110717'; 

;WITH n AS 
(
    SELECT TOP (DATEDIFF(DAY, @start, @end) + 1) 
    n = ROW_NUMBER() OVER (ORDER BY [object_id]) 
    FROM sys.all_objects 
) 
SELECT 'Bob', DATEADD(DAY, n-1, @start) 
FROM n; 

परिणाम:

DECLARE @t TABLE 
(
    Member NVARCHAR(32), 
    RegistrationDate DATE, 
    CheckoutDate DATE 
); 

INSERT @t SELECT N'Bob', '20110714', '20110717' 
UNION ALL SELECT N'Sam', '20110712', '20110715' 
UNION ALL SELECT N'Jim', '20110716', '20110719'; 

;WITH [range](d,s) AS 
(
    SELECT DATEDIFF(DAY, MIN(RegistrationDate), MAX(CheckoutDate))+1, 
    MIN(RegistrationDate) 
    FROM @t -- WHERE ? 
), 
n(d) AS 
(
    SELECT DATEADD(DAY, n-1, (SELECT MIN(s) FROM [range])) 
    FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id]) 
    FROM sys.all_objects) AS s(n) 
    WHERE n <= (SELECT MAX(d) FROM [range]) 
) 
SELECT t.Member, n.d 
FROM n CROSS JOIN @t AS t 
WHERE n.d BETWEEN t.RegistrationDate AND t.CheckoutDate; 
----------^^^^^^^ not many cases where I'd advocate between! 
:

Bob  2011-07-14 
Bob  2011-07-15 
Bob  2011-07-16 
Bob  2011-07-17 

मुमकिन है कि आप इस एक सेट के रूप में, एक भी सदस्य के लिए नहीं है, इसलिए यहां की आवश्यकता होगी इस तकनीक अनुकूल करने के लिए एक रास्ता है

परिणाम:

Member d 
-------- ---------- 
Bob  2011-07-14 
Bob  2011-07-15 
Bob  2011-07-16 
Bob  2011-07-17 
Sam  2011-07-12 
Sam  2011-07-13 
Sam  2011-07-14 
Sam  2011-07-15 
Jim  2011-07-16 
Jim  2011-07-17 
Jim  2011-07-18 
Jim  2011-07-19 

रूप @Dems कहे अनुसार, इस के लिए सरल किया जा सकता है: मैं आम तौर पर कुछ मेज पर एक चाल का उपयोग कर ROW_NUMBER() के साथ ऐसा कर

;WITH natural AS 
(
    SELECT ROW_NUMBER() OVER (ORDER BY [object_id]) - 1 AS val 
    FROM sys.all_objects 
) 
SELECT t.Member, d = DATEADD(DAY, natural.val, t.RegistrationDate) 
    FROM @t AS t INNER JOIN natural 
    ON natural.val <= DATEDIFF(DAY, t.RegistrationDate, t.CheckoutDate); 
+0

AFAIK SQL सर्वर के ऑप्टिमाइज़र का अर्थ है कि आपको वास्तव में 'WHERE n <= (SELECT MAX()) की आवश्यकता नहीं है जिसका अर्थ है कि यह और भी सरलीकृत हो सकता है ...' प्राकृतिक AS के साथ (चुनें ROW_NUMBER() ओवर (ऑर्डर आईडी द्वारा) - sys.objects से 1 एएस वैल) चुनें टी। मेम्बर, DATEADD (DAY, natural.val, t.start) @ टी एएस टी इनरर प्राकृतिक प्राकृतिक पर प्राकृतिक जुड़ें <= DATEDIFF (DAY, t.start , t.end) '* [लेकिन, फिर भी, एक सीधी लुक-अप तालिका कम से कम कम CPU चक्रों का उपयोग करने जा रही है।] * – MatBailie

+0

@Dems जब मैंने अपना लक्ष्य लिखना शुरू किया था तो उच्चतम सीमा का उपयोग करना था 'Sys.all_objects' के खिलाफ' TOP'। आप सही हैं कि इसे सरल बनाया जा सकता है। –

+0

धन्यवाद, आपकी क्वेरी वही करता है जो मैं खोज रहा था। एक सवाल - क्या 'रेंज' टेबल पर MAX और MIN का उपयोग करना आवश्यक है? इस उदाहरण में, मैं केवल 'रेंज' के लिए उत्पन्न एक पंक्ति देख रहा हूं, इसलिए अधिकतम या न्यूनतम के लिए केवल एक उम्मीदवार है (जिस स्थिति में मैं केवल सीमा को रखता हूं और नियमित चर में प्रारंभ तिथि)। मैं आपकी एसक्यूएल चॉप से ​​बहुत प्रभावित हूं और उत्सुक हूं कि वहां कुछ सूक्ष्मता है, मुझे याद आ रही है। –

0

यह फिर के लिए काम कर सकते हैं:

with mycte as 
(
    select cast('2000-01-01' as datetime) DateValue, 'Bob' as Name 
    union all 
    select DateValue + 1 ,'Bob' as Name 
    from mycte 
    where DateValue + 1 < '2000-12-31' 
) 
select * 
from mycte 
OPTION (MAXRECURSION 0) 
+2

जिसमें एक "रिकर्सिव सीटीई गिनती"। निम्नलिखित आलेख देखें कि छोटी संख्याओं की गणना करते समय भी वे इतने बुरे क्यों हैं। http://www.sqlservercentral.com/articles/T-SQL/74118/ –

-4

मैं अतिरिक्त रिकॉर्ड बनाने और चेकआउट पर इसे चलाने के लिए एक ट्रिगर बनाएं होगा। वैकल्पिक रूप से, आप एक दैनिक मध्यरात्रि नौकरी कर सकते हैं (यदि आपको अपने डेटाबेस में अद्यतित जानकारी की आवश्यकता है)।

+1

यह वास्तव में एक उत्तर नहीं है - ट्रिगर "अतिरिक्त रिकॉर्ड कैसे बनाता है"? –

+0

@AaronBertrand यह किसी भी भाषा में मामूली प्रोग्रामिंग कार्य है। – Andy

+3

यदि यह बहुत छोटा था, तो ओपी नहीं पूछेगा, है ना? और * इस * भाषा के लिए कुछ कोड के साथ वास्तव में अपने उत्तर का बैक अप लेना मुश्किल नहीं होना चाहिए? –

6

। तो:

select t.name, dateadd(d, seq.seqnum, t.start_date) 
from t left outer join 
    (select row_number() over (order by (select NULL)) as seqnum 
     from t 
    ) seq 
    on seqnum <= datediff(d, t.start_date, t.end_date) 

सीईसी की गणना बहुत तेज हो जाती है, क्योंकि कोई गणना या ऑर्डरिंग की आवश्यकता नहीं होती है। हालांकि, आपको यह सुनिश्चित करने की ज़रूरत है कि तालिका हर समय स्पैन के लिए काफी बड़ी हो।

1

यदि आपके पास "टैली" या "नंबर" तालिका है, तो जीवन इस तरह की चीजों के लिए वास्तविक सरल हो जाता है।

SELECT Member, DatePresent = DATEADD(dd,t.N,RegistrationDate) 
    FROM @t 
    CROSS JOIN dbo.Tally t 
    WHERE t.N BETWEEN 0 AND DATEDIFF(dd,RegistrationDate,CheckoutDate) 
; 

यहां "टैली" तालिका बनाने का तरीका बताया गया है।

--=================================================================== 
--  Create a Tally table from 0 to 11000 
--=================================================================== 
--===== Create and populate the Tally table on the fly. 
SELECT TOP 11001 
     IDENTITY(INT,0,1) AS N 
    INTO dbo.Tally 
    FROM Master.sys.ALL_Columns ac1 
    CROSS JOIN Master.sys.ALL_Columns ac2 
; 
--===== Add a CLUSTERED Primary Key to maximize performance 
    ALTER TABLE dbo.Tally 
    ADD CONSTRAINT PK_Tally_N 
     PRIMARY KEY CLUSTERED (N) WITH FILLFACTOR = 100 
; 
--===== Allow the general public to use it 
    GRANT SELECT ON dbo.Tally TO PUBLIC 
; 
GO 
क्या एक "टैली" तालिका एसक्यूएल में है और यह कैसे को बदलने के लिए एक ओर जहां छोरों और reursive सीटीई की "छुपे हुए RBAR" है कि गिनती, कृपया निम्नलिखित आलेख देखें इस्तेमाल किया जा सकता पर अधिक जानकारी के लिए

http://www.sqlservercentral.com/articles/T-SQL/62867/

संबंधित मुद्दे