2013-06-21 43 views
7

Tôi có một cơ sở dữ liệu tôi muốn chuyển đổi để sử dụng UUID làm khóa chính trong postgresql.Cách di chuyển cơ sở dữ liệu Rails phức tạp để sử dụng các khóa chính UUID Postgresql

Tôi có khoảng 30 bảng với các liên kết đa cấp sâu. Có cách nào 'dễ dàng' để chuyển đổi tất cả ID hiện tại thành UUID không?

Từ đây: https://coderwall.com/p/n_0awq, tôi có thể thấy rằng tôi có thể thay đổi bảng khi di chuyển. Tôi đã suy nghĩ một cái gì đó như thế này:

for client in Client.all 
    # Retrieve children 
    underwritings = client.underwritings 
    # Change primary key 
    execute 'ALTER TABLE clients ALTER COLUMN id TYPE uuid;' 
    execute 'ALTER TABLE clients ALTER COLUMN id SET DEFAULT uuid_generate_v1();' 
    # Get new id - is this already generated? 
    client_id = client.id 
    for underwriting in underwritings 
    locations = underwriting.locations 
    other_record = underwriting.other_records... 

    execute 'ALTER TABLE underwritings ALTER COLUMN id TYPE uuid;' 
    execute 'ALTER TABLE underwritings ALTER COLUMN id SET DEFAULT uuid_generate_v1();' 
    underwriting.client_id = client_id 
    underwriting.saved 
    underwriting_id = underwriting.id 

    for location in locations 
     buildings = location.buildings 
     execute 'ALTER TABLE locations ALTER COLUMN id TYPE uuid;' 
     execute 'ALTER TABLE locations ALTER COLUMN id SET DEFAULT uuid_generate_v1();' 
     location.undewriting_id = underwriting_id 
     location.save 
     location_id = location.id 

     for building in buildings 
     ... 
     end 
    end 
    for other_record in other_records 
     ... 
    end 
    ... 
    ... 
    end 
end 

Câu hỏi:

  • Sẽ làm việc này?
  • Có cách nào dễ dàng hơn để thực hiện việc này không?
  • Hồ sơ trẻ em có được truy xuất đúng cách miễn là chúng được truy lục trước khi khóa chính được thay đổi không?
  • Khóa chính mới có được tạo ngay khi bảng thay đổi được gọi không?

Cảm ơn rất nhiều sự giúp đỡ hoặc mẹo khi thực hiện việc này.

+0

Không, tôi chưa thử bản sao này và tất nhiên sẽ không chỉ thử nghiệm trên cơ sở dữ liệu sản xuất. Tôi đã tự hỏi nếu a) đây là cách để đi về điều này, và b) là có một cách dễ dàng hơn (một số phép thuật Rails tôi không biết về). Cảm ơn – riley

Trả lời

4

Tôi nghĩ việc cố gắng làm điều gì đó như thế này thông qua Rails sẽ làm phức tạp vấn đề. Tôi sẽ bỏ qua phía Rails của sự vật hoàn toàn và chỉ cần làm điều đó trong SQL.

Bước đầu tiên của bạn là lấy bản sao lưu hoàn chỉnh của cơ sở dữ liệu của bạn. Sau đó, khôi phục bản sao lưu đó vào cơ sở dữ liệu khác để:

  1. Đảm bảo rằng công việc sao lưu của bạn hoạt động.
  2. Cung cấp cho bạn một playpen thực tế, nơi bạn có thể phạm sai lầm mà không có hậu quả.

Trước tiên, bạn muốn xóa dữ liệu của mình bằng cách thêm khóa ngoài thực sự để khớp với tất cả các liên kết Rails của bạn. Có một cơ hội tốt mà một số FK của bạn sẽ thất bại, nếu họ làm bạn sẽ phải làm sạch tài liệu tham khảo bị hỏng của bạn.

Bây giờ bạn đã có dữ liệu sạch, rename all your tables để dành chỗ cho các phiên bản UUID mới. Đối với một bảng t, chúng tôi sẽ tham chiếu đến bảng được đổi tên thành t_tmp. Đối với mỗi t_tmp, tạo bảng khác để giữ ánh xạ từ cái cũ integerid s đến UUID mới id s, một cái gì đó như thế này:

create table t_id_map (
    old_id integer not null, 
    new_id uuid not null default uuid_generate_v1() 
) 

và sau đó cư nó:

insert into t_id_map (old_id) 
select id from t_tmp 

Và bạn' có thể sẽ muốn lập chỉ mục t_id_map.old_id khi bạn ở đây.

Điều này cung cấp cho chúng tôi các bảng cũ với các mã số integer và bảng tra cứu cho mỗi t_tmp ánh xạ bản đồ cũ id sang số mới.

Bây giờ, tạo các bảng mới với UUID thay thế tất cả các cột integerserial cũ giữ id s; Tôi cũng sẽ thêm các khóa ngoại thực sự vào thời điểm này; bạn nên hoang tưởng về dữ liệu của mình: mã bị hỏng là tạm thời, dữ liệu bị hỏng thường là vĩnh viễn.

Việc điền các bảng mới là khá dễ dàng tại thời điểm này: chỉ cần sử dụng insert into ... select ... from cấu trúc và THAM GIA các bảng t_id_map thích hợp để ánh xạ những cái mới id s. Khi dữ liệu đã được ánh xạ và sao chép, bạn sẽ muốn thực hiện kiểm tra sanity để đảm bảo mọi thứ vẫn hợp lý. Sau đó, bạn có thể thả các bảng t_tmpt_id_map của bạn và tiếp tục cuộc sống của bạn.

Thực hành quy trình đó trên bản sao cơ sở dữ liệu của bạn, viết kịch bản lên và bạn không sử dụng.

Bạn sẽ muốn tắt bất kỳ ứng dụng nào truy cập cơ sở dữ liệu của bạn trong khi bạn đang thực hiện công việc này.

1

Không muốn thêm khóa ngoại, và muốn sử dụng di chuyển đường ray. Dù sao, đây là những gì tôi đã làm nếu những người khác đang tìm cách để làm điều này (ví dụ cho 2 bảng, tôi đã làm 32 tổng số):

def change 
    execute 'CREATE EXTENSION "uuid-ossp";' 
    execute <<-SQL 
     ALTER TABLE buildings ADD COLUMN guid uuid DEFAULT uuid_generate_v1() NOT NULL; 
     ALTER TABLE buildings ALTER COLUMN guid SET DEFAULT uuid_generate_v1(); 
     ALTER TABLE buildings ADD COLUMN location_guid uuid; 

     ALTER TABLE clients ADD COLUMN guid uuid DEFAULT uuid_generate_v1() NOT NULL; 
     ALTER TABLE clients ALTER COLUMN guid SET DEFAULT uuid_generate_v1(); 
     ALTER TABLE clients ADD COLUMN agency_guid uuid; 
     ALTER TABLE clients ADD COLUMN account_executive_guid uuid; 
     ALTER TABLE clients ADD COLUMN account_representative_guid uuid; 
    SQL 

    for record in Building.all 
     location = record.location 
     record.location_guid = location.guid 

     record.save 
    end 

    for record in Client.all 
     agency = record.agency 
     record.agency_guid = agency.guid 

     account_executive = record.account_executive 
     record.account_executive_guid = account_executive.guid unless account_executive.blank? 

     account_representative = record.account_representative 
     record.account_representative_guid = account_representative.guid unless account_representative.blank? 

     record.save 
    end 

    execute <<-SQL 
     ALTER TABLE buildings DROP CONSTRAINT buildings_pkey; 
     ALTER TABLE buildings DROP COLUMN id; 
     ALTER TABLE buildings RENAME COLUMN guid TO id; 
     ALTER TABLE buildings ADD PRIMARY KEY (id); 
     ALTER TABLE buildings DROP COLUMN location_id; 
     ALTER TABLE buildings RENAME COLUMN location_guid TO location_id; 

     ALTER TABLE clients DROP CONSTRAINT clients_pkey; 
     ALTER TABLE clients DROP COLUMN id; 
     ALTER TABLE clients RENAME COLUMN guid TO id; 
     ALTER TABLE clients ADD PRIMARY KEY (id); 
     ALTER TABLE clients DROP COLUMN agency_id; 
     ALTER TABLE clients RENAME COLUMN agency_guid TO agency_id; 
     ALTER TABLE clients DROP COLUMN account_executive_id; 
     ALTER TABLE clients RENAME COLUMN account_executive_guid TO account_executive_id; 
     ALTER TABLE clients DROP COLUMN account_representative_id; 
     ALTER TABLE clients RENAME COLUMN account_representative_guid TO account_representative_id; 
    SQL 
    end 
9

Tôi thấy chúng khá tẻ nhạt. Có thể sử dụng các truy vấn trực tiếp tới PostgreSQL để chuyển đổi bảng với dữ liệu hiện có.

Đối với khóa chính:

ALTER TABLE students 
     ALTER COLUMN id DROP DEFAULT, 
     ALTER COLUMN id SET DATA TYPE UUID USING (uuid(lpad(replace(text(id),'-',''), 32, '0'))), 
     ALTER COLUMN id SET DEFAULT uuid_generate_v4() 

Đối với tài liệu tham khảo khác:

ALTER TABLE students 
     ALTER COLUMN city_id SET DATA TYPE UUID USING (uuid(lpad(replace(text(city_id),'-',''), 32, '0'))) 

Các miếng đệm bên trái phía trên giá trị số nguyên với số không và chuyển đến một UUID. Cách tiếp cận này không yêu cầu ánh xạ id và nếu id cũ cần thiết có thể được lấy ra.

Vì không có sao chép dữ liệu, cách tiếp cận này hoạt động khá nhanh.

Để xử lý các trường hợp phức tạp và phức tạp hơn này của các hiệp hội đa hình, vui lòng sử dụng https://github.com/kreatio-sw/webdack-uuid_migration. Đá quý này bổ sung thêm những người trợ giúp cho ActiveRecord :: Migration để giảm bớt sự di trú này.

+0

Cảm ơn - điều này giúp đơn giản hóa việc di chuyển sang uuid từ số nguyên – house9

+0

Điều này không cập nhật quan hệ? – riley

+0

@riley phụ thuộc vào phiên bản di chuyển đường ray ban đầu được sử dụng. Nếu có các quan hệ Foreign Key được định nghĩa (một số phiên bản Rails làm), nó sẽ cố gắng cập nhật các cột liên quan. Nếu không, bạn sẽ phải tự cập nhật quan hệ (có phương pháp trợ giúp để thực hiện việc này). –