web-dev-qa-db-fra.com

L'acte de disparition du "paramètre de longueur non valide passé à la fonction LEFT ou SUBSTRING"

J'exécute l'erreur "Paramètre de longueur non valide transmis à la fonction GAUCHE ou SUBSTRING", mais il disparaît et la requête fonctionne lorsque j'inclus la colonne que je passe dans ces fonctions, un indice?

Ne fonctionne pas:

SELECT 
  SQT.QUOTATIONID, 
  UPPER(LEFT(Email.LOCATOR, CHARINDEX('@', Email.Locator) - 1)) AS Manager
  --, Email.LOCATOR
FROM SALESQUOTATIONTABLE AS SQT
INNER JOIN HCMWORKER AS H
    ON SQT.WORKERSALESRESPONSIBLE = H.RECID
INNER JOIN DIRPARTYTABLE AS D
    ON H.PERSON = D.RECID
INNER JOIN LOGISTICSELECTRONICADDRESS AS Email
    ON D.PRIMARYCONTACTEMAIL = Email.RECID

Paramètre de longueur non valide transmis à la fonction LEFT ou SUBSTRING

Travaux:

SELECT 
  SQT.QUOTATIONID, 
  UPPER(LEFT(Email.LOCATOR, CHARINDEX('@', Email.Locator) - 1)) AS Manager 
  , Email.LOCATOR
FROM SALESQUOTATIONTABLE AS SQT
INNER JOIN HCMWORKER AS H
    ON SQT.WORKERSALESRESPONSIBLE = H.RECID
INNER JOIN DIRPARTYTABLE AS D
    ON H.PERSON = D.RECID
INNER JOIN LOGISTICSELECTRONICADDRESS AS Email
    ON D.PRIMARYCONTACTEMAIL = Email.RECID

Ceci est reproductible en continu, et la seule modification que j'ai apportée à la requête a été incluse dans la colonne "Email.LOCATOR". Cette requête fonctionnait depuis des années et a cessé de fonctionner de manière aléatoire aujourd'hui. Je suis presque certain qu'il s'agit d'un problème de données, mais je ne comprends toujours pas pourquoi la sélection de la colonne Email.LOCATOR résout le problème.

6
J.D.

Je pense que le problème est similaire à celui-ci: Comportement étrange dans la fonction TSQL (paramètre avec variable int ou NULL se comporte différemment)?

Plus précisément, ce qu'Aaron Bertrand mentionne dans sa réponse:

... car vous ne pouvez pas toujours compter sur les lignes de filtrage SQL Server avant de tenter des calculs

Je pense que ce qui se passe est que Email.Locator A des valeurs qui ne contiennent pas de @. Lorsque ces valeurs sont traitées, la CHARINDEX() est 0 et la LEFT() est appelée avec le paramètre -1, Donc l'erreur est levée.

Mais pourquoi l'erreur est-elle lancée dans une requête et non dans l'autre? C'est probablement parce que les deux requêtes sont exécutées avec des plans différents. L'optimiseur choisit un plan différent (en raison de la colonne supplémentaire ou de statistiques différentes du mois dernier ou pour une raison quelconque) et toutes les valeurs de la colonne sont lues (et les calculs sont effectués) avant les jointures aux autres tables.


Pour éviter le problème, je vous suggère d'utiliser CASE, en remplaçant

LEFT(Email.Locator, CHARINDEX('@', Email.Locator) - 1)

avec:

LEFT(Email.Locator, CASE WHEN CHARINDEX('@', Email.Locator) > 0 
                         THEN CHARINDEX('@', Email.Locator) - 1
                         ELSE 0
                    END)
8
ypercubeᵀᴹ

Selon toute vraisemblance, le problème est en effet un problème de données. Une nouvelle ligne a été ajoutée à la table LOGISTICSELECTRONICADDRESS, avec un LOCATOR qui ne contient pas de "@".

La modification de la requête signifie que la valeur complète de LOCATOR doit être transmise aux résultats finaux. Dans ce cas, il n'y a aucun avantage particulier à effectuer le calcul jusqu'à ce que le jeu de résultats final soit déterminé, donc SQL Server attend jusqu'à la fin du processus de sélection pour calculer la valeur de Manager.

En fonction des résultats que vous obtenez, sansLOCATOR dans la liste SELECT, SQL Server choisit de calculer la valeur de Manager - avant décider quelles sont les lignes de résultat final. Il est possible que la valeur de Manager soit beaucoup plus petite que la valeur complète de LOCATOR, donc calculer au lieu de transporter la valeur complète de LOCATOR en avant économiserait de la mémoire. Si les données de LOGISTICSELECTRONICADDRESS sont jointes aux données du jeu de résultats avant que certaines des autres tables ne soient jointes, alors le calcul pourrait être effectué sur des lignes qui ne seront pas retournées dans le jeu de résultats final.


Vous n'avez pas demandé comment résoudre ce problème, mais (par souci d'exhaustivité), vous devez vérifier la valeur renvoyée par CHARINDEX. Si vous voulez des lignes où LOCATOR n'a pas de "@", vous pouvez utiliser une instruction CASE:

SELECT SQT.QUOTATIONID,
       CASE WHEN CHARINDEX('@', Email.Locator) > 0
         THEN UPPER(LEFT(Email.LOCATOR, CHARINDEX('@', Email.Locator) - 1))
         ELSE ''
       END AS Manager
FROM SALESQUOTATIONTABLE AS SQT
INNER JOIN HCMWORKER AS H
    ON SQT.WORKERSALESRESPONSIBLE = H.RECID
INNER JOIN DIRPARTYTABLE AS D
    ON H.PERSON = D.RECID
INNER JOIN LOGISTICSELECTRONICADDRESS AS Email
    ON D.PRIMARYCONTACTEMAIL = Email.RECID

(Bien sûr, vous pouvez renvoyer la valeur complète de LOCATOR au lieu d'une chaîne vide, c'est votre appel)

Si vous ne pas voulez voir les lignes où le "@" est manquant, vous pouvez simplement vérifier la clause WHERE:

SELECT SQT.QUOTATIONID,
       UPPER(LEFT(Email.LOCATOR, CHARINDEX('@', Email.Locator) - 1)) AS Manager
FROM SALESQUOTATIONTABLE AS SQT
INNER JOIN HCMWORKER AS H
    ON SQT.WORKERSALESRESPONSIBLE = H.RECID
INNER JOIN DIRPARTYTABLE AS D
    ON H.PERSON = D.RECID
INNER JOIN LOGISTICSELECTRONICADDRESS AS Email
    ON D.PRIMARYCONTACTEMAIL = Email.RECID
WHERE CHARINDEX('@', Email.Locator) > 1

Testé sur une de mes propres tables; cela a bien fonctionné là-bas.

3
RDFozz