Oracle и SQL Server допускают использование внешних ключей со значением NULL, и легко понять, почему это необходимо.
Представьте, например, дерево, в котором у каждой строки есть родительский ключ, который ссылается на первичный ключ той же таблицы. В дереве должен быть корневой узел, у которого нет родителя, а родительский ключ будет нулевым.
Более осязаемый пример: подумайте о сотрудниках и менеджерах. У некоторых людей в компании, и если это будет только генеральный директор, не будет менеджера. Если бы для идентификатора менеджера в таблице сотрудников было невозможно установить значение NULL, вам бы пришлось создать сотрудника без менеджера - что-то, что просто неправильно, поскольку у него нет реальной корреспонденции.
Теперь, когда мы знаем это, становится очевидным, почему ваши составные ключи ведут себя так же, как и они. Логически, если часть составного элемента NULL, весь ключ является нулевым. Конкатенация строк возвращает NULL, если одна из частей - NULL. Не может быть совпадения, и в этих случаях ограничение не применяется.