Des remplacements efficaces D'ISNUMERIC () sur SQL Server?

donc je viens de passer 5 heures à résoudre un problème qui s'est avéré être due non seulement à la Vieux peu fiable ISNUMERIC mais il semble que mon problème apparaît seulement quand L'UDF dans lequel ISNUMERIC est déclaré WITH SCHEMABINDING et est appelé dans un proc stocké (j'ai beaucoup de travail à faire pour le distiller vers le bas dans un cas de test, mais mon premier besoin est de le remplacer par quelque chose de fiable).

toute recommandation relative à une remplacements pour ISNUMERIC() . Évidemment, il y a vraiment besoin de variations pour int , money , etc., mais qu'est-ce que les gens utilisent (de préférence en T-SQL, parce que sur ce projet, je suis limité à SQL Server parce que c'est un haut volume SQL Server à SQL Server tâche de traitement de données)?

17
demandé sur Tieson T. 2008-11-23 05:54:08

11 réponses

vous pouvez utiliser les fonctions T-SQL TRY_CAST () ou TRY_CONVERT() si vous utilisez SQL Server 2012 comme les Bits Bacon mentionnés dans les commentaires:

SELECT CASE WHEN TRY_CAST('foo' AS INT) IS NULL THEN 0 ELSE 1 END

SELECT CASE WHEN TRY_CAST(1 AS INT) IS NULL THEN 0 ELSE 1 END

si vous utilisez SQL 2008 R2 ou plus, vous devrez utiliser une fonction .NET CLR, et le système wrap.Décimal.TryParse().

20
répondu Dave Markle 2015-12-01 14:49:57

selon les circonstances et les caractéristiques de performance de la validation, j'utilise parfois une variante de l'expression similaire. Par exemple:

NOT LIKE '%[^0-9]%'

Notez que cet exemple est assez naïf. Il ne garantit pas que la valeur est valide pour la conversion à un type de données particulier. Il ne tient pas compte non plus des signes +/- ou des points décimaux si vous en avez besoin.

16
répondu RoadWarrior 2008-11-23 15:58:58

une autre option pourrait être d'écrire une procédure stockée prolongée dans une langue telle que C, la rendre dans une DLL et la rendre disponible pour SQL Server.

Je ne pense pas qu'il faudrait trop de lignes de code pour le faire, et il serait probablement plus rapide que d'écrire une procédure gérée stockée dans .NET, parce que vous ne seriez pas encourir la surchauffe supplémentaire de charger le CLR.

Voici une information: http://msdn.microsoft.com/en-us/library/ms175200.aspx

voici un peu de code C++ qui pourrait fonctionner pour vous:

using namespace std;

int checkNumber() {
  int number = 0;
  cin >> number;
  cin.ignore(numeric_limits<int>::max(), '\n');

  if (!cin || cin.gcount() != 1)
    cout << "Not a number.";
  else
    cout << "Your entered: " << number;
  return 0;
}
6
répondu Sam Schutte 2009-01-29 17:34:34

en suivant la route .NET CLR, vous pouvez utiliser une expression régulière, surtout si vous vous attendez à une certaine plage de nombres.

SQL 2005 et expressions régulières

3
répondu Lurker Indeed 2009-01-27 20:41:07

en général, en tant que pratique, j'essaie de ne pas laisser les données non dactylographiées dans la base de données, car il est mieux adapté pour le traiter soit à la couche application, ou pour les importations par lots le traiter dans les Services D'intégration SQL de sorte que les données sont dactylographiées correctement dès le début.

j'ai dû le faire de nombreuses fois dans le passé et habituellement la manière la plus rapide est d'écrire votre propre fonction définie par l'utilisateur pour vérifier les données est dans le format que vous attendez, comme la plupart du temps le l'appel direct à un code proc ou managé stocké étendu pour une validation simple est plus lent que juste le faire en T-SQL.

2
répondu duckworth 2008-11-23 13:15:02

selon le soutien de Microsoft la seule façon efficace pour remplacer la fonction UDF est d'écrire votre propre version de fonction .NET.

bien sûr, si votre administrateur de base de données permet de ça :).

Mine de ne pas :(.

1
répondu Grzegorz Gierlik 2009-01-27 20:16:52

pour SQL Server 2005 et au-dessus.... profitez de try/catch...

declare @test varchar(10), @num decimal
select @test = '0123A'

begin try
    select @num = cast(@test as decimal)
    print '1'
end try 
begin catch
    print '0'
end catch

imprime 0.

Modification @test = '01234" ou @test = '01234.5" et de l'impression 1.

1
répondu c'est moi 2011-10-10 14:02:58

allez-vous jamais être en train de manipuler des systèmes de nombres en dehors de votre propre langue (humaine), comme le Chinois etc? Si c'est le cas, je suggère d'utiliser la bibliothèque libuninum .

0
répondu bugmagnet 2008-11-23 08:22:46

comment mettre en œuvre ces deux fonctions:

CREATE FUNCTION dbo.isReallyNumeric  
(  
    @num VARCHAR(64)  
)  
RETURNS BIT  
BEGIN  
    IF LEFT(@num, 1) = '-'  
        SET @num = SUBSTRING(@num, 2, LEN(@num))  

    DECLARE @pos TINYINT  

    SET @pos = 1 + LEN(@num) - CHARINDEX('.', REVERSE(@num))  

    RETURN CASE  
    WHEN PATINDEX('%[^0-9.-]%', @num) = 0  
        AND @num NOT IN ('.', '-', '+', '^') 
        AND LEN(@num)>0  
        AND @num NOT LIKE '%-%' 
        AND  
        (  
            ((@pos = LEN(@num)+1)  
            OR @pos = CHARINDEX('.', @num))  
        )  
    THEN  
        1  
    ELSE  
    0  
    END  
END  
GO  

CREATE FUNCTION dbo.isReallyInteger  
(  
    @num VARCHAR(64)  
)  
RETURNS BIT  
BEGIN  
    IF LEFT(@num, 1) = '-'  
        SET @num = SUBSTRING(@num, 2, LEN(@num))  

    RETURN CASE  
    WHEN PATINDEX('%[^0-9-]%', @num) = 0  
        AND CHARINDEX('-', @num) <= 1  
        AND @num NOT IN ('.', '-', '+', '^') 
        AND LEN(@num)>0  
        AND @num NOT LIKE '%-%' 
    THEN  
        1  
    ELSE  
        0  
    END  
END  
GO

Source Originale

0
répondu GateKiller 2009-01-30 09:44:15

IsNumeric () semble avoir des problèmes avec les espaces, "D", "E", les signes dollar et toutes sortes d'autres caractères. Ce que nous voulons typiquement c'est quelque chose qui nous dit si un casting ou un CONVERT réussira. Cet UDF, bien que n'étant pas la solution la plus rapide, a très bien fonctionné pour moi.

create function dbo.udf_IsNumeric(@str varchar(50))
  returns int
as
begin
  declare @rtn int
  select @rtn =
    case
      when ltrim(rtrim(@str)) in('.', '-', '-.', '+', '+.') then 0
      when ltrim(rtrim(@str)) like '%[^-+.0-9]%' then 0
      else isnumeric(@str)
    end
  return @rtn
end
0
répondu mattmc3 2009-10-17 02:02:28
À partir de 2012, vous pouvez utiliser la fonction TRY_PARSE() au lieu de ISNUMERIC().

SELECT
 TRY_PARSE('123' as int) as '123'
,TRY_PARSE('abc' as int) as 'abc'
0
répondu CleanBold 2015-10-07 11:02:25