2010-10-26 41 views
14

Tôi muốn một cách khá hiệu quả để ngưng tụ toàn bộ bảng thành giá trị băm.Làm cách nào tôi có thể nhận được băm của toàn bộ bảng trong postgresql?

Tôi có một số công cụ tạo toàn bộ bảng dữ liệu, sau đó có thể được sử dụng để tạo thêm các bảng, v.v. Tôi đang cố gắng để thực hiện một hệ thống xây dựng đơn giản để phối hợp chạy xây dựng và tránh lặp lại công việc. Tôi muốn có thể ghi lại các băm của các bảng đầu vào để sau này tôi có thể kiểm tra xem chúng có thay đổi hay không. Việc xây dựng một bảng chỉ mất vài phút hoặc vài giờ, do đó, chi tiêu vài giây xây dựng băm là chấp nhận được. Một hack Tôi đã sử dụng là để chỉ đường ống đầu ra của pg_dump để md5sum, nhưng điều đó đòi hỏi phải chuyển toàn bộ bãi chứa trên mạng để băm nó trên hộp địa phương. Quay lại đầu trang |||| Lý tưởng nhất là tôi muốn sản xuất băm trên máy chủ cơ sở dữ liệu.

Finding the hash value of a row in postgresql cung cấp cho tôi cách tính giá trị băm cho một hàng tại một thời điểm, sau đó có thể được kết hợp bằng cách nào đó.

Mọi mẹo sẽ được đánh giá cao.

Chỉnh sửa để đăng những gì tôi đã kết thúc với: Câu trả lời của tinychen không làm việc trực tiếp cho tôi, bởi vì tôi không thể sử dụng 'plpgsql' rõ ràng. Khi tôi thực hiện hàm trong SQL thay vào đó, nó hoạt động, nhưng lại không hiệu quả đối với các bảng lớn. Vì vậy, thay vì ghép tất cả các băm hàng và sau đó băm, tôi chuyển sang sử dụng một "băm cán", trong đó băm trước được nối với biểu diễn văn bản của một hàng và sau đó được băm để tạo ra băm tiếp theo. Điều này tốt hơn nhiều; dường như chạy md5 trên chuỗi ngắn hàng triệu lần thêm là tốt hơn so với nối chuỗi ngắn hàng triệu lần.

create function zz_concat(text, text) returns text as 
    'select md5($1 || $2);' language 'sql'; 

create aggregate zz_hashagg(text) (
    sfunc = zz_concat, 
    stype = text, 
    initcond = ''); 
+0

Tôi không biết cách nào để làm điều này. Bản năng đầu tiên của tôi là đăng nhập tạo bảng và so sánh dấu thời gian. – mikerobi

+1

Tôi cho rằng bạn không thể chạy lệnh pg_dump trên máy chủ, phải không? –

+0

@Joey: +1. rất thực dụng, có lẽ là nhanh nhất. Biến nó thành câu trả lời. – Thilo

Trả lời

7

chỉ cần làm như thế này để tạo hàm tổng hợp bảng băm.

create function pg_concat(text, text) returns text as ' 
begin 
    if $1 isnull then 
     return $2; 
    else 
     return $1 || $2; 
    end if; 
end;' language 'plpgsql'; 

create function pg_concat_fin(text) returns text as ' 
begin 
    return $1; 
end;' language 'plpgsql'; 

create aggregate pg_concat (
    basetype = text, 
    sfunc = pg_concat, 
    stype = text, 
    finalfunc = pg_concat_fin); 

thì bạn có thể sử dụng hàm pg_concat để tính giá trị băm của bảng.

select md5(pg_concat(md5(CAST((f.*)AS text)))) from f order by id 
1

Đối với thuật toán, bạn có thể XOR tất cả các băm MD5 riêng biệt hoặc ghép chúng và băm nối.

Nếu bạn muốn làm điều này hoàn toàn phía máy chủ, bạn có thể phải create your own aggregation function, mà sau đó bạn có thể gọi.

select my_table_hash(md5(CAST((f.*)AS text)) from f order by id 

Là bước trung gian, thay vì sao chép toàn bộ bảng cho khách hàng, bạn chỉ có thể chọn kết quả MD5 cho tất cả các hàng và chạy chúng thông qua md5sum.

Dù bằng cách nào bạn cần thiết lập thứ tự sắp xếp cố định, nếu không, bạn có thể kết thúc bằng các kiểm tra khác nhau ngay cả đối với cùng một dữ liệu.

+0

" bạn cần thiết lập thứ tự sắp xếp cố định ". Đó là nếu bạn muốn phục hồi băm. Đối với XOR, điều này là không cần thiết. Làm cho tôi nghĩ rằng XOR có thể không phải là một ý tưởng tốt. – Thilo

+1

Bạn nói đúng; XOR tổng hợp các băm có nghĩa là nếu bạn có hai hàng giống hệt nhau, và cả hai đều thay đổi theo cùng một cách, giá trị băm cuối cùng sẽ giống như giá trị ban đầu. Các hàng giống hệt nhau có lẽ không nên ở đó, nhưng tôi đặt cược có những đặc tính khác của XOR làm tăng khả năng xảy ra va chạm. – Ben

+0

Cảm ơn con trỏ; Tôi sẽ xem xét việc này. Thật không may tôi sử dụng rất nhiều DB khác nhau (và những cái mới được tạo ra mọi lúc), vì vậy tôi sẽ phải viết kịch bản việc tạo ra hàm tổng hợp như là một phần của hệ thống xây dựng. Tôi sẽ trở lại và chấp nhận câu trả lời này nếu tôi không nhận được bất cứ điều gì khác. – Ben

16

Tôi biết đây là câu hỏi cũ, tuy nhiên đây là giải pháp của tôi:

SELECT   
    md5(CAST((array_agg(f.* order by id))AS text)) /* id is a primary key of table (to avoid random sorting) */ 
FROM 
    foo f; 
1

Tôi đã có một yêu cầu tương tự, để sử dụng khi thử nghiệm một giải pháp chuyên dụng bảng nhân rộng.

@ Giải pháp MD5 lăn của Ben (mà ông đã thêm vào câu hỏi) có vẻ khá hiệu quả, nhưng có một vài bẫy bẫy tôi.

Đầu tiên (được đề cập trong một số câu trả lời khác) là bạn cần đảm bảo rằng tổng hợp được thực hiện theo thứ tự đã biết trên bảng bạn đang kiểm tra. Cú pháp cho điều đó là ví dụ.

select zz_hashagg(CAST((example.*)AS text) order by id) from example; 

Lưu ý rằng order by nằm trong tổng hợp.

Cách thứ hai là sử dụng CAST((example.*)AS text sẽ không cung cấp kết quả giống hệt nhau cho hai bảng có cùng nội dung cột trừ khi các cột được tạo theo cùng thứ tự. Trong trường hợp của tôi mà không được đảm bảo, vì vậy để có được một sự so sánh đúng tôi phải liệt kê các cột riêng biệt, ví dụ:

select zz_hashagg(CAST((example.id, example.a, example.c)AS text) order by id) from example; 

Để hoàn chỉnh (trong trường hợp một chỉnh sửa tiếp theo nên loại bỏ nó) đây là định nghĩa của zz_hashagg từ @ câu hỏi của Ben:

create function zz_concat(text, text) returns text as 
    'select md5($1 || $2);' language 'sql'; 

create aggregate zz_hashagg(text) (
    sfunc = zz_concat, 
    stype = text, 
    initcond = ''); 
3
SELECT md5(array_agg(md5((t.*)::varchar))::varchar) 
    FROM (
     SELECT * 
      FROM my_table 
     ORDER BY 1 
     ) AS t 
0

lớn câu trả lời.

Trong trường hợp bất kỳ người nào yêu cầu không sử dụng chức năng tổng hợp nhưng vẫn duy trì hỗ trợ cho các bảng có kích thước GiB, bạn có thể sử dụng điều này có số hiệu suất là ít.

CREATE OR REPLACE FUNCTION table_md5(
    table_name CHARACTER VARYING 
    , VARIADIC order_key_columns CHARACTER VARYING []) 
RETURNS CHARACTER VARYING AS $$ 
DECLARE 
    order_key_columns_list CHARACTER VARYING; 
    query CHARACTER VARYING; 
    first BOOLEAN; 
    i SMALLINT; 
    working_cursor REFCURSOR; 
    working_row_md5 CHARACTER VARYING; 
    partial_md5_so_far CHARACTER VARYING; 
BEGIN 
    order_key_columns_list := ''; 

    first := TRUE; 
    FOR i IN 1..array_length(order_key_columns, 1) LOOP 
    IF first THEN 
     first := FALSE; 
    ELSE 
     order_key_columns_list := order_key_columns_list || ', '; 
    END IF; 
    order_key_columns_list := order_key_columns_list || order_key_columns[i]; 
    END LOOP; 

    query := (
    'SELECT ' || 
     'md5(CAST(t.* AS TEXT)) ' || 
    'FROM (' || 
     'SELECT * FROM ' || table_name || ' ' || 
     'ORDER BY ' || order_key_columns_list || 
    ') t'); 

    OPEN working_cursor FOR EXECUTE (query); 
    -- RAISE NOTICE 'opened cursor for query: ''%''', query; 

    first := TRUE; 
    LOOP 
    FETCH working_cursor INTO working_row_md5; 
    EXIT WHEN NOT FOUND; 
    IF first THEN 
     SELECT working_row_md5 INTO partial_md5_so_far; 
    ELSE 
     SELECT md5(working_row_md5 || partial_md5_so_far) 
     INTO partial_md5_so_far; 
    END IF; 
    -- RAISE NOTICE 'partial md5 so far: %', partial_md5_so_far; 
    END LOOP; 

    -- RAISE NOTICE 'final md5: %', partial_md5_so_far; 
    RETURN partial_md5_so_far :: CHARACTER VARYING; 
END; 
$$ LANGUAGE plpgsql; 

Được sử dụng như:

SELECT table_md5(
    'table_name', 'sorting_col_0', 'sorting_col_1', ..., 'sorting_col_n' 
); 
Các vấn đề liên quan