web-dev-qa-db-ja.com

数値を受け取り、そのバイナリ形式を返すPL / SQL関数

数値を受け取り、そのバイナリ形式を返す関数を記述しようとしています。これは私がこれまでに開発したものであり、入力:4、8、16では問題ありませんが、他の数値の正しいバイナリ形式を返しません。私は問題を見つけることができず、あなたが問題を見つけることができるかどうか疑問に思っていましたか?

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

これはOracleやPL/SQLの問題ではなく、適切なアルゴリズムの実装に関する問題です。

次に例を示します。

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

LISTAGG +階層クエリ+その他のSQL関数のオーバーヘッドは、単純な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

そして、これには DF Pragma がありません。 12c以降では、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

関数を使用する必要はありません Oracleの)SQLで実行できます。

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;

結果:

BIN
101011

この致命的なスニペット---(here を見つけました。フィドルは here です。 43という数値は、選択した列に置き換えてください。おそらく再帰的なCTEsを使用してこれを行うことは可能ですが、それは私の給与等級を少し上回っています:-)。

プロセスを逆にするには、このスニペットを使用できます

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

結果:

OUTPUT
8

ここ から-dbfiddle ここ 。繰り返しますが、再帰的なCTEでうまくいく場合があります。繰り返しますが、「1000」の場合は、列を置き換えてください。

キックのためだけに、再帰的なCTEを持たない古いバージョンのデータベースで(微調整で)機能する別の関数を見つけました。 excellentOrafaqサイト here から。

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;

結果:

MY_BIN
1000

フィドル ここ 。あなたがより注意深く見ると、そのコードは先行ゼロを削除しないことに気づくでしょう-上記のコードは、このスニペットで包む(entombed?)必要があります。

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

これは here で見つかります。

4
Vérace