ใช่ฉันรู้ว่านี่เป็นโพสต์เก่า คิดว่าฉันจะให้ความแตกต่างกับสิ่งต่าง ๆ แม้ว่าอายุของมัน เฮ้ ... และขอโทษด้วย ฉันเพิ่งรู้ว่าฉันเกือบซ้ำซ้อนกับสิ่งที่ @jyao โพสต์ไว้ด้านบน
จากการแก้ไขคำถามเดิมของ OP ปัจจุบันฉันไม่สามารถเข้าใจได้ว่าทำไมผู้คนที่โพสต์คำตอบที่พวกเขาทำ
เมื่อดูการแก้ไขแล้วฉันพบคำถามเดิมและโพสต์ไว้ด้านล่าง ...
ฉันมีอนุกรมเวลาตั้งแต่ 1.1.1996 - 30.8.2014 ในฐานข้อมูล SQL เช่น กับตาราง "db.dbo.datestable"
ฉันต้องกำหนดวันที่ซึ่งเป็น "วันศุกร์ที่ 3 ของแต่ละเดือน" สำหรับช่วงวันที่นี้ใน SQL
ฉันคาดว่าฉันควรใช้ "DENSE_RANK ()" และ "PARTITION BY ()" เพื่อตั้งค่า "rank = 3" อย่างไรก็ตามฉันใหม่กับ SQL และไม่สามารถค้นหารหัสที่ถูกต้องได้
คุณช่วยแก้ปัญหานี้ได้ไหม?
ส่วนหนึ่งของคำถามดั้งเดิมที่ฉันได้เน้นย้ำว่าเป็นกุญแจสำคัญ ฉันอาจไม่ถูกต้องอย่างแน่นอน แต่สำหรับฉันแล้ว OP พบว่าเขามีตาราง "ปฏิทิน" ที่เรียกว่า "dbo.datestable" และสำหรับฉันแล้วนั่นทำให้แตกต่างกันมากและตอนนี้ฉันเข้าใจว่าทำไมคำตอบหลายข้อ คือสิ่งที่พวกเขารวมถึงที่สร้างวันเพราะมันถูกโพสต์เมื่อวันที่ 10 พฤศจิกายน ... หนึ่งวันหลังจากการแก้ไขครั้งสุดท้ายในคำถามซึ่งลบร่องรอยสุดท้ายของการอ้างอิงถึง "dbo.datestable"
อย่างที่ฉันพูดฉันอาจผิด แต่นี่คือการตีความคำถามดั้งเดิมของฉัน
ฉันมีตาราง "ปฏิทิน" ชื่อ "dbo.datestable" เมื่อกำหนดช่วงวันที่ใด ๆ ที่ครอบคลุมโดยตารางนั้นฉันจะคืนวันที่ที่เป็นวันศุกร์ที่ 3 ของแต่ละเดือนภายในช่วงวันที่ที่กำหนดได้อย่างไร
เนื่องจากวิธีการทั่วไปในการทำเช่นนี้ได้รับการคุ้มครองแล้วฉันจะเพิ่มทางเลือกที่อาจเป็นประโยชน์สำหรับบางคน
ลองจำลองสองคอลัมน์ที่ฉันคิดว่า OP จะมีอยู่ในตาราง แน่นอนฉันเดาชื่อคอลัมน์ โปรดย่อยสิ่งที่คอลัมน์เทียบเท่าสำหรับตาราง "ปฏิทิน" ของคุณ นอกจากนี้ฉันกำลังทำสิ่งนี้ทั้งหมดใน TempDB ดังนั้นฉันจึงไม่ได้มีโอกาสรบกวนตาราง "ปฏิทิน" ที่แท้จริงของใครบางคน
--=================================================================================================
-- Simulate just a couple of the necessary columns of the OPs "Calendar" table.
-- This is not a part of the solution. We're just trying to simulate what the OP has.
--=================================================================================================
--===== Variables to control the dates that will appear in the "Calendar" table.
DECLARE @StartDT DATETIME
,@EndDT DATETIME
;
SELECT @StartDT = '1900' --Will be inclusive start of this year in calculations.
,@EndDT = '2100' --Will be exclusive start of this year in calculations.
;
--===== Create the "Calendar" table with just enough columns to simulate the OP's.
CREATE TABLE #datestable
(
TheDate DATETIME NOT NULL
,DW TINYINT NOT NULL --SQL standard abbreviate of "Day of Week"
)
;
--===== Populate the "Calendar" table (uses "Minimal Logging" in 2008+ this case).
WITH cteGenDates AS
(
SELECT TOP (DATEDIFF(dd,@StartDT,@EndDT)) --You can use "DAY" instead of "dd" if you prefer. I don't like it, though.
TheDate = DATEADD(dd, ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1, @StartDT)
FROM sys.all_columns ac1
CROSS JOIN sys.all_columns ac2
)
INSERT INTO #datestable WITH (TABLOCK)
SELECT TheDate
,DW = DATEDIFF(dd,0,TheDate)%7+1 --Monday is 1, Friday is 5, Sunday is 7 etc.
FROM cteGenDates
OPTION (RECOMPILE) -- Help keep "Minimal Logging" in the presence of variables.
;
--===== Add the expected named PK for this example.
ALTER TABLE #datestable
ADD CONSTRAINT PK_datestable PRIMARY KEY CLUSTERED (TheDate)
;
นอกจากนี้ยังมีการระบุว่าฉันไม่ทราบว่า OP สามารถเปลี่ยนแปลงตาราง "ปฏิทิน" ของเขาได้หรือไม่ดังนั้นสิ่งนี้อาจช่วยเขาไม่ได้ แต่อาจช่วยเหลือผู้อื่นได้ เมื่อทราบแล้วให้เพิ่มคอลัมน์ DWoM (วันของสัปดาห์สำหรับเดือน) หากคุณไม่ชอบชื่อโปรดเปลี่ยนเป็นอะไรก็ได้ที่คุณต้องการในกล่องของคุณเอง
--===== Add the new column.
ALTER TABLE #datestable
ADD DWOM TINYINT NOT NULL DEFAULT (0)
;
ต่อไปเราต้องเติมคอลัมน์ใหม่ OP มีความรู้สึกเช่นนี้ในตำแหน่งเดิมที่ไม่มีการเจือปนของเขา
--===== Populate the new column using the CTE trick for updates so that
-- we can use a Windowing Function in an UPDATE.
WITH cteGenDWOM AS
(
SELECT DW# = ROW_NUMBER() OVER (PARTITION BY DATEDIFF(mm,0,TheDate), DW
ORDER BY TheDate)
,DWOM
FROM #datestable
)
UPDATE cteGenDWOM
SET DWOM = DW#
;
ตอนนี้เนื่องจากเป็นคอลัมน์ที่มีความยาวคงที่ซึ่งเพิ่งสร้างการแบ่งหน้าเป็นจำนวนมากดังนั้นเราจึงจำเป็นต้องสร้างดัชนีแบบกลุ่มใหม่เพื่อ "บรรจุใหม่" ตารางเพื่อให้มีแถวมากที่สุดต่อหน้าเพื่อประสิทธิภาพสูงสุด
--===== "Repack" the Clustered Index to get rid of the page splits we
-- caused by adding the new column.
ALTER INDEX PK_datestable
ON #datestable
REBUILD WITH (FILLFACTOR = 100, SORT_IN_TEMPDB = ON)
;
เมื่อเสร็จสิ้นแล้วแบบสอบถามที่ทำสิ่งต่าง ๆ เช่นคืนวันศุกร์ที่ 3 ของทุกเดือนในช่วงวันที่ที่กำหนดกลายเป็นเรื่องเล็กน้อยและอ่านได้ค่อนข้างชัดเจน
--===== Return the 3rd Friday of every month included in the given date range.
SELECT *
FROM #datestable
WHERE TheDate >= '1996-01-01' --I never use "BETWEEN" for dates out of habit for end date offsets.
AND TheDate <= '2014-08-30'
AND DW = 5 --Friday
AND DWOM = 3 --The 3rd one for every month
ORDER BY TheDate
;