Menggunakan fungsi mysql standar apakah ada cara untuk menulis kueri yang akan mengembalikan daftar hari antara dua tanggal.
Misalnya, diberikan 2009-01-01 dan 2009-01-13 itu akan mengembalikan tabel satu kolom dengan nilai:
2009-01-01
2009-01-02
2009-01-03
2009-01-04
2009-01-05
2009-01-06
2009-01-07
2009-01-08
2009-01-09
2009-01-10
2009-01-11
2009-01-12
2009-01-13
Edit: Sepertinya saya belum jelas. Saya ingin MEMBUAT daftar ini. Saya memiliki nilai yang disimpan dalam database (berdasarkan datetime) tetapi ingin mereka dikumpulkan di sebelah kiri luar bergabung ke daftar tanggal seperti di atas (Saya mengharapkan null dari sisi kanan beberapa gabungan ini untuk beberapa hari dan akan menangani ini ).
mysql
sql
date
gaps-and-islands
Gilgad
sumber
sumber
Jawaban:
Saya akan menggunakan prosedur tersimpan ini untuk menghasilkan interval yang Anda butuhkan ke dalam tabel temp bernama time_intervals , kemudian BERGABUNG dan menggabungkan tabel data Anda dengan tabel interval_waktu temp .
Prosedur dapat menghasilkan interval dari semua tipe berbeda yang Anda lihat ditentukan di dalamnya:
call make_intervals('2009-01-01 00:00:00','2009-01-10 00:00:00',1,'DAY') . select * from time_intervals . interval_start interval_end ------------------- ------------------- 2009-01-01 00:00:00 2009-01-01 23:59:59 2009-01-02 00:00:00 2009-01-02 23:59:59 2009-01-03 00:00:00 2009-01-03 23:59:59 2009-01-04 00:00:00 2009-01-04 23:59:59 2009-01-05 00:00:00 2009-01-05 23:59:59 2009-01-06 00:00:00 2009-01-06 23:59:59 2009-01-07 00:00:00 2009-01-07 23:59:59 2009-01-08 00:00:00 2009-01-08 23:59:59 2009-01-09 00:00:00 2009-01-09 23:59:59 . call make_intervals('2009-01-01 00:00:00','2009-01-01 02:00:00',10,'MINUTE') . select * from time_intervals . interval_start interval_end ------------------- ------------------- 2009-01-01 00:00:00 2009-01-01 00:09:59 2009-01-01 00:10:00 2009-01-01 00:19:59 2009-01-01 00:20:00 2009-01-01 00:29:59 2009-01-01 00:30:00 2009-01-01 00:39:59 2009-01-01 00:40:00 2009-01-01 00:49:59 2009-01-01 00:50:00 2009-01-01 00:59:59 2009-01-01 01:00:00 2009-01-01 01:09:59 2009-01-01 01:10:00 2009-01-01 01:19:59 2009-01-01 01:20:00 2009-01-01 01:29:59 2009-01-01 01:30:00 2009-01-01 01:39:59 2009-01-01 01:40:00 2009-01-01 01:49:59 2009-01-01 01:50:00 2009-01-01 01:59:59 . I specified an interval_start and interval_end so you can aggregate the data timestamps with a "between interval_start and interval_end" type of JOIN. . Code for the proc: . -- drop procedure make_intervals . CREATE PROCEDURE make_intervals(startdate timestamp, enddate timestamp, intval integer, unitval varchar(10)) BEGIN -- ************************************************************************* -- Procedure: make_intervals() -- Author: Ron Savage -- Date: 02/03/2009 -- -- Description: -- This procedure creates a temporary table named time_intervals with the -- interval_start and interval_end fields specifed from the startdate and -- enddate arguments, at intervals of intval (unitval) size. -- ************************************************************************* declare thisDate timestamp; declare nextDate timestamp; set thisDate = startdate; -- ************************************************************************* -- Drop / create the temp table -- ************************************************************************* drop temporary table if exists time_intervals; create temporary table if not exists time_intervals ( interval_start timestamp, interval_end timestamp ); -- ************************************************************************* -- Loop through the startdate adding each intval interval until enddate -- ************************************************************************* repeat select case unitval when 'MICROSECOND' then timestampadd(MICROSECOND, intval, thisDate) when 'SECOND' then timestampadd(SECOND, intval, thisDate) when 'MINUTE' then timestampadd(MINUTE, intval, thisDate) when 'HOUR' then timestampadd(HOUR, intval, thisDate) when 'DAY' then timestampadd(DAY, intval, thisDate) when 'WEEK' then timestampadd(WEEK, intval, thisDate) when 'MONTH' then timestampadd(MONTH, intval, thisDate) when 'QUARTER' then timestampadd(QUARTER, intval, thisDate) when 'YEAR' then timestampadd(YEAR, intval, thisDate) end into nextDate; insert into time_intervals select thisDate, timestampadd(MICROSECOND, -1, nextDate); set thisDate = nextDate; until thisDate >= enddate end repeat; END;
Contoh skenario data serupa di bagian bawah posting ini , di mana saya membangun fungsi serupa untuk SQL Server.
sumber
Untuk MSSQL Anda dapat menggunakan ini. Ini SANGAT cepat.
Anda dapat membungkus ini dalam fungsi nilai tabel atau proc dan parse yang disimpan di tanggal mulai dan akhir sebagai variabel.
DECLARE @startDate DATETIME DECLARE @endDate DATETIME SET @startDate = '2011-01-01' SET @endDate = '2011-01-31'; WITH dates(Date) AS ( SELECT @startdate as Date UNION ALL SELECT DATEADD(d,1,[Date]) FROM dates WHERE DATE < @enddate ) SELECT Date FROM dates OPTION (MAXRECURSION 0) GO
sumber
Kami memiliki masalah yang sama dengan laporan BIRT yang ingin kami laporkan pada hari-hari yang tidak memiliki data. Karena tidak ada entri untuk tanggal tersebut, solusi termudah bagi kami adalah membuat tabel sederhana yang menyimpan semua tanggal dan menggunakannya untuk mendapatkan rentang atau bergabung untuk mendapatkan nilai nol untuk tanggal tersebut.
Kami memiliki tugas yang dijalankan setiap bulan untuk memastikan bahwa tabel diisi 5 tahun ke depan. Tabel dibuat sebagai berikut:
create table all_dates ( dt date primary key );
Tidak diragukan lagi ada cara ajaib yang rumit untuk melakukan ini dengan DBMS berbeda, tetapi kami selalu memilih solusi yang paling sederhana. Persyaratan penyimpanan untuk tabel tersebut minimal dan membuat kueri jadi lebih sederhana dan portabel. Solusi semacam ini hampir selalu lebih baik dari sudut pandang kinerja karena tidak memerlukan penghitungan per baris pada data.
Opsi lain (dan kami telah menggunakan ini sebelumnya) adalah memastikan ada entri di tabel untuk setiap tanggal. Kami menyapu tabel secara berkala dan menambahkan nol entri untuk tanggal dan / atau waktu yang tidak ada. Ini mungkin bukan pilihan dalam kasus Anda, itu tergantung pada data yang disimpan.
Jika Anda benar-benar berpikir itu merepotkan untuk menjaga
all_dates
tabel terisi, prosedur tersimpan adalah cara yang akan mengembalikan dataset yang berisi tanggal-tanggal tersebut. Ini hampir pasti akan lebih lambat karena Anda harus menghitung rentang setiap kali dipanggil daripada hanya menarik data yang telah dihitung sebelumnya dari tabel.Tapi, sejujurnya, Anda dapat mengisi tabel selama 1000 tahun tanpa masalah penyimpanan data yang serius - 365.000 16-byte (misalnya) tanggal ditambah indeks yang menggandakan tanggal ditambah 20% overhead untuk keamanan, saya kira-kira akan memperkirakan di sekitar 14M [365.000 * 16 * 2 * 1.2 = 14.016.000 byte]), tabel yang sangat kecil dalam skema hal.
sumber
Anda dapat menggunakan variabel pengguna MySQL seperti ini:
SET @num = -1; SELECT DATE_ADD( '2009-01-01', interval @num := @num+1 day) AS date_sequence, your_table.* FROM your_table WHERE your_table.other_column IS NOT NULL HAVING DATE_ADD('2009-01-01', interval @num day) <= '2009-01-13'
@num adalah -1 karena Anda menambahkannya saat pertama kali Anda menggunakannya. Selain itu, Anda tidak dapat menggunakan "HAVING date_sequence" karena itu membuat variabel pengguna bertambah dua kali untuk setiap baris.
sumber
Meminjam ide dari jawaban ini , Anda dapat membuat tabel dengan 0 hingga 9 dan menggunakannya untuk menghasilkan daftar tanggal.
CREATE TABLE num (i int); INSERT INTO num (i) VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9); select adddate('2009-01-01', numlist.id) as `date` from (SELECT n1.i + n10.i*10 + n100.i*100 AS id FROM num n1 cross join num as n10 cross join num as n100) as numlist where adddate('2009-01-01', numlist.id) <= '2009-01-13';
Ini akan memungkinkan Anda membuat daftar hingga 1000 tanggal. Jika Anda perlu memperbesar, Anda bisa menambahkan gabungan silang lain ke kueri dalam.
sumber
date
; TIDAK menghasilkan bagian itu.Untuk Access (atau bahasa SQL apa pun)
Buat satu tabel yang memiliki 2 field, kita akan memanggil tabel ini
tempRunDates
:--Fields
fromDate
dantoDate
--Lalu masukkan hanya 1 record, yang memiliki tanggal mulai dan tanggal akhir.
Buat tabel lain:
Time_Day_Ref
--Impor daftar tanggal (buat daftar di excel itu mudah) ke dalam tabel ini.
--Nama bidang dalam kasus saya adalah
Greg_Dt
, untuk TanggalMasehi --Saya membuat daftar saya dari 1 Januari 2009 hingga 1 Januari 2020.
Jalankan kueri:
SELECT Time_Day_Ref.GREG_DT FROM tempRunDates, Time_Day_Ref WHERE Time_Day_Ref.greg_dt>=tempRunDates.fromDate And greg_dt<=tempRunDates.toDate;
Mudah!
sumber
Biasanya seseorang akan menggunakan tabel bilangan tambahan yang biasanya Anda simpan hanya untuk tujuan ini dengan beberapa variasi pada ini:
SELECT * FROM ( SELECT DATEADD(d, number - 1, '2009-01-01') AS dt FROM Numbers WHERE number BETWEEN 1 AND DATEDIFF(d, '2009-01-01', '2009-01-13') + 1 ) AS DateRange LEFT JOIN YourStuff ON DateRange.dt = YourStuff.DateColumn
Saya telah melihat variasi dengan fungsi nilai tabel, dll.
Anda juga dapat menyimpan daftar tanggal permanen. Kami memilikinya di gudang data kami serta daftar waktu dalam sehari.
sumber
Nah bagaimana menemukan tanggal antara dua tanggal tertentu di SQL server dijelaskan di http://ektaraval.blogspot.com/2010/09/writing-recursive-query-to-find-out-all.html
sumber
CREATE FUNCTION [dbo].[_DATES] ( @startDate DATETIME, @endDate DATETIME ) RETURNS @DATES TABLE( DATE1 DATETIME ) AS BEGIN WHILE @startDate <= @endDate BEGIN INSERT INTO @DATES (DATE1) SELECT @startDate SELECT @startDate = DATEADD(d,1,@startDate) END RETURN END
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'.
sumber
Kami menggunakan ini di Sistem HRMS kami, Anda akan merasa berguna
SELECT CAST(DAYNAME(daydate) as CHAR) as dayname,daydate FROM (select CAST((date_add('20110101', interval H.i*100 + T.i*10 + U.i day) )as DATE) as daydate from erp_integers as H cross join erp_integers as T cross join erp_integers as U where date_add('20110101', interval H.i*100 + T.i*10 + U.i day ) <= '20110228' order by daydate ASC )Days
sumber
Solusi ini bekerja dengan MySQL 5.0
Buat tabel -
mytable
.Skema tidak material. Yang penting adalah jumlah baris di dalamnya.
Jadi, Anda hanya dapat menyimpan satu kolom tipe INT dengan 10 baris, nilai - 1 hingga 10.
SQL:
set @tempDate=date('2011-07-01') - interval 1 day; select date(@tempDate := (date(@tempDate) + interval 1 day)) as theDate from mytable x,mytable y group by theDate having theDate <= '2011-07-31';
Batasan: Jumlah tanggal maksimum yang dikembalikan oleh kueri di atas adalah
(rows in mytable)*(rows in mytable) = 10*10 = 100.
Anda dapat meningkatkan rentang ini dengan mengubah bagian formulir di sql:
dari mytable x, mytable y, mytable z
Jadi, range be
10*10*10 =1000
dan seterusnya.sumber
Buat prosedur tersimpan yang membutuhkan dua parameter a_begin dan a_end. Buat tabel sementara di dalamnya bernama t, deklarasikan variabel d, tetapkan a_begin ke d, dan jalankan
WHILE
perulanganINSERT
ke d ke t dan panggilADDDATE
fungsi untuk menaikkan nilai d. AkhirnyaSELECT * FROM t
.sumber
Saya akan menggunakan sesuatu yang mirip dengan ini:
DECLARE @DATEFROM AS DATETIME DECLARE @DATETO AS DATETIME DECLARE @HOLDER TABLE(DATE DATETIME) SET @DATEFROM = '2010-08-10' SET @DATETO = '2010-09-11' INSERT INTO @HOLDER (DATE) VALUES (@DATEFROM) WHILE @DATEFROM < @DATETO BEGIN SELECT @DATEFROM = DATEADD(D, 1, @DATEFROM) INSERT INTO @HOLDER (DATE) VALUES (@DATEFROM) END SELECT DATE FROM @HOLDER
Kemudian
@HOLDER
tabel Variabel menyimpan semua tanggal yang bertambah hari antara dua tanggal tersebut, siap untuk digabungkan sesuka hati Anda.sumber
Saya telah bertengkar dengan ini cukup lama. Karena ini adalah hit pertama di Google ketika saya mencari solusinya, izinkan saya memposting di mana saya mendapatkan sejauh ini.
SET @d := '2011-09-01'; SELECT @d AS d, cast( @d := DATE_ADD( @d , INTERVAL 1 DAY ) AS DATE ) AS new_d FROM [yourTable] WHERE @d <= '2012-05-01';
Ganti
[yourTable]
dengan tabel dari database Anda. Caranya adalah jumlah baris dalam tabel yang Anda pilih harus> = jumlah tanggal yang ingin Anda kembalikan. Saya mencoba menggunakan placeholder tabel DUAL, tetapi hanya akan mengembalikan satu baris.sumber
select * from table_name where col_Date between '2011/02/25' AND DATEADD(s,-1,DATEADD(d,1,'2011/02/27'))
Di sini, pertama-tama tambahkan hari ke tanggal akhir saat ini, yaitu 2011-02-28 00:00:00, lalu Anda kurangi satu detik untuk membuat tanggal akhir 2011-02-27 23:59:59. Dengan melakukan ini, Anda bisa mendapatkan semua tanggal di antara interval yang diberikan.
keluaran:
2011/02/25
2011/02/26
2011/02/27
sumber
DELIMITER $$ CREATE PROCEDURE popula_calendario_controle() BEGIN DECLARE a INT Default 0; DECLARE first_day_of_year DATE; set first_day_of_year = CONCAT(DATE_FORMAT(curdate(),'%Y'),'-01-01'); one_by_one: LOOP IF dayofweek(adddate(first_day_of_year,a)) <> 1 THEN INSERT INTO calendario.controle VALUES(null,150,adddate(first_day_of_year,a),adddate(first_day_of_year,a),1); END IF; SET a=a+1; IF a=365 THEN LEAVE one_by_one; END IF; END LOOP one_by_one; END $$
prosedur ini akan memasukkan semua tanggal dari awal tahun sampai sekarang, cukup ganti hari "awal" dan "akhir", dan Anda siap untuk pergi!
sumber
IF a=365 THEN
dipengaruhi oleh tahun kabisat?150
pada baris 9? Kode ini adalah "jawaban tepuk" tanpa banyak penjelasan yang sebenarnya.Saya membutuhkan daftar dengan semua bulan antara 2 tanggal untuk statistik. 2 tanggal tersebut adalah tanggal mulai dan tanggal akhir dari langganan. Jadi daftar tersebut menunjukkan semua bulan dan jumlah langganan per bulan.
MYSQL
CREATE PROCEDURE `get_amount_subscription_per_month`() BEGIN -- Select the ultimate start and enddate from subscribers select @startdate := min(DATE_FORMAT(a.startdate, "%Y-%m-01")), @enddate := max(DATE_FORMAT(a.enddate, "%Y-%m-01")) + interval 1 MONTH from subscription a; -- Tmp table with all months (dates), you can always format them with DATE_FORMAT) DROP TABLE IF EXISTS tmp_months; create temporary table tmp_months ( year_month date, PRIMARY KEY (year_month) ); set @tempDate=@startdate; #- interval 1 MONTH; -- Insert every month in tmp table WHILE @tempDate <= @enddate DO insert into tmp_months (year_month) values (@tempDate); set @tempDate = (date(@tempDate) + interval 1 MONTH); END WHILE; -- All months select year_month from tmp_months; -- If you want the amount of subscription per month else leave it out select mnd.year_month, sum(subscription.amount) as subscription_amount from tmp_months mnd LEFT JOIN subscription ON mnd.year_month >= DATE_FORMAT(subscription.startdate, "%Y-%m-01") and mnd.year_month <= DATE_FORMAT(subscription.enddate, "%Y-%m-01") GROUP BY mnd.year_month; END
sumber
Anda bisa menggunakan ini
SELECT CAST(cal.date_list AS DATE) day_year FROM ( SELECT SUBDATE('2019-01-01', INTERVAL 1 YEAR) + INTERVAL xc DAY AS date_list FROM ( SELECT @xi:=@xi+1 as xc from (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc1, (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc2, (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc3, (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc4, (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc5, (SELECT @xi:=-1) xc0 ) xxc1 ) cal WHERE cal.date_list BETWEEN '2019-01-01' AND '2019-12-31' ORDER BY cal.date_list DESC;
sumber
saya menggunakan
Server version: 5.7.11-log MySQL Community Server (GPL)
Sekarang kita akan menyelesaikannya dengan cara yang sederhana.
Saya telah membuat tabel bernama "datetable"
mysql> describe datetable; +---------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+---------+------+-----+---------+-------+ | colid | int(11) | NO | PRI | NULL | | | coldate | date | YES | | NULL | | +---------+---------+------+-----+---------+-------+ 2 rows in set (0.00 sec)
sekarang, kita akan melihat catatan yang disisipkan di dalamnya.
mysql> select * from datetable; +-------+------------+ | colid | coldate | +-------+------------+ | 101 | 2015-01-01 | | 102 | 2015-05-01 | | 103 | 2016-01-01 | +-------+------------+ 3 rows in set (0.00 sec)
dan di sini kueri kami untuk mengambil rekaman dalam dua tanggal, bukan tanggal tersebut.
mysql> select * from datetable where coldate > '2015-01-01' and coldate < '2016-01-01'; +-------+------------+ | colid | coldate | +-------+------------+ | 102 | 2015-05-01 | +-------+------------+ 1 row in set (0.00 sec)
berharap ini akan membantu banyak orang.
sumber