truy vấn liên quan đến việc kết hợp một bản cập nhật và một truy vấn chèn vào một truy vấn duy nhất trong mysql


9

Tôi muốn theo dõi lịch sử thay đổi của người dùng, vì vậy, bất cứ khi nào anh ta thay đổi hồ sơ của mình, tôi cần lấy dữ liệu cũ và lưu trữ trong lịch sử và cập nhật dữ liệu mới.

Tôi có thể sử dụng a selectđể lấy dữ liệu cũ, insertlịch sử và cuối cùng là updatethay đổi dữ liệu.

Tôi có thể có tất cả những điều này trong một truy vấn duy nhất trong mysql mà không cần sử dụng các thủ tục được lưu trữ, kích hoạt, v.v. như sử dụng khóa vv .. nếu vậy hãy cho tôi một mẫu nhỏ.


1
@savaranan: Câu hỏi này xứng đáng là +1 vì nó đưa ra một lời nhắc nhở mạnh mẽ cho các DBA và Nhà phát triển để sử dụng các giao dịch và tận dụng tối đa các thuộc tính ACID của cơ sở dữ liệu.
RolandoMySQLDBA

2
@savaranan: Đối với tất cả ý định và mục đích, Jack đã cung cấp câu trả lời hợp lý DUY NHẤT. Trên thực tế, Jack Douglas đã thực hiện một bước bổ sung và buộc khóa không liên tục trên mỗi hàng với id = 10 để thêm bảo vệ MVCC bằng cách thực hiện CHỌN ... ĐỂ CẬP NHẬT. Câu trả lời của anh ấy nhấn mạnh thêm điểm mà Jack và tôi đã nói cùng nhau: một CẬP NHẬT và CHỨNG MINH không thể là một truy vấn duy nhất, chúng chỉ có thể là một giao dịch duy nhất cho hành vi SQL mà câu hỏi của bạn đề xuất.
RolandoMySQLDBA

Câu trả lời:


13

Để thực hiện điều này mà không có nguy cơ bị chặn người dùng khác cố gắng cập nhật cùng một cấu hình cùng một lúc, bạn cần phải khóa hàng trong t1lần đầu tiên, sau đó sử dụng một giao dịch (như Rolando chỉ ra trong các ý kiến cho câu hỏi của bạn):

start transaction;
select id from t1 where id=10 for update;
insert into t2 select * from t1 where id=10;
update t1 set id = 11 where id=10;
commit;

Điều này chỉ đơn giản là tuyệt vời khi tiếp tục khóa mọi hàng với id = 10. Đó phải là +2. Tất cả những gì tôi có thể cho là +1 !!!
RolandoMySQLDBA

1

Tôi không tin có một cách để kết hợp cả ba tuyên bố. Điều gần nhất không thực sự giúp ích cho bạn, và đó là TÙY CHỌN. Đặt cược tốt nhất của bạn là một kích hoạt. Dưới đây là một mẫu trình kích hoạt mà tôi thường sử dụng để duy trì một bản kiểm toán như vậy (được xây dựng bằng PHP):

$trigger = "-- audit trigger --\nDELIMITER $ \n".
    "DROP TRIGGER IF EXISTS `{$prefix}_Audit_Trigger`$\n".
    "CREATE TRIGGER `{$prefix}_Audit_Trigger` AFTER UPDATE ON `$this->_table_name` FOR EACH ROW BEGIN\n";

foreach ($field_defs as $field_name => $field) {
    if ($field_name != $id_name) {
       $trigger .= "IF (NOT OLD.$field_name <=> NEW.$field_name) THEN \n".'INSERT INTO AUDIT_LOG ('.
                    'Table_Name, Row_ID, Field_Name, Old_Value, New_Value, modified_by, DB_User) VALUES'.
                    "\n ('$this->_table_name',OLD.$this->_id_name,'$field_name',OLD.$field_name,NEW.$field_name,".
                    "NEW.modified_by, USER()); END IF;\n";
    }
}
$trigger .= 'END$'."\n".'DELIMITER ;';

-3

Tôi đã thấy rằng truy vấn này hoạt động trên các Máy chủ SQL và MySQL INSERT INTO t2 SELECT * FROM t1 WHERE id=10; UPDATE t1 SET id=11 WHERE id=10;

Hy vọng điều này hữu ích cho một số người khác trong tương lai.


4
Đây không thực sự là một truy vấn. Đây thực sự là hai truy vấn nên được coi là một giao dịch.
RolandoMySQLDBA

@rolandomysqldba: điều này hoạt động tốt như một truy vấn duy nhất khi tôi gửi đến máy chủ db từ mã ứng dụng, trong đó tôi coi bộ này là một truy vấn duy nhất. tại sao bạn nói vậy?. bạn có thể từ chối điều này với những lý do mạnh mẽ ..
Saravanan

2
@saravanan: Trong mắt của InnoDB hoặc bất kỳ RDBMS tuân thủ ACID nào (Oracle, SQLServer, PostreQuery, Sybase, v.v.), không thể gọi hai câu lệnh SQL đó là một truy vấn. Là một cơ sở dữ liệu tuân thủ ACID sẽ coi chúng là hai câu lệnh. Theo mặc định, InnoDB đã tự động bật ON. Câu lệnh đầu tiên, INSERT, sẽ thực thi như một giao dịch đơn lẻ. Dữ liệu điều khiển đồng thời đa biến (MVCC) sẽ được tạo để giữ một bản sao của dữ liệu gốc trong bảng t2 trên cơ sở từng hàng. Nếu MySQL gặp sự cố trong quá trình thực thi INSERT, InnoDB sử dụng dữ liệu MVCC để khôi phục t2 về trạng thái ban đầu.
RolandoMySQLDBA

1
@saravanan: Giả sử INSERT hoạt động thành công. Dữ liệu thu được từ INSERT đã được cam kết (với chế độ tự động BẬT) và bảng bảo vệ MVCC t2 bị loại bỏ. Khi bạn thực hiện CẬP NHẬT, MVCC được tạo so với bảng t1 và CẬP NHẬT được thực hiện. Nếu MySQL gặp sự cố trong quá trình CẬP NHẬT, InnoDB sử dụng dữ liệu MVCC trên t1 để khôi phục lại CẬP NHẬT. Ngay cả khi CẬP NHẬT chỉ thay đổi một hàng, khả năng một trong một triệu vẫn tồn tại của việc chuyển các bản ghi từ t1 sang t2 với id 10 và không thay đổi id 10 thành id 11 trong t1. Để ngăn kịch bản độc đáo này, bạn cần thực hiện các thao tác sau ...
RolandoMySQLDBA

@savaranan: Xử lý hai câu lệnh SQL như một giao dịch. Cách đơn giản để làm điều này là: BEGIN; XÁC NHẬN VÀO t2 CHỌN * TỪ t1 WHERE id = 10; CẬP NHẬT t1 SET id = 11 WHERE id = 10; CAM KẾT; Lý do mạnh nhất để coi hai câu lệnh SQL là một giao dịch duy nhất là thực tế là MVCC được tạo cho INSERT sẽ vẫn tồn tại trong CẬP NHẬT. Nếu sự cố MySQL xảy ra trong quá trình CẬP NHẬT bên trong một giao dịch (BEGIN; ... CAM KẾT; chặn) MVCC sẽ khôi phục tất cả các thay đổi về trạng thái nhất quán. Nếu cả CHERTN và CẬP NHẬT được hoàn thành, thì MVCC sẽ bị loại bỏ vào phút cuối.
RolandoMySQLDBA
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.