Joining tables back to themselves in MySql - 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

Related

mySQL Left Join 5 Tables?

Thanks in advance for any help. I am working with 5 tables in a mySQL database. The system is such that I have a top level table called "owners" (clients) that have local business (shops). These owners go out and create accounts at websites like yelp (citation_sources) and as such have login credential (citation_login). Once they have an account at a citation source, they add shops to the directory.
I am hoping to create one query that would select ALL of the citation sources, regardless of if an owner has an account or not, and loop through the recordset, showing login for each citation source they have an account with, as well as any shop listings.
My question pertains to doing a left join on 5 tables. I left out most fo the fields but have set up primary and foreign keys Is the sequence of the join important, ie. start with one particular table, ending with another?
I tried this command but it only brings back 33 rows when in fact there are 96 citation_sources.
I think I figured it out. I created a new table called "citation_shop" with a composite primary key - citation - shop. I then ran a query and it got me the results I was after. I ended up putting a condition in the first left join.
SELECT citation_sources.name, citation_shop.shop from citation_sources left join citation_shop on citation_sources.id = citation_shop.citation and citation_shop.shop in (6,7) left join shops on citation_shop.shop = shops.id group by citation_sources.name, citation_shop.shop limit 100
CREATE TABLE `citation_shop` (
`shop` smallint(5) UNSIGNED NOT NULL,
`citation` smallint(6) UNSIGNED NOT NULL,
`url` text NOT NULL,
`count` smallint(3) UNSIGNED NOT NULL,
`status` tinyint(1) UNSIGNED NOT NULL,
`sort` tinyint(3) UNSIGNED NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `citation_shop`
--
ALTER TABLE `citation_shop`
ADD PRIMARY KEY (`citation`,`shop`);
select owners.id as owner_id, shops.id as shop_id, citation_sources.name, citation_shop_urls.url, citation_logins.password
from owners
inner join shops on owners.id = shops.owner_id
left join citation_logins on owners.id = citation_logins.owner
left join citation_sources on citation_logins.c_source = citation_sources.id
left join citation_shop_urls on citation_sources.id = citation_shop_urls.citation_id
where owners.id = 3
group by citation_sources.name
Here are my tables in order of what I think is relevlance:
CREATE TABLE `owners` (
`id` smallint(6) UNSIGNED NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
CREATE TABLE `shops` (
`id` smallint(5) UNSIGNED NOT NULL,
`title` varchar(50) DEFAULT '',
`owner_id` smallint(5) UNSIGNED NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `citation_sources` (
`id` smallint(6) UNSIGNED NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `citation_shop_urls` (
`shop` smallint(5) UNSIGNED NOT NULL DEFAULT '0',
`citation_id` tinyint(5) UNSIGNED NOT NULL DEFAULT '0',
`owner` smallint(6) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `citation_logins` (
`c_source` smallint(5) UNSIGNED NOT NULL DEFAULT '0',
`owner` smallint(6) NOT NULL,
`user_name` text NOT NULL,
`password` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
In a LEFT JOIN, the first table is the one where you get all the rows, even if they don't have a match in other tables. So if you want all citation_sources, even those not associated with any owner, then citation_sources should be the table on the left of the LEFT JOIN.
To filter the owner information only to id = 3, put o.id = 3 in the ON clause that joins with owners. Then use a WHERE clause to remove all the other rows.
SELECT o.id as owner_id, s.id as shop_id, cs.name, u.url, cl.password
FROM citation_sources AS cs
LEFT JOIN citation_shop_urls AS u ON u.citation_id = cs.id
LEFT JOIN citation_logins AS cl ON cs.id = cl.c_source
LEFT JOIN owners AS o ON o.id = cl.owner AND o.id = 3
LEFT JOIN shops AS s ON s.owner_id = o.id
WHERE o.id IS NULL OR o.id = 3

Internationalization of sql query

I have the following SQL tables:
-- Create syntax for TABLE 'companies'
CREATE TABLE `companies` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
`country` varchar(255) NOT NULL DEFAULT '',
`city` varchar(255) NOT NULL DEFAULT '',
`address` text NOT NULL,
`logo` varchar(255) NOT NULL DEFAULT 'empty',
`size` int(11) NOT NULL DEFAULT '32',
PRIMARY KEY (`id`),
KEY `city` (`city`),
KEY `country` (`country`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
-- Create syntax for TABLE 'i18n'
CREATE TABLE `i18n` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`lang` varchar(2) DEFAULT NULL,
`word` text,
`english` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'tags'
CREATE TABLE `tags` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
-- Create syntax for TABLE 'tagsForCompany'
CREATE TABLE `tagsForCompany` (
`company` int(11) DEFAULT NULL,
`tid` int(11) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I have the following sql query (thanks to #Rockse):
SELECT c.*
FROM
(SELECT *
FROM companies
WHERE city = "<Some City>" AND country = "<Some Country>") AS c
INNER JOIN tagsForCompany AS tc ON c.id = tc.Company
INNER JOIN tags AS t ON t.id = tc.TID
WHERE t.Name REGEXP '<a keyword>'
I need to be able to search for Companies with a keyword written in another language by searching in the i18n table for a translation. So that searching for restaurant will give the same results as searching for レストラン (restaurant in Japanese) or ristorante (restaurant in italian).
Honestly I have no idea what to edit in the query, my SQL knowledge is a bit limited.
The solution was easier than I thought:
If someone would improve, I would be more than happy
SELECT c.*
FROM i18n, (SELECT *
FROM companies
WHERE city = "<some city>" AND country = "<some country>") AS c
INNER JOIN tagsForCompany AS tc ON c.id = tc.Company
INNER JOIN tags AS t ON t.id = tc.TID
WHERE t.Name = i18n.`english` AND i18n.word REGEXP "<some word>" OR t.Name REGEXP "<the same word as before>"
But for doing that I had to change all the tables to utf-8

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

Get information about all rows from one table, when in join table that row is not

How obtain in case that in table userItems is not record joined with moduleItems just null?
SELECT `users`.*, `useritems`.*, `moduleitems`.*, `modulesubitems`.* FROM `users`
LEFT JOIN `useritems` ON useritems.f_user_id = users.user_id
LEFT JOIN `moduleitems` ON moduleitems.moduleItem_id = useritems.f_moduleItem_id
LEFT JOIN `modulesubitems` ON modulesubitems.modulesubitem_id = useritems.userItem_value
Edit: My DB structure
CREATE TABLE IF NOT EXISTS `moduleitems` (
`moduleItem_id` int(11) NOT NULL AUTO_INCREMENT,
`f_module_id` int(11) NOT NULL,
PRIMARY KEY (`moduleItem_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=63 ;
CREATE TABLE IF NOT EXISTS `modules` (
`module_id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`module_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
CREATE TABLE IF NOT EXISTS `modulesubitems` (
`moduleSubitem_id` int(11) NOT NULL AUTO_INCREMENT,
`f_moduleItem_id` int(11) NOT NULL,
PRIMARY KEY (`moduleSubitem_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=19 ;
CREATE TABLE IF NOT EXISTS `useritems` (
`f_user_id` int(11) NOT NULL,
`f_moduleItem_id` int(11) NOT NULL,
`userItem_value` varchar(255) DEFAULT NULL,
PRIMARY KEY (`f_user_id`,`f_moduleItem_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=35 ;
If you are would like to see every moduleitem for each user, regardless if they have a matching record in the useritems table, this should work:
SELECT `users`.*, `useritems`.*, `moduleitems`.*, `modulesubitems`.*
FROM `users`
CROSS JOIN `moduleitems`
LEFT JOIN `useritems`
ON useritems.f_user_id = users.user_id
AND moduleitems.moduleItem_id = useritems.f_moduleItem_id
LEFT JOIN `modulesubitems`
ON modulesubitems.modulesubitem_id = useritems.userItem_value
If you are trying to find all records that are not in a joined table (when using a LEFT JOIN), add to your WHERE clause where the field you are joining in that table is NULL. All the fields in the joining table will be NULL if there is no match.
For example this would work if you are trying to find cases where there is no match in the moduleitems table.:
SELECT `users`.*, `useritems`.*, `moduleitems`.*, `modulesubitems`.*
FROM `users`
LEFT JOIN `useritems` ON useritems.f_user_id = users.user_id
LEFT JOIN `moduleitems` ON moduleitems.moduleItem_id = useritems.f_moduleItem_id
LEFT JOIN `modulesubitems` ON modulesubitems.modulesubitem_id = useritems.userItem_value
WHERE moduleitems.moduleItem_id IS NULL

trouble in find child field from primary field in 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