Domanda

Quindi ho appena trascorso 5 ore a risolvere un problema che si è rivelato non solo dovuto alla vecchio non affidabile ISNUMERIC ma sembra che il mio problema appaia solo quando l'UDF in cui < code> ISNUMERIC è dichiarato WITH SCHEMABINDING ed è chiamato all'interno di un proc memorizzato (ho molto lavoro da fare per distillarlo in un caso di test, ma il mio primo bisogno è per sostituirlo con qualcosa di affidabile).

Eventuali raccomandazioni su sostituzioni valide ed efficienti per ISNUMERIC () . Ovviamente ci sono davvero delle variazioni per int , money , ecc., Ma cosa usano le persone (preferibilmente in T-SQL, perché su questo progetto sono limitato a SQL Server perché si tratta di un'attività di elaborazione dati da SQL Server a SQL Server ad alto volume)?

È stato utile?

Soluzione

È possibile utilizzare le funzioni T-SQL TRY_CAST () o TRY_CONVERT () se si esegue SQL Server 2012 come Bacon Bits menziona nei commenti:

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

Se si utilizza SQL 2008 R2 o versioni precedenti, è necessario utilizzare una funzione CLR .NET e avvolgere System.Decimal.TryParse ().

Altri suggerimenti

A seconda delle circostanze e delle caratteristiche prestazionali della convalida, a volte utilizzo invece una variante dell'espressione LIKE. Ad esempio:

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

Nota che questo esempio specifico è abbastanza ingenuo. Non garantisce che il valore sia valido per la conversione in un particolare tipo di dati. Inoltre non consente segni +/- o punti decimali se ne hai bisogno.

Un'altra opzione potrebbe essere quella di scrivere una stored procedure estesa in una lingua come C, trasformarla in una DLL e renderla disponibile a SQL Server.

Non immagino che ci vorrebbe troppe righe di codice da fare, e probabilmente sarebbe più veloce della scrittura di una Stored procedure gestita in .NET, perché non dovresti incorrere in un extra sentito dal caricamento del CLR.

Ecco un po 'di informazioni: http://msdn.microsoft.com/en-us/library/ms175200. aspx

Ecco del codice C ++ che potrebbe funzionare per te:

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;
}

Seguendo il percorso CLR .NET, potresti usare un'espressione regolare, in particolare se ti aspetti un determinato intervallo di numeri.

SQL 2005 ed espressioni regolari

Generalmente, come pratica, cerco di non lasciare dati non tipizzati nel database, poiché è più adatto a gestirli a livello di applicazione o per le importazioni in batch a gestirli in SQL Integration Services in modo che i dati arrivino digitato correttamente dall'inizio.

Ho dovuto farlo molte volte in passato e di solito il modo più veloce è scrivere la tua funzione definita dall'utente per verificare che i dati siano nel formato che ti aspetti, come il più delle volte l'overhead per chiamare un il proc memorizzato esteso o il codice gestito per una semplice convalida è più lento del semplice farlo in T-SQL.

Secondo il supporto di Microsoft, l'unico modo efficiente per sostituire la funzione UDF è scrivere la propria versione della funzione .NET.

Naturalmente, se l'amministratore del database lo consente :).

Il mio no :(.

Per SQL Server 2005 e versioni successive ... approfitta di 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

stampa 0.

Cambia @test = '01234' o @test = '01234.5' e stampa 1.

Hai mai intenzione di gestire sistemi numerici al di fuori della tua lingua (umana), come il cinese ecc.? In tal caso, suggerirei di utilizzare la libreria libuninum .

Che ne dici di implementare queste due funzioni:

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

Fonte originale

IsNumeric () sembra avere problemi con spazi, 'D', 'E', simboli di dollari e ogni sorta di altri caratteri. Ciò che in genere vogliamo è qualcosa che ci dice se un CAST o un CONVERT avranno successo. Questo UDF, sebbene non sia la soluzione più veloce, ha funzionato molto bene per me.

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

A partire da SQL 2012, è possibile utilizzare la funzione TRY_PARSE () anziché ISNUMERIC ().

SELECT
 TRY_PARSE('123' as int) as '123'
,TRY_PARSE('abc' as int) as 'abc'
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top