Вы действительно не можете сделать это напрямую с языком на основе множеств, таким как SQL.
Ваша простая версия "И" также не будет работать, если у вас нет дубликатов (productId, tagId).
Для ваших сложных отношений необходимо разбить ваш запрос на несколько подзапросов. Первый разрыв по всем пунктам «И»:
WHERE tag_id IN (tag1, tag2, tag3)
WHERE tag_id IN (tag4, tag5, tag6)
WHERE tag_id IN (tag7, tag8, tag9)
Затем выполните ПЕРЕКЛЮЧЕНИЕ результатов запроса.
Если какой-либо из этих подзапросов не является просто списком «ИЛИ», но, в свою очередь, содержит «И» в более сложной логической структуре, вам необходимо рекурсивно разбивать эти подзапросы.
Другими словами, вы можете рекурсивно разбивать логическое дерево по предложениям «И», а затем на каждом уровне дерева выполнять ИНТЕРСЕКТ результатов запроса.
Выполнение этого, вероятно, будет намного быстрее, чем генерация огромного SQL, который вернет результат за один раз - потому что каждый из простого списка OR может использовать индекс, который у вас есть для tag_id.