MySQL: group occurrences per date - php

I guess this is a recurring question but I haven't found the right topic to point me in the right direction.
I have chat table which pretty much looks like:
+---------+----------------+---------------------+-----------------+
| id(int) | author(string) | date(datetime) | message(string) |
+---------+----------------+---------------------+-----------------+
| 1 | John | 2016-01-01 17:18:00 | I |
| 2 | Mary | 2016-01-01 14:22:00 | Just |
| 3 | John | 2016-01-01 09:02:00 | Want |
| 4 | John | 2016-01-02 17:18:00 | To |
| 5 | Mary | 2016-01-03 18:26:00 | Say |
| 6 | John | 2016-01-03 10:42:00 | Hello |
+---------+----------------+---------------------+-----------------+
What I would like to get:
+------------+------+------+
| day | Mary | John |
+------------+------+------+
| 2016-01-01 | 1 | 2 |
| 2016-01-02 | 0 | 1 |
| 2016-01-03 | 1 | 1 |
+------------+------+------+
Am I obligated to do a subquery in the COUNT statement ?
So far I came up with:
SELECT DATE(date) as day,
(SELECT COUNT(id) FROM chat WHERE author = 'Mary') AS 'Mary'
(SELECT COUNT(id) FROM chat WHERE author = 'John') AS 'John'
FROM chat
GROUP BY day
ORDER BY day ASC
But this is giving me the total message count per author at each row:
+------------+------+------+
| day | Mary | John |
+------------+------+------+
| 2016-01-01 | 2 | 4 |
| 2016-01-02 | 2 | 4 |
| 2016-01-03 | 2 | 4 |
+------------+------+------+

Just use conditional aggregation:
SELECT DATE(date) as day,
SUM(author = 'Mary') AS Mary,
SUM(author = 'John') AS John
FROM chat
GROUP BY day
ORDER BY day ASC

I might be wrong, but it looks for me like you search for a solution, where you have a small group of the same users? Even if it is true, I would change the logic in a way to avoid generating the query and having problems for larger amounts of users. It might not be the perfect solution for your current problem, but might help to save some time in the future. So I would use a different pattern.
As query, I would use something like this:
SELECT DATE(`date`) AS day,
`author`,
COUNT(`id`) AS messagecount
FROM `chat`
GROUP BY `day`, `author`
ORDER BY `day` ASC
With this you would get a result like this:
+------------+--------+--------------+
| day | author | messagecount |
+------------+--------+--------------+
| 2016-01-01 | Mary | 2 |
| 2016-01-01 | John | 4 |
| 2016-01-02 | Mary | 2 |
| 2016-01-02 | John | 4 |
+------------+--------+--------------+
After that, you can group the result in PHP to have the desired effect, for example generate an array like this using the date as key:
array(
'2016-01-01' => array(
'day' => '2016-01-01',
'Mary' => 2,
'John' => 4
),
'2016-01-02' => array(
'day' => '2016-01-02',
'Mary' => 2,
'John' => 4
),
)

Related

count total rows date wise from last 7 days in PHP

I want to count the registered user from database for last 7 days includes date of today and I am mentioning the structure is given below for understanding :-
reg_users
+----+---------------------+------+
| id | added-date | name |
+----+---------------------+------+
| 1 | 2020-06-01 00:02:40 | john |
+----+---------------------+------+
| 2 | 2020-06-01 00:02:41 | sue |
+----+---------------------+------+
| 3 | 2020-06-03 00:02:42 | fran |
+----+---------------------+------+
| 4 | 2020-06-04 00:02:40 | mark |
+----+---------------------+------+
| 5 | 2020-06-05 00:02:41 | tim |
+----+---------------------+------+
now suppose How I count the total registered use date wise from last 7 days.. where I am considering today is 2020-06-07 [dd-mm-yyy] and I want get result in array like [2, 0, 1, 1, 1, 0, 0] here 2 because 2 user registered on 2020-06-01 then on 2020-06-02 no user resisted so 0.
Please help me..
I think something like this is what you're after?
select count(*) as total, DATE_FORMAT(your_date, '%y%m%d') as date_yyyymmdd from your_table group by date_yyyymmdd;
It will return you a result like
+-------+----------------+
| total | date_yyyymmdd |
+-------+----------------+
| 1 | 20200727 |
| 0 | 20200726 |
| 3 | 20200725 |
+-------+----------------+

Laravel Eloquent Query Multiple records based on set criteria

I have a form where my users can set Criteria to search their customer database, however I am struggling to pull the query together for this (working in Laravel 5.7).
Currently the criteria customers can set is as follows:
Customer has “exactly | more | less” than X visits
Customer first visit was “exactly | more | less” than X days
Customer last visit was “exactly | more | less” than X days
Customer provider is “facebook | twitter | email | any”
I am now trying to get my head around how I can build this into a query, I can’t even produce a tangible example! My hurdle seems to be checking the first record & the last record to make sure it meets the criteria.
SQLFiddle: http://sqlfiddle.com/#!9/68407/1
My Table:
| id | name | email | provider | created_at
---------------------------------------------------------------------------------------
| 1 | Mr Smith | mr.smith#example.com | facebook | 2018-11-01 09:00:00 |
| 2 | Mrs Smith | mrs.smith#example.com | facebook | 2018-11-01 09:00:00 |
| 3 | Miss Smith | miss.smith#example.com | email | 2018-11-01 09:00:00 |
| 4 | Doctor Smith | doctor.smith#example.com | email | 2018-11-01 09:00:00 |
| 5 | Lord Smith | lord.smith#example.com | twitter | 2018-11-01 09:00:00 |
| 6 | Lady Smith | lady.smith#example.com | email | 2018-11-01 09:00:00 |
| 7 | Mr Smith | mr.smith#example.com | facebook | 2018-11-02 09:00:00 |
| 8 | Mrs Smith | mrs.smith#example.com | facebook | 2018-11-02 09:00:00 |
| 9 | Doctor Smith | doctor.smith#example.com | email | 2018-11-02 09:00:00 |
| 10 | Lord Smith | lord.smith#example.com | twitter | 2018-11-02 09:00:00 |
| 11 | Lady Smith | lady.smith#example.com | email | 2018-11-02 09:00:00 |
| 12 | Mr Smith | mr.smith#example.com | facebook | 2018-11-03 09:00:00 |
| 13 | Mrs Smith | mrs.smith#example.com | facebook | 2018-11-03 09:00:00 |
| 14 | Miss Smith | miss.smith#example.com | email | 2018-11-03 09:00:00 |
| 15 | Lord Smith | ord.smith#example.com | twitter | 2018-11-03 09:00:00 |
| 16 | Lady Smith | lady.smith#example.com | email | 2018-11-03 09:00:00 |
Example customer criteria for the query:
Customer with more than 2 visits
Customer first visit was more than 2 days ago
Customer last visit was more than 1 day ago
Customer provider is Facebook
Current query:
$Customers = Customer::groupBy('email')
->havingRaw('COUNT(*) > 2’)
->where('created_at', '<', Carbon::now()->subDays(2)->toDateTimeString())
->get();
I can’t figure out how to pull the first customer record check that it more than 2 days old, and then pull their last record and make sure it is more than 1 day old.
I know that my current query is completely useless for what I am trying to achieve, but again I am struggling to pull this together.
Expected results:
| id | name | email | provider | created_at
---------------------------------------------------------------------------------------
| 12 | Mr Smith | mr.smith#example.com | facebook | 2018-11-03 09:00:00 |
| 13 | Mrs Smith | mrs.smith#example.com | facebook | 2018-11-03 09:00:00 |
The query you are looking for is:
select * from customers group by email having count(*) > 2 and min(created_at) <= '2018-10-02 09:00:00' and max(created_at) <= '2018-10-03 09:00:00' and provider = 'facebook'
assuming the current time is 2018-10-04 09:00:00.
In eloquent:
$Customers = Customer::groupBy('email')
->havingRaw('COUNT(*) > 2')
->havingRaw('max(created_at) < ?' , [Carbon::now()->subDays(2)->toDateTimeString()])
->havingRaw('min(created_at) < ?' , [Carbon::now()->subDays(1)->toDateTimeString()])
->havingRaw('provider = ?' , ['facebook'])
->get();
On a separate note, using eloquent you can chain methods, like the following
$customers = Customer::groupBy('email');
if( $includeCount ) {
$customers->havingRaw('COUNT(*) > 2');
}
...
...
...
The SQL query in the MySQL dialect to make that selection is as follow
SELECT id, name, email, provider, created_at
FROM customers
WHERE provider = 'facebook'
GROUP BY email
HAVING
count(*) > 2
AND min(created_at) < date_sub(now(), interval 2 day)
AND max(created_at) < date_sub(now(), interval 1 day)
That can be translated as an Eloquent query like so
$havingClause = 'count(*) > ? AND min(created_at) < ? AND max(created_at) < ?';
$havingClauseBindings = [
2,
Carbon::now()->subDays(2)->toDateTimeString(),
Carbon::now()->subDays(1)->toDateTimeString()
];
$customers = Customer::where('provider', 'facebook')
->groupBy('email')
->havingRaw($havingClause, $havingClauseBindings)
->get();

Mysql query: Find least used words

I have a table of words used in the title of articles. I want to find which words which are used the least in the set or article titles.
Example:
Titles:
"Congressman Joey of Texas does not sign bill C1234."
"The pretty blue bird flies at night in Texas."
"Congressman Bob of Arizona is the signs bill C1234."
The table would contain the following.
Table WORDS_LIST
----------------------------------------------------
| INDEX ID | WORD | ARTICLE ID |
----------------------------------------------------
| 1 | CONGRESSMAN | 1234 |
| 2 | JOEY | 1234 |
| 3 | SIGN | 1234 |
| 4 | BILL | 1234 |
| 5 | C1234 | 1234 |
| 6 | TEXAS | 1234 |
| 7 | PRETTY | 1235 |
| 8 | BLUE | 1245 |
| 9 | BIRD | 1245 |
| 10 | FLIES | 1245 |
| 11 | NIGHT | 1245 |
| 12 | TEXAS | 1245 |
| 13 | CONGRESSMAN | 1246 |
| 14 | BOB | 1246 |
| 15 | ARIZONA | 1246 |
| 16 | SIGNS | 1246 |
| 17 | BILL | 1246 |
| 18 | C1234 | 1246 |
----------------------------------------------------
In this case, the words "pretty,blue, flies, night" would be the used in the least number of articles.
I would appreciate any ideas on how to best create this query. So far below is what I started with. I can also write something in PHP but figured a query would be faster.
SELECT distinct a1.`word`, count(a1.`word`)
FROM mmdb.words_list a1
JOIN mmdb.words_list b1
ON a1.id = b1.id AND
upper(a1.word) = upper(b1.word)
where date(a1.`publish_date`) = '2017-06-09'
group by `word`
order by count(a1.`word`);
I don't see why a self-join is necessary. Just do something like this:
select wl.word, count(*)
from mmdb.words_list wl
where date(wl.`publish_date`) = '2017-06-09'
group by wl.word
order by count(*);
You can add a limit to get a fixed number of words. If publish_date is already a date, you should do the comparison as:
where publish_date = '2017-06-09'
If it has a time component:
where publish_date >= '2017-06-09' and publish_date < '2017-06-10'
This expression allows MySQL to use an index.
Try this. It's a bit more simple and should return the correct results:
SELECT `WORD`,
COUNT(*) as `num_articles`
FROM `WORDS_LIST`
WHERE date(`publish_date`) = '2017-06-09'
GROUP BY `WORD`
ORDER BY COUNT(*) ASC;

calculating age based on date and matching against race results for sorting

I'm trying to add a new page onto an old site, a records page that'd show which players won the most money or won the race when they were a certain age
The user table looks like this
***********************************
| id | name | age | bday |
| 1 | bob | 15 | 2000-07-30 |
| 2 | john | 14 | 2001-07-30 |
| 3 | mary | 13 | 2002-07-30 |
***********************************
the race_results table looks like this
************************************************************
| id | raceid | userid | place | winnings | date |
| 1 | 1 | 1 | 1 | 1000 | 2006-04-10 |
| 2 | 1 | 2 | 5 | 50 | 2005-02-15 |
| 3 | 1 | 3 | 6 | 50 | 2010-06-12 |
| 4 | 2 | 1 | 1 | 1000 | 2009-05-29 |
| 5 | 2 | 2 | 3 | 250 | 2003-01-12 |
************************************************************
What's the most practical approach to a query that'd calculate the year range when Bob was 3 years old and match that with the race results table to see how many times he won 1st place within that particular date range?
SELECT COUNT(*)
FROM 'race_results'
INNER JOIN 'user' on user.id = race_results.userid
WHERE user.name = 'bob'
AND race_results.place = 1
AND race_results.date >= ADDDATE(user.bday, INTERVAL 3 YEAR)
AND race_results.date < ADDDATE(user.bday, INTERVAL 4 YEAR);
To get a list of all 3 yr old 1st placers and how many times they won, not just for 'bob' ...
SELECT user.name, COUNT(*)
FROM 'race_results'
INNER JOIN 'user' on user.id = race_results.userid
WHERE race_results.place = 1
AND race_results.date >= ADDDATE(user.bday, INTERVAL 3 YEAR)
AND race_results.date < ADDDATE(user.bday, INTERVAL 4 YEAR)
GROUP BY user.name;

SQL request for several tables and operations in MySQL

I have the following 3 tables: unit, stage, stats.
unit stage
+----+--------+ +----+-------+---------------------+
| id | status | | id |unit_id| date |
+----+--------+ +----+-------+---------------------+
| 1 | 2 | | 1 | 2 | 2013-11-22 00:00:00 |
| 2 | 3 | | 2 | 2 | 2013-11-26 12:00:00 |
| 3 | 3 | | 3 | 3 | 2013-10-11 00:00:00 |
| 4 | 0 | | 4 | 1 | 2013-12-29 00:00:00 |
+----+--------+ +----+-------+---------------------+
stats
+----+----------+---------------------+-------+
| id | stage_id | date | clicks|
+----+----------+---------------------+-------+
| 1 | 1 | 2013-11-22 00:00:00 | 10 |
| 2 | 1 | 2013-11-23 00:00:00 | 20 |
| 3 | 1 | 2013-11-24 00:00:00 | 25 |
| 4 | 2 | 2013-11-26 00:00:00 | 15 |
| 5 | 2 | 2013-11-27 12:00:00 | 21 |
| 6 | 3 | 2013-12-29 00:00:00 | 8 |
+----+----------+---------------------+-------+
I need a request, that will produce the following response:
+---------+---------------------+-----------------------+
| unit.id | stage.min.date | sum(stats.max.clicks) |
+---------+---------------------+-----------------------+
| 2 | 2013-11-22 00:00:00 | 46 |
| 3 | 2013-12-29 00:00:00 | 8 |
+---------+---------------------+-----------------------+
by the following rules:
1) unit.id - show only units with unit.status=3
2) stage.min.date - minimal stage.date for corresponding unit_id
3) sum(stats.max.clicks) - sum of stats.clicks with max dvalues for each stage_id associated with corresponding unit_id. In my example 46 = 25(stage_id=1) + 21(stage_id=2)
The problem is in min.date and sum of clicks - I have no idea how to get it in one query. Definitely it`s not a problem to do it using php code and several requests.
Schema in SQL Fiddle
Thanks in advance.
I just ask myself, why I do this? Your example resonse has an error, and does not match your fiddle... but:
SELECT
cc.unit_id, MIN(cc.date) as stage_min_date , SUM(dd.clicks) as stats_max_clicks
FROM
stage cc
LEFT JOIN (
SELECT
bb.stage_id, bb.clicks
FROM
stats bb LEFT JOIN (
SELECT id, stage_id, MAX(date) AS max_date
FROM stats
GROUP BY stage_id
) aa
ON
aa.max_date = bb.date
WHERE
aa.max_date IS NOT NULL
) dd
ON cc.id = dd.stage_id
LEFT JOIN unit ee
ON ee.id = cc.unit_id
WHERE ee.status = 3
GROUP BY cc.unit_id
...

Categories