Domanda

Ho una tabella con una colonna varchar, e voglio trovare i valori che corrispondono a un certo numero. Quindi diciamo che colonna contiene le seguenti voci (tranne che con milioni di righe nella vita reale):

123456789012
2345678
3456
23 45
713?2
00123456789012

Così ho deciso che voglio tutte le righe che sono numericamente 123456789012 scrivere una dichiarazione che sembra qualcosa di simile:

SELECT * FROM MyTable WHERE CAST(MyColumn as bigint) = 123456789012

Si dovrebbe restituire la prima e l'ultima fila, ma invece l'intera query scoppia perché non può convertire il "23 45" e "713? 2" per bigint.

C'è un altro modo per eseguire la conversione che restituirà NULL per i valori che non possono convertire?

È stato utile?

Soluzione

SQL Server non garantisce booleano operatore cortocircuito, vedere su SQL Server operatore booleano cortocircuito . Quindi tutto soluzione con ISNUMERIC(...) AND CAST(...) sono fondamentalmente errata (possono lavorare, ma hey possono arbitrariamente fallire in seguito dependiong sul piano generato). Una soluzione migliore sta usando CASE, come suggerisce Thomas: CASE ISNUMERIC(...) WHEN 1 THEN CAST(...) ELSE NULL END. Ma, come ha sottolineato GBN, ISNUMERIC è notoriamente schizzinoso nell'identificare cosa significa 'numerici' e molti casi in cui ci si aspetta che ritorni 0 restituisce 1. Quindi mescolando il caso con il LIKE:

CASE WHEN MyRow NOT LIKE '%[^0-9]%' THEN CAST(MyRow as bigint) ELSE NULL END

Ma il vero problema è che se si dispone di milioni di righe e si deve a loro la ricerca in questo modo, si finisce sempre per la scansione di end-to-end in quanto l'espressione non è SARG-in grado (non importa quanto riscriviamo esso). Il vero problema qui è la purezza dei dati, e deve essere affrontata a livello appropriato, in cui è popolato il dato. Un'altra cosa da considerare è se è possibile creare una colonna calcolata persistente con questa espressione e creare un indice filtrato su di esso che elimina NULL (es. Non numerico). Ciò potrebbe accelerare le cose un po '.

Altri suggerimenti

Se si utilizza SQL Server 2012 è possibile utilizzare i 2 nuovi metodi:

Entrambi i metodi sono equivalenti. Tornano un cast valore al tipo di dati specificato se il cast ha successo; in caso contrario, restituisce null. L'unica differenza è che convertono è SQL Server specifica, CAST è ANSI. utilizzando CAST renderanno il vostro codice più portabile (anche se non so se altri attrezzi provider di database TRY_CAST)

ISNUMERIC accetterà i valori stringa e vuota come 1,23 o 5E-04 così potrebbe essere inaffidabile.

E non sai che ordine le cose saranno valutati in modo che possa ancora fallire (SQL è dichiarativa, non procedurale, quindi la clausola WHERE probabilmente non essere valutati da sinistra a destra)

  • si desidera accettare il valore che consistono solo dei personaggi 0-9
  • è necessario materializzare il filtro "numero" in modo che sia applicata prima CAST

Qualcosa di simile:

SELECT 
*
FROM
   (
   SELECT TOP 2000000000 *
   FROM MyTable
   WHERE MyColumn NOT LIKE '%[^0-9]%'  --double negative rejects anything except 0-9
   ORDER BY MyColumn 
   ) foo
WHERE 
    CAST(MyColumn as bigint) = 123456789012 --applied after number check

Modifica:. Rapido esempio che non riesce

CREATE TABLE #foo (bigintstring varchar(100))
INSERT #foo (bigintstring )VALUES ('1.23')
INSERT #foo (bigintstring )VALUES ('1 23')
INSERT #foo (bigintstring )VALUES ('123')

SELECT * FROM #foo
WHERE
   ISNUMERIC(bigintstring) = 1 
   AND 
   CAST(bigintstring AS bigint) = 123
SELECT * 
    FROM MyTable 
    WHERE ISNUMERIC(MyRow) = 1
        AND CAST(MyRow as float) = 123456789012

L'ISNUMERIC () funzione dovrebbe darti quello che ti serve.

SELECT * FROM MyTable
WHERE ISNUMERIC(MyRow) = 1
AND CAST(MyRow as bigint) = 123456789012

E per aggiungere una dichiarazione caso come Thomas ha suggerito:

SELECT * FROM MyTable
WHERE CASE(ISNUMERIC(MyRow)
        WHEN 1 THEN CAST(MyRow as bigint)
        ELSE NULL
      END = 123456789012

http://msdn.microsoft.com/en-us/library/ ms186272.aspx

SELECT *
FROM MyTable
WHERE (ISNUMERIC(MyColumn) = 1) AND (CAST(MyColumn as bigint) = 123456789012)

Inoltre è possibile utilizzare un'istruzione caso al fine di ottenere i valori nulli.

SELECT 
    CASE
        WHEN (ISNUMERIC(MyColumn) = 1) THEN CAST(MyColumn as bigint)
        ELSE NULL
    END AS 'MyColumnAsBigInt'
FROM tableName

Se avete bisogno di ulteriore filtraggio, per i valori numerici che non sono validi ed essere gettato a bigint, è possibile utilizzare il seguente invece di ISNUMERIC:

PATINDEX ( '% [^ 0-9]%', MyColumn)) = 0

Se avete bisogno di valori decimali al posto dei numeri interi, il cast di galleggiare invece e cambiare l'espressione regolare a '% [^ 0-9.]%'

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top