7
votes

Comment puis-je interroger des données numériques sur un champ non numérique?

J'ai une requête que je viens de trouver dans la base de données qui échoue à l'égard de l'automne. Le gist de base de la requête: xxx pré>

maintenant, MyField ne contient pas de données numériques dans toutes les lignes [il est de type NvarcharN] ... mais cette requête a évidemment été conçue de telle sorte qu'il Seulement se soucie des lignes où les données de ce champ sont numériques. p>

Le problème avec ceci est que T-SQL est que T-SQL (proche tel que je comprends) ne court pas la clause de l'endroit qui l'entraîne ainsi de dépasser des enregistrements où les données ne sont pas numériques à l'exception: p>

msg 245, niveau 16, état 1, ligne 1 La conversion a échoué lors de la conversion de la valeur NVARCHAR '/ A' sur le type de données INT. CODE> P>

Courgé de déversement Toutes les lignes où MyField est numérique dans une table temporaire, puis interrogeant que les lignes où le champ est dans la plage spécifiée, que puis-je faire qui est optimale? p>

Mon premier analyse uniquement pour tenter d'analyser les données retournées et de voir ce qui se passait était: P>

Select *
From (
   Select *
   From table
   Where IsNull(myField, '') <> ''
   And IsNumeric(myField) = 1
) t0
Where Convert(int, myField) Between @StartRange And @EndRange


2 commentaires

La table dérivée ne se matérialise pas d'abord alors le où l'application appliquée. Il est traité plus comme une vue sur laquelle l'optimisateur ne réécrirea que le 2e à être comme le premier comme d'un algèbre de POV relationnel, ils sont identiques.


Selon [cette page] ( msdn.microsoft. Com / fr-US / Bibliothèque / AA226054 (SQL.80% 29.As px) , il n'est pas nécessaire de convertir explicitement nvchar à int .


4 Réponses :


3
votes

Vous devez forcer SQL à évaluer les expressions dans un certain ordre. Voici une solution xxx

et une autre xxx

  • La première technique est "matérialisation intermédiaire": elle force une sorte sur une table de travail.
  • Le 2e repose sur l'évaluation de la commande de cas est garanti
  • ni est joli ou whizzy

    SQL est déclaratif: vous dites à l'optimiseur ce que vous voulez, pas comment le faire. Les astuces au-dessus de la force des choses à faire dans un certain ordre.


5 commentaires

En fait, la question prouve que cette implémentation SQL n'est pas déclarante. Vous dites à l'optimiseur ce que vous voulez, et cela ne convertit pas votre déclaration dans un plan d'exécution viable. Dans un véritable langage déclaratif, même la condition convert (int, myfield) entre @startrange et @endrange et isnumeric (myfield) = 1 est correct. (Facile avec la logique ternaire, où null et false est false )


@Msalters: Je dirais que les entre les deux ou les isnumériques sont contradictoires à ce niveau. SQL fait sa chose déclarante et décider de la meilleure façon d'obtenir les données, de bot de l'ordre d'évaluer les conditions. Entre est plus susceptible d'utiliser un indice, ISnumeric n'est pas. Réécrire une requête d'ordures en raison de données de la merde ne fait pas partie de la définition "déclarative"


Désolé, pensais que le "logique ternaire" le rendait clairement. Le côté convert de l'expression évaluerait à NULL (erreur) chaque fois que le côté iSnumeric est évalué sur false. Un vrai langage déclaratif ne jugerait pas cela une "requête d'ordures" comme le résultat est très bien défini.


@Msalters: je suis en désaccord. Faux et NULL est faux aussi. "Et" nécessite que toutes les expressions soient évaluées: il n'y a pas de court-circuit et dans SQL Server. S'appuyant sur l'évaluation de la clause WHERE dans un certain ordre est procédural , non? Le fait que les types de données ont mal, la conception est faux et les tests contradictoires n'invalident pas "SQL est déclaratif". Sauf si vous voulez que SQL permet de toutes sortes de permutations de mauvais design ... Juste pourquoi existe-t-il sur une colonne de chaîne quand même?


Je ne pense pas que vous puissiez compter sur un court-circuit ou dans SQL Server.



1
votes

Je ne sais pas si cela vous aide, mais j'ai lu quelque part qu'une conversion incorrecte à l'aide de Convert générera toujours une erreur dans SQL. Donc, je pense qu'il serait préférable d'utiliser un cas dans la clause Où éviter de convertir pour courir sur toutes les lignes


1 commentaires

Cela fonctionne, je ne suis pas sûr que c'est optimal, mais c'est au moins un pas plus loin que moi auparavant. Merci.



1
votes

Utilisez un cas code> instruction.

declare @StartRange int
declare @EndRange int

set @StartRange = 1
set @EndRange = 3

select *
from TestData
WHERE Case WHEN ISNUMERIC(Value) = 0 THEN 0
            WHEN Value IS NULL THEN 0
            WHEN Value = '' THEN 0
            WHEN CONVERT(int, Value) BETWEEN @StartRange AND @EndRange THEN 1
            END = 1


0 commentaires

6
votes

Isnumeric ne vous dit que si la chaîne peut être convertie en l'un des types numériques de em> dans SQL Server. Il peut être en mesure de la convertir en argent ou à un flotteur, mais peut ne pas être capable de le convertir en int.

Changer votre P>

----------- ----------- -----------
1           1           1

(1 row(s) affected)


2 commentaires

Ceci en combinaison avec la déclaration de cas fonctionne comme un charme. Vous avez effectivement m'aidé à résoudre un débordement arithmétique convertissant un nombre trop long pour Bigint Même. Donc cela était utile. +1


@Benalabaster - Excellent - j'ai donc répondu à votre question de suivi avant d'avoir eu l'occasion de le poster? :-)