Question

I have a statement that selects the substring of a charindex as follows:

SELECT SUBSTRING(StringField, 5, CHARINDEX('ABC', StringField) - 5)...
WHERE 
CHARINDEX('ABC', StringField) > 5

When I run the above statement in a select query, the results are returned just fine. When I run the above statement in a schema bound indexed view, I get this error:

Invalid length parameter passed to the LEFT or SUBSTRING function

To solve the problem I will write a function to get the Max of the CharIndex and 0 as to remove the possibilities of a negative value. But does anyone know why the where clause would not be filtering out the select statement?

Was it helpful?

Solution

Because order of operations behind the scenes in a query isn't guaranteed.

I'm guessing if you check the execution plan you will see it's doing both checks in parallel - this because neither operation can use an index!

SQL needs to load every row of that field into memory anyways, so it's processing it for both criteria at the same time.

You can try WITH (MAXDOP(1)) as a query hint to see if that keeps the issue from appearing, or you could do a subselect to force execution order:

SELECT SUBSTRING(StringField, 5, CHARINDEX('ABC', StringField) - 5)...
FROM (
      SELECT Stringfield 
      FROM Table 
      WHERE CHARINDEX('ABC', StringField) > 5
      ) as [X]

I had a similar issue once when I was checking to see if a field was numeric using PATINDEX and one of the columns in my view was converting this to an int - I got conversion errors because the engine was converting every row since my filters were not SARGable.

OTHER TIPS

This is an ugly workaround but you could do something like this perhaps:

DECLARE @x TABLE(StringField VARCHAR(32));

INSERT @x SELECT 'ABC'
UNION ALL SELECT 'A'
UNION ALL SELECT 'AAAAAAAABC';

SELECT SUBSTRING(StringField, 5, CHARINDEX('ABC', StringField) - 5)
FROM @x
WHERE CHARINDEX('ABC', StringField) > 5;

SELECT SUBSTRING(StringField,
    CASE WHEN CHARINDEX('ABC', StringField) > 5 THEN 5 ELSE 1 END,
    CHARINDEX('ABC', StringField) - 
    CASE WHEN CHARINDEX('ABC', StringField) > 5 THEN 5 ELSE 0 END)
FROM @x
WHERE CHARINDEX('ABC', StringField) > 5;

Both yield:

---
AAA

But I suspect the latter will be allowed in your view. It's ugly but unfortunately unless you dump the filtered data to a #temp table first (or try to see if MAXDOP reliably removes the issue) you're not going to have much control over order of processing.

Another idea is to try putting a computed column in the table (but I'm not sure that will help if you are trying to create an indexed view - there may still be complications). Or to use a filtered index with that expression, instead of an indexed view. There could be several "solutions" if we know what the indexed view is meant to accomplish and what version of SQL Server you are using.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top