I like the entity-attribute-value thing because I can add new fields and have the rows automatically removed when the foreign table row is removed, but I don't like the fact that I can't enforce a data type. And the select queries are complicated.
Are there better ways that don't involve creating a table for each attribute?
If I create a very big table with every possible attribute, will this table take up space even if most rows will have NULL on most columns?
You can enforce data types in an EAV model by using multiple value fields. This gets a bit tricky because you need another column to specify the type and then additional constraints to specify that only one value is filled and that it matches the type.
In most databases, you can handle this using check constraints.
In addition, you can use just a single string value and then enforce contents of the string using check constraints. This is often sufficient. Such constraints make good use of regular expressions in databases that support them.
As for your second question. Each row is going to occupy space for the entity/attribute columns. Whether or not the NULL
value occupies any space depends on the database, but this space would typically be small.