I'm currently working with a large table that contains several hundred different types of records (with around 100 million rows in total). To fit all of these types of records into the same table, it has ~400 nvarchar(1000)
"flexible" columns that are either filled with some form of data or left null
, depending on the record type (specified by a typeId
). Most record types don't use anywhere near that many columns and so are mostly null
, but a few of them do. Users are able to add or modify record types through the system's UI, and can define which fields are stored in which columns.
Most queries on this table are on a single record type at a time. The queries may filter or sort on any of the flexible columns in use by the record type, but some are much more likely to be filtered/sorted on than others. However, given that the same column might be important/unimportant/unused depending on the record type, there are no indexes on any of the flexible columns.
I'm not sure why this design was originally chosen, but I have been considering different options for a redesign, as it is not very performant. Furthermore, I'm unable to make any updates to the current table schema, as this causes SQL Server to attempt to verify that each row remains within the row size limit, and this process takes too long to feasibly complete.
Some options I've considered so far:
- Combining all flexible columns into a single
nvarchar(max)
column that contains record data in JSON format. In a test, this resulted in generally slower queries, seemingly due to the overhead of querying with SQL Server's JSON functions. It also ballooned the storage size of the table, due to all the extra characters needed for JSON objects/properties, and because the table could no longer effectively use compression. - Storing one record field/value per row in a new table, which I see from other questions is usually called EAV. In a test, this also resulted in slower queries, due to the additional joining required. It also resulted in much slower inserts, as it seems inserting many small rows takes noticeably longer than one large row.
- Creating a new table for each record type. This has not yet been tested, but I'm unsure if it would be wise to create so many tables in SQL Server for this data. It would also present challenges in maintaining these tables, given that record types can be added or modified by users (I suppose it might involve dynamic SQL to add tables/columns, but I'm unsure how removing columns would be handled without losing historical data). However, it would have the huge benefit of enabling the use of proper indexes, as well as column types that better reflect the data.
I'm hoping to get some feedback on best practices in handling this kind of data, and whether option 3 is the right direction to explore next, or if there are better options I haven't yet considered.