Mysql: Update using select and local variable - mysql

I am looking for an update statement that will group terms by language in the following table
CREATE TABLE _tempTerms(
ID int(8) unsigned NOT NULL AUTO_INCREMENT,
TTC_ART_ID mediumint(8) unsigned,
TTC_TYP_ID mediumint(8) unsigned,
Name varchar(200),
Value varchar(200),
ID_Lang tinyint(3) unsigned,
Sequence smallint unsigned,
Group_ID int(8) unsigned DEFAULT 0,
PRIMARY KEY(TTC_ART_ID, TTC_TYP_ID, Name, Value),
UNIQUE KEY(ID)
);
All data except Group_ID is inserted into the table. I need to update the table so that I auto-generate new Group_IDs and the Group_ID for all records with same combination of TTC_ART_ID, TTC_TYP_ID and Sequence will get the same Group_ID. I guess I need a variable to store the current value for Group_ID and so far I experimented with
SET #group_id:=1;
UPDATE _tempTerms
SET Group_ID = (#group_id := #group_id + 1);
which just gives a new group_id to every new record. I believe I need a SELECT Statement somewhere to check if there is a group_id already given, but I am confused on how I go about it.
Thank you

Schema:
create database xGrpId; -- create a test db
use xGrpId; -- use it
CREATE TABLE _tempTerms(
ID int(8) unsigned NOT NULL AUTO_INCREMENT,
TTC_ART_ID mediumint(8) unsigned,
TTC_TYP_ID mediumint(8) unsigned,
Name varchar(200),
Value varchar(200),
ID_Lang tinyint(3) unsigned,
Sequence smallint unsigned,
Group_ID int(8) unsigned DEFAULT 0,
PRIMARY KEY(TTC_ART_ID, TTC_TYP_ID, Name, Value),
UNIQUE KEY(ID)
);
-- truncate table _tempTerms;
insert _tempTerms(TTC_ART_ID,TTC_TYP_ID,Name,Value,ID_Lang,Sequence) values
(1,2,'n','v1',66,4),
(1,1,'n','v2',66,4),
(1,1,'n','v3',66,3),
(1,1,'n','v4',66,4),
(1,1,'n','v5',66,4),
(1,1,'n','v6',66,3),
(2,1,'n','v7',66,4),
(1,2,'n','v8',66,4);
View them:
select * from _tempTerms order by id;
select distinct TTC_ART_ID,TTC_TYP_ID,Sequence from _tempTerms;
-- 4 rows
-- update _tempTerms set Group_ID=0; -- clear before testing
The query:
update _tempTerms t
join
( select TTC_ART_ID,TTC_TYP_ID,Sequence,#rn:=#rn+1 as rownum
from
( select distinct TTC_ART_ID,TTC_TYP_ID,Sequence
from _tempTerms
-- put your `order by` here if needed
order by TTC_ART_ID,TTC_TYP_ID,Sequence
) d1
cross join (select #rn:=0) as xParams
) d2
on d2.TTC_ART_ID=t.TTC_ART_ID and d2.TTC_TYP_ID=t.TTC_TYP_ID and d2.Sequence=t.Sequence
set t.Group_ID=d2.rownum;
Results:
select * from _tempTerms order by TTC_ART_ID,TTC_TYP_ID,Sequence;
+----+------------+------------+------+-------+---------+----------+----------+
| ID | TTC_ART_ID | TTC_TYP_ID | Name | Value | ID_Lang | Sequence | Group_ID |
+----+------------+------------+------+-------+---------+----------+----------+
| 3 | 1 | 1 | n | v3 | 66 | 3 | 1 |
| 6 | 1 | 1 | n | v6 | 66 | 3 | 1 |
| 2 | 1 | 1 | n | v2 | 66 | 4 | 2 |
| 4 | 1 | 1 | n | v4 | 66 | 4 | 2 |
| 5 | 1 | 1 | n | v5 | 66 | 4 | 2 |
| 1 | 1 | 2 | n | v1 | 66 | 4 | 3 |
| 8 | 1 | 2 | n | v8 | 66 | 4 | 3 |
| 7 | 2 | 1 | n | v7 | 66 | 4 | 4 |
+----+------------+------------+------+-------+---------+----------+----------+
Cleanup:
drop database xGrpId;
d1, d2, and xParams are derived tables. Every derived table needs a name. The purpose of xParams and the cross join is merely to bring in a variable to initialize the row number. This is because mysql lacks CTE functionality found in other RDBMS's. So, don't overthink the cross join. It is like saying LET i=0.

Related

Combine two tables with same Primary key

This seems like a simple question but I can't seem to find an answer. I have two tables. Table 1:
+---------+-----------+--------+-------+----------+
| userid | username | date | time | footsize |
+---------+-----------+--------+-------+----------+
| 1 | user1 | 103999 | 1010 | 9 |
| 2 | user2 | 484883 | 984 | 6 |
+---------+-----------+--------+-------+----------+
and Table 2:
+---------+-----------+----------+
| userid | natural | synthetic|
+---------+-----------+----------+
| 1 | y | n |
| 2 | n | y |
+---------+-----------+----------+
What I'd like to do is delete table 2.
But I need to move the columns and data natural and synthetic from table 2 and insert them into table 1, using userid as a primary key to make sure the data goes to the right customer.
I tried using the join statements but I can't seem to move them from joining to inserting without an error.
The general (loose) idea I want is
select userid from table1, select * from table2.
Insert into table1, table2.natural, table2.synthetic where table1.userid = table2.userid;
So that table 1 looks like this:
+---------+-----------+--------+-------+----------+-----------+----------+
| userid | username | date | time | footsize | natural | synthetic|
+---------+-----------+--------+-------+----------+-----------+----------+
| 1 | user1 | 103999 | 1010 | 9 | y | n |
| 2 | user2 | 484883 | 984 | 6 | n | y |
+---------+-----------+--------+-------+----------+-----------+----------+
I'm aware that's not a real query, but it should clarify what I'm trying to do. Thanks!
first you create a table that has all the columns
CREATE TABLE `table3` (
`userid` INT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(45) NULL,
`date` INT NULL,
`time` INT NULL,
`footsize` INT NULL,
`natural` VARCHAR(45) NULL,
`synthetic` VARCHAR(45) NULL,
PRIMARY KEY (`userid`));
insert into table3
select t1.userid, t1.username, t1.`date`, t1.`time`, t1.footsize,
t2.natural, t2.synthetic
from table1 as t1
join table2 as t2
on t1.userid = t2.userid

MySql calendar table and performances

for a project i'm working on, i have a single table with two dates meaning a range of dates and i needed a way to "multiply" my rows for every day in between the two dates.
So for instance i have start 2017-07-10, end 2017-07-14
I needed to have 4 lines with 2017-07-10, 2017-07-11, 2017-07-12, 2017-07-13
In order to do this i found here someone mentioning using a "calendar table" with all the dates for years.
So i built it, now i have these two simple tables:
CREATE TABLE `time_sample` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`start` varchar(16) DEFAULT NULL,
`end` varchar(16) DEFAULT NULL,
PRIMARY KEY (`societa_id`),
KEY `start_idx` (`start`),
KEY `end_idx` (`end`)
) ENGINE=MyISAM AUTO_INCREMENT=222 DEFAULT CHARSET=latin1;
This table contains my date ranges, start and end are indexed, the primary key is an incremental int.
Sample Row:
id start end
1 2015-05-13 2015-05-18
Second table:
CREATE TABLE `time_dimension` (
`id` int(11) NOT NULL,
`db_date` date NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `td_dbdate_idx` (`db_date`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
This has a date indexed for every day for many years to come.
Sample row:
id db_date
20120101 2012-01-01
Now, i made the join:
select * from time_sample s join time_dimension t on (t.db_date >= start and t.db_date < end);
This takes 3ms. Even if my first table is HUGE, this query will always be very quick (max i've seen was 50ms with a lot of records).
The issue i have is while grouping results (i need them grouped for my application):
select * from time_sample s join time_dimension t on (t.db_date >= start and t.db_date < end) group by db_date;
This takes more than one second with not so many rows in the first table, increasing dramatically. Why is this happening and how can i avoid this?
Changing the data types doesn't help, having the second table with just one column doesn't help.
Can i have suggestions, please :(
I cannot replicate this result...
I have a calendar table with lots of dates: calendar(dt) where dt is a PRIMARY KEY DATE data type.
DROP TABLE IF EXISTS time_sample;
CREATE TABLE time_sample (
id int(11) NOT NULL AUTO_INCREMENT,
start date not NULL,
end date null,
PRIMARY KEY (id),
KEY (start,end)
);
INSERT INTO time_sample (start,end) VALUES ('2010-03-13','2010-05-09);
SELECT *
FROM calendar x
JOIN time_sample y
ON x.dt BETWEEN y.start AND y.end;
+------------+----+------------+------------+
| dt | id | start | end |
+------------+----+------------+------------+
| 2010-03-13 | 1 | 2010-03-13 | 2010-05-09 |
| 2010-03-14 | 1 | 2010-03-13 | 2010-05-09 |
| 2010-03-15 | 1 | 2010-03-13 | 2010-05-09 |
| 2010-03-16 | 1 | 2010-03-13 | 2010-05-09 |
...
| 2010-05-09 | 1 | 2010-03-13 | 2010-05-09 |
+------------+----+------------+------------+
58 rows in set (0.10 sec)
EXPLAIN
SELECT * FROM calendar x JOIN time_sample y ON x.dt BETWEEN y.start AND y.end;
+----+-------------+-------+--------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+------+------+--------------------------+
| 1 | SIMPLE | y | system | start | NULL | NULL | NULL | 1 | |
| 1 | SIMPLE | x | range | PRIMARY | PRIMARY | 3 | NULL | 57 | Using where; Using index |
+----+-------------+-------+--------+---------------+---------+---------+------+------+--------------------------+
2 rows in set (0.00 sec)
Even with a GROUP BY, I'm struggling to reproduce the problem. Here's a simple COUNT...
SELECT SQL_NO_CACHE dt, COUNT(1) FROM calendar x JOIN time_sample y WHERE x.dt BETWEEN y.start AND y.end GROUP BY dt ORDER BY COUNT(1) DESC LIMIT 3;
+------------+----------+
| dt | COUNT(1) |
+------------+----------+
| 2010-04-03 | 2 |
| 2010-05-05 | 2 |
| 2010-03-13 | 2 |
+------------+----------+
3 rows in set (0.36 sec)
EXPLAIN
SELECT SQL_NO_CACHE dt, COUNT(1) FROM calendar x JOIN time_sample y WHERE x.dt BETWEEN y.start AND y.end GROUP BY dt ORDER BY COUNT(1) DESC LIMIT 3;
+----+-------------+-------+-------+---------------+---------+---------+------+---------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+----------------------------------------------+
| 1 | SIMPLE | y | index | start | start | 7 | NULL | 2 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | x | index | PRIMARY | PRIMARY | 3 | NULL | 1000001 | Using where; Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+----------------------------------------------+

MySQL Sort By Column, Then Pull The Rest

I have a unique situation where I need to pull many records, but sort SOME at the top if they are considered "favorites"...but then I need to pull all the rest of the records below this group.
Can this be done in one query...or will I need two? Any examples?
You can have a custom Order By with a CASE/WHEN clause:
Schema:
create table music
( mId int auto_increment primary key,
song varchar(100) not null
);
create table person
( pId int auto_increment primary key,
pName varchar(100) not null
);
create table person_owns_music
( id int auto_increment primary key,
mId int not null,
pId int not null,
favorite int not null,
rating int not null, -- 100 loves it. 0 hates it
foreign key `pom_2_music` (mId) references music(mId),
foreign key `pom_2_person` (pId) references person(pId)
);
-- truncate table person_owns_music;
insert music(song) values ('s1'),('s2'),('s3'),('s4'),('s5'),('s6');
insert person(pName) values ('p1'),('p2'),('p3');
insert person_owns_music(mId,pId,favorite,rating) values
(1,1,1,10),(2,1,1,100),(3,1,0,65),(4,1,1,15),(6,1,1,5),
(1,2,1,10),(2,2,1,100),(5,2,1,100);
Query:
SELECT pom.mId,m.song,pom.favorite,pom.rating
FROM person_owns_music pom
JOIN music m ON m.mId=pom.mId
WHERE pom.pId=1
ORDER BY CASE pom.favorite
WHEN 1 THEN 1
WHEN 0 THEN 2
END, pom.rating DESC;
+-----+------+----------+--------+
| mId | song | favorite | rating |
+-----+------+----------+--------+
| 2 | s2 | 1 | 100 |
| 4 | s4 | 1 | 15 |
| 1 | s1 | 1 | 10 |
| 6 | s6 | 1 | 5 |
| 3 | s3 | 0 | 65 |
+-----+------+----------+--------+

Message and reply massage functionality

We've a table called message.
CREATE TABLE IF NOT EXISTS `message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`from_user_id` int(11) NOT NULL,
`to_user_id` int(11) NOT NULL,
`content` text NOT NULL,
`club_id` int(11) NOT NULL,
`read_flag` int(11) NOT NULL DEFAULT '0',
`parent_id` int(11) NOT NULL,
`status` tinyint(1) DEFAULT NULL,
`create_user_id` int(11) NOT NULL,
`update_user_id` int(11) NOT NULL,
`create_dt_tm` datetime NOT NULL,
`update_dt_tm` datetime NOT NULL,
`delete_flag` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
Need to display the messages and message reply to the user.
Entries in the table will like this,
id | from_user_id | to_user_id | content | parent_id
1 | 2 | 3 | hai | 0
2 | 3 | 2 | hi | 1
3 | 3 | 2 | hwru | 1
4 | 3 | 4 | hwru | 1
5 | 4 | 5 | u added | 1
6 | 4 | 5 | new msg | 0
Here is the flow,
lets assume 2=>A, 3 =>B, 4 =>C, 5=> D,
A send a message to B
B reply to that message
B send again one more reply by adding new recipient C
C reply to that thread again by adding new recipient D
All users part of this thread, should able to read full message thread.
A,B,C and D can see the all (1,2,3,4,5) messages if they login except 6th
6th message only C and D can see and it is a different thread
Two queries I'm using now,
One for to list all messages.
Second is for to see the details for that message(when user click on that will show all thread related to that message).
By using single query I need to show the all threads to the login user.
Please help some one to select query for this.
Make the default for parent_id NULL. Gets threads user is allowed to view, replace <thisuserid> with user id
SELECT DISTINCT(COALESCE(parent_id, id)) thread_id FROM message m WHERE from_user_id = <thisuserid> OR to_user_id = <thisuserid>
Get whole thread, including duplicates when sending to many recipients since i cant think of a fool proof way to filter them out as they are stored as separate messages. replace <thisuserid> with user id
SELECT * from message m WHERE id = <threadid> OR parent_id = <threadid>
Although, i would totally separate the recipient from the message itself, not only to make querying the whole chain easier, but also to save space. They way you do it now, every new recipient of a message increases the storage required by an amount equal to the size of the message, which can get out of hand very quickly.
CREATE TABLE IF NOT EXISTS `message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`from_user_id` int(11) NOT NULL,
`content` text NOT NULL,
`parent_id` int(11),
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `message_to` (
`message_id` int(11) NOT NULL,
`recipient_id` int(11) NOT NULL,
`read_flag` int(11) NOT NULL DEFAULT '0',
`status` tinyint(1) DEFAULT NULL,
`delete_flag` tinyint(1) NOT NULL DEFAULT '0',
UNIQUE KEY (`message_id`, `recipient_id`)
);
INSERT INTO message VALUES (1, 2, 'hai', null), (2, 3, 'hi', 1), (3, 3, 'hwru', 1), (4, 4, 'u added', 1), (5, 4, 'new msg', null);
INSERT INTO message_to (`message_id`, `recipient_id`) VALUES (1,3), (2,2), (3,2), (3,4), (4,5), (5,5);
Get threads user is allowed to view
SET #user := 2;
SELECT DISTINCT(COALESCE(parent_id, id)) thread_id FROM message m WHERE id IN (
SELECT message_id as id FROM message_to WHERE recipient_id = #user
union
SELECT id from message where from_user_id = #user
);
Get whole thread
SELECT * FROM message m WHERE m.id = 1 OR m.parent_id = 1
Your storage type is called Adjacency list, i.e. just store immediate parent id in parent_id column.
To query node's children:
mysql> SELECT * FROM message m1 INNER JOIN message m2 ON m2.parent_id = m1.id WHERE m1.id = 1;
+----+--------------+------------+---------+-----------+----+--------------+------------+---------+-----------+
| id | from_user_id | to_user_id | content | parent_id | id | from_user_id | to_user_id | content | parent_id |
+----+--------------+------------+---------+-----------+----+--------------+------------+---------+-----------+
| 1 | 2 | 3 | hai | 0 | 2 | 3 | 2 | hi | 1 |
| 1 | 2 | 3 | hai | 0 | 3 | 3 | 2 | hwru | 1 |
| 1 | 2 | 3 | hai | 0 | 4 | 3 | 4 | hwru | 1 |
| 1 | 2 | 3 | hai | 0 | 5 | 4 | 5 | u added | 1 |
+----+--------------+------------+---------+-----------+----+--------------+------------+---------+-----------+
4 rows in set (0.00 sec)
If you would like a flat structure, you can do the following query:
mysql> select * from message m WHERE id = 1 OR parent_id = 1;
+----+--------------+------------+---------+-----------+
| id | from_user_id | to_user_id | content | parent_id |
+----+--------------+------------+---------+-----------+
| 1 | 2 | 3 | hai | 0 |
| 2 | 3 | 2 | hi | 1 |
| 3 | 3 | 2 | hwru | 1 |
| 4 | 3 | 4 | hwru | 1 |
| 5 | 4 | 5 | u added | 1 |
+----+--------------+------------+---------+-----------+
5 rows in set (0.00 sec)
Adjacency list has serious drawbacks: it's hard to query deeply nested trees (we're querying only immediate children of message #1 here).
Please, take a look at linked question and also this excellent presentation by Bill Karwin for other options.

Merging rows in SQL for similar IDs?

I've got an interesting dilemma now. I have a database schema like the following:
GameList:
+-------+----------+-----------+------------+--------------------------------+
| id | steam_id | origin_id | impulse_id | game_title |
+-------+----------+-----------+------------+--------------------------------+
| 1 | 17450 | NULL | NULL | Dragon Age: Origins |
| 2 | NULL | 138994900 | NULL | Dragon Age(TM): Origins |
| 3 | NULL | NULL | dragonage | Dragon Age Origins |
| 4 | 47850 | 201841300 | fifamgr11 | FIFA Manager 11 |
| ... | ... | ... | ... | ... |
+-------+----------+-----------+------------+--------------------------------+
GameAlias:
+----------+-----------+
| old_id | new_id |
+----------+-----------+
| 2 | 1 |
| 3 | 1 |
| ... | ... |
+----------+-----------+
Depending on whether the stores use the same title for the game there may be no issues, or there may be multiple rows for the same game. The Alias table exists to resolve this issue, by stating that id 2 and id 3 are just aliases for id 1.
What I need is an SQL query which uses both the GameList table and the GameAlias table and returns the following:
ConglomerateGameList:
+-------+----------+-----------+------------+--------------------------------+
| id | steam_id | origin_id | impulse_id | game_title |
+-------+----------+-----------+------------+--------------------------------+
| 1 | 17450 | 138994900 | dragonage | Dragon Age: Origins |
| 4 | 47850 | 201841300 | fifamgr11 | FIFA Manager 11 |
| ... | ... | ... | ... | ... |
+-------+----------+-----------+------------+--------------------------------+
Note that I want the game title of the "new id". The game title for any "old ids" should simply be discarded/ignored.
I would also like to note that I can't make any modifications to the GameList table to solve this issue. If I were to simply re-write the table to look like my desired output then every night when I grab an updated game list from the stores it would fail to find the game in the database, generating yet another row like so:
+-------+----------+-----------+------------+--------------------------------+
| id | steam_id | origin_id | impulse_id | game_title |
+-------+----------+-----------+------------+--------------------------------+
| 1 | 17450 | 138994900 | dragonage | Dragon Age: Origins |
| 4 | 47850 | 201841300 | fifamgr11 | FIFA Manager 11 |
| ... | ... | ... | ... | ... |
| 8139 | NULL | 138994900 | NULL | Dragon Age(TM): Origins |
| 8140 | NULL | NULL | dragonage | Dragon Age Origins |
+-------+----------+-----------+------------+--------------------------------+
I also can't work on the assumption that a game's id will never change as Steam has been known to change them when a major update to the game is released.
Bonus points if it can recognize recursive aliases, like the following:
GameAlias:
+----------+-----------+
| old_id | new_id |
+----------+-----------+
| 2 | 1 |
| 3 | 2 |
| ... | ... |
+----------+-----------+
Since id 3 is an alias for id 2 which itself is an alias for id 1. If recursive aliases is impossible then I can just develop my application logic to prevent them.
Does this work? Correct the table names.
select ga1.new_id, max(gl1.steam_id), max(gl1.origin_id), max(gl1.impulse_id),
max(if(gl1.id = ga1.new_id,gl1.game_title,NULL)) as game_title
from gl1, ga1
where (gl1.id = ga1.new_id OR gl1.id = ga1.old_id)
group by ga1.new_id
union
select gl2.id, gl2.steam_id, gl2.origin_id, gl2.impulse_id, gl2.game_title
from gl2, ga2
where (gl2.id not in (
select ga3.new_id from ga3
union
select ga4.old_id from ga4))
1.First solution (without recursion):
CREATE TABLE GameList
(
id INT NOT NULL PRIMARY KEY
,steam_id INT NULL
,origin_id INT NULL
,impulse_id NVARCHAR(50) NULL
,game_title NVARCHAR(50) NOT NULL
);
INSERT GameList(id, steam_id, origin_id, impulse_id, game_title)
SELECT 1, 17450, NULL, NULL, 'Dragon Age: Origins'
UNION ALL
SELECT 2, NULL, 138994900, NULL, 'Dragon Age(TM): Origins'
UNION ALL
SELECT 3, NULL, NULL, 'dragonage','Dragon Age Origins'
UNION ALL
SELECT 4, 47850, 201841300, 'fifamgr11','FIFA Manager 11';
CREATE TABLE GameAlias
(
old_id INT NOT NULL PRIMARY KEY
,new_id INT NOT NULL
);
INSERT GameAlias (old_id, new_id) VALUES (2,1);
INSERT GameAlias (old_id, new_id) VALUES (3,1);
-- Solution 1
SELECT COALESCE(ga.new_id, gl.id) new_id
,MAX(gl.steam_id) new_steam_id
,MAX(gl.origin_id) new_origin_id
,MAX(gl.impulse_id) new_impulse_id
,MAX( CASE WHEN ga.old_id IS NULL THEN gl.game_title ELSE NULL END ) new_game_title
FROM GameList gl
LEFT OUTER JOIN GameAlias ga ON gl.id = ga.old_id
GROUP BY COALESCE(ga.new_id, gl.id);
-- End of Solution 1
DROP TABLE GameList;
DROP TABLE GameAlias;
Results:
1 17450 138994900 dragonage Dragon Age: Origins
4 47850 201841300 fifamgr11 FIFA Manager 11
2.Second solution (levels of recursion = three levels):
CREATE TABLE GameList
(
id INT NOT NULL PRIMARY KEY
,steam_id INT NULL
,origin_id INT NULL
,impulse_id NVARCHAR(50) NULL
,game_title NVARCHAR(50) NOT NULL
);
INSERT GameList(id, steam_id, origin_id, impulse_id, game_title)
SELECT 1, 17450, NULL, NULL, 'Dragon Age: Origins'
UNION ALL
SELECT 2, NULL, 138994900, NULL, 'Dragon Age(TM): Origins'
UNION ALL
SELECT 3, NULL, NULL, 'dragonage','Dragon Age Origins'
UNION ALL
SELECT 4, 47850, 201841300, 'fifamgr11','FIFA Manager 11'
UNION ALL
SELECT 5, 11111, NULL, NULL, 'Starcraft 1'
UNION ALL
SELECT 6, NULL, 1111111111, NULL, 'Starcraft 1.1'
UNION ALL
SELECT 7, NULL, NULL, NULL, 'Starcraft 1.2'
UNION ALL
SELECT 8, NULL, NULL, 'sc1', 'Starcraft 1.3';
CREATE TABLE GameAlias
(
old_id INT NOT NULL PRIMARY KEY
,new_id INT NOT NULL
);
INSERT GameAlias (old_id, new_id) VALUES (2,1);
INSERT GameAlias (old_id, new_id) VALUES (3,1);
INSERT GameAlias (old_id, new_id) VALUES (6,5);
INSERT GameAlias (old_id, new_id) VALUES (7,6);
INSERT GameAlias (old_id, new_id) VALUES (8,7);
-- Solution 2
CREATE TEMPORARY TABLE Mappings
(
old_id INT NOT NULL PRIMARY KEY
,new_id INT NOT NULL
);
INSERT Mappings (old_id, new_id)
-- first level mapping
SELECT ga.old_id, ga.new_id
FROM GameAlias ga
WHERE ga.new_id NOT IN (SELECT t.old_id FROM GameAlias t)
-- second level mapping
UNION ALL
SELECT ga.old_id, ga2.new_id
FROM GameAlias ga
INNER JOIN GameAlias ga2 ON ga.new_id = ga2.old_id
WHERE ga2.new_id NOT IN (SELECT t.old_id FROM GameAlias t)
-- third level mapping
UNION ALL
SELECT ga.old_id, ga3.new_id
FROM GameAlias ga
INNER JOIN GameAlias ga2 ON ga.new_id = ga2.old_id
INNER JOIN GameAlias ga3 ON ga2.new_id = ga3.old_id;
SELECT COALESCE(ga.new_id, gl.id) new_id
,MAX(gl.steam_id) new_steam_id
,MAX(gl.origin_id) new_origin_id
,MAX(gl.impulse_id) new_impulse_id
,MAX( CASE WHEN ga.old_id IS NULL THEN gl.game_title ELSE NULL END ) new_game_title
FROM GameList gl
LEFT OUTER JOIN Mappings ga ON gl.id = ga.old_id
GROUP BY COALESCE(ga.new_id, gl.id);
DROP TEMPORARY TABLE Mappings;
-- End of Solution 2
DROP TABLE GameList;
DROP TABLE GameAlias;
Results:
1 17450 138994900 dragonage Dragon Age: Origins
4 47850 201841300 fifamgr11 FIFA Manager 11
5 11111 1111111111 sc1 Starcraft 1
I'm sorry, but MySQL doesn't has recursive queries/CTEs.