Làm thế nào để kiểm tra một câu lệnh SQL Update trước khi chạy nó?


96

Trong một số trường hợp, chạy câu lệnh UPDATE trong phiên bản sản xuất có thể tiết kiệm thời gian. Tuy nhiên, một bản cập nhật borked có thể tồi tệ hơn sự cố ban đầu.

Không sử dụng cơ sở dữ liệu thử nghiệm, các tùy chọn để cho biết câu lệnh cập nhật sẽ làm gì trước khi chạy nó?

Câu trả lời:


51

Ngoài việc sử dụng một giao dịch như Imad đã nói (dù sao cũng nên bắt buộc), bạn cũng có thể thực hiện kiểm tra thông minh xem các hàng nào bị ảnh hưởng bằng cách chạy một lựa chọn bằng cách sử dụng mệnh đề WHERE giống như UPDATE.

Vì vậy, nếu bạn CẬP NHẬT là

UPDATE foo
  SET bar = 42
WHERE col1 = 1
  AND col2 = 'foobar';

Phần sau sẽ cho bạn biết những hàng nào sẽ được cập nhật:

SELECT *
FROM foo
WHERE col1 = 1
  AND col2 = 'foobar';

1
Sử dụng các giao dịch sẽ tốt hơn để kiểm tra dữ liệu sau đó. Giả sử anh ấy muốn kiểm tra kết quả, tôi kết luận rằng câu lệnh của anh ấy phức tạp hơn 'SET bar = 42', vì vậy trong phiên của anh ấy, anh ấy sẽ có thể thực hiện một số truy vấn để kiểm tra tập dữ liệu kết quả ...
Imad Moqaddem

3
@ImadMoqaddem: Tôi đồng ý và đó là lý do tôi đã viết " Ngoài việc sử dụng một giao dịch như Imad nói "
a_horse_with_no_name

Và nếu bạn có FOREIGN KEY UPDATE CASCADEsql của bạn bị lỗi
Xanh

@Green: ý bạn là gì với "fail"?
a_horse_with_no_name

73

Còn về Giao dịch? Họ có Tính năng ROLLBACK-Tính năng.

@see https://dev.mysql.com/doc/refman/5.0/en/commit.html

Ví dụ:

START TRANSACTION;
SELECT * FROM nicetable WHERE somthing=1;
UPDATE nicetable SET nicefield='VALUE' WHERE somthing=1;
SELECT * FROM nicetable WHERE somthing=1; #check

COMMIT;
# or if you want to reset changes 
ROLLBACK;

SELECT * FROM nicetable WHERE somthing=1; #should be the old value

Câu trả lời cho câu hỏi từ @rickozoe bên dưới:

Nói chung, các dòng này sẽ không được thực hiện một lần. Trong PHP fe, bạn sẽ viết một cái gì đó như thế (có lẽ sạch hơn một chút, nhưng muốn trả lời nhanh ;-)):

$MysqlConnection->query('START TRANSACTION;');
$erg = $MysqlConnection->query('UPDATE MyGuests SET lastname='Doe' WHERE id=2;');
if($erg)
    $MysqlConnection->query('COMMIT;');
else
    $MysqlConnection->query('ROLLBACK;');

Một cách khác là sử dụng Biến MySQL (xem https://dev.mysql.com/doc/refman/5.7/en/user-variables.htm l và https://stackoverflow.com/a/18499823/1416909 ):

# do some stuff that should be conditionally rollbacked later on

SET @v1 := UPDATE MyGuests SET lastname='Doe' WHERE id=2;
IF(v1 < 1) THEN
    ROLLBACK;
ELSE
    COMMIT;
END IF;

Nhưng tôi khuyên bạn nên sử dụng các trình bao bọc ngôn ngữ có sẵn trong ngôn ngữ lập trình yêu thích của bạn.


1
Điều này sẽ có kết quả bất ngờ với các giao dịch lồng vào nhau.
bánh nướng

Bạn có thể vui lòng cho một ví dụ?
Marcel Lange

@JCM và những người khác, làm thế nào bạn có thể biết liệu nó có thành công hay không khi câu lệnh cập nhật thành công ở dòng 3 để bạn có thể cam kết và khôi phục?
ricko zoe

56

Tự động gửi TẮT ...

MySQL

set autocommit=0;

Nó đặt chế độ tự động tắt cho phiên hiện tại.

Bạn thực hiện câu lệnh của mình, xem nó đã thay đổi những gì và sau đó khôi phục nếu nó sai hoặc cam kết nếu đó là những gì bạn mong đợi!

CHỈNH SỬA: Lợi ích của việc sử dụng các giao dịch thay vì chạy truy vấn chọn lọc là bạn có thể kiểm tra tập hợp kết quả dễ dàng hơn.


4
@dystroy: mọi DBMS hợp lý đều hỗ trợ các giao dịch.
a_horse_with_no_name

7
Chỉ cần nhớ cam kết hoặc khôi phục giao dịch một cách nhanh chóng, nếu không bạn có nguy cơ chặn các giao dịch khác - và trong trường hợp xấu nhất, ứng dụng của bạn phải tạm dừng. Không phải là ý kiến ​​hay khi thực hiện truy vấn, sau đó ăn trưa, sau đó quay lại xem kết quả! :-)
Gary McGill

@GaryMcGill: giao dịch đang chờ xử lý sẽ (ít nhất là trong DBMS hiện đại) chỉ chặn các giao dịch ghi khác .
a_horse_with_no_name

5
@dystroy: Thật không may, MyISAM được sử dụng ở khắp mọi nơi và tôi không phải là DBA.
static_rtti

1
Tuyên bố sql thêm :)
Imad Moqaddem

11

Tôi biết đây là sự lặp lại của các câu trả lời khác, nhưng nó có một số hỗ trợ tinh thần để thực hiện thêm bước cho bản cập nhật thử nghiệm: D

Đối với bản cập nhật thử nghiệm, hash # là bạn của bạn.

Nếu bạn có một tuyên bố cập nhật như:

UPDATE 
wp_history
SET history_by="admin"
WHERE
history_ip LIKE '123%'

Bạn băm CẬP NHẬT và THIẾT LẬP để thử nghiệm, sau đó băm chúng trở lại:

SELECT * FROM
#UPDATE
wp_history
#SET history_by="admin"
WHERE
history_ip LIKE '123%'

Nó hoạt động cho các câu lệnh đơn giản.

Một giải pháp thực tế bắt buộc bổ sung là lấy một bản sao (bản sao dự phòng), bất cứ khi nào sử dụng cập nhật trên bảng sản xuất. Phpmyadmin> các hoạt động> sao chép: table_yearmonthday. Chỉ mất vài giây cho các bảng <= 100M.


5

Không phải là một câu trả lời trực tiếp, nhưng tôi đã thấy nhiều tình huống dữ liệu phức tạp có thể tránh được bằng cách gõ WHEREmệnh đề trước ! Đôi khi một WHERE 1 = 0cũng có thể giúp bạn đưa một tuyên bố làm việc lại với nhau một cách an toàn. Và việc xem xét một kế hoạch thực thi ước tính, sẽ ước tính các hàng bị ảnh hưởng, có thể hữu ích. Ngoài ra, trong một giao dịch mà bạn quay trở lại như những người khác đã nói.


2
@SystemParadox - không có gì, mặc dù WHERE 1 = 0có tính di động hơn nếu ai đó bắt gặp điều này đang làm việc với một DBMS khác. Ví dụ: SQL Server sẽ không chấp nhận WHERE FALSE.
David M,

2

Trong những trường hợp bạn muốn kiểm tra này, bạn nên chỉ tập trung vào các giá trị cột hiện tại và giá trị cột sắp được cập nhật .

Vui lòng xem đoạn mã sau mà tôi đã viết để cập nhật giá WHMCS:

# UPDATE tblinvoiceitems AS ii

SELECT                        ###  JUST
    ii.amount AS old_value,   ###  FOR
    h.amount AS new_value     ###  TESTING
FROM tblinvoiceitems AS ii    ###  PURPOSES.

JOIN tblhosting AS h ON ii.relid = h.id
JOIN tblinvoices AS i ON ii.invoiceid = i.id

WHERE ii.amount <> h.amount   ### Show only updatable rows

# SET ii.amount = h.amount

Bằng cách này, chúng tôi so sánh rõ ràng các giá trị đã có với các giá trị mới.


1

Chạy truy vấn chọn trên cùng một bảng với tất cả các wheređiều kiện bạn đang áp dụng trong truy vấn cập nhật.


0

làm cho SELECTnó,

thích nếu bạn có

UPDATE users SET id=0 WHERE name='jan'

chuyển đổi nó thành

SELECT * FROM users WHERE name='jan'

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.