SQL - Foreign Key and Primary Key not Matching - mysql

I am new to SQL and I am running into trouble.
I have 3 tables:
CREATE TABLE indexCodes
{
(indexNum VARCHAR(5) PRIMARY KEY,
courseString VARCHAR(10),
title VARCHAR(20)
}
CREATE TABLE user
{
(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL)
}
CREATE TABLE snipes
{
(snipeNumber INT NOT NULL PRIMARY KEY AUTO_INCREMENT),
FOREIGN KEY indexNum REFERENCES indexcodes(indexNum),
FOREIGN KEY userID REFERENCES user(id)
}
and inserting into snipes with
INSERT INTO snipes(indexNum, userID) VALUES ("06666", 1);
INSERT INTO snipes(indexNum, userID) VALUES ("06675", 1);
When I run
SELECT * FROM snipes, user, indexcodes where id=1
two indexNum columns appear with two different values, I am assuming that the snipes indexNum column shows whatever was inserted but the indexCodes indexNum displays a different result (I believe it is the first two entries in indexCodes)

When you put all the tables in the FROM clause divided only by comma without any restrictions of joining the tables with a WHERE clause you get a cartesian product of all the tables referenced in the FROM clause, for example, or using JOIN syntax instead of putting all the tables int the FROM clause. Here is two examples that would work on your scenario:
WHERE clause
SELECT * FROM snipes s, t_user u, indexcodes i where
s.indexNum = i.indexnum
and s.userid = u.id
and id=1;
JOIN syntax
SELECT * FROM snipes s
inner join t_user u on s.userid = u.id
inner join indexcodes i on s.indexNum = i.indexnum
where id=1;
Personally, I prefer to use the join syntax, it's a cleaner way to see the query.

Related

SQL Chaining joins

EDIT: The isssue was mismatching data the B-ids in tables A_B and B_C had no overlap
I have the following tables:
A(id, …)
A_B(a_id, b_id)
B_C(b_id, c_id)
C_D(c_id, d_id)
D(id, value, …)
Where I want some rows from A based on the value of the row in D.
The tables A_B, B_C and C_D are just id mappings from one table to another.
I was trying to get to something that would look something like this:
select * from A where D.value = "true"
I got this far:
select * from A
inner join A_B on A_B.a_id = A.id
inner join B_C on B_C.b_id = A_B.b_id
which is just an empty table.
I am starting to think that I am approaching this issue in the wrong way or perhaps have misunderstood how one should go about joining tables.
What you've got works OK if your data is coherent. Here's a version of your database and query which demonstrates that. For simplicity, I inferred the existence of tables b and c, and made the various columns in the X_Y style tables into foreign keys to the single-letter tables.
CREATE TABLE a
(
id INTEGER NOT NULL PRIMARY KEY,
info VARCHAR(20) NOT NULL
);
CREATE TABLE b
(
id INTEGER NOT NULL PRIMARY KEY,
data VARCHAR(20) NOT NULL
);
CREATE TABLE C
(
id INTEGER NOT NULL PRIMARY KEY,
extra VARCHAR(20) NOT NULL
);
CREATE TABLE d
(
id INTEGER NOT NULL PRIMARY KEY,
value VARCHAR(20) NOT NULL
);
CREATE TABLE a_b
(
a_id INTEGER NOT NULL REFERENCES a,
b_id INTEGER NOT NULL REFERENCES b,
PRIMARY KEY (a_id, b_id)
);
CREATE TABLE b_c
(
b_id INTEGER NOT NULL REFERENCES b,
c_id INTEGER NOT NULL REFERENCES C,
PRIMARY KEY(b_id, c_id)
);
CREATE TABLE c_d
(
c_id INTEGER NOT NULL REFERENCES C,
d_id INTEGER NOT NULL REFERENCES d,
PRIMARY KEY(c_id, d_id)
);
INSERT INTO a VALUES(1, "Quasimodo");
INSERT INTO b VALUES(20, "Quiet");
INSERT INTO C VALUES(333, "Contemporaneous");
INSERT INTO d VALUES(4444, "true");
INSERT INTO a_b VALUES(1, 20);
INSERT INTO b_c VALUES(20, 333);
INSERT INTO c_d VALUES(333, 4444);
SELECT *
FROM a
JOIN a_b ON a_b.a_id = a.id
JOIN b_c ON b_c.b_id = a_b.b_id
JOIN c_d ON c_d.c_id = b_c.c_id
JOIN d ON d.id = c_d.d_id
WHERE d.value = "true";
For the given data, that produces:
1 Quasimodo 1 20 20 333 333 4444 4444 true
So, if the data is correct, the query you were building can produce an answer. However, if you were getting an empty table on your incomplete query, then there is a data problem in your tables — or (outside chance) your outline schema misled us.
Testing performed on a Mac running macOS High Sierra 10.13.4, using Informix 12.10.FC6, but using what is believed to be a subset of SQL common to Informix and MySQL.

Unknown column 'R.movie_name' in 'on clause' for Inner join with subquery

I tried to rename the table using join, but I'm running into the Unknown column 'R.movie_name' in 'on clause'. I referenced this question to correct, but I'm still get the same error.
What am I doing wrong?
My SQL query
SELECT R.movie_name, AVG(S.similarity*R.rating)
FROM Ratings AS R, Similarity S
INNER JOIN (
SELECT DISTINCT R.movie_name
FROM Ratings R, Users U
WHERE U.name = 'Adam Brody' AND U.user_id != R.user_id
) AS MoviesAdamDidntWatch ON R.movie_name = MoviesAdamDidntWatch.movie_name
GROUP BY R.movie_name;
My Tables
CREATE TABLE Users (user_id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL);
CREATE TABLE Ratings (user_id INTEGER NOT NULL,
rating INTEGER NOT NULL,
movie_name varchar(100) NOT NULL,
PRIMARY KEY(user_id, movie_name),
FOREIGN KEY (user_id) REFERENCES Users(user_id));
CREATE TABLE Similarity (user1_id INTEGER NOT NULL,
user2_id INTEGER NOT NULL,
similarity FLOAT,
PRIMARY KEY (user1_id, user2_id),
FOREIGN KEY (user1_id) REFERENCES Users(user_id),
FOREIGN KEY (user2_id) REFERENCES Users(user_id));
You have used R twice as Alias in your query . You can fix it by replacing outer R with some other alias. like -
SELECT Rtg.movie_name, AVG(S.similarity*Rtg.rating)
FROM Ratings AS Rtg, Similarity S
INNER JOIN (
SELECT DISTINCT R.movie_name
FROM Ratings R, Users U
WHERE U.name = 'Adam Brody' AND U.user_id != R.user_id
) AS MoviesAdamDidntWatch ON Rtg.movie_name = MoviesAdamDidntWatch.movie_name
GROUP BY Rtg.movie_name;
But your query is not optimised, Consider optimising it by removing redundant inner query and cross products.I may help you with it if you post your table structure.

MySQL : IF EXISTS THEN UPDATE ELSE INSERT from one table to another using IF ELSE statement

I have two tables user & user_tmp having primary key as id only. The fields of both the tables are likely as :
`id` PRIMARY AUTO_INCREMENT INT,
`Name` VARCHAR(80),
`Contact No` VARCHAR(10),
`Status` INT
I need a simple query based on IF EXISTS ON user_tmp table THEN update value in user ELSE insert in user table.
I tried with REPLACE & ON DUPLICATE KEY, but it din't work as id is the PRIMARY KEY and the value varies in both the tables.
Regards.
You need to do it as two queries. One query to update the records that match:
UPDATE user AS u
JOIN user_tmp AS t ON u.Contact_No = t.Contact_no
SET u.Name = t.Name, u.Status = t.Status;
and a second to insert the ones that don't already exist:
INSERT INTO user (Name, Contact_No, Status)
SELECT t.Name, t.Contact_No, t.Status
FROM user_tmp AS t
LEFT JOIN user AS u ON u.Contact_No = t.Contact_no
WHERE t.id IS NULL
See Return row only if value doesn't exist for an explanation of how the SELECT query in the INSERT works to find rows that don't match.

Using SQL Sub-queries in an INSERT Statement

Here are the two tables created:
CREATE TABLE category_tbl(
id int NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
subcategory varchar(255) NOT NULL,
PRIMARY KEY(id),
CONSTRAINT nameSubcategory UNIQUE KEY(name, subcategory)
) ENGINE=InnoDB;
CREATE TABLE device(
id INT NOT NULL AUTO_INCREMENT,
cid INT DEFAULT NULL,
name VARCHAR(255) NOT NULL,
received DATE,
isbroken BOOLEAN,
PRIMARY KEY(id),
FOREIGN KEY(cid) REFERENCES category_tbl(id)
) ENGINE=InnoDB;
Below is the instruction that was given to me:
-- insert the following devices instances into the device table (you should use a subquery to set up foriegn keys referecnes, no hard coded numbers):
-- cid - reference to name: phone subcategory: maybe a tablet?
-- name - Samsung Atlas
-- received - 1/2/1970
-- isbroken - True
I'm getting errors on the insert statement below from attempting to use a sub-query within an insert statement. How would you solve this issue?
INSERT INTO devices(cid, name, received, isbroken)
VALUES((SELECT id FROM category_tbl WHERE subcategory = 'tablet') , 'Samsung Atlas', 1/2/1970, 'True');
You have different table name in CREATE TABLE and INSERT INTO so just choose one device or devices
When insert date format use the good one like DATE('1970-02-01')
When insert boolean - just TRUE with no qoutes I beleive.
http://sqlfiddle.com/#!9/b7180/1
INSERT INTO devices(cid, name, received, isbroken)
VALUES((SELECT id FROM category_tbl WHERE subcategory = 'tablet') , 'Samsung Atlas', DATE('1970-02-01'), TRUE);
It's not possible to use a SELECT in an INSERT ... VALUES ... statement. The key here is the VALUES keyword. (EDIT: It is actually possible, my bad.)
If you remove the VALUES keyword, you can use the INSERT ... SELECT ... form of the INSERT statement statement.
For example:
INSERT INTO mytable ( a, b, c) SELECT 'a','b','c'
In your case, you could run a query that returns the needed value of the foreign key column, e.g.
SELECT c.id
FROM category_tbl c
WHERE c.name = 'tablet'
ORDER BY c.id
LIMIT 1
If we add some literals in the SELECT list, like this...
SELECT c.id AS `cid`
, 'Samsung Atlas' AS `name`
, '1970-01-02' AS `received`
, 'True' AS `isBroken`
FROM category_tbl c
WHERE c.name = 'tablet'
ORDER BY c.id
LIMIT 1
That will return a "row" that we could insert. Just precede the SELECT with
INSERT INTO device (`cid`, `name`, `received`, `isbroken`)
NOTE: The expressions returned by the SELECT are "lined up" with the columns in the column list by position, not by name. The aliases assigned to the expressions in the SELECT list are arbitrary, they are basically ignored. They could be omitted, but I think having the aliases assigned makes it easier to understand when we run just the SELECT portion.

Mysql query to get detail of comma-separated ids data

I have 2 tables, items and members :
CREATE TABLE IF NOT EXISTS `items` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`member` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `members` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
What if, for example I have a record inside items, such as
INSERT INTO `test`.`items` (
`id` ,
`name` ,
`member`
)
VALUES (
NULL , 'xxxx', '1, 2, 3'
);
in members :
INSERT INTO `members` (`id`, `name`) VALUES
(1, 'asdf'),
(2, 'qwert'),
(3, 'uiop'),
(4, 'jkl;');
and I'd like to display items.member data with members.name, something like 1#asdf, 2#qwert, 3#uiop??
I've tried the following query,
SELECT items.id, items.name, GROUP_CONCAT(CONCAT_WS('#', members.id, members.name) ) as member
FROM `items`
LEFT JOIN members AS members on (members.id = items.member)
WHERE items.id = 1
But the result is not like I expected. Is there any other way to display the data via one call query? Because I'm using PHP, right now, i'm explode items.member and loop it one by one, to display the members.name.
You could look into using FIND_IN_SET() in your join criteria:
FROM items JOIN members ON FIND_IN_SET(members.id, items.member)
However, note from the definition of FIND_IN_SET():
A string list is a string composed of substrings separated by “,” characters.
Therefore the items.member column should not contain any spaces (I suppose you could use FIND_IN_SET(members.id, REPLACE(items.member, ' ', '')) - but this is going to be extremely costly as your database grows).
Really, you should normalise your schema:
CREATE TABLE memberItems (
item_id INT(5) NOT NULL,
member_id INT(5) NOT NULL,
FOREIGN KEY item_id REFERENCES items (id),
FOREIGN KEY member_id REFERENCES members (id)
);
INSERT INTO memberItems
(item_id, member_id)
SELECT items.id, members.id
FROM items
JOIN members ON FIND_IN_SET(members.id, REPLACE(items.member,' ',''))
;
ALTER TABLE items DROP member;
This is both index-friendly (and therefore can be queried very efficiently) and has the database enforce referential integrity.
Then you can do:
FROM items JOIN memberItems ON memberItems.item_id = items.id
JOIN members ON members.id = memberItems.member_id
Note also that it's generally unwise to use GROUP_CONCAT() to combine separate records into a string in this fashion: your application should instead be prepared to loop over the resultset to fetch each member.
Please take a look at this sample:
SQLFIDDLE
Your query seems to work for what you have mentioned in the question... :)
SELECT I.ID, I.ITEM,
GROUP_CONCAT(CONCAT("#",M.ID,
M.NAME, " ")) AS MEMB
FROM ITEMS AS I
LEFT JOIN MEMBERS AS M
ON M.ID = I.MID
WHERE i.id = 1
;
EDITTED ANSWER
This query will not work for you¬ as your schema doesn't seem to have any integrity... or proper references. Plus your memeber IDs are delimtted by a comma, which has been neglected in this answer.