2015-11-02 28 views
5

Sto cercando di convertire la stringa esadecimale di un numero molto elevato ad una colonna numericaPostgreSQL: convertire la stringa esadecimale di un numero molto elevato per un NUMERICO

CREATE OR REPLACE FUNCTION hex_to_int(hexval varchar) RETURNS NUMERIC AS $$ 
DECLARE 
    result NUMERIC; 
BEGIN 
EXECUTE 'SELECT x''' || hexval || '''::NUMERIC(40,0)' INTO result; 
RETURN result; 
END; 
$$ 
LANGUAGE 'plpgsql' IMMUTABLE STRICT; 

Ci sto provando a fare questo:

select hex_to_int(tx.value) from internal_transaction tx 

L'errore che ottengo è:

[42846] ERROR: cannot cast type bit to numeric Where: PL/pgSQL function hex_to_int(character varying) line 5 at EXECUTE statement 

risposta

4

Questa è una sorta di forza bruta e non a tutti i B ulletproof:

CREATE OR REPLACE FUNCTION hex_to_int(hexval varchar) RETURNS numeric AS $$ 
DECLARE 
    result NUMERIC; 
    i integer; 
    len integer; 
    hexchar varchar; 
BEGIN 

    result := 0; 
    len := length(hexval); 

    for i in 1..len loop 
    hexchar := substr(hexval, len - i + 1, 1); 
    result := result + 16^(i - 1) * case 
     when hexchar between '0' and '9' then cast (hexchar as int) 
     when upper (hexchar) between 'A' and 'F' then ascii(upper(hexchar)) - 55 
    end; 
    end loop; 

RETURN result; 
END; 
$$ 
LANGUAGE 'plpgsql' IMMUTABLE STRICT; 

select hex_to_int('12AE34F'); -- returns 19587919 

Oppure, se avete installato PL/Perl, si può lasciar fare il lavoro pesante:

CREATE OR REPLACE FUNCTION hex_to_int_perl(varchar) 
    RETURNS numeric AS 
$BODY$ 
    my ($hex) = @_; 
    return sprintf "%d", hex($hex); 
$BODY$ 
    LANGUAGE plperl VOLATILE 
    COST 100; 

select hex_to_int_perl('12AE34F'); -- returns 19587919 

Non credo che il non-Perl si lavora con i numeri negativi, e sono abbastanza sicuro che entrambi ti daranno cattivi risultati se inserirai un valore non esadecimale, ma quelli sarebbero scenari abbastanza facili da catturare e gestire, a seconda di cosa vuoi che faccia la funzione.

+3

Grazie, molto utile –

2

Il mio caso d'uso per questo era la conversione di valori hash SHA-1 esadecimali in numeri interi. Senza attenzione alla precisione numerica in tutta la funzione, questo caso d'uso rivela facilmente le carenze; ma certamente i casi d'uso con numeri "molto grandi" ancora più grandi sono facili da identificare.

dato qualche hash SHA-1, la prima delle soluzioni della risposta accettata ha prodotto i seguenti:

SELECT hex_to_int('356e90d2a2d414ba8757ec2ab91f2f19c481d4c3'); 
-- returns 305042208670409000000000000000000000000000000000 

SELECT hex_to_int('aaa9f7193cc8efe7e98145b0f8d9ae5f1712c25b'); 
-- returns 974318782301086000000000000000000000000000000000 

Naturalmente ogni risultato essere di destra imbottito con tanti zeri è un segno di precisione inadeguato per l'uso Astuccio.

Qui è dove ho finito per ottenere la precisione di cui avevo bisogno:

CREATE OR REPLACE FUNCTION hex_to_int(hexVal varchar) RETURNS numeric(1000) AS $$ 
DECLARE 
    intVal numeric(1000) := 0; 
    hexLength integer; 
    i integer; 
    hexDigit varchar; 
BEGIN 
    hexLength := length(hexVal); 

    FOR i IN 1..hexLength 
    LOOP 
     hexDigit := substr(hexVal, hexLength - i + 1, 1); 
     intVal := 
      intVal + 
      CASE 
       WHEN hexDigit BETWEEN '0' AND '9' THEN CAST(hexDigit AS numeric(1000)) 
       WHEN upper(hexDigit) BETWEEN 'A' AND 'F' THEN CAST(ascii(upper(hexDigit)) - 55 AS numeric(1000)) 
      END * 
      CAST(16 AS numeric(1000))^CAST(i - 1 AS numeric(1000)); 
    END LOOP; 

    RETURN intVal; 
END; 
$$ 
LANGUAGE 'plpgsql' IMMUTABLE STRICT; 


SELECT hex_to_int('356e90d2a2d414ba8757ec2ab91f2f19c481d4c3'); 
-- returns 305042208670409212880202819376501392142011323587 

SELECT hex_to_int('aaa9f7193cc8efe7e98145b0f8d9ae5f1712c25b'); 
-- returns 974318782301085717223606317572453925850501530203 
+1

Touche ... molto meglio di quanto la mia risposta. Questa dovrebbe essere la soluzione accettata. – Hambone