Your problem is mainly related to database design. You must add columns dynamically because you have translated what would be a column ( in 3NF ) into a row in your tables. Obviously, this is due to the fact that you allow users to add their own columns - my brain is trembling, but the way the application works :-).
Due to the structure of the MetaDataColumns I’m going to assume that the user has the ability to define a set of column names that they can then select to apply to a single document as they see fit.
I think the problem is that when you try to normalize everything correctly, in a completely de-normalized database you managed to cause a lot of trouble. My solution would be to denormalize the MetaDataValues table. You do not mention which DBMS you use, but MySQL has a hard limit of 4096 columns or 65k bytes. The limit in Oracle is 1000 and 1024 in SQL Server.
If you change the structure of MetaDataValues to the following, you can place at least 332 sets of information. This would be uniquely unique on the UserID , DocumentID , so you could theoretically remove the surrogate ID key.
MetaDataValues: ID int UserID int foreign key DocumentID int foreign key KeyName1 varchar Order1 int Value1 varchar(100) ... KeyNameN varchar OrderN int ValueN varchar(100)
Of course, this sets an upper limit on the number of columns that you can allow an individual user to create up to 332; but, it’s okay to limit the ability of users to go crazy, and anyone who can think of 332 separate bits of metadata to store in a single PDF document somehow deserves a limitation.
If you have users who are especially obsessed with information, you can always declare a second table with the same structure and continue to fill it.
Doing this means that MetaDataColumns will not be used for anything other than displaying user settings for them. You will need to update MetaDataValues every time a change is made, and making sure that you do not overwrite existing information can be a little painful. I would suspect that you would have to do something like selecting an entry before updating it, iterating through KeyName1 .. KeyNameN and populating the first one with no data. Alternatively, you can simply write an absolutely terrible SQL query. In any case, this will become a “fading point”.
Another option is to add an add column to MetaDataColumns , which indicates to which N the column belongs, but this restricts the user to 332 columns, not 332 to the document.
However, your choice from the database is now insanely simple:
select d.*, m.* from DocumentsTable d join MetaDataValues m on d.ID = m.DocumentID and d.UserID = m.UserID where d.UserId = ?
There is no need to try to iterate through the tables, dynamically generating 1000 column select statements. All information is right there and easily accessible for you.
At the end of the day, the “right” answer to your question depends on where you want to spend your time. You want it to take half a second longer to create or update a document or half a second (maybe more) to select the information in this document.
Personally, I believe that users understand that creating something takes time, but there is nothing more annoying than waiting for time to see something.
There is another, social, and not a database solution. Do not let your users create their own columns. Select the most common pieces of metadata that your users want and create them correctly in a normalized form in the database. You can create columns with the correct data type (which ultimately saves you a lot of trouble), and it will be much easier for them. I doubt that you are lucky that this will happen; but it is worth keeping in mind.