繁体   English   中英

如何在PostgreSQL中将长NUMERIC整数转换为位字符串?

[英]How can I cast an long NUMERIC integer into a bit string in PostgreSQL?

我正在尝试使用Postgres的pg_similarity扩展来为Django应用中的长整数对(每个20位数字)计算汉明距离,并且很难弄清楚该如何做。 Django似乎没有当前的BitString字段(这是理想的选择,但django_postgres似乎已经失效了),因此我试图将整数转换为SQL查询本身中的位串。 我当前的查询:

    sql = ''' SELECT id, hamming(
        "HashString"::BIT(255),
        %s::BIT(255)
    ) as hamming_distance
    FROM images
    HAVING hamming_distance < %s
    ORDER BY hamming_distance;'''

正在引发数据库错误: cannot cast type numeric to bit 我究竟做错了什么? 我还能尝试什么?

根据手册 ,如果您的“长整数”实际上是“长整数”(即bigint / int8),则强制转换是正确的方法:

regress=> SELECT ('1324'::bigint)::bit(64);
                               bit                                
------------------------------------------------------------------
 0000000000000000000000000000000000000000000000000000010100101100
(1 row)

但是(编辑)您实际上是在问如何将仅整数numericbit 不是那么简单,请稍等。

您也不能对数字进行位移位,因此也无法轻松地将其位移位为64位块,进行转换和重新组装。

您将不得不使用除法和模数。

鉴于:

SELECT '1792913810350008736973055638379610855835'::numeric(40,0);

您可以以“ bigint”块的形式获取该块,将其乘以max-long(922337203685477575807)的位置值即可得出原始值。

例如,这将获得最低的64位:

SELECT ('1792913810350008736973055638379610855835'::numeric(40,0) / '9223372036854775807'::numeric(256,0)) % '9223372036854775807'::numeric(40,0);

这将获取给定值(最多256位)的所有块及其指数

WITH numval(v) AS (VALUES ('1792913810350008736973055638379610855835'::numeric(40,0)))
SELECT exponent, floor(v / ('9223372036854775807'::numeric(256,0) ^ exponent) % '9223372036854775807'::numeric(40,0)) from numval, generate_series(1,3) exponent;

您可以将其重组为原始值:

WITH
  numval(v) AS (
    VALUES ('1792913810350008736973055638379610855835'::numeric(40,0))
  ),
  chunks (exponent, chunk) AS (
     SELECT exponent, floor(v / ('9223372036854775807'::numeric(40,0) ^ exponent) % '9223372036854775807'::numeric(40,0))::bigint from numval, generate_series(1,3) exponent
  )
SELECT floor(sum(chunk::numeric(40,0) * ('9223372036854775807'::numeric(40,0) ^ exponent))) FROM chunks;

因此我们知道它已正确分解。

现在,我们正在处理一系列64位整数,我们可以将每个整数转换为位字段。 因为我们使用带符号整数,所以每个整数只有63个有效位,因此:

WITH
  numval(v) AS (
    VALUES ('1792913810350008736973055638379610855835'::numeric(40,0))
  ),
  chunks (exponent, chunk) AS (
     SELECT exponent, floor(v / ('9223372036854775807'::numeric(40,0) ^ exponent) % '9223372036854775807'::numeric(40,0))::bigint from numval, generate_series(1,3) exponent
  )
SELECT
  exponent,
  chunk::bit(63)
FROM chunks;

为我们提供了每个63位块的位值。 然后我们可以重新组装它们。 没有位域串联运算符,但是我们可以移动和bit_or ,然后将其包装到一个SQL函数中,从而产生怪异性:

CREATE OR REPLACE FUNCTION numericint40_to_bit189(numeric(40,0)) RETURNS bit(189)
LANGUAGE sql
AS
$$
    WITH
      chunks (exponent, chunk) AS (
         SELECT exponent, floor($1 / ('9223372036854775807'::numeric(40,0) ^ exponent) % '9223372036854775807'::numeric(40,0))::bigint 
         FROM generate_series(1,3) exponent
      )
    SELECT
      bit_or(chunk::bit(189) << (63*(exponent-1)))
    FROM chunks;
$$;

可以在这里看到使用:

regress=> SELECT numericint40_to_bit189('1792913810350008736973055638379610855835');
                                                                                    numericint40_to_bit189                                                                                     
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010101000100110101101010001110110110101001111100011100011110000010110
(1 row)

感谢您的初步回答克雷格·林格! 这是该功能的正确版本。 它最多支持300位,并且可以根据需要扩展。

CREATE OR REPLACE FUNCTION numeric_to_bit(NUMERIC)
  RETURNS BIT VARYING AS $$
DECLARE
  num ALIAS FOR $1;
  -- 1 + largest positive BIGINT --
  max_bigint NUMERIC := '9223372036854775808' :: NUMERIC(19, 0);
  result BIT VARYING;
BEGIN
  WITH
      chunks (exponent, chunk) AS (
        SELECT
          exponent,
          floor((num / (max_bigint ^ exponent) :: NUMERIC(300, 20)) % max_bigint) :: BIGINT
        FROM generate_series(0, 5) exponent
    )
  SELECT bit_or(chunk :: BIT(300) :: BIT VARYING << (63 * (exponent))) :: BIT VARYING
  FROM chunks INTO result;
  RETURN result;
END;
$$ LANGUAGE plpgsql;

使用python尝试一下:

sql = sorted(([hamming("HashString", %s,image.id) for image in Image.objects.all() if hamming("HashString",%s) < %s])

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM