web-dev-qa-db-fra.com

Erreur CTE (Nvarchar à Numérique)

J'utilise un CTE pour convertir les SSRS stockés ProC en BO stocké Proc, comme apparemment, je ne peux pas utiliser de tables Temps avec des objets métier.

J'ai cette requête:

;WITH cte1
AS
( 
    SELECT  cv.issue  
          , cv.customfield  
          , CAST(STRINGVALUE AS NUMERIC) AS priority_num  
    --INTO    #temp_priority_val  
    FROM    proddb1.customfieldvalue cv WITH (NOLOCK)  
            INNER JOIN proddb1.customfield e WITH (NOLOCK)
                 ON  cv.CUSTOMFIELD = e.id 
                 AND e.cfname = 'Issue Priority')  

,cte2
AS
( 
    SELECT  a.ISSUE  
          , f.customvalue priority_num  
    --INTO    #temp_priority  
    FROM    cte1 a 
            INNER JOIN proddb1.customfieldoption f WITH (NOLOCK)
                 ON  a.CUSTOMFIELD = f.CUSTOMFIELD        
                 AND CAST(a.priority_num AS NUMERIC) = f.id) 

SELECT * FROM cte2 

Je continue à obtenir un Error converting data type nvarchar to numeric. Erreur sur cette exécution. Le priority_num colonne est une colonne Nvarparar qui est lancée dans le premier CTE. Lorsque j'exécute cela dans la déclaration originale, les tables TEMP fonctionnaient parfaitement. Ce doit être quelque chose avec la périmètre CTE?

5
Andy

Vous ne pouvez pas facilement contrôler l'ordre dans lequel SQL Server évaluera le contenu d'une colonne qui n'utilise pas le type de données correct (ou a une précision incompatible). Si vous essayez de lancer une colonne nvarchar à Numérique, même si vous avez des filtres qui devraient éliminer toutes les valeurs non numériques à partir de la considération, SQL Server peut toujours essayer ces premiers (voir plainte d'Erland à ce sujet sur UserVoice =). Il y a des cas où vous pouvez nidifier des CTES jusqu'à ce que les vaches rentrent à la maison, mais SQL Server poussera toujours cette évaluation à un endroit où vous ne l'attendez pas.

Tant que les agrégats et les fonctions du texte complet ne sont pas impliqués (voir ici et ici ), vous pouvez forcer cette évaluation avant la conversion à l'aide de CASE expression. Voici une version simplifiée de votre requête sans CTES:

SELECT cfv.issue,
  priority_num = CONVERT(NUMERIC(something, something), 
    CASE WHEN ISNUMERIC(cfo.customvalue)=1 THEN cfo.customvalue END)
FROM
  proddb1.customfieldvalue AS cfv
  INNER JOIN proddb1.customfield AS cf
  ON cfv.CUSTOMFIELD = cf.id
  INNER JOIN proddb1.customfieldoption AS cfo
  ON cfv.CUSTOMFIELD = cfo.CUSTOMFIELD
  AND CONVERT(NUMERIC(something, something), 
    CASE WHEN ISNUMERIC(cfo.customvalue)=1 THEN cfo.customvalue END) = cfo.id
  WHERE cf.cfname = 'Issue Priority';

Vous devrez changer something, something à la bonne précision/échelle (ou utilisez l'un des types INT si vous n'avez pas besoin de décimales). Vous ne devez jamais déclarer des types de variation sans spécifier la longueur - Ce message de blog est à peu près Varchar, mais il s'applique vraiment à tous les types .

Si vous êtes sur SQL Server 2012 (il est toujours utile d'inclure des informations telles que la version de SQL Server que vous utilisez), vous pouvez simplifier ceci:

CONVERT(NUMERIC(something, something), 
    CASE WHEN ISNUMERIC(cfo.customvalue)=1 THEN cfo.customvalue END)

pour ça:

TRY_CONVERT(NUMERIC(something, something), cfo.customvalue)

Et cela sera effectivement plus fiable (puisque ISNUMERIC peut retourner 1 et toujours échouer au temps de conversion pour des types spécifiques). J'ai blogué sur ce chemin de retour en 2002 .

11
Aaron Bertrand