Merging rows in SQL for similar IDs? - mysql

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.

Related

How to add a set of different values in one column(MySQL)?

Do not judge strictly, but I can not figure it out in any way
My table:
CREATE table courses (id INT PRIMARY KEY AUTO_INCREMENT,
-> faculty VARCHAR(55) NULL,
-> number INT(10) NULL,
-> diff VARCHAR(10) NULL);
mysql> select * from courses;
Target. Inject values ('ez', 'mid', 'hard') into diff column.
For exampl, im trying this:
mysql> INSERT courses (diff) VALUES ('ez');
OR
mysql> UPDATE courses SET faculty = 'chem', number = 2, diff = 'mid';
Add rows with empty id(values NULL).
PLZ help me!
I want to get this result
+----+---------+--------+------+
| id | faculty | number | diff |
+----+---------+--------+------+
| 1 | bio | 1 | ez |
| 2 | chem | 2 | mid |
| 3 | math | 3 | hard |
| 4 | geo | 4 | mid |
| 5 | gum | 5 | ez |
+----+---------+--------+------+
You can use a case expression in an UPDATE statements:
UPDATE courses
SET diff=CASE
WHEN faculty in ('bio', 'gum') THEN 'ez'
WHEN faculty in ('chem', 'geo') THEN 'mid'
WHEN faculty = 'math' THEN 'hard'
END;

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: Update using select and local variable

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.

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.

Mysql query of multiple tables joined

I have the following tables:
machine_machine
id | machineid
1 | EE100034442
item_item
id | upc | name
2 | 10001 | Snickers
machine_setup
id | machine_id | selection | item_id
3 | 1 | A1 | 1
Im trying to get the following output by joining the tables.
machine_setup.machine_id=machine_machine.machineid, machine_setup.selection, item_item.upc, item_item.name
EE100034442 A1 10001 Snickers
Table machine_setup will by the main referenced table as it has multiple selection for each machine_id.
Based on the only id's I can see at the moment to join on, consider this:
create table machine_machine
( id int auto_increment primary key,
machineid varchar(50) not null
);
create table item_item
( id int auto_increment primary key,
upc varchar(30) not null,
name varchar(100) not null
);
create table machine_setup
( id int auto_increment primary key,
machine_id int not null,
selection varchar(30) not null
);
insert machine_machine(machineid) values ('EE100034442');
insert item_item(upc,name) values ('10001','Snickers');
insert machine_setup(machine_id,selection) values (1,'A1'),(1,'A2'),(1,'A(n)');
select mm.machineid,ms.selection,ii.upc,ii.name
from machine_setup ms
join machine_machine mm
on mm.id=ms.machine_id
join item_item ii
on ii.id=ms.machine_id;
+-------------+-----------+-------+----------+
| machineid | selection | upc | name |
+-------------+-----------+-------+----------+
| EE100034442 | A1 | 10001 | Snickers |
| EE100034442 | A2 | 10001 | Snickers |
| EE100034442 | A(n) | 10001 | Snickers |
+-------------+-----------+-------+----------+
I'm not quite sure I understand the question, but the sql you want is like;
Select machine1.machineid, setup.Selection, item.upc, item.name
From Machine_machine machine1 --Set alias for the table
Inner Join machine_setup setup on setup.machine_id = machine1.id --This looks like a link table to me
Inner Join item_item item on setup.item_id = item.id -- in your example this wouldn't link as item_id is 1 in the machine_setup
In your example the machine_setup item_id is set to 1, which means it wouldn't link to the item_item table. i'm assuming this is a mistake.
Let me know if you need more information.