함수를 사용하여 두 날짜 사이의 날짜 목록 가져오기
내 질문은 이 MySQL 질문과 비슷하지만 SQL Server를 대상으로 합니다.
두 날짜 사이의 날짜 목록을 반환하는 함수 또는 쿼리가 있습니까?예를 들어 폭발 날짜라는 함수가 있다고 가정합니다.
SELECT ExplodeDates('2010-01-01', '2010-01-13');
이렇게 하면 다음 값을 가진 단일 열 테이블이 반환됩니다.
2010-01-01
2010-01-02
2010-01-03
2010-01-04
2010-01-05
2010-01-06
2010-01-07
2010-01-08
2010-01-09
2010-01-10
2010-01-11
2010-01-12
2010-01-13
달력/숫자표가 도움이 될 것 같습니다.
갱신하다
제공된 세 가지 코드 답변을 살펴보기로 결정했으며, 실행 결과는 전체 배치의 %입니다.
낮을수록 좋습니다.
비록 숫자표 솔루션(KM과 인색한 잭 모두 답변에 사용됨)이 제가 가장 좋아하는 것임에도 불구하고, 저는 그것이 가장 빨랐기 때문에 롭 팔리의 답변을 받아들였습니다.롭 팔리의 것이 3분의 2 더 빨랐습니다.
업데이트 2
알리비아의 대답은 훨씬 더 간결합니다.저는 수락된 답변을 변경했습니다.
이 몇 줄은 SQL Server에서 이 질문에 대한 간단한 대답입니다.
WITH mycte AS
(
SELECT CAST('2011-01-01' AS DATETIME) DateValue
UNION ALL
SELECT DateValue + 1
FROM mycte
WHERE DateValue + 1 < '2021-12-31'
)
SELECT DateValue
FROM mycte
OPTION (MAXRECURSION 0)
다음과 같은 방법을 사용해 보십시오.
CREATE FUNCTION dbo.ExplodeDates(@startdate datetime, @enddate datetime)
returns table as
return (
with
N0 as (SELECT 1 as n UNION ALL SELECT 1)
,N1 as (SELECT 1 as n FROM N0 t1, N0 t2)
,N2 as (SELECT 1 as n FROM N1 t1, N1 t2)
,N3 as (SELECT 1 as n FROM N2 t1, N2 t2)
,N4 as (SELECT 1 as n FROM N3 t1, N3 t2)
,N5 as (SELECT 1 as n FROM N4 t1, N4 t2)
,N6 as (SELECT 1 as n FROM N5 t1, N5 t2)
,nums as (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as num FROM N6)
SELECT DATEADD(day,num-1,@startdate) as thedate
FROM nums
WHERE num <= DATEDIFF(day,@startdate,@enddate) + 1
);
그런 다음 다음을 사용합니다.
SELECT *
FROM dbo.ExplodeDates('20090401','20090531') as d;
편집됨(승인 후):
참고하시기 바랍니다...이미 충분히 큰 숫자 테이블이 있는 경우 다음을 사용해야 합니다.
CREATE FUNCTION dbo.ExplodeDates(@startdate datetime, @enddate datetime)
returns table as
return (
SELECT DATEADD(day,num-1,@startdate) as thedate
FROM nums
WHERE num <= DATEDIFF(day,@startdate,@enddate) + 1
);
다음을 사용하여 이러한 테이블을 만들 수 있습니다.
CREATE TABLE dbo.nums (num int PRIMARY KEY);
INSERT dbo.nums values (1);
GO
INSERT dbo.nums SELECT num + (SELECT COUNT(*) FROM nums) FROM nums
GO 20
이 선들은 1M개의 행을 포함하는 숫자 표를 만듭니다...하나씩 삽입하는 것보다 훨씬 빠릅니다.
쿼리 옵티마이저는 쿼리를 전혀 단순화할 수 없기 때문에 BEGIN과 END를 포함하는 함수를 사용하여 BreakthroughDates 함수를 생성하면 안 됩니다.
이것은 윌의 이전 게시물에서 수정된 당신이 원하는 것을 정확히 수행합니다.도우미 테이블이나 루프가 필요하지 않습니다.
WITH date_range (calc_date) AS (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, '2010-01-13') - DATEDIFF(DAY, '2010-01-01', '2010-01-13'), 0)
UNION ALL SELECT DATEADD(DAY, 1, calc_date)
FROM date_range
WHERE DATEADD(DAY, 1, calc_date) <= '2010-01-13')
SELECT calc_date
FROM date_range;
DECLARE @MinDate DATETIME = '2012-09-23 00:02:00.000',
@MaxDate DATETIME = '2012-09-25 00:00:00.000';
SELECT TOP (DATEDIFF(DAY, @MinDate, @MaxDate) + 1) Dates = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @MinDate)
FROM sys.all_objects a CROSS JOIN sys.all_objects b;
저는 Oracle이지만 MS SQL Server가 connect by clause를 지원한다고 생각합니다.
select sysdate + level
from dual
connect by level <= 10 ;
출력은 다음과 같습니다.
SYSDATE+LEVEL
05-SEP-09
06-SEP-09
07-SEP-09
08-SEP-09
09-SEP-09
10-SEP-09
11-SEP-09
12-SEP-09
13-SEP-09
14-SEP-09
듀얼은 오라클과 함께 제공되는 '더미' 테이블에 불과합니다(1개의 행과 '더미'라는 단어를 단일 열 값으로 포함).
몇 가지 아이디어:
목록 날짜를 반복하기 위해 목록 날짜가 필요한 경우 시작 날짜 및 일 수 매개 변수를 사용하여 날짜를 만들고 사용하는 동안 잠시 반복할 수 있습니다.
C# CLR Stored Procedures를 사용하여 C#에 코드를 작성합니다.
코드에서 데이터베이스 외부에서 이 작업 수행
이 모든 날짜가 이미 데이터베이스에 있습니까? 아니면 두 날짜 사이의 날짜만 알고 싶으십니까?첫 번째인 경우 BETHIN 또는 <= > =를 사용하여 다음 날짜 사이의 날짜를 찾을 수 있습니다.
예:
SELECT column_name(s)
FROM table_name
WHERE column_name
BETWEEN value1 AND value2
OR
SELECT column_name(s)
FROM table_name
WHERE column_name
value1 >= column_name
AND column_name =< value2
아래 제공된 코드에서 하드 코드 값을 변경하기만 하면 됩니다.
DECLARE @firstDate datetime
DECLARE @secondDate datetime
DECLARE @totalDays INT
SELECT @firstDate = getDate() - 30
SELECT @secondDate = getDate()
DECLARE @index INT
SELECT @index = 0
SELECT @totalDays = datediff(day, @firstDate, @secondDate)
CREATE TABLE #temp
(
ID INT NOT NULL IDENTITY(1,1)
,CommonDate DATETIME NULL
)
WHILE @index < @totalDays
BEGIN
INSERT INTO #temp (CommonDate) VALUES (DATEADD(Day, @index, @firstDate))
SELECT @index = @index + 1
END
SELECT CONVERT(VARCHAR(10), CommonDate, 102) as [Date Between] FROM #temp
DROP TABLE #temp
파티에 조금 늦었지만, 저는 이 해결책이 꽤 마음에 듭니다.
CREATE FUNCTION ExplodeDates(@startDate DateTime, @endDate DateTime)
RETURNS table as
return (
SELECT TOP (DATEDIFF(DAY, @startDate, @endDate) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @startDate) AS DATE
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
)
내 기능을 사용하기 전에 "도우미" 테이블을 설정해야 합니다. 데이터베이스당 이 작업을 한 번만 수행하면 됩니다.
CREATE TABLE Numbers
(Number int NOT NULL,
CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
DECLARE @x int
SET @x=0
WHILE @x<8000
BEGIN
SET @x=@x+1
INSERT INTO Numbers VALUES (@x)
END
기능은 다음과 같습니다.
CREATE FUNCTION dbo.ListDates
(
@StartDate char(10)
,@EndDate char(10)
)
RETURNS
@DateList table
(
Date datetime
)
AS
BEGIN
IF ISDATE(@StartDate)!=1 OR ISDATE(@EndDate)!=1
BEGIN
RETURN
END
INSERT INTO @DateList
(Date)
SELECT
CONVERT(datetime,@StartDate)+n.Number-1
FROM Numbers n
WHERE Number<=DATEDIFF(day,@StartDate,CONVERT(datetime,@EndDate)+1)
RETURN
END --Function
사용:
select * from dbo.ListDates('2010-01-01', '2010-01-13')
출력:
Date
-----------------------
2010-01-01 00:00:00.000
2010-01-02 00:00:00.000
2010-01-03 00:00:00.000
2010-01-04 00:00:00.000
2010-01-05 00:00:00.000
2010-01-06 00:00:00.000
2010-01-07 00:00:00.000
2010-01-08 00:00:00.000
2010-01-09 00:00:00.000
2010-01-10 00:00:00.000
2010-01-11 00:00:00.000
2010-01-12 00:00:00.000
2010-01-13 00:00:00.000
(13 row(s) affected)
아마도 당신이 더 쉬운 길을 가고 싶다면, 이것이 그것을 해야 할 것입니다.
WITH date_range (calc_date) AS (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP) - 6, 0)
UNION ALL SELECT DATEADD(DAY, 1, calc_date)
FROM date_range
WHERE DATEADD(DAY, 1, calc_date) < CURRENT_TIMESTAMP)
SELECT calc_date
FROM date_range;
하지만 임시 테이블은 매우 좋은 접근법이기도 합니다.아마도 여러분은 채워진 일정표도 고려해야 할 것입니다.
확실히 숫자 표입니다. 성능이 정말로 필요한 경우 CLR 프로세스/어셈블리에 대한 Mark Redman의 아이디어를 사용할 수도 있습니다.
날짜 표를 만드는 방법(및 초고속 숫자 표를 만드는 방법)
/*Gets a list of integers into a temp table (Jeff Moden's idea from SqlServerCentral.com)*/
SELECT TOP 10950 /*30 years of days*/
IDENTITY(INT,1,1) as N
INTO #Numbers
FROM Master.dbo.SysColumns sc1,
Master.dbo.SysColumns sc2
/*Create the dates table*/
CREATE TABLE [TableOfDates](
[fld_date] [datetime] NOT NULL,
CONSTRAINT [PK_TableOfDates] PRIMARY KEY CLUSTERED
(
[fld_date] ASC
)WITH FILLFACTOR = 99 ON [PRIMARY]
) ON [PRIMARY]
/*fill the table with dates*/
DECLARE @daysFromFirstDateInTheTable int
DECLARE @firstDateInTheTable DATETIME
SET @firstDateInTheTable = '01/01/1998'
SET @daysFromFirstDateInTheTable = (SELECT (DATEDIFF(dd, @firstDateInTheTable ,GETDATE()) + 1))
INSERT INTO
TableOfDates
SELECT
DATEADD(dd,nums.n - @daysFromFirstDateInTheTable, CAST(FLOOR(CAST(GETDATE() as FLOAT)) as DateTime)) as FLD_Date
FROM #Numbers nums
이제 날짜 표가 생겼으니 KM과 같은 함수(NOT A PROC)를 사용하여 날짜 표를 가져올 수 있습니다.
CREATE FUNCTION dbo.ListDates
(
@StartDate DATETIME
,@EndDate DATETIME
)
RETURNS
@DateList table
(
Date datetime
)
AS
BEGIN
/*add some validation logic of your own to make sure that the inputs are sound.Adjust the rest as needed*/
INSERT INTO
@DateList
SELECT FLD_Date FROM TableOfDates (NOLOCK) WHERE FLD_Date >= @StartDate AND FLD_Date <= @EndDate
RETURN
END
Declare @date1 date = '2016-01-01'
,@date2 date = '2016-03-31'
,@date_index date
Declare @calender table (D date)
SET @date_index = @date1
WHILE @date_index<=@date2
BEGIN
INSERT INTO @calender
SELECT @date_index
SET @date_index = dateadd(day,1,@date_index)
IF @date_index>@date2
Break
ELSE
Continue
END
## 6개 중 6개가 더 있습니다.MsSql을 가정한 다른 방법
Declare @MonthStart datetime = convert(DateTime,'07/01/2016')
Declare @MonthEnd datetime = convert(DateTime,'07/31/2016')
Declare @DayCount_int Int = 0
Declare @WhileCount_int Int = 0
set @DayCount_int = DATEDIFF(DAY, @MonthStart, @MonthEnd)
select @WhileCount_int
WHILE @WhileCount_int < @DayCount_int + 1
BEGIN
print convert(Varchar(24),DateAdd(day,@WhileCount_int,@MonthStart),101)
SET @WhileCount_int = @WhileCount_int + 1;
END;
특정 연도에서 현재 날짜까지의 연도를 인쇄하려는 경우.방금 승인된 답변을 변경했습니다.
WITH mycte AS
(
SELECT YEAR(CONVERT(DATE, '2006-01-01',102)) DateValue
UNION ALL
SELECT DateValue + 1
FROM mycte
WHERE DateValue + 1 < = YEAR(GETDATE())
)
SELECT DateValue
FROM mycte
OPTION (MAXRECURSION 0)
이 쿼리는 Microsoft SQL Server에서 작동합니다.
select distinct format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) as aDate
from (
SELECT ones.n + 10 * tens.n + 100 * hundreds.n + 1000 * thousands.n as v
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
) a
where format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) < cast('2010-01-13' as datetime)
order by aDate asc;
이제 어떻게 작동하는지 살펴보겠습니다.
내부 쿼리는 0에서 9999까지의 정수 목록만 반환합니다.그것은 우리에게 날짜를 계산하기 위한 10,000개의 값 범위를 줄 것입니다.10_000, 100_000 등의 행을 추가하면 더 많은 날짜를 얻을 수 있습니다.
SELECT ones.n + 10 * tens.n + 100 * hundreds.n + 1000 * thousands.n as v
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
) a;
이 파트는 문자열을 날짜로 변환하고 내부 쿼리에서 숫자를 추가합니다.
cast('2010-01-01' as datetime) + ( a.v / 10 )
그러면 우리는 결과를 당신이 원하는 형식으로 변환합니다.열 이름이기도 합니다!
format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' )
그런 다음 고유한 값만 추출하고 열 이름에 날짜의 별칭을 지정합니다.
distinct format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) as aDate
원하는 범위 내의 날짜만 필터링하려면 where 절을 사용합니다.SQL Server는 where 절 내의 열 별칭인 aDate를 허용하지 않으므로 여기서 열 이름을 사용합니다.
where format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) < cast('2010-01-13' as datetime)
마지막으로 결과를 정렬합니다.
order by aDate asc;
저처럼 절차와 기능이 금지되어 있고 SQL 사용자에게 삽입 권한이 없으므로 "@c is not allowed"와 같은 임시 변수도 삽입할 수 없습니다. 하지만 특정 기간의 날짜 목록을 생성하고 싶습니다. 예를 들어, 현재 연도를 집계하려면 이를 사용하십시오.
select * from
(select adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) gen_date from
(select 0 t0 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 t1 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 t2 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 t3 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 t4 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
where gen_date between '2017-01-01' and '2017-12-31'
WITH TEMP (DIA, SIGUIENTE_DIA ) AS
(SELECT
1,
CAST(@FECHAINI AS DATE)
FROM
DUAL
UNION ALL
SELECT
DIA,
DATEADD(DAY, DIA, SIGUIENTE_DIA)
FROM
TEMP
WHERE
DIA < DATEDIFF(DAY, @FECHAINI, @FECHAFIN)
AND DATEADD(DAY, 1, SIGUIENTE_DIA) <= CAST(@FECHAFIN AS DATE)
)
SELECT
SIGUIENTE_DIA AS CALENDARIO
FROM
TEMP
ORDER BY
SIGUIENTE_DIA
자세한 내용은 테이블 DUAL에 있지만 이 테이블을 더미 테이블로 교환하면 작동합니다.
SELECT dateadd(dd,DAYS,'2013-09-07 00:00:00') DATES
INTO #TEMP1
FROM
(SELECT TOP 365 colorder - 1 AS DAYS from master..syscolumns
WHERE id = -519536829 order by colorder) a
WHERE datediff(dd,dateadd(dd,DAYS,'2013-09-07 00:00:00'),'2013-09-13 00:00:00' ) >= 0
AND dateadd(dd,DAYS,'2013-09-07 00:00:00') <= '2013-09-13 00:00:00'
SELECT * FROM #TEMP1
여기서 답변은 유효합니다. 두 날짜 사이의 모든 날짜를 나열하는 방법
Create Procedure SelectDates(@fromDate Date, @toDate Date)
AS
BEGIN
SELECT DATEADD(DAY,number,@fromDate) [Date]
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number,@fromDate) < @toDate
END
DECLARE @StartDate DATE = '2017-09-13', @EndDate DATE = '2017-09-16'
SELECT date FROM ( SELECT DATE = DATEADD(DAY, rn - 1, @StartDate) FROM (
SELECT TOP (DATEDIFF(DAY, @StartDate, DATEADD(DAY,1,@EndDate)))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
ORDER BY s1.[object_id] ) AS x ) AS y
결과:
2017-09-13
2017-09-14
2017-09-15
2017-09-16
언급URL : https://stackoverflow.com/questions/1378593/get-a-list-of-dates-between-two-dates-using-a-function
'programing' 카테고리의 다른 글
Mongodb: mongo 셸의 ObjectId에서 Date 범위 쿼리 수행 (0) | 2023.07.04 |
---|---|
DropDownList의 ListItems 특성이 포스트백 시 손실됩니까? (0) | 2023.06.29 |
MariaDB Galera Cluster 노드가 디스크에 많이 액세스하여 성능이 크게 저하됨 (0) | 2023.06.29 |
모든 하위 플롯 위에 단일 주 제목을 설정하는 방법 (0) | 2023.06.29 |
각도 2 구성 요소 지침이 작동하지 않음 (0) | 2023.06.29 |