รับรายการวันที่ระหว่างวันที่สองวัน


91

การใช้ฟังก์ชัน mysql มาตรฐานมีวิธีการเขียนแบบสอบถามที่จะส่งคืนรายการวันระหว่างวันที่สองวัน

เช่นให้ 2009-01-01 และ 2009-01-13 มันจะส่งคืนตารางคอลัมน์เดียวที่มีค่า:

 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

แก้ไข: ดูเหมือนว่าฉันยังไม่ชัดเจน ฉันต้องการสร้างรายการนี้ ฉันมีค่าที่เก็บไว้ในฐานข้อมูล (ตามวันที่และเวลา) แต่ต้องการให้รวมในการรวมภายนอกด้านซ้ายไปยังรายการวันที่ตามด้านบน (ฉันคาดว่าค่าว่างจากด้านขวาของการรวมนี้บางวันและจะจัดการสิ่งนี้ ).


2
ฉันคิดว่าทางออกที่ดีที่สุดอธิบายไว้ในคำตอบstackoverflow.com/a/2157776/466677
Marek Gregor

คำตอบ:


68

ผมจะใช้ขั้นตอนการเก็บนี้เพื่อสร้างช่วงเวลาที่คุณต้องการลงในตาราง temp ชื่อtime_intervalsแล้วเข้าร่วมและรวมตารางข้อมูลของคุณด้วยชั่วคราวtime_intervalsตาราง

โพรซีเดอร์สามารถสร้างช่วงเวลาของประเภทต่างๆทั้งหมดที่คุณเห็นระบุไว้:

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;

ตัวอย่างสถานการณ์ข้อมูลที่คล้ายกันที่ด้านล่างของโพสต์นี้ซึ่งฉันสร้างฟังก์ชันที่คล้ายกันสำหรับ SQL Server


4
โดยส่วนตัวฉันหวังว่าจะมีบางอย่างที่คล้ายกับลำดับที่สร้างขึ้นใน PostgreSQL
Phillip Whelan

หากคุณสร้างข้อมูลเพียงครั้งเดียวคุณสามารถใช้ตารางถาวรและใช้สคริปต์นี้เพื่อเติมวันที่ที่ขาดหายไป
Bimal Poudel

ฉันต้องเปลี่ยนตัวคั่นก่อนคำสั่งสร้างขั้นตอนเพื่อให้มันทำงานผ่าน phpMyAdmin (เพื่อหลีกเลี่ยงข้อผิดพลาดทางไวยากรณ์ในการประกาศคำสั่ง) [code] DECLARE // [/ code]
Defkon1

โซลูชันนี้มีประสิทธิภาพและยืดหยุ่นมากโชคดีที่ mariaDB ฉันได้รับข้อผิดพลาด # 1067 - ค่าเริ่มต้นไม่ถูกต้องสำหรับ 'interval_end' เมื่อเรียกใช้การโทร make_intervals ('2009-01-01 00:00:00', '2009-01-10 00: 00: 00 ', 1,' วัน '); ฉันใช้เวอร์ชัน 5.7.28
shelbypereira

28

สำหรับ MSSQL คุณสามารถใช้สิ่งนี้ได้ มันรวดเร็วมาก

คุณสามารถรวมสิ่งนี้ไว้ในฟังก์ชันที่มีมูลค่าตารางหรือจัดเก็บ proc และแยกวิเคราะห์ในวันที่เริ่มต้นและวันที่สิ้นสุดเป็นตัวแปร

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

2
การใช้ OPTION (MAXRECURSION 0) ที่นี่คืออะไร?
พระอิศวร

14

เรามีปัญหาคล้ายกันกับรายงาน BIRT ซึ่งเราต้องการรายงานในวันนั้นที่ไม่มีข้อมูล เนื่องจากไม่มีรายการสำหรับวันที่เหล่านั้นทางออกที่ง่ายที่สุดสำหรับเราคือสร้างตารางง่ายๆที่เก็บวันที่ทั้งหมดและใช้เพื่อรับช่วงหรือเข้าร่วมเพื่อให้ได้ค่าเป็นศูนย์สำหรับวันนั้น

เรามีงานที่ดำเนินการทุกเดือนเพื่อให้แน่ใจว่าตารางจะมีประชากร 5 ปีในอนาคต ตารางถูกสร้างขึ้นดังนี้:

create table all_dates (
    dt date primary key
);

ไม่ต้องสงสัยเลยว่ามีวิธีที่ยุ่งยากมหัศจรรย์ในการทำเช่นนี้กับ DBMS ที่แตกต่างกัน แต่เรามักจะเลือกใช้โซลูชันที่ง่ายที่สุด ข้อกำหนดในการจัดเก็บสำหรับโต๊ะมีน้อยและทำให้การสืบค้นง่ายและพกพาได้ง่ายขึ้นมาก โซลูชันประเภทนี้มักจะดีกว่าจากมุมมองด้านประสิทธิภาพเนื่องจากไม่จำเป็นต้องมีการคำนวณข้อมูลต่อแถว

อีกทางเลือกหนึ่ง (และเราเคยใช้สิ่งนี้มาก่อน) คือเพื่อให้แน่ใจว่ามีรายการในตารางสำหรับทุกวันที่ เรากวาดตารางเป็นระยะและเพิ่มรายการเป็นศูนย์สำหรับวันที่และ / หรือเวลาที่ไม่มีอยู่จริง นี่อาจไม่ใช่ตัวเลือกในกรณีของคุณขึ้นอยู่กับข้อมูลที่จัดเก็บ

หากคุณจริงๆคิดว่ามันเป็นความยุ่งยากที่จะเก็บไว้ที่all_datesโต๊ะที่มีประชากร, การจัดเก็บเป็นวิธีที่จะไปซึ่งจะกลับชุดข้อมูลที่มีวันนั้น สิ่งนี้จะช้าลงอย่างแน่นอนเนื่องจากคุณต้องคำนวณช่วงทุกครั้งที่มีการเรียกแทนที่จะดึงข้อมูลที่คำนวณไว้ล่วงหน้าจากตาราง

แต่ตามจริงแล้วคุณสามารถเติมข้อมูลในตารางได้เป็นเวลา 1,000 ปีโดยไม่มีปัญหาในการจัดเก็บข้อมูลที่ร้ายแรง - 365,000 วันที่ 16 ไบต์ (ตัวอย่าง) บวกกับดัชนีที่ซ้ำวันที่บวกค่าใช้จ่าย 20% เพื่อความปลอดภัยฉันจะประมาณโดยประมาณที่ ประมาณ 14M [365,000 * 16 * 2 * 1.2 = 14,016,000 ไบต์]) ซึ่งเป็นตารางขนาดเล็กในโครงร่างของสิ่งต่างๆ


12

คุณสามารถใช้ตัวแปรผู้ใช้ของ MySQL ดังนี้:

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 คือ -1 เนื่องจากคุณเพิ่มในครั้งแรกที่ใช้ นอกจากนี้คุณไม่สามารถใช้ "HAVING date_sequence" ได้เนื่องจากจะทำให้ตัวแปรของผู้ใช้เพิ่มขึ้นสองครั้งสำหรับแต่ละแถว


8

เมื่อยืมแนวคิดจากคำตอบนี้คุณสามารถตั้งค่าตารางที่มี 0 ถึง 9 และใช้เพื่อสร้างรายการวันที่ของคุณ

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

ซึ่งจะช่วยให้คุณสร้างรายการวันที่ได้สูงสุด 1,000 วัน หากคุณต้องการขยายให้ใหญ่ขึ้นคุณสามารถเพิ่มการรวมไขว้อื่นในการสืบค้นภายใน


โซลูชันนี้ทำงานได้ดีขึ้นมาก แต่มีปัญหากับ UNIX_TIMESTAMP () - ให้ผลลัพธ์เช่น 1231185600.000000; ส่วนมิลลิวินาทีหลังจุดทศนิยม ในขณะที่ - SELECT UNIX_TIMESTAMP (ADDDATE ('2009-01-01', 0)) AS date; ไม่ส่งผลให้ส่วนนั้น
Bimal Poudel

3

สำหรับ Access (หรือภาษา SQL)

  1. สร้างตารางหนึ่งตารางที่มี 2 ฟิลด์เราจะเรียกตารางนี้tempRunDatesว่า -
    ฟิลด์fromDateและ - จากtoDate
    นั้นแทรกเพียง 1 เรกคอร์ดที่มีวันที่เริ่มต้นและวันที่สิ้นสุด

  2. สร้างตารางอื่น: - Time_Day_Ref
    นำเข้ารายการวันที่ (สร้างรายการใน excel เป็นเรื่องง่าย) ลงในตารางนี้
    - ชื่อฟิลด์ในกรณีของฉันคือGreg_Dtสำหรับ Gregorian Date
    - ฉันสร้างรายชื่อของฉันตั้งแต่ 1 มกราคม 2009 ถึง 1 มกราคม 2020

  3. เรียกใช้แบบสอบถาม:

    SELECT Time_Day_Ref.GREG_DT
    FROM tempRunDates, Time_Day_Ref
    WHERE Time_Day_Ref.greg_dt>=tempRunDates.fromDate And greg_dt<=tempRunDates.toDate;
    

ง่าย!


2

โดยทั่วไปแล้วเราจะใช้ตารางตัวเลขเสริมที่คุณมักจะเก็บไว้เพื่อจุดประสงค์นี้โดยมีรูปแบบบางอย่างเกี่ยวกับสิ่งนี้:

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

ฉันเคยเห็นรูปแบบที่มีฟังก์ชันมูลค่าตาราง ฯลฯ

คุณยังสามารถเก็บรายการวันที่ถาวรได้ เรามีสิ่งนั้นในคลังข้อมูลของเรารวมทั้งรายการช่วงเวลาของวัน


2

วิธีค้นหาวันที่ระหว่างสองวันที่กำหนดในเซิร์ฟเวอร์ SQL อธิบายได้ที่http://ektaraval.blogspot.com/2010/09/writing-recursive-query-to-find-out-all.html


1
MySQL ยังไม่รองรับการสืบค้นแบบเรียกซ้ำดังนั้นจึงไม่สามารถใช้โซลูชันนี้ได้ที่นี่
Mark Byers


2

โซลูชันที่หรูหราโดยใช้ฟังก์ชันการเรียกซ้ำ (Common Table Expressions) ใหม่ใน MariaDB> = 10.3 และ 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;

ข้างต้นแสดงตารางวันที่ระหว่าง '2019-01-01' ถึง '2019-04-30'


"select '2019-01-01' เป็น dt" ตัวเลือกนี้คืออะไร เป็นไปได้ไหมที่จะเลือกเขตข้อมูลจากและใน
Miguel Stevens

1

เราใช้สิ่งนี้ในระบบ HRMS ของเราคุณจะพบว่ามีประโยชน์

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

1

การแก้ปัญหานี้คือการทำงานกับ MySQL 5.0
สร้างตาราง mytable-
สคีมาไม่ได้มีสาระสำคัญ สิ่งที่สำคัญคือจำนวนแถวในนั้น
ดังนั้นคุณสามารถเก็บ INT ได้เพียงคอลัมน์เดียวโดยมี 10 แถวค่า - 1 ถึง 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';

ข้อ จำกัด : จำนวนวันที่สูงสุดที่ส่งกลับโดยข้อความค้นหาข้างต้นจะเป็น
(rows in mytable)*(rows in mytable) = 10*10 = 100.

คุณสามารถเพิ่มช่วงนี้ได้โดยการเปลี่ยนส่วนฟอร์มใน sql:
จาก mytable x, mytable y, mytable z
ดังนั้นช่วง10*10*10 =1000จึงเป็นไปเรื่อย ๆ


0

สร้างกระบวนงานที่เก็บไว้ซึ่งใช้พารามิเตอร์สองตัว a_begin และ a_end สร้างตารางชั่วคราวภายในเรียกว่า t ประกาศตัวแปร d กำหนด a_begin ให้กับ d และรันการWHILEวนซ้ำในINSERTd เป็น t และเรียกADDDATEฟังก์ชันเพื่อเพิ่มค่า d สุดท้ายSELECT * FROM t.


อันที่จริงฉันเชื่อว่าตารางถาวรจะดีกว่าสำหรับความเร็วในการดำเนินการ (มีข้อกำหนดในการจัดเก็บน้อยที่สุด) การคำนวณวันที่ล่วงหน้าลงในตารางและแยกข้อมูลเมื่อจำเป็นทำได้เร็วกว่าการสร้างช่วงทุกครั้งที่คุณต้องการ
paxdiablo

@Pax ขึ้นอยู่กับว่าการปรับปรุงความเร็วที่ได้รับนั้นคุ้มค่ากับการบำรุงรักษาโต๊ะหรือไม่ ตัวอย่างเช่นหากการสร้างรายงานใช้เวลา 3 วินาทีและการรัน WHILE loop เพื่อสร้างวันที่ใช้เวลา 0.001 วินาทีฉันขอยืนยันว่าประสิทธิภาพที่เพิ่มขึ้นนั้นไม่สามารถใช้ได้ Knuth: การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากฐานของความชั่วร้ายทั้งหมด
Eugene Yokota

@ eed3si9n การเพิ่มประสิทธิภาพก่อนกำหนดใช่ไม่ใช่การเพิ่มประสิทธิภาพทั้งหมด :-) ถ้าตัวอย่างของคุณถูกต้องใช่ฉันเห็นด้วยกับคุณ แต่ควรวัดผลเพื่อความแน่นอน การบำรุงรักษาตารางจะหายไปหากคุณสร้างมันขึ้นมา 1,000 ปี (ต้นทุนเพียงครั้งเดียว) ตามที่ฉันแนะนำ แต่วินาทีเหล่านั้นจะเพิ่มขึ้น
paxdiablo

0

ฉันจะใช้สิ่งที่คล้ายกับสิ่งนี้:

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

จากนั้น@HOLDERตารางตัวแปรจะเก็บวันที่ทั้งหมดที่เพิ่มขึ้นในแต่ละวันระหว่างวันที่สองวันนั้นพร้อมที่จะเข้าร่วมในเนื้อหาในดวงใจ


สวัสดีฉันกำลังพยายามใช้รหัสและแทรกผลลัพธ์ลงในตารางที่ฉันกำหนด DateTest ด้วยคอลัมน์ datefill ประเภท DATETIME ... แต่ล้มเหลว ... คุณสามารถระบุรหัสที่ใช้งานได้หรือไม่ หมายเหตุ: การใช้ SQL Server ที่นี่ขอบคุณ :)
sys_debug

0

ฉันต่อสู้กับสิ่งนี้มาระยะหนึ่งแล้ว เนื่องจากนี่เป็นการเข้าชมครั้งแรกใน Google เมื่อฉันค้นหาวิธีแก้ปัญหาให้ฉันโพสต์ว่าฉันไปถึงไหนแล้ว

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

แทนที่[yourTable]ด้วยตารางจากฐานข้อมูลของคุณ เคล็ดลับคือจำนวนแถวในตารางที่คุณเลือกต้องเป็น> = จำนวนวันที่ที่คุณต้องการส่งคืน ฉันลองใช้ตัวยึดตาราง DUAL แต่จะส่งคืนเพียงแถวเดียวเท่านั้น


1
แนวทางที่น่าสนใจ แต่ .. นี่ไม่ใช่สิ่งที่ Vihang แนะนำข้างต้น;)
Leigh

0
select * from table_name where col_Date between '2011/02/25' AND DATEADD(s,-1,DATEADD(d,1,'2011/02/27'))

ขั้นแรกให้เพิ่มวันใน endDate ปัจจุบันซึ่งจะเป็น 2011-02-28 00:00:00 จากนั้นคุณจะลบหนึ่งวินาทีเพื่อให้เป็นวันที่สิ้นสุด 2011-02-27 23:59:59 น. เมื่อทำเช่นนี้คุณจะได้รับวันที่ทั้งหมดระหว่างช่วงเวลาที่กำหนด

เอาท์พุท:
2011/02/25
2011/02/26
2011/02/27


0
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 $$

ขั้นตอนนี้จะแทรกวันที่ทั้งหมดตั้งแต่ต้นปีจนถึงตอนนี้เพียงแค่แทนที่วันที่ "เริ่ม" และ "สิ้นสุด" เท่านี้คุณก็พร้อมที่จะไปแล้ว!


เส้นIF a=365 THENได้รับผลกระทบอย่างไรจากปีอธิกสุรทิน?
Stewart

นอกจากนี้ความสำคัญของตัวเลข150ในบรรทัด 9 คืออะไร? รหัสนี้เป็น "คำตอบ" โดยไม่มีคำอธิบายที่แท้จริงมากนัก
Stewart

0

ฉันต้องการรายชื่อที่มีทุกเดือนระหว่าง 2 วันที่สำหรับสถิติ วันที่ 2 วันคือวันที่เริ่มต้นและวันที่สิ้นสุดจากการสมัครสมาชิก ดังนั้นรายการจะแสดงทุกเดือนและจำนวนการสมัครสมาชิกต่อเดือน

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

0

คุณสามารถใช้สิ่งนี้

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;

ที่มา: - shayanderson.com/mysql/…
Dixon

-2

ฉันใช้ Server version: 5.7.11-log MySQL Community Server (GPL)

ตอนนี้เราจะแก้ปัญหานี้ด้วยวิธีง่ายๆ

ฉันได้สร้างตารางชื่อ"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)

ตอนนี้กระจ้อยร่อยจะเห็นบันทึกที่แทรกอยู่ภายใน

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)

และที่นี่แบบสอบถามของเราเพื่อดึงบันทึกภายในสองวันที่ไม่ใช่วันที่เหล่านั้น

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)

หวังว่านี่จะช่วยหลาย ๆ คนได้


ลงคะแนนโดยไม่มีเหตุผลใด ๆ SQL นี้ใช้ได้ดีและอยู่ในสถานการณ์การทำงาน
ArifMustafa
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.