Это классический ОО-дизайн для несоответствия импеданса реляционных таблиц. Описанный вами дизайн таблицы известен как «таблица на подкласс». Все три наиболее распространенных проекта - это компромиссы по сравнению с тем, как на самом деле выглядят ваши объекты в вашем приложении:
- Таблица по конкретному классу
- Таблица в иерархии
- Таблица на подкласс
Дизайн, который вам не нравится - «где таблицы имеют 100 столбцов, а большинство значений NULL» - это 2. одна таблица для хранения всей иерархии специализации. Это наименее гибкий подход по всем причинам, в том числе - если вашему приложению требуется новый подкласс, необходимо добавить столбцы. Конструкция, которую вы описываете, учитывает изменения намного лучше, потому что вы можете добавить расширение, добавив новую таблицу подклассов, описанную значением в product_type.
Оставшийся вариант - 1. Таблица для каждого конкретного класса - обычно нежелательна из-за дублирования, возникающего при реализации всех общих полей в каждой таблице специализации. Хотя преимущества в том, что вам не нужно выполнять никаких объединений, а таблицы подклассов могут даже находиться в разных экземплярах БД в очень большой системе.
Описанный вами дизайн вполне жизнеспособен. Вариант ниже - это может выглядеть, если вы используете инструмент ORM для выполнения операций CRUD. Обратите внимание, как идентификатор в каждой таблице подкласса является значением FK для родительской таблицы в иерархии. Хороший ORM будет автоматически управлять правильной таблицей CRUD подкласса на основе значения значений дискриминатора только в product.id и product.product_type_id. Планируете ли вы использовать ORM или нет, посмотрите документацию присоединенного подкласса hibernate, хотя бы для того, чтобы увидеть решения, которые они приняли.
product
=======
id INT
product_name VARCHAR
product_type_id INT -> Foreign key to product_type.product_type_id
valid_since DATETIME
valid_to DATETIME
magazine
========
id INT -> Foreign key to product.product_id
title VARCHAR
..
web_site
========
id INT -> Foreign key to product.product_id INT
name VARCHAR
..