Question

J'ai une déclaration qui sélectionne la sous-chaîne d'un charindex comme suit:

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

Lorsque j'exécute l'instruction ci-dessus dans une requête sélectionnée, les résultats sont très bien renvoyés. Lorsque j'exécute l'instruction ci-dessus dans une vue indexée liée au schéma, j'obtiens cette erreur:

Invalid length parameter passed to the LEFT or SUBSTRING function

Pour résoudre le problème, j'écrirai une fonction pour obtenir le maximum du charindex et 0 pour supprimer les possibilités d'une valeur négative. Mais quelqu'un sait-il pourquoi la clause WHERE ne filtrerait pas l'instruction SELECT?

Était-ce utile?

La solution

Parce que l'ordre des opérations dans les coulisses d'une requête n'est pas garanti.

Je suppose que si vous vérifiez le plan d'exécution, vous verrez qu'il fait les deux vérifications en parallèle - car aucune des opérations ne peut utiliser un index!

SQL doit quand même charger chaque rangée de ce champ en mémoire, il le traite donc en même temps pour les deux critères.

Tu peux essayer WITH (MAXDOP(1)) Comme indice de requête pour voir si cela empêche le problème d'apparaître, ou vous pourriez faire un sous-sélection pour forcer l'ordre d'exécution:

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

J'ai eu un problème similaire une fois quand je vérifiais si un champ était numérique en utilisant PATINDEX Et l'une des colonnes à mon avis a été de convertir cela en un int - J'ai eu des erreurs de conversion parce que le moteur convertissait chaque rangée car mes filtres n'étaient pas sargables.

Autres conseils

C'est une solution de contournement laide, mais vous pourriez faire quelque chose comme ça peut-être:

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;

Les deux rendement:

---
AAA

Mais je soupçonne que ce dernier sera autorisé à votre avis. C'est moche mais malheureusement à moins que vous ne vidiez d'abord les données filtrées sur une table #tempe (ou essayez de voir si MAXDOP Supprime de manière fiable le problème), vous n'aurez pas beaucoup de contrôle sur l'ordre de traitement.

Une autre idée est d'essayer de mettre une colonne calculée dans le tableau (mais je ne suis pas sûr que cela vous aidera si vous essayez de créer une vue indexée - il peut toujours y avoir des complications). Ou pour utiliser un indice filtré avec cette expression, au lieu d'une vue indexée. Il pourrait y avoir plusieurs «solutions» si nous savons ce que la vue indexée est destinée à accomplir et quelle version de SQL Server que vous utilisez.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top