2017-09-21 19 views
6

Tôi có một cơ sở dữ liệu trong đó một trong các bảng lưu trữ một blob (bytea) của tất cả các loại dữ liệu chung được thu thập từ một hệ thống khác. Trường bytea có thể có bất kỳ thứ gì trong đó. Để biết cách diễn giải dữ liệu, bảng cũng có một trường định dạng. Tôi đã viết một ứng dụng Java để đọc trường bytea từ cơ sở dữ liệu dưới dạng byte[] và sau đó tôi có thể dễ dàng chuyển đổi nó thành double[] hoặc int[] hoặc bất kỳ trường định dạng nào bằng cách sử dụng ByteBuffer và các chế độ xem khác nhau (, v.v.).Chuyển đổi bytea thành độ chính xác gấp đôi trong PostgreSQL

Bây giờ tôi có tình huống mà tôi cần thực hiện một số thao tác của dữ liệu trên cơ sở dữ liệu trong một hàm kích hoạt để duy trì tính toàn vẹn với một bảng khác. Tôi có thể tìm thấy chuyển đổi cho bất kỳ loại dữ liệu nào có thể tưởng tượng được nhưng tôi không thể tìm thấy bất kỳ thứ gì để chuyển từ bytea (hoặc thậm chí bit) thành double precision và ngược lại. Một bytea có thể được chia nhỏ, chuyển đổi thành bit, và sau đó chuyển đổi thành một số int hoặc bigint, nhưng không phải là double precision. Ví dụ: x'deadbeefdeadbeef'::bit(64)::bigint sẽ chuyển đổi thành -2401053088876216593 mà không có sự cố, nhưng x'deadbeefdeadbeef'::bit(64)::double precision không thành công với "L ERI: không thể truyền bit loại tới độ chính xác gấp đôi" thay vì đưa ra câu trả lời IEEE 754 của -1.1885959257070704E148.

Tôi tìm thấy câu trả lời này https://stackoverflow.com/a/11661849/5274457, về cơ bản thực hiện tiêu chuẩn IEEE để chuyển đổi các bit thành gấp đôi, nhưng thực sự không có chức năng chuyển đổi cơ bản trong PostgreSQL để làm điều này? Ngoài ra, tôi cũng cần quay ngược lại từ double precision đến bytea khi tôi hoàn tất thao tác dữ liệu và cần cập nhật các bảng mà câu trả lời này không cung cấp.

Bất kỳ ý tưởng nào?

+0

Có thể bạn sẽ cần một tiện ích C đơn giản để thêm dàn diễn viên được yêu cầu. Pg thực sự có thể sử dụng một số chức năng chuyển đổi khác đến/từ các dạng nhị phân thô. –

+0

Tôi sẽ xem xét ý tưởng tiện ích. Từ những gì tôi có thể nói, đây không chỉ là một vấn đề PostgreSQL. HSQL và SQLServer, từ những gì tôi có thể nói, có cùng một vấn đề. Mỗi ngôn ngữ lập trình tôi đã sử dụng đều có các phương thức để chuyển đổi các byte thô thành gấp đôi, nhưng cơ sở dữ liệu SQL dường như không có. – Keith

+0

Chúng thường hoạt động ở mức trừu tượng cao hơn. Với PostgreSQL, một tùy chọn thực tế có lẽ là sử dụng một thủ tục plperlu hoặc plpythonu đơn giản để làm điều đó, theo cách đó bạn không phải viết một phần mở rộng C. –

Trả lời

1

Ok, tôi đã tìm thấy câu trả lời. Trong PostgreSQL, bạn có thể viết các hàm bằng Python. Để cho phép sử dụng Python, bạn phải cài đặt phiên bản Python cụ thể cần thiết bằng cách cài đặt PostgreSQL và có sẵn trong biến môi trường PATH. Bạn có thể tìm phiên bản Python nào mà PostgreSQL của bạn cần cài đặt bằng cách xem các ghi chú cài đặt. Tôi hiện đang sử dụng PostgreSQL 9.6.5 trên Windows và nó gọi cho Python 3.3. Ban đầu tôi đã thử Python 3.6 mới nhất, nhưng nó sẽ không hoạt động. Tôi đã giải quyết với Python 3.3 mới nhất cho Windows, là 3.3.5.

Sau khi cài đặt Python, bạn bật nó trong PostgreSQL bằng cách thực hiện CREATE EXTENSION plpython3u; trên cơ sở dữ liệu của bạn như được ghi ở đây https://www.postgresql.org/docs/current/static/plpython.html. Từ đó, bạn có thể viết bất kỳ hàm nào với các phần tử Python.

Đối với trường hợp cụ thể của tôi để chuyển đổi bytea-double precision[] và trở lại, tôi đã viết các chức năng sau:

CREATE FUNCTION bytea_to_double_array(b bytea) 
    RETURNS double precision[] 
    LANGUAGE 'plpython3u' 
AS $BODY$ 
    if 'struct' in GD: 
    struct = GD['struct'] 
    else: 
    import struct 
    GD['struct'] = struct 

    return struct.unpack('<' + str(int(len(b)/8)) + 'd', b) 
$BODY$; 

CREATE FUNCTION double_array_to_bytea(dblarray double precision[]) 
    RETURNS bytea 
    LANGUAGE 'plpython3u' 
AS $BODY$ 
    if 'struct' in GD: 
    struct = GD['struct'] 
    else: 
    import struct 
    GD['struct'] = struct 

    # dblarray here is really a list. 
    # PostgreSQL passes SQL arrays as Python lists 
    return struct.pack('<' + str(int(len(dblarray))) + 'd', *dblarray) 
$BODY$; 

Trong trường hợp của tôi, tất cả các nội dung đôi được lưu trữ trong little endian, vì vậy tôi sử dụng <. Tôi cũng lưu trữ bộ nhớ cache của mô-đun struct trong từ điển chung như được mô tả trong https://stackoverflow.com/a/15025425/5274457. Tôi đã sử dụng GD thay vì SD vì tôi muốn nhập có sẵn trong các chức năng khác mà tôi có thể viết. Để biết thông tin về GD và SD, hãy xem https://www.postgresql.org/docs/current/static/plpython-sharing.html.

Để nhìn thấy nó trong hành động biết các đốm màu trong cơ sở dữ liệu của tôi được lưu trữ như little endian,

SELECT bytea_to_double_array(decode('efbeaddeefbeadde', 'hex')), encode(double_array_to_bytea(array[-1.1885959257070704E148]), 'hex'); 

Và câu trả lời tôi nhận được là

bytea_to_double_array | encode 
double precision[]  | text 
-------------------------+------------------ 
{-1.18859592570707e+148} | efbeaddeefbeadde 

nơi 'efbeaddeefbeadde''deadbeefdeadbeef' trong little endian.

+0

Làm tốt lắm, cảm ơn :) –

Các vấn đề liên quan