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.
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)
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.