I was able to reproduce the issue using your attached project (it's probably the same issue as here with connect item here)
computed columns are first expanded out to the underlying expression and then may or may not be matched back to the computed column later.
The filter in your plan shows that it gets expanded out to
CONVERT(date,[computed-column-index-problem].[dbo].[toTimeZone](CONVERT_IMPLICIT(datetime,[computed-column-index-problem].[dbo].[t].[c2],0),CONVERT_IMPLICIT(nvarchar(max),[computed-column-index-problem].[dbo].[t].[c3],0),CONVERT_IMPLICIT(nvarchar(max),'UTC',0)),0)>=CONVERT_IMPLICIT(date,[@1],0)
AND
CONVERT(date,[computed-column-index-problem].[dbo].[toTimeZone](CONVERT_IMPLICIT(datetime,[computed-column-index-problem].[dbo].[t].[c2],0),CONVERT_IMPLICIT(nvarchar(max),[computed-column-index-problem].[dbo].[t].[c3],0),CONVERT_IMPLICIT(nvarchar(max),'UTC',0)),0)<=CONVERT_IMPLICIT(date,[@2],0)
These implicit casts to nvarchar(max)
appear to be doing the damage. A simple repro that does not require CLR is
DROP TABLE IF EXISTS t
DROP FUNCTION IF EXISTS [dbo].[toTimeZone]
GO
CREATE FUNCTION [dbo].[toTimeZone] (@newTimeZone [NVARCHAR](max))
RETURNS DATE
WITH schemabinding
AS
BEGIN
RETURN DATEFROMPARTS(1970, 01, 02)
END
GO
CREATE TABLE t
(
c1 INT IDENTITY PRIMARY KEY,
c4 AS dbo.toTimeZone(N'UTC') persisted
);
CREATE INDEX i
ON t (c4);
INSERT INTO t
DEFAULT VALUES
SELECT c1
FROM t WITH (forceseek)
WHERE c4 >= '1970-01-02'
AND c4 <= '1970-03-04';
Msg 8622, Level 16, State 1, Line 27 Query processor could not produce
a query plan because of the hints defined in this query. Resubmit the
query without specifying any hints and without using SET FORCEPLAN.
If I change the function definition to
public static DateTime toTimeZone(DateTime dateTime,
[SqlFacet(IsFixedLength=false, IsNullable=true, MaxSize=50)]
string originalTimeZone,
[SqlFacet(IsFixedLength=false, IsNullable=true, MaxSize=50)]
string newTimeZone)
{
return dateTime.AddHours(-8);
}
So the string parameters become nvarchar(50)
. Then it is able to match and give a seek
Specifically it is the second parameter that is being passed the literal UTC
that requires this. If the annotation is applied to only the first parameter then the plan will not produce a seek even with the with (forceseek)
hint. If the annotation is applied to only the second parameter then it can produce a seek - though the plan shows a warning.
persisted
. – Assegaii
. – Deandrato_time_zone
so the chances of anyone else being able to reproduce this on their own systems is now zero. – Indicesi
in your first test. The statistics were way out of date when you ran your test and optimizer made a wrong decision. If you rebuild an index (or create an index) after the table is populated with data, then its statistics would be up-to-date and optmizer would make a correct decision. – Packardupdate statistics t
in the updated test. Execution plan stayed the same. – Deandra