147

次のようなクエリを実行したい

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

そして、次のようなデータを返します。

日々
----------
2010-01-20
2010-01-21
2010-01-22
2010-01-23
2010-01-24
4

29 に答える 29

343

このソリューションでは、ループ、プロシージャ、一時テーブルは使用されません。サブクエリは過去 10,000 日間の日付を生成し、必要に応じて前後に拡張できます。

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' 

出力:

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

性能上の注意

ここでテストすると、パフォーマンスは驚くほど良好です。上記のクエリは 0.0009 秒かかります。

サブクエリを拡張して約を生成するとします。100,000 の数値 (したがって、約 274 年分の日付) を 0.0458 秒で実行します。

ちなみに、これは非常に移植性の高い手法であり、ほとんどのデータベースでわずかな調整を行うだけで機能します。

1,000日を返すSQL Fiddleの例

于 2010-01-28T20:38:26.773 に答える
34

ビューを使用した別のバリエーションを次に示します。

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;

そして、あなたは簡単に行うことができます (それがどれほどエレガントか分かりますか?):

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

アップデート

現在の日付から始まる過去の日付のみを生成できることに注意してください。任意の種類の日付範囲 (過去、未来、およびその間) を生成する場合は、代わりに次のビューを使用する必要があります。

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;
于 2012-06-21T16:36:53.370 に答える
29

受け入れられた回答は PostgreSQL では機能しませんでした (「a」またはその付近の構文エラー)。

PostgreSQL でこれを行う方法は、generate_series関数を使用することです。

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)
于 2015-07-05T11:36:25.807 に答える
17

再帰的な Common Table Expression (CTE) を使用して、日付のリストを生成し、そこから選択できます。明らかに、通常は 300 万の日付を作成したくないため、これはその可能性を示しています。CTE 内の日付範囲を単純に制限し、CTE を使用して select ステートメントから where 句を省略できます。

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 では、考えられるすべての日付の CTE リストを生成するのに 1:08 かかりました。100 年を生成するのに 1 秒もかかりませんでした。

于 2013-08-14T00:41:32.413 に答える
7

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

出力

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
于 2014-09-28T08:41:15.013 に答える
4

ループ/カーソルなしでこれを行うための古い学校の解決策はNUMBERS、値が 1 から始まる単一の整数列を持つテーブルを作成することです。

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

ニーズを満たすのに十分な数のレコードをテーブルに入力する必要があります。

INSERT INTO NUMBERS (id) VALUES (NULL);

テーブルを取得したらNUMBERS、次を使用できます。

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'

絶対的なローテク ソリューションは次のようになります。

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

何に使うの?


LEFT JOIN するために、日付または数値のリストを生成します。シーケンシャル データのリストに LEFT JOIN しているため、データのどこにギャップがあるかを確認するためにこれを行います。null 値により、ギャップが存在する場所が明らかになります。

于 2010-01-28T19:51:00.920 に答える
3

手順 + 一時テーブル:

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
于 2014-06-12T15:32:16.890 に答える
2

すでに与えられた素晴らしい回答の多くで述べられている (または少なくともほのめかされている) ように、この問題は、作業する一連の数値があれば簡単に解決できます。

注:以下は T-SQL ですが、これは、ここおよびインターネット全体で既に言及されている一般的な概念を単に私が特定して実装したものです。コードを選択した方言に変換するのは比較的簡単です。

どのように?次のクエリを検討してください。

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

上記は日付範囲 1/22/0001 から 1/27/0001 を生成し、非常に簡単です。上記のクエリには、 の開始日0001-01-22オフセットという 2 つの重要な情報があります5。これら 2 つの情報を組み合わせると、明らかに終了日が分かります。したがって、2 つの日付が与えられた場合、範囲の生成は次のように分類できます。

  • 与えられた 2 つの日付の差 (オフセット) を簡単に見つけます。

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

    ここを使用ABS()すると、日付の順序が無関係になります。

  • 限られた数のセットを生成します。これも簡単です。

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

    ここで何を選択しているかは実際には気にしないことに注意してくださいFROM。その中の行数をカウントするために、作業するセットが必要なだけです。私は個人的に TVF を使用していますが、CTE を使用する人もいれば、代わりに数値表を使用する人もいます。あなたも理解している最もパフォーマンスの高いソリューションを使用することをお勧めします。

これら 2 つの方法を組み合わせると、問題が解決します。

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

上記の例はひどいコードですが、すべてがどのように組み合わされるかを示しています。

もっと楽しく

この種のことを何度も行う必要があるため、ロジックを 2 つの TVF にカプセル化しました。1 つ目は数値の範囲を生成し、2 つ目はこの機能を使用して日付の範囲を生成します。計算は、入力順序が重要ではないことを確認するためのものであり、GenerateRangeSmallInt.

次の関数は、最大範囲 65536 の日付を返すのに約 16 ミリ秒の CPU 時間を必要とします。

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
);
于 2014-08-23T02:21:34.413 に答える
2

これを試して。

SELECT TO_DATE('20160210','yyyymmdd') - 1 + LEVEL AS start_day 
from DUAL
connect by level <= (TO_DATE('20160228','yyyymmdd') + 1) - TO_DATE('20160210','yyyymmdd') ;
于 2016-07-28T09:16:45.740 に答える
1

受け入れられた回答よりも短い、同じ考え:

(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')
于 2016-01-21T09:23:39.953 に答える
1

数日以上必要な場合は、テーブルが必要です。

mysql で日付範囲を作成する

それから、

select from days.day, count(mytable.field) as fields from days left join mytable on day=date where date between x and y;
于 2010-01-29T13:33:03.960 に答える
1

2 つの日付フィールド間の日付を生成する

SQL CTEクエリを知っている場合、このソリューションは質問を解決するのに役立ちます

ここに例があります

1 つのテーブルに日付があります

テーブル名: 「testdate」</p>

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

結果が必要:

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

解決:

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

説明: CTE 再帰クエリの説明

  • クエリの最初の部分:

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

    説明: 最初の列は「開始日」、2 番目の列は開始日と終了日の差 (日数) であり、「差」列と見なされます。

  • クエリの 2 番目の部分:

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

    説明: Union all は、結果が null になるまで上記のクエリの結果を継承します。したがって、「StartTime」の結果は、生成された CTE クエリから継承され、diff から - 1 減少するため、3、2、および 1 のように見えます。 0まで

例えば

STARTDATE   DIFF
10/24/2012  0
10/27/2012  0
10/27/2012  1
10/27/2012  2
10/30/2012  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
  • クエリの 3 番目の部分

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

    「startdate」に日「diff」が追加されるため、結果は次のようになります。

結果

STARTDATE
10/24/2012
10/27/2012
10/28/2012
10/29/2012
10/30/2012
于 2012-10-27T08:52:33.210 に答える
0

2 つの日付の間の日付のリストが必要な場合:

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

* ここでフィドル: http://sqlfiddle.com/#!6/9eecb/3469

于 2015-04-08T17:50:55.193 に答える
0

Oracle の場合、私の解決策は次のとおりです。

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

Sysdate は特定の日付に変更でき、レベル番号を変更してより多くの日付を指定できます。

于 2013-10-29T16:57:29.847 に答える
0

RedFilters トップ ソリューションのSQLiteバージョン

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
于 2016-06-20T08:55:13.590 に答える
0

わかりました..これを試してください: 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

それを使用して、たとえば、一時テーブルを生成し、一時テーブルで select * を実行します。または、結果を 1 つずつ出力します。
あなたがやりたいと言っていることは、SELECT ステートメントでは実行できませんが、MySQL 固有のことで実行できる可能性があります。
もう一度、カーソルが必要かもしれません: http://dev.mysql.com/doc/refman/5.0/en/cursors.html

于 2010-01-28T19:38:31.210 に答える
0

powerpivot日付テーブル用 のカスタム休日テーブル 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)
于 2016-07-14T16:39:10.157 に答える
0

日とは異なるtimestmap でカレンダー テーブルを作成するプロシージャも作成できます。 四半期ごとのテーブルが必要な場合

例えば

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

あなたが使用することができます

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


操作してから

select ts, dt from calendar_table_tmp;

それはあなたにもtsを与えます

'1548143100', '2019-01-22 08:45:00'
'1548144000', '2019-01-22 09:00:00'
'1548144900', '2019-01-22 09:15:00'
'1548145800', '2019-01-22 09:30:00'
'1548146700', '2019-01-22 09:45:00'
'1548147600', '2019-01-22 10:00:00'

ここから、次のような他の情報の追加を開始できます。

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

または create table ステートメントで実際のテーブルを作成します

于 2019-02-19T20:53:01.347 に答える