MySQL Full Group by - mysql

Since my web-host has updated the MySQL server version, my old SQL Query is not working anymore:
select COUNT(ID) AS Anzahl,
DAY(STR_TO_DATE(created_at, '%Y-%m-%d')) AS Datum
from `leads`
where `created_at` >= 2018-02-01
and `created_at` <= 2018-02-15
and `shopID` = 20
group by DAY(created_at)
order by DAY(created_at) asc
That means, I have to create a full group by query. I already have read this article but I don't really get it.
I should name all columns which are unique
Thats what I don't get. If I want to count the ID, I cannot create a group by ID query because in this case my count would always be 1. Could anybody please explain to me how full group by works and how my statement would like with a full group by statement?

Just use the same expression in the select as in the group by:
select COUNT(ID) AS Anzahl, DAY(created_at) AS Datum
from `leads` l
where `created_at` >= '2018-02-01' and
`created_at` <= '2018-02-15' and
`shopID` = 20
group by DAY(created_at)
order by DAY(created_at) asc;
You also need single quotes around the date constants.

Your select and group by columns doesn't match. You should make them same. Try below query:
select COUNT(ID) AS Anzahl,
DAY(STR_TO_DATE(created_at, '%Y-%m-%d')) AS Datum
from `leads`
where `created_at` >= 2018-02-01
and `created_at` <= 2018-02-15
and `shopID` = 20
group by Datum
order by Datum asc

Related

How to do a cumulative count using Raw SQl / Laravel - Eloquent ORM

This is more of a SQL question than a Laravel one.
I'd like to accomplish something like the following based on my User model with the created_at field. (table users in database)
created_at
user_id
2022-04-30
1
2022-05-02
2
2022-05-03
4
date
created_users_to_this_date
total_users_created_to_date
2022-04
1
1
2022-05
2
3
Any idea on how to do so ?
What I have done so far (using Eloquent ORM) :
User::query()
->selectRaw("COUNT(*) created_users_to_this_date, DATE_FORMAT(created_at, '%Y-%m') date")
->orderBy('date')
->groupBy('date')
->get();
Equivalent SQL request
select COUNT(*) created_users_to_this_date, DATE_FORMAT(created_at, '%Y-%m') date from `users` where `users`.`deleted_at` is null group by `date` order by `date` asc
Thus returning
date
created_users_to_this_date
2022-04
1
2022-05
2
I thank you for your help
If your mysql version support window function, you can try to use SUM window function to do cumulative count
DB::table(DB::raw('(select COUNT(*) created_users_to_this_date, DATE_FORMAT(created_at, \'%Y-%m\') date
from `users`
where `users`.`deleted_at` is null
group by `date`) t1'))
->select('created_users_to_this_date','date',DB::raw('SUM(created_users_to_this_date) OVER(ORDER BY date) total_users_created_to_date'))
->get();
your equivalent sql will be
SELECT DATE ,
#running_number:=#running_number+created_users_to_this_date AS created_users_to_this_date
FROM (SELECT
COUNT(*) AS created_users_to_this_date,
DATE_FORMAT(created_at, '%Y-%m') DATE
FROM
users
where users.deleted_at is null
GROUP BY `date`
ORDER BY `date` ASC ) final
JOIN (SELECT #running_number:=0) rn
User::query()
->select('
DB::raw('COUNT(DATE_FORMAT(created_at, \'%Y-%m\') = DATE_FORMAT(now(), \'%Y-%m\')) as created_users_to_this_date'),
DB::raw('COUNT(DATE_FORMAT(created_at, \'%Y-%m\') <= DATE_FORMAT(now(), \'%Y-%m\')) as total_users_created_to_date'),
DB::raw('DATE_FORMAT(created_at, '%Y-%m') as date')
')->orderBy('date')->groupBy('date')->get();

mysql - group by the last record [duplicate]

This question already has answers here:
Retrieving the last record in each group - MySQL
(33 answers)
Closed 3 years ago.
My table has multiple records of n_no column, and when I GROUP BY, it returns me the first record of the group, how do I get the last record?
SELECT * FROM mytable WHERE category = 16 AND status = 1 AND datetime >= "2020-01-06 00:00:00" AND datetime <= "2020-01-06 23:59:59" GROUP BY n_no ORDER BY datetime DESC LIMIT 0,30
Thanks.
[Solved by doing]
SELECT * FROM mytable WHERE (SELECT MAX(datetime) from mytable GROUP BY n_no) AND category = 16 AND status = 1 AND datetime >= "2020-01-06 00:00:00" AND datetime <= "2020-01-06 23:59:59" GROUP BY n_no ORDER BY datetime DESC LIMIT 0,30
Assuming you are using MySQL 8+, then ROW_NUMBER() can work here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY n_no ORDER BY datetime DESC) rn
FROM mytable
WHERE category = 16 AND status = 1 AND
datetime >= '2020-01-06' AND datetime < '2020-01-07'
)
SELECT *
FROM cte
WHERE rn = 1;
You can try something like below,
SELECT * FROM mytable WHERE pk_col in (SELECT max(pk_col) FROM mytable WHERE category = 16 AND status = 1 AND datetime >= "2020-01-06 00:00:00" AND datetime <= "2020-01-06 23:59:59" GROUP BY n_no ORDER BY datetime DESC LIMIT 0,30)
Here, replace pk_col with the primary column name of your table.
Please find a sample here.
The idea of "GROUP BY" is to get categories. not to select a record.
SELECT DISTINCT n_no, datetime, category, status, etc
FROM mytable
WHERE category = 16
AND status = 1
AND datetime BETWEEN "2020-01-06 00:00:00" AND "2020-01-06 23:59:59"
ORDER BY datetime DESC
LIMIT 1
in this way you get all records from each n_no with the last date

MySQL Subquery with datetime comparison from main query values

I am trying to do what seems like a simple query.. I have a query which works fine until I try to add a subquery to the select clause. I am trying to add a column by querying a second table with the dates I get from the first. I don't know if a join might be better. If you look at my first sample it returns every record in my second table instead of using the date range from the outer select statement.
SELECT `sales`.`date` as 'newdate', `sales`.`material`,
`customer_logs`.`name`, `sales`.`billingqty` ,
(select count(*) from iis_logs where datetime > (select
Date_add(date_format(newdate, "%Y-%m-%d 00:00:00"), interval - 1 day))
and datetime < date_format(newdate, '%Y-%m-%d 00:00:00' and url like
CONCAT('%',material,'%') limit 1) as tr
FROM `sales`
JOIN `customer_logs` ON `customer_logs`.`customer_number` =
`sales`.`soldtopt`
WHERE `date` >= '2017-09-01'
AND `date` <= '2017-09-30'
ORDER BY `date` DESC
LIMIT 10;
If I just type the string as a date in like this it returns within a second:
SELECT `sales`.`date` as 'newdate', `sales`.`material`,
`customer_logs`.`name`, `sales`.`billingqty` ,
(select count(*) from iis_logs where datetime > '2017-09-01 00:00:00'
and datetime < '2017-09-03 00:00:00' and url like
CONCAT('%',material,'%') limit 1) as tr
FROM `sales`
JOIN `customer_logs` ON `customer_logs`.`customer_number` =
`sales`.`soldtopt`
WHERE `date` >= '2017-09-01'
AND `date` <= '2017-09-30'
ORDER BY `date` DESC
LIMIT 10;
It is not taking the value of newdate I am trying to get in select statement, instead it is returning every row in iis_logs table...
This looks like a perfect candidate for a join. FWIW, mysql query optimizer typically does better on joins that subqueries anyway.

Reverse order for GROUP BY in MySQL

I need to select first value for every hour from my db. But I don't know how to reverse order on GROUP BY statement.
How can i rewrite my query (now it selects last value in hour)?
SELECT HOUR(`time`) as hour, mytable.*
FROM mytable
WHERE DATE(`time`) ="2015-09-12" GROUP BY HOUR(`time`) ORDER BY `time` ASC;
This query gave me expected result:
SELECT HOUR(`time`) as hour, sortedTable.* FROM
(SELECT electrolysis.* FROM electrolysis
WHERE DATE(`time`)='2015-09-12' ORDER BY `time`) as sortedTable
GROUP BY HOUR(`time`);
You can just select the MIN HOUR in sub query , try using the query:
SELECT * from mytable WHERE `time` IN (
SELECT MIN(HOUR(`time`)) as `hour`
FROM mytable
WHERE DATE(`time`) ="2015-09-12"
GROUP BY HOUR(`time`) ) ORDER BY `time` ASC;
You can do something like this:-
SELECT sub0.min_time,
mytable.*
FROM mytable
INNER JOIN
(
SELECT MIN(`time`) AS min_time
FROM mytable
GROUP BY HOUR(`time`)
) sub0
ON mytable.`time` = sub0.min_time
WHERE DATE(`time`) ="2015-09-12"
ORDER BY `time` ASC
This is using a sub query to get the smallest time in each hour. This is then joined back against your main table on this min time to get the record that has this time.
Note that there is a potential problem here if there are multiple records that share the same time as the smallest one for an hour. There are ways around this, but that will depend on your data (eg, if you have a unique id field which is always ascending with time then you could select the min id for each hour and join based on that)
You can use below query, which is more optimized just make sure that time field should be indexed.
SELECT HOUR(m.time), m.*
FROM mytable AS m
JOIN
(
SELECT MIN(`time`) AS tm
FROM mytable
WHERE `time` >= '2015-09-12 00:00:00' AND `time` <= '2015-09-12 23:59:59'
GROUP BY HOUR(`time`)
) AS a ON m.time=a.tm
GROUP BY HOUR(m.time)
ORDER BY m.time;

MySQL date query syntax

If I have a date column, example "2013-05-05", how do I select the previous record? I tried
SELECT DATE, percent_change
FROM aa
WHERE DATE >2012 -12 -31
ORDER BY DATE DESC
LIMIT 1
Assuming that date is a unique key:
SELECT `date`, percentage_change FROM aa WHERE `date`>'2012-12-31' ORDER BY `date` DESC LIMIT 1;
Then, to get the previous record:
SELECT `date`, percentage_change FROM aa WHERE `date`>'2012-12-31' ORDER BY `date` DESC LIMIT 1 OFFSET 1;
And the record before that:
SELECT `date`, percentage_change FROM aa WHERE `date`>'2012-12-31' ORDER BY `date` DESC LIMIT 1 OFFSET 2;
etc.
To get the record before a given date, this should work:
SELECT DATE, percent_change
FROM aa
WHERE DATE < '2013-05-05'
ORDER BY DATE DESC
LIMIT 1
SQL Fiddle Demo