I am trying to understand the best way to work with permissions and as far as I'm aware there are two major options.
The first option is to use bitwise operations, and for this I would use the following database structure:
users
user_id | user_permission
---------------------
1 | 15
2 | 1
permissions
permission_id | permission_name
-----------------------
1 | Read
2 | Write
4 | Execute
8 | Delete
And then to check that the user has permission I would use the operation:
$user_permission & $permission_id
The main benefits I see to this are:
Trivial to set, get, and validate permissions
Less storage (no child database; no additional rows per user permission)
The main drawbacks I see to this are:
Listing users' permissions slightly more complicated
Cannot use foreign key constraints
Limited permissions (64 if using BIGINT)
The second option is to use a many-to-many child table, and for this I would use the following database structure:
users
user_id
-------
1
2
permissions
permission_id | permission_name
-----------------------
1 | Read
2 | Write
3 | Execute
4 | Delete
user_permissions
user_id | permission_id
-----------------------
1 | 1
1 | 2
1 | 3
1 | 4
2 | 1
And then to check that the user has permission I would use the operation (where $user_permission is an array of permission_ids):
in_array($permission_id, $user_permission);
The main benefits I see to this are:
Can use foreign key constraints
Trivial to list users' permissions
Allows for a far greater number of permissions
The main drawbacks I see to this are:
Greater storage (child database; additional rows per user permission)
Setting and getting permissions slightly more complicated
Question
Which of these would be the better option? I see benefits and drawbacks to each and am unsure which would be more suitable. Although I am aware that context probably plays a role; so in which situations would bitwise operations be better and in which would a many-to-many child table be better? Or is there a third option of which I'm unaware?
I'm currently more inclined to use a many-to-many table for the benefits of foreign key constraints and a greater number of permission possibilities, but I wonder if I'm missing something else; bitwise operation permissions seem to be quite prevalent so I'd assume there is a good reason for using them.
I Think bitwise operator are the best way to implement user permission.
Here I am showing how we can implement it with Mysql.
Below is a sample tables with some sample data:
Table 1 : Permission table to store permission name along with it bit like 1,2,4,8..etc (multiple of 2)
CREATE TABLE IF NOT EXISTS `permission` (
`bit` int(11) NOT NULL,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`bit`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Insert some sample data into the table.
INSERT INTO `permission` (`bit`, `name`) VALUES
(1, 'User-Add'),
(2, 'User-Edit'),
(4, 'User-Delete'),
(8, 'User-View'),
(16, 'Blog-Add'),
(32, 'Blog-Edit'),
(64, 'Blog-Delete'),
(128, 'Blog-View');
Table 2: User table to store user id,name and role. Role will be calculated as sum of permissions.
Example :
If user 'Ketan' having permission of 'User-Add' (bit=1) and 'Blog-Delete' (bit-64) so role will be 65 (1+64).
If user 'Mehata' having permission of 'Blog-View' (bit=128) and 'User-Delete' (bit-4) so role will be 132 (128+4).
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`role` int(11) NOT NULL,
`created_date` datetime NOT NULL
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Sample data-
INSERT INTO `user` (`id`, `name`, `role`, `created_date`)
VALUES (NULL, 'Ketan', '65', '2013-01-09 00:00:00'),
(NULL, 'Mehata', '132', '2013-01-09 00:00:00');
Loding permission of user
After login if we want to load user permission than we can query below to get the permissions:
SELECT permission.bit,permission.name
FROM user LEFT JOIN permission ON user.role & permission.bit
WHERE user.id = 1
Here user.role "&" permission.bit is a Bitwise operator which will give output as -
User-Add - 1
Blog-Delete - 64
If we want to check weather a particular user have user-edit permission or not-
SELECT * FROM `user`
WHERE role & (select bit from permission where name='user-edit')
Output = No rows.
You can see also : http://goo.gl/ATnj6j
I would not go with the bitwise operations solution. Unless you are really really cramped for space, breaking this out into its own table and mapping table won't cost that much disk. It would be easier for people who aren't you to understand, and you can more easily enforce FK relationships this way. Also, as you mentioned, the number of permissions can grow virtually without limit. Depending on how you index the table, queries like "show me all users with Read permission" seems like it would be quicker to execute and easier to understand (that's subjective, I realize).
Related
I have two tables in MySQL. Table Person has the following columns:
id
name
fruits
The fruits column may hold null or an array of strings like ('apple', 'orange', 'banana'), or ('strawberry'), etc. The second table is Table Fruit and has the following three columns:
fruit_name
color
price
apple
red
2
orange
orange
3
-----------
--------
------
So how should I design the fruits column in the first table so that it can hold array of strings that take values from the fruit_name column in the second table? Since there is no array data type in MySQL, how should I do it?
The proper way to do this is to use multiple tables and JOIN them in your queries.
For example:
CREATE TABLE person (
`id` INT NOT NULL PRIMARY KEY,
`name` VARCHAR(50)
);
CREATE TABLE fruits (
`fruit_name` VARCHAR(20) NOT NULL PRIMARY KEY,
`color` VARCHAR(20),
`price` INT
);
CREATE TABLE person_fruit (
`person_id` INT NOT NULL,
`fruit_name` VARCHAR(20) NOT NULL,
PRIMARY KEY(`person_id`, `fruit_name`)
);
The person_fruit table contains one row for each fruit a person is associated with and effectively links the person and fruits tables together, I.E.
1 | "banana"
1 | "apple"
1 | "orange"
2 | "straberry"
2 | "banana"
2 | "apple"
When you want to retrieve a person and all of their fruit you can do something like this:
SELECT p.*, f.*
FROM person p
INNER JOIN person_fruit pf
ON pf.person_id = p.id
INNER JOIN fruits f
ON f.fruit_name = pf.fruit_name
The reason that there are no arrays in SQL, is because most people don't really need it. Relational databases (SQL is exactly that) work using relations, and most of the time, it is best if you assign one row of a table to each "bit of information". For example, where you may think "I'd like a list of stuff here", instead make a new table, linking the row in one table with the row in another table.[1] That way, you can represent M:N relationships. Another advantage is that those links will not clutter the row containing the linked item. And the database can index those rows. Arrays typically aren't indexed.
If you don't need relational databases, you can use e.g. a key-value store.
Read about database normalization, please. The golden rule is "[Every] non-key [attribute] must provide a fact about the key, the whole key, and nothing but the key.". An array does too much. It has multiple facts and it stores the order (which is not related to the relation itself). And the performance is poor (see above).
Imagine that you have a person table and you have a table with phone calls by people. Now you could make each person row have a list of his phone calls. But every person has many other relationships to many other things. Does that mean my person table should contain an array for every single thing he is connected to? No, that is not an attribute of the person itself.
[1]: It is okay if the linking table only has two columns (the primary keys from each table)! If the relationship itself has additional attributes though, they should be represented in this table as columns.
MySQL 5.7 now provides a JSON data type. This new datatype provides a convenient new way to store complex data: lists, dictionaries, etc.
That said, arrays don't map well databases which is why object-relational maps can be quite complex. Historically people have stored lists/arrays in MySQL by creating a table that describes them and adding each value as its own record. The table may have only 2 or 3 columns, or it may contain many more. How you store this type of data really depends on characteristics of the data.
For example, does the list contain a static or dynamic number of entries? Will the list stay small, or is it expected to grow to millions of records? Will there be lots of reads on this table? Lots of writes? Lots of updates? These are all factors that need to be considered when deciding how to store collections of data.
Also, Key/Value data stores, Document stores such as Cassandra, MongoDB, Redis etc provide a good solution as well. Just be aware of where the data is actually being stored (if its being stored on disk or in memory). Not all of your data needs to be in the same database. Some data does not map well to a relational database and you may have reasons for storing it elsewhere, or you may want to use an in-memory key:value database as a hot-cache for data stored on disk somewhere or as an ephemeral storage for things like sessions.
A sidenote to consider, you can store arrays in Postgres.
In MySQL, use the JSON type.
Contra the answers above, the SQL standard has included array types for almost twenty years; they are useful, even if MySQL has not implemented them.
In your example, however, you'll likely want to create three tables: person and fruit, then person_fruit to join them.
DROP TABLE IF EXISTS person_fruit;
DROP TABLE IF EXISTS person;
DROP TABLE IF EXISTS fruit;
CREATE TABLE person (
person_id INT NOT NULL AUTO_INCREMENT,
person_name VARCHAR(1000) NOT NULL,
PRIMARY KEY (person_id)
);
CREATE TABLE fruit (
fruit_id INT NOT NULL AUTO_INCREMENT,
fruit_name VARCHAR(1000) NOT NULL,
fruit_color VARCHAR(1000) NOT NULL,
fruit_price INT NOT NULL,
PRIMARY KEY (fruit_id)
);
CREATE TABLE person_fruit (
pf_id INT NOT NULL AUTO_INCREMENT,
pf_person INT NOT NULL,
pf_fruit INT NOT NULL,
PRIMARY KEY (pf_id),
FOREIGN KEY (pf_person) REFERENCES person (person_id),
FOREIGN KEY (pf_fruit) REFERENCES fruit (fruit_id)
);
INSERT INTO person (person_name)
VALUES
('John'),
('Mary'),
('John'); -- again
INSERT INTO fruit (fruit_name, fruit_color, fruit_price)
VALUES
('apple', 'red', 1),
('orange', 'orange', 2),
('pineapple', 'yellow', 3);
INSERT INTO person_fruit (pf_person, pf_fruit)
VALUES
(1, 1),
(1, 2),
(2, 2),
(2, 3),
(3, 1),
(3, 2),
(3, 3);
If you wish to associate the person with an array of fruits, you can do so with a view:
DROP VIEW IF EXISTS person_fruit_summary;
CREATE VIEW person_fruit_summary AS
SELECT
person_id AS pfs_person_id,
max(person_name) AS pfs_person_name,
cast(concat('[', group_concat(json_quote(fruit_name) ORDER BY fruit_name SEPARATOR ','), ']') as json) AS pfs_fruit_name_array
FROM
person
INNER JOIN person_fruit
ON person.person_id = person_fruit.pf_person
INNER JOIN fruit
ON person_fruit.pf_fruit = fruit.fruit_id
GROUP BY
person_id;
The view shows the following data:
+---------------+-----------------+----------------------------------+
| pfs_person_id | pfs_person_name | pfs_fruit_name_array |
+---------------+-----------------+----------------------------------+
| 1 | John | ["apple", "orange"] |
| 2 | Mary | ["orange", "pineapple"] |
| 3 | John | ["apple", "orange", "pineapple"] |
+---------------+-----------------+----------------------------------+
In 5.7.22, you'll want to use JSON_ARRAYAGG, rather than hack the array together from a string.
Use database field type BLOB to store arrays.
Ref: http://us.php.net/manual/en/function.serialize.php
Return Values
Returns a string containing a byte-stream representation of value that
can be stored anywhere.
Note that this is a binary string which may include null bytes, and
needs to be stored and handled as such. For example, serialize()
output should generally be stored in a BLOB field in a database,
rather than a CHAR or TEXT field.
you can store your array using group_Concat like that
INSERT into Table1 (fruits) (SELECT GROUP_CONCAT(fruit_name) from table2)
WHERE ..... //your clause here
HERE an example in fiddle
I'm learning SQL. I find very few information about command line on the web. In every tutorial I've seen on the net they stay for the create/drop/select commands and then pass to phpmyadmin.
I would like to know how to do the following by command line:
- I have 3 tables, imagine: users, cars, fuel;
- Every user has a car (from cars table) and every car has a fuel (from fuel table).
I would like to create relations between them. Not only the user could only select the cars from Cars table but I could check (by join command) who has that car type and what is spending each user for the fuel of that car.
I just want to learn how to do this by command line, it's a self made exercise to be able to solve larger problems, because relationship is one of the most important things on databases and I don't know how to use it and can't find it anywhere.
If you like do work in command line you can do it by
shell> mysql --user=[user_name] --password=[your_password db_name]
this will start the mysql command line tool. It looks like this:
mysql>
now you can do some queries like this:
mysql> show databases; // to list all your dbs
mysql> use [db_name]; // to change to your db
mysql> show tables; // to list all tables of your db
see the manual: http://dev.mysql.com/doc/refman/5.6/en/mysql.html
You can do what you want in command line but it is much more comfortable to use a gui tool like sqlyog, heidisql or the webbased phpmyadmin tool. You can see and edit your data/databases/tables here and you are able to send the same queries via your favourite gui tool.
Using constraints
If you ask for creating relations it sounds like you want to create constrains.
You dont really need constrains for a database schema. It is possible to build what you want without any constraint. The relations itself are only in your brain.
Little example with users and cars (MYISAM tables)
table user
- id_user (int)
- name (varchar)
table car
- id_car (int)
- name (varchar)
- fi_user (int)
Create Query:
CREATE TABLE `user` (
`id_user` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 64 ) NOT NULL
) ENGINE = MYISAM ;
CREATE TABLE `car` (
`id_car` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 64 ) NOT NULL ,
`fi_user` INT NOT NULL
) ENGINE = MYISAM ;
This is all you need. Simple put the id of your user into the fi_user field of your car and you have a relation.
INSERT INTO `user` (`id_user`,`name`) VALUES (1, 'testuser');
INSERT INTO `car` (`id_car`, `name`, `fi_user`) VALUES (1, 'testcar', 1);
Now you can do:
SELECT * FROM `car` c JOIN `user` u ON (u.id_user=c.fi_user) WHERE 1
It is possible to use constraints with INNODB but you dont have to use them.
I prefere MYISAM because it is faster. Please read this to find out what constraints are and why you may like to use it
http://dev.mysql.com/doc/refman/5.6/en/innodb-foreign-key-constraints.html
So I have an application that has several modules (think of modules as different pages), each module has a set of permissions; view, add, edit, delete
I want each user role to have privileges for each module, for example
Role A Permissions
Module 1 -> view
Module 2 -> add, edit
Module 3 -> view, add, edit, delete
etc.
How can I design the database to support that and how would I go about implementing it using bitwise operators (or would there be a more efficient way for this particular case?)
I already have the user, user_role and role tables but I'm unsure on how to design the Module table.
Here I am showing how we can implement it with Mysql.
Below is a sample tables with some sample data:
Table 1 : Permission table to store permission name along with it bit like 1,2,4,8..etc (multiple of 2)
CREATE TABLE IF NOT EXISTS `permission` (
`bit` int(11) NOT NULL,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`bit`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Insert some sample data into the table.
INSERT INTO `permission` (`bit`, `name`) VALUES
(1, 'User-Add'),
(2, 'User-Edit'),
(4, 'User-Delete'),
(8, 'User-View'),
(16, 'Blog-Add'),
(32, 'Blog-Edit'),
(64, 'Blog-Delete'),
(128, 'Blog-View');
Table 2: User table to store user id,name and role. Role will be calculated as sum of permissions.
Example :
If user 'Ketan' having permission of 'User-Add' (bit=1) and 'Blog-Delete' (bit-64) so role will be 65 (1+64).
If user 'Mehata' having permission of 'Blog-View' (bit=128) and 'User-Delete' (bit-4) so role will be 132 (128+4).
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`role` int(11) NOT NULL,
`created_date` datetime NOT NULL
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Sample data-
INSERT INTO `user` (`id`, `name`, `role`, `created_date`)
VALUES (NULL, 'Ketan', '65', '2013-01-09 00:00:00'),
(NULL, 'Mehata', '132', '2013-01-09 00:00:00');
Loding permission of user
After login if we want to load user permission than we can query below to get the permissions:
SELECT permission.bit,permission.name
FROM user LEFT JOIN permission ON user.role & permission.bit
WHERE user.id = 1
Here user.role "&" permission.bit is a Bitwise operator which will give output as -
User-Add - 1
Blog-Delete - 64
If we want to check whether a particular user have user-edit permission or not-
SELECT * FROM `user`
WHERE role & (select bit from permission where name='user-edit')
Output = No rows.
Click here for more information.
If you decide to use a bitmask, remember that the number of permissions you can keep track of is limited (you can track 31 permissions in a signed 4-byte integer database column). Each permission would then be assigned a value that is a power of two (1, 2, 4, 8, etc), and you could perform bitwise operations to check for permission matches.
From what you're looking to accomplish, I would suggest creating a role_has_module_privs table instead. This approach is much more scalable, and more efficient from a querying perspective. But if you have a finite number of combinations, bitmasks may be more efficient.
I'm creating a blog, and am storing user permissions (to post/edit/delete blog posts) in a mysql table.
Should I create one column per permission, or combine all percussions into a string in one column such as 101 would mean that a user could post and delete but not edit.
The reason I ask is that I am worried about having too many column in my table.
First of all, I would rule out combining all permissions into a single field. It seems economical at first, but it can turn into a bit of a problem if you will ever need to expand or modify your permissions structure.
Creating a column for each permission in the user table is a good design for a simple system, but may limit your future expandability.
I recommend implementing a many-to-many relationship between users and permissions. This allows you to add as many types of permissions you want without changing the schema. It is very easy to query with a simple join, and is portable to other databases.
You accomplish this by creating two new tables. Assuming the following schema:
CREATE TABLE `users` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(100),
-- other user fields --
);
We can add the m2m permissions schema like this:
CREATE TABLE `permissions` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(50) NOT NULL UNIQUE,
);
CREATE TABLE `users_permissions` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY_KEY,
`user_id` INT NOT NULL,
`permission_id` INT NOT NULL
);
You might then add some sample users and permissions:
INSERT INTO `users` (DEFAULT, 'joe');
INSERT INTO `users` (DEFAULT, 'beth');
INSERT INTO `users` (DEFAULT, 'frank');
INSERT INTO `permissions` (DEFAULT, 'Administrator');
INSERT INTO `permissions` (DEFAULT, 'Write Blog');
INSERT INTO `permissions` (DEFAULT, 'Edit Blog');
INSERT INTO `permissions` (DEFAULT, 'Delete Blog');
And finally you can associate users with permissions like so:
-- joe gets all permissions
INSERT INTO `permissions` (DEFAULT, 1, 1);
INSERT INTO `permissions` (DEFAULT, 1, 2);
INSERT INTO `permissions` (DEFAULT, 1, 3);
INSERT INTO `permissions` (DEFAULT, 1, 4);
-- beth can write and edit
INSERT INTO `permissions` (DEFAULT, 2, 2);
INSERT INTO `permissions` (DEFAULT, 2, 3);
-- frank can only write
INSERT INTO `permissions` (DEFAULT, 3, 2);
For a smaller blog, you may not need a flexible schema like this, but it is a proven design. If you like, you can also take this one step further and create a role system. This works by giving each user a role (one-to-many), and each role has a number of permissions (many-to-many). This way permissions don't need to be set on a per-user basis, and you can simply assign them a role like "Administrator", or "Editor" or "Contributor", along with the associated permissions for that role.
My choice would be separate columns. Makes it easier to query later on if you are looking for specific permissions.
You might want to check out some standard designs on permissions, no need to invent the wheel for the 4th time :)
Consider mysql's (nonstandard) SET type. More experienced coders may favor a bit field (which is really what's underneath mysql's SET type).
Don't use a string because:
A string is a very inefficient way to store bit values -- you're using a byte per flag, where you only need a single bit
Querying against that field would require heinous string manipulations, and would never be efficient
I'd say it would be fine to put a Post, Edit and Delete Column.
But, if you take Wordpress's take on permissions, they simply serialize it into an array, and then store that array in a table of Settings (4 Columns: UserID, Settings Key, Setting Value). I think Wordpress's method only really works if you aren't going to give permissions their own table.
Another method is to do a User_ID - Permission Relationship Table. In one column put the User_ID, and in the other the permission. But, make each row a permissions. IE, if you wanted to give User ID 1 all permissions it would be:
Permissions: Add: 1, Edit: 2, Delete: 3
Table
Row 1: UserID: 1 Permission: 1
Row 2: UserID: 1 Permission: 2
Row 3: UserID: 1 Permission: 3
You could use bitwise combinations (bit fields) within one column like
const READ = 1;
const WRITE = 2;
const DELETE = 4;
...
so resulting permission would be
read-only: 1
read-write: 3
read & delete, but not write: 5
and so on...
To check the permission in a bit field, your query has to look like
SELECT * FROM table t WHERE t.permission & required_permission
with required_permission being the bitwise or of the required permission flags.
But I also would recommend to check out some resources to find out about standard designs...
I need to make the databse structure for a real estate website where users can create properties of many types and with many features related to the property.
The main categories will be:
1. House (subtype apartment, house, loft)
2. Commercial (subtype: hotels, buildings, offices, factory)
3. Terrains (subtype: urban, agricola, industrial, for sports)
All this above can have many features defined, for example an apartment: light, gas, # of rooms, bathrooms, floor number, balcony, and so on, and this features are diferent from one property type to another.
At the moment I have one master table named property containing the basic info like address and price, and three subtables property_house, property_commercial, and property_terrain with as many fields as features a property can have.
Is this structure okay? I need to do the creation and modification of all the property types into one form maybe with 3-4 steps and will differ from one property type to another. Will it be easier if I have just one master table like property and a second property_features where to store the property_id, feature_name, and feature_value? What's best for performance and maintaining? What would you people vote for?
Thank you! :)
I have experience with both ways you have mentioned. ( I'm co-developer of iRealty http://www.irealtysoft.com/ ver 3. and ver 4 have two different storage methods). After several years of dealing with both ways I recommend to create a single table for all properties. This pattern is called Single Table Inheritance (http://martinfowler.com/eaaCatalog/singleTableInheritance.html by Martin Fowler).
I see only two disadvantages of this method:
field names should be unique within all property types
a lot of records will have NULL in about a have of their columns which wastes disk space a little bit
A the same time with this database structure all CRUD routines are very simple and straightforward. You will save a lot of time building queries/ORM layer. With this structure you are free to create indexes and utilize arithmetic and other database functions in WHERE clauses and avoid costly JOINs.
The disk space is cheap, the development time is expensive.
The | property_id | feature_name | feature_value | allows to keep the same database structure when changing fields and property types, which is good when you have a complex upgrade/update routines. If you are going to build a single (production) instance application the upgrades should not be an issue. However this method make CRUD model complex and hence more expensive and bug-prone. (More code --- more bugs.)
Well, are these three main categories set in stone? Is there a possibility of a fourth one cropping up in the future? I would probably go with something like this:
CREATE TABLE property (
id int not null auto_increment,
name varchar(250) not null,
property_type int not null,
property_subtype int not null,
primary key(id)
);
CREATE TABLE property_type (
id int not null auto_increment,
name varchar(250) not null,
primary key(id)
);
CREATE TABLE property_subtype (
id int not null auto_increment,
type int not null,
name varchar(250) not null,
primary key(id)
);
CREATE TABLE property_feature (
id int not null auto_increment,
property int not null,
feature int not null,
value varchar(250) not null,
primary key(id)
);
CREATE TABLE property_feature (
id int not null auto_increment,
feature int not null,
value varchar(250) not null,
primary key(id)
);
I think this would be the most effective in the long run and the most flexible if - when - the time comes.
With this structure, you can then add the data like this:
mysql> INSERT INTO property_type (name) VALUES ('House'),('Commercial'),('Terrains');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> INSERT INTO property_subtype (type, name) VALUES (1, 'Apartment'),(1, 'House'), (1,'Loft');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> INSERT INTO subtype_feature (subtype, name) VALUES (1, 'Light'),(1, 'Floor #');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> INSERT INTO property (name, property_type, property_subtype) VALUES ('Som
e Apartment', 1, 1);
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO property_feature (feature, value) VALUES (1, 'Yes'),(2, '5th');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> INSERT INTO property_feature (property, feature, value) VALUES (1, 1, 'Yes'),(1, 2, '5th');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
You can then get all the features of a particular property pretty easily:
mysql> SELECT s.name, f.value FROM property_feature f INNER JOIN subtype_feature
s ON f.feature = s.id WHERE f.property = 1;
+---------+-------+
| name | value |
+---------+-------+
| Light | Yes |
| Floor # | 5th |
+---------+-------+
2 rows in set (0.00 sec)