From MSDN:
"Insert Operations Occur on Ascending or Descending Key Columns
Statistics on ascending or descending key columns, such as IDENTITY or real-time timestamp columns, might require more frequent statistics updates than the query optimizer performs. Insert operations append new values to ascending or descending columns. The number of rows added might be too small to trigger a statistics update. If statistics are not up-to-date and queries select from the most recently added rows, the current statistics will not have cardinality estimates for these new values. This can result in inaccurate cardinality estimates and slow query performance.
For example, a query that selects from the most recent sales order dates will have inaccurate cardinality estimates if the statistics are not updated to include cardinality estimates for the most recent sales order dates.
After Maintenance Operations
Consider updating statistics after performing maintenance procedures that change the distribution of data, such as truncating a table or performing a bulk insert of a large percentage of the rows. This can avoid future delays in query processing while queries wait for automatic statistics updates."
You might use "EXEC sp_updatestats" from time to time on your system (scheduled some time) or use the function STATS_DATE on all the objects and see when their statistics were actually updated last time and if there was too much time since then, use UPDATE STATISTICS for that particular object. In my experience, even with Automatic statistics enabled we're still forced to update stats from time to time, because of insert operations that didn't trigger automatic update.
To add my personal code (used in a weekly job that builds dynamic statements for stats update):
select distinct
'update statistics [' + stats.SchemaName + '].[' + stats.TableName + ']'
+ case when stats.RowCnt > 50000 then ' with sample 30 percent;'
else
';' end
as UpdateStatement
from (
select
ss.name SchemaName,
so.name TableName,
so.id ObjectId,
st.name AS StatsName,
STATS_DATE(st.object_id, st.stats_id) AS LastStatisticsUpdateDate
, si.RowModCtr
, (select case si2.RowCnt when 0 then 1 else si2.RowCnt end from sysindexes si2 where si2.id = si.id and si2.indid in (0,1)) RowCnt
from sys.stats st
join sysindexes si on st.object_id = si.id and st.stats_id = si.indid
join sysobjects so on so.id = si.id and so.xtype = 'U' --user table
join sys.schemas ss on ss.schema_id = so.uid
) stats
where cast(stats.RowModCtr as float)/cast(stats.RowCnt as FLOAT)*100 >= 10 --more than 10% of the rows have changed
or ( --update statistics that were not updated for more than 3 months (and rows no > 0)
datediff(month, stats.LastStatisticsUpdateDate, getdate()) >= 3
and stats.RowCnt > 0
)
Here I get all objects where didn't have stats updated for more then 3 months or since the last stats update it had more than 10% of the rows changed.