Here's a demo of a nice little bug in SQL Server 2005 when using LIKE comparisons for searching.
What is so "nice" about it is that it only manifests itself if your search ends in number 9 followed by a wildcard when
selecting only columns covered by a nonclustered index.
I've been able to reproduce the error on different collations and servers. They were all SQL Server 2005 SP2+. SQL Server 2000 isn't affected by this.
USE tempdb
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Projects](
[id] [INT] IDENTITY(1,1) NOT NULL,
[ProjectNumber] [VARCHAR](12) NULL,
CONSTRAINT [PK_Project_Id] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
CREATE NONCLUSTERED INDEX [IX_Projects_ProjectNumber] ON [dbo].[Projects]
(
[ProjectNumber] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF,
ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
SET NOCOUNT ON;
SET XACT_ABORT ON;
GO
SET IDENTITY_INSERT [dbo].[Projects] ON;
INSERT INTO [dbo].[Projects]([id], [ProjectNumber])
SELECT '6', 'P08.781' UNION ALL
SELECT '5', 'P08.794' UNION ALL
SELECT '4', 'P08.795' UNION ALL
SELECT '2', 'P08.796' UNION ALL
SELECT '3', 'P08.798' UNION ALL
SELECT '1', 'P08.799' UNION ALL
SELECT '7', 'P08.871' UNION ALL
SELECT '8', 'P08.872' UNION ALL
SELECT '9', 'P08.873' UNION ALL
SELECT '10', 'P08.874' UNION ALL
SELECT '11', 'P08.875' UNION ALL
SELECT '12', 'P08.876' UNION ALL
SELECT '13', 'P08.877' UNION ALL
SELECT '14', 'P08.878' UNION ALL
SELECT '15', 'P08.879' UNION ALL
SELECT '16', 'P08.891' UNION ALL
SELECT '17', 'P08.892' UNION ALL
SELECT '18', 'P08.893' UNION ALL
SELECT '19', 'P08.894' UNION ALL
SELECT '20', 'P08.895'
GO
SET IDENTITY_INSERT [dbo].[Projects] OFF;
-- NO ROWS should be 5
SELECT * FROM Projects
WHERE ProjectNumber LIKE N'P08.79%'
-- 5 ROWS
SELECT * FROM Projects
WHERE ProjectNumber LIKE N'P08.79%' COLLATE Latin1_General_CI_AI
-- NO ROWS should be 5
SELECT * FROM Projects
WHERE ProjectNumber LIKE N'P08.89%'
-- 5 ROWS
SELECT * FROM Projects
WHERE ProjectNumber LIKE N'P08.89%' COLLATE Latin1_General_CI_AI
-- 1 ROW
SELECT * FROM Projects
WHERE ProjectNumber LIKE N'P08.78%'
-- 9 ROWS
SELECT * FROM Projects
WHERE ProjectNumber LIKE N'P08.87%'
GO
DROP TABLE [dbo].[Projects]
By looking at the query plan we can see that the internal GetRangeThroughConvert method is used to get the search range in the new range seek optimizations in SQL Server 2005. Of course I have no idea what this method does internally but my guess would be that it uses column statistics to plot the correct search range. You can read more about that in this this post by Craig Freedman who was nice enough to help me confirm that this issue is actually a bug.
This bug is apparently fixed in SQL Server 2008, but I haven't tested this.