2012-03-19 23 views
12

Tôi chỉ mới bắt đầu sử dụng phần tiếp theo trong một ứng dụng Sinatra thực sự nhỏ. Vì tôi chỉ có một bảng DB, tôi không cần sử dụng các mô hình.Làm thế nào để cập nhật hoặc chèn vào bộ dữ liệu Sequel?

Tôi muốn cập nhật một bản ghi nếu nó tồn tại hoặc chèn một bản ghi mới nếu nó không. Tôi đã đưa ra giải pháp sau:

rec = $nums.where(:number => n, :type => t) 
    if $nums.select(1).where(rec.exists) 
    rec.update(:counter => :counter + 1) 
    else 
    $nums.insert(:number => n, :counter => 1, :type => t) 
    end 

Nơi dữ liệu là DB[:numbers].

Tôi tin rằng cách này không phải là cách triển khai "cập nhật hoặc chèn" thanh lịch nhất.

Làm cách nào để thực hiện?

+0

http://stackoverflow.com/questions/3647454/increment-counter-or-insert-row-in-one-statement-in-sqlite – Reactormonk

Trả lời

17

Có thể bạn không nên kiểm tra trước khi cập nhật/chèn; bởi vì:

  1. Đây là cuộc gọi db phụ.
  2. Điều này có thể giới thiệu một điều kiện chủng tộc.

Bạn nên làm gì thay vì là để kiểm tra giá trị trả về của update:

rec = $nums.where(:number => n, :type => t) 
if 1 != rec.update(:counter => :counter + 1) 
    $nums.insert(:number => n, :counter => 1, :type => t) 
end 
+0

Đây là một giải pháp tốt đẹp. Cảm ơn – Akarsh

+1

Giải pháp này vẫn giới thiệu khả năng điều kiện chủng tộc. Nếu hai tiến trình/luồng song song thực hiện cập nhật (dòng 2) trước khi một trong số chúng đạt đến chèn (dòng 3), hai bản ghi sẽ được chèn vào. Xem xét sử dụng một cái gì đó như mutex, db khóa hoặc chiến lược giao dịch thích hợp. – Flexoid

+0

Flexoid: bạn đúng, và giải pháp dưới đây - về cơ bản "đưa mọi thứ vào một giao dịch" là chính xác. Tuy nhiên, không có điểm trong lệnh "SELECT, UPDATE, INSERT" khi "UPDATE + INSERT" là đủ. (+ các giao dịch, tất nhiên.) Điều thú vị w/giao dịch: nếu hai giao dịch chạy song song tăng cùng một truy cập bạn * vẫn * chạy vào vấn đề. – radiospiel

2

Tôi tin rằng bạn không thể làm cho nó sạch hơn nhiều (mặc dù một số cơ sở dữ liệu có cú pháp nâng cao cụ thể, có thể là supported by Sequel). Bạn chỉ có thể bọc những gì bạn có trong một phương pháp riêng biệt và giả vờ rằng nó không tồn tại. :)

Chỉ cần vài gợi ý:

  • Kèm theo tất cả mọi thứ trong một giao dịch.
  • Tạo chỉ mục duy nhất trên các trường (number, type).
  • Không sử dụng các biến toàn cục.
1

Bạn có thể sử dụng upsert, ngoại trừ nó không hiện đang làm việc để cập nhật quầy. Hy vọng rằng một phiên bản tương lai sẽ - ý tưởng chào đón!

8

Sequel 4.25.0 (phát hành ngày 31 tháng 7 năm 2015) thêm insert_conflict for Postgres v9.5+
Sequel 4.30.0 (phát hành 04 tháng 1 năm 2016) thêm insert_conflict for SQLite

này có thể được sử dụng để hoặc chèn hoặc cập nhật liên tục, như vậy :

DB[:table_name].insert_conflict(:update).insert(number:n, type:t, counter:c) 
Các vấn đề liên quan