Kekson
Kekson

Reputation: 63

MySQL get duplicate rows in subquery

I want to display all duplicate records from my table, rows are like this

uid   planet degree    
1     1      104
1     2      109
1     3      206
2     1      40
2     2      76
2     3      302

I have many different OR statements with different combinations in subquery and I want to count every one of them which matches, but it only displays the first match of each planet and degree.

Query:

    SELECT DISTINCT
  p.uid,
  (SELECT COUNT(*)
   FROM Params AS p2
   WHERE p2.uid = p.uid
  AND(
(p2.planet = 1 AND p2.degree BETWEEN 320 - 10 AND 320 + 10) OR
          (p2.planet = 7 AND p2.degree BETWEEN 316 - 10 AND 316 + 10)
                            ...Some more OR statements...
     )
      ) AS counts FROM Params AS p HAVING counts > 0 ORDER BY p.uid DESC

any solution folks?

Upvotes: 0

Views: 1880

Answers (1)

Jakumi
Jakumi

Reputation: 8374

updated

So, the problem most people have with their counting-joined-sub-query-group-queries, is that the base query isn't right, and the following may seem like a complete overkill for this question ;o)

base data

in this particular example what you would want as a data basis is at first this:

(uidA, planetA, uidB, planetB) for every combination of player A and player B planets. that one is quite simple (l is for left, r is for right):

SELECT l.uid, l.planet, r.uid, r.planet
FROM params l, params r

first step done.

filter data

now you want to determine if - for one row, meaning one pair of planets - the planets collide (or almost collide). this is where the WHERE comes in.

WHERE ABS(l.degree-r.degree) < 10

would for example only leave those pairs of planet with a difference in degrees of less than 10. more complex stuff is possible (your crazy conditional ...), for example if the planets have different diameter, you may add additional stuff. however, my advise would be, that you put some additional data that you have in your query into tables.

for example, if all 1st planets players have the same size, you could have a table with (planet_id, size). If every planet can have different sizes, add the size to the params table as a column.

then your WHERE clause could be like:

WHERE l.size+r.size < ABS(l.degree-r.degree)

if for example two big planets with size 5 and 10 should at least be 15 degrees apart, this query would find all those planets that aren't.

we assume, that you have a nice conditional, so at this point, we have a list of (uidA, planetA, uidB, planetB) of planets, that are close to colliding or colliding (whatever semantics you chose). the next step is to get the data you're actually interested in:

limit uidA to a specific user_id (the currently logged in user for example)

add l.uid = <uid> to your WHERE.

count for every planet A, how many planets B exist, that threaten collision

add GROUP BY l.uid, l.planet,

replace r.uid, r.planet with count(*) as counts in your SELECT clause

then you can even filter: HAVING counts > 1 (HAVING is the WHERE for after you have GROUPed)

and of course, you can

filter out certain players B that may not have planetary interactions with player A

add to your WHERE

r.uid NOT IN (1)

find only self collisions

WHERE l.uid = r.uid

find only non-self collisions

WHERE l.uid <> r.uid

find only collisions with one specific planet

WHERE l.planet = 1

conclusion

a structured approach where you start from the correct base data, then filter it appropriately and then group it, is usually the best approach. if some of the concepts are unclear to you, please read up on them online, there are manuals everywhere

final query could look something like this

SELECT l.uid, l.planet, count(*) as counts
FROM params l, params r
WHERE [ collision-condition ]
GROUP BY l.uid, l.planet
HAVING counts > 0

if you want to collide a non-planet object, you might want to either make a "virtual table", so instead of FROM params l, params r you do (with possibly different fields, I just assume you add a size-field that is somehow used):

FROM params l, (SELECT 240 as degree, 2 as planet, 5 as size) r

multiple:

FROM params l, (SELECT 240 as degree, 2 as planet, 5 as size 
                UNION 
                SELECT 250 as degree, 3 as planet, 10 as size 
                UNION ...) r

Upvotes: 2

Related Questions