Tôi đang sử dụng bulk_create để tải hàng nghìn hoặc hàng vào một DB postgresql. Thật không may một số hàng đang gây ra IntegrityError và ngừng quá trình bulk_create. Tôi đã tự hỏi nếu có một cách để nói với django để bỏ qua các hàng như vậy và tiết kiệm càng nhiều của lô càng tốt?Django bulk_create có bỏ qua các hàng gây ra IntegrityError?
Trả lời
(Lưu ý: Tôi không sử dụng Django, vì vậy có thể có câu trả lời khuôn khổ cụ thể phù hợp hơn)
Nó không phải là có thể cho Django để làm điều này bằng cách đơn giản bỏ qua INSERT
thất bại vì PostgreSQL hủy bỏ toàn bộ giao dịch trên lỗi đầu tiên.
Django sẽ cần một trong những cách tiếp cận:
INSERT
mỗi hàng trong một giao dịch riêng biệt và bỏ qua lỗi (rất chậm);- Tạo
SAVEPOINT
trước mỗi lần chèn (có thể có vấn đề về tỷ lệ); - Sử dụng quy trình hoặc truy vấn để chèn chỉ nếu hàng không tồn tại (phức tạp và chậm); hoặc
- Chèn hàng loạt hoặc (tốt hơn)
COPY
dữ liệu vào bảngTEMPORARY
, sau đó hợp nhất vào bảng phía máy chủ chính.
Cách tiếp cận giống như cận cảnh (3) có vẻ như là một ý tưởng hay, nhưng upsert and insert-if-not-exists are surprisingly complicated.
Cá nhân, tôi muốn thực hiện (4): tôi muốn số lượng lớn chèn vào một bảng riêng biệt mới, có lẽ UNLOGGED
hoặc TEMPORARY
, sau đó tôi muốn chạy một số lệnh SQL bằng tay để:
LOCK TABLE realtable IN EXCLUSIVE MODE;
INSERT INTO realtable
SELECT * FROM temptable WHERE NOT EXISTS (
SELECT 1 FROM realtable WHERE temptable.id = realtable.id
);
Các LOCK TABLE ... IN EXCLUSIVE MODE
ngăn chặn một chèn đồng thời tạo ra một hàng từ gây ra một cuộc xung đột với một chèn được thực hiện bởi các tuyên bố trên và không. Số điện thoại không ngăn đồng thời SELECT
s, chỉ SELECT ... FOR UPDATE
, INSERT
, UPDATE
và DELETE
, vì vậy đọc từ bảng tiếp tục như bình thường.
Nếu bạn không thể chặn đồng thời ghi quá lâu, bạn có thể sử dụng CTE có thể ghi để sao chép phạm vi hàng từ temptable
thành realtable
, thử lại mỗi khối nếu không thành công.
Cảm ơn @ craig-ringer Tôi đã kết thúc thanh toán bù trừ danh sách các đối tượng python của tôi trước khi chèn chúng vào DB, một cái gì đó tương tự như cách tiếp cận # 3 của bạn, nhưng trong python tinh khiết. – Meitham
Có một ví dụ chi tiết về (4) tại https://rodmtech.net/docs/django/django-bulk_create-without-integrityerror-rollback/ – eugene
Hoặc 5. Chia và chinh phục
Tôi đã không kiểm tra hoặc đo điểm chuẩn kỹ lưỡng, nhưng nó hoạt động khá tốt đối với tôi. YMMV, tùy thuộc vào số lượng lỗi bạn mong đợi nhận được trong một thao tác hàng loạt.
def psql_copy(records):
count = len(records)
if count < 1:
return True
try:
pg.copy_bin_values(records)
return True
except IntegrityError:
if count == 1:
# found culprit!
msg = "Integrity error copying record:\n%r"
logger.error(msg % records[0], exc_info=True)
return False
finally:
connection.commit()
# There was an integrity error but we had more than one record.
# Divide and conquer.
mid = count/2
return psql_copy(records[:mid]) and psql_copy(records[mid:])
# or just return False
Một cách giải quyết nhanh chóng và không có liên quan đến SQL thủ công và bảng tạm thời là cố gắng chèn hàng loạt dữ liệu. Nếu không thành công, hãy hoàn nguyên về chèn nối tiếp.
objs = [(Event), (Event), (Event)...]
try:
Event.objects.bulk_create(objs)
except IntegrityError:
for obj in objs:
try:
obj.save()
except IntegrityError:
continue
Nếu bạn có rất nhiều và rất nhiều sai sót này có thể không được như vậy hiệu quả (bạn sẽ dành nhiều thời gian serially chèn hơn làm như vậy với số lượng lớn), nhưng tôi đang làm việc thông qua một tập dữ liệu số cao với vài trùng lặp để giải quyết hầu hết các vấn đề của tôi.
Ngay cả ở Django 1.11 không có cách nào để thực hiện việc này.Tôi đã tìm thấy một lựa chọn tốt hơn so với sử dụng SQL thô. Nó sử dụng djnago-query-builder. Nó có một phương pháp upsert
from querybuilder.query import Query
q = Query().from_table(YourModel)
# replace with your real objects
rows = [YourModel() for i in range(10)]
q.upsert(rows, ['unique_fld1', 'unique_fld2'], ['fld1_to_update', 'fld2_to_update'])
Lưu ý: Các thư viện chỉ hỗ trợ PostgreSQL
- 1. django số lượng lớn tạo ra bỏ qua trùng lặp
- 2. Django chức năng bulk_create dụ
- 3. Django 1.4 - bulk_create với một danh sách
- 4. django bỏ qua admin.py
- 5. Django - bỏ qua hàng đầu tiên của mảng
- 6. SQLAlchemy IntegrityError và nhập dữ liệu hàng loạt
- 7. KnockoutJs với Jquery.tablesorter - Gây ra các hàng trùng lặp
- 8. IntegrityError khi tải cố định trong django thử nghiệm
- 9. IntegrityError: vi phạm khóa ngoài khi xóa
- 10. MySQL Bỏ qua đầu hàng
- 11. Đơn vị DB phải bỏ qua thứ tự các hàng
- 12. Thay thế các hàng hiện có bằng các hàng mới gây ra ngoại lệ khóa trùng lặp
- 13. Bỏ phiếu qua hàng nghìn cổng TCP
- 14. chuyển đổi JSF gây validator (s) để được bỏ qua
- 15. Bỏ qua SVN bỏ qua ... có thể?
- 16. Chọn bản ghi bỏ qua các hàng trong MS Access
- 17. numpy loadtxt bỏ qua hàng đầu tiên
- 18. Hủy liên kết drawables onPause() gây ra điều hướng ngược lại và bỏ qua bước này gây ra tràn bộ nhớ
- 19. bỏ qua các dòng có các từ cụ thể trong đầu ra vimdiff
- 20. Bỏ qua đầu ra từ subprocess.Popen
- 21. NET DataTable bỏ qua hàng trên Load (DataReader)
- 22. Sum() gây ra ngoại lệ thay vì trở về 0 khi không có hàng
- 23. Chú thích Django() nhiều lần gây ra các câu trả lời sai
- 24. ipdb, nhiều chủ đề và các chương trình tự động tải lại gây ra Lập trìnhError
- 25. SQL chọn các hàng riêng biệt và bỏ qua hàng nếu trống
- 26. Tại sao chức năng makemessages cho nội dung ngôn ngữ Django bỏ qua các tệp html?
- 27. Tiếp tục tải sau khi IntegrityError
- 28. SVN bỏ qua các tệp
- 29. Sao chép các hàng từ bảng này sang bảng khác, bỏ qua các bản sao
- 30. MySQL: "LIMIT 5" gây ra cảnh báo
Điều đó có thể không thực hiện được vì PostgreSQL hủy bỏ giao dịch về lỗi đầu tiên. Django sẽ cần hoặc là (a) tạo ra một SAVEPOINT trước mỗi chèn, làm chậm mọi thứ xuống và chi phí tài nguyên; hoặc (b) Sử dụng một thủ tục hoặc truy vấn để chèn chỉ khi hàng không tồn tại. Cá nhân, tôi muốn chèn hàng loạt vào một bảng riêng biệt mới, có thể là 'UNLOGGED' hoặc' TEMPORARY', sau đó 'INSERT INTO SELECT có thể thực hiện được * TỪ CÓ THỂ KHÔNG CÓ (SELECT 1 FROM realtable WHERE temptable.id = realtable.id)' hoặc tương tự. –