I have a Table "Teaches". This has a PK and a Name.
mysql> Select * from Teacher;
+-----+---------+
| TID | Tname |
+-----+---------+
| 1 | RC |
| 2 | Dinesha |
| 3 | JLT |
+-----+---------+
I have another Table "TeachesSubject". It has _surrid which is a PK, the TID references Teacher and SName is the Name of the subject taught.
mysql> select * from TeachesSubject;
+---------+------+-------+
| _surrid | TID | SNAME |
+---------+------+-------+
| 100 | 1 | DBMS |
| 101 | 1 | DM |
| 102 | 2 | DBMS |
| 103 | 2 | OOAD |
| 104 | 3 | OOAD |
| 105 | 3 | SE |
+---------+------+-------+
These two tables are already populated. I now run a query in TeachesSubject that points out the SNAMEs with duplicate values.
mysql> select SNAME from TeachesSubject group by SNAME having count(*)>1;
+-------+
| SNAME |
+-------+
| DBMS |
| OOAD |
+-------+
Once I detect this, I want my sql script to create a new table:
Teaches
_surrId TID
100 1
101 1
100 2
103 2
103 3
105 3
My old Teaches Subject Must look like
mysql> select * from TeachesSubject;
+---------+------+
| _surrid | SNAME |
+---------+-------+
| 100 | DBMS |
| 101 | DM |
| 103 | OOAD |
| 105 | SE |
+---------+------+
How Do I do this?
Here's how you can do it:
CREATE TABLE TeachesSubject2
(`_surrid` int, `SNAME` varchar(4))
;
INSERT INTO TeachesSubject2
SELECT MIN(_surrid), sname
FROM TeachesSubject
GROUP BY sname
;
CREATE TABLE Teaches
(`_surrid` int, `TID` int)
;
INSERT INTO Teaches
SELECT t2._surrid, t1.tid
FROM TeachesSubject AS t1
LEFT OUTER JOIN TeachesSubject2 AS t2
ON t1.sname = t2.sname
;
Working example is here: http://sqlfiddle.com/#!9/13d417/2
In future it would help if you created a fiddle yourself and attempted to write the SQL yourself. Then I wouldn't have to spend my time setting up for the answer, I could just fix whatever problem you are having.
Related
I read that the output of the subquery doesn't matter and only its existence matter. But, when I change the code in the subquery, why is my output changing?
These are the tables:
mysql> select * from boats;
+------+-----------+-------+
| bid | bname | color |
+------+-----------+-------+
| 101 | Interlake | blue |
| 102 | Interlake | red |
| 103 | Clipper | green |
| 104 | Marine | red |
+------+-----------+-------+
mysql> select * from sailors;
+------+---------+--------+------+
| sid | sname | rating | age |
+------+---------+--------+------+
| 22 | Dustin | 7 | 45 |
| 29 | Brutus | 1 | 33 |
| 31 | Lubber | 8 | 55.5 |
| 32 | Andy | 8 | 25.5 |
| 58 | Rusty | 10 | 35 |
| 64 | Horatio | 7 | 35 |
| 71 | Zorba | 10 | 16 |
| 74 | Horatio | 9 | 40 |
| 85 | Art | 3 | 25.5 |
| 95 | Bob | 3 | 63.5 |
+------+---------+--------+------+
10 rows in set (0.00 sec)
mysql> select * from reserves;
+------+------+------------+
| sid | bid | day |
+------+------+------------+
| 22 | 101 | 1998-10-10 |
| 22 | 102 | 1998-10-10 |
| 22 | 103 | 1998-10-08 |
| 22 | 104 | 1998-10-08 |
| 31 | 102 | 1998-11-10 |
| 31 | 103 | 1998-11-06 |
| 31 | 104 | 1998-11-12 |
| 64 | 101 | 1998-09-05 |
| 64 | 102 | 1998-09-08 |
| 74 | 103 | 1998-09-08 |
+------+------+------------+
select sname from sailors s where exists(select * from reserves r where r.bid=103);
+---------+
| sname |
+---------+
| Dustin |
| Brutus |
| Lubber |
| Andy |
| Rusty |
| Horatio |
| Zorba |
| Horatio |
| Art |
| Bob |
+---------+
10 rows in set (0.00 sec)
mysql> select sname from sailors s where exists(select * from reserves r where r.bid=103 and r.sid=s.sid);
+---------+
| sname |
+---------+
| Dustin |
| Lubber |
| Horatio |
+---------+
Also, I am not able to understand what r.sid=s.sid is doing here. All the sid in reserves are already from sailors table. Please someone explain it to me.
The EXISTS is a Boolean Operator which indicates that if there is ANY row in the sub-query you passed to it. When you execute this:
EXISTS(SELECT * FROM reserves r WHERE r.bid=103)
It will return TRUE after finding the FIRST row which has the condition bid = 103 in Reserves table. The first part of the query doesn't matter, it does not matter what you SELECT in Exists and MySQL engine will ignore it, just the WHERE clause is the part which makes the difference, you can use Exists even like this:
EXISTS(SELECT 1 FROM reserves r WHERE r.bid=103)
In the query above, nothing depends on the values in main query, nothing depends on Sailors table, and if there is ANY row in the Reserves table with bid = 103, then it always will return TRUE.
In the second sub-query with EXISTS, you have a different WHERE clause, and it depend on the value of the fields of the main Query, so it will have different result per each row:
EXISTS(SELECT * FROM reserves r WHERE r.bid=103 AND r.sid=s.sid)
In the above query, per each row in Sailors table, MySQL uses sid value to produce the WHERE condition of the sub-query in EXISTS operator, so it will returns TRUE for a row in Sailors table if there are ANY rows in Reserves table which has a bid = 103 and sid = Sailors.sid, and it will returns False for those that has not such a record in Reserves table, and finally you will get a different result
I think I got that. Exists is used to check if the subquery is existing for the main query. I didn't give any link for the main query and subquery in the first query.
For every name in sailors, independently, the subquery is existing. Hence, I got all the names. In the second query, I added s.sid=r.sid which links the main query and subquery. It checks if for a sname, if bid=103, and also, if s.sid=r.sid.
Please comment if I got that right.
I am doing a mini project using MySQL. I came with the following problem:
I created 2 tables, student and book with 6 and 5 columns respectively.
mysql> select * from book;
+--------+------+------------+---------+------+
| bookid | Name | Authorname | edition | cost |
+--------+------+------------+---------+------+
| cc12 | dbms | guna | 5 | 500 |
| cc34 | CA | fasil | 5 | 600 |
| cs113 | OS | rohan | 3 | 300 |
| cs12 | AI | ganesh | 2 | 1000 |
| cs343 | c# | jackesh | 4 | 300 |
+--------+------+------------+---------+------+
5 rows in set (0.00 sec)
mysql> select * from studentbook;
+-----+--------+-----------+
| Sno | bookid | Studid |
+-----+--------+-----------+
| 1 | cc12 | 14vec1088 |
| 2 | cs113 | 14vec1099 |
| 3 | cc34 | 14vec1132 |
| 4 | cs343 | 14vec2011 |
| 5 | cs12 | 14vec100 |
+-----+--------+-----------+
5 rows in set (0.00 sec)
Now, when I enter any of the studid mentioned in the table studentbook (This is performed by PHP in the backend) it should display the details of book associated with the respective studid from the table book.
How can I perform the above using MySQL Query?
This might be work.
SELECT * FROM book
WHERE bookid IN
(SELECT bookid FROM studentbook
WHERE studid = "[Id of which you want book]");
This should get you what you need.
SELECT b.*
FROM book b
INNER JOIN studentbook sb on b.bookid = sb.bookid
WHERE sb.Studid = [your id]
I'm fairly new to sql. This might be basic. I have two tables one with groups and one with members, I want to link them up so that a third table contains id_group and id_member. The value MYGROUP is supplied during the import. I tried this:
insert ignore into member_group (id_group, id_member)
values ( ( select id_group from group where group_name='MYGROUP' ) ,
( select id_member from member ) );
But I end up with one row in member_group containing a null value.
on it's own this yields 1 for example:
select id_group from group where group_name='MYGROUP';
+----------+
| id_group |
+----------+
| 1 |
+----------+
on it's own this yields a list of id_members
mysql> select id_member from member;
+-----------+
| id_member |
+-----------+
| 123 |
| 456 |
| 789 |
I want member_group to then look like this
+-----------+----------+
| id_group |id_member |
+-----------+----------+
| 1 | 123 |
| 1 | 456 |
| 1 | 789 |
How can I do this (without resorting to shell scripts, for loops and sed) ?
As requested,
mysql> select * from group;
+----------+------------------+
| id_group | group_name |
+----------+------------------+
| 1 | vip-member |
| 2 | standard-member |
mysql> select * from member;
+-----------+----------+
| id_member | fullname |
+-----------+----------+
| 123 | Bob |
| 456 | Pete |
Which, if I could get it working, should look like below.
mysql> select * from member_group;
+------------------+----------+-----------+
| id_member_groups | id_group | id_member |
+------------------+----------+-----------+
| 1 | 1 | 123 |
| 2 | 1 | 456 |
| 3 | 1 | 789 |
| 4 | 2 | 123 |
| 5 | 2 | 789 |
id_group is supplied during the import phase. One batch of say 200 members, will be members of the same id_group. I was thinking about adding the group_id to a temporary table. But I'm a tad lost to be honest.
You can do a cross join to achieve your goal.
insert ignore into member_group (id_group, id_member)
select member_group.id_group, member.id_member
from member_group
cross join member
where group_name = 'MYGROUP';
I have a table that has approximately 4 million records. I would like to make it have 240 million like so:
Add an additional column of type BIGINT,
Import 59 times the data I already have,
And for each 4 million group of records, have the additional column to have a different value
The value of the additional column would come from another table.
So I have these records (except that I have 4 millions of them and not just 3):
| id | value |
+----+-------+
| 1 | 123 |
| 2 | 456 |
| 3 | 789 |
And I want to achieve this (except that I want 60 copies and not just 3):
| id | value | data |
+----+-------+------+
| 1 | 123 | 1 |
| 2 | 456 | 1 |
| 3 | 789 | 1 |
| 4 | 123 | 2 |
| 5 | 456 | 2 |
| 6 | 789 | 2 |
| 7 | 123 | 3 |
| 8 | 456 | 3 |
| 9 | 789 | 3 |
I tried to export my data (using SELECT .. INTO OUTFILE ...), then re-import it (using LOAD DATA INFILE ...) but it is really painfully slow.
Is there a fast way to do this?
Thank you!
Sounds like you'd like to take the cartesian product of 2 tables and create a new table since you say The value of the additional column would come from another table? If so, something like this should work:
create table yourtable (id int, value int);
create table yournewtable (id int, value int, data int);
create table anothertable (data int);
insert into yourtable values (1, 123), (2, 456), (3, 789);
insert into anothertable values (1), (2), (3);
insert into yournewtable
select t.id, t.value, a.data
from yourtable t, anothertable a
SQL Fiddle Demo
Results:
ID VALUE DATA
1 123 1
2 456 1
3 789 1
1 123 2
2 456 2
3 789 2
1 123 3
2 456 3
3 789 3
Edit, Side Note -- it looks like your ID field in your new table is not suppose to keep repeating the same ids? If so, you can use an AUTO_INCREMENT field instead. However, this could mess up the original rows if they aren't sequential.
First, I would recommend that you create a new table. You can do this using a cross join:
create table WayBigTable as
select t.*, n
from table t cross join
(select 1 as n union all select 2 union all select 3 union all select 4 union all select 5 union all
. . .
select 60
) n;
I'm not sure why you would want a bigint for this column. If you really need that, you can cast to unsigned.
Hmm. You need a cross join of your table with a range. Something in a line of this:
INSERT INTO table (id,value,data) SELECT id, value from table
CROSS JOIN (SELECT 2 UNION SELECT 3 UNION ... SELECT 60) AS data;
Use this answer Generating a range of numbers in MySQL as reference on number range.
Here's one idea...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,value INT NOT NULL
);
INSERT INTO my_table VALUES
(1 ,123),
(2 ,456),
(3 ,789);
ALTER TABLE my_table ADD COLUMN data INT NOT NULL DEFAULT 1;
SELECT * FROM my_table;
+----+-------+------+
| id | value | data |
+----+-------+------+
| 1 | 123 | 1 |
| 2 | 456 | 1 |
| 3 | 789 | 1 |
+----+-------+------+
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
INSERT INTO my_table SELECT NULL,value,data+i2.i*10+i1.i+1 FROM my_table,ints i1,ints i2;
SELECT * FROM my_table;
+-----+-------+------+
| id | value | data |
+-----+-------+------+
| 1 | 123 | 1 |
| 2 | 456 | 1 |
| 3 | 789 | 1 |
| 4 | 123 | 2 |
| 5 | 456 | 2 |
| 6 | 789 | 2 |
| 7 | 123 | 3 |
| 8 | 456 | 3 |
...
...
| 296 | 456 | 97 |
| 297 | 789 | 97 |
| 298 | 123 | 98 |
| 299 | 456 | 98 |
| 300 | 789 | 98 |
| 301 | 123 | 99 |
| 302 | 456 | 99 |
| 303 | 789 | 99 |
+-----+-------+------+
303 rows in set (0.00 sec)
Note, for 240 million rows, this is still going to be a bit slow :-(
Let's say we have the table like
id | group_id | TEXT |
--------------------------------------
1 | | NBA
--------------------------------------
2 | | NHL
--------------------------------------
3 | | NBA
--------------------------------------
4 | | NHL
--------------------------------------
5 | | NHL
--------------------------------------
Is it possible to create group_id with a MySQL function or query or anything :) using the fact of repeating (duplicate) text in the column TEXT ? to have a table like that
id | group_id | TEXT |
--------------------------------------
1 | 10 | NBA
--------------------------------------
2 | 11 | NHL
--------------------------------------
3 | 10 | NBA
--------------------------------------
4 | 11 | NHL
--------------------------------------
5 | 11 | NHL
--------------------------------------
You could try to use HEX function:
SELECT id, HEX(Text) as group_id, Text
FROM Tbl
If you want decimal value, you can conver it from hex:
SELECT id, CONV(HEX(Text), 16, 10) as group_id, Text
FROM Tbl
Result:
ID GROUP_ID TEXT
1 5128769 NBA
2 5128769 NBA
3 5130316 NHL
4 5130312 NHH
5 5130316 NHL
8 4342081 BAA
9 4342081 BAA
SQLFiddle
CREATE TEMPORARY TABLE nums (n int AUTO_INCREMENT PRIMARY KEY, txt varchar(4)) AUTO_INCREMENT=10;
INSERT INTO nums (txt) SELECT DISTINCT text FROM table1;
UPDATE table1 INNER JOIN nums ON txt=text SET group_id=n
SQLfiddle
Looking at my old answer I just thought of another way to solve the problem without a tmp table:
CREATE Table tbl (id int,grpid int, text varchar(10));
INSERT INTO tbl (id,text) VALUES (1,'NBA'),(2,'NBA'),
(3,'NHL'),(4,'NHH'),(5,'NHL'),(8,'BAA'),(9,'BAA');
SET #i:=100; -- set the start sequence number for grpid
UPDATE tbl INNER JOIN (
SELECT #i:=#i+1 gid,text FROM (
SELECT DISTINCT text FROM tbl ORDER BY text ) dt ) gi
ON tbl.text=gi.text
SET tbl.grpid=gi.gid;
tbl after the UPDATE:
| ID | GRPID | TEXT |
---------------------
| 1 | 102 | NBA |
| 2 | 102 | NBA |
| 3 | 104 | NHL |
| 4 | 103 | NHH |
| 5 | 104 | NHL |
| 8 | 101 | BAA |
| 9 | 101 | BAA |
sqlfiddle