날짜 범위에서 일을 생성


135

같은 쿼리를 실행하고 싶습니다

select ... as days where `date` is between '2010-01-20' and '2010-01-24'

그리고 다음과 같은 데이터를 반환하십시오.

일
----------
2010-01-20
2010-01-21
2010-01-22
2010-01-23
2010-01-24

10
이 질문에는 다른 문제가 없습니다. 위의 질문은 SQL 과정을 마스터하는 문제입니다.
Pentium10

선택한 날짜 범위를 기준으로 날짜 배열이 필요합니까?
Derek Adair

1
나는 당신에게 문제를 찾기 위해 사용법을 생각하고 있습니다 ... 테이블에 누락 된 레코드를 채우는 작업이있는 경우. 그리고 당신은 매일 같은 생각을하고 있습니다insert into table select ... as days date between '' and ''
Pentium10

13
사용 예는 통계를 생성하고 데이터가없는 날짜에 대한 행을 포함하는 것입니다. 그룹별로 일종의 작업을 수행하는 경우 실제로 데이터를 언어로 덤프하지 않고 SQL로 모든 정보를 생성하고 필요한 형식으로 추가하는 것이 훨씬 빠를 수 있습니다. 빈 상자.
Nanne

1
@ Nanne 그것이 정확하게이 질문을 저장 한 이유입니다. 특정 날짜에는 존재하지 않을 수있는 데이터에 왼쪽으로 참여하려면 위의 내용이 필요합니다.
Josh Diehl

답변:


318

이 솔루션은 루프, 프로 시저 또는 임시 테이블을 사용 하지 않습니다 . 하위 쿼리는 지난 10,000 일 동안 날짜를 생성하며 원하는만큼 뒤로 또는 앞으로 확장 될 수 있습니다.

select a.Date 
from (
    select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a) ) DAY as Date
    from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as d
) a
where a.Date between '2010-01-20' and '2010-01-24' 

산출:

Date
----------
2010-01-24
2010-01-23
2010-01-22
2010-01-21
2010-01-20

성능에 대한 참고 사항

여기서 테스트 하면 성능이 놀랍게 좋습니다. 위의 쿼리는 0.0009 초가 걸립니다.

하위 쿼리를 확장하여 약. 100,000 개의 숫자 (따라서 약 274 년의 날짜)는 0.0458 초로 실행됩니다.

또한 이것은 약간의 조정만으로 대부분의 데이터베이스에서 작동하는 매우 이식성이 뛰어난 기술입니다.

1,000 일을 반환하는 SQL Fiddle 예제


6
변경할 경우 더 나은 성능을 볼 수 UNIONUNION ALL존재하지 않는 것을 제거하기 위해 중복 검사 그것의 시간 낭비 -. 그럼에도 불구하고 너무 복잡합니다. UNION을 사용하여 결과 집합을 구성하려는 경우 날짜를 지정하고 완료하는 것이 어떻습니까?
OMG Ponies

7
왜 날짜를 지정하고 완료해야합니까 ? 위의 방법을 사용하면 테이블 생성이 필요없는 임의로 많은 숫자 (및 날짜)를 만들 수 있기 때문에 제안하는 방식으로 하드 코딩하기가 어렵습니다. 분명히 5 날짜 동안 그것은 과잉입니다; 그러나 사전에 날짜를 모르지만 잠재적 인 최소 및 최대 값이있는 테이블에 대해 조인하는 경우 의미가 있습니다.
RedFilter

2
이미 생성 한 UNION 문 대신 DATETIME 함수를 사용하는 것이 "고통"입니까? 그것은 당신이 추가 한 논리에 대한 필요성을 완화 . 따라서 쿼리 가 너무 복잡해 졌습니다. 어느 쪽이든 UNION 문은 확장 할 수 없습니다-날짜 또는 숫자를 지정하면 누가 20 또는 30 개의 날짜를 수용하도록 업데이트 하시겠습니까?
OMG Ponies

23
질문에 대한 답변을 보는 것이 좋으며, 어떻게 할 수 없거나하지 말아야하는지에 대한 끝없는 의견이 아닙니다. 대부분의 작업을 수행 할 수 있으며 "컨텍스트"는 상황에 따라 의미가 있으며 모든 사람에게 다릅니다. 이 답변은 대부분의 상황에서 더 나은 방법이 있다는 것을 잘 알고 있지만 도움이되었습니다.
joe

7
이 쿼리를 작동시킬 수없는 사용자 : 얼굴을 마주 친 다음 1000 개의 날짜를 생성하는이 쿼리에 대한 OP의 의견을 다시 읽으십시오. 2010 년이 1000 일이 넘었으므로 그에 따라 쿼리를 조정해야합니다.
Noel Baron

32

뷰를 사용하는 또 다른 변형은 다음과 같습니다.

CREATE VIEW digits AS
  SELECT 0 AS digit UNION ALL
  SELECT 1 UNION ALL
  SELECT 2 UNION ALL
  SELECT 3 UNION ALL
  SELECT 4 UNION ALL
  SELECT 5 UNION ALL
  SELECT 6 UNION ALL
  SELECT 7 UNION ALL
  SELECT 8 UNION ALL
  SELECT 9;

CREATE VIEW numbers AS
  SELECT
    ones.digit + tens.digit * 10 + hundreds.digit * 100 + thousands.digit * 1000 AS number
  FROM
    digits as ones,
    digits as tens,
    digits as hundreds,
    digits as thousands;

CREATE VIEW dates AS
  SELECT
    SUBDATE(CURRENT_DATE(), number) AS date
  FROM
    numbers;

그런 다음 간단하게 수행 할 수 있습니다 (얼마나 우아합니까?).

SELECT
  date
FROM
  dates
WHERE
  date BETWEEN '2010-01-20' AND '2010-01-24'
ORDER BY
  date

최신 정보

현재 날짜부터 시작 하여 과거 날짜 만 생성 할 수 있습니다 . 모든 종류의 날짜 범위 (과거, 미래 및 그 사이)를 생성하려면 대신이보기를 사용해야합니다.

CREATE VIEW dates AS
  SELECT
    SUBDATE(CURRENT_DATE(), number) AS date
  FROM
    numbers
  UNION ALL
  SELECT
    ADDDATE(CURRENT_DATE(), number + 1) AS date
  FROM
    numbers;

1
모든 경우에 작동하지는 않습니다. 날짜 선택 날짜에서 '2014-12-01'과 '2014-12-28'사이의 날짜 별 주문
vasanth

3
@ user927258에게 전화하십시오. dates위에서 언급 한 첫 번째보기 는 현재 날짜부터 시작하는 날짜를 계산 하기 때문에 미래에 설정된 날짜를 검색 할 수 없기 때문입니다. @RedFilter의 답변에는 동일한 디자인 결함이 있습니다. 그래도 답변에 해결 방법을 추가했습니다.
Stéphane

일부 뷰를 사용하면 쿼리가 간단 해지고 재사용이 가능해집니다. 기본적으로 동일한 작업을 수행하지만 UNION단일 SQL 문에서 모든 절이 이상하게 보입니다.
스튜어트

24

수락 된 답변이 PostgreSQL에서 작동하지 않았습니다 ( "a"또는 그 근처의 구문 오류).

PostgreSQL에서이를 수행하는 방법은 다음과 같은 generate_series기능 을 사용하는 것입니다.

SELECT day::date
FROM generate_series('2010-01-20', '2010-01-24', INTERVAL '1 day') day;

    day
------------
 2010-01-20
 2010-01-21
 2010-01-22
 2010-01-23
 2010-01-24
(5 rows)

14

재귀 공통 테이블 표현식 (CTE)을 사용하여 날짜 목록을 생성 한 후 선택할 수 있습니다. 분명히 당신은 일반적으로 3 백만 날짜를 만들고 싶지 않을 것이므로 이것은 가능성을 보여줍니다. CTE 내부의 날짜 범위를 간단히 제한하고 CTE를 사용하여 select 문에서 where 절을 생략 할 수 있습니다.

with [dates] as (
    select convert(datetime, '1753-01-01') as [date] --start
    union all
    select dateadd(day, 1, [date])
    from [dates]
    where [date] < '9999-12-31' --end
)
select [date]
from [dates]
where [date] between '2013-01-01' and '2013-12-31'
option (maxrecursion 0)

Microsoft SQL Server 2005에서 가능한 모든 날짜의 CTE 목록을 생성하는 데는 1:08이 걸렸습니다. 백 년을 생성하는 데 1 초도 걸리지 않았습니다.


7

MSSQL 쿼리

select datetable.Date 
from (
    select DATEADD(day,-(a.a + (10 * b.a) + (100 * c.a)),getdate()) AS Date
    from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
     union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a

    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
     union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b

    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
     union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) datetable
where datetable.Date between '2014-01-20' and '2014-01-24' 
order by datetable.Date DESC

산출

Date
-----
2014-01-23 12:35:25.250
2014-01-22 12:35:25.250
2014-01-21 12:35:25.250
2014-01-20 12:35:25.250

2
좀 더 아래로 스크롤 한 경우 ... 한숨. 어쨌든 고마워 내 버전의 시간을 제거하기 위해 CAST (<expression> AS DATE)를 추가했습니다. 또한 GETDATE ()-365 AND GETDATE () 사이의 a.Date 날짜를 사용합니다. 오늘 쿼리를 실행하면 WHERE = P
Ricardo C

4

루프 / 커서없이이 작업을 수행하는 구식 솔루션은 NUMBERS값이 1부터 시작하는 단일 정수 열이있는 테이블 을 만드는 것 입니다.

CREATE TABLE  `example`.`numbers` (
  `id` int(10) unsigned NOT NULL auto_increment,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

필요에 맞는 충분한 레코드로 테이블을 채워야합니다.

INSERT INTO NUMBERS (id) VALUES (NULL);

당신은 일단 NUMBERS테이블을, 당신은 사용할 수 있습니다 :

SELECT x.start_date + INTERVAL n.id-1 DAY
  FROM NUMBERS n
  JOIN (SELECT STR_TO_DATE('2010-01-20', '%Y-%m-%d') AS start_date 
          FROM DUAL) x
 WHERE x.start_date + INTERVAL n.id-1 DAY <= '2010-01-24'

절대적인 최첨단 솔루션은 다음과 같습니다.

SELECT STR_TO_DATE('2010-01-20', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-21', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-22', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-23', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-24', '%Y-%m-%d')
 FROM DUAL

무엇을 위해 사용 하시겠습니까?


왼쪽에 참여하기 위해 날짜 또는 숫자 목록을 생성합니다. 시퀀셜 데이터 목록에 왼쪽으로 참여하기 때문에 데이터에 차이가있는 곳을 확인하기 위해이 작업을 수행합니다. null 값은 간격이 존재하는 곳을 분명하게합니다.


1
DUAL테이블은 FROM절 에서 독립 테이블로 사용하기 위해 Oracle 및 MySQL에서 지원됩니다 . 존재하지 않으며 값을 선택하면 값이 무엇이든 반환됩니다. SELECT 쿼리에는 FROM적어도 하나의 테이블을 지정 하는 절이 필요하기 때문에이 아이디어는 독립형을 유지하는 것이 었습니다 .
OMG Ponies

1
RDBMS가 쿼리가 필요할 때마다 테이블을 빌드하는 대신 실제로 영구 숫자 테이블을 작성하는 경우 +1입니다. 보조 테이블은 악하지 않습니다!
베이컨 비트

4

Access 2010의 경우 여러 단계가 필요합니다. 위에 게시 된 것과 같은 패턴을 따랐지만 Access의 누군가를 도울 수 있다고 생각했습니다. 나를 위해 훌륭하게 일 했으므로 씨앗 날짜 테이블을 유지할 필요가 없었습니다.

DUAL이라는 테이블을 만듭니다 (Oracle DUAL 테이블의 작동 방식과 유사)

  • 아이디 (식 번호)
  • DummyColumn (텍스트)
  • 하나의 행 값 추가 (1, "DummyRow")

"ZeroThru9Q"라는 쿼리를 작성하십시오. 다음 구문을 수동으로 입력하십시오.

SELECT 0 AS a
FROM dual
UNION ALL
SELECT 1
FROM dual
UNION ALL
SELECT 2
FROM dual
UNION ALL
SELECT 3
FROM dual
UNION ALL
SELECT 4
FROM dual
UNION ALL
SELECT 5
FROM dual
UNION ALL
SELECT 6
FROM dual
UNION ALL
SELECT 7
FROM dual
UNION ALL
SELECT 8
FROM dual
UNION ALL
SELECT 9
FROM dual;

"TodayMinus1KQ"라는 이름의 쿼리를 작성하십시오 (오늘 이전 날짜의 경우). 다음 구문을 수동으로 입력하십시오.

SELECT date() - (a.a + (10 * b.a) + (100 * c.a)) AS MyDate
FROM
  (SELECT *
   FROM ZeroThru9Q) AS a,

  (SELECT *
   FROM ZeroThru9Q) AS b,

  (SELECT *
   FROM ZeroThru9Q) AS c

"TodayPlus1KQ"라는 이름의 쿼리를 작성하십시오 (오늘 이후 날짜). 다음 구문을 수동으로 입력하십시오.

SELECT date() + (a.a + (10 * b.a) + (100 * c.a)) AS MyDate
FROM
  (SELECT *
   FROM ZeroThru9Q) AS a,

  (SELECT *
   FROM ZeroThru9Q) AS b,

  (SELECT *
   FROM ZeroThru9Q) AS c;

"TodayPlusMinus1KQ"라는 이름의 통합 쿼리를 만듭니다 (날짜 +/- 1000 일).

SELECT MyDate
FROM TodayMinus1KQ
UNION
SELECT MyDate
FROM TodayPlus1KQ;

이제 쿼리를 사용할 수 있습니다 :

SELECT MyDate
FROM TodayPlusMinus1KQ
WHERE MyDate BETWEEN #05/01/2014# and #05/30/2014#

3

절차 + 임시 테이블 :

DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `days`(IN dateStart DATE, IN dateEnd DATE)
BEGIN

    CREATE TEMPORARY TABLE IF NOT EXISTS date_range (day DATE);

    WHILE dateStart <= dateEnd DO
      INSERT INTO date_range VALUES (dateStart);
      SET dateStart = DATE_ADD(dateStart, INTERVAL 1 DAY);
    END WHILE;

    SELECT * FROM date_range;
    DROP TEMPORARY TABLE IF EXISTS date_range;

END

3

thx Pentium10-당신이 나를 stackoverflow에 합류하게 만들었습니다 :)-이것은 msaccess 로의 포팅입니다-모든 버전에서 작동한다고 생각합니다 :

SELECT date_value
FROM (SELECT a.espr1+(10*b.espr1)+(100*c.espr1) AS integer_value,
dateadd("d",integer_value,dateserial([start_year], [start_month], [start_day])) as date_value
FROM (select * from 
    (
    select top 1 "0" as espr1 from MSysObjects
    union all
    select top 1 "1" as espr2 from MSysObjects
    union all
    select top 1 "2" as espr3 from MSysObjects
    union all
    select top 1 "3" as espr4 from MSysObjects
    union all
    select top 1 "4" as espr5 from MSysObjects
    union all
    select top 1 "5" as espr6 from MSysObjects
    union all
    select top 1 "6" as espr7 from MSysObjects
    union all
    select top 1 "7" as espr8 from MSysObjects
    union all
    select top 1 "8" as espr9 from MSysObjects
    union all
    select top 1 "9" as espr9 from MSysObjects
    ) as a,
    (
    select top 1 "0" as espr1 from MSysObjects
    union all
    select top 1 "1" as espr2 from MSysObjects
    union all
    select top 1 "2" as espr3 from MSysObjects
    union all
    select top 1 "3" as espr4 from MSysObjects
    union all
    select top 1 "4" as espr5 from MSysObjects
    union all
    select top 1 "5" as espr6 from MSysObjects
    union all
    select top 1 "6" as espr7 from MSysObjects
    union all
    select top 1 "7" as espr8 from MSysObjects
    union all
    select top 1 "8" as espr9 from MSysObjects
    union all
    select top 1 "9" as espr9 from MSysObjects
    ) as b,
    (
    select top 1 "0" as espr1 from MSysObjects
    union all
    select top 1 "1" as espr2 from MSysObjects
    union all
    select top 1 "2" as espr3 from MSysObjects
    union all
    select top 1 "3" as espr4 from MSysObjects
    union all
    select top 1 "4" as espr5 from MSysObjects
    union all
    select top 1 "5" as espr6 from MSysObjects
    union all
    select top 1 "6" as espr7 from MSysObjects
    union all
    select top 1 "7" as espr8 from MSysObjects
    union all
    select top 1 "8" as espr9 from MSysObjects
    union all
    select top 1 "9" as espr9 from MSysObjects
    ) as c   
)  as d) 
WHERE date_value 
between dateserial([start_year], [start_month], [start_day]) 
and dateserial([end_year], [end_month], [end_day]);

참조 MSysObject는 from 절에서 적어도 하나의 레코드가 필요하므로 '액세스에 테이블 카운트가 필요합니다.'레코드가 1 개 이상인 모든 테이블이 수행합니다.


2

이미 주어진 많은 훌륭한 답변들에서 언급했듯이 (또는 적어도 언급 된 바와 같이),이 문제는 일련의 숫자를 가지고 있으면 쉽게 해결됩니다.

참고 : 다음은 T-SQL이지만 여기에서 이미 인터넷에 언급 된 일반적인 개념을 구현 한 것입니다. 코드를 선택한 언어로 변환하는 것은 비교적 간단해야합니다.

어떻게? 이 쿼리를 고려하십시오.

SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;

위의 날짜 범위는 1/22/0001-1/27/0001이며 매우 사소합니다. : 위의 질의에있는 정보의 2 가지 핵심이 있습니다 시작 날짜0001-01-22오프셋 의는 5. 이 두 가지 정보를 결합하면 분명히 종료 날짜가됩니다. 따라서 두 개의 날짜가 주어지면 범위 생성은 다음과 같이 분류 될 수 있습니다.

  • 주어진 두 날짜 (오프셋)의 차이점을 쉽게 찾으십시오.

    -- Returns 125 SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))

    ABS()여기를 사용 하면 날짜 순서와 관련이 없습니다.

  • 제한된 숫자 세트를 생성하기도 쉽습니다.

    -- Returns the numbers 0-2 SELECT N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1 FROM(SELECT 'A' AS S UNION ALL SELECT 'A' UNION ALL SELECT 'A')

    FROM여기서 선택하는 내용은 실제로 중요하지 않습니다 . 우리는 그 안에있는 행의 수를 계산할 수있는 세트가 필요합니다. 나는 개인적으로 TVF를 사용하고, 일부는 CTE를 사용하고, 다른 사람들은 대신 숫자 테이블을 사용합니다. 또한 귀하가 이해하는 가장 성능이 우수한 솔루션을 사용하도록 옹호합니다.

이 두 가지 방법을 결합하면 문제가 해결됩니다.

DECLARE @date1 DATE = '9001-11-21';
DECLARE @date2 DATE = '9001-11-23';

SELECT D = DATEADD(d, N, @date1)
FROM (
    SELECT N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1
    FROM (SELECT 'A' AS S UNION ALL SELECT 'A' UNION ALL SELECT 'A') S
) Numbers
WHERE N <= ABS(DATEDIFF(d, @date1, @date2));

위의 예제는 끔찍한 코드이지만 모든 것이 어떻게 결합되는지 보여줍니다.

더 재미있는

나는 이런 종류의 일을 많이해야하므로 논리를 두 개의 TVF로 캡슐화했습니다. 첫 번째는 숫자 범위를 생성하고 두 번째는이 기능을 사용하여 날짜 범위를 생성합니다. 수학은 입력 순서가 중요하지 않고에서 사용할 수있는 모든 범위의 숫자를 사용하기를 원한다는 것입니다 GenerateRangeSmallInt.

다음 함수는 최대 65536 날짜 범위를 반환하기 위해 ~ 16ms의 CPU 시간이 걸립니다.

CREATE FUNCTION dbo.GenerateRangeDate (   
    @date1 DATE,   
    @date2 DATE   
)   
RETURNS TABLE
WITH SCHEMABINDING   
AS   
RETURN (
    SELECT D = DATEADD(d, N + 32768, CASE WHEN @date1 <= @date2 THEN @date1 ELSE @date2 END)
    FROM dbo.GenerateRangeSmallInt(-32768, ABS(DATEDIFF(d, @date1, @date2)) - 32768)
);

GO

CREATE FUNCTION dbo.GenerateRangeSmallInt (
    @num1 SMALLINT = -32768
  , @num2 SMALLINT = 32767
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN (
    WITH Numbers(N) AS (
        SELECT N FROM(VALUES
            (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 16
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 32
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 48
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 64
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 80
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 96
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 112
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 128
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 144
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 160
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 176
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 192
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 208
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 224
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 240
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 256
        ) V (N)
    )
    SELECT TOP(ABS(CAST(@num1 AS INT) - CAST(@num2 AS INT)) + 1)
           N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + CASE WHEN @num1 <= @num2 THEN @num1 ELSE @num2 END - 1
    FROM Numbers A
       , Numbers B
);

2

이 시도.

SELECT TO_DATE('20160210','yyyymmdd') - 1 + LEVEL AS start_day 
from DUAL
connect by level <= (TO_DATE('20160228','yyyymmdd') + 1) - TO_DATE('20160210','yyyymmdd') ;

2

날짜 범위를 얻으려고합니다.

귀하의 예에서 '2010-01-20'과 '2010-01-24'사이의 날짜를 원합니다.

가능한 해결책:

 select date_add('2010-01-20', interval row day) from
 ( 
    SELECT @row := @row + 1 as row FROM 
    (select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
    (select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2, 
    (select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3, 
    (select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t4, 
    (SELECT @row:=-1) r
 ) sequence
 where date_add('2010-01-20', interval row day) <= '2010-01-24'

설명

MySQL에는 date_add 함수가 있으므로

select date_add('2010-01-20', interval 1 day)

너에게 줄거야

2010-01-21

DATEDIFF의 기능은이를 반복해야 할 것 자주 알려 것

select datediff('2010-01-24', '2010-01-20')

어떤 반환

 4

날짜 범위에서 날짜 목록을 얻는 것은 정수 시퀀스를 만드는 것으로 요약됩니다 .MySQL 에서 정수 시퀀스 생성을 참조하십시오.

여기에서 가장 찬란한 답변은 https://stackoverflow.com/a/2652051/1497139 와 비슷한 접근 방식을 취했습니다 .

SELECT @row := @row + 1 as row FROM 
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2, 
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3, 
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t4, 
(SELECT @row:=0) r
limit 4

결과는

row
1.0
2.0
3.0
4.0

이제 행을 사용하여 지정된 시작 날짜로부터 날짜 목록을 작성할 수 있습니다. 시작 날짜를 포함시키기 위해 행 -1로 시작합니다.

select date_add('2010-01-20', interval row day) from
 ( 
    SELECT @row := @row + 1 as row FROM 
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2, 
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3, 
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t4, 
    (SELECT @row:=-1) r
 ) sequence
 where date_add('2010-01-20', interval row day) <= '2010-01-24'

1

며칠 이상이 필요할 경우 테이블이 필요합니다.

mysql에서 날짜 범위 만들기

그때,

select from days.day, count(mytable.field) as fields from days left join mytable on day=date where date between x and y;

3
위의 답변에는 표가 필요하지 않고 솔루션을 제공하기 때문에 왜 이것을 게시 했습니까?
Pentium10

1

두 날짜 필드 사이에 날짜 생성

SQL CTE 쿼리를 알고 있다면이 솔루션을 사용하면 질문을 해결할 수 있습니다.

여기에 예가 있습니다

우리는 하나의 테이블에 날짜가 있습니다

테이블 이름 :“testdate”

STARTDATE   ENDDATE
10/24/2012  10/24/2012
10/27/2012  10/29/2012
10/30/2012  10/30/2012

결과 필요 :

STARTDATE
10/24/2012
10/27/2012
10/28/2012
10/29/2012
10/30/2012

해결책:

WITH CTE AS
  (SELECT DISTINCT convert(varchar(10),StartTime, 101) AS StartTime,
                   datediff(dd,StartTime, endTime) AS diff
   FROM dbo.testdate
   UNION ALL SELECT StartTime,
                    diff - 1 AS diff
   FROM CTE
   WHERE diff<> 0)
SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime
FROM CTE

설명 : CTE 재귀 쿼리 설명

  • 조회의 첫 번째 부분 :

    SELECT DISTINCT convert(varchar(10), StartTime, 101) AS StartTime, datediff(dd, StartTime, endTime) AS diff FROM dbo.testdate

    설명 : firstcolumn은 "startdate"이고 두 번째 열은 시작 날짜와 종료 날짜의 차이 (일)이며 "diff"열로 간주됩니다.

  • 쿼리의 두 번째 부분 :

    UNION ALL SELECT StartTime, diff-1 AS diff FROM CTE WHERE diff<>0

    설명 : Union all은 결과가 null이 될 때까지 위 쿼리의 결과를 상속하므로 "StartTime"결과는 생성 된 CTE 쿼리에서 상속되며 diff, reduce-1에서 상속되므로 3, 2 및 1과 같습니다. 0까지

예를 들어

STARTDATE   DIFF
10/24/2012  0
10/27/2012  0
10/27/2012  1
10/27/2012  2
10/30/2012  0

결과 사양

STARTDATE       Specification
10/24/2012  --> From Record 1
10/27/2012  --> From Record 2
10/27/2012  --> From Record 2
10/27/2012  --> From Record 2
10/30/2012  --> From Record 3
  • 쿼리의 세 번째 부분

    SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime FROM CTE

    "startdate"에 "diff"일을 추가하므로 결과는 다음과 같아야합니다.

결과

STARTDATE
10/24/2012
10/27/2012
10/28/2012
10/29/2012
10/30/2012

1

허용 된 답변보다 짧은 동일한 아이디어 :

(SELECT TRIM('2016-01-05' + INTERVAL a + b DAY) date
FROM
(SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
UNION SELECT 8 UNION SELECT 9 ) d,
(SELECT 0 b UNION SELECT 10 UNION SELECT 20
UNION SELECT 30 UNION SELECT 40) m
WHERE '2016-01-05' + INTERVAL a + b DAY  <=  '2016-01-21')

1

이것을 저장된 뷰로 원하는 사람 (MySQL은 뷰에서 중첩 된 select 문을 지원하지 않습니다) :

create view zero_to_nine as
    select 0 as n union all 
    select 1 union all 
    select 2 union all 
    select 3 union all 
    select 4 union all 
    select 5 union all 
    select 6 union all 
    select 7 union all 
    select 8 union all 
    select 9;

create view date_range as
    select curdate() - INTERVAL (a.n + (10 * b.n) + (100 * c.n)) DAY as date
    from zero_to_nine as a
    cross join zero_to_nine as b
    cross join zero_to_nine as c;

그런 다음 할 수 있습니다

select * from date_range

얻을

date
---
2017-06-06
2017-06-05
2017-06-04
2017-06-03
2017-06-02
...

1

MariaDB> = 10.3 및 MySQL> = 8.0의 새로운 재귀 (공통 테이블 표현식) 기능을 사용하는 우아한 솔루션.

WITH RECURSIVE t as (
    select '2019-01-01' as dt
  UNION
    SELECT DATE_ADD(t.dt, INTERVAL 1 DAY) FROM t WHERE DATE_ADD(t.dt, INTERVAL 1 DAY) <= '2019-04-30'
)
select * FROM t;

위는 '2019-01-01'과 '2019-04-30'사이의 날짜 표를 반환합니다. 또한 매우 빠릅니다. 1000 년 분량의 날짜 (~ 365,000 일)를 반환하려면 내 컴퓨터에서 약 400ms가 걸립니다.


1

이 날짜를 즉석에서 생성하는 것이 좋습니다. 그러나 꽤 넓은 범위 에서이 작업을 수행하는 것이 편하지 않기 때문에 다음 솔루션으로 끝났습니다.

  1. 날짜 계산에 사용되는 숫자를 보유 할 테이블 "DatesNumbers"를 작성했습니다.
CREATE TABLE DatesNumbers (
    i MEDIUMINT NOT NULL,
    PRIMARY KEY (i)
)
COMMENT='Used by Dates view'
;
  1. -59999에서 40000까지의 숫자로 위의 기술을 사용하여 표를 채웠습니다.이 범위는 59999 일 (~ 164 년)에서 40000 일 (109 년)까지의 날짜를 제공합니다.
INSERT INTO DatesNumbers
SELECT 
    a.i + (10 * b.i) + (100 * c.i) + (1000 * d.i) + (10000 * e.i) - 59999 AS i
FROM 
  (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS a,
  (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS b,
  (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS c,
  (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS d,
  (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS e
;
  1. "날짜"보기를 작성했습니다.
SELECT
      i,
      CURRENT_DATE() + INTERVAL i DAY AS Date
FROM
    DatesNumbers

그게 다야.

  • (+) 읽기 쉬운 쿼리
  • (+) 즉석 번호 생성 불가
  • (+) 과거와 미래의 날짜를 제공하며이 게시물 과 같이 이에 대한 견해가 없습니다 .
  • (+) "과거에만"또는 "미래에만"날짜를 WHERE i < 0또는 WHERE i > 0(PK)를 사용하여 필터링 할 수 있습니다.
  • (-) '임시'테이블 및 뷰가 사용됩니다.

0

좋습니다. 이것을 시도하십시오 : http://www.devshed.com/c/a/MySQL/Delving-Deeper-into-MySQL-50/
http://dev.mysql.com/doc/refman/5.0/en/ loop-statement.html
http://www.roseindia.net/sql/mysql-example/mysql-loop.shtml

이를 사용하여 임시 테이블을 생성 한 다음 임시 테이블에서 select *를 수행하십시오. 또는 한 번에 하나씩 결과를 출력하십시오.
당신이하고 싶은 말은 SELECT 문 으로 할 수 없지만 MySQL과 관련된 것들로 할 수 있습니다.
그런 다음 커서가 필요할 수도 있습니다. http://dev.mysql.com/doc/refman/5.0/en/cursors.html


0

Oracle의 경우 내 솔루션은 다음과 같습니다.

select trunc(sysdate-dayincrement, 'DD') 
  from dual, (select level as dayincrement 
                from dual connect by level <= 30)

Sysdate를 특정 날짜로 변경하고 레벨 번호를 변경하여 더 많은 날짜를 제공 할 수 있습니다.


0

두 날짜 사이의 날짜 목록을 원하는 경우 :

create table #dates ([date] smalldatetime)
while @since < @to
begin
     insert into #dates(dateadd(day,1,@since))
     set @since = dateadd(day,1,@since)
end
select [date] from #dates

* 바이올린 여기 : http://sqlfiddle.com/#!6/9eecb/3469


0
set language  'SPANISH'
DECLARE @table table(fechaDesde datetime , fechaHasta datetime ) 
INSERT @table VALUES('20151231' , '20161231');
WITH x AS 
    (
        SELECT   DATEADD( m , 1 ,fechaDesde ) as fecha  FROM @table
        UNION ALL
        SELECT  DATEADD( m , 1 ,fecha )
        FROM @table t INNER JOIN x ON  DATEADD( m , 1 ,x.fecha ) <= t.fechaHasta
    )
SELECT LEFT( CONVERT( VARCHAR, fecha , 112 ) , 6 ) as Periodo_Id 
,DATEPART ( dd, DATEADD(dd,-(DAY(fecha)-1),fecha)) Num_Dia_Inicio
,DATEADD(dd,-(DAY(fecha)-1),fecha) Fecha_Inicio
,DATEPART ( mm , fecha ) Mes_Id
,DATEPART ( yy , fecha ) Anio
,DATEPART ( dd, DATEADD(dd,-(DAY(DATEADD(mm,1,fecha))),DATEADD(mm,1,fecha))) Num_Dia_Fin
,DATEADD(dd,-(DAY(DATEADD(mm,1,fecha))),DATEADD(mm,1,fecha)) ultimoDia
,datename(MONTH, fecha) mes
,'Q' + convert(varchar(10),  DATEPART(QUARTER, fecha)) Trimestre_Name
FROM x 
OPTION(MAXRECURSION 0)

0
DELIMITER $$
CREATE PROCEDURE GenerateRangeDates(IN dateStart DATE, IN dateEnd DATE)
BEGIN

    CREATE TEMPORARY TABLE IF NOT EXISTS dates (day DATE);

    loopDate: LOOP
        INSERT INTO dates(day) VALUES (dateStart); 
        SET dateStart = DATE_ADD(dateStart, INTERVAL 1 DAY);

        IF dateStart <= dateEnd 
            THEN ITERATE loopDate;
            ELSE LEAVE loopDate;
        END IF;
    END LOOP loopDate;

    SELECT day FROM dates;
    DROP TEMPORARY TABLE IF EXISTS dates;

END 
$$

-- Call procedure
call GenerateRangeDates( 
        now() - INTERVAL 40 DAY,
        now()
    );

0

RedFilters 최고 솔루션의 SQLite 버전

select d.Date
from (
    select 
    date(julianday('2010-01-20') + (a.a + (10 * b.a) + (100 * c.a))) as Date
    from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) d
where 
d.Date between '2010-01-20' and '2010-01-24' 
order by d.Date

0

평일 개선 사용자 지정 휴일 테이블에 합류 마이크로 소프트 MSSQL 의 PowerPivot 날짜 테이블에 대한 2012 https://gist.github.com/josy1024/cb1487d66d9e0ccbd420bc4a23b6e90e

with [dates] as (
    select convert(datetime, '2016-01-01') as [date] --start
    union all
    select dateadd(day, 1, [date])
    from [dates]
    where [date] < '2018-01-01' --end
)
select [date]
, DATEPART (dw,[date]) as Wochentag
, (select holidayname from holidaytable 
where holidaytable.hdate = [date]) 
as Feiertag
from [dates]
where [date] between '2016-01-01' and '2016-31-12'
option (maxrecursion 0)

0
WITH
  Digits AS (SELECT 0 D 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),
  Dates AS (SELECT adddate('1970-01-01',t4.d*10000 + t3.d*1000 + t2.d*100 + t1.d*10 +t0.d) AS date FROM Digits AS t0, Digits AS t1, Digits AS t2, Digits AS t3, Digits AS t4)
SELECT * FROM Dates WHERE date BETWEEN '2017-01-01' AND '2017-12-31'

0

날짜다른 시간 소인이있는 달력 테이블 을 작성하는 프로 시저를 작성할 수도 있습니다. 각 분기마다 테이블을 원할 경우

예 :

2019-01-22 08:45:00
2019-01-22 09:00:00
2019-01-22 09:15:00
2019-01-22 09:30:00
2019-01-22 09:45:00
2019-01-22 10:00:00

당신이 사용할 수있는

CREATE DEFINER=`root`@`localhost` PROCEDURE `generate_calendar_table`()
BEGIN

select unix_timestamp('2014-01-01 00:00:00') into @startts;
select unix_timestamp('2025-01-01 00:00:00') into @endts;

if ( @startts < @endts ) then

    DROP TEMPORARY TABLE IF EXISTS calendar_table_tmp;

    CREATE TEMPORARY TABLE calendar_table_tmp (ts int, dt datetime); 

    WHILE ( @startts < @endts)
        DO 
        SET @startts = @startts + 900;
        INSERT calendar_table_tmp VALUES (@startts, from_unixtime(@startts));
    END WHILE;

END if;

END

다음을 통해 조작

select ts, dt from calendar_table_tmp;

그것은 또한 당신에게 TS를 제공

'1548143100', '2019-01-22 08:45:00'
'1548144000', '2019-01-22 09:00:00'
'1548144900', '2019-01-22 09:15:00'
'1548145800', '2019-01-22 09:30:00'
'1548146700', '2019-01-22 09:45:00'
'1548147600', '2019-01-22 10:00:00'

여기에서 다음과 같은 다른 정보를 추가 할 수 있습니다.

select ts, dt, weekday(dt) as wd from calendar_table_tmp;

또는 create table 문 으로 실제 테이블을 만듭니다.


0

AWS MySQL에서 작동하는보다 일반적인 답변입니다.

select datetable.Date
from (
    select date_format(adddate(now(),-(a.a + (10 * b.a) + (100 * c.a))),'%Y-%m-%d') AS Date
    from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
     union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a

    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
     union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b

    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4
     union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) datetable
where datetable.Date between now() - INTERVAL 14 Day and Now()
order by datetable.Date DESC

-1

재귀 공통 테이블 표현식을 사용하는 mysql 8.0.1 및 mariadb 10.2.2에 대한 추가 솔루션 :

with recursive dates as (
    select '2010-01-20' as date
    union all
    select date + interval 1 day from dates where date < '2010-01-24'
)
select * from dates;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.