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 (
selectcurdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a) ) DAYasDatefrom (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as a
crossjoin (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as b
crossjoin (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as c
crossjoin (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) 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.
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:
CREATEVIEW digits ASSELECT0AS digit UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9;
CREATEVIEW numbers ASSELECT
ones.digit + tens.digit * 10 + hundreds.digit * 100 + thousands.digit * 1000ASnumberFROM
digits as ones,
digits as tens,
digits as hundreds,
digits as thousands;
CREATEVIEW dates ASSELECTSUBDATE(CURRENT_DATE(), number) ASdateFROM
numbers;
Dan kemudian Anda dapat melakukannya (lihat betapa elegannya itu?):
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:
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:
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 (
selectconvert(datetime, '1753-01-01') as [date] --startunionallselectdateadd(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.
select datetable.Date
from (
selectDATEADD(day,-(a.a + (10 * b.a) + (100 * c.a)),getdate()) ASDatefrom (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as a
crossjoin (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as b
crossjoin (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as c
) datetable
where datetable.Date between'2014-01-20'and'2014-01-24'orderby 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
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.
Anda perlu mengisi tabel dengan catatan yang cukup untuk memenuhi kebutuhan Anda:
INSERTINTO NUMBERS (id) VALUES (NULL);
Setelah Anda memiliki NUMBERStabel, Anda dapat menggunakan:
SELECT x.start_date + INTERVAL n.id-1DAYFROM NUMBERS n
JOIN (SELECTSTR_TO_DATE('2010-01-20', '%Y-%m-%d') AS start_date
FROM DUAL) x
WHERE x.start_date + INTERVAL n.id-1DAY <= '2010-01-24'
Solusi berteknologi rendah mutlak adalah:
SELECTSTR_TO_DATE('2010-01-20', '%Y-%m-%d')
FROM DUAL
UNIONALLSELECTSTR_TO_DATE('2010-01-21', '%Y-%m-%d')
FROM DUAL
UNIONALLSELECTSTR_TO_DATE('2010-01-22', '%Y-%m-%d')
FROM DUAL
UNIONALLSELECTSTR_TO_DATE('2010-01-23', '%Y-%m-%d')
FROM DUAL
UNIONALLSELECTSTR_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.
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:
Buat kueri bernama "TodayMinus1KQ" (untuk tanggal sebelum hari ini); masukkan sintaks berikut secara manual:
SELECTdate() - (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:
SELECTdate() + (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
UNIONSELECT MyDate
FROM TodayPlus1KQ;
Sekarang Anda dapat menggunakan kueri:
SELECT MyDate
FROM TodayPlusMinus1KQ
WHERE MyDate BETWEEN#05/01/2014# and #05/30/2014#
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
unionallselect top 1"1"as espr2 from MSysObjects
unionallselect top 1"2"as espr3 from MSysObjects
unionallselect top 1"3"as espr4 from MSysObjects
unionallselect top 1"4"as espr5 from MSysObjects
unionallselect top 1"5"as espr6 from MSysObjects
unionallselect top 1"6"as espr7 from MSysObjects
unionallselect top 1"7"as espr8 from MSysObjects
unionallselect top 1"8"as espr9 from MSysObjects
unionallselect top 1"9"as espr9 from MSysObjects
) as a,
(
select top 1"0"as espr1 from MSysObjects
unionallselect top 1"1"as espr2 from MSysObjects
unionallselect top 1"2"as espr3 from MSysObjects
unionallselect top 1"3"as espr4 from MSysObjects
unionallselect top 1"4"as espr5 from MSysObjects
unionallselect top 1"5"as espr6 from MSysObjects
unionallselect top 1"6"as espr7 from MSysObjects
unionallselect top 1"7"as espr8 from MSysObjects
unionallselect top 1"8"as espr9 from MSysObjects
unionallselect top 1"9"as espr9 from MSysObjects
) as b,
(
select top 1"0"as espr1 from MSysObjects
unionallselect top 1"1"as espr2 from MSysObjects
unionallselect top 1"2"as espr3 from MSysObjects
unionallselect top 1"3"as espr4 from MSysObjects
unionallselect top 1"4"as espr5 from MSysObjects
unionallselect top 1"5"as espr6 from MSysObjects
unionallselect top 1"6"as espr7 from MSysObjects
unionallselect top 1"7"as espr8 from MSysObjects
unionallselect top 1"8"as espr9 from MSysObjects
unionallselect 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.
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:
SELECTDATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through NWHERE 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:
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(ORDERBY (SELECTNULL)) - 1FROM (SELECT'A'AS S UNIONALLSELECT'A'UNIONALLSELECT'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.
WITH CTE AS
(SELECTDISTINCTconvert(varchar(10),StartTime, 101) AS StartTime,
datediff(dd,StartTime, endTime) AS diff
FROM dbo.testdate
UNIONALLSELECT StartTime,
diff - 1AS diff
FROM CTE
WHERE diff<> 0)
SELECTDISTINCTDateAdd(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
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
Jawaban yang lebih pendek dari yang diterima, ide yang sama:
(SELECTTRIM('2016-01-05' + INTERVAL a + b DAY) dateFROM
(SELECT0 a UNIONSELECT1 a UNIONSELECT2UNIONSELECT3UNIONSELECT4UNIONSELECT5UNIONSELECT6UNIONSELECT7UNIONSELECT8UNIONSELECT9 ) d,
(SELECT0 b UNIONSELECT10UNIONSELECT20UNIONSELECT30UNIONSELECT40) m
WHERE'2016-01-05' + INTERVAL a + b DAY <= '2016-01-21')
Solusi elegan menggunakan fungsionalitas rekursif (Common Table Expressions) baru di MariaDB> = 10.3 dan MySQL> = 8.0.
WITHRECURSIVE t as (
select'2019-01-01'as dt
UNIONSELECTDATE_ADD(t.dt, INTERVAL1DAY) FROM t WHEREDATE_ADD(t.dt, INTERVAL1DAY) <= '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.
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:
Membuat tabel "DatesNumbers" yang akan menampung angka-angka yang digunakan untuk penghitungan tanggal:
CREATETABLE DatesNumbers (
i MEDIUMINT NOTNULL,
PRIMARY KEY (i)
)
COMMENT='Used by Dates view'
;
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:
INSERTINTO DatesNumbers
SELECT
a.i + (10 * b.i) + (100 * c.i) + (1000 * d.i) + (10000 * e.i) - 59999AS i
FROM
(SELECT0AS i UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9) AS a,
(SELECT0AS i UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9) AS b,
(SELECT0AS i UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9) AS c,
(SELECT0AS i UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9) AS d,
(SELECT0AS i UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9) AS e
;
Membuat tampilan "Tanggal":
SELECT
i,
CURRENT_DATE() + INTERVAL i DAYASDateFROM
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)
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
setlanguage'SPANISH'DECLARE @tabletable(fechaDesde datetime , fechaHasta datetime )
INSERT @tableVALUES('20151231' , '20161231');
WITH x AS
(
SELECTDATEADD( m , 1 ,fechaDesde ) as fecha FROM @tableUNIONALLSELECTDATEADD( m , 1 ,fecha )
FROM @table t INNERJOIN x ONDATEADD( m , 1 ,x.fecha ) <= t.fechaHasta
)
SELECTLEFT( 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)
select d.Date
from (
selectdate(julianday('2010-01-20') + (a.a + (10 * b.a) + (100 * c.a))) asDatefrom (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as a
crossjoin (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as b
crossjoin (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as c
) d
where
d.Date between'2010-01-20'and'2010-01-24'orderby d.Date
with [dates] as (
selectconvert(datetime, '2016-01-01') as [date] --startunionallselectdateadd(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)
WITH
Digits AS (SELECT0 D UNIONSELECT1UNIONSELECT2UNIONSELECT3UNIONSELECT4UNIONSELECT5UNIONSELECT6UNIONSELECT7UNIONSELECT8UNIONSELECT9),
Dates AS (SELECT adddate('1970-01-01',t4.d*10000 + t3.d*1000 + t2.d*100 + t1.d*10 +t0.d) ASdateFROM Digits AS t0, Digits AS t1, Digits AS t2, Digits AS t3, Digits AS t4)
SELECT * FROM Dates WHEREdateBETWEEN'2017-01-01'AND'2017-12-31'
Jawaban yang lebih umum yang berfungsi di AWS MySQL.
select datetable.Date
from (
selectdate_format(adddate(now(),-(a.a + (10 * b.a) + (100 * c.a))),'%Y-%m-%d') ASDatefrom (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as a
crossjoin (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as b
crossjoin (select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9) as c
) datetable
where datetable.Date betweennow() - INTERVAL14DayandNow()
orderby datetable.Date DESC
insert into table select ... as days date between '' and ''
Jawaban:
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
sumber
UNION
keUNION 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?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;
sumber
dates
disebutkan 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.UNION
klausa tersebut terlihat aneh dalam satu pernyataan SQL.Jawaban yang diterima tidak berfungsi untuk PostgreSQL (kesalahan sintaks di atau dekat "a").
Cara Anda melakukan ini di PostgreSQL adalah dengan menggunakan
generate_series
fungsi, 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)
sumber
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.
sumber
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
sumber
Solusi jadul untuk melakukan ini tanpa perulangan / kursor adalah dengan membuat
NUMBERS
tabel, 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
NUMBERS
tabel, 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.
sumber
DUAL
table didukung oleh Oracle dan MySQL untuk digunakan sebagai stand-in meja diFROM
klausa. Itu tidak ada, memilih nilai darinya akan mengembalikan apa pun nilainya. Idenya adalah untuk memiliki stand-in karena query SELECT memerlukanFROM
klausa yang menentukan setidaknya satu tabel.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)
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#
sumber
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
sumber
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.
sumber
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-22
dan mengimbangi dari5
. 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
FROM
sini. 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 );
sumber
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') ;
sumber
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
Fungsi Dateiff akan memberi tahu Anda bahwa Anda harus sering mengulanginya
select datediff('2010-01-24', '2010-01-20')
yang kembali
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
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'
sumber
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;
sumber
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"
Membutuhkan Hasil:
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
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
sumber
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')
sumber
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 ...
sumber
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.
sumber
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:
CREATE TABLE DatesNumbers ( i MEDIUMINT NOT NULL, PRIMARY KEY (i) ) COMMENT='Used by Dates view' ;
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 ;
SELECT i, CURRENT_DATE() + INTERVAL i DAY AS Date FROM DatesNumbers
Itu dia.
WHERE i < 0
atauWHERE i > 0
(PK)sumber
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
sumber
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.
sumber
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
sumber
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)
sumber
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() );
sumber
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
sumber
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)
sumber
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'
sumber
Dapat membuat prosedur juga untuk membuat tabel kalender dengan peta waktu yang berbeda dari hari. Jika Anda menginginkan meja untuk setiap kuartal
misalnya
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
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
sumber
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
sumber
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;
sumber