Ok, so I almost have this figured out.
SELECT f.`file_id`, f.`name`
FROM `file` AS f
WHERE f.`file_id` NOT IN(
SELECT fi.`file_id`
FROM `file` AS fi
INNER JOIN `file_keyword` AS fki
ON fi.`file_id` = fki.`file_id`
WHERE fki.`keyword` NOT IN('first', 'second', 'third')
);
There you can just list the keywords in the last NOT IN clause.
$keywords = array ("first", "second", "third");
$keyword_list = implode(", ", $keywords);
$sql = <<<SQL
SELECT f.`file_id`, f.`name`
FROM `file` AS f
WHERE f.`file_id` NOT IN(
SELECT fi.`file_id`
FROM `file` AS fi
INNER JOIN `file_keyword` AS fki
ON fi.`file_id` = fki.`file_id`
WHERE fki.`keyword` NOT IN({$keyword_list})
);
SQL;
echo $sql;
Only problem is, this returns files where all of it's keywords are in the list, but it doesn't have to match every keyword in the list.
For example, if I have created these rows:
INSERT INTO `file`(`name`) VALUES ('File1'), ('File2');
INSERT INTO `file_keyword`(`file_id`, `keyword`)
VALUES
(1, 'first'), (1, 'second'),
(2, 'third'), (2, 'fourth');
Running the above query, it would return 'File1', even tho it only matches two out of three keywords.