I've got a table that looks something like this:
CREATE TABLE `mailer__opens` (
`id` int(10) unsigned NOT NULL auto_increment,
`idSubscriber` int(10) unsigned NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`id`)
)
I'm trying to build a query which returns only the results where the value in idSubscriber is repeated 5 or more times. (I hope I'm explaining this right).
EG, if the data in the table looked like this:
id | idSubscriber | date
------------------------------
1 | 00001 | 2010-01-01
2 | 00002 | 2010-01-02
3 | 00001 | 2010-01-05
4 | 00003 | 2010-01-26
5 | 00004 | 2010-02-14
6 | 00001 | 2010-02-28
7 | 00002 | 2010-03-05
8 | 00001 | 2010-03-06
9 | 00003 | 2010-03-10
10 | 00001 | 2010-04-01
11 | 00004 | 2010-05-06
12 | 00002 | 2010-05-08
I'd be interested in records 1, 3, 6, 8 and 10, because the idSubscriber 00001 has 5 or more records.
Can anyone provide me with a query that would do this? Thank you.
To list the idSubscriber that has repeated five of more times you can use:
select idSubscriber
from mailer__opens
group by(idSubscriber) having count(*) >= 5;
To get the rows corresponding to such an idSubscriber you can use:
select *
from mailer__opens
where idSubscriber in
( select idSubscriber
from mailer__opens
group by(idSubscriber) having count(*) >= 5 )
You must use GROUP BY with a HAVING clause:
SELECT id FROM mailer__opens GROUP BY idSubscriber HAVING COUNT(id) >= 5
First of all you need to get the different idSubscriber values:
SELECT idSubscriber
FROM `mailer__opens`
GROUP BY idSubscriber
HAVING count( * ) >=5
For the given dataset, this will fetch only one value: 1
Then you need to select all rows where the idSubscriber is equal to those values. Therefore, your final query becomes:
SELECT *
FROM mailer__opens
WHERE idsubscriber
IN (
SELECT idSubscriber
FROM `mailer__opens`
GROUP BY idSubscriber
HAVING count( * ) >=5
)
SELECT id FROM mailer__opens WHERE idSubscriber IN (SELECT idSubscriber FROM mailer__opens GROUP BY idSubscriber HAVING COUNT(id) >= 5)
Related
I got a DATETIME to store when the values where introduced, like this example shows:
CREATE TABLE IF NOT EXISTS salary (
change_id INT(11) NOT NULL AUTO_INCREMENT,
emp_salary FLOAT(8,2),
change_date DATETIME,
PRIMARY KEY (change_id)
);
I gonna fill the example like this:
+-----------+------------+---------------------+
| change_id | emp_salary | change_date |
+-----------+------------+---------------------+
| 1 | 200.00 | 2018-06-18 13:17:17 |
| 2 | 700.00 | 2018-06-25 15:20:30 |
| 3 | 300.00 | 2018-07-02 12:17:17 |
+-----------+------------+---------------------+
I want to get the last inserted value of each month for every year.
So for the example I made, this should be the output of the Select:
+-----------+------------+---------------------+
| change_id | emp_salary | change_date |
+-----------+------------+---------------------+
| 2 | 700.00 | 2018-06-25 15:20:30 |
| 3 | 300.00 | 2018-07-02 12:17:17 |
+-----------+------------+---------------------+
1 won't appear because is an outdated version of 2
You could use a self join to pick group wise maximum row, In inner query select max of change_date by grouping your data month and year wise
select t.*
from your_table t
join (
select max(change_date) max_change_date
from your_table
group by date_format(change_date, '%Y-%m')
) t1
on t.change_date = t1.max_change_date
Demo
If you could use Mysql 8 which has support for window functions you could use common table expression and rank() function to pick row with highest change_date for each year and month
with cte as(
select *,
rank() over (partition by date_format(change_date, '%Y-%m') order by change_date desc ) rnk
from your_table
)
select * from cte where rnk = 1;
Demo
The below query should work for you.
It uses group by on month and year to find max record for each month and year.
SELECT s1.*
FROM salary s1
INNER JOIN (
SELECT MAX(change_date) maxDate
FROM salary
GROUP BY MONTH(change_date), YEAR(change_date)
) s2 ON s2.maxDate = s1.change_date;
Fiddle link : http://sqlfiddle.com/#!9/1bc20b/15
I searched and found similar post to what I am trying to accomplish but not an exact solution. I have a table of grouped articles (articles that have information in common). I need to select articles from said table where there are at least 10 articles belonging to the group.
Group ID | Article ID | Posting Date
------------------------------------
| 1 | 1234 | 2017-07-14
| 1 | 5678 | 2017-07-14
| 1 | 9000 | 2017-07-14
| 2 | 8001 | 2017-07-14
| 2 | 8002 | 2017-07-14
------------------------------------
SELECT `groupid`, `article_id`, `publish_date`
FROM `article_group`
WHERE `groupid` IN ( SELECT `groupid`, count(`groupid`) as cnt
FROM `article_group`
WHERE date(`publish_date`) = '2017-07-14'
group by `groupid`
having cnt > 10
order by cnt desc
)
I understand the sub-query should just return the one column, but how do I accomplish this with the count and having?
You are very close. You should only be selecting one column in the subquery and the ORDER BY is not necessary:
SELECT `groupid`, `article_id`, `publish_date`
FROM `article_group`
WHERE `groupid` IN (SELECT `groupid`
FROM `article_group`
WHERE date(`publish_date`) = '2017-07-14'
GROUP BY `groupid`
HAVING COUNT(*) > 10
)
Let's say I have the following two entries:
`id` | `timestamp` | `content` | `reference`
1 | 2012-01-01 | NEWER | 1
2 | 2013-01-01 | NEWEST | 1
3 | 2011-01-01 | OLD | 2
I need the following result from my query:
`id` | `timestamp` | `content` | `reference`
2 | 2013-01-01 | NEWEST | 1
3 | 2011-01-01 | OLD | 2
Here's what I have so far, but it is incorrect:
SELECT * FROM table GROUP BY reference
What would be the correct query here?
I am looking to get the newest piece of content per reference id. In the example above, there are two reference id's (1 & 2), and I want to get the most recent entry for each.
SELECT *
FROM (SELECT * FROM table ORDER BY timestamp desc) as sub
GROUP BY reference
If you wish to expand the query, put limiting logic into the subquery like so for better performance:
SELECT *
FROM (SELECT *
FROM table
WHERE 1=1 and 2=2
ORDER BY timestamp desc
) as sub
GROUP BY reference
I take it you want the newest of each reference? Something like this:
SELECT * FROM my_table
WHERE id IN (
SELECT id FROM my_table ORDER BY timestamp DESC GROUP BY reference LIMIT 1
);
select * from table where reference_id in
(select max(id) from table group by reference)
I currently have quite a messy query, which joins data from multiple tables involving two subqueries. I now have a requirement to group this data by DAY(), WEEK(), MONTH(), and QUARTER().
I have three tables: days, qos and employees. An employee is self-explanatory, a day is a summary of an employee's performance on a given day, and qos is a random quality inspection, which can be performed many times a day.
At the moment, I am selecting all employees, and LEFT JOINing day and qos, which works well. However, now, I need to group the data in order to breakdown a team or individual's performance over a date range.
Taking this data:
Employee
id | name
------------------
1 | Bob Smith
Day
id | employee_id | day_date | calls_taken
---------------------------------------------
1 | 1 | 2011-03-01 | 41
2 | 1 | 2011-03-02 | 24
3 | 1 | 2011-04-01 | 35
Qos
id | employee_id | qos_date | score
----------------------------------------
1 | 1 | 2011-03-03 | 85
2 | 1 | 2011-03-03 | 95
3 | 1 | 2011-04-01 | 91
If I were to start by grouping by DAY(), I would need to see the following results:
Day__date | Day__Employee__id | Day__calls | Day__qos_score
------------------------------------------------------------
2011-03-01 | 1 | 41 | NULL
2011-03-02 | 1 | 24 | NULL
2011-03-03 | 1 | NULL | 90
2011-04-01 | 1 | 35 | 91
As you see, Day__calls should be SUM(calls_taken) and Day__qos_score is AVG(score). I've tried using a similar method as above, but as the date isn't known until one of the tables has been joined, its only displaying a record where there's a day saved.
Is there any way of doing this, or am I going about things the wrong way?
Edit: As requested, here's what I've come up with so far. However, it only shows dates where there's a day.
SELECT COALESCE(`day`.day_date, qos.qos_date) AS Day__date,
employee.id AS Day__Employee__id,
`day`.calls_taken AS Day__Day__calls,
qos.score AS Day__Qos__score
FROM faults_employees `employee`
LEFT JOIN (SELECT `day`.employee_id AS employee_id,
SUM(`day`.calls_taken) AS `calls_in`,
FROM faults_days AS `day`
WHERE employee.id = 7
GROUP BY (`day`.day_date)
) AS `day`
ON `day`.employee_id = `employee`.id
LEFT JOIN (SELECT `qos`.employee_id AS employee_id,
AVG(qos.score) AS `score`
FROM faults_qos qos
WHERE employee.id = 7
GROUP BY (qos.qos_date)
) AS `qos`
ON `qos`.employee_id = `employee`.id AND `qos`.qos_date = `day`.day_date
WHERE employee.id = 7
GROUP BY Day__date
ORDER BY `day`.day_date ASC
The solution I'm comming up with looks like:
SELECT
`date`,
`employee_id`,
SUM(`union`.`calls_taken`) AS `calls_taken`,
AVG(`union`.`score`) AS `score`
FROM ( -- select from union table
(SELECT -- first select all calls taken, leaving qos_score null
`day`.`day_date` AS `date`,
`day`.`employee_id`,
`day`.`calls_taken`,
NULL AS `score`
FROM `employee`
LEFT JOIN
`day`
ON `day`.`employee_id` = `employee`.`id`
)
UNION -- union both tables
(
SELECT -- now select qos score, leaving calls taken null
`qos`.`qos_date` AS `date`,
`qos`.`employee_id`,
NULL AS `calls_taken`,
`qos`.`score`
FROM `employee`
LEFT JOIN
`qos`
ON `qos`.`employee_id` = `employee`.`id`
)
) `union`
GROUP BY `union`.`date` -- group union table by date
For the UNION to work, we have to set the qos_score field in the day table and the calls_taken field in the qos table to null. If we don't, both calls_taken and score would be selected into the same column by the UNION statement.
After this, I selected the required fields with the aggregation functions SUM() and AVG() from the union'd table, grouping by the date field in the union table.
I have a LoginTime table like this:
id | user_id | datetime
1 | 1 | 2011-01-19 18:51:01
2 | 1 | 2011-01-19 18:51:02
3 | 1 | 2011-01-19 18:51:03
4 | 1 | 2011-01-19 18:51:04
5 | 1 | 2011-01-19 18:51:05
6 | 1 | 2011-01-19 18:51:06
7 | 1 | 2011-01-19 18:51:07
8 | 1 | 2011-01-19 18:51:08
9 | 1 | 2011-01-19 18:51:09
10 | 2 | 2011-01-19 18:51:10
I want to keep only 5 latest(by 'datetime' column) records and delete all previous records where user_id=1
Is it possible to achieve this with one mysql query ?
DELETE
FROM LoginTime
WHERE user_id = 1
ORDER BY datetime ASC
LIMIT 5
I believe this will work...
DELETE FROM LoginTime WHERE id IN (
SELECT id
WHERE user_id = 1
ORDER BY datetime DESC
LIMIT 0, 5
)
delete LoginTime
from
LoginTime
left join
(
select id
from LoginTime
where user_id=1
order by `datetime` desc
limit 5
) as not_to_delete
on LoginTime.id=not_to_delete.id
where
not_to_delete.id is null;
PS: please don't use CamelCase for table name, and avoid using reserved keywords for the column name
delete LoginTime
from
LoginTime
left join
(
select id
from LoginTime
where user_id=1
order by datetime desc
limit 5
) as not_to_delete
on LoginTime.id=not_to_delete.id
left join
(
select id
from LoginTime
where user_id=1
) as existance
on LoginTime.id=existance.id
where
not_to_delete.id is null and existance.id is not null;