menghasilkan hari dari rentang tanggal


138

Saya ingin menjalankan kueri seperti

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

Dan kembalikan data seperti:

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

10
Tidak ada masalah lain yang dilampirkan pada pertanyaan ini. Pertanyaan di atas adalah masalahnya, menguasai kursus SQL.
Pentium10

Apakah Anda hanya memerlukan rangkaian tanggal berdasarkan rentang tanggal yang dipilih?
Derek Adair

1
Saya sedang memikirkan penggunaan, untuk menemukan masalah bagi Anda ... Jika Anda mendapat tugas untuk mengisi beberapa catatan yang hilang di tabel Anda. Dan Anda harus menjalankan kueri untuk setiap hari. Saya memikirkan sesuatu sepertiinsert into table select ... as days date between '' and ''
Pentium10

14
Contoh penggunaannya adalah untuk menghasilkan statistik, dan menyertakan baris untuk tanggal yang tidak Anda miliki datanya. Jika Anda melakukan semacam group-by, akan lebih cepat untuk benar-benar menghasilkan semua informasi dalam SQL dan menambahkannya ke dalam format apa pun yang Anda butuhkan, daripada membuang data Anda apa adanya ke bahasa Anda, dan mulai mengulang dan menambahkan kosong.
Nanne

1
@Nanne itulah tepatnya mengapa saya menyimpan pertanyaan ini. Saya membutuhkan yang di atas untuk KIRI BERGABUNG menjadi data yang mungkin tidak ada untuk tanggal tertentu.
Josh Diehl

Jawaban:


325

Solusi ini tidak menggunakan loop, prosedur, atau tabel temp . Subkueri menghasilkan tanggal untuk 10.000 hari terakhir, dan dapat diperpanjang sejauh yang Anda inginkan.

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' 

Keluaran:

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

Catatan tentang Kinerja

Pengujian itu di sini , kinerja yang mengejutkan baik: query di atas membutuhkan 0,0009 detik.

Jika kita memperluas subquery untuk menghasilkan kira-kira. 100.000 angka (dan dengan demikian tanggal sekitar 274 tahun), itu berjalan dalam 0,0458 detik.

Kebetulan, ini adalah teknik yang sangat portabel yang bekerja dengan sebagian besar database dengan sedikit penyesuaian.

Contoh SQL Fiddle menghasilkan 1.000 hari


6
Anda akan melihat kinerja yang lebih baik jika Anda mengubahnya UNIONke UNION ALL- membuang-buang waktu memeriksa duplikat untuk dihapus yang tidak ada. Ini IMO yang terlalu rumit - jika Anda akan membuat kumpulan hasil menggunakan UNION, mengapa tidak menentukan tanggal dan selesai dengannya?
OMG Ponies

7
mengapa tidak hanya menentukan tanggal dan menyelesaikannya - karena metode di atas memungkinkan Anda untuk membuat kumpulan angka (dan tanggal) yang besar secara sewenang-wenang yang tidak memerlukan pembuatan tabel, itu akan menyakitkan untuk hard-code dengan cara yang Anda sarankan. Jelas untuk 5 tanggal itu berlebihan; tetapi meskipun demikian, jika Anda bergabung dengan tabel di mana Anda tidak mengetahui tanggal sebelumnya, tetapi hanya potensi nilai min dan maks, itu masuk akal.
RedFilter

2
Sungguh "menyakitkan" untuk hanya menggunakan fungsi DATETIME sebagai pengganti pernyataan UNION yang telah Anda buat? Ini mengurangi kebutuhan logika yang harus Anda tambahkan . Karenanya - Anda telah memperumit kueri. Pernyataan UNION, bagaimanapun juga, tidak dapat diskalakan - menentukan tanggal atau angka, siapa yang ingin memperbaruinya untuk mengakomodasi, katakanlah 20 atau 30 tanggal?
OMG Ponies

25
Sangat menyenangkan melihat jawaban atas pertanyaan, bukan komentar tanpa akhir bagaimana hal itu tidak dapat, atau tidak seharusnya, dilakukan. Kebanyakan hal dapat dilakukan, dan "harus" hanya bermakna dalam konteks, yang berbeda untuk setiap orang. Jawaban ini membantu saya, meskipun saya sangat menyadari ada cara yang lebih baik dalam banyak situasi.
joe

7
Bagi Anda yang tidak bisa mendapatkan kueri ini untuk bekerja: Silakan tampar diri Anda sendiri dan kemudian baca kembali komentar OP tentang kueri ini yang menghasilkan 1000 tanggal. Karena 2010 lebih dari 1000 hari yang lalu, Anda harus menyesuaikan kueri yang sesuai.
Noel Baron

32

Berikut variasi lain menggunakan tampilan:

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;

Dan kemudian Anda dapat melakukannya (lihat betapa elegannya itu?):

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

Memperbarui

Perlu dicatat bahwa Anda hanya dapat menghasilkan tanggal yang sudah lewat mulai dari tanggal sekarang . Jika Anda ingin membuat segala jenis rentang tanggal (lampau, mendatang, dan di antaranya), Anda harus menggunakan tampilan ini sebagai gantinya:

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
Ini tidak berhasil di semua kasus. PILIH tanggal DARI tanggal DIMANA tanggal ANTARA '2014-12-01' DAN '2014-12-28' ORDER BERDASARKAN tanggal
vasanth

3
Panggilan bagus @ user927258. Ini karena tampilan pertama yang datesdisebutkan di atas menghitung tanggal mulai dari tanggal saat ini, itulah sebabnya Anda tidak dapat mengambil tanggal yang ditetapkan di masa mendatang. Jawaban dari @RedFilter menderita cacat desain yang sama. Saya telah menambahkan solusi dalam jawaban saya.
Stéphane

Menggunakan beberapa tampilan pasti menyederhanakan kueri, dan membuatnya dapat digunakan kembali. Meskipun pada dasarnya mereka melakukan hal yang sama, semua UNIONklausa tersebut terlihat aneh dalam satu pernyataan SQL.
Stewart

25

Jawaban yang diterima tidak berfungsi untuk PostgreSQL (kesalahan sintaks di atau dekat "a").

Cara Anda melakukan ini di PostgreSQL adalah dengan menggunakan generate_seriesfungsi, yaitu:

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

Dengan menggunakan Common Table Expression (CTE) rekursif, Anda dapat membuat daftar tanggal, lalu memilihnya. Jelas Anda biasanya tidak ingin membuat tiga juta tanggal, jadi ini hanya menggambarkan kemungkinannya. Anda dapat dengan mudah membatasi rentang tanggal di dalam CTE dan menghilangkan klausa where dari pernyataan pemilihan menggunakan 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)

Di Microsoft SQL Server 2005, membuat daftar CTE dari semua kemungkinan tanggal membutuhkan waktu 1:08. Menghasilkan seratus tahun membutuhkan waktu kurang dari satu detik.


7

Kueri 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

Keluaran

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
Jika saya hanya menggulir sedikit lagi ... menghela napas. Terima kasih. Saya menambahkan CAST (<expression> AS DATE) untuk menghapus waktu pada versi saya. Juga digunakan di mana a. Tanggal antara GETDATE () - 365 DAN GETDATE () ... jika Anda menjalankan kueri Anda hari ini, tidak akan ada baris jika Anda tidak memperhatikan tanggal di WHERE = P
Ricardo C

4

Solusi jadul untuk melakukan ini tanpa perulangan / kursor adalah dengan membuat NUMBERStabel, yang memiliki kolom Bilangan bulat tunggal dengan nilai mulai dari 1.

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

Anda perlu mengisi tabel dengan catatan yang cukup untuk memenuhi kebutuhan Anda:

INSERT INTO NUMBERS (id) VALUES (NULL);

Setelah Anda memiliki NUMBERStabel, Anda dapat menggunakan:

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'

Solusi berteknologi rendah mutlak adalah:

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

Untuk apa Anda akan menggunakannya?


Untuk menghasilkan daftar tanggal atau nomor agar KIRI BERGABUNG ke. Anda akan melakukannya untuk melihat di mana ada celah dalam data, karena Anda KIRI BERGABUNG ke daftar data berurutan - nilai null akan memperjelas di mana ada celah.


1
The DUALtable didukung oleh Oracle dan MySQL untuk digunakan sebagai stand-in meja di FROMklausa. Itu tidak ada, memilih nilai darinya akan mengembalikan apa pun nilainya. Idenya adalah untuk memiliki stand-in karena query SELECT memerlukan FROMklausa yang menentukan setidaknya satu tabel.
OMG Ponies

1
1 untuk benar-benar membuat tabel angka permanen alih-alih membuat RDBMS membuatnya setiap kali Anda membutuhkan kueri. Meja bantu tidak jahat, orang!
Bacon Bits

4

Untuk Access 2010 - diperlukan beberapa langkah; Saya mengikuti pola yang sama seperti yang diposting di atas, tetapi saya pikir saya dapat membantu seseorang di Access. Bekerja dengan baik untuk saya, saya tidak harus menyimpan tabel tanggal unggulan.

Buat tabel bernama DUAL (mirip dengan cara kerja tabel Oracle DUAL)

  • ID (AutoNumber)
  • DummyColumn (Teks)
  • Tambahkan satu nilai baris (1, "DummyRow")

Buat kueri bernama "ZeroThru9Q"; masukkan sintaks berikut secara manual:

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;

Buat kueri bernama "TodayMinus1KQ" (untuk tanggal sebelum hari ini); masukkan sintaks berikut secara manual:

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

Buat kueri bernama "TodayPlus1KQ" (untuk tanggal setelah hari ini); masukkan sintaks berikut secara manual:

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;

Buat kueri gabungan bernama "TodayPlusMinus1KQ" (untuk tanggal +/- 1000 hari):

SELECT MyDate
FROM TodayMinus1KQ
UNION
SELECT MyDate
FROM TodayPlus1KQ;

Sekarang Anda dapat menggunakan kueri:

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

3

Prosedur + tabel sementara:

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 - Anda membuat saya bergabung dengan stackoverflow :) - ini adalah porting saya ke msaccess - pikir ini akan berfungsi pada versi apa pun:

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

MSysObjects yang direferensikan hanya karena akses memerlukan tabel yang menghitung setidaknya 1 record, dalam klausa from - tabel apa pun dengan minimal 1 record akan dilakukan.


2

Seperti yang dinyatakan (atau setidaknya disinggung) di banyak jawaban luar biasa yang telah diberikan, masalah ini mudah diselesaikan setelah Anda memiliki serangkaian angka untuk dikerjakan.

Catatan: Berikut ini adalah T-SQL tetapi ini hanyalah implementasi khusus dari konsep umum saya yang sudah disebutkan di sini dan di internet secara luas. Seharusnya relatif sederhana untuk mengubah kode ke dialek pilihan Anda.

Bagaimana? Pertimbangkan pertanyaan ini:

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

Di atas menghasilkan rentang tanggal 1/22/0001 - 1/27/0001 dan sangat sepele. Ada 2 buah kunci informasi dalam query di atas: tanggal mulai dari 0001-01-22dan mengimbangi dari 5. Jika kita menggabungkan kedua informasi ini, maka jelas kita memiliki tanggal akhir. Jadi, dengan dua tanggal, menghasilkan rentang dapat dipecah seperti ini:

  • Temukan perbedaan antara dua tanggal tertentu (offset), mudah:

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

    Menggunakan di ABS()sini memastikan bahwa urutan tanggal tidak relevan.

  • Hasilkan serangkaian angka terbatas, juga mudah:

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

    Perhatikan bahwa kami sebenarnya tidak peduli dengan apa yang kami pilih di FROMsini. Kami hanya perlu satu set untuk bekerja sehingga kami menghitung jumlah baris di dalamnya. Saya pribadi menggunakan TVF, beberapa menggunakan CTE, yang lain menggunakan tabel angka, Anda mengerti. Saya menganjurkan untuk menggunakan solusi paling berkinerja yang juga Anda pahami.

Menggabungkan kedua metode ini akan menyelesaikan masalah kita:

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

Contoh di atas adalah kode yang mengerikan tetapi menunjukkan bagaimana semuanya menjadi satu.

Lebih menyenangkan

Saya perlu melakukan banyak hal seperti ini jadi saya merangkum logika menjadi dua TVF. Yang pertama menghasilkan rentang angka dan yang kedua menggunakan fungsi ini untuk menghasilkan rentang tanggal. Perhitungannya adalah untuk memastikan bahwa urutan input tidak penting dan karena saya ingin menggunakan berbagai angka yang tersedia di GenerateRangeSmallInt.

Fungsi berikut membutuhkan ~ 16ms waktu CPU untuk mengembalikan kisaran maksimum 65536 tanggal.

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

coba ini.

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

Anda ingin mendapatkan rentang tanggal.

Dalam contoh Anda, Anda ingin mendapatkan tanggal antara '2010-01-20' dan '2010-01-24'

solusi yang mungkin:

 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'

Penjelasan

MySQL memiliki fungsi date_add

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

akan memberimu

2010-01-21

Fungsi Dateiff akan memberi tahu Anda bahwa Anda harus sering mengulanginya

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

yang kembali

 4

Mendapatkan daftar tanggal dalam rentang tanggal bermuara pada membuat urutan bilangan bulat lihat menghasilkan urutan bilangan bulat di MySQL

Jawaban yang paling banyak disukai di sini menggunakan pendekatan yang mirip dengan https://stackoverflow.com/a/2652051/1497139 sebagai dasarnya:

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

yang akan menghasilkan

row
1.0
2.0
3.0
4.0

Baris sekarang dapat digunakan untuk membuat daftar tanggal dari tanggal mulai tertentu. Untuk memasukkan tanggal mulai kita mulai dengan baris -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

jika Anda membutuhkan lebih dari beberapa hari, Anda membutuhkan meja.

Buat rentang tanggal di mysql

kemudian,

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

3
mengapa Anda memposting ini, karena balasan di atas tidak memerlukan tabel dan memberikan solusi?
Pentium10

1

Hasilkan tanggal antara dua bidang tanggal

Jika Anda mengetahui kueri SQL CTE, maka solusi ini akan membantu Anda untuk menjawab pertanyaan Anda

Berikut ini contohnya

Kami memiliki tanggal dalam satu tabel

Nama Tabel: "tanggal ujian"

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

Membutuhkan Hasil:

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

Larutan:

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

Penjelasan: Penjelasan kueri rekursif CTE

  • Bagian pertama dari query:

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

    Penjelasan: firstcolumn is “startdate”, kolom kedua adalah selisih tanggal mulai dan tanggal akhir dalam hari dan akan dianggap sebagai kolom “diff”

  • Bagian kedua dari kueri:

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

    Penjelasan: Gabungan semua akan mewarisi hasil kueri di atas sampai hasilnya menjadi nol, Jadi "Waktu Mulai" hasil diwarisi dari kueri CTE yang dihasilkan, dan dari diff, kurangi - 1, jadi terlihat seperti 3, 2, dan 1 sampai 0

Sebagai contoh

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

Spesifikasi Hasil

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
  • Bagian ke-3 dari Query

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

    Ini akan menambahkan hari "diff" di "startdate" jadi hasilnya harus seperti di bawah ini

Hasil

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

1

Jawaban yang lebih pendek dari yang diterima, ide yang sama:

(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

Untuk siapa saja yang menginginkan ini sebagai tampilan tersimpan (MySQL tidak mendukung pernyataan pemilihan bersarang dalam tampilan):

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;

Anda kemudian bisa melakukannya

select * from date_range

mendapatkan

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

1

Solusi elegan menggunakan fungsionalitas rekursif (Common Table Expressions) baru di MariaDB> = 10.3 dan 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;

Di atas mengembalikan tabel tanggal antara '2019-01-01' dan '2019-04-30'. Ini juga cukup cepat. Mengembalikan tanggal senilai 1000 tahun (~ 365.000 hari) membutuhkan waktu sekitar 400ms di mesin saya.


1

Ada baiknya membuat tanggal-tanggal ini dengan cepat. Namun, saya tidak merasa nyaman melakukan ini dengan jangkauan yang cukup besar sehingga saya berakhir dengan solusi berikut:

  1. Membuat tabel "DatesNumbers" yang akan menampung angka-angka yang digunakan untuk penghitungan tanggal:
CREATE TABLE DatesNumbers (
    i MEDIUMINT NOT NULL,
    PRIMARY KEY (i)
)
COMMENT='Used by Dates view'
;
  1. Mengisi tabel menggunakan teknik di atas dengan angka dari -59999 hingga 40000. Rentang ini akan memberi saya tanggal dari 59999 hari (~ 164 tahun) di belakang hingga 40000 hari (109 tahun) ke depan:
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. Membuat tampilan "Tanggal":
SELECT
      i,
      CURRENT_DATE() + INTERVAL i DAY AS Date
FROM
    DatesNumbers

Itu dia.

  • (+) Kueri yang mudah dibaca
  • (+) Tidak ada generasi nomor terbang
  • (+) Memberikan tanggal di masa lalu dan masa depan dan TIDAK ADA UNION yang terlihat untuk ini seperti pada posting ini .
  • (+) Tanggal "Hanya di masa lalu" atau "hanya di masa mendatang" dapat difilter menggunakan WHERE i < 0atau WHERE i > 0(PK)
  • (-) tabel & tampilan 'sementara' digunakan

0

Baiklah .. Coba ini: 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

Gunakan itu untuk, katakanlah, menghasilkan tabel temp, lalu lakukan pemilihan * pada tabel temp. Atau keluarkan hasil satu per satu.
Apa yang Anda katakan ingin Anda lakukan tidak dapat dilakukan dengan pernyataan SELECT , tetapi mungkin dapat dilakukan dengan hal-hal khusus untuk MySQL.
Kemudian lagi, mungkin Anda membutuhkan kursor: http://dev.mysql.com/doc/refman/5.0/en/cursors.html


0

Untuk Oracle, solusi saya adalah:

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

Sysdate dapat diubah menjadi tanggal tertentu dan nomor level dapat diubah untuk memberikan lebih banyak tanggal.


0

jika Anda ingin daftar tanggal antara dua tanggal:

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

* biola di sini: 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

Versi SQLite dari solusi teratas 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

ditingkatkan dengan hari kerja dan bergabung dengan tabel hari libur khusus microsoft MSSQL 2012 untuk tabel tanggal powerpivot 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

Dapat membuat prosedur juga untuk membuat tabel kalender dengan peta waktu yang berbeda dari hari. Jika Anda menginginkan meja untuk setiap kuartal

misalnya

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

kamu bisa memakai

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


dan kemudian memanipulasinya

select ts, dt from calendar_table_tmp;

yang memberi Anda juga 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'

dari sini Anda dapat mulai menambahkan informasi lain seperti

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

atau buat tabel nyata dengan membuat pernyataan tabel


0

Jawaban yang lebih umum yang berfungsi di 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

Satu lagi solusi untuk mysql 8.0.1 dan mariadb 10.2.2 menggunakan ekspresi tabel umum rekursif:

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;
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.