select ONLY repeated rows in a table - mysql

I need to find users that received bonus at my DB. The only users that interest to me, are those who got bonus more than one time.
How should I work this query to get ONLY users who got bonus more than once?
select Bonus, BonusUser, BonusType, Amount
from Bonus
where BonusType="1"
order by BonusUser asc;
I need a query that prompts all "duplicated" rows, so I can remove bonus from them.
I haven't explained before, but some users exploited a bug and could get free bonus, so I must select those duplicated rows, analyze and remove if it's abuse case.

you can do like below
select BonusUser,
count(*)
from Bonus
where BonusType="1"
group by BonusUser
having count(*)>1
order by BonusUser asc
you must provide some dummy data with expected result ..

Add a GROUP BY and a HAVING clause
SELECT Bonus
, BonusUser
, BonusType
, Ammount
FROM Bonus
WHERE BonusType="1"
GROUP BY BonusUser
HAVING Count(*) > 1
ORDER BY BonusUser asc;
Based on your comment, I think this is what you want, this will give you the list of all users with a bonus but it will give you the count of those who had more than one bonus:
SELECT Bonus
, t.BonusUser
, BonusType
, amount
, t2.cntbonus
FROM Bonus t
inner join
(
select count(*) as CntBonus, bonususer
from Bonus
where BonusType='1'
group by bonususer
) t2
on t.bonususer = t2.bonususer
WHERE BonusType='1'
ORDER BY BonusUser asc

As an alternative to the other answers, you can also do the following:
SELECT DISTINCT b1.*
FROM Bonus b1
JOIN Bonus b2
ON b1.BonusUser = b2.BonusUser
AND b1.Id > b2.Id
WHERE b1.BonusType = "1"
AND b2.BonusType = "1"
ORDER BY b1.BonusUser ASC;

Related

SQL Ordering rows in a select with multiple conditions

I am trying to create an SQL Query to select rows from a database, ordered by a numerical field, however there are repeated entries in the table.
The table consists of the following columns.
UID - Numerical Unique ID
ACCOUNT_NAME - Account Name, unchanged
NICK_NAME - Can be changed by the user at any time
POINTS - Records points held by the user's account
The goal of the query is to display the Account_Name ordered by Points. However, Account_Name is not unique and can appear multiple times in the table.
To deal with this I would like to display only the latest row for each Account_Name.
This meaning that in the results from the select each Account_Name should only appear once. I am trying to have the selection be decided by the UID, meaning that I want only the row with the greatest UID where each account_name appears to be displayed.
I have tried the following without desired results. (The name of the table is ACCOUNT)
SELECT DISTINCT A.account_name , A.uid, A.points
FROM account A, account B
where A.account_name = B.account_name
and A.points > 0
and A.uid >= B.uid
order by A.points DESC;
This doesn't give me the desired results, specifically, there is an account in the database where an outdated row exists with a high value in the Points column. This record appears as the first result in the select, even though it is outdated.
How would you recommend adjusting this Query to select the desired information?
I hope this is enough information to work off of (first time posting a question) Thank you for you help :)
EDIT: Adding in examples with data.
Sample Table Data:
Sample Table Data
Current Results:
Current Results
Desired Results:
Desired Results
Consider joining on an aggregate query calculating MAX(UID)
SELECT a.account_name, a.uid, a.points
FROM account a
INNER JOIN
(
SELECT account_name, MAX(uid) AS max_uid
FROM account
GROUP BY account_name
) agg
ON a.account_name = agg.account_name
AND a.uid = agg.max_uid)
WHERE a.points > 0
ORDER by a.points DESC;
Alternatively, with MySQL 8.0, consider a window function:
SELECT a.account_name, a.uid, a.points
FROM account a
WHERE a.points > 0
AND a.uid = MAX(a.uid) OVER (PARTITION BY a.account_name)
ORDER by a.points DESC;

How to Insert Data into Tempdb Until Amount of Distinct Values Met

I have a main table named tblorder.
It contains CUID(Customer ID), CuName(Customer Name) and OrDate(Order Date) that I care about.
It is currently ordered by date in ascending order(ex. 2001 before 2002).
Objective:
Trying to retrieve most recent 1 Million DISTINCT Customer's CUID and CuNameS, and Insert them Into a Tempdb(#Recent1M) for Later Joining Uses.
So I:
Would Need Order By desc to flip the date to retrieve most recent 1 Million Customers
Only want first 1 Million DISTINCT Customer Information(CUID, CuName)
I know following code is not correct, but it is the main idea. I just can't figure out the correct syntax. So far I have the While Loop with Select Into as the most plausible solution.
SQL Platform: SSMS
Declare #DC integer
Set #DC = Count(distinct(CUID)) from #Recent1M))
While (#DC <1000000)
Begin
Select CuID,CuName into #Recent1MCus from tblorder
End
Thank you very much, I appreciate any help!
TOP 1000000 is the way to go, but you're going to need an ORDER BY clause or you will get arbitrary results. In your case, you mentioned that you wanted the most recent ones, so:
ORDER BY OrderDate DESC
Also, you might consider using GROUP BY rather than DISTINCT. I think it looks cleaner and keeps the select list a select list so you have the option to include whatever else you might want (as I took the liberty of doing). Notice that, because of the grouping, the ORDER BY now uses MAX(ordate) since customers can presumably have multiple ordate's and we are interested in the most recent. So:
select top 1000000 cuid, cuname, sum(order_value) as ca_ching, count(distinct(order_id)) as order_count
into #Recent1MCus
from tblorder
group by cuid, cuname
order by max(ordate) desc
I hope this helps.
Wouldn't you just do this?
select distinct top 1000000 cuid, cuname
into #Recent1MCus
from tblorder;
If the names might not be distinct, you can do:
select top 1000000 cuid, cuname
into #Recent1MCus
from (select o.*, row_number() over (partition by cuid order by ordate desc) as seqnum
from tblorder o
) o
where seqnum = 1;
Use DISTINCT and ORDER BY <colname> DESC to get latest unique records.
Try this SQL query:
SELECT DISTINCT top 1000000
cuid,
cuname
INTO #Recent1MCus
FROM tblorder
ORDER BY OrDate DESC;

Mysql subquery field unknown

I have read many posts have a solution for this but this does not work in my case. What am I doing wrong?
- This gives me the SUM of scores for every user and this is the first part.( Aggregated Data)
The Query result
SELECT user_id, sum(score) as total_user_score
FROM (
SELECT comments_proper.user_id, comments_proper.score
FROM assignment_2.comments_proper
) AS rsch
GROUP BY user_id;
However, I want only 2 records which contain the min and the max score values.
What am I doing wrong?
Oh dear, where to begin.
I have read many posts
You should have been paying attention to which ones got up-voted and good answers, and which were down-voted/closed. The former would have included the table structures, examples of input and expected output. And unambiguous questions.
I want only 2 records
Is that from the source data set or from the aggregated data set?
The latter is a slightly tricky problem which has been asked and answered many times here on SO, there are multiple solutions with different performance characteristics. There's even a chapter in the manual covering just this question. The current content at that link uses subqueries to identify the min/max value which replaces an earlier version of the documentation which explained the max-concat trick, but its also possible to use variables to identify the right caddidate rows in a sub-query or to use sorting.
However the SQL you've shown us here, has very little to do with solving the problem you describe, and is very badly written.
I won't provide examples of every solution, but this will solve your problem...
SELECT user_id, SUM(score)
FROM assignment_2.comments_proper
GROUP BY user_id
ORDER BY SUM(score)
UNION
SELECT user_id, SUM(score)
FROM assignment_2.comments_proper
GROUP BY user_id
ORDER BY SUM(score)
updated
I hadn't tested the above. I did test this:
SELECT *
FROM (
SELECT user_id, SUM(score)
FROM assignment_2.comments_proper
GROUP BY user_id
ORDER BY SUM(score) LIMIT 0,1
) as lowest
UNION ALL
SELECT *
FROM (
SELECT user_id, SUM(score)
FROM assignment_2.comments_proper
GROUP BY user_id
ORDER BY SUM(score) DESC LIMIT 0,1
) as highest
In your queries you have some problem of sintax and a too complex calculation for aggregated resul anyway .. in cp1.* result you have the min related values in cp2.* the max related.
If you need all the resuly for min and max rows on the same row you can use a couple of inner join based on the aggregated result
select cp1.* , cp2.*
from ( SELECT cp.user_id, sum(cp.score), min(cp.score) min_score, max(cp.score) max_score
FROM assignment_2.comments_proper cp
group by cp.user_id ) t
inner join assignment_2.comments_proper cp1 on cp1.user_id = t.user_id
and cp1.score = t.min_score
inner join assignment_2.comments_proper cp2 on cp2.user_id = t.user_id
and cp2.score = t.max_score
otherwise if you want the result in two rows one for min and one for max
select 'min' , cp1.*
from ( SELECT cp.user_id, sum(cp.score), min(cp.score) min_score, max(cp.score) max_score
FROM assignment_2.comments_proper cp
group by cp.user_id ) t
inner join assignment_2.comments_proper cp1 on cp1.user_id = t.user_id
and cp1.score = t.min_score
union
select 'max' , cp2.*
from ( SELECT cp.user_id, sum(cp.score), min(cp.score) min_score, max(cp.score) max_score
FROM assignment_2.comments_proper cp
group by cp.user_id ) t
inner join assignment_2.comments_proper cp2 on cp2.user_id = t.user_id
and cp2.score = t.max_score

Write a query to display the name of the player who has been declared as 'player of match' for maximum number of times

Two tables- Player,Outcome
In player table - id int(11),name varchar(50)
in outcome table - id int(11);
Another way (if you only want the name):
SELECT name
FROM player
WHERE id IN (
SELECT TOP 1 player_of_match
FROM outcome
GROUP BY player_of_match
ORDER BY count(player_of_match) DESC
)
or alternatively:
SELECT TOP 1 b.name
FROM outcome a, player b
WHERE a.player_of_match = b.id
GROUP BY b.name
ORDER BY count(player_of_match) DESC
Honzy's answer is also valid, though you have to include p.name in your GROUP BY clause as it's not an aggregate field.
EDIT Apologies - above queries are SQL Server, just realised you tagged MySql, I don't think you can use the TOP 1 syntax - replace that with a LIMIT 1 at the end.
Also poster has asked to list all names of players if there is a tie - in that case you may need to do something along these lines:
SELECT name
FROM player
WHERE id IN (
SELECT player_of_match FROM outcome
GROUP BY player_of_match
HAVING COUNT(player_of_match) = (
SELECT TOP 1 COUNT(player_of_match) from outcome
GROUP BY player_of_match
ORDER BY COUNT(player_of_match) DESC
)
)
I'm sure there's probably a more elegant way of doing this, but it gets the job done (this is SQL Server notation again, in MySql TOP will again probably have to be replaced with LIMIT notation).
SELECT p.id, p.name, COUNT(o.id) `number_of_times` FROM player p
LEFT JOIN outcome o ON (p.id = o.player_of_match)
GROUP BY p.id
ORDER BY COUNT(o.id) DESC
LIMIT 1
SELECT p.name
FROM outcome o
LEFT JOIN player p on p.id=player_of_match
GROUP BY player_of_match
ORDER BY COUNT(player_of_match) DESC
LIMIT 1;

Mysql Groupby and Orderby problem

Here is my data structure
alt text http://luvboy.co.cc/images/db.JPG
when i try this sql
select rec_id, customer_id, dc_number, balance
from payments
where customer_id='IHS050018'
group by dc_number
order by rec_id desc;
something is wrong somewhere, idk
I need
rec_id customer_id dc_number balance
2 IHS050018 DC3 -1
3 IHS050018 52 600
I want the recent balance of the customer with respective to dc_number ?
Thanx
There are essentially two ways to get this
select p.rec_id, p.customer_id, p.dc_number, p.balance
from payments p
where p.rec_id IN (
select s.rec_id
from payments s
where s.customer_id='IHS050018' and s.dc_number = p.dc_number
order by s.rec_id desc
limit 1);
Also if you want to get the last balance for each customer you might do
select p.rec_id, p.customer_id, p.dc_number, p.balance
from payments p
where p.rec_id IN (
select s.rec_id
from payments s
where s.customer_id=p.customer_id and s.dc_number = p.dc_number
order by s.rec_id desc
limit 1);
What I consider essentially another way is utilizing the fact that select rec_id with order by desc and limit 1 is equivalent to select max(rec_id) with appropriate group by, in full:
select p.rec_id, p.customer_id, p.dc_number, p.balance
from payments p
where p.rec_id IN (
select max(s.rec_id)
from payments s
group by s.customer_id, s.dc_number
);
This should be faster (if you want the last balance for every customer), since max is normally less expensive then sort (with indexes it might be the same).
Also when written like this the subquery is not correlated (it need not be run for every row of the outer query) which means it will be run only once and the whole query can be rewritten as a join.
Also notice that it might be beneficial to write it as correlated query (by adding where s.customer_id = p.customer_id and s.dc_number = p.dc_number in inner query) depending on the selectivity of the outer query.
This might improve performance, if you look for the last balance of only one or few rows.
I don't think there is a good way to do this in SQL without having window functions (like those in Postgres 8.4). You probably have to iterate over the dataset in your code and get the recent balances that way.
ORDER comes before GROUP:
select rec_id, customer_id, dc_number, balance
from payments
where customer_id='IHS050018'
order by rec_id desc
group by dc_number