I have a table which defines what things another table can have, for example:
CREATE TABLE `objects` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(50) NOT NULL
);
INSERT INTO `objects` (`name`) VALUES ('Test');
INSERT INTO `objects` (`name`) VALUES ('Test 2');
CREATE TABLE `properties` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(50) NOT NULL
);
INSERT INTO `properties` (`name`) VALUES ('colour');
INSERT INTO `properties` (`name`) VALUES ('size');
CREATE TABLE `objects_properties` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`object_id` INT UNSIGNED NOT NULL,
`property_id` INT UNSIGNED NOT NULL,
`value` VARCHAR(50) NOT NULL,
FOREIGN KEY (`object_id`)
REFERENCES `objects` (`id`),
FOREIGN KEY (`property_id`)
REFERENCES `properties` (`id`)
);
INSERT INTO `objects_properties` (`object_id`, `property_id`, `value`) VALUES 1, 1, 'red');
INSERT INTO `objects_properties` (`object_id`, `property_id`, `value`) VALUES 1, 2, 'small');
INSERT INTO `objects_properties` (`object_id`, `property_id`, `value`) VALUES 2, 1, 'blue');
INSERT INTO `objects_properties` (`object_id`, `property_id`, `value`) VALUES 2, 2, 'large');
Hopefully this makes sense. Basically instead of having columns for colour, size etc. in the objects table, I have two other tables, one that defines the properties any object can have, and another that links objects to some or all of these properties.
My question is if there's some way to retrieve this information like this:
+--------+------------+------------+
| object | colour | size |
+--------+------------+------------+
| Test | red | small |
| Test 2 | blue | large |
+--------+------------+------------+
So you can see the column headings are actually row values. I'm not sure if it's possible or how costly it would be compared to doing a few separate queries and putting everything together in PHP.
SELECT o.name, c.colour, s.size
FROM objects o
LEFT JOIN (SELECT op.object_id, op.value colour
FROM objects_properties op
join properties p on op.property_id = p.id and p.name = 'colour') c
ON o.id = c.object_id
LEFT JOIN (SELECT op.object_id, op.value size
FROM objects_properties op
join properties p on op.property_id = p.id and p.name = 'size') s
ON o.id = s.object_id
The keyword here is "pivot table" "crosstab" (but a "pivot table" lies also in that direction) and no, MySQL cannot do this directly. You can create a query that will select this, but you will have to explicitly define the columns yourself in the query. No fetching of columns from another table. Other RDBMS may have capabilities for this.
pivot (or something like that) could be useful. In MS SQL Server you can use it BUT the values to pivot the table must be constant or you can use a stored procedure to calculate it.
Here you can find more info.
Have a nice day!
SELECT o.*,
(
SELECT *
FROM object_properties op
WHERE op.object_id = o.object_id
AND op.property_id = $prop_color_id
) AS color,
(
SELECT *
FROM object_properties op
WHERE op.object_id = o.object_id
AND op.property_id = $prop_size_id
) AS size
FROM objects o
Substitute the $prop_color_id and $prop_size_id with the color and size property id's.
For this query to be efficient, make (object_id, property_id) a PRIMARY KEY in the object_properties and get rid of the surrogate key.
Related
I have a table that looks like this:
id int primary key
uniqueID string --not uniquely indexed
foreignKeyID int --foreignKey to another table
I want to find all the uniqueIds in this table that exist for foreign key 1 that do not exist for foreign key 2
I thought I could do something like this:
SELECT * FROM table t1
LEFT JOIN table t2
ON t1.uniqueID = t2.uniqueID
WHERE
t1.foreignKeyID = 1
AND t2.uniqueID IS NULL
However this is never giving me results. I can make it work with a NOT IN subquery but this is a very large table so I suspect a solution using joins will be faster.
Looking for the best way to structure this query.
Here's an sample data set and SQL Fiddle with an example of the working NOT IN query I am trying to convert to a LEFT JOIN:
CREATE TABLE `table` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uniqueID` varchar(255),
`foreignKeyID` int(5) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `table` (uniqueID, foreignKeyID) VALUES ('aaa', 1), ('bbb', 1);
http://sqlfiddle.com/#!9/48a3f3/4 and a non-working LEFT JOIN I thought would be equivalent.
Thanks!
Try this, seems to be working if understood the question properly:
SELECT *
FROM `table` t
LEFT JOIN `table` tt ON tt.uniqueID = t.uniqueID AND tt.foreignKeyID <> 1
WHERE t.foreignKeyID = 1 AND tt.id IS NULL;
I'm checking if 2 foreign ids of a table are equal to 2 foreign keys in another table to the same table, but I don't care about the order of the ids, just that they have the same values.
i.e.
SELECT (1, 2, 3) = (1, 2, 3);
> 1
SELECT (1, 2, 3) = (2, 1, 3);
> 0
I'd like a way so that (1,2,3) matches (2,1,3) as well as (1,3,2) and (2,3,1).
Unfortunately searching for information on this has proved difficult, most advice is "Lists don't exist in MySQL" and searches for sorting, or unordered checking result in various SQL calls that aren't relevant.
Fiddle: https://www.db-fiddle.com/f/eqz27tR9uDMQriDhkwBo2a/0
I deliberately put an event in the table with the participants ordered differently to the participants in not_event, and it's that join,
SELECT * FROM event
JOIN not_event ON (
(event.participant_1_id, event.participant_2_id) =
(not_event.participant_1_id, not_event.participant_2_id));
That's the issue. I don't care what order participant_1_id and participant_2_id are, in either table, so long as they're the same 2.
Rest of code from fiddle,
CREATE TABLE `participant` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`key` varchar(15) NOT NULL,
PRIMARY KEY (`id`));
CREATE TABLE `event` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`participant_1_id` int(11) NOT NULL,
`participant_2_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `event_ibfk_1` FOREIGN KEY (`participant_1_id`) REFERENCES `participant` (`id`),
CONSTRAINT `event_ibfk_2` FOREIGN KEY (`participant_2_id`) REFERENCES `participant` (`id`)
);
CREATE TABLE `not_event` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`participant_1_id` int(11) NOT NULL,
`participant_2_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `not_event_ibfk_1` FOREIGN KEY (`participant_1_id`) REFERENCES `participant` (`id`),
CONSTRAINT `not_event_ibfk_2` FOREIGN KEY (`participant_2_id`) REFERENCES `participant` (`id`)
);
INSERT INTO `participant` VALUES (1, 'Team1');
INSERT INTO `participant` VALUES (2, 'Team2');
INSERT INTO `event` VALUES (NULL, 1, 2);
INSERT INTO `not_event` VALUES (NULL, 2, 1);
SELECT (1, 2, 3) = (1, 2, 3);
SELECT (1, 2, 3) = (2, 1, 3);
SELECT * FROM event
JOIN not_event ON (
(event.participant_1_id, event.participant_2_id) =
(not_event.participant_1_id, not_event.participant_2_id));
SELECT * FROM event
JOIN not_event ON (
(event.participant_1_id, event.participant_2_id) =
(not_event.participant_2_id, not_event.participant_1_id));
A few options, none I'm really happy with,
For binary joins, with only 2 fields, using LEAST and GREATEST works, but on more than 2 fields, it wouldn't work,
SELECT * FROM event
JOIN not_event ON (
LEAST(event.participant_1_id, event.participant_2_id) =
LEAST(not_event.participant_1_id, not_event.participant_2_id)
AND
GREATEST(event.participant_1_id, event.participant_2_id) =
GREATEST(not_event.participant_1_id, not_event.participant_2_id));
After that, there seems to be a dreadfully inefficient LENGTH check, with multiple REPLACEs with CONCAT_WS,
SELECT * FROM event
JOIN not_event ON (
1 = LENGTH(REPLACE(REPLACE(
CONCAT_WS(
',', event.participant_1_id, event.participant_2_id),
not_event.participant_1_id, ''), not_event.participant_2_id, ''))
);
But this one sucks, and is unreliable, because "1" would replace "11" with "", "2" replaces "222" with "", etc.
Updated fiddle (with these solutions): https://www.db-fiddle.com/f/eqz27tR9uDMQriDhkwBo2a/1
I've found you can achieve this via 3 different methods. 1 is querying the event and not_event tables, and joining the participant table twice, grouping them together, and running a GROUP BY with HAVING comparing GROUP_CONCATs,
SELECT event.*, not_event.*
FROM event
JOIN participant p1 ON p1.id IN (event.participant_1_id, event.participant_2_id),
not_event
JOIN participant p2 ON p2.id IN (not_event.participant_1_id, not_event.participant_2_id)
GROUP BY event.id, not_event.id
HAVING
GROUP_CONCAT(p1.key ORDER BY p1.key) =
GROUP_CONCAT(p2.key ORDER BY p2.key)
Or by running 2 subqueries that do GROUP_CONCAT on the field that you're interested in joining them on, and then join them afterwards,
SELECT *
FROM (
SELECT GROUP_CONCAT(participant.id ORDER BY participant.id) `key`, event.*
FROM event
JOIN participant ON (participant.id IN (event.participant_1_id, event.participant_2_id))
GROUP BY event.id) _event
JOIN (
SELECT GROUP_CONCAT(participant.id ORDER BY participant.id) `key`, not_event.*
FROM not_event
JOIN participant ON (participant.id IN (not_event.participant_1_id, not_event.participant_2_id))
GROUP BY not_event.id) _not_event
ON _event.key = _not_event.key;
And then the super "direct" or manual way,
SELECT event.*, not_event.*
FROM event, not_event
WHERE
event.participant_1_id IN (not_event.participant_1_id, not_event.participant_2_id) AND
event.participant_2_id IN (not_event.participant_1_id, not_event.participant_2_id) AND
not_event.participant_1_id IN (event.participant_1_id, event.participant_2_id) AND
not_event.participant_2_id IN (event.participant_1_id, event.participant_2_id)
These all join them correctly as wanted.
Updated fiddle:
https://www.db-fiddle.com/f/eqz27tR9uDMQriDhkwBo2a/5
With both solutions, Event(1, 2) correctly joins with NotEvent(2, 1) and nothing else, and Event(2, 3) correctly joins with NotEvent(3, 2) and nothing else.
I still think it's crazy that you have to join a table like this, in this way, rather than just comparing the keys within the table directly, but these work, one way or another.
I have problem with my query,
I have tables below:
CREATE TABLE IF NOT EXISTS `klik_zona` (
`kode_zona` int(10) unsigned NOT NULL,
`klik` int(10) unsigned NOT NULL,
PRIMARY KEY (`kode_zona`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `klik_zona` (`kode_zona`, `klik`) VALUES
(1, 45);
CREATE TABLE IF NOT EXISTS `tampil_zona` (
`kode_zona` int(10) unsigned NOT NULL,
`tanggal` date NOT NULL,
`tampil` int(10) unsigned NOT NULL,
PRIMARY KEY (`kode_zona`,`tanggal`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tampil_zona` (`kode_zona`, `tanggal`, `tampil`) VALUES
(1, '2014-03-16', 100),
(1, '2014-03-17', 23);
CREATE TABLE IF NOT EXISTS `zona_iklan` (
`kode_zona` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`kode_zona`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
INSERT INTO `zona_iklan` (`kode_zona`) VALUES
(1),
(2),
(3);
I have query:
SELECT z.kode_zona, SUM( tz.tampil ) , SUM( kz.klik )
FROM zona_iklan z
LEFT JOIN tampil_zona tz ON tz.kode_zona = z.kode_zona
LEFT JOIN klik_zona kz ON kz.kode_zona = z.kode_zona
GROUP BY z.kode_zona
but it give result:
kode_zona SUM(tz.tampil) SUM(kz.klik)
1 123 90
2 NULL NULL
3 NULL NULL
I want get result:
kode_zona SUM(tz.tampil) SUM(kz.klik)
1 123 45
2 NULL NULL
3 NULL NULL
please help me.. how to make query so that I get result that I hope it..
thanks,
In your example you join two records from tampil_zona on to one record from zona_iklan, which essentially causes that one record to duplicate. Then you are joining one record in klik_zona on to both of those duplicated records, causing the doubling of results that you want to avoid.
Instead, you need to aggregate the records before you join them, to ensure that you are always joining the records 1-to-1.
SELECT
z.kode_zona, tz.tampil, kz.klik
FROM
zona_iklan AS z
LEFT JOIN
(SELECT kode_zona, SUM(tampil) AS tampil FROM tampil_zona GROUP BY kode_zona) AS tz
ON tz.kode_zona = z.kode_zona
LEFT JOIN
(SELECT kode_zona, SUM(klik) AS klik FROM klik_zona GROUP BY kode_zona) AS kz
ON kz.kode_zona = z.kode_zona
Try removing the GROUP BY and look at the result. You will see that there are two records with kode_zona = 1. This because there are two records in tampil_zona matching that id. You could divide by count(*) but that seems futile. You probably want to think about how to modify the join.
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.
Can a single column in a table can be referenced to multiple tables?
A very late answer, but for whoever is wondering & googeling.
YES this can be done, but it is NOT good practice and even though it is quite simple, it will probably blow up in your face if you're not very aware of what you are doing. Not recommended.
However, I can see uses. For instance, you have a large table of millions of records, and you want in exceptional cases link to unknown or multiple tables (in which case it better be many). With multiple tables, if you would make a foreign key for all of them, that would be a huge bloat in your database size. An unknown table would be possible for instance in a technical support system, where you want to link to record in a table where there might be a problem, and this could be (almost) all tables in the database, including future ones.
Of course you will need two fields to link with: a foreign key field and the name of the table it is linking to. Lets call them foreignId and linkedTable
linkedTable could be an enum or a string, preferrably enum (less space), but that's only possible if the different tables you want to link to, are fixed.
Let's give an extremely silly example. You have an enormous user table users of which some user can add exactly one personal set of data to their profile. This can be about a hobby, a pet, a sport they practice or their profession. Now this info is different in all four cases. (4 possible tables is in reality not enough to justify this structure)
Now let's say linkedTable is an enum with possible values pets, hobbies, sports and professions, which are the names of four differently structured tables. Let's say id is the pkey in all four of them.
You join for instance as follows:
SELECT * FROM users
LEFT JOIN pets ON linkedTable = 'pets' AND foreignId = pets.id
LEFT JOIN hobbies ON linkedTable = 'hobbies' AND foreignId = hobbies.id
LEFT JOIN sports ON linkedTable = 'sports' AND foreignId = sports.id
LEFT JOIN professions ON linkedTable = 'professions' AND foreignId = professions.id
This is just to give a basic jest. Since you probably only need the link in rare cases, you will more likely do the lookup in your programming language, like PHP, when you loop through the users (without join).
Want to try out? You can try it yourself with building this test database (make sure you use a test database):
CREATE TABLE IF NOT EXISTS `users` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(100) NOT NULL ,
`linkedTable` ENUM('pets','hobbies','sports','professions') NULL DEFAULT NULL ,
`foreignId` INT NULL DEFAULT NULL ,
PRIMARY KEY (`id`), INDEX (`linkedTable`)
) ;
CREATE TABLE IF NOT EXISTS `pets` (
`id` INT NOT NULL AUTO_INCREMENT ,
`animalTypeId` INT NOT NULL ,
`name` VARCHAR(100) NOT NULL ,
`colorId` INT NOT NULL ,
PRIMARY KEY (`id`), INDEX (`animalTypeId`), INDEX (`colorId`)
) ;
CREATE TABLE IF NOT EXISTS `hobbies` (
`id` INT NOT NULL AUTO_INCREMENT ,
`hobbyTypeId` INT NOT NULL ,
`hoursPerWeekSpend` INT NOT NULL ,
`websiteUrl` VARCHAR(300) NULL ,
PRIMARY KEY (`id`), INDEX (`hobbyTypeId`)
) ;
CREATE TABLE IF NOT EXISTS `sports` (
`id` INT NOT NULL AUTO_INCREMENT ,
`sportTypeId` INT NOT NULL ,
`hoursPerWeekSpend` INT NOT NULL ,
`nameClub` VARCHAR(100) NULL ,
`professional` TINYINT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`), INDEX (`sportTypeId`)
) ;
CREATE TABLE IF NOT EXISTS `professions` (
`id` INT NOT NULL AUTO_INCREMENT ,
`professionId` INT NOT NULL ,
`hoursPerWeek` INT NOT NULL ,
`nameCompany` VARCHAR(100) NULL ,
`jobDescription` VARCHAR(400) NULL,
PRIMARY KEY (`id`), INDEX (`professionId`)
) ;
INSERT INTO `users` (`id`, `name`, `linkedTable`, `foreignId`)
VALUES
(NULL, 'Hank', 'pets', '1'),
(NULL, 'Peter', 'hobbies', '2'),
(NULL, 'Muhammed', 'professions', '1'),
(NULL, 'Clarice', NULL, NULL),
(NULL, 'Miryam', 'professions', '2'),
(NULL, 'Ming-Lee', 'hobbies', '1'),
(NULL, 'Drakan', NULL, NULL),
(NULL, 'Gertrude', 'sports', '2'),
(NULL, 'Mbase', NULL, NULL);
INSERT INTO `pets` (`id`, `animalTypeId`, `name`, `colorId`)
VALUES (NULL, '1', 'Mimi', '3'), (NULL, '2', 'Tiger', '8');
INSERT INTO `hobbies` (`id`, `hobbyTypeId`, `hoursPerWeekSpend`, `websiteUrl`)
VALUES (NULL, '123', '21', NULL), (NULL, '2', '1', 'http://www.freesoup.org');
INSERT INTO `sports` (`id`, `sportTypeId`, `hoursPerWeekSpend`, `nameClub`, `professional`)
VALUES (NULL, '2', '3', 'Racket to Racket', '0'), (NULL, '12', '34', NULL, '1');
INSERT INTO `professions` (`id`, `professionId`, `hoursPerWeek`, `nameCompany`, `jobDescription`)
VALUES (NULL, '275', '40', 'Ben & Jerry\'s', 'Ice cream designer'), (NULL, '21', '24', 'City of Dublin', 'Garbage collector');
Then run the first query.
Fun note for discussion: How would you index this?
If you mean "can a column in one table be used as a foreign key in multiple tables", then the answer is YES. This is the whole point of a relational database
Yes, you can do that so. here is a sample on how to do it:
Here is the table that has a column(CountryID) that will be referenced by multiple tables:
CREATE TABLE DLAccountingSystem.tblCountry
(
CountryID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
CountryName VARCHAR(128) NOT NULL,
LastEditUser VARCHAR(128) NOT NULL,
LastEditDate DATETIME NOT NULL
) ENGINE=INNODB;
Here are the tables that is going to reference the column(CountryID):
CREATE TABLE DLAccountingSystem.tblCity
(
CityID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
CountryID INT NOT NULL,
CityName VARCHAR(128) NOT NULL,
LastEditUser VARCHAR(128) NOT NULL,
LastEditDate DATETIME NOT NULL
) ENGINE=INNODB;
CREATE TABLE DLAccountingSystem.tblProvince
(
ProvinceID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
CountryID INT NOT NULL,
ProvinceName VARCHAR(128) NOT NULL,
LastEditUser VARCHAR(128) NOT NULL,
LastEditDate DATETIME NOT NULL
) ENGINE=INNODB;
Here is how you create a reference to the column:
ALTER TABLE DLAccountingSystem.tblCity
ADD CONSTRAINT fk_tblcitycountryid FOREIGN KEY CountryID (CountryID)
REFERENCES DLAccountingSystem.tblCountry (CountryID)
ON DELETE NO ACTION
ON UPDATE NO ACTION
ALTER TABLE DLAccountingSystem.tblProvince
ADD CONSTRAINT fk_tblprovincecountryid FOREIGN KEY CountryID (CountryID)
REFERENCES DLAccountingSystem.tblCountry (CountryID)
ON DELETE NO ACTION
ON UPDATE NO ACTION
here is a table that has column that references different columns from (CountryID, ProvinceID, CityID) multiple tables(I Don't personally advice this way of table structuring. Just my opinion no offense ;) )
CREATE TABLE DLAccountingSystem.tblPersons
(
PersonID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
PlaceID INT NOT NULL,
PlaceTypeID INT NOT NULL, -- this property refers to what table are you referencing.
//Other properties here.....
) ENGINE=INNODB;
you should also have a lookup table that would contain the PlaceType:
CREATE TABLE DLAccountingSystem.tblPlaceType
(
PlaceTypeID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
PlaceTypeName INT NOT NULL
//Other properties here.....
) ENGINE=INNODB;
here is how you fetch it:
SELECT p1.PersonID,
tcity.CityName,
tprov.ProvinceName,
tcoun.CountryName
FROM DLAccountingSystem.tblPersons p1
LEFT JOIN (SELECT p2.PersonID, p2.PlaceTypeID, c.CityName FROM DLAccountingSystem.tblPersons p2 INNER JOIN DLAccountingSystem.tblCity c ON p2.ObjectID = c.CityID WHERE PlaceTypeID = #CityTypeID) tcity ON p1.PersonID = tcity.PersonID
LEFT JOIN (SELECT p2.PersonID, p2.PlaceTypeID, c.ProvinceName FROM DLAccountingSystem.tblPersons p2 INNER JOIN DLAccountingSystem.tblProvince c ON p2.ObjectID = c.ProvinceID WHERE PlaceTypeID = #ProvinceTypeID) tprov ON p1.PersonID = tprov.PersonID
LEFT JOIN (SELECT p2.PersonID, p2.PlaceTypeID, c.CountryName FROM DLAccountingSystem.tblPersons p2 INNER JOIN DLAccountingSystem.tblCountry c ON p2.ObjectID = c.CountryID WHERE PlaceTypeID = #CountryTypeID) tcoun ON p1.PersonID = tcoun.PersonID
you can select from other tables like
A same column or set of columns can act as a parent and/or as a child endpoint of a foreign key or foreign keys.