这取代了我之前的答案,以便完全回答问题。事实证明,这是比看起来要困难得多的事情之一。我真的认为下面的答案 #2(创建计算字段)将是可靠的后备答案,但事实证明您不能在 PERSISTED 字段中使用带有 DATE 类型的 CAST() ,从而增加了复杂性。
这是答案 1,如果...则使用它
- 您需要经常进行此查询
- 您有大量数据,需要索引
- 您不介意更改表结构
“正确的方法”是通过添加包含实际 MONTH/YEAR 的字段和当天的虚拟“1st”来添加作为实际 DATE 的列。将其存储为 DATE,然后将其用于查询。这可以被索引,并且会尽可能快。
这是答案 2,如果...则使用它
- 您需要经常进行此查询
- 您的数据很少,因此不需要索引
- 您不想更改表结构
您可以使用以下任一方法在表上创建计算字段。这样,每当您编写查询时,您就可以使用日期字段并且生活很简单——除非您不会点击索引。
ALTER TABLE table1
ADD THE_DATE1 AS CONVERT(VARCHAR(4), THE_YEAR ) + '-' + CONVERT(VARCHAR(2), THE_MONTH) + '-01'
ALTER TABLE table1
ADD THE_DATE2 AS DATEADD(mm, THE_MONTH -1, dateadd(yy,THE_YEAR-1900,'19000101'))
ALTER TABLE table1 -- SQL212+
ADD THE_DATE3 AS DATEFROMPARTS(THE_YEAR, THE_MONTH, 1)
这是答案 3,如果...则使用它
- 您需要经常进行此查询
- 您有大量数据,确实需要索引
- 你不介意疯狂的查询
您可以使用疯狂的 WHERE 语句来获取数据。下面有两个版本,第二个版本包含一些 AND 语句,可以帮助 SQL SERVER 命中某些索引(因为 SQL 不喜欢在 WHERE 语句中有一堆 OR 运算符)。
DECLARE @table1 AS TABLE (
ID INT ,
THE_YEAR INT ,
THE_MONTH INT ,
THE_MONEY INT
)
INSERT INTO @table1
VALUES ( 1, 2015, 5, 20 ),
( 2, 2015, 7, 50 ),
( 3, 2015, 9, 8 ),
( 4, 2015, 10, 60 ),
( 5, 2015, 12, 30 ),
( 6, 2016, 1, 90 ),
( 7, 2016, 2, 120 ),
( 8, 2017, 1, 220 ), -- Added for testing
( 9, 2017, 2, 320 ) -- Added for testing
DECLARE @START_YEAR INT = 2015;
DECLARE @START_MONTH INT = 7;
DECLARE @STOP_YEAR INT = 2015;
DECLARE @STOP_MONTH INT = 10;
-- QUERY THAT ANSWERS YOUR QUESTION
SELECT *
FROM @table1
WHERE (
( @START_YEAR <> @STOP_YEAR AND THE_YEAR = @START_YEAR AND THE_MONTH >= @START_MONTH )
OR
( @START_YEAR <> @STOP_YEAR AND THE_YEAR > @START_YEAR AND THE_YEAR < @STOP_YEAR )
OR
( @START_YEAR <> @STOP_YEAR AND THE_YEAR = @STOP_YEAR AND THE_MONTH <= @STOP_MONTH )
OR
-- Handle when Start/Stop year are the same
( @START_YEAR = @STOP_YEAR AND THE_MONTH >= @START_MONTH AND THE_MONTH <= @STOP_MONTH )
)
ORDER BY THE_YEAR ,
THE_MONTH
-- QUERY THAT ANSWERS YOUR QUESTION, with INDEX optimizations
-- because once you have a bunch of data, this may help you hit on an index.
SELECT *
FROM @table1
WHERE THE_YEAR >= @START_YEAR -- Will hit an INDEX if you have one on THE_YEAR
AND THE_YEAR <= @STOP_YEAR -- Will hit an INDEX if you have one on THE_YEAR
AND
(
( @START_YEAR <> @STOP_YEAR AND THE_YEAR = @START_YEAR AND THE_MONTH >= @START_MONTH )
OR
( @START_YEAR <> @STOP_YEAR AND THE_YEAR > @START_YEAR AND THE_YEAR < @STOP_YEAR )
OR
( @START_YEAR <> @STOP_YEAR AND THE_YEAR = @STOP_YEAR AND THE_MONTH <= @STOP_MONTH )
OR
-- Handle when Start/Stop year are the same
( @START_YEAR = @STOP_YEAR AND THE_MONTH >= @START_MONTH AND THE_MONTH <= @STOP_MONTH )
)
ORDER BY THE_YEAR ,
THE_MONTH