2010-01-19 37 views
36

Tôi đã tìm thấy câu trả lời "SQL thuần túy" cho câu hỏi này. Có cách nào không, trong Rails, để đặt lại trường id cho một bảng cụ thể?
Tại sao tôi muốn làm điều này? Bởi vì tôi có các bảng với dữ liệu liên tục di chuyển - hiếm khi có hơn 100 hàng, nhưng luôn khác nhau. Bây giờ nó lên đến 25k, và không có điểm nào trong đó. Tôi có ý định sử dụng một trình lên lịch nội bộ cho ứng dụng Rails (rufus-scheduler) để chạy trường id reset hàng tháng hoặc lâu hơn.Đường ray để đặt lại hạt giống trên trường id

+2

Tôi ghét nó khi người ta nói điều này, nhưng các ý kiến ​​là đúng ... có thực sự không có cách nào để làm điều này trong đường ray, và, to và lớn, nó không cần thiết phải làm gì cả. Nếu bạn chỉ tránh nhận được số lượng lớn bởi vì bạn muốn hiển thị nó cho người dùng, bạn chỉ nên có một cột PID, và sử dụng nó cho màn hình của bạn. – aronchick

+0

Và làm thế nào tôi sẽ đi về việc tạo ra một cột PID? Đó có phải là điều tương tự như Jonas đã gợi ý bên dưới trong câu trả lời của anh ta không? – Trevoke

Trả lời

51

Tôi đã đưa ra giải pháp dựa trên câu trả lời của hgimenez và this other one.

Vì tôi thường làm việc với Sqlite hoặc PostgreSQL, tôi chỉ phát triển cho những người đó; nhưng mở rộng nó ra, nói MySQL, không nên quá phiền hà.

Đặt này bên trong lib/và yêu cầu nó trên một initializer:

# lib/active_record/add_reset_pk_sequence_to_base.rb 
module ActiveRecord 
    class Base 
    def self.reset_pk_sequence 
     case ActiveRecord::Base.connection.adapter_name 
     when 'SQLite' 
     new_max = maximum(primary_key) || 0 
     update_seq_sql = "update sqlite_sequence set seq = #{new_max} where name = '#{table_name}';" 
     ActiveRecord::Base.connection.execute(update_seq_sql) 
     when 'PostgreSQL' 
     ActiveRecord::Base.connection.reset_pk_sequence!(table_name) 
     else 
     raise "Task not implemented for this DB adapter" 
     end 
    end  
    end 
end 

Cách sử dụng:

Client.count # 10 
Client.destroy_all 
Client.reset_pk_sequence 
Client.create(:name => 'Peter') # this client will have id=1 

EDIT: Kể từ khi trường hợp thông thường nhất mà bạn sẽ muốn làm điều này là sau khi thanh toán bù trừ một bảng cơ sở dữ liệu, tôi khuyên bạn nên xem xét database_cleaner. Nó xử lý ID đặt lại tự động. Bạn có thể nói với nó để xóa bảng vừa chọn như thế này:

DatabaseCleaner.clean_with(:truncation, :only => %w[clients employees]) 
+2

Nếu bạn mới sử dụng Rails như tôi, [bài đăng trên blog này] (http://blog.chrisblunt.com/rails-3-how-to-autoload-and-autorequire-your-custom -library-code /) đã giúp tôi thiết lập mã này trong trình khởi tạo. –

1

Một vấn đề là các loại lĩnh vực được thực hiện khác nhau cho chuỗi databases- khác nhau, tính năng tự động gia tăng, vv

Bạn luôn có thể thả và tái thêm bàn.

1

Không có điều gì trong Rails. Nếu bạn cần một id tốt đẹp để hiển thị người dùng sau đó lưu trữ chúng trong một bảng riêng biệt và tái sử dụng chúng.

+3

Đó là thông minh! Tôi không hiển thị nó cho người dùng mặc dù. Tôi chỉ muốn tránh tràn ngập trong 10.000 năm. :-) – Trevoke

1

Bạn chỉ có thể thực hiện điều này trong đường ray nếu _ids đang được đặt bằng đường ray. Miễn là các _ids đang được thiết lập bởi cơ sở dữ liệu của bạn, bạn sẽ không thể kiểm soát chúng mà không cần sử dụng SQL.

Lưu ý phụ: Tôi đoán sử dụng đường ray để thường xuyên gọi thủ tục SQL để đặt lại hoặc giảm và tạo lại chuỗi sẽ không phải là giải pháp hoàn toàn SQL, nhưng tôi không nghĩ đó là những gì bạn đang yêu cầu ...

EDIT:

Tuyên bố từ chối trách nhiệm: Tôi không biết nhiều về đường ray.

Từ góc nhìn SQL, nếu bạn có một bảng với các cột id first_name last_name và bạn thường insert into table (first_name, last_name) values ('bob', 'smith') bạn chỉ có thể thay đổi các truy vấn của bạn để insert into table (id, first_name, last_name) values ([variable set by rails], 'bob', 'smith') Bằng cách này, _id được thiết lập bởi một biến, thay vì được tự động thiết lập bởi SQL. Tại thời điểm đó, đường ray có toàn quyền kiểm soát những gì các _ids (mặc dù nếu nó là một PK bạn cần phải chắc chắn rằng bạn không sử dụng cùng một giá trị trong khi nó vẫn còn ở đó).

Nếu bạn đang đi để lại sự phân công lên đến cơ sở dữ liệu, bạn phải có đường ray chạy (trên bất cứ thời gian lịch trình) một cái gì đó như:

DROP SEQUENCE MY_SEQ; 
CREATE SEQUENCE MY_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 1; 

để bất cứ điều gì tự kiểm soát ids cho bảng của bạn. Điều này sẽ loại bỏ chuỗi hiện tại và tạo chuỗi mới. Đây là cách đơn giản nhất mà tôi biết về việc bạn 'đặt lại' một chuỗi.

+0

Vâng, tôi sẽ phải sử dụng Rails để thường xuyên gọi một câu lệnh SQL để tái hạt giống, tôi nghĩ :) Ngoài sự tò mò, làm cách nào để Rails đặt _id? – Trevoke

114

Bạn chưa bao giờ đề cập đến DBMS bạn đang sử dụng. Nếu đây là PostgreSQL, các ActiveRecord postgres bộ chuyển đổi có một phương pháp reset_pk_sequences! mà bạn có thể sử dụng:

ActiveRecord::Base.connection.reset_pk_sequence!('table_name') 
+0

Cảm ơn, Harold. Điều này là tiết kiệm ass của tôi ngay bây giờ. –

+0

Thứ tốt Harold! – RubyFanatic

+11

Tên bảng có thể được tăng bởi Rails với 'Class.table_name', có thể làm cho nó trở nên đẹp hơn. Cảm ơn! –

5

Tôi giả sử bạn không quan tâm đến dữ liệu:

def self.truncate! 
    connection.execute("truncate table #{quoted_table_name}") 
end 

Hoặc nếu bạn làm thế, nhưng không phải quá nhiều (có một lát thời gian mà dữ liệu chỉ tồn tại trong bộ nhớ):

def self.truncate_preserving_data! 
    data = all.map(&:clone).each{|r| raise "Record would not be able to be saved" unless r.valid? } 
    connection.execute("truncate table #{quoted_table_name}") 
    data.each(&:save) 
end 

này sẽ cung cấp hồ sơ mới, với các thuộc tính như nhau, nhưng id bắt đầu từ 1.

Bất cứ điều gì belongs_to trong bảng này có thể trở nên khó khăn.

+0

Đó là một câu trả lời gọn gàng, nhưng cắt ngắn không phải là một lựa chọn cho tôi. Tôi đã học được điều gì đó mặc dù :) – Trevoke

1

Dựa trên @hgmnz 's câu trả lời, tôi đã thực hiện phương pháp này sẽ thiết lập các chuỗi với bất kỳ giá trị mà bạn thích ... (Chỉ thử nghiệm với Postgres bộ chuyển đổi.)

# change the database sequence to force the next record to have a given id 
def set_next_id table_name, next_id 
    connection = ActiveRecord::Base.connection 
    def connection.set_next_id table, next_id 
    pk, sequence = pk_and_sequence_for(table) 
    quoted_sequence = quote_table_name(sequence) 
    select_value <<-end_sql, 'SCHEMA' 
     SELECT setval('#{quoted_sequence}', #{next_id}, false) 
    end_sql 
    end 
    connection.set_next_id(table_name, next_id) 
end 
+0

Điều này hoạt động ra khỏi hộp ... cảm ơn bạn! – ChaosFreak

0

Đường ray cho ví dụ MySQL, nhưng với mất tất cả dữ liệu trong bảng users:

ActiveRecord::Base.connection.execute('TRUNCATE TABLE users;') 

Có lẽ sẽ giúp người;)

+0

Đây là câu trả lời giống như https://stackoverflow.com/a/2098639/234025. – Trevoke

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