tạo ngày từ phạm vi ngày


135

Tôi muốn chạy một truy vấn như

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

Và trả về dữ liệu như:

ngày
----------
2010-01-20
2010-01-21
2010-01-22
2010-01-23
2010-01-24

10
Không có vấn đề khác kèm theo câu hỏi này. Câu hỏi trên là vấn đề, thành thạo các khóa học SQL.
Pentium10

Bạn chỉ cần một mảng ngày dựa trên phạm vi ngày đã chọn?
Derek Adair

1
Tôi đang nghĩ đến việc sử dụng, để tìm cho bạn một vấn đề ... Nếu bạn nhận được một nhiệm vụ để điền vào một số hồ sơ còn thiếu trong bảng của bạn. Và bạn phải chạy một truy vấn cho mỗi ngày tôi đang nghĩ một cái gì đó giống nhưinsert into table select ... as days date between '' and ''
Pentium10

13
Một ví dụ cho việc sử dụng nó sẽ là tạo số liệu thống kê và bao gồm một hàng cho những ngày bạn không có dữ liệu. Nếu bạn đang thực hiện một số loại nhóm, việc tạo ra tất cả thông tin trong SQL có thể nhanh hơn nhiều và thêm nó vào bất kỳ định dạng nào bạn cần, thay vì bỏ dữ liệu của bạn theo ngôn ngữ của bạn, và bắt đầu lặp và thêm trống rỗng.
Nanne

1
@Nanne đó chính xác là lý do tại sao tôi lưu câu hỏi này. Tôi cần những điều trên để TRÁI THAM GIA vào dữ liệu có thể không tồn tại trong một số ngày nhất định.
Josh Diehl

Câu trả lời:


318

Giải pháp này không sử dụng các vòng lặp, quy trình hoặc bảng tạm thời . Truy vấn con tạo ngày trong 10.000 ngày qua và có thể được mở rộng để quay lại hoặc chuyển tiếp theo ý muốn.

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' 

Đầu ra:

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

Ghi chú về Hiệu suất

Thử nghiệm ở đây , hiệu suất tốt đáng ngạc nhiên: truy vấn trên mất 0,0009 giây.

Nếu chúng tôi mở rộng truy vấn con để tạo khoảng. 100.000 số (và do đó có giá trị khoảng 274 năm ngày), nó chạy trong 0,0458 giây.

Ngẫu nhiên, đây là một kỹ thuật rất di động, hoạt động với hầu hết các cơ sở dữ liệu với các điều chỉnh nhỏ.

Ví dụ SQL Fiddle trở lại 1.000 ngày


6
Bạn sẽ thấy hiệu suất tốt hơn nếu bạn thay đổi UNIONthành UNION ALL- thật lãng phí thời gian kiểm tra các bản sao để loại bỏ không tồn tại. Mặc dù đó là IMO quá phức tạp - nếu bạn định xây dựng một tập kết quả bằng UNION, tại sao không chỉ định ngày và được thực hiện với nó?
Ngựa vằn OMG

7
tại sao không chỉ định ngày và được thực hiện với nó - bởi vì phương pháp trên cho phép bạn tạo các bộ số lớn (và ngày) tùy ý không yêu cầu tạo bảng, điều đó sẽ gây khó khăn cho mã cứng theo cách bạn đang đề xuất. Rõ ràng trong 5 ngày nó là quá mức cần thiết; nhưng ngay cả sau đó, nếu bạn đang tham gia vào một bảng mà bạn không biết trước ngày, nhưng chỉ các giá trị tối thiểu và tối đa tiềm năng, điều đó có ý nghĩa.
RedFilter

2
Thật là "đau đớn" khi chỉ sử dụng hàm DATETIME thay cho câu lệnh UNION mà bạn đã tạo? Nó làm giảm bớt bất kỳ nhu cầu logic nào bạn phải thêm . Do đó - bạn đã quá phức tạp truy vấn. Dù sao đi nữa, tuyên bố của UNION không thể mở rộng - chỉ định ngày hoặc số, ai muốn cập nhật nó để phù hợp với việc nói 20 hoặc 30 ngày?
Ngựa Non OMG

23
Thật tuyệt khi thấy một câu trả lời cho câu hỏi, không phải là những bình luận vô tận về cách nó không thể, hoặc không nên, được thực hiện. Hầu hết mọi thứ có thể được thực hiện và "nên" chỉ có ý nghĩa trong bối cảnh, điều này khác với mọi người. Câu trả lời này đã giúp tôi, mặc dù tôi nhận thức rõ có nhiều cách tốt hơn trong hầu hết các tình huống.
joe

7
Những người bạn không thể làm cho truy vấn này hoạt động: Vui lòng tự tát vào mặt và sau đó đọc lại nhận xét của OP về truy vấn này tạo ra 1000 ngày. Vì năm 2010 là hơn 1000 ngày trước, bạn sẽ cần điều chỉnh truy vấn cho phù hợp.
Noel Baron

32

Đây là một biến thể khác sử dụng lượt xem:

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;

Và sau đó bạn có thể chỉ cần làm (xem nó thanh lịch như thế nào?):

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

Cập nhật

Điều đáng chú ý là bạn sẽ chỉ có thể tạo các ngày trong quá khứ bắt đầu từ ngày hiện tại . Nếu bạn muốn tạo bất kỳ loại phạm vi ngày nào (quá khứ, tương lai và ở giữa), bạn sẽ phải sử dụng chế độ xem này thay thế:

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
Điều này không hoạt động trong tất cả các trường hợp. CHỌN ngày TỪ ngày NGÀY GIỮA '2014-12-01' VÀ '2014-12-28' ĐẶT HÀNG theo ngày
vasanth

3
Cuộc gọi tốt @ user927258. Điều này là do chế độ xem đầu tiên datesđược đề cập ở trên tính toán các ngày bắt đầu từ ngày hiện tại, đó là lý do tại sao bạn sẽ không thể truy xuất các ngày được đặt trong tương lai. Trả lời từ @RedFilter bị lỗi thiết kế tương tự. Tôi đã thêm một cách giải quyết trong câu trả lời của tôi mặc dù.
Stéphane

Sử dụng một số khung nhìn chắc chắn đơn giản hóa các truy vấn và làm cho nó có thể sử dụng lại. Mặc dù về cơ bản chúng đang làm điều tương tự, nhưng tất cả các UNIONmệnh đề đó trông lạ trong một câu lệnh SQL.
Stewart

24

Câu trả lời được chấp nhận không hoạt động đối với PostgreSQL (lỗi cú pháp tại hoặc gần "a").

Cách bạn làm điều này trong PostgreSQL là bằng cách sử dụng generate_serieshàm, tức là:

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

Sử dụng Biểu thức bảng chung đệ quy (CTE), bạn có thể tạo danh sách ngày, sau đó chọn từ đó. Rõ ràng bạn thường không muốn tạo ra ba triệu ngày, vì vậy điều này chỉ minh họa các khả năng. Bạn chỉ có thể giới hạn phạm vi ngày bên trong CTE và bỏ qua mệnh đề where từ câu lệnh select bằng cách sử dụng CTE.

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)

Trên Microsoft SQL Server 2005, việc tạo danh sách CTE của tất cả các ngày có thể mất 1:08. Tạo ra một trăm năm mất ít hơn một giây.


7

Truy vấn 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

Đầu ra

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
Nếu tôi chỉ cuộn xuống thêm một chút nữa ... thở dài. Dù sao, cảm ơn bạn. Tôi đã thêm CAST (<biểu thức> NHƯ NGÀY) để xóa thời gian trên phiên bản của tôi. Cũng được sử dụng trong trường hợp a.Date giữa GETDATE () - 365 VÀ GETDATE () ... nếu bạn chạy truy vấn của mình ngày hôm nay, nó sẽ không có hàng nếu bạn không nhận thấy ngày trong WHERE = P
Ricardo C

4

Giải pháp trường học cũ để thực hiện việc này mà không có vòng lặp / con trỏ là tạo một NUMBERSbảng, có một cột Số nguyên duy nhất với các giá trị bắt đầu từ 1.

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

Bạn cần điền vào bảng với đủ hồ sơ để đáp ứng nhu cầu của bạn:

INSERT INTO NUMBERS (id) VALUES (NULL);

Khi bạn đã có NUMBERSbảng, bạn có thể sử dụng:

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'

Giải pháp công nghệ thấp tuyệt đối sẽ là:

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

Bạn sẽ sử dụng nó để làm gì?


Để tạo danh sách ngày hoặc số để TRÁI THAM GIA vào. Bạn sẽ làm điều này để xem nơi nào có khoảng trống trong dữ liệu, bởi vì bạn đang TRẢ LỜI vào một danh sách dữ liệu tuần tự - các giá trị null sẽ làm cho nó rõ ràng nơi tồn tại khoảng trống.


1
Các DUALbảng được hỗ trợ bởi Oracle và MySQL để sử dụng như một stand-trong bảng trong FROMmệnh đề. Nó không tồn tại, chọn giá trị từ nó sẽ trả về bất cứ giá trị nào. Ý tưởng là để có độc lập vì một truy vấn CHỌN yêu cầu một FROMmệnh đề chỉ định ít nhất một bảng.
Ngựa Non OMG

1
+1 để thực sự tạo bảng số cố định thay vì tạo RDBMS xây dựng bảng đó với mỗi lần bạn cần truy vấn. Bảng phụ trợ không ác, mọi người!
Bacon Bits

4

Đối với Access 2010 - cần nhiều bước; Tôi đã làm theo mô hình tương tự như được đăng ở trên, nhưng nghĩ rằng tôi có thể giúp ai đó trong Access. Làm việc rất tốt cho tôi, tôi đã không phải giữ một bảng ngày tháng.

Tạo một bảng có tên DUAL (tương tự như cách bảng Oracle DUAL hoạt động)

  • ID (Số tự động)
  • DummyColumn (Văn bản)
  • Thêm một giá trị hàng (1, "DummyRow")

Tạo một truy vấn có tên "ZeroThru9Q"; tự nhập cú pháp sau:

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;

Tạo một truy vấn có tên "TodayMinus1KQ" (cho các ngày trước ngày hôm nay); tự nhập cú pháp sau:

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

Tạo một truy vấn có tên "TodayPlus1KQ" (cho các ngày sau ngày hôm nay); tự nhập cú pháp sau:

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;

Tạo truy vấn hợp nhất có tên "TodayPlusMinus1KQ" (cho các ngày +/- 1000 ngày):

SELECT MyDate
FROM TodayMinus1KQ
UNION
SELECT MyDate
FROM TodayPlus1KQ;

Bây giờ bạn có thể sử dụng truy vấn:

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

3

Thủ tục + bảng tạm thời:

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 - bạn đã khiến tôi tham gia stackoverflow :) - đây là phần chuyển sang msaccess của tôi - nghĩ rằng nó sẽ hoạt động trên mọi phiên bản:

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]);

tham chiếu MSysObjects chỉ 'vì quyền truy cập cần một bảng đếm' ít nhất 1 bản ghi, trong một mệnh đề từ - bất kỳ bảng nào có ít nhất 1 bản ghi sẽ làm.


2

Như đã nêu (hoặc ít nhất là ám chỉ) trong nhiều câu trả lời tuyệt vời đã được đưa ra, vấn đề này dễ dàng được giải quyết một khi bạn có một bộ số để làm việc.

Lưu ý: Dưới đây là T-SQL nhưng đơn giản là việc triển khai các khái niệm chung của tôi đã được đề cập ở đây và trên internet nói chung. Nó sẽ tương đối đơn giản để chuyển đổi mã theo phương ngữ bạn chọn.

Làm sao? Xem xét truy vấn này:

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

Ở trên tạo ra phạm vi ngày 1/22/0001 - 1/27/0001 và cực kỳ tầm thường. Có 2 phần quan trọng của thông tin trong các truy vấn trên: các ngày bắt đầu của 0001-01-22bù đắp của 5. Nếu chúng ta kết hợp hai thông tin này thì rõ ràng chúng ta có ngày kết thúc. Do đó, trong hai ngày, việc tạo một phạm vi có thể được chia nhỏ như vậy:

  • Tìm sự khác biệt giữa hai ngày đã cho (phần bù), dễ dàng:

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

    Sử dụng ABS()ở đây đảm bảo rằng thứ tự ngày là không liên quan.

  • Tạo một bộ số giới hạn, cũng dễ dàng:

    -- 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')

    Lưu ý rằng chúng tôi không thực sự quan tâm những gì chúng tôi đang chọn FROMở đây. Chúng ta chỉ cần một tập hợp để làm việc để chúng ta đếm số lượng hàng trong đó. Cá nhân tôi sử dụng TVF, một số sử dụng CTE, một số khác sử dụng bảng số thay thế, bạn hiểu ý. Tôi ủng hộ việc sử dụng giải pháp hiệu quả nhất mà bạn cũng hiểu.

Kết hợp hai phương pháp này sẽ giải quyết vấn đề của chúng tôi:

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));

Ví dụ trên là mã khủng khiếp nhưng chứng minh làm thế nào mọi thứ kết hợp với nhau.

Vui hơn

Tôi cần phải làm điều này rất nhiều vì vậy tôi đã gói gọn logic vào hai TVF. Cái đầu tiên tạo ra một dãy số và cái thứ hai sử dụng chức năng này để tạo ra một phạm vi ngày. Toán học là để đảm bảo rằng thứ tự đầu vào không thành vấn đề và vì tôi muốn sử dụng đầy đủ các số có sẵn trong GenerateRangeSmallInt.

Chức năng sau đây mất ~ 16ms thời gian CPU để trả về phạm vi tối đa 65536 ngày.

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

thử cái này.

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

Bạn muốn có được một phạm vi ngày.

Trong ví dụ của bạn, bạn muốn có được các ngày trong khoảng từ '2010-01-20' và '2010-01-24'

giải pháp có thể:

 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'

Giải trình

MySQL có hàm date_add vậy

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

sẽ cho bạn

2010-01-21

Hàm dateiff sẽ cho bạn biết thường xuyên bạn phải lặp lại điều này

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

trả về

 4

Lấy danh sách các ngày trong phạm vi ngày sôi xuống để tạo một chuỗi số nguyên xem tạo ra một chuỗi số nguyên trong MySQL

Câu trả lời được đánh giá cao nhất ở đây đã sử dụng một cách tiếp cận tương tự như https://stackoverflow.com/a/2652051/1497139 làm cơ sở:

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

sẽ dẫn đến

row
1.0
2.0
3.0
4.0

Các hàng bây giờ có thể được sử dụng để tạo danh sách ngày từ ngày bắt đầu đã cho. Để bao gồm ngày bắt đầu, chúng tôi bắt đầu với hàng -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

Nếu bạn cần nhiều hơn một vài ngày, bạn cần một cái bàn.

Tạo một phạm vi ngày trong mysql

sau đó,

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

3
Tại sao bạn đã đăng bài này, vì câu trả lời ở trên không cần bảng và cung cấp giải pháp?
Pentium10

1

Tạo ngày giữa hai trường ngày

Nếu bạn biết về truy vấn SQL CTE, thì giải pháp này sẽ giúp bạn giải quyết câu hỏi của bạn

Đây là ví dụ

Chúng tôi có ngày trong một bảng

Tên bảng: ăn thử

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

Yêu cầu kết quả:

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

Giải pháp:

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

Giải thích: Giải thích truy vấn đệ quy CTE

  • Phần đầu tiên của truy vấn:

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

    Giải thích: Firstcolumn là Bắt đầu và ngày, cột thứ hai là sự khác biệt của ngày bắt đầu và ngày kết thúc sau vài ngày và nó sẽ được coi là cột Khác biệt

  • Phần thứ hai của truy vấn:

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

    Giải thích: Liên minh tất cả sẽ kế thừa kết quả của truy vấn trên cho đến khi kết quả không có giá trị, vì vậy, kết quả Bắt đầu xuất hiện được kế thừa từ truy vấn CTE được tạo và từ diff, giảm - 1, do đó, nó trông giống như 3, 2 và 1 cho đến 0

Ví dụ

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

Đặc điểm kỹ thuật kết quả

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
  • Phần thứ 3 của truy vấn

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

    Nó sẽ thêm ngày vào ngày hôm nay

Kết quả

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

1

Câu trả lời ngắn hơn được chấp nhận, cùng một ý tưởng:

(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

Đối với bất kỳ ai muốn đây là chế độ xem được lưu (MySQL không hỗ trợ các câu lệnh chọn lồng nhau trong chế độ xem):

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;

Bạn có thể làm

select * from date_range

để có được

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

1

Giải pháp tinh tế sử dụng chức năng đệ quy mới (Biểu thức bảng chung) trong MariaDB> = 10.3 và 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;

Ở trên trả về một bảng ngày giữa '2019-01-01' và '2019-04-30'. Nó cũng nhanh chóng. Trả lại ngày có giá trị 1000 năm (~ 365.000 ngày) mất khoảng 400ms trên máy của tôi.


1

Đó là một ý tưởng tốt với việc tạo ra những ngày này một cách nhanh chóng. Tuy nhiên, tôi không cảm thấy thoải mái khi làm điều này với phạm vi khá lớn nên tôi đã kết thúc với giải pháp sau:

  1. Đã tạo bảng "DatesNumbers" sẽ giữ các số được sử dụng để tính ngày:
CREATE TABLE DatesNumbers (
    i MEDIUMINT NOT NULL,
    PRIMARY KEY (i)
)
COMMENT='Used by Dates view'
;
  1. Đưa vào bảng bằng các kỹ thuật trên với các số từ -59999 đến 40000. Phạm vi này sẽ cho tôi ngày từ 59999 ngày (~ 164 năm) phía sau đến 40000 ngày (109 năm) phía trước:
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. Đã tạo chế độ xem "Ngày":
SELECT
      i,
      CURRENT_DATE() + INTERVAL i DAY AS Date
FROM
    DatesNumbers

Đó là nó.

  • (+) Dễ đọc các truy vấn
  • (+) Không có các thế hệ số bay
  • (+) Cung cấp ngày trong quá khứ và trong tương lai và KHÔNG CÓ ĐOÀN KẾT nào trong quan điểm này như trong bài viết này .
  • (+) Ngày chỉ "trong quá khứ" hoặc "chỉ trong tương lai" có thể được lọc bằng cách sử dụng WHERE i < 0hoặc WHERE i > 0(PK)
  • (-) 'tạm thời' bảng & chế độ xem được sử dụng

0

Được rồi .. Hãy thử điều này: http://www.devshed.com/c/a/MySQL/Delving-Deeper-into-MyQuery-50/
http://dev.mysql.com/doc/refman/5.0/en/ loop-statement.html
http://www.roseindia.net/sql/mysql-example/mysql-loop.shtml

Sử dụng để tạo bảng tạm thời và sau đó chọn * trên bảng tạm thời. Hoặc xuất kết quả một lần.
Những gì bạn nói bạn muốn làm không thể được thực hiện bằng câu lệnh CHỌN , nhưng có thể thực hiện được với những điều cụ thể đối với MySQL.
Sau đó, một lần nữa, có thể bạn cần con trỏ: http://dev.mysql.com/doc/refman/5.0/en/cursors.html


0

Đối với Oracle, giải pháp của tôi là:

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

Sysdate có thể được thay đổi thành ngày cụ thể và số cấp có thể được thay đổi để cung cấp thêm ngày.


0

nếu bạn muốn danh sách ngày giữa hai ngày:

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

* fiddle ở đây: 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

Phiên bản SQLite của giải pháp hàng đầu RedFilters

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

được cải thiện với ngày trong tuần khi tham gia bảng ngày lễ tùy chỉnh microsoft MSSQL 2012 cho bảng ngày powerpOLL 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

Có thể tạo một thủ tục cũng để tạo bảng lịch với thời gian biểu khác với ngày. Nếu bạn muốn có một bảng cho mỗi quý

ví dụ

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

bạn có thể dùng

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

và sau đó thao tác thông qua

select ts, dt from calendar_table_tmp;

cung cấp cho bạn cũng 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'

từ đây bạn có thể bắt đầu thêm các thông tin khác như

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

hoặc tạo một bảng thực với tạo bảng tuyên bố


0

Một câu trả lời chung chung hơn hoạt động trong 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

Thêm một giải pháp cho mysql 8.0.1 và mariadb 10.2.2 bằng cách sử dụng các biểu thức bảng chung đệ quy:

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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.