Database indexes missing on foreign keys
See original GitHub issueDescription
UPDATE: I’ve added notes about specific indexes that need to be created below
SQL server does not enforce that indexes are created on FK columns but in many (most) cases they should have indexes since quite often is the case that these columns are queried against.
One important one that is missing is:
CREATE NONCLUSTERED INDEX TEST_IX_umbracoContentVersion_nodeId ON dbo.umbracoContentVersion (nodeId)
We query against this column a lot!
So we definitely need that Index created but there’s probably lots of others…
I found an SQL script that lists all FK columns that are missing indexes from this site: https://www.mssqltips.com/sqlservertip/5004/script-to-identify-all-nonindexed-foreign-keys-in-a-sql-server-database/
For reference, here’s an article describing the benefits of indexing FK columns: https://sqlperformance.com/2012/11/t-sql-queries/benefits-indexing-foreign-keys
Here are the results of that script:
Table_Name | Column_Name |
---|---|
cmsContentType2ContentType | childContentTypeId |
cmsContentTypeAllowedContentType | AllowedId |
cmsDictionary | parent |
cmsDocumentType | templateNodeId |
cmsLanguageText | languageId |
cmsLanguageText | UniqueId |
cmsMember2MemberGroup | MemberGroup |
cmsMemberType | NodeId |
cmsPropertyType | contentTypeId |
cmsPropertyType | dataTypeId |
cmsPropertyType | propertyTypeGroupId |
cmsPropertyTypeGroup | contenttypeNodeId |
cmsTagRelationship | propertyTypeId |
cmsTagRelationship | tagId |
cmsTask | nodeId |
cmsTask | parentUserId |
cmsTask | taskTypeId |
cmsTask | userId |
umbracoAccess | loginNodeId |
umbracoAccess | noAccessNodeId |
umbracoAccessRule | accessId |
umbracoContent | contentTypeId |
umbracoContentSchedule | languageId |
umbracoContentSchedule | nodeId |
nodeId | |
userId | |
umbracoContentVersionCultureVariation | availableUserId |
umbracoDocumentVersion | templateId |
umbracoDomain | domainRootStructureID |
umbracoLog | userId |
umbracoNode | nodeUser |
umbracoRedirectUrl | contentKey |
umbracoRelation | childId |
umbracoRelation | relType |
umbracoUser2NodeNotify | nodeId |
umbracoUser2UserGroup | userGroupId |
umbracoUserGroup | startContentId |
umbracoUserGroup | startMediaId |
umbracoUserLogin | userId |
umbracoUserStartNode | startNode |
umbracoUserStartNode | userId |
What to fix
We definitely need the umbracoContentVersion.nodeId indexed but we should review all of the above and determine if adding indexes to any of these will be beneficial. We only really want to add indexes if they are used in queries in our codebase. Adding too many indexes will slow down data insertion which we don’t want.
In most cases you can find the “DTO” object for a table such as: Umbraco.Core.Persistence.Dtos.ContentVersionDto
and then you can righ click on it’s column that you want to search for to see if it’s used in queries. For example, for the ContentVersionDto.NodeId, i right click this member and say find references in VS, then i can see how many queries it’s used in
How to fix
For indexes that should exist, the DTO classes will need to be updated to declare the index and then a migration will need to be written to add all of the new indexes.
This item has been added to our backlog AB#4131
Issue Analytics
- State:
- Created 4 years ago
- Comments:12 (11 by maintainers)
Hey, I took a look through this last night and found the following uses on those properties. Anything that I could see definitely did a JOIN, SELECT, WHERE, ORDERBY I’ve listed as
needs index
. There’s a few things I couldn’t find (CMSTask for example), and then I’ve listed anything that’s just used for inserting as “no” (I assume they don’t get indexed). There’s also a handful that I didn’t know enough about the codebase to work out, where the property is handed off to another method which may or may not do querying somewhere else.Hopefully it’s a useful start though. I found all of the dtos using the class here
If these make sense, I could maybe look at updating the DTOs to include the relevant attributes. Can you give an example of a DTO that already has an index attribute, and then I can follow that style.
Where SqlSyntax.GetQuotedColumnName("Parent")
Hey @liamlaverty, i think it would be easiest for now to just make the migration simple, don’t worry for now about trying to be fancy and making a single migration to do all of the indexes. Might be easiest to just start with the basics, make one migration for this one index. Don’t worry about reflection, etc…
Maybe later we can combine this into a more fancy approach to multiple indexes at once. In which case we don’t need to use reflection like is being done there, you can get a tables metadata by doing this:
which will give you all of the table data you need.