I do not know if this is the best or most optimized way to do it, but here's one way to do it. :)
First, the tables you'll need: (I've left out name columns etc. for simplicity)
CREATE TABLE skills (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
) ENGINE=InnoDB;
CREATE TABLE users (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
) ENGINE=InnoDB;
CREATE TABLE projects (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
) ENGINE=InnoDB;
CREATE TABLE user_skills (
user_id INT UNSIGNED NOT NULL,
skill_id INT UNSIGNED NOT NULL,
PRIMARY KEY (user_id, skill_id),
FOREIGN KEY (user_id) REFERENCES users (id),
FOREIGN KEY (skill_id) REFERENCES skills (id)
) ENGINE=InnoDB;
CREATE TABLE project_skills (
project_id INT UNSIGNED NOT NULL,
skill_id INT UNSIGNED NOT NULL,
PRIMARY KEY (project_id, skill_id),
FOREIGN KEY (project_id) REFERENCES projects (id),
FOREIGN KEY (skill_id) REFERENCES skills (id)
) ENGINE=InnoDB;
If you want to show all users that have at least the skills numbered 1, 3 and 5:
SELECT user_id
FROM user_skills
WHERE skill_id IN (1, 3, 5)
GROUP BY user_id
HAVING COUNT(*) = 3;
So here we first filter out all the irrelevant skills with the where clause and then group by user. After that, COUNT(*) tells us how many matching skills each user had. The having clause then only shows users with 3 matching skills, which means those users had at least the skills we were looking for.
If you want to show all users that have skills 1, 3 and 5 but no other skills:
SELECT s1.user_id
FROM (
SELECT user_id, COUNT(*) count
FROM user_skills
GROUP BY user_id
) s1
INNER JOIN (
SELECT user_id, COUNT(*) count
FROM user_skills
WHERE skill_id IN (1, 3, 5)
GROUP BY user_id
) s2 ON (s1.user_id = s2.user_id)
WHERE s1.count = 3 AND s2.count = 3;
Here the first subquery finds the total skill count for each user and the second subquery finds the number of matching skills for each user. If both are 3, the user has exactly the skills we are looking for.
If you want to find all "compatible" users and projects, that is, all (project, user) -pairs where the user has at least all the skills the project needs:
SELECT s2.project_id, s2.user_id
FROM (
SELECT project_id, COUNT(*) count
FROM project_skills
GROUP BY project_id
) s1
INNER JOIN (
SELECT project_skills.project_id, user_skills.user_id, COUNT(*) count
FROM project_skills
INNER JOIN user_skills ON (user_skills.skill_id = project_skills.skill_id)
GROUP BY project_skills.project_id, user_skills.user_id
) s2 ON (s1.project_id = s2.project_id)
WHERE s2.count = s1.count;
Here, the first subquery finds out the number of skills needed for each project. The second subquery finds out how many common skills there are for each (project, user) -pair. It does this by joining project_skills and user_skills on skill and then grouping by project and user. After that, COUNT(*) tells how many common skills each project and user has. Finally, we join the two subqueries and only show (project, user) -pairs where the number of common skills equals the number of skills the project needs.