web-dev-qa-db-fra.com

Fonction PL / SQL pour recevoir un nombre et renvoyer son format binaire

J'essaie d'écrire une fonction pour recevoir un nombre et retourner son format binaire. C'est ce que j'ai développé jusqu'à présent et c'est correct pour les entrées: 4,8,16 mais il ne renvoie pas le format binaire correct pour les autres nombres. Je n'ai pas pu trouver le problème et je me demandais si vous pouviez trouver le problème?

create or replace function Show_Binary(i_Number in Number) Return Varchar2 
  AS

     V_Binary varchar2(50) := '';
     i_Number2 Number := i_Number;
       Begin
         While i_Number2>=2 LOOP

          V_Binary := V_Binary || Remainder(i_Number2, 2);
          i_Number2 := i_Number2 / 2;

       End LOOP;
          V_Binary := V_Binary || TO_CHAR(i_Number2);
          select reverse (V_Binary) into V_Binary from dual;
      return (V_Binary);
   End;
4
Pantea Tourang

Ce n'est pas un problème Oracle ou PL/SQL, mais une question d'implémentation de l'algorithme approprié.

Voici un exemple:

https://www.orafaq.com/wiki/Binary

CREATE OR REPLACE FUNCTION dec2bin (N in number) RETURN varchar2 IS
  binval varchar2(64);
  N2     number := N;
BEGIN
  while ( N2 > 0 ) loop
     binval := mod(N2, 2) || binval;
     N2 := trunc( N2 / 2 );
  end loop;
  return binval;
END dec2bin;
/

SQL> SELECT dec2bin(22) FROM dual;
DEC2BIN(22)
----------------
10110

La surcharge de LISTAGG + requête hiérarchique + autres fonctions SQL est plus grande que la surcharge d'une simple fonction PL/SQL.

SQL> with g as (select * from dual connect by level <= 1000) select count(distinct dec2bin(rownum)) as bincd from g,g;

     BINCD
----------
   1000000

Elapsed: 00:00:13.75

with g as (select * from dual connect by level <= 1000),
g2 as (select rownum as r from g, g)
select count(distinct bin) as bincd from (
select (SELECT LISTAGG(SIGN(BITAND(r, POWER(2,LEVEL-1))),'') 
       WITHIN GROUP(ORDER BY LEVEL DESC) bin
FROM dual
CONNECT BY POWER(2, LEVEL-1)<=r
) as bin 
from g2
);

     BINCD
----------
   1000000

Elapsed: 00:00:35.53

Et cela sans DF Pragma . Sur 12c et supérieur, l'utilisation de la fonction devient légèrement plus rapide avec l'ajout de PRAGMA_UDF.

CREATE OR REPLACE FUNCTION dec2bin (N in number) RETURN varchar2 IS
  PRAGMA UDF; -- <==================================================== MAGIC
  binval varchar2(64);
  N2     number := N;
BEGIN
  while ( N2 > 0 ) loop
     binval := mod(N2, 2) || binval;
     N2 := trunc( N2 / 2 );
  end loop;
  return binval;
END dec2bin;
/

SQL> with g as (select * from dual connect by level <= 1000) select count(distinct dec2bin(rownum)) as bincd from g,g;

     BINCD
----------
   1000000

Elapsed: 00:00:12.01
11
Balazs Papp

Pas besoin d'utiliser une fonction - vous pouvez le faire en SQL (Oracle).

SELECT LISTAGG(SIGN(BITAND(43, POWER(2,LEVEL-1))),'') 
       WITHIN GROUP(ORDER BY LEVEL DESC) bin
FROM dual
CONNECT BY POWER(2, LEVEL-1)<=43;

Résultat:

BIN
101011

J'ai trouvé cet extrait mortel ici et le violon est ici . Pour le nombre 43, remplacez simplement votre colonne de choix. Il est probablement possible de le faire en utilisant des récursifs CTE, mais c'est un peu au-dessus de mon niveau de salaire :-).

Et pour inverser le processus, vous pouvez utiliser cet extrait

WITH INPUT AS
(SELECT REVERSE('1000') AS X FROM DUAL)
SELECT SUM(TO_NUMBER(SUBSTR(X,LEVEL,1)*POWER(2,LEVEL-1))) AS OUTPUT
FROM INPUT CONNECT BY LEVEL<=LENGTH(X);

Résultat:

OUTPUT
8

De ici - dbfiddle ici . Encore une fois, un CTE récursif pourrait faire l'affaire. Encore une fois, pour "1000", remplacez simplement votre colonne.

Juste pour les coups de pied, j'ai trouvé une autre fonction qui fonctionnera (avec des ajustements) sur les anciennes versions de bases de données qui n'ont pas de CTE récursif. Du excellent site Orafaq ici .

SELECT 
  DECODE(BITAND(VALUE, 128), 128, '1', '0') ||
  DECODE(BITAND(VALUE, 64), 64, '1', '0') ||
  DECODE(BITAND(VALUE, 32), 32, '1', '0') ||
  DECODE(BITAND(VALUE, 16), 16, '1', '0') ||
  DECODE(BITAND(VALUE, 8), 8, '1', '0') ||
  DECODE(BITAND(VALUE, 4), 4, '1', '0') ||
  DECODE(BITAND(VALUE, 2), 2, '1', '0') ||
  DECODE(BITAND(VALUE, 1), 1, '1', '0') as bin_number from 
(select 8 as value from dual) A;

Résultat:

MY_BIN
1000

Violon ici . Les plus observateurs d'entre vous remarqueront que ce code ne supprime pas les zéros de tête - le code ci-dessus doit être encapsulé (enterré?) Dans cet extrait

select replace(ltrim(replace(ColumnName,'0',' ')),' ','0')

qui peut être trouvé ici .

4
Vérace