我有一个表,其中包含列Year,Month,Day,它们是INT,还有其他一些列。桌子的大小非常大。
现在,我想检索日期范围内的数据,比如在@StartDate和@EndDate之间。
我使用的查询是:
SELECT * FROM DataTable
WHERE 10000*M.[YEAR]+100*M.[Month]+M.[Day] >= 10000*YEAR(@StartDate)+100*MONTH(@StartDate)+DAY(@StartDate)
AND 10000*M.[YEAR]+100*M.[Month]+M.[Day] < 10000*YEAR(@EndDate)+100*MONTH(@EndDate)+DAY(@EndDate)
这使得SQL Server扫描索引,而不是搜索索引。
有没有办法让它成为Index Seek?我的意思是如何在这里改变逻辑?
我使用的是SQL Server,但我想它也适用于其他数据库。
答案 0 :(得分:2)
您可能对使用DATEFROMPARTS ( year, month, day )
感兴趣SELECT * FROM DataTable M
WHERE DATEFROMPARTS(M.[Year], M.[Month], M.[Day]) between @StartDate and @EndDate
修改强> 根据评论,我建议创建一个日期持久计算字段,该字段将结合年,月和日
希望这会对你有所帮助
答案 1 :(得分:0)
也许这样的事情会这样做:
DECLARE @Start date = '2016-01-23'
DECLARE @End date = '2017-04-22';
SELECT * FROM DataTable DT
JOIN (
SELECT YEAR(@Start) SY
, MONTH(@Start) SM
, DAY(@Start) SD
, YEAR(@End) EY
, MONTH(@End) EM
, DAY(@End) ED
) MMM
ON (
MMM.SY < DT.YEAR
OR ( MMM.SY = DT.YEAR AND
( MMM.SM < DT.MONTH OR
( MMM.SM = DT.MONTH AND MMM.SD <= DT.DAY )
)
)
)
AND
(
MMM.EY > DT.YEAR
OR ( MMM.EY = DT.YEAR AND
( MMM.EM > DT.MONTH OR
( MMM.EM = DT.MONTH AND MMM.ED >= DT.DAY )
)
)
)
;
答案 2 :(得分:0)
你能告诉我如何使用虚拟数据填充表格吗?
不要使用*,而是提及所有需要列及其数据类型 所以它给出了创造索引的合理想法
如果你回答我的上述问题,那么我会相应地更新我的答案。
试试这个,
DECLARE @Start date = '2000-01-23'
DECLARE @End date = '2017-04-22';
CREATE table MainTable(yr int not null,mnth int not null,d int not null)
create nonclustered index #MainTable_ymd on MainTable(yr ,mnth ,d )
----create nonclustered index #MainTable_y on #MainTable(yr)include(mnth ,d )
----here include(other column) so that it is covering index
----truncate table MainTable
insert into MainTable WITH(tablock)
select year(dt),month(dt),day(dt)
from
(
select top (10000000) dateadd(day,ROW_NUMBER()over(order by a.number)/5,'1900-01-01')dt from master..spt_values a
,master..spt_values b
)t4
I populate maintable with dummy data with 63,25,225 rows
--Solution 1
SELECT *
FROM (
SELECT yr
,mnth
,d
,cast(cast(yr AS VARCHAR(4)) + right('00' + cast(mnth AS VARCHAR), 2) + right('00' + cast(d AS VARCHAR), 2) AS DATE) dt
FROM MainTable
WHERE yr >= year(@Start)
AND yr <= year(@End)
) t4
WHERE dt >= @Start
AND dt <= @End
--RETURN
-- Solution 2
-- here put the result in temp table
CREATE table #tmp1(yr int,mnth int,d int,dt date not null)
INSERT INTO #tmp1
SELECT yr
,mnth
,d
,cast(cast(yr AS VARCHAR(4)) + right('00' + cast(mnth AS VARCHAR), 2) + right('00' + cast(d AS VARCHAR), 2) AS DATE) dt
FROM MainTable
WHERE yr >= year(@Start)
AND yr <= year(@End)
create clustered index #tmp1dt on #tmp1(dt)
select * from #tmp1
where dt>=@Start and dt<=@End
--drop table MainTable
drop table #tmp1
我在两个解决方案中都获得了索引。
使用JOhnRC查询我获得表扫描和并行性,使用urlreader我也可以使用相同的样本数据进行表扫描。
@Monah query-- TableScan
答案 3 :(得分:0)
可以我们以这种方式尝试:
select *
from dateCompare
where concat(Eyear, Emonth, Eday) between replace(@startDate, '-', '')
and replace(@endDate, '-', '')
答案 4 :(得分:0)
你可以试试这个。
select * from DataTable M
where
( M.[YEAR] > YEAR(@StartDate)
OR (M.[YEAR] = YEAR(@StartDate)
AND M.[Month] > Month(@StartDate) )
OR (M.[YEAR] = YEAR(@StartDate)
AND M.[Month] = Month(@StartDate)
AND M.[Day] >= Day(@StartDate) )
)
AND
( M.[YEAR] < YEAR(@EndDate)
OR (M.[YEAR] = YEAR(@EndDate)
AND M.[Month] < Month(@EndDate) )
OR (M.[YEAR] = YEAR(@StartDate)
AND M.[Month] = Month(@EndDate)
AND M.[Day] < Day(@EndDate) )
)
答案 5 :(得分:0)
这是另一个肯定在DataTable上使用索引搜索的答案(假设在使用SQL SERVER 2014时索引是(YEAR,MONTH,DAY)。但是,我怀疑这可能比简单的方法花费更长的时间,除非选择的日期范围是非常狭窄。我很想知道它对真实数据的影响。
方法是创建一个包含开始日期和结束日期之间所有可能日期的表格,然后将此表格与YEAR,MONTH和DAY完全匹配的DataTable连接。
-- Paramters setting date range
DECLARE @Start date = '2016-01-23'
DECLARE @End date = '2017-04-22';
-- Create a table of all dates between start and end dates
CREATE TABLE #DATES
(
YY int
, MM int
, DD int
);
-- There are better ways to do this but for the sake of simplicity...
WHILE @Start <= @End
begin
INSERT INTO #DATES VALUES ( YEAR(@Start), MONTH(@Start), DAY(@Start) );
SET @Start = Dateadd(Day,1, @Start);
end ;
-- Finally, join the DataTable to the created dates with exact match
SELECT DT.*
FROM DataTable DT
JOIN #DATES DD
ON DD.YY = DT.YEAR
AND DD.MM = DT.MONTH
AND DD.DD = DT.DAY;
-- Finished with the dates table
DROP TABLE #DATES;
答案 6 :(得分:0)
可以创建一个持久计算列:
CREATE TABLE DataTable(
year int NOT NULL,
month int NOT NULL,
day int NOT NULL,
date AS (DATEFROMPARTS(year, month, day)) PERSISTED NOT NULL
)
...并将其编入索引:
CREATE NONCLUSTERED INDEX IX_CVR_ALL ON DataTable
(date) INCLUDE (...)
替代解决方案:
我创建了一个“日期表”,并填写了1990年到2050年之间的所有日期:
CREATE TABLE T_Temp_DateList(
date date NOT NULL PRIMARY KEY,
year int NOT NULL,
month int NOT NULL,
day int NOT NULL,
)
CREATE NONCLUSTERED INDEX KIX_SMALL
ON T_Temp_DateList(date)
INCLUDE (year, month, day)
“数据表”的索引如下:
CREATE NONCLUSTERED INDEX KIX_YMD
ON T_Temp_DataTable(year, month, day)
并使用了这个查询:
DECLARE @StartDate AS DATE = '2013-12-01'
DECLARE @EndDate AS DATE = '2017-01-31'
SELECT dt.*
FROM T_Temp_DateList dl
INNER JOIN T_Temp_DataTable dt ON dl.year = dt.year AND dl.month = dt.month AND dl.day = dt.day
WHERE dl.date BETWEEN @StartDate AND @EndDate
结果如下,请注意两个索引都是搜索而不是扫描: