tarih aralığından günler oluştur


135

Gibi bir sorgu çalıştırmak istiyorum

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

Ve aşağıdaki gibi verileri döndürün:

günler
----------
2010-01-20
2010-01-21
2010-01-22
2010-01-23
2010-01-24

10
Bu soruya eklenmiş başka bir sorun yok. Yukarıdaki soru, SQL kurslarına hakim olma problemidir.
Pentium10

Seçilen bir tarih aralığına göre bir tarih dizisine mi ihtiyacınız var?
Derek Adair

1
Sana bir sorun bulmak için bir kullanım düşünüyorum ... Tablonuzda bazı eksik kayıtları doldurmak için bir görev alırsanız. Ve ben her gün için bir sorgu çalıştırmak zorunda gibi bir şey düşünüyoruminsert into table select ... as days date between '' and ''
Pentium10

13
Kullanımının bir örneği, istatistik oluşturmak ve üzerinde veri bulunmadığı tarihler için bir satır eklemek olabilir. Bir çeşit gruplama yapıyorsanız, verilerinizi SQL'e olduğu gibi dökmek yerine, SQL'deki tüm bilgileri gerçekten oluşturmak ve ihtiyacınız olan herhangi bir formata eklemek çok daha hızlı olabilir. boşaltır.
Nanne

1
@Nanne bu soruyu tam olarak bu yüzden sakladım. Yukarıda belirli tarihler için mevcut olmayabilir veriye katılmak için yukarıdaki gerekir.
Josh Diehl

Yanıtlar:


318

Bu çözüm hiçbir döngü, yordam veya geçici tablo kullanmaz . Alt sorgu son 10.000 gün için tarih oluşturur ve istediğiniz kadar ileri veya geri gitmek için genişletilebilir.

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' 

Çıktı:

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

Performansla İlgili Notlar

Burada test , performans şaşırtıcı derecede iyi: yukarıdaki sorgu 0.0009 saniye sürer.

Alt sorguyu yakl. 100.000 sayı (ve dolayısıyla yaklaşık 274 yıl değerinde), 0.0458 saniyede çalışır.

Bu arada, bu, küçük ayarlarla çoğu veritabanıyla çalışan çok taşınabilir bir tekniktir.

1.000 gün döndüren SQL Fiddle örneği


6
Eğer değiştirirseniz daha iyi performans göreceksiniz UNIONiçin UNION ALLdeğil var olduğunu kaldırmak Çift kayıtların kontrolünü 's kaybı zaman -. Yine de çok karmaşık IMO - eğer UNION'ları kullanarak bir sonuç kümesi oluşturacaksanız, neden sadece tarihi belirtip bununla yapılmayasınız?
OMG Ponies

7
neden sadece tarihi belirtip bununla birlikte yapılmayacak - çünkü yukarıdaki yöntem, tablo oluşturma gerektirmeyen keyfi olarak büyük sayılar (ve tarihler) oluşturmanıza izin veriyor, bu da önerdiğiniz şekilde zor kodlamak için acı verici olacaktır. Açıkçası 5 tarih için aşırıya kaçmış; ancak o zaman bile, tarihleri ​​önceden bilmediğiniz bir tabloya katılıyorsanız, sadece potansiyel min ve maks değerleri, mantıklıdır.
Ocak'ta RedFilter

2
DATETIME işlevini daha önce oluşturduğunuz UNION ifadesinin yerine kullanmak "acı verici" mi? Bu eklemek zorunda mantık için herhangi ihtiyacı azaltan bir . Bu nedenle , sorguyu aşırı karmaşık hale getirdiniz . UNION ifadesi her iki şekilde de ölçeklenebilir değil - bir tarih veya sayı belirterek, bunu 20 veya 30 tarih söyleyecek şekilde güncellemek isteyen var mı?
OMG Ponies

23
Sorunun bir cevabını görmek gerçekten güzel, nasıl yapılamayacağı veya yapılmaması gerektiğine dair sonsuz yorumlar değil. Çoğu şey yapılabilir ve "gerekir" sadece bağlam açısından anlamlıdır, bu herkes için farklılık gösterir. Çoğu durumda daha iyi yollar olduğunu bilsem de, bu cevap bana yardımcı oldu.
joe

7
Bu sorguyu işe yaramayanlar: Lütfen kendinizi yüzünüze tokatlayın ve OP'nin 1000 sorgu üreten bu sorgu hakkındaki yorumunu tekrar okuyun. 2010 yılı 1000 günden fazla olduğu için sorguyu buna göre ayarlamanız gerekir.
Noel Baron

32

Görünümleri kullanan başka bir varyasyon:

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;

Ve sonra sadece yapabilirsiniz (ne kadar zarif olduğunu görüyor musunuz?):

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

Güncelleme

Yalnızca geçerli tarihten başlayarak geçmiş tarihler oluşturabileceğinizi belirtmek gerekir . Herhangi bir tarih aralığı (geçmiş, gelecek ve arada) oluşturmak istiyorsanız, bunun yerine bu görünümü kullanmanız gerekir:

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
Bu her durumda çalışmaz. '2014-12-01' VE '2014-12-28' SİPARİŞ
TARİHİNDEN TARİH

3
İyi arama @ user927258. Bunun nedeni, datesyukarıda belirtilen ilk görünümün geçerli tarihten başlayarak tarihleri ​​hesaplamasıdır, bu nedenle gelecekte ayarlanan tarihleri ​​alamazsınız. @RedFilter yanıt aynı tasarım kusuru yaşıyor. Yine de cevabımda bir geçici çözüm ekledim.
Stéphane

Bazı görünümleri kullanmak sorguları kesinlikle basitleştirir ve yeniden kullanılabilir hale getirir. Aslında aynı şeyi yapıyor olmalarına rağmen, tüm bu UNIONmaddeler tek bir SQL ifadesinde garip görünüyor.
Stewart

24

Kabul edilen cevap PostgreSQL için işe yaramadı ("a" ya da yakınında sözdizimi hatası).

PostgreSQL'de bunu yapmanın yolu generate_seriesfonksiyonu kullanmaktır , yani:

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

Özyinelemeli Ortak Tablo İfadesi (CTE) kullanarak bir tarih listesi oluşturabilir ve ardından bu listeden seçim yapabilirsiniz. Açıkçası normalde üç milyon tarih oluşturmak istemezsiniz, bu yüzden bu olasılıkları gösterir. CTE içindeki tarih aralığını sınırlayabilir ve CTE'yi kullanarak select deyimindeki where yan tümcesini atlayabilirsiniz.

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

Microsoft SQL Server 2005'te, tüm olası tarihlerin CTE listesini oluşturmak 1:08 sürdü. Yüz yıl üretmek bir saniyeden az sürdü.


7

MSSQL Sorgusu

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

Çıktı

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
Sadece biraz daha aşağı kaydırsaydım ... iç çek. Her neyse, teşekkür ederim. Sürümümdeki zamanı kaldırmak için bir CAST (<ifade> AS DATE) ekledim. Ayrıca, GETDATE () - 365 VE GETDATE () arasında a.Date kullanılır. Sorgunuzu bugün çalıştırırsanız, WHERE = P
Ricardo C

4

Bunu bir döngü / imleç olmadan yapmak için eski okul çözümü NUMBERS, 1'den başlayan değerlere sahip tek bir Tamsayı sütunu olan bir tablo oluşturmaktır .

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

Tabloyu ihtiyaçlarınızı karşılayacak kadar kayıtla doldurmanız gerekir:

INSERT INTO NUMBERS (id) VALUES (NULL);

NUMBERSMasayı aldıktan sonra şunları kullanabilirsiniz:

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'

Mutlak düşük teknoloji çözümü:

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

Ne için kullanırdın?


Tarihine kadar LEFT JOIN için tarih veya sayı listeleri oluşturmak. Verilerdeki boşlukların nerede olduğunu görmek için bunu yaparsınız, çünkü sıralı veriler listesine LEFT KATILIRSINIZ - null değerler boşlukların bulunduğu yeri belirginleştirecektir.


1
DUALTablo bir stand-tabloda olarak kullanım için, Oracle ve MySQL ile desteklenir FROMmadde. Mevcut değil, değer seçildiğinde değer ne olursa olsun döndürülür. Bir SELECT sorgusu FROMen az bir tablo belirten bir cümle gerektirdiğinden, buradaki fikir öne çıkmaktı.
OMG Ponies

1
Sorguya her ihtiyacınız olduğunda RDBMS'yi oluşturmak yerine kalıcı bir sayılar tablosu oluşturmak için +1. Yardımcı masalar kötü değil, insanlar!
Bacon Bits

4

Access 2010 için - birden çok adım gereklidir; Yukarıda yayınlananla aynı modeli izledim, ancak Access'te birine yardım edebileceğimi düşündüm. Benim için harika çalıştı, tohumlanmış bir tarih tablosu tutmak zorunda değildim.

DUAL adlı bir tablo oluşturun (Oracle DUAL tablosunun çalışma şekline benzer)

  • Kimlik (Otomatik Sayı)
  • DummyColumn (Metin)
  • Bir satır değeri ekleyin (1, "DummyRow")

"ZeroThru9Q" adlı bir sorgu oluşturun; aşağıdaki sözdizimini manuel olarak girin:

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

"TodayMinus1KQ" adlı bir sorgu oluşturun (bugünden önceki tarihler için); aşağıdaki sözdizimini manuel olarak girin:

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

  (SELECT *
   FROM ZeroThru9Q) AS b,

  (SELECT *
   FROM ZeroThru9Q) AS c

"TodayPlus1KQ" adlı bir sorgu oluşturun (bugünden sonraki tarihler için); aşağıdaki sözdizimini manuel olarak girin:

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

  (SELECT *
   FROM ZeroThru9Q) AS b,

  (SELECT *
   FROM ZeroThru9Q) AS c;

"TodayPlusMinus1KQ" adlı bir birleşim sorgusu oluşturun (tarihler +/- 1000 gün için):

SELECT MyDate
FROM TodayMinus1KQ
UNION
SELECT MyDate
FROM TodayPlus1KQ;

Şimdi sorguyu kullanabilirsiniz:

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

3

Prosedür + geçici tablo:

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 - bana stackoverflow katılmak :) - bu benim msaccess için benim port - herhangi bir sürüm üzerinde çalışacağını düşünüyorum:

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

başvurulan MSysObjects sadece 'neden bir tablo yan tümcesinde en az 1 kayıt saymak bir tabloya gerek - en az 1 kayıt ile herhangi bir tablo yapardı.


2

Daha önce verilen harika cevapların çoğunda belirtildiği (veya en azından belirtildiği gibi), çalışmak için bir dizi sayınız olduğunda bu sorun kolayca çözülür.

Not: Aşağıdakiler T-SQL'dir, ancak burada ve genel olarak internette bahsettiğim genel kavramları özel olarak benim uygulamam. Kodu seçtiğiniz lehçenize dönüştürmek nispeten basit olmalıdır.

Nasıl? Bu sorguyu düşünün:

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

Yukarıdaki tarih 1/22/0001 - 1/27/0001 tarih aralığını üretir ve son derece önemsizdir. : Yukarıdaki sorguda bilgilerin 2 anahtar parçaları vardır başlangıç tarihi arasında 0001-01-22ve ofset arasında 5. Eğer bu iki bilgiyi birleştirirsek, son tarihimiz açıktır. Böylece, iki tarih verildiğinde, bir aralık oluşturmak şu şekilde parçalanabilir:

  • Verilen iki tarih (ofset) arasındaki farkı bulun, kolay:

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

    ABS()Burada kullanılması tarih sırasının alakasız olmasını sağlar.

  • Sınırlı bir sayı kümesi oluşturun, ayrıca kolay:

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

    FROMBurada ne seçtiğimiz umrumda değil . Çalışmak için bir sete ihtiyacımız var, böylece içindeki satır sayısını sayıyoruz. Şahsen bir TVF kullanıyorum, bazıları CTE kullanıyor, diğerleri bunun yerine bir sayı tablosu kullanıyor, fikri anlıyorsunuz. Ben de sizin anladığınız en performanslı çözümü kullanmayı savunuyorum.

Bu iki yöntemi birleştirmek sorunumuzu çözecektir:

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

Yukarıdaki örnek korkunç bir koddur, ancak her şeyin nasıl bir araya geldiğini gösterir.

Daha fazla eğlence

Bu tür şeyleri çok yapmam gerekiyor, bu yüzden mantığı iki TVF'ye kapsülledim. Birincisi bir dizi sayı üretir ve ikincisi bu işlevselliği bir tarih aralığı oluşturmak için kullanır. Matematik, giriş sırasının önemli olmadığından emin olmak ve mevcut sayıların tamamını kullanmak istediğimden emin olmaktır GenerateRangeSmallInt.

Aşağıdaki işlev, maksimum 65536 tarih aralığını döndürmek için ~ 16ms CPU zaman alır.

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

bunu dene.

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

Bir tarih aralığı almak istiyorsunuz.

Örneğinizde, '2010-01-20' ve '2010-01-24' arasındaki tarihleri ​​almak istiyorsunuz

olası çözüm:

 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'

açıklama

MySQL'in date_add işlevi vardır

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

sana vereceğim

2010-01-21

Datediff işlevi bu tekrarlamak zorunda kalacak sık sık bildirmek istiyorum

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

hangisi geri döner

 4

Bir tarih aralığındaki tarihlerin listesini almak bir tamsayı sayı dizisi oluşturmak için kaybolur bkz. MySQL'de bir tamsayı dizisi oluşturma

Burada en çok oylanan cevap, temel olarak https://stackoverflow.com/a/2652051/1497139 ile benzer bir yaklaşım benimsemiştir :

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

sonuçlanacak

row
1.0
2.0
3.0
4.0

Artık satırlar, belirtilen başlangıç ​​tarihinden itibaren bir tarih listesi oluşturmak için kullanılabilir. Başlangıç ​​tarihini eklemek için -1 satırıyla başlıyoruz;

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

birkaç günden daha fazlasına ihtiyacınız olacaksa, bir masaya ihtiyacınız var.

MySQL'de bir tarih aralığı oluşturma

sonra,

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

3
Yukarıdaki cevaba bir tablo gerekmediği ve çözüm sağladığı için bunu neden yayınladınız?
Pentium10

1

İki tarih alanı arasında tarih oluşturma

SQL CTE sorgusunun farkındaysanız, bu çözüm sorunuzu çözmenize yardımcı olacaktır.

İşte örnek

Tek bir masada tarihlerimiz var

Tablo Adı: “testdate”

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

Sonuç İste:

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

Çözüm:

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

Açıklama: CTE Özyinelemeli sorgu açıklaması

  • Sorgunun ilk bölümü:

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

    Açıklama: ilk sütun "başlangıç ​​tarihi", ikinci sütun başlangıç ​​ve bitiş tarihinin gün olarak farkıdır ve "fark" sütunu olarak kabul edilir

  • Sorgunun ikinci kısmı:

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

    Açıklama: Birlik, sonuç geçersiz hale gelinceye kadar yukarıdaki sorgunun sonucunu devralır. Bu nedenle, "StartTime" sonucu oluşturulan CTE sorgusundan miras alınır ve diff, reduce - 1'den, bu nedenle 3, 2 ve 1 gibi görünür 0'a kadar

Örneğin

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

Sonuç Özellikleri

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
  • Sorgunun 3. Bölümü

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

    “Startdate” içine “diff” gününü ekleyeceğinden sonuç aşağıdaki gibi olmalıdır

Sonuç

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

1

Kabul edilen cevaptan daha kısa, aynı fikir:

(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

Bunu kaydedilmiş bir görünüm olarak isteyen herkes için (MySQL, görünümlerde iç içe seçme ifadelerini desteklemez):

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;

Sonra yapabilirsin

select * from date_range

almak

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

1

MariaDB> = 10.3 ve MySQL> = 8.0'da yeni özyinelemeli (Ortak Tablo İfadeleri) işlevselliğini kullanan zarif çözüm.

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;

Yukarıdaki '2019-01-01' ve '2019-04-30' tarihleri ​​arasında bir tarih tablosu döndürür. Aynı zamanda oldukça hızlı. 1000 yıllık tarihi (~ 365.000 gün) döndürmek makinemde yaklaşık 400 ms sürüyor.


1

Bu tarihleri ​​anında oluşturmak iyi bir fikirdir. Ancak, kendimi oldukça geniş bir yelpazede yapmak için kendimi rahat hissetmiyorum, bu yüzden aşağıdaki çözümü buldum:

  1. Tarih hesaplaması için kullanılan sayıları tutacak bir tablo "DatesNumbers" oluşturdu:
CREATE TABLE DatesNumbers (
    i MEDIUMINT NOT NULL,
    PRIMARY KEY (i)
)
COMMENT='Used by Dates view'
;
  1. Tabloyu -59999'dan 40000'e kadar olan sayılarla yukarıdaki teknikleri kullanarak doldurdu. Bu aralık bana 59999 gün (~ 164 yıl) ila 40000 gün (109 yıl) öncesinde tarihler verecektir:
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. Bir görünüm "Tarihler" oluşturuldu:
SELECT
      i,
      CURRENT_DATE() + INTERVAL i DAY AS Date
FROM
    DatesNumbers

Bu kadar.

  • (+) Kolay okunan sorgular
  • (+) Anında numaralar nesil
  • (+) Geçmişte ve gelecekte tarihleri verir ve NO BİRLİĞİ olduğu gibi bunun için görünümde olduğu bu yazı .
  • (+) "Yalnızca geçmişte" veya "yalnızca gelecekte" tarihleri WHERE i < 0veya WHERE i > 0(PK) kullanılarak filtrelenebilir
  • (-) 'geçici' tablo ve görünüm kullanılıyor

0

Tamam .. Şunu deneyin: http://www.devshed.com/c/a/MySQL/Delving-Deeper-into-MySQL-50/
http://dev.mysql.com/doc/refman/5.0/en/ döngü ifadesi.html
http://www.roseindia.net/sql/mysql-example/mysql-loop.shtml

Bunu, örneğin bir geçici tablo oluşturmak için kullanın ve sonra geçici tabloda bir seçim * yapın. Veya sonuçları birer birer çıktılayın.
Yapmak istediğinizi söylediğiniz bir SELECT deyimi ile yapılamaz , ancak MySQL'e özgü şeylerle yapılabilir.
Sonra tekrar, belki imleçlere ihtiyacınız var: http://dev.mysql.com/doc/refman/5.0/en/cursors.html


0

Oracle için benim çözümüm:

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

Sysdate belirli bir tarihe ve seviye numarası daha fazla tarih vermek için değiştirilebilir.


0

iki tarih arasındaki tarihlerin listesini istiyorsanız:

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

* burada keman: http://sqlfiddle.com/#!6/9eecb/3469


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

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

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

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

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

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

END 
$$

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

0

RedFilters üst çözümünün SQLite sürümü

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

hafta içi özel bir tatil masasına katılma ile geliştirildi powerpivot tarih tablosu için microsoft MSSQL 2012 https://gist.github.com/josy1024/cb1487d66d9e0ccbd420bc4a23b6e90e

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

0
WITH
  Digits AS (SELECT 0 D UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9),
  Dates AS (SELECT adddate('1970-01-01',t4.d*10000 + t3.d*1000 + t2.d*100 + t1.d*10 +t0.d) AS date FROM Digits AS t0, Digits AS t1, Digits AS t2, Digits AS t3, Digits AS t4)
SELECT * FROM Dates WHERE date BETWEEN '2017-01-01' AND '2017-12-31'

0

Ayrıca günden farklı bir zaman çizelgesi ile takvim tablosu oluşturmak için bir prosedür oluşturabilir . Her çeyrek için bir masa istiyorsanız

Örneğin

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

kullanabilirsiniz

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

ve sonra

select ts, dt from calendar_table_tmp;

sana ts veren

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

buradan gibi diğer bilgileri eklemeye başlayabilirsiniz.

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

veya create table deyimi ile gerçek bir tablo oluşturma


0

AWS MySQL'de çalışan daha genel bir cevap.

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

Yinelemeli ortak tablo ifadeleri kullanarak mysql 8.0.1 ve mariadb 10.2.2 için bir çözüm daha:

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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.