inner join not giving results as expected - mysql

Product_table
Product_table_link
This be the product_table data that is stored inside the database.
This is the product_table_link data that is present inside the DB.I was trying to join these two tables where the product code=something.For instance let us take xyz as the product.
I was hoping to get the combined results of the two without any nulls present.
I tried :
SELECT s1.* FROM (SELECT p1.* FROM product_table p1 INNER JOIN product_table_link p2
ON p1.product_code=p2.product_code ) s1 WHERE product_code="xyz"
But the result is not the combination of the both tables rather it shows me the product_table.
CREATE TABLE `product_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_name` varchar(60) NOT NULL,
`product_code` varchar(60) NOT NULL,
`product_description` text,
`product_type` varchar(20) NOT NULL,
`product_image_path` varchar(60) NOT NULL,
`product_company_name` varchar(20) NOT NULL,
`product_company_id` varchar(60) NOT NULL,
`product_landing_page` varchar(15) NOT NULL,
`product_shape` varchar(20) DEFAULT NULL,
`product_flavour` varchar(20) NOT NULL,
`product_veg_mark` varchar(8) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique` (`product_code`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
CREATE TABLE `product_table_link` (
`product_code` varchar(60) NOT NULL,
`product_weight` varchar(5) NOT NULL,
`product_price` int(5) NOT NULL,
`product_quantity` int(5) NOT NULL,
PRIMARY KEY (`product_code`,`product_weight`),
CONSTRAINT `product_table_link_ibfk_1` FOREIGN KEY (`product_code`) REFERENCES `product_table` (`product_code`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
How do i get the combined results of the two tables?

Change your subquery to this
SELECT * FROM product_table p1 INNER JOIN product_table_link p2
ON p1.product_code=p2.product_code
You were selecting just from p1.
You were doing select p1.* which will only select rows from p1. Inner join means you want to select rows from different tables on the basis of some conditions. You need to do select * which will select all the rows filtered from the join condition.
Your simplified final query.
SELECT s1.* FROM (SELECT p1.*,p2.product_weight,p2.product_price,p2.product_quantity
FROM product_table p1 INNER JOIN product_table_link p2
ON p1.product_code=p2.product_code)
s1 WHERE s1.product_code="xyz"

SELECT p1.* FROM product_table p1 INNER JOIN product_table_link p2
ON p1.product_code=p2.product_code
In the above subquery you are selecting values from p1 only. So, you have to change your query to what FallAndLearn has written.

Related

Mysql Inner Join a table with matching key column but null

I am trying to join 4 tables.
registration_mt, admission_mt, student_mt, and schoolyear_student_lt
Currently registration_mt, admission_mt and student_mt tables have 1 record each.
There are no problems inner-joining all three of them except for schoolyear_student_lt which has no records yet.
I want to be able to get the columns of schoolyear_student_lt and join it with the result set of the 3 other tables even when the matching key student_id HAS OR HAS-NO records
I want to add schoolyear_id, student_id, gradelevel_id, section_id, passedfrom schoolyear_student_lt to the result set I get from my join query where isActive = 0;
CREATE table statements
CREATE TABLE `registration_mt` (
`registration_id` int(11) NOT NULL AUTO_INCREMENT,
`student_type` varchar(45) NOT NULL,
PRIMARY KEY (`registration_id`)
) ;
CREATE TABLE `admission_mt` (
`admission_id` int(11) NOT NULL AUTO_INCREMENT,
`registration_id` int(11) NOT NULL,
`isComplete` bit(1) NOT NULL DEFAULT b'0',
`completion_date` datetime DEFAULT NULL,
PRIMARY KEY (`admission_id`),
UNIQUE KEY `registration_id_UNIQUE` (`registration_id`),
CONSTRAINT `fk_admission_mtTABLE_registration_idCOL` FOREIGN KEY (`registration_id`) REFERENCES `registration_mt` (`registration_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ;
CREATE TABLE `schoolyear_student_lt` (
`schoolyear_id` int(11) NOT NULL,
`student_id` int(11) NOT NULL,
`gradelevel_id` int(11) NOT NULL,
`section_id` int(11) DEFAULT NULL,
`passed` bit(1) DEFAULT b'0',
UNIQUE KEY `uk_schoolyear_idCOL_student_idCOL` (`schoolyear_id`,`student_id`)
);
CREATE TABLE `student_mt` (
`student_id` int(11) NOT NULL AUTO_INCREMENT,
`registration_id` int(11) NOT NULL,
`entry_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`isGraduated` bit(1) NOT NULL DEFAULT b'0',
`date_graduated` datetime DEFAULT NULL,
`isActive` bit(1) DEFAULT b'0' ,
PRIMARY KEY (`student_id`),
UNIQUE KEY `registration_id_UNIQUE` (`registration_id`),
KEY `fk_student_mtTABLE_registration_idCOL_idx` (`registration_id`),
CONSTRAINT `fk_student_mtTABLE_registration_idCOL` FOREIGN KEY (`registration_id`) REFERENCES `registration_mt` (`registration_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
);
My INNER-JOIN statement
SELECT
a.admission_id,
a.isComplete,
a.completion_date,
s.student_id,
s.entry_date,
s.isGraduated,
s.date_graduated,
s.isActive
FROM admission_mt a
INNER JOIN registration_mt r ON a.registration_id = r.registration_id
INNER JOIN student_mt s ON s.registration_id = a.registration_id
-- INNER JOIN schoolyear_student_lt sslt ON s.student_id = sslt.student_id
-- LEFT JOIN schoolyear_student_lt sslt ON s.student_id = sslt.student_id
WHERE
s.isActive = 0 ;
INSERT statements to registration_mt, admission_mt, and student_mt tables
START TRANSACTION;
INSERT INTO registration_mt(student_type) VALUES('New'); -- insert to registration_mt
INSERT INTO admission_mt(registration_id)
VALUES(LAST_INSERT_ID()); --insert to admissiont_mt
COMMIT;
START TRANSACTION;
UPDATE admission_mt
SET isComplete = 1
WHERE registration_id = 1;
INSERT INTO student_mt(registration_id)
VALUES(1); --insert to student_mt
COMMIT;
I commented out the -- INNER JOIN schoolyear_student_lt sslt ON s.student_id = sslt.student_id because I get no results when I include it in my select query since there are no records yet in schoolyear_student_lt table.
This is the current resultset I get. There are no columns from schoolyear_student_lt table which I wish to add even when NULL and even when NOT NULL on schoolyear_student_lt.student_id
LEFT-JOIN doesn't display the columns from schoolyear_student_lt when NULL
Thanks.
You should try the LEFT OUTER JOIN
The idea of an left outer join is that it takes the rows of left relation and fills the fields of the right relation with NULL if there is no join
partner.
There is also a right outer join...

How can I query for rows with latest date and do an inner join on a second table?

All the examples I've seen show how to do an inner join using an alias to get rows with the latest date. I can do that with my data but I also want to do an inner join on another table and can't figure how to do both with the same query.
Here are the two tables:
CREATE TABLE `titles` (
`titleID` int(11) unsigned NOT NULL AUTO_INCREMENT,
`titlename` tinytext NOT NULL,
`url` varchar(255) DEFAULT '',
`category` int(2) unsigned NOT NULL,
`postdate` date NOT NULL,
PRIMARY KEY (`titleID`),
KEY `category` (`category`),
CONSTRAINT `titles_ibfk_1` FOREIGN KEY (`category`) REFERENCES `categories` (`catid`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
CREATE TABLE `stats` (
`statid` int(11) unsigned NOT NULL AUTO_INCREMENT,
`score` decimal(3,2) DEFAULT NULL,
`views` int(11) unsigned DEFAULT NULL,
`favs` int(11) DEFAULT NULL,
`comments` int(11) DEFAULT NULL,
`updatedate` date NOT NULL,
`title` int(11) unsigned NOT NULL,
PRIMARY KEY (`statid`),
KEY `title` (`title`),
CONSTRAINT `stats_ibfk_1` FOREIGN KEY (`title`) REFERENCES `titles` (`titleID`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
My goals:
1) I want a query that gives me all the latest stats for each title.
2) I want to see the text name of the title (from the titles table).
I can use this query to get the latest score for each title.
select t.score, t.views, t.favs, t.comments, t.updatedate, t.title
from stats t
inner join (
select title, max(updatedate) as updatedate
from stats
GROUP BY title
) tm on t.title = tm.title and t.updatedate = tm.updatedate
But the problem with this query is that it displays the title column from stats which is an int. I want the text name of the title.
I can do this to get the title name and the score, but then I'm not getting the row with the latest date.
select titlename, score, updatedate
from stats
inner join titles
on titleid = title
How can I write a query that achieves both my goals?
You need to join the title table in this case as
select
s1.score,
s1.views,
s1.favs,
s1.comments,
s1.updatedate,
t.titlename
from titles t
join stats s1 on s1.title = t.titleID
join (
select title, max(updatedate) as updatedate
from stats
GROUP BY title
) s2 on s2.title = s1.title and s1.updatedate = s2.updatedate

How to optimize this query as the in array seems to slow things down significantly

I am looking to find out the best way to optimize a query like this:
SELECT
a.ID,
a.ECPCodeID,
a.RegDate,
a.BusName,
a.City,
a.AccountNum,
b.ID as RepCodeID,
b.RepCode
FROM ECPs_Registration a,
Reps_Codes b
WHERE (SUBSTR(a.PostalCode,1,5)IN(SELECT
SUBSTR(Zip,1,5)
FROM Reps_Zip
WHERE RepCodeID = b.ID)
AND a.AccountNum NOT IN(SELECT
ShipTo
FROM Reps_ShipTo))
OR a.AccountNum IN(SELECT
ShipTo
FROM Reps_ShipTo
WHERE RepCodeID = b.ID)
ORDER BY b.RepCode,a.BusName,a.City
I know there are more factors involved such as indexes and such, I just am asking about the query part of it for now. Mainly, since I have to go through the Reps_ShipTo and Reps_Zip tables for tons of records. I thought about changing something like:
a.AccountNum NOT IN (SELECT ShipTo FROM Reps_ShipTo)
INTO
(SELECT count(*) FROM Reps_ShipTo WHERE a.AccountNum = ShipTo) = 0
Not sure if that is proper or if there is a better way. Any help would be appreciated. Thanks.
EDIT:
Schema:
CREATE TABLE IF NOT EXISTS `ECPs_Codes` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`ECPCode` char(4) NOT NULL,
PRIMARY KEY (`ID`),
KEY `ECPCode` (`ECPCode`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
CREATE TABLE IF NOT EXISTS `ECPs_Registration` (
`RegDate` datetime NOT NULL,
`ID` int(10) NOT NULL AUTO_INCREMENT,
`ECPCodeID` int(11) NOT NULL,
`FirstName` varchar(200) NOT NULL,
`LastName` varchar(200) NOT NULL,
`BusName` varchar(200) NOT NULL,
`Address` varchar(200) NOT NULL,
`Address2` varchar(200) NOT NULL,
`City` varchar(100) NOT NULL,
`Province` char(2) NOT NULL,
`Country` varchar(100) NOT NULL,
`PostalCode` varchar(10) NOT NULL,
`Email` varchar(200) NOT NULL,
`AccountNum` int(8) NOT NULL,
PRIMARY KEY (`ID`),
KEY `ECPCodeID` (`ECPCodeID`),
KEY `PostalCode` (`PostalCode`),
KEY `AccountNum` (`AccountNum`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `Reps_Codes` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(50) NOT NULL,
`RepCode` varchar(16) NOT NULL,
`AllAccess` tinyint(4) NOT NULL,
PRIMARY KEY (`ID`),
KEY `RepCode` (`RepCode`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `Reps_ShipTo` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`RepCodeID` int(11) NOT NULL,
`ShipTo` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`ID`),
KEY `RepID` (`RepCodeID`),
KEY `ShipTo` (`ShipTo`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE IF NOT EXISTS `Reps_Zip` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`RepCodeID` int(11) NOT NULL,
`Zip` varchar(10) NOT NULL,
PRIMARY KEY (`ID`),
KEY `RepCodeID` (`RepCodeID`),
KEY `Zip` (`Zip`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
There are two things that massively hurt performance on your query.
You are joining two tables by combining multiple conditions, each needing subqueries
You're doing a join on two tables using SUBSTR(Zip,1,5)=SUBSTR(postalcode,1,5)
The logic behind your query seems to be something like:
For every ECPs_Registration find the matching record in Rep_Codes
using the following rules:
If there is a matching record in Reps_ShipTo, to for that registration, use that table to look it up (primary match)
If there isn't a matching record in Reps_ShipTo, seek through Reps_Zip for a matching RepCode by Zipcode-match (secondary)
Now if the above fully describes your situation, you should probably start off by redesigning your database.
The Reps_ShipTo table creates a 0:N relationship between ECPs_Registration and Rep_Codes. Such relations don't need an extra table - they can simply be stored as nullable foreign keys - in your case a RepCodeId in ECPs_Registration would do the trick, and would remove the entire Reps_ShipTo table from the database.
You should probably also create (yes, redundant) extra columns that only store the first 5 letters of the zip codes in both ECPs_Registration and Reps_Zip. This will allow simple equality matches instead of the SUBSTR-functions. Or, you might decide to do this match only once for every record, and store the result in above RepCodeId, which totally eliminates the dual join.
The following query assumes you for some reason don't want to or can't change your database:
SELECT
a.ID, a.ECPCodeID, a.RegDate, a.BusName, a.City, a.AccountNum,
CASE (b1.ID IS NOT NULL, b1.ID, b2.ID) as RepCodeID,
CASE (b1.ID IS NOT NULL, b1.RepCode, b2.RepCode) as MyRepCode
FROM ECPs_Registration a
LEFT JOIN Reps_ShipTo ON (Reps_ShipTo.Shipto=a.AccountNum)
LEFT JOIN Rep_Codes b1 ON (b1.ID=Reps_ShipTo.RepCodeId)
LEFT JOIN Reps_Zip ON (SUBSTR(Zip,1,5)=SUBSTR(a.postalcode,1,5))
LEFT JOIN Rep_Codes b2 ON (b2.ID=Reps_Zip.RepCodeID)
ORDER BY MyRepCode,a.BusName,a.City
Without your database schema and sample data, I have no way to test if above query actually works and has the same result as your original.
SELECT
a.ID,
a.ECPCodeID,
a.RegDate,
a.BusName,
a.City,
a.AccountNum,
b.ID as RepCodeID,
b.RepCode
FROM ECPs_Registration a, Reps_Codes b
INNER JOIN Reps_Zip as r on SUBSTR(a.PostalCode,1,5) = SUBSTR(r.Zip,1,5)
LEFT JOIN Reps_ShipTo as rs on a.AccountNum = rs.ShipTo
LEFT JOIN ShipTo as s on a.AccountNum = s.ShipTo
WHERE (s.id is null or rs.id is null)
ORDER BY b.RepCode,a.BusName,a.City

mysql join, need suggestion

I have created a MySQL table with a foreign key value. I want to use MySQL join to fetch foreign key value.
I have a table called employee and foreign key value sex, Sex table contain Male & Female.
this is my simple join query which is working:
SELECT * FROM sex JOIN employee ON employee.sex_id=sex.id
but i want to use join query here, but it seems I am missing some thing, Please complete this:
SELECT employee_id, first_name, last_name, sex_id FROM employee
And please tell me how i will insert to forigen key in single query for this table:
CREATE TABLE IF NOT EXISTS `employee` (
`employee_id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(20) NOT NULL,
`middle_name` varchar(20) DEFAULT NULL,
`last_name` varchar(20) NOT NULL,
`employee_employee_id` int(11) DEFAULT NULL,
`address_id` int(11) DEFAULT NULL,
`phone_id` int(11) DEFAULT NULL,
`dob` date DEFAULT NULL,
`maritial_id` int(11) NOT NULL,
`sex_id` int(11) NOT NULL,
PRIMARY KEY (`employee_id`),
KEY `fk_employee_employee` (`employee_employee_id`),
KEY `fk_employee_address1` (`address_id`),
KEY `fk_employee_phone1` (`phone_id`),
KEY `fk_employee_maritial1` (`maritial_id`),
KEY `fk_employee_sex1` (`sex_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
I have not good idea in MySQL
SELECT emp.employee_id, emp.first_name, emp.last_name, emp.sex_id, sx.sex_label
FROM employee AS emp
JOIN sex AS sx
ON emp.sex_id = sx.id
Where sex_label is a field name in sex table that would contain either Male or Female.
Try something like:
SELECT e.employee_id, e.first_name, e.last_name, s.id
FROM employee AS e
JOIN sex AS s
ON e.sex_id = s.id

Join two tables, matching a column with multiple values

I am trying to get a product matching some custom parameters.
So I have to three tables - products, parameters and parametersitems.
Products table:
CREATE TABLE `products` (
`ID` int(10) unsigned NOT NULL AUTO_INCREMENT
`Title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`Content` longtext COLLATE utf8_unicode_ci NOT NULL,
`Price` float(10,2) unsigned NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Parameter table:
CREATE TABLE `parameters` (
`ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`Label` varchar(80) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Parameter items table:
CREATE TABLE `parametersitems` (
`ProductID` int(10) unsigned NOT NULL DEFAULT '0',
`ParameterID` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`ProductID`,`ParameterID`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
So my question is how can I get only the products matching all the parameters.
The only way I could think of is joining the parameteritems table couple of times.
For example, here is a query to get the products matching two parameters:
SELECT
products.*
FROM
products
INNER JOIN
parametersitems AS paritems1
ON
paritems1.ItemID = products.ID
AND paritems1.ParameterID = 7
INNER JOIN
parametersitems AS paritems2
ON
paritems2.ItemID = products.ID
AND paritems2.ParameterID = 11
My only concern is that the SELECT query will get slower and slower if there more parameters selected.
So is there a better way to handle this problem?
Thank you
Adjust the value tested in the HAVING clause to match the number of values listed in the IN clause.
SELECT p.*
FROM products p
WHERE p.ID IN (SELECT pi.ItemID
FROM parameteritems pi
WHERE pi.ItemID = p.ID
AND pi.ParameterID IN (7,11)
GROUP BY pi.ItemID
HAVING COUNT(DISTINCT pi.ParameterID) = 2)
select p.*
from products p
inner join (
select ItemID
from parametersitems
where ParameterID in (7, 11)
group by ItemID
having count(distinct ParameterID) = 2
) pm on p.ID = pm.ItemID
SELECT
p.ID, p.Title, p.Content, p.Price
FROM
products AS p
INNER JOIN
parametersitems AS pi ON pi.ProductID = p.ID
GROUP BY
p.ID, p.Title, p.Content, p.Price
HAVING COUNT(DISTINCT pi.ParameterID) = (SELECT COUNT(ID) FROM parameters);
This will always get you products matching every parameter no matter how many parameters you add. (This could become bogus if you delete a parameter without deleting the corresponding rows in paramatersitems. This is what constraints are for.)