trouble in find child field from primary field in mysql - mysql

I have two table like below
CREATE TABLE IF NOT EXISTS `countries` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=196 ;
ANd ANother one
CREATE TABLE IF NOT EXISTS `students` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`admission_no` varchar(255) DEFAULT NULL,
`nationality_id` int(11) DEFAULT NULL,
`country_id` int(11) DEFAULT NULL,
`is_active` tinyint(1) DEFAULT '1',
`is_deleted` tinyint(1) DEFAULT '0',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `admission_no` (`admission_no`)
) ENGINE=InnoDB DEFAULT CHARSET=latin
1 AUTO_INCREMENT=2 ;
So the problem is i want fetch both nationality_id,country_id name from countries table for this im have to use LEFT JOIN query so in this case i am facing problem as im getting same name for both if nationality_id,country_id are different as i can only join on one table only so could someone plz help me to solve this.

If I understand you correctly, you can achieve this by LEFT JOINING the same table twice, using aliases.
Something like
SELECT *
FROM students s LEF TJOIN
countries c ON s.country_id = c.id LEFT JOIN
countries n ON s.nationality_id = n.id

#astander there is a little bug in your query (second alias for countries n is not used in on statement). here is a correct statement.
select s.Id, cNationality.Name, cCountry.Name
from Students as s
left outer join Countries as cNationality on cNationality.Id = s.Nationality_id
left outer join Countries as cCountry on cCountry.Id = s.Country_id

Related

Unknown column in 'on clause' after MySQL upgrade

I've been updating some old code which used PHP4 and MySQL 4.1 up to MySQL 5.6 / MariaDB 10. I've had a few issues with SQL JOINs
and precedence but this one has really stumped me and is giving me the error -
#1054 - Unknown column 'grouping_id' in 'on clause'
I've tried messing about with the order of the JOIN statements in the query below but I haven't had any success yet, as mentioned this query worked fine on MySQL 4.
SELECT
team.team_id,
team.team_name,
competition.rel_sport_id,
country.country_name
FROM
team
LEFT JOIN team_grouping ON(
rel_team_id = team_id AND team_grouping.rel_grouping_id = grouping_id
)
LEFT JOIN grouping ON grouping_id = team_grouping.rel_grouping_id
LEFT JOIN country ON team.rel_country_id = country_id
LEFT JOIN sport ON team.rel_sport_id = sport_id
LEFT JOIN competition_country ON(
rel_competition_id = competition_id AND competition_country.rel_country_id = country_id
)
LEFT JOIN competition ON competition_id = '985'
WHERE
team.rel_country_id = competition_country.rel_country_id AND team.rel_sport_id = competition.rel_sport_id AND grouping_id = '3'
ORDER BY
team_name
Can anyone help with what could be wrong with the above query?
EDIT - Added table schemas:
CREATE TABLE `grouping` (
`grouping_id` int(11) NOT NULL AUTO_INCREMENT,
`grouping_name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`grouping_id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=latin
CREATE TABLE `team` (
`team_id` int(11) NOT NULL AUTO_INCREMENT,
`team_name` varchar(200) DEFAULT NULL,
`image` varchar(100) DEFAULT NULL,
`rel_country_id` int(11) DEFAULT NULL,
`rel_sport_id` int(11) DEFAULT NULL,
`modified_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`team_id`),
KEY `IDX_team_1` (`rel_country_id`),
KEY `IDX_team_2` (`rel_sport_id`)
) ENGINE=MyISAM AUTO_INCREMENT=11918 DEFAULT CHARSET=latin1
CREATE TABLE `country` (
`country_id` int(11) NOT NULL AUTO_INCREMENT,
`country_name` varchar(40) DEFAULT NULL,
`image` varchar(40) DEFAULT NULL,
`rel_geographic_id` int(11) DEFAULT NULL,
PRIMARY KEY (`country_id`),
KEY `IDX_country_2` (`rel_geographic_id`)
) ENGINE=MyISAM AUTO_INCREMENT=237 DEFAULT CHARSET=latin1
CREATE TABLE `competition` (
`competition_id` int(11) NOT NULL AUTO_INCREMENT,
`competition_name` varchar(200) DEFAULT NULL,
`rel_sport_id` int(11) DEFAULT NULL,
`rel_grouping_id` int(11) DEFAULT NULL,
`rel_competition_tz_id` int(11) NOT NULL DEFAULT '2',
`modified_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`competition_id`),
KEY `IDX_competition_1` (`rel_sport_id`),
KEY `IDX_competition_2` (`rel_grouping_id`)
) ENGINE=MyISAM AUTO_INCREMENT=1133 DEFAULT CHARSET=latin1
CREATE TABLE `sport` (
`sport_id` int(11) NOT NULL AUTO_INCREMENT,
`sport_name` varchar(40) DEFAULT NULL,
`image` varchar(40) DEFAULT NULL,
`modified_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`sport_id`)
) ENGINE=MyISAM AUTO_INCREMENT=29 DEFAULT CHARSET=latin1
CREATE TABLE `competition_country` (
`rel_competition_id` int(11) NOT NULL DEFAULT '0',
`rel_country_id` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`rel_competition_id`,`rel_country_id`),
KEY `IDX_competition_country_1` (`rel_competition_id`),
KEY `IDX_competition_country_2` (`rel_country_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
Table names in an ON clause can only refer to tables preceding it in the query. When you're joining through a relationship table, the first ON clause just relates with the table before it, you relate to the second table in the next ON clause.
So the ON clause for team_grouping should only have rel_team_id = team_id, and the ON clause for competition_country should only have rel_country_id = country_id.
I don't think you need the WHERE clause at the end. Those relationships should be implied already by the earlier joins. And since you're doing a LEFT JOIN with grouping, you should put restrictions on that table in the ON clause; otherwise, the null values from non-matching rows will be filtered out by the WHERE clause.
SELECT
team.team_id,
team.team_name,
competition.rel_sport_id,
country.country_name
FROM team
LEFT JOIN team_grouping ON rel_team_id = team_id
LEFT JOIN grouping ON grouping_id = team_grouping.rel_grouping_id AND grouping_id = 3
LEFT JOIN country ON team.rel_country_id = country_id
LEFT JOIN sport ON team.rel_sport_id = sport_id
LEFT JOIN competition_country ON competition_country.rel_country_id = country_id
LEFT JOIN competition ON competition_id = '985' AND competition_id = competition_country.rel_competition_id
ORDER BY team_name

how to create a view from three tables

I have problem with getting combined records from 3 tables.
Here is the structure of the tables
CREATE TABLE IF NOT EXISTS `adds` (
`addid` int(11) NOT NULL AUTO_INCREMENT,
`addtypeid` varchar(45) NOT NULL,
`addcreatedon` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`addtitle` varchar(255) DEFAULT NULL,
`addtext` text NOT NULL,
PRIMARY KEY (`addid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=40 ;
CREATE TABLE IF NOT EXISTS `adds_filters` (
`addfilterid` int(11) NOT NULL AUTO_INCREMENT,
`addid` int(11) NOT NULL,
`filterid` int(11) NOT NULL,
PRIMARY KEY (`addfilterid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=40 ;
CREATE TABLE IF NOT EXISTS `categories_filters` (
`filterid` int(11) NOT NULL AUTO_INCREMENT,
`catid` varchar(45) NOT NULL,
`filtername` varchar(45) NOT NULL,
`sorder` int(11) DEFAULT NULL,
`visible` int(11) DEFAULT NULL,
PRIMARY KEY (`filterid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=230 ;
Adds have one to many relationship with adds_filters. That is, one add can have more then one filter.
What I need is the following:
I would like to create a view which when select * would return all rows from adds, together with filterid(s) and respective filtername(s). Please note that one add may have many filterid(s)
Can anyone help me with this?
Regards
You do not need a view.
I think you want to use a combination of LEFT OUTER JOIN and GROUP_CONCAT(). That way you will get 1 result for each row in the adds table, along with a list of related filter_ids and filter_names, if any.
Something like this:
select adds.addid, adds.addtypeid, adds.addcreatedon, adds.addtitle, adds.addtext,
group_concat(adds_filters.filterid) as filter_ids,
group_concat(categories.filtername) as filter_names
from adds
left outer join adds_filters on adds_filters.addid = adds.addid
left outer join categories_filters on categories_filters.filterid = adds_filters.filterid
group by adds.addid, adds.addtypeid, adds.addcreatedon, adds.addtitle, adds.addtext;
create view v1 as
select adds.addid as addid, categories_filters.filtername as filtername, categories_filters.filterid as filterid
from adds inner join adds_filters on adds.addid = adds_filters.addid
inner join categories_filters on categories_filters.filterid = adds_filters.filterid

Joining tables back to themselves in MySql

Let's say I have three tables:
user table:
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`loc` int(11) DEFAULT NULL,
`doc` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
location table:
CREATE TABLE `location` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
and document table:
CREATE TABLE `document` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`maintainer` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
I can successfully pull the user info and it's corresponding location and document info with the following query:
SELECT * from `user` LEFT JOIN `location` on user.loc = location.id LEFT JOIN `document` on user.doc = document.id;
The location info is easily referenced as its information doesn't refer to any other rows in any other tables. The document table, however, contains a maintainer field which directly corresponds back to another user in the user table. This field encapsulates the user information, and does not give me actual user data.
Is there not a way of querying the tables such that the data for the maintainer returns the actual user data as opposed to an id?
select
u.name as user_name,
m.name as maintainer_name,
l.name as location_name
from user as u
left outer join document as d on d.id = u.doc
left outer join user as m on m.id = d.maintainer
left outer join location as l on l.id = u.loc

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

Can I do a sort of DELETE with JOIN?

I have this kind of table in my MySql Database :
CREATE TABLE `forum_categories` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL,
`description` VARCHAR(255) NOT NULL,
`date` DATETIME NOT NULL,
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
CREATE TABLE `forum_topics` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`category_id` INT(11) UNSIGNED NOT NULL,
`title` VARCHAR(255) NOT NULL,
`author` VARCHAR(255) NOT NULL,
`date` DATETIME NOT NULL,
`visits` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`sticky` TINYINT(11) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
And I'd like, for example, to remove the category (from the table forum_categories) with id=4.
But, when I do this, I'd like to remove all rows on the table forum_topics with category_id=4.
Is it possible to do a sort of DELETE+JOIN?
Unfortunatly (as you can see) my host provider doesnt support InnoDB (what a shame..), so I can't use FOREIGN KEYS :(
SOLUTION
Solved with :
DELETE forum_categories.*, forum_topics.* , forum_visits.*, forum_messages.*
FROM forum_categories
JOIN forum_topics ON forum_categories.id=forum_topics.category_id
JOIN forum_visits ON forum_topics.id=forum_visits.topic
JOIN forum_messages ON forum_topics.id=forum_messages.topic_id
WHERE forum_categories.id=4
you can use the multi-table syntax also:
delete a.*, b.* from forum_categories a inner join forum_topics b on a.id = b.category_id where a.id = 4
Setup a TRIGGER to provide the "cascading" effect.
This MySQL cascading example should provide what you are looking for. It specifically calls out how to do it with MyISAM-based tables.
looks like you might be stuck with
DELETE FROM fourm_topics WHERE category_id = 4
DELETE FROM forum_categories WHERE id = 4
in the same call.
I addressed this question a while back
Mysql - delete multi table