I generally use this query as a first step in determining missing indexes.
DECLARE @DBName VARCHAR(50) = 'yourDB';
SELECT [Total Cost] = ROUND(avg_total_user_cost * avg_user_impact * (user_seeks + user_scans), 0),
avg_user_impact, TableName = statement, [EqualityUsage] = equality_columns,
[InequalityUsage] = inequality_columns, [Include Cloumns] = included_columns
FROM sys.dm_db_missing_index_groups g
INNER JOIN sys.dm_db_missing_index_group_stats s ON s.group_handle = g.index_group_handle
INNER JOIN sys.dm_db_missing_index_details d ON d.index_handle = g.index_handle
WHERE DB_NAME(database_id) = @DBName
ORDER BY [Total Cost] DESC;
Which could be written like this: -
DECLARE @DBName VARCHAR(50) = 'yourDB';
SELECT
[Total Cost] = ROUND(avg_total_user_cost * avg_user_impact * (user_seeks + user_scans), 0),
avg_user_impact, TableName = statement,
'CREATE NONCLUSTERED INDEX [IX_' + OBJECT_NAME(d.object_id, database_id) + '_' +
REPLACE(REPLACE(REPLACE(ISNULL(REPLACE(d.equality_columns,', ','_') + '_','') +
ISNULL(REPLACE(d.inequality_columns,', ','_'),''),'[',''),']','') + ']','_]',']') +
' ON ' + QUOTENAME(OBJECT_SCHEMA_NAME(d.object_id, database_id)) + '.' + QUOTENAME(OBJECT_NAME(d.object_id, database_id)) +
' ( ' + CASE WHEN d.equality_columns IS NOT NULL
THEN d.equality_columns + CASE WHEN d.inequality_columns IS NOT NULL
THEN ', ' + d.inequality_columns
ELSE '' END
ELSE ISNULL(d.inequality_columns,'') END + ' ) ' + ISNULL('INCLUDE ( ' + d.included_columns + ' )','') +
';' AS [Create Index Statement]
FROM sys.dm_db_missing_index_groups g
INNER JOIN sys.dm_db_missing_index_group_stats s ON s.group_handle = g.index_group_handle
INNER JOIN sys.dm_db_missing_index_details d ON d.index_handle = g.index_handle
WHERE DB_NAME(database_id) = @DBName
ORDER BY [Total Cost] DESC;
Now, I'm not saying that indexes that are suggested from the above are the ones that need creating. They're more of a guide that points me towards the areas of the database that I need to investigate.