1

While attempting to grab both a player's name as well as their damage was done from a players table and a join table including reports and players(reports_players table), I'm receiving ERROR 1066(42000): Not unique table/alias: 'rp'

I had also wanted to include item_level into this query but wanted to get this part working first.

I've tried using this query to grab the two values I want:

SELECT p.name
FROM players p
LEFT JOIN rp ON p.player_id=rp.player_id
UNION
SELECT rp.damage_done
FROM reports_players rp
RIGHT JOIN rp ON p.player_id=rp.player_id
GROUP BY p.name;

which is directly referenced from How to do a FULL OUTER JOIN in MySQL? and I'm still unclear as to why it's wrong. I've used multiple of the examples of a full join and I'm still not able to get anything other than an empty set or a query that gets hung up forever.

This is my db structure:

CREATE TABLE reports(
report_id INT UNSIGNED auto_increment PRIMARY KEY,
zone INT UNSIGNED,
report_url VARCHAR(20),
title VARCHAR(50)
)engine=InnoDB CHARSET utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE reports_players(
id INT UNSIGNED auto_increment PRIMARY KEY,
report_id INT UNSIGNED UNIQUE KEY,
player_id INT UNSIGNED UNIQUE KEY,
damage_done INT UNSIGNED,
item_level INT UNSIGNED
)engine=InnoDB CHARSET utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE players(
player_id INT UNSIGNED auto_increment PRIMARY KEY,
name VARCHAR(20)
)engine=InnoDB CHARSET utf8mb4 COLLATE = utf8mb4_unicode_ci;

expected: The outer join of players and reports_players to display a player's name as well as their damage dealt. actual: ERROR 1066(42000): Not unique table/alias: 'rp'

Lin Du
  • 88,126
  • 95
  • 281
  • 483
bynary
  • 89
  • 7
  • what is the meaning of join a table self? – wl.GIG Apr 30 '19 at 02:48
  • I'm sorry, I don't fully understand what you mean, do you mean why am I using a join table here? If so, it's to use a many-to-many relationship in that many players may have many different reports associated with them, one player may have many reports as well as one report containing many players. – bynary Apr 30 '19 at 02:51
  • 2
    You're invoking MySQL sorcery I've never seen the likes before if you think you can reference an alias from a totally separate query union'd together like... the way union works is that the two queries are ran separately then the results are union'd together. They can't reference each other... Although the results can be combined together and help aid another query afterwards... Although if you specified the full table names in each of the queries so they don't reference each other, you'd be able to unionize your results in a manner that looks like what you're trying to do. – Ultimater Apr 30 '19 at 02:52
  • ah, not the many-to-many, I mean 'FROM reports_players rp RIGHT JOIN rp ' . This part is for what? – wl.GIG Apr 30 '19 at 02:53
  • this part is to simulate a FULL OUTER JOIN in MySQL, I want the data from the parts of the tables that DO NOT overlap at all, so I need to use an outer join, which in mysql is done with a left and right join in a union. – bynary Apr 30 '19 at 02:57
  • If you're just trying to do a full outer join, refer to: https://stackoverflow.com/questions/4796872/how-to-do-a-full-outer-join-in-mysql – Ultimater Apr 30 '19 at 03:01
  • Possible duplicate of [How to do a FULL OUTER JOIN in MySQL?](https://stackoverflow.com/questions/4796872/how-to-do-a-full-outer-join-in-mysql) – Ultimater Apr 30 '19 at 03:01
  • 1
    well, I think distinct would help you. just like @Ultimater said, you are using some sorcery lol. – wl.GIG Apr 30 '19 at 03:02
  • I've referenced both of the links prior to asking this question and I'm unclear still about this. `SELECT players.name FROM players LEFT OUTER JOIN reports_players ON players.player_id=reports_players.player_id UNION SELECT reports_players.damage_done FROM reports_players RIGHT OUTER JOIN reports_players ON players.player_id=reports_players.player_id GROUP BY players.name;` is what I'm trying now – bynary Apr 30 '19 at 03:11
  • The solution over there doesn't use a GROUP BY at all. If it did, it would be on both queries. Also note the order of the tables is important when joining. Over there you see `t1 LEFT JOIN t2` and `t1 RIGHT JOIN t2` and your queries need to follow suit. Also notice the same thing is being selected by both queries `*`. What you're selecting with both queries `SELECT p.name` and `SELECT rp.damage_done` isn't compatible with one another. You should instead be using `SELECT p.name, rp.damage_done` in both queries. A `union` gives you a collection of rows, It doesn't add columns to those rows. – Ultimater Apr 30 '19 at 03:49
  • I'm still getting an empty set when using SELECT p.name AS 'Player Name', rp.damage_done AS 'Damage Done' FROM players p JOIN reports_players rp ON p.player_id=rp.player_id UNION SELECT p.name AS 'Player Name', rp.damage_done AS 'Damage Done' FROM players p JOIN reports_players rp ON rp.player_id=p.player_id – bynary Apr 30 '19 at 13:16

1 Answers1

0

If your not dead set on showing your values inline, the below should give you a report of distinct player names, and the damage they have done.

SELECT p.name, sum(rp.damage_done) damage_done
FROM players p 
JOIN reports_players rp
    ON p.player_id=rp.player_id
Group By p.name

If you need all players, even if they have not done damage:

SELECT p.name, sum(ifnull(rp.damage_done,0)) damage_done
FROM players p 
LEFT JOIN reports_players rp
    ON p.player_id=rp.player_id
Group By p.name

To fix your query to a full outer join, you need to do the following:

SELECT p.name, sum(rp.damage_done)
FROM players p
LEFT JOIN reports_players rp ON p.player_id=rp.player_id
GROUP BY p.name
UNION
SELECT p.name, sum(rp.damage_done)
FROM reports_players rp
RIGHT JOIN players p ON p.player_id=rp.player_id
GROUP BY p.name;

But I don't really think that will solve your problem if the left joins are not working for you. I think the reason the above selects are not working for you as the data that is in your tables is not matching up. To show you that the selects that I made do work I make the below example.

-- make the tables. 
CREATE TABLE reports(
report_id INT UNSIGNED auto_increment PRIMARY KEY,
zone INT UNSIGNED,
report_url VARCHAR(20),
title VARCHAR(50)
)engine=InnoDB CHARSET utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- note that I removed the unique keys from this table on report_id and player_id.  
CREATE TABLE reports_players(
id INT UNSIGNED auto_increment PRIMARY KEY,
report_id INT UNSIGNED ,
player_id INT UNSIGNED ,
damage_done INT UNSIGNED,
item_level INT UNSIGNED
)engine=InnoDB CHARSET utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE players(
player_id INT UNSIGNED auto_increment PRIMARY KEY,
name VARCHAR(20)
)engine=InnoDB CHARSET utf8mb4 COLLATE = utf8mb4_unicode_ci;

create index reports_players_player_id on reports_players (player_id);
create index reports_players_report_id on reports_players (report_id);

insert into players (name) 
values 
('last one'),
('Jack'),
('Jaragoth'),
('Jim'),
('Joe'),
('Jill');

insert into reports (zone, report_url, title) VALUES
(1,'www.place.com','The Place'),
(2,'www.theother.com','The Other Place');


drop PROCEDURE if EXISTS makeData;
-- make a procedure to build a data set. 
CREATE PROCEDURE makeData() 
BEGIN
    DECLARE i INT DEFAULT 0;

    TRUNCATE TABLE reports_players;

    start transaction;
    WHILE i < 10000 DO
    insert into reports_players (report_id, player_id, damage_done, item_level)    
    select floor(rand() *2) + 1 report_id, floor(rand() * 5) + 1 player_id, floor(rand() *20) +1 damage_done, floor(rand() * 5) + 1 item_level;
    set i = (i + 1);
    END WHILE;
    commit;
END ;

-- make the data
call makeData();

-- we will want an index
create index reports_players_player_id on reports_players (player_id);
create index reports_players_report_id on reports_players (report_id);

-- prove we made the data
select count(1) from reports_players;

---- some reporting examples. 
-- Inner join
SELECT p.name, sum(ifnull(rp.damage_done,0)) damage_done
FROM players p 
JOIN reports_players rp
    ON p.player_id=rp.player_id
Group By p.name

-- Left outer join (all players regarless of if they have damaged)
SELECT p.name, sum(ifnull(rp.damage_done,0)) damage_done
FROM players p 
LEFT JOIN reports_players rp
    ON p.player_id=rp.player_id
Group By p.name

-- Left outer join grouped by player, and item
SELECT p.name, rp.item_level, sum(ifnull(rp.damage_done,0)) damage_done
FROM players p 
LEFT JOIN reports_players rp
    ON p.player_id=rp.player_id
Group By p.name, rp.item_level
Jaragoth
  • 58
  • 6
  • First query returns empty set, second hasn't finished yet which I'm not taking as a good sign given over 100000 records – bynary Apr 30 '19 at 03:16
  • Hmm. Maybe I am not understanding the data. Just noticed that you have a `UNIQUE KEY` constraint on reports_players for report_id and player_id. Will your player_id's never put in more than one damage report? – Jaragoth May 02 '19 at 02:10