As is common with software, the SQL caching issue is more subtle than it looks. For example, let's look at caching of an ad-hoc SQL query:
-- First, clear the cache
DBCC FREEPROCCACHE
-- Look at what executable plans are in cache
SELECT sc.*
FROM master.dbo.syscacheobjects AS sc
WHERE sc.cacheobjtype = 'Executable Plan'
-- Execute the following statement
SELECT t.*
FROM pubs.dbo.titles AS t
WHERE t.price = 19.99
-- Look at what executable plans are in cache and you'll
-- find that there's a plan for a NUMERIC(4,2)
SELECT sc.*
FROM master.dbo.syscacheobjects AS sc
WHERE sc.cacheobjtype = 'Executable Plan'
-- If you execute the EXACT same statement with a 4,2
-- then you will get THAT plan. But if you execute with a 5,2
-- then you'll get a new plan. Try this:
SELECT t.*
FROM pubs.dbo.titles AS t
WHERE price = 199.99
-- Look again at the cached executable plans, and you'll see a NEW one...
SELECT sc.*
FROM master.dbo.syscacheobjects AS sc
WHERE sc.cacheobjtype = 'Executable Plan'
You can however use sp_executesql to type the parameters and force the plan to be cached. All subsequent uses will get the same plan, but some people don't like the obscurity of this approach:
DECLARE @ExecStr nvarchar(4000)
SELECT @ExecStr = N'SELECT t.* FROM dbo.titles AS t WHERE t.price = @price'
EXEC sp_executesql @ExecStr, N'@price money', 19.99
Now if you create a similar query as a stored procedure with a parameter for the price, the plan will be created and cached in memory (not disk) on the first execution, and reused regardless of the parameter's value.
The fact that the stored procedure plan is cached in memory and not on disk means that it will fall out of the cache on a server restart or due to low re-use. It can also fall out of cache if the data on which the procedure depends changes enough to cause the statistics to be invalidated. This causes SQL Server to invalidate the plan.