Working with date ranges in PHP/Oracle

2019-08-08 12:18发布

问题:

I need to work out how many different instances occur on a different day, from many different ranges. Probably best to explain it with an example.

18-JAN-09 to 21-JAN-09
19-JAN09 to 20-JAN-09
20-JAN-09 to 20-JAN-09

Using the three examples above, I need it to collect this information and display something a little like...

18th Jan: 1
19th Jan: 2
20th Jan: 3
21st Jan: 1

... I'll be grabbing the information from an Oracle database fwiw (hence the format above ^) and there will be hundreds, maybe thousands of records, so my lame attempt to do all sorts of loops and if statements would take forever to run.

Is there any fairly simple and efficient way of doing this? I'm really not too sure where to start unfortunately...

Thanks

回答1:

Either your DB engine or your PHP code is going to have to loop over the date range.

Here's some PHP code to do the summation. The day counts are stored by year-month to avoid having a huge array for a wide date range.

<?php

// Get the date ranges from the database, hardcoded for example
$dateRanges[0][0] = mktime(0, 0, 0, 1, 18, 2009);
$dateRanges[0][1] = mktime(0, 0, 0, 1, 21, 2009);
$dateRanges[1][0] = mktime(0, 0, 0, 1, 19, 2009);
$dateRanges[1][1] = mktime(0, 0, 0, 1, 20, 2009);
$dateRanges[2][0] = mktime(0, 0, 0, 1, 20, 2009);
$dateRanges[2][1] = mktime(0, 0, 0, 1, 20, 2009);

for ($rangeIndex = 0; $rangeIndex < sizeof($dateRanges); $rangeIndex++)
{
  $startDate = $dateRanges[$rangeIndex][0];
  $endDate = $dateRanges[$rangeIndex][1];

  // Add 60 x 60 x 24 = 86400 seconds for each day
  for ($thisDate = $startDate; $thisDate <= $endDate; $thisDate += 86400)
  {
    $yearMonth = date("Y-m", $thisDate);
    $day = date("d", $thisDate);

    // Store the count by year-month, then by day
    $months[$yearMonth][$day]++;
  }
}

foreach ($months as $yearMonth => $dayCounts)
{
  foreach ($dayCounts as $dayNumber => $dayCount)
  {
    echo $yearMonth . "-" . $dayNumber . ": " . $dayCount . "<br>";
  }
}

?>


回答2:

You could use the method described in another SO:

SQL> WITH DATA AS (
  2  SELECT to_date('18-JAN-09', 'dd-mon-rr') begin_date,
  3         to_date('21-JAN-09', 'dd-mon-rr') end_date FROM dual UNION ALL
  4  SELECT to_date('19-JAN-09', 'dd-mon-rr'),
  5         to_date('20-JAN-09', 'dd-mon-rr') FROM dual UNION ALL
  6  SELECT to_date('20-JAN-09', 'dd-mon-rr'),
  7         to_date('20-JAN-09', 'dd-mon-rr') FROM dual
  8  ),calendar AS (
  9  SELECT to_date(:begin_date, 'dd-mon-rr') + ROWNUM - 1 c_date
 10    FROM dual
 11  CONNECT BY LEVEL <= to_date(:end_date, 'dd-mon-rr')
 12                      - to_date(:begin_date, 'dd-mon-rr') + 1
 13  )
 14  SELECT c.c_date, COUNT(d.begin_date)
 15    FROM calendar c
 16    LEFT JOIN DATA d ON c.c_date BETWEEN d.begin_date AND d.end_date
 17   GROUP BY c.c_date
 18  ORDER BY c.c_date;

C_DATE      COUNT(D.BEGIN_DATE)
----------- -------------------
18/01/2009                    1
19/01/2009                    2
20/01/2009                    3
21/01/2009                    1


回答3:

You need a table with one row for each day

test_calendar:

Day
16.01.2009
17.01.2009
18.01.2009
19.01.2009
20.01.2009
21.01.2009
22.01.2009
23.01.2009
24.01.2009
25.01.2009
26.01.2009

table test contains bagin and finish of inctance:

DFROM   DTILL
18.01.2009  21.01.2009
19.01.2009  20.01.2009
20.01.2009  20.01.2009

Here is a query you need:

select day, (select count(1) 
  from test where dfrom<=t.day and dtill>=t.day) from test_calendar t
 where day>to_date('15.01.2009','DD.MM.YYYY')
 order by day


回答4:

Huge thanks for the solutions guys - managed to get it working using some of the SQL from above and also bits of PHP from the second solution.

Really appreciate it, cheers :)