简体   繁体   中英

SQL for count all in last 12 months with PHP

I have a MySQL/MariaDB database which includes timestamps created with the PHP function date() .

Like this:

ID | Name | Date
--------------------------
12 | John | 123456789
13 | Mike | 987654321
...
29 | Rick | 123498765
30 | Adam | 987651234

Now I need to get the count of all persons per month based in the last 12 months since the current one .

Example:

Currently is March, so I need to get something like this:

March has 3 persons
February has 14 persons
January has 16 persons
December has 13
November has 16
October has 30
...

And goes on.

How can I do this with PHP?


So far I have this simple for loop which returns last 12 months and then the timestamp for each, but I don't really know how to compare the returned timestamp with the DB timestamp.

for ($number = 0; $number <= 11; $number++) {
    $month = strtotime("-".$number." month");

    echo "Timestamp: ".$month."<br>";
    echo "Month: ".date("F", $month);
    echo "<hr>";
};

That loop returns something like this:

Timestamp: 1488398035
Month: March
---
Timestamp: 1485978835
Month: February
---
Timestamp: 1483300435
Month: January
---
Timestamp: 1480622035
Month: December
---
Timestamp: 1478026435
Month: November
---
Timestamp: 1475348035
Month: October
---
Timestamp: 1472756035
Month: September
---
Timestamp: 1470077635
Month: August
---
Timestamp: 1467399235
Month: July
---
Timestamp: 1464807235
Month: June
---
Timestamp: 1462128835
Month: May
---
Timestamp: 1459536835
Month: April

You can use FROM_UNIXTIME and DATE_FORMAT functions to convert timestamp into date and date into string respectively, and get the counts, eg:

SELECT DATE_FORMAT(FROM_UNIXTIME(`date`), '%Y-%m') as `month`, COUNT(ID) as count
FROM table
GROUP BY `month`;

You can do this in MySQL, simply sub table_name for the actual name of your table:

SELECT
    YEAR(FROM_UNIXTIME(`Date`)),
    MONTH(FROM_UNIXTIME(`Date`)),
    count(*) as total
FROM table_name
WHERE `Date` > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 12 month))
GROUP BY YEAR(FROM_UNIXTIME(`Date`)), MONTH(FROM_UNIXTIME(`Date`))

Generally this concept is more efficient (especially once you have large amounts of data) if you you use a datetime column instead. This will work though. This query also assumes 12 months ago literally, to the day, you could calculate the exact timestamp of the first day of the month and etc... Again, this would be way easier and more efficient with a datetime column.

To return twelve rows, one for the current month, and eleven for the preceding months, along with a count (even when the count is zero), we can do something like this:

SELECT CONCAT(DATE_FORMAT(r.sd,'%M is ')
             ,COUNT(t.name)
             ,' person'
             ,IF(COUNT(t.name)=1,'','s')
       ) AS `something_like_this`
  FROM ( SELECT d.d - INTERVAL i.i - 0 MONTH AS sd
              , d.d - INTERVAL i.i - 1 MONTH AS ed
           FROM ( SELECT DATE_FORMAT(NOW(),'%Y-%m-01') AS d ) d
          CROSS
           JOIN ( 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
                  UNION ALL SELECT 10 UNION ALL SELECT 11
                ) i
          ORDER BY sd DESC
       ) r        
 LEFT
 JOIN mytable t
   ON t.date >= UNIX_TIMESTAMP(r.sd)
  AND t.date <  UNIX_TIMESTAMP(r.ed)
GROUP BY r.sd
ORDER BY r.sd DESC

The inline view r is designed to return 12 rows. On those rows, we return the start and end date of the period. In this case, the first day of the month, and the first day of the following month.

An outer join to the table we want to get a count of rows from. Given that the date column is stored as a unix style 32-bit integer (seconds since epoch), we can use the MySQL UNIX_TIMESTAMP function to convert the MySQL DATE values (from r ), so we have values we can compare.


To answer the question that was asked, yes, it's also possible to achieve this type of functionality in PHP.

With the current code that generates the "beginning" timestamp for the period, also generate the "ending" timestamp (the first of the following month.)

Then execute a query that gets a count.

  SELECT COUNT(t.name) AS cnt
    FROM mytable t
   WHERE t.date >= 1488326400    /* 2017-03-01 00:00 UTC */
     AND t.date <  1491004800    /* 2017-04-01 00:00 UTC */

Fetch the result, and output a line. For the previous month, run another query

  SELECT COUNT(t.name) cnt
    FROM mytable t
   WHERE t.date >= 1485907200    /* 2017-02-01 00:00 UTC */
     AND t.date <  1488326400    /* 2017-03-01 00:00 UTC */

Fetch the result, output a line. And repeat.

Try this

SELECT
YEAR(FROM_UNIXTIME(`Date`)),
MONTH(FROM_UNIXTIME(`Date`)),
count(*) as total
FROM table_name
WHERE date(from_unixtime(`Date`)) 
BETWEEN CURDATE() AND CURDATE() + INTERVAL 12 MONTH;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM