So, I have a table that is something like this...
person | account | accountbalance
--------------------------------------------
1 a 100
1 b 250
1 c 283
2 a 25
2 b 199
3 a 65
and for each person, I need to find the account that has the highest balance. I am doing this right now:
SELECT person, account, accountbalance FROM mytable
AND accountbalance=
(SELECT MAX(accountbalance) FROM mytable);
But this only returns the ONE top account among them all, not for each person.
One way is to compute the max in a derived table and join with that:
SELECT mytable.person, account, accountbalance
FROM mytable
JOIN (
SELECT person, MAX(accountbalance) MaxAccountBalance
FROM mytable
GROUP BY person
) t ON mytable.person = t.person
AND mytable.accountbalance = t.MaxAccountBalance;
or you could do a correlated subquery in the where clause (which is what you almost did - you just missed the necessary correlation):
SELECT person, account, accountbalance
FROM mytable m1
WHERE accountbalance = (SELECT MAX(accountbalance) FROM mytable WHERE person = m1.person);
Sample SQL Fiddle
For MYSQL, you can try this way also
select *
from (select * from mytable order by `person`, accountbalance desc, account) x
group by `person`
Related
Here I have this table:
Copies
nInv | Subject | LoanDate | BookCode |MemberCode|
1 |Storia |15/04/2019 00:00:00 |7844455544| 1 |
2 |Geografia |12/09/2020 00:00:00 |8004554785| 4 |
4 |Francese |17/05/2006 00:00:00 |8004894886| 3 |
5 |Matematica |17/06/2014 00:00:00 |8004575185| 3 |
I'm trying to find the value of the highest number of duplicates in the MemberCode column. So in this case I should get 3 as result, as its value appears two times in the table. Also, MemberCode is PK in another table, so ideally I should select all rows of the second table that match the MemberCode in both tables. For the second part I guess I should write something like SELECT * FROM Table2, Copies WHERE Copies.MemberCode = Table2.MemberCode but I'm missing out almost everything on the first part. Can you guys help me?
Use group by and limit:
select membercode, count(*) as num
from t
group by membercode
order by count(*) desc
limit 1;
SELECT MAX(counted) FROM
(SELECT COUNT(MemberCode) AS counted
FROM table_name GROUP BY MemberCode)
Using analytic functions, we can assign a rank to each member code based on its count. Then, we can figure out what its count is.
WITH cte AS (
SELECT t2.MemberCode, COUNT(*) AS cnt,
RANK() OVER (ORDER BY COUNT(*) DESC, t2.MemberCode) rnk
FROM Table2 t2
INNER JOIN Copies c ON c.MemberCode = t2.MemberCode
GROUP BY t2.MemberCode
)
SELECT cnt
FROM cte
WHERE rnk = 1;
Something like this
with top_dupe_member_cte as (
select top(1) MemberCode, Count(*)
from MemberTable
group by MemberCode
order by 2 desc)
select /* columns from your other table */
from OtherTable ot
join top_dupe_member_cte dmc on ot.MemberCode=dmc.MemberCode;
Hello – I have a DB table (MySQL ver 5.6.41-84.1-log) that has about 92,000 entries, with columns for:
id (incremental unique ID)
post_type (not important)
post_id (not important, but shows relation to another table)
user_id (not important)
vote (not important)
ip (IP Address, ie. 123.123.123.123)
voted (Datestamp in GMT, ie. 2018-12-03 04:50:05)
I recently ran a contest and we had a rule that no single IP could vote more than 60 times per day. So now I need to run a custom SQL formula that applies the following rule:
For each IP address, for each day, if there are > 60 rows, delete those additional rows.
Thank you for your help!
This is a complicated one, and I think it is hard to provide a 100% sure answer without actual table and data to play with.
However let me try to describe the logic, and build the query step by step so you can paly around with it and possibly fix lurking erros.
1) We start with selecting all ip adresses that posted more than 60 votes on a given day. For this we use a group by on the voting day and on the ip adress, combined with a having clause
select date(voted), ip_adress
from table
group by date(voted), ip_adress
having count(*) > 60
2) From then, we go back to the table and select the first 60 ids corresponding to each voting day / ip adress couple. id is an autoincremented field so we just sort using this field and the use the mysql limit instruction
select id, ip_adress, date(voted) as day_voted
from table
where ip_adress, date(voted) in (
select date(voted), ip_adress
from table
group by date(voted), ip_adress
having count(*) > 60
)
order by id
limit 60
3) Finally, we go back once again to the table and search for the all ids whose ip adress and day of vote belong to the above list, but whose id is greater than the max id of the list. This is achieved with a join and requires a group by clause.
select t1.id
from
table t1
join (
select id, ip_adress, date(voted) as day_voted
from table
where ip_adress, date(voted) in (
select date(voted), ip_adress
from table
group by date(voted), ip_adress
having count(*) > 60
)
order by id
limit 60
) t2
on t1.ip_adress = t2.ip_adress
and date(t1.voted) = t2.day_voted and t1.id > max(t2.id)
group by t1.id
That should return the list of all ids that we need to delete. Test if before you go further.
4) The very last step is to delete those ids. There are limitations in mysql that make a delete with subquery condition quite uneasy to achieve. See the following SO question for more information on the technical background. You can either use a temporary table to store the selected ids, or try to outsmart mysql by wrapping the subquery and aliasing it. Let us try with the second option :
delete t.* from table t where id in ( select id from (
select t1.id
from
table t1
join (
select id, ip_adress, date(voted) as day_voted
from table
where ip_adress, date(voted) in (
select date(voted), ip_adress
from table
group by date(voted), ip_adress
having count(*) > 60
)
order by id
limit 60
) t2
on t1.ip_adress = t2.ip_adress
and date(t1.voted) = t2.day_voted
and t1.id > max(t2.id)
group by t1.id
) x );
Hope this helps !
You could approach this by vastly simplifying your sample data and using row number simulation for mysql version prior to 8.0 or window function for versions 8.0 or above. I assume you are not on version 8 or above in the following example
drop table if exists t;
create table t(id int auto_increment primary key,ip varchar(2));
insert into t (ip) values
(1),(1),(3),(3),
(2),
(3),(3),(1),(2);
delete t1 from t t1 join
(
select id,rownumber from
(
select t.*,
if(ip <> #p,#r:=1,#r:=#r+1) rownumber,
#p:=ip p
from t
cross join (select #r:=0,#p:=0) r
order by ip,id
)s
where rownumber > 2
) a on a.id = t1.id;
Working in to out the sub query s allocates a row number per ip, sub query a then picks row numbers > 2 and the outer multi-table delete deletes from t joined to a to give
+----+------+
| id | ip |
+----+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 3 |
| 4 | 3 |
| 5 | 2 |
| 9 | 2 |
+----+------+
6 rows in set (0.00 sec)
I had someone help me write the following query, which addressed my question.
SET SQL_SAFE_UPDATES = 0;
create table temp( SELECT id, ip, voted
FROM
(SELECT id, ip, voted,
#ip_rank := IF(#current_ip = ip, #ip_rank + 1, 1) AS ip_rank,
#current_ip := ip
FROM `table_name` where ip in (SELECT ip from `table_name` group by date(voted),ip having count(*) >60)
ORDER BY ip, voted desc
) ranked
WHERE ip_rank <= 2);
DELETE FROM `table_name`
WHERE id not in (select id from temp) and ip in (select ip from temp);
drop table temp;
I have a table that has user_id and purchase_id. I want to filter the table so that only users with more than 2 purchases (i.e. there are more than 2 rows for that user in the table). I used count and group by, but it does not work in a way I want.
create view myview as
select user_Id, purchase_id, count(*) as count from mytable group by user_Id;
select user_id, purchase_id from myview where count >2;
But it gives me only users (only one user_id) that has more than 2 purchase and it does not give me all of their purchases. For example if the table looks like this:
user_id purchase_id
1 1212
1 1312
2 1232
1 1321
3 1545
3 4234
My query gives me this :
1 1212
3 1545
But I want this:
1 1212
1 1312
1 1321
3 1545
3 4234
change your last sql like this
select mt.user_id, mt.purchase_id
from myview mv
inner join mytable mt
on mt.user_id=mv.user_id where mv.count >5;
SELECT
*
FROM
mytable mt,
(SELECT user_id, count(*) AS purchase_count FROM mytable GROUP BY user_id) ct
WHERE
mt.user_id = ct.user_id AND ct.purchase_count > 5;
SELECT *
FROM MYTABLE
WHERE USER_ID IN (SELECT USER_ID
FROM MYTABLE
GROUP BY USER_ID
HAVING COUNT(*)>=2)
I tested in my netezza,it works. hopefully, it's also working in mysql
Try GROUP BY with HAVING comment.
SELECT user_Id, purchase_id
FROM mytable
GROUP BY user_Id
HAVING count( * ) >5
As far as I can tell you want to list the user id's and purchase id's of all users that have over 5 purchases.
In order to do this you could do a join on two queries.
For example:
SELECT tblLeft.user_id,
tblLeft.purchase_id
FROM myview tblLeft
JOIN (SELECT user_id,
Count(*) AS purchases
FROM myview
GROUP BY user_id
HAVING purchases > 1) tblRight
ON tblLeft.user_id = tblRight.user_id
The tblRight is essentially a table containing the user_id's of all users with over 5 purchases.
We then do a select (tblLeft) and join it on the tbl right, ensuring only customers with over 5 purchases remain.
I am facing a problem with MySQL query which is a variant of "Id for row with max value". I am either getting error or incorrect result for all my trials.
Here is the table structure
Row_id
Group_id
Grp_col1
Grp_col2
Field_for_aggregate_func
Another_field_for_row
For all rows with a particular group_id, I want to group by fields Grp_col1, Grp_col2 then get max value of Field_for_aggregate_func and then corresponding value of Another_field_for_row.
Query I have tried is like below
SELECT c.*
FROM mytable as c left outer join mytable as c1
on (
c.group_id=c1.group_id and
c.Grp_col1 = c1.Grp_col1 and
c.Grp_col2 = c1.Grp_col2 and
c.Field_for_aggregate_func > c1.Field_for_aggregate_func
)
where c.group_id=2
Among alternative solutions for this problem I want a high performance solution as this will be used for large set of data.
EDIT: Here is the sample set of row and expected answer
Group_ID Grp_col1 Grp_col2 Field_for_aggregate_func Another_field_for_row
2 -- N 12/31/2015 35
2 -- N 1/31/2016 15 select 15 from group for max value 1/31/2016
2 -- Y 12/31/2015 5
2 -- Y 1/1/2016 15
2 -- Y 1/2/2016 25
2 -- Y 1/3/2016 30 select 30 from group for max value 1/3/2016
You can use a sub-query to find the maximums, then join that with the original table, along the lines of:
select m1.group_id, m1.grp_col1, m1.grp_col2, m1.another_field_for_row, max_value
from mytable m1, (
select group_id, grp_col1, grp_col2, max(field_for_aggregate_func) as max_value
from mytable
group by group_id, grp_col1, grp_col2) as m2
where m1.group_id=m2.group_id
and m1.grp_col1=m2.grp_col1
and m1.grp_col2=m2.grp_col2
and m1.field_for_aggregate_func=m2.max_value;
Watch out for when there is more than one max_value for the given grouping. You'll get multiple rows for that grouping. Fiddle here.
Try this.
See Fiddle demo here
http://sqlfiddle.com/#!9/9a3c26/8
Select t1.* from table1 t1 inner join
(
Select a.group_id,a.grp_col2,
A.Field_for_aggregate_func,
count(*) as rnum from table1 a
Inner join table1 b
On a.group_id=b.group_id
And a.grp_col2=b.grp_col2
And a.Field_for_aggregate_func
<=b.Field_for_aggregate_func
Group by a.group_id,
a.grp_col2,
a.Field_for_aggregate_func) t2
On t1.group_id=t2.group_id
And t1.grp_col2=t2.grp_col2
And t1.Field_for_aggregate_func
=t2.Field_for_aggregate_func
And t2.rnum=1
Here first I am assigning a rownumber in descending order based on date. The selecting all the records for that date.
I have 1000 account. each account has account_name, account_id, time_zone_id
I want to generate activities for every account to 3 users.
So I will need to generate 333 activities for used #10 and 333 for user #11 and 334 for user #12. But I need to make sure that the time zone is distributed equally. so if I have 200 account in a time zone 18 and 400 account in time zone 10 and 200 in time zone 7 and 200 in time zone 39 then I want to make sure I distribute those new activities for the users equally
I have tried something like this as a select to get the count and see if I am going the correct direction
SELECT count(ac.account_id) AS total, ac.time_zone_id,
(SELECT user_id FROM users where team_id = 4 ORDER BY RAND() LIMIT 1 ) AS user_id
FROM accounts AS ac
GROUP BY ac.time_zone_id
this created the activities but it is not equal distribution.
The following would return a user_no ( 10-12 ) for each account.
It sorts by time_zone_id and then uses the mod function to pick each of the three users in turn (user 10 for the first result, 11 for second, 12 for third, 10 for fourth and so on).
set #r = 0 ;
select
#r:=#r+1 row_no,
account_id,
account_name,
mod(#r,3)+10 user_no
from
account
order by
time_zone_id
Revision
you can get users in a similar way
set #ur = 0;
select
#ur:=#ur+1 user_row_no,
user_id
from users
where team_id = 4
Revised again
It would be something like this
Make some sample data
create table users( user_id int, team_id int) ;
insert into users select 2,4
union select 3,4
union select 1,2
union select 7,4
union select 6,4;
create table account ( account_id int,
account_name varchar(20),
time_zone_id varchar(3),
assigned_to int
);
insert into account(account_id ,account_name,time_zone_id)
select 1,'Janice','pst'
union select 2,'Jonny','gmt'
union select 3,'Jane','gmt'
union select 4,'Janet','pst'
union select 5,'James','gmt';
Make a table to pop the users in that we are interested in
(could/should be a temp_table)
create table temp_user( id int AUTO_INCREMENT primary key,
user_id int
);
insert into temp_user( user_id )
select user_id
from users
where team_id = 4;
The update
set #r=0;
update account join
(
select
#r:=#r+1 row_no,
account_id,
account_name,
assigned_to
from
account
order by
time_zone_id
) x
on x.account_id = account.account_id
join
temp_user
on
temp_user.id=(1+ mod(row_no,(select count(*) from temp_user)))
set account.assigned_to = temp_user.user_id
http://sqlfiddle.com/#!2/164733/10