{"id":496666,"date":"2026-09-30T07:30:03","date_gmt":"2026-09-30T07:30:03","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=496666"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2026-09-30T07:30:03","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=496666","title":{"rendered":"\u0422\u044b\u0441\u044f\u0447\u0430 INSERT \u0432 \u043b\u043e\u0433\u0430\u0445 \u0438 get_or_create \u043f\u043e\u0434 \u0433\u043e\u043d\u043a\u043e\u0439: \u043a\u0430\u043a \u043c\u044b \u0443\u0441\u043a\u043e\u0440\u0438\u043b\u0438 \u0437\u0430\u043f\u0438\u0441\u044c \u0432 Django"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u044b \u043a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u0432 \u043b\u043e\u0433\u0438 \u0438 \u0432\u0438\u0434\u0435\u043b\u0438 \u0442\u044b\u0441\u044f\u0447\u0443 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0445 <code>INSERT<\/code> \u043f\u043e\u0434\u0440\u044f\u0434? \u0418\u043b\u0438 \u0437\u0430\u043c\u0435\u0447\u0430\u043b\u0438, \u0447\u0442\u043e \u043f\u043e\u0434 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439 <code>get_or_create<\/code> \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u0442\u044c \u0438\u0437\u2011\u0437\u0430 \u043b\u0438\u0448\u043d\u0438\u0445 round-trip \u0438 retry?<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u2014 \u043f\u0440\u043e \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c\u0438 \u043e\u0431\u044a\u0451\u043c\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 Django ORM. \u041e\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0445 <code>bulk_create<\/code> \/ <code>bulk_update<\/code> \u0434\u043e PostgreSQL <code>COPY<\/code>, \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0439 \u0441 \u043f\u0440\u043e\u0434\u0430 \u0438 \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u044b\u0445 \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0445 \u0430\u043d\u0430\u043b\u043e\u0433\u043e\u0432 <code>get_or_create<\/code> \/ <code>update_or_create<\/code> \u043d\u0430 \u043e\u0434\u043d\u043e\u043c UPSERT.<\/p>\n<p>\u042f \u0432\u0435\u0434\u0443\u0449\u0438\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432 CodeScoring; \u043d\u0438\u0436\u0435 \u2014 \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u043b\u0438 \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043a\u0430\u043d\u0430\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u0430 \u043d\u0435 \u043f\u0435\u0440\u0435\u0441\u043a\u0430\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438. \u0415\u0441\u043b\u0438 \u0432\u044b \u0443\u0436\u0435 \u0437\u043d\u0430\u0435\u0442\u0435, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 <code>bulk_create<\/code>, \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0445 \u043a\u0430\u043c\u043d\u0435\u0439 \u0438\u043b\u0438 \u0441 \u043f\u0440\u043e\u0434\u043e\u0432\u044b\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432.<\/p>\n<h4>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h4>\n<ul>\n<li>\n<p><a href=\"#bulk-why\" rel=\"noopener nofollow\">\u0417\u0430\u0447\u0435\u043c \u043d\u0443\u0436\u043d\u044b bulk-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#bulk-builtin\" rel=\"noopener nofollow\">\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 bulk_create \u0438 bulk_update<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#bulk-pitfalls\" rel=\"noopener nofollow\">\u041f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u0438 bulk<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#bulk-conflicts\" rel=\"noopener nofollow\">\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430\u043c\u0438: ignore_conflicts \u0438 update_conflicts<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#django-bulk-load\" rel=\"noopener nofollow\">django-bulk-load \u0438 PostgreSQL COPY<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#benchmarks\" rel=\"noopener nofollow\">\u0411\u0435\u043d\u0447\u043c\u0430\u0440\u043a\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#set-diff\" rel=\"noopener nofollow\">\u0420\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432: \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0431\u0435\u0437 upsert<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#prod-examples\" rel=\"noopener nofollow\">\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0441 \u043f\u0440\u043e\u0434\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#atomic-get-or-create\" rel=\"noopener nofollow\">\u0421\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u044b\u0435 atomic_get_or_create \/ update_or_create<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#when-to-use\" rel=\"noopener nofollow\">\u041a\u043e\u0433\u0434\u0430 \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#conclusion\" rel=\"noopener nofollow\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"bulk-why\" id=\"bulk-why\"><\/a><\/p>\n<h3>\u0417\u0430\u0447\u0435\u043c \u043d\u0443\u0436\u043d\u044b bulk-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/h3>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043a\u0430\u0441\u0441\u0443 \u0432 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0435. \u0412\u044b \u043a\u0443\u043f\u0438\u043b\u0438 100 \u0442\u043e\u0432\u0430\u0440\u043e\u0432. \u041a\u0430\u0441\u0441\u0438\u0440 \u043c\u043e\u0436\u0435\u0442:<\/p>\n<ol>\n<li>\n<p>\u041f\u0440\u043e\u0431\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0442\u043e\u0432\u0430\u0440 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0447\u0435\u043a\u043e\u043c \u2014 100 \u043f\u043e\u0445\u043e\u0434\u043e\u0432 \u043a \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0443<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0431\u0438\u0442\u044c \u0432\u0441\u0451 \u043e\u0434\u043d\u0438\u043c \u0447\u0435\u043a\u043e\u043c<\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u0442\u043e\u0440\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u043d\u0435 \u0438\u0437-\u0437\u0430 \u043c\u0430\u0433\u0438\u0438, \u0430 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432\u044b \u0443\u0431\u0440\u0430\u043b\u0438 \u043d\u0430\u043a\u043b\u0430\u0434\u043d\u044b\u0435 \u0440\u0430\u0441\u0445\u043e\u0434\u044b: \u0441\u0435\u0442\u0435\u0432\u043e\u0439 round-trip (\u0442\u0443\u0434\u0430-\u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0434\u043e \u0411\u0414), \u0440\u0430\u0437\u0431\u043e\u0440 SQL, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<p>\u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0441 ORM:<\/p>\n<pre><code class=\"python\">for i in range(1000):    Product.objects.create(name=f'Product {i}', price=100 + i)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u0442\u044b\u0441\u044f\u0447\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 <code>INSERT<\/code>. \u041f\u0440\u0438 latency 1 \u043c\u0441 \u0432\u044b \u0443\u0436\u0435 \u043f\u043e\u0434\u0430\u0440\u0438\u043b\u0438 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0430 \u0441\u0435\u0442\u044c. \u0410 \u0435\u0449\u0451 \u0438\u043d\u0434\u0435\u043a\u0441\u044b, WAL, \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438. \u0412 \u0440\u0435\u0436\u0438\u043c\u0435 autocommit \u043a\u0430\u0436\u0434\u044b\u0439 <code>create()<\/code> \u0435\u0449\u0451 \u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f.<\/p>\n<p>Bulk-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u043a\u043b\u0435\u0438\u0432\u0430\u044e\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0432 \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u0440\u0443\u043f\u043d\u044b\u0445 SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u041d\u0430 \u0438\u043c\u043f\u043e\u0440\u0442\u0435, \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u0438 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0435 \u044d\u0442\u043e \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u0435 \u0432 \u0440\u0430\u0437\u044b, \u0430 \u0438\u043d\u043e\u0433\u0434\u0430 \u0432 \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u0440\u0430\u0437.<\/p>\n<blockquote>\n<p>Bulk \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u043e\u043b\u0435\u0437\u0435\u043d, \u043a\u043e\u0433\u0434\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043c\u043d\u043e\u0433\u043e, \u0430 \u043b\u043e\u0433\u0438\u043a\u0430 \u043d\u0430 \u043e\u0434\u0438\u043d \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u0440\u043e\u0441\u0442\u0430\u044f. \u0415\u0441\u043b\u0438 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u043d\u0443\u0436\u043d\u0430 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u0441 \u043a\u0443\u0447\u0435\u0439 \u0432\u0435\u0442\u0432\u043b\u0435\u043d\u0438\u0439 \u2014 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u043e\u0442\u043e\u043c \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u0443\u0439\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<\/blockquote>\n<p><a class=\"anchor\" name=\"bulk-builtin\" id=\"bulk-builtin\"><\/a><\/p>\n<h3>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 bulk_create \u0438 bulk_update<\/h3>\n<h4>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435<\/h4>\n<p>\u041d\u0430\u0438\u0432\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434:<\/p>\n<pre><code class=\"python\">for i in range(1000):    Product.objects.create(        name=f'Product {i}',        price=100 + i,    )<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"sql\">INSERT INTO products (name, price) VALUES ('Product 0', 100);INSERT INTO products (name, price) VALUES ('Product 1', 101);-- ... \u0435\u0449\u0451 998 \u0440\u0430\u0437<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Bulk:<\/p>\n<pre><code class=\"python\">products = [    Product(name=f'Product {i}', price=100 + i)    for i in range(1000)]Product.objects.bulk_create(products)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"sql\">INSERT INTO products (name, price)VALUES    ('Product 0', 100),    ('Product 1', 101),    -- ...    ('Product 999', 1099);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0434\u0438\u043d \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u044b\u0441\u044f\u0447\u0438.<\/p>\n<h4>\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435<\/h4>\n<p>\u041d\u0430\u0438\u0432\u043d\u043e:<\/p>\n<pre><code class=\"python\">for product in Product.objects.all():    product.price = new_price(product.price)    product.save(update_fields=('price',))<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0427\u0435\u0440\u0435\u0437 <code>bulk_update<\/code>:<\/p>\n<pre><code class=\"python\">products = list(Product.objects.all())for product in products:    product.price = new_price(product.price)Product.objects.bulk_update(products, fields=('price',))<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c Django \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0447\u0442\u043e-\u0442\u043e \u0432\u0440\u043e\u0434\u0435:<\/p>\n<pre><code class=\"sql\">UPDATE productsSET price = CASE    WHEN id = 1 THEN 200    WHEN id = 2 THEN 202    -- ...ENDWHERE id IN (1, 2, ...);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u2014 \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 \u0432\u044b \u043d\u0435 \u0436\u0434\u0451\u0442\u0435 \u043e\u0442 bulk \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e <code>save()<\/code>.<\/p>\n<p><a class=\"anchor\" name=\"bulk-pitfalls\" id=\"bulk-pitfalls\"><\/a><\/p>\n<h3>\u041f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u0438 bulk<\/h3>\n<h4>\u0421\u0438\u0433\u043d\u0430\u043b\u044b \u043c\u043e\u043b\u0447\u0430\u0442<\/h4>\n<p><code>bulk_create<\/code> \u0438 <code>bulk_update<\/code> <strong>\u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442<\/strong> \u0441\u0438\u0433\u043d\u0430\u043b\u044b <code>pre_save<\/code> \/ <code>post_save<\/code> \u0438 \u043d\u0435 \u0437\u0430\u0445\u043e\u0434\u044f\u0442 \u0432 <code>Model.save()<\/code>. \u041d\u043e \u0441 <code>auto_now<\/code> \/ <code>auto_now_add<\/code> \u0432\u0430\u0436\u043d\u044b\u0439 \u043d\u044e\u0430\u043d\u0441: \u044d\u0442\u043e \u043d\u0435 \u043e\u0434\u043d\u043e \u0438 \u0442\u043e \u0436\u0435, \u0447\u0442\u043e \u0441\u0438\u0433\u043d\u0430\u043b\u044b.<\/p>\n<p>\u041f\u0440\u0438 <code>bulk_create<\/code> Django \u0432\u0441\u0451 \u0436\u0435 \u0437\u043e\u0432\u0451\u0442 <code>Field.pre_save()<\/code> \u043d\u0430 \u043f\u043e\u043b\u044f\u0445 \u2014 \u0438 <code><strong>auto_now<\/strong><\/code><strong> \/ <\/strong><code><strong>auto_now_add<\/strong><\/code><strong> \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u044f\u0442\u0441\u044f<\/strong>. \u041f\u0440\u0438 <code>bulk_update<\/code> \u2014 \u043d\u0435\u0442: \u0432 SQL \u0443\u0445\u043e\u0434\u044f\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432, \u043f\u043e\u043b\u0435 \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0440\u0443\u043a\u0430\u043c\u0438 \u0438 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u0442\u044c \u0432 <code>fields<\/code>.<\/p>\n<pre><code class=\"python\">class Product(models.Model):    name = models.CharField(max_length=200, unique=True)    price = models.IntegerField()    updated_at = models.DateTimeField(auto_now=True)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"python\"># bulk_create: updated_at \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 Field.pre_saveProduct.objects.bulk_create(products)# bulk_update: updated_at \u0441\u0430\u043c \u041d\u0415 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0441\u044fProduct.objects.bulk_update(products, fields=('price',))# \u041d\u0443\u0436\u043d\u043e \u0440\u0443\u043a\u0430\u043c\u0438now = timezone.now()for product in products:    product.updated_at = nowProduct.objects.bulk_update(products, fields=('price', 'updated_at'))<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0430 \u0441\u0438\u0433\u043d\u0430\u043b <code>post_save<\/code> \u0432\u0438\u0441\u0438\u0442 \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043a\u044d\u0448\u0430 \u0438\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u2014 bulk \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0440\u043e\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u0442. \u041f\u043e\u0431\u043e\u0447\u043d\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0438\u043b\u0438 \u043e\u0441\u043e\u0437\u043d\u0430\u043d\u043d\u043e \u043e\u0442 \u043d\u0438\u0445 \u043e\u0442\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0422\u0440\u0438\u0433\u0433\u0435\u0440\u044b \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 PostgreSQL \u2014 \u0434\u0440\u0443\u0433\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f: \u043e\u043d\u0438 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 <code>INSERT<\/code>\/<code>UPDATE<\/code> \u0432 \u0446\u0435\u043b\u0435\u0432\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443. \u0410 \u0432\u043e\u0442 <code>COPY<\/code> \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0438\u0445 \u043e\u0431\u044b\u0447\u043d\u043e \u043d\u0435 \u043a\u0430\u0441\u0430\u0435\u0442\u0441\u044f. \u0423 django-bulk-load \u043d\u0435\u0442 \u043f\u0443\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 <code>Field.pre_save<\/code>, \u0442\u0430\u043a \u0447\u0442\u043e <code>auto_now<\/code> \u0442\u0430\u043c \u0442\u043e\u0436\u0435 \u043d\u0435 \u00ab\u043c\u0430\u0433\u0438\u044f\u00bb \u2014 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u043d\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u0445 \u0434\u043e \u0432\u044b\u0437\u043e\u0432\u0430.<\/p>\n<h4>primary key \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430<\/h4>\n<p>\u041a\u043e\u0440\u043e\u0442\u043a\u043e:<\/p>\n<ul>\n<li>\n<p>\u0431\u0435\u0437 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432, \u043d\u0430 PostgreSQL \/ MariaDB 10.5+ \/ SQLite 3.35+, PK \u043f\u043e\u0441\u043b\u0435 <code>bulk_create<\/code> \u043e\u0431\u044b\u0447\u043d\u043e \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u0445;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0430 MySQL \/ Oracle \u2014 \u0447\u0430\u0449\u0435 \u043d\u0435\u0442;<\/p>\n<\/li>\n<li>\n<p>\u0441 <code>ignore_conflicts=True<\/code> Django PK <strong>\u043d\u0435<\/strong> \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0434\u0430\u0436\u0435 \u043d\u0430 \u00ab\u0445\u043e\u0440\u043e\u0448\u0438\u0445\u00bb \u0411\u0414;<\/p>\n<\/li>\n<li>\n<p>\u0441 <code>update_conflicts=True<\/code> \u043d\u0430 PostgreSQL \/ MariaDB \/ SQLite PK \u043e\u0431\u044b\u0447\u043d\u043e <strong>\u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f<\/strong> (RETURNING \u043d\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f), \u043d\u0430 MySQL \/ Oracle \u2014 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430 \u044d\u0442\u043e \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c.<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\">db_products = Product.objects.bulk_create(products)all(p.pk for p in db_products)  # True \u043d\u0430 PostgreSQL \/ MariaDB 10.5+ \/ SQLite 3.35+db_products = Product.objects.bulk_create(products, ignore_conflicts=True)any(p.pk for p in db_products)  # Falsedb_products = Product.objects.bulk_create(    products,    update_conflicts=True,    update_fields=('price',),    unique_fields=('name',),)all(p.pk for p in db_products)  # \u043e\u0431\u044b\u0447\u043d\u043e True \u043d\u0430 PostgreSQL<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043f\u043e\u0441\u043b\u0435 \u0432\u0441\u0442\u0430\u0432\u043a\u0438 \u0441 <code>ignore_conflicts<\/code> \u043d\u0443\u0436\u043d\u044b id \u2014 \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 <code>SELECT<\/code> \u043f\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u043a\u043b\u044e\u0447\u0430\u043c.<\/p>\n<details class=\"spoiler\">\n<summary>\u0414\u0435\u0442\u0430\u043b\u0438 \u0438\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 Django \u0438 \u043d\u044e\u0430\u043d\u0441\u044b \u043f\u043e \u0432\u0435\u0440\u0441\u0438\u044f\u043c<\/summary>\n<div class=\"spoiler__content\">\n<p><a href=\"https:\/\/docs.djangoproject.com\/en\/6.0\/ref\/models\/querysets\/#bulk-create\" rel=\"noopener nofollow\">\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/a>:<\/p>\n<blockquote>\n<p>If the model\u2019s primary key is an AutoField or has a db_default value, and ignore_conflicts is False, the primary key attribute can only be retrieved on certain databases (currently PostgreSQL, MariaDB, and SQLite 3.35+). On other databases, it will not be set.<\/p>\n<\/blockquote>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u0423\u0441\u043b\u043e\u0432\u0438\u0435<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u041f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>AutoField<\/code> \/ <code>db_default<\/code>, \u0431\u0435\u0437 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432<\/p>\n<\/td>\n<td>\n<p align=\"left\">PK \u043d\u0430 <strong>PostgreSQL<\/strong>, <strong>MariaDB 10.5+<\/strong>, <strong>SQLite 3.35+<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">MySQL, Oracle \u0438 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435<\/p>\n<\/td>\n<td>\n<p align=\"left\">PK <strong>\u043d\u0435<\/strong> \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>ignore_conflicts=True<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">PK <strong>\u043d\u0435<\/strong> \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f (RETURNING \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>update_conflicts=True<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u043d\u0430 PostgreSQL \/ MariaDB \/ SQLite PK \u043e\u0431\u044b\u0447\u043d\u043e <strong>\u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f<\/strong>; \u043d\u0430 MySQL \/ Oracle \u2014 \u043d\u0435\u0442<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u041f\u043e \u0432\u0435\u0440\u0441\u0438\u044f\u043c: <code>RETURNING<\/code> \u043d\u0430 PostgreSQL \u0435\u0441\u0442\u044c \u0434\u0430\u0432\u043d\u043e; \u0432 Django 4.2 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u0431\u044b\u043b\u0438 PostgreSQL, MariaDB 10.5+ \u0438 SQLite 3.35+; \u0441 Django 5.0 \u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u043a\u0430 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 <code>db_default<\/code>; <code>update_conflicts<\/code> \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0432 Django 4.1.<\/p>\n<\/div>\n<\/details>\n<h4>\u0414\u0440\u0443\u0433\u0438\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f Django bulk<\/h4>\n<p>\u0418\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0438:<\/p>\n<ul>\n<li>\n<p><code>bulk_create<\/code> <strong>\u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442<\/strong> \u0441 multi-table inheritance \u0438 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 M2M;<\/p>\n<\/li>\n<li>\n<p><code>bulk_update<\/code> <strong>\u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442<\/strong> \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447;<\/p>\n<\/li>\n<li>\n<p>\u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b PK \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0431\u0430\u0442\u0447\u0430 <code>bulk_update<\/code> \u043f\u0440\u0438\u0432\u043e\u0434\u044f\u0442 \u043a \u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043c\u0435\u043d\u044c\u0448\u0435 \u0441\u0442\u0440\u043e\u043a, \u0447\u0435\u043c \u0432\u044b \u0436\u0434\u0451\u0442\u0435;<\/p>\n<\/li>\n<li>\n<p>\u0434\u0430\u0436\u0435 \u0441 <code>batch_size<\/code> Django \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0433\u043e\u0442\u043e\u0432\u0438\u0442 SQL\/<code>WHEN<\/code> \u0434\u043b\u044f \u0431\u0430\u0442\u0447\u0430 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u2014 \u043e\u0447\u0435\u043d\u044c \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0431\u0430\u0442\u0447 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0440\u0430\u0437\u0434\u0443\u0432\u0430\u0435\u0442 \u043a\u043b\u0438\u0435\u043d\u0442.<\/p>\n<\/li>\n<\/ul>\n<h4>\u0420\u0430\u0437\u043c\u0435\u0440 \u0431\u0430\u0442\u0447\u0430 \u0438\u043c\u0435\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435<\/h4>\n<p>\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043c\u0438\u043b\u043b\u0438\u043e\u043d \u0441\u0442\u0440\u043e\u043a \u043e\u0434\u043d\u0438\u043c <code>INSERT<\/code> \u0438\u043b\u0438 \u043e\u0434\u043d\u0438\u043c \u0433\u0438\u0433\u0430\u043d\u0442\u0441\u043a\u0438\u043c <code>CASE WHEN<\/code> \u2014 \u0438\u0434\u0435\u044f \u0441\u043f\u043e\u0440\u043d\u0430\u044f. \u0423\u043f\u0438\u0440\u0430\u0435\u0442\u0435\u0441\u044c \u0432 \u0440\u0430\u0437\u043c\u0435\u0440 statement, \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0434\u043b\u0438\u043d\u043d\u0443\u044e \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>batch_size<\/code> \u0440\u0435\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430 \u043a\u0443\u0441\u043a\u0438: \u043d\u0430\u0447\u0438\u043d\u0430\u0439\u0442\u0435 \u0441 1\u20135 \u0442\u044b\u0441\u044f\u0447, \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0430 \u0432\u0440\u0435\u043c\u044f \u0438 \u043f\u0430\u043c\u044f\u0442\u044c, \u043a\u0440\u0443\u0442\u0438\u0442\u0435. \u0411\u0435\u0437 \u043d\u0435\u0433\u043e Django <code>bulk_update<\/code> \u043d\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043e\u0431\u044a\u0451\u043c\u0430\u0445 \u043b\u0435\u0433\u043a\u043e \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u0430\u0442\u043e\u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u043e\u0431 \u044d\u0442\u043e\u043c \u043d\u0438\u0436\u0435 \u0432 \u0431\u0435\u043d\u0447\u043c\u0430\u0440\u043a\u0430\u0445.<\/p>\n<p><a class=\"anchor\" name=\"bulk-conflicts\" id=\"bulk-conflicts\"><\/a><\/p>\n<h3>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430\u043c\u0438: ignore_conflicts \u0438 update_conflicts<\/h3>\n<h4>\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b<\/h4>\n<pre><code class=\"python\">Product.objects.bulk_create(products, ignore_conflicts=True)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"sql\">INSERT INTO products (name, price)VALUES ('Product 0', 100), ('Product 1', 101), ...ON CONFLICT DO NOTHING;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0423\u0434\u043e\u0431\u043d\u043e \u0434\u043b\u044f \u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u043e\u0433\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0430: \u00ab\u0432\u0441\u0442\u0430\u0432\u044c, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0448\u044c, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u00bb.<\/p>\n<p>\u041d\u044e\u0430\u043d\u0441 \u043f\u0440\u043e target: \u0443 <code>ON CONFLICT DO NOTHING<\/code> \u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u2014 \u0442\u043e\u0433\u0434\u0430 \u0442\u0438\u0445\u043e \u043f\u0440\u043e\u0433\u043b\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442 \u043f\u043e <strong>\u043b\u044e\u0431\u043e\u043c\u0443<\/strong> unique\/exclusion constraint \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e <code>name<\/code>. \u0418\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a Django \u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 SQL \u043f\u0440\u0438 <code>ignore_conflicts=True<\/code> \u043d\u0430 PostgreSQL: \u0433\u043e\u043b\u044b\u0439 <code>ON CONFLICT DO NOTHING<\/code>, \u0431\u0435\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>unique_fields<\/code> \u0437\u0434\u0435\u0441\u044c \u043d\u0438 \u043f\u0440\u0438 \u0447\u0451\u043c \u2014 \u043e\u043d \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f <code>update_conflicts<\/code>. \u0415\u0441\u043b\u0438 unique-\u0438\u043d\u0434\u0435\u043a\u0441\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e, \u00ab\u043b\u0438\u0448\u043d\u0438\u0439\u00bb \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442 \u0442\u043e\u0436\u0435 \u0437\u0430\u043c\u043e\u043b\u0447\u0438\u0442; \u0447\u0442\u043e\u0431\u044b \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u043a\u043b\u044e\u0447, \u043f\u0438\u0448\u0438\u0442\u0435 raw SQL \u0441 \u044f\u0432\u043d\u044b\u043c target \u0438\u043b\u0438 \u0441\u0443\u0436\u0430\u0439\u0442\u0435 \u0441\u0445\u0435\u043c\u0443.<\/p>\n<h4>\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u0438 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0435 (upsert)<\/h4>\n<p>\u041d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 Django 4.1:<\/p>\n<pre><code class=\"python\">Product.objects.bulk_create(    products,    update_conflicts=True,    update_fields=('price', 'updated_at'),    unique_fields=('name',),)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"sql\">INSERT INTO products (name, price, updated_at)VALUES ('Product 0', 100, NOW()), ('Product 1', 101, NOW()), ...ON CONFLICT (name)DO UPDATE SET    price = EXCLUDED.price,    updated_at = EXCLUDED.updated_at;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0434\u0438\u043d \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441: \u043d\u043e\u0432\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f, \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f. \u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u2014 \u043e\u0442\u043b\u0438\u0447\u043d\u043e.<\/p>\n<p>Upsert \u0445\u043e\u0440\u043e\u0448 \u0434\u043b\u044f \u043f\u043b\u043e\u0441\u043a\u043e\u0433\u043e \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0430 \u00ab\u0432\u0441\u0442\u0430\u0432\u044c \u0438\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u0438 price\u00bb. \u041f\u043b\u043e\u0445 \u2014 \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0435\u0449\u0451 \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0438\u0441\u0447\u0435\u0437\u043d\u0443\u0432\u0448\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438, \u043a\u0440\u0443\u0442\u0438\u0442\u044c M2M \u0438\u043b\u0438 \u043d\u0435 \u0442\u0440\u043e\u0433\u0430\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044f \u043d\u0435 \u043c\u0435\u043d\u044f\u043b\u0438\u0441\u044c. \u0422\u043e\u0433\u0434\u0430 \u2014 \u0440\u0430\u0437\u0434\u0435\u043b \u043f\u0440\u043e \u0440\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432.<\/p>\n<p><a class=\"anchor\" name=\"django-bulk-load\" id=\"django-bulk-load\"><\/a><\/p>\n<h3>django-bulk-load \u0438 PostgreSQL COPY<\/h3>\n<p>\u041d\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 <code>bulk_create<\/code> \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0435\u0441\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u043d\u0435 \u043d\u0430 INSERT, \u0430 \u043d\u0430 \u0440\u0430\u0437\u0434\u0443\u0432\u0430\u043d\u0438\u0435 SQL. \u041c\u044b \u043f\u043e\u0448\u043b\u0438 \u0432 <a href=\"https:\/\/github.com\/cedar-team\/django-bulk-load\" rel=\"noopener nofollow\">django-bulk-load<\/a>: \u043e\u043d\u0430 \u0433\u043e\u043d\u044f\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0447\u0435\u0440\u0435\u0437 <code>COPY<\/code> \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443, \u0430 \u043f\u043e\u0442\u043e\u043c \u043e\u0434\u043d\u0438\u043c-\u0434\u0432\u0443\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u0432 \u0431\u043e\u0435\u0432\u0443\u044e. \u0412 \u043a\u043e\u0434\u0435 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043b\u0438 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c <code>bulk_insert_models<\/code>, <code>bulk_update_models<\/code> \u0438 <code>bulk_upsert_models<\/code>; \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u2014 \u0432 <a href=\"https:\/\/github.com\/cedar-team\/django-bulk-load\" rel=\"noopener nofollow\">README<\/a>.<\/p>\n<p>\u041c\u0438\u043d\u0443\u0441\u044b \u0441\u0440\u0430\u0437\u0443: \u0442\u043e\u043b\u044c\u043a\u043e PostgreSQL, \u0437\u0430\u0432\u044f\u0437\u043a\u0430 \u043d\u0430 <strong>psycopg2<\/strong>, \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 \u2014 pin \u0432\u0435\u0440\u0441\u0438\u0438 \u0438 \u043d\u0435 \u0441\u0442\u0435\u0441\u043d\u044f\u0439\u0442\u0435\u0441\u044c \u0444\u043e\u0440\u043a\u043d\u0443\u0442\u044c, \u0435\u0441\u043b\u0438 \u0437\u0430\u0441\u0442\u0440\u044f\u043d\u0435\u0442\u0435.<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0432\u0441\u0442\u0430\u0432\u043a\u0438:<\/p>\n<pre><code class=\"python\">from django_bulk_load import bulk_insert_modelsproducts = [    Product(name='Product 1', price=100),    Product(name='Product 2', price=200),    Product(name='Product 3', price=150),]bulk_insert_models(products)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u0445\u0435\u043c\u0430 \u043f\u0440\u043e\u0441\u0442\u0430\u044f: temp table \u2192 <code>COPY<\/code> \u2192 <code>UPDATE<\/code>\/<code>INSERT<\/code> \u0432 \u0446\u0435\u043b\u0435\u0432\u0443\u044e \u2192 temp \u0438\u0441\u0447\u0435\u0437\u0430\u0435\u0442 \u043d\u0430 commit. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 PgBouncer \u0432 transaction pooling, \u0441 temp tables \u0431\u044b\u0441\u0442\u0440\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0433\u0440\u0443\u0441\u0442\u043d\u043e \u2014 \u043d\u0443\u0436\u0435\u043d session pooling \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u0443\u0442\u044c.<\/p>\n<h4>\u041a\u0430\u043a \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 bulk_upsert_models<\/h4>\n<p>\u0421\u043b\u043e\u0432\u043e \u00abupsert\u00bb \u0443 Django \u0438 \u0443 django-bulk-load \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 <strong>\u0440\u0430\u0437\u043d\u044b\u0439 SQL<\/strong>. Django \u2014 \u043e\u0434\u0438\u043d <code>ON CONFLICT DO UPDATE<\/code>. Cedar \u2014 <code>COPY<\/code>, \u043f\u043e\u0442\u043e\u043c <code>UPDATE ... FROM<\/code> \u0442\u043e\u043b\u044c\u043a\u043e \u0433\u0434\u0435 <code>IS DISTINCT FROM<\/code>, \u043f\u043e\u0442\u043e\u043c <code>INSERT<\/code> \u0447\u0435\u0440\u0435\u0437 <code>LEFT JOIN<\/code> \u0434\u043b\u044f \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0441\u0442\u0440\u043e\u043a. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043c\u0430\u0442\u0447 \u043f\u043e PK; \u0434\u043b\u044f \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u043f\u043e <code>name<\/code> \u0435\u0441\u0442\u044c <code>pk_field_names<\/code>.<\/p>\n<p>\u041f\u043b\u044e\u0441: \u043d\u0435 \u0437\u0430\u0442\u0438\u0440\u0430\u0435\u0442 <code>created_at<\/code> \u0438 \u043d\u0435 \u0448\u043b\u0451\u0442 \u0433\u0438\u0433\u0430\u043d\u0442\u0441\u043a\u0438\u0439 <code>CASE WHEN<\/code>. \u041c\u0438\u043d\u0443\u0441: \u0434\u0432\u0430 \u0432\u043e\u0440\u043a\u0435\u0440\u0430 \u043c\u043e\u0433\u0443\u0442 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043d\u0435 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 \u0438 \u043e\u0431\u0430 \u043f\u043e\u043b\u0435\u0437\u0443\u0442 \u0432 INSERT \u2014 \u043f\u0440\u0438 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e\u043c unique \u0431\u0443\u0434\u0435\u0442 <code>IntegrityError<\/code>, \u043d\u0435 \u0434\u0443\u0431\u043b\u044c. \u0414\u043b\u044f \u043e\u0434\u043d\u043e\u0433\u043e job\u2019\u0430 \u043d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442 \u043c\u044b \u0436\u0438\u0432\u0451\u043c \u0441 \u044d\u0442\u0438\u043c; \u0434\u043b\u044f \u0433\u043e\u0440\u044f\u0447\u0435\u0433\u043e \u043a\u043b\u044e\u0447\u0430 \u0438\u0437 \u0434\u0435\u0441\u044f\u0442\u0438 \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u0432 \u2014 Django <code>ON CONFLICT<\/code> \u0438\u043b\u0438 advisory lock.<\/p>\n<h4>\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f<\/h4>\n<ul>\n<li>\n<p><strong>\u0422\u043e\u043b\u044c\u043a\u043e PostgreSQL.<\/strong><\/p>\n<\/li>\n<li>\n<p>\u0421\u0438\u0433\u043d\u0430\u043b\u044b \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f. <code>auto_now<\/code> \/ <code>auto_now_add<\/code> \u0441\u0430\u043c\u0438 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u044f\u0442\u0441\u044f \u2014 \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 Django <code>bulk_create<\/code>, \u0437\u0434\u0435\u0441\u044c \u043d\u0435\u0442 <code>Field.pre_save<\/code>.<\/p>\n<\/li>\n<li>\n<p>Edge case: \u043f\u043b\u043e\u0445\u043e \u0434\u0440\u0443\u0436\u0438\u0442 \u0441 <code>ArrayField<\/code>. \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f <code>COPY<\/code> \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u043a\u0430\u043a \u0441\u0442\u0440\u043e\u043a\u0438, \u0430 \u043d\u0435 \u0447\u0435\u0440\u0435\u0437 \u0430\u0434\u0430\u043f\u0442\u0435\u0440 psycopg. \u0418\u043d\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a postgres-\u043b\u0438\u0442\u0435\u0440\u0430\u043b\u0443 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 (\u043d\u0438\u0436\u0435 \u2014 \u0443\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440; \u0434\u043b\u044f \u0441\u0442\u0440\u043e\u043a \u0441 \u0437\u0430\u043f\u044f\u0442\u044b\u043c\u0438\/\u043a\u0430\u0432\u044b\u0447\u043a\u0430\u043c\u0438 \u043d\u0443\u0436\u043d\u0430 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0430\u044f \u043b\u0438\u0442\u0435\u0440\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f):<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\">occurrence.files = f'{{{\",\".join(occurrence.files)}}}' if occurrence.files else Nonebulk_insert_models(models=(occurrence,))<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0430 \u0441\u043e\u0442\u043d\u044f\u0445 \u0441\u0442\u0440\u043e\u043a temp table \u0441\u044a\u0435\u0434\u0430\u0435\u0442 \u0432\u044b\u0438\u0433\u0440\u044b\u0448 \u2014 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 <code>bulk_create<\/code> \u0441 <code>batch_size<\/code>, COPY \u043f\u043e\u0442\u043e\u043c. \u041f\u0440\u043e <code>ArrayField<\/code> \u043c\u044b \u0443\u0437\u043d\u0430\u043b\u0438 \u043f\u043e \u043b\u043e\u0433\u0443 <code>malformed array literal<\/code> \u2014 \u0441\u043c. \u0445\u0430\u043a \u0432 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0432\u044b\u0448\u0435.<\/p>\n<p><a class=\"anchor\" name=\"benchmarks\" id=\"benchmarks\"><\/a><\/p>\n<h3>\u0411\u0435\u043d\u0447\u043c\u0430\u0440\u043a\u0438<\/h3>\n<p>\u0426\u0438\u0444\u0440\u044b \u043d\u0438\u0436\u0435 \u2014 \u0438\u0437 README <a href=\"https:\/\/github.com\/cedar-team\/django-bulk-load\" rel=\"noopener nofollow\">django-bulk-load<\/a> (Cedar), \u043d\u0435 \u0441 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0434\u0430. \u0410\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u044b\u0435 \u0441\u0435\u043a\u0443\u043d\u0434\u044b \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 \u0436\u0435\u043b\u0435\u0437\u0430 \u0438 \u0441\u0445\u0435\u043c\u044b; \u0432\u0430\u0436\u0435\u043d \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0432\u0435\u043b\u0438\u0447\u0438\u043d.<\/p>\n<p>\u0412\u0430\u0436\u043d\u043e \u0447\u0438\u0442\u0430\u0442\u044c \u0438\u0445 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e:<\/p>\n<ul>\n<li>\n<p>\u043a\u043e\u043b\u043e\u043d\u043a\u0430 <strong>Django <\/strong><code><strong>bulk_update<\/strong><\/code> \u2014 \u0432\u044b\u0437\u043e\u0432 <strong>\u0431\u0435\u0437<\/strong> <code>batch_size<\/code>, \u0442\u043e \u0435\u0441\u0442\u044c \u043e\u0434\u0438\u043d \u043e\u0433\u0440\u043e\u043c\u043d\u044b\u0439 <code>CASE WHEN<\/code>. \u042d\u0442\u043e \u043f\u0430\u0442\u043e\u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 Django docs \u043a\u0430\u043a \u0440\u0430\u0437 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0435\u0440\u0435\u0433\u0430\u044e\u0442 \u043d\u0435 \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c;<\/p>\n<\/li>\n<li>\n<p><strong>django-bulk-update<\/strong> \u2014 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 (<a href=\"https:\/\/github.com\/aykut\/django-bulk-update\" rel=\"noopener nofollow\">aykut\/django-bulk-update<\/a>), \u043d\u0435 \u043f\u0443\u0442\u0430\u0442\u044c \u0441 django-bulk-load;<\/p>\n<\/li>\n<li>\n<p><code><strong>bulk_update_models<\/strong><\/code> \u2014 \u043c\u0435\u0442\u043e\u0434 django-bulk-load \u0447\u0435\u0440\u0435\u0437 <code>COPY<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>647 \u0441\u0435\u043a\u0443\u043d\u0434 \u043d\u0430 100K \u2014 \u044d\u0442\u043e \u043d\u0435 \u00abDjango \u043f\u043b\u043e\u0445\u043e\u0439\u00bb, \u0430 \u00ab\u043a\u0442\u043e-\u0442\u043e \u0432\u044b\u0437\u0432\u0430\u043b <code>bulk_update<\/code> \u0431\u0435\u0437 <code>batch_size<\/code> \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u043c\u043e\u043d\u0441\u0442\u0440\u0430 \u0438\u0437 <code>CASE WHEN<\/code>\u00bb. \u0421 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0431\u0430\u0442\u0447\u0438\u043d\u0433\u043e\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u0430 \u043c\u044f\u0433\u0447\u0435, \u043d\u043e \u043d\u0430 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u0430\u0445 \u0441\u0442\u0440\u043e\u043a COPY \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0447\u0430\u0441\u0442\u043e \u0432\u044b\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442 \u2014 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043d\u0438\u0436\u0435 \u043a\u0430\u043a \u0440\u0430\u0437 \u043f\u0440\u043e \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0432\u0435\u043b\u0438\u0447\u0438\u043d, \u043d\u0435 \u043f\u0440\u043e \u0432\u0430\u0448 \u043d\u043e\u0443\u0442\u0431\u0443\u043a.<\/p>\n<h4>\u041c\u0430\u0441\u0441\u043e\u0432\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435<\/h4>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u0417\u0430\u043f\u0438\u0441\u0435\u0439<\/p>\n<\/th>\n<th>\n<p align=\"left\">Django <code>bulk_update<\/code> (\u0431\u0435\u0437 batch_size)<\/p>\n<\/th>\n<th>\n<p align=\"left\">django-bulk-update<\/p>\n<\/th>\n<th>\n<p align=\"left\"><code>bulk_update_models<\/code> (COPY)<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">1K<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.45 \u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.10 \u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>0.05 \u0441<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">10K<\/p>\n<\/td>\n<td>\n<p align=\"left\">6.08 \u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\">2.43 \u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>0.11 \u0441<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">100K<\/p>\n<\/td>\n<td>\n<p align=\"left\">647.66 \u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\">619.06 \u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>0.96 \u0441<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">1M<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0441\u044f<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0441\u044f<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>14.92 \u0441<\/strong><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<h4>\u041c\u0430\u0441\u0441\u043e\u0432\u0430\u044f \u0432\u0441\u0442\u0430\u0432\u043a\u0430<\/h4>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u0417\u0430\u043f\u0438\u0441\u0435\u0439<\/p>\n<\/th>\n<th>\n<p align=\"left\">Django <code>bulk_create<\/code><\/p>\n<\/th>\n<th>\n<p align=\"left\"><code>bulk_insert_models<\/code> (COPY)<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">1K<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.05 \u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>0.03 \u0441<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">10K<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.46 \u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>0.19 \u0441<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">100K<\/p>\n<\/td>\n<td>\n<p align=\"left\">4.88 \u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>1.76 \u0441<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">1M<\/p>\n<\/td>\n<td>\n<p align=\"left\">59.17 \u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>18.65 \u0441<\/strong><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043e\u0431\u044a\u0451\u043c\u043e\u0432 \u043d\u0430 PostgreSQL <code>django-bulk-load<\/code> \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432 2\u20133 \u0440\u0430\u0437\u0430 \u043d\u0430 insert \u0438 \u043d\u0430 \u043f\u043e\u0440\u044f\u0434\u043a\u0438 \u2014 \u043d\u0430 \u00ab\u043b\u043e\u043c\u0430\u044e\u0449\u0435\u043c\u00bb update \u0431\u0435\u0437 \u0431\u0430\u0442\u0447\u0438\u043d\u0433\u0430. \u041d\u0430 \u0442\u044b\u0441\u044f\u0447\u0435 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u043a\u043e\u043f\u0435\u0435\u0447\u043d\u0430\u044f \u2014 \u0432\u044b\u0431\u0438\u0440\u0430\u0439\u0442\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u043f\u043e \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0443 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<p><a class=\"anchor\" name=\"set-diff\" id=\"set-diff\"><\/a><\/p>\n<h3>\u0420\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432: \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0431\u0435\u0437 upsert<\/h3>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u043a\u0430\u043d\u0430 \u0443 \u0432\u0430\u0441 \u043d\u0435 \u00ab\u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432\u0441\u0451 \u043f\u043e\u0434\u0440\u044f\u0434\u00bb, \u0430 \u0442\u0440\u0438 \u043a\u043e\u0440\u0437\u0438\u043d\u044b: \u0447\u0442\u043e \u043f\u043e\u044f\u0432\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u0438\u0441\u0447\u0435\u0437\u043b\u043e \u0438\u0437 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043d\u0438\u043c\u043a\u0430 (\u0443 \u043d\u0430\u0441 \u0438\u0441\u0447\u0435\u0437\u043d\u0443\u0432\u0448\u0438\u0435 \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0447\u0435\u0440\u0435\u0437 <code>end_date<\/code>).<\/p>\n<p>\u041f\u0430\u0442\u0442\u0435\u0440\u043d \u043c\u043e\u043b\u0447\u0430 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442: \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043a\u043b\u044e\u0447 <strong>\u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0439<\/strong> \u00ab\u0442\u0435\u043a\u0443\u0449\u0435\u0439\u00bb \u0441\u0442\u0440\u043e\u043a\u0438 (<code>end_date IS NULL<\/code>). \u041e\u0431\u044b\u0447\u043d\u044b\u0439 <code>UniqueConstraint<\/code> \u043d\u0430 <code>(project, dependency, license, end_date)<\/code> \u044d\u0442\u043e\u0433\u043e <strong>\u043d\u0435<\/strong> \u0434\u0430\u0451\u0442: \u0432 PostgreSQL \u0434\u0432\u0430 <code>NULL<\/code> \u0432 unique-\u043a\u043e\u043b\u043e\u043d\u043a\u0430\u0445 \u043d\u0435 \u0440\u0430\u0432\u043d\u044b \u0434\u0440\u0443\u0433 \u0434\u0440\u0443\u0433\u0443, \u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0434\u0443\u0431\u043b\u0435\u0439 \u0441 \u043e\u0434\u043d\u0438\u043c \u043a\u043b\u044e\u0447\u043e\u043c \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b. \u041d\u0443\u0436\u0435\u043d <strong>partial unique index<\/strong> \u2014 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e \u043f\u043e\u043b\u044f\u043c \u043a\u043b\u044e\u0447\u0430 <strong>\u0442\u043e\u043b\u044c\u043a\u043e<\/strong> \u043f\u0440\u0438 <code>end_date IS NULL<\/code>:<\/p>\n<pre><code class=\"python\">UniqueConstraint(    fields=['project', 'dependency', 'license'],    condition=Q(end_date__isnull=True),    name='uniq_license_record_current',)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u043e\u0433\u0434\u0430 set-diff, <code>filter(end_date=None)<\/code> \u0438 \u00abpartial unique \u043a\u0430\u043a \u043f\u0440\u0435\u0434\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0435\u043b\u044c\u00bb \u0438\u0437 \u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u043f\u0440\u043e \u0433\u043e\u043d\u043a\u0438 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 \u043e\u0434\u043d\u0443 \u0438 \u0442\u0443 \u0436\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>\u041d\u0430\u0438\u0432\u043d\u044b\u0439 \u043a\u043e\u0434:<\/p>\n<pre><code class=\"python\">for item in incoming:    obj, created = Model.objects.get_or_create(...)    if not created:        obj.save(update_fields=...)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0430 \u0442\u044b\u0441\u044f\u0447\u0430\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u044d\u0442\u043e \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432 \u0442\u044b\u0441\u044f\u0447\u0438 SELECT + INSERT\/UPDATE. \u041f\u043b\u044e\u0441 \u0437\u0430\u0431\u044b\u0442\u044b\u0435 \u00ab\u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f\u00bb \u0441\u0442\u0430\u0440\u044b\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439.<\/p>\n<p>\u041f\u0430\u0442\u0442\u0435\u0440\u043d \u0447\u0435\u0440\u0435\u0437 \u0440\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432 \u043a\u043b\u044e\u0447\u0435\u0439:<\/p>\n<pre><code class=\"python\">existing = {    (row.dependency_id, row.relation): row    for row in Model.objects.filter(project=project, end_date=None)}actual = {    (dto.dependency_id, dto.relation): build_model(dto)    for dto in dtos}to_create = actual.keys() - existing.keys()   # \u043d\u043e\u0432\u044b\u0435to_update = actual.keys() &amp; existing.keys()   # \u043f\u0435\u0440\u0435\u0441\u0435\u0447\u0435\u043d\u0438\u0435to_close = existing.keys() - actual.keys()    # \u0438\u0441\u0447\u0435\u0437\u043d\u0443\u0432\u0448\u0438\u0435<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u0430\u043b\u044c\u0448\u0435:<\/p>\n<pre><code class=\"python\"># 1. \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u043e\u0432\u044b\u0435Model.objects.bulk_create([actual[k] for k in to_create])# 2. \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0432\u0448\u0438\u0435\u0441\u044fchanged = []for key in to_update:    db_row = existing[key]    new_row = actual[key]    if update_if_fields_changed(db_row, new_row, fields):        changed.append(db_row)Model.objects.bulk_update(changed, fields=fields)# 3. \u0417\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u043e\u0434\u043d\u0438\u043c UPDATEModel.objects.filter(    id__in=[existing[k].id for k in to_close]).update(end_date=analysis_time)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0430 \u0434\u0435\u0441\u044f\u0442\u043a\u0430\u0445 \u0442\u044b\u0441\u044f\u0447 id \u0432 <code>IN<\/code> \u0437\u0430\u043f\u0440\u043e\u0441 \u0442\u044f\u0436\u0435\u043b\u0435\u0435\u0442 \u2014 \u0442\u043e\u0433\u0434\u0430 \u043b\u0443\u0447\u0448\u0435 temp table \/ <code>unnest<\/code>, \u0430 \u043d\u0435 \u0433\u0438\u0433\u0430\u043d\u0442\u0441\u043a\u0438\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0432 SQL.<\/p>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 upsert \u0437\u0434\u0435\u0441\u044c \u0447\u0430\u0441\u0442\u043e \u043d\u0435 \u0437\u0430\u043c\u0435\u043d\u0430<\/h4>\n<ul>\n<li>\n<p>upsert \u043d\u0435 \u0443\u043c\u0435\u0435\u0442 \u00ab\u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0442\u043e, \u0447\u0435\u0433\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435\u0442 \u0432\u043e \u0432\u0445\u043e\u0434\u043d\u043e\u043c \u043d\u0430\u0431\u043e\u0440\u0435\u00bb;<\/p>\n<\/li>\n<li>\n<p>Django <code>ON CONFLICT DO UPDATE<\/code> \u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0435\u0442 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u0430\u0436\u0435 \u0431\u0435\u0437 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439; django-bulk-load upsert \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f <code>IS DISTINCT FROM<\/code> \u0437\u0434\u0435\u0441\u044c \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u0435\u0435;<\/p>\n<\/li>\n<li>\n<p>\u0434\u043b\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0441 <code>end_date IS NULL<\/code> \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0447\u0430\u0441\u0442\u043e \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u0430\u044f, \u0438 \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430 \u00ab\u0442\u0435\u043a\u0443\u0449\u0435\u0439\u00bb \u0437\u0430\u043f\u0438\u0441\u0438 \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e <code>ON CONFLICT (a, b)<\/code>;<\/p>\n<\/li>\n<li>\n<p>M2M \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 insert\/delete.<\/p>\n<\/li>\n<\/ul>\n<p>Upsert \u0445\u043e\u0440\u043e\u0448 \u0434\u043b\u044f \u043f\u043b\u043e\u0441\u043a\u0438\u0445 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432. \u0414\u043b\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0441 create \/ update \/ close \u2014 \u0440\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432.<\/p>\n<h4>\u0413\u043e\u043d\u043a\u0438 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u043a\u0430\u043d\u043e\u0432<\/h4>\n<p><code>SELECT<\/code> \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u2192 diff \u2192 bulk-\u0437\u0430\u043f\u0438\u0441\u044c <strong>\u043d\u0435 \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b<\/strong> \u0441\u0430\u043c\u0438 \u043f\u043e \u0441\u0435\u0431\u0435. \u0414\u0432\u0430 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u043a\u0430\u043d\u0430 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043c\u043e\u0433\u0443\u0442:<\/p>\n<ul>\n<li>\n<p>\u043e\u0431\u0430 \u0440\u0435\u0448\u0438\u0442\u044c \u00ab\u0441\u043e\u0437\u0434\u0430\u0442\u044c\u00bb \u043e\u0434\u043d\u0443 \u0438 \u0442\u0443 \u0436\u0435 \u0441\u0442\u0440\u043e\u043a\u0443;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0434\u0438\u043d \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432\u0442\u043e\u0440\u043e\u0439 \u0435\u0449\u0451 \u0441\u0447\u0438\u0442\u0430\u0435\u0442 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439;<\/p>\n<\/li>\n<li>\n<p>\u0437\u0430\u0442\u0435\u0440\u0435\u0442\u044c \u0447\u0443\u0436\u0438\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ul>\n<p>\u0427\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0441\u0442\u0430\u0432\u044f\u0442 \u0441\u0432\u0435\u0440\u0445\u0443:<\/p>\n<ul>\n<li>\n<p>\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u043a\u0430\u043d\u043e\u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 (\u043e\u0447\u0435\u0440\u0435\u0434\u044c \/ \u043e\u0434\u0438\u043d \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0439 job);<\/p>\n<\/li>\n<li>\n<p><code>pg_advisory_xact_lock(project_id)<\/code> \u0432\u043e\u043a\u0440\u0443\u0433 sync;<\/p>\n<\/li>\n<li>\n<p><code>select_for_update()<\/code> \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0441\u0442\u0440\u043e\u043a\u0430\u0445 \u0432\u043d\u0443\u0442\u0440\u0438 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p>partial unique index \u043a\u0430\u043a \u043f\u0440\u0435\u0434\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0435\u043b\u044c \u043e\u0442 \u0434\u0443\u0431\u043b\u0435\u0439 \u00ab\u0442\u0435\u043a\u0443\u0449\u0438\u0445\u00bb \u0437\u0430\u043f\u0438\u0441\u0435\u0439.<\/p>\n<\/li>\n<\/ul>\n<p>\u0411\u0435\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u044d\u0442\u0438\u0445 \u0441\u043b\u043e\u0451\u0432 \u043f\u0430\u0442\u0442\u0435\u0440\u043d \u0431\u044b\u0441\u0442\u0440\u044b\u0439, \u043d\u043e \u0445\u0440\u0443\u043f\u043a\u0438\u0439 \u043f\u043e\u0434 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0446\u0438\u0435\u0439.<\/p>\n<p>\u0425\u0435\u043b\u043f\u0435\u0440 \u00ab\u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u043b\u043e\u0441\u044c\u00bb:<\/p>\n<pre><code class=\"python\">def update_if_fields_changed(    model: models.Model,    new_data: models.Model,    update_fields: Iterable[str],    *,    allow_update_to_none: bool = False,) -&gt; bool:    updated = False    for field in update_fields:        existing_value = getattr(model, field, None)        new_value = getattr(new_data, field)        can_apply = new_value is not None or allow_update_to_none        if can_apply and new_value != existing_value:            setattr(model, field, new_value)            updated = True    return updated<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041c\u0435\u043b\u043e\u0447\u044c, \u0430 \u043d\u0430 100K \u0441\u0442\u0440\u043e\u043a \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442 \u043a\u0443\u0447\u0443 \u0431\u0435\u0441\u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 UPDATE \u0438 \u043d\u0435 \u0442\u0440\u043e\u0433\u0430\u0435\u0442 <code>updated_at<\/code> \u0437\u0440\u044f.<\/p>\n<p><a class=\"anchor\" name=\"prod-examples\" id=\"prod-examples\"><\/a><\/p>\n<h3>\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0441 \u043f\u0440\u043e\u0434\u0430<\/h3>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0447\u0435\u0441\u0442\u043d\u043e \u043f\u0440\u043e\u0444\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u043b\u0438: \u044d\u0442\u0430\u043f \u0437\u0430\u043f\u0438\u0441\u0438 \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0439 \u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u043e\u0441\u043b\u0435 SCA \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u043b \u0432\u0435\u0441\u044c \u0441\u043a\u0430\u043d, \u0432 \u043b\u043e\u0433\u0430\u0445 \u2014 \u0441\u043f\u043b\u043e\u0448\u043d\u044b\u0435 <code>SELECT<\/code>\/<code>INSERT<\/code>. \u041e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044f <code>select_related<\/code> \u0442\u0443\u0442 \u043d\u0435 \u0441\u043f\u0430\u0441\u0430\u043b\u0430: \u0443\u0437\u043a\u043e\u0435 \u043c\u0435\u0441\u0442\u043e \u0431\u044b\u043b\u043e \u0432 \u0437\u0430\u043f\u0438\u0441\u0438, \u043d\u0435 \u0432 \u0447\u0442\u0435\u043d\u0438\u0438. \u041f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043b\u0438 \u043d\u0430 set-diff + <code>bulk_insert_models<\/code> \u2014 \u043d\u0438\u0436\u0435 \u0446\u0438\u0444\u0440\u044b \u0441 \u0442\u0438\u043f\u0438\u0447\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 PostgreSQL; \u0443 \u0432\u0430\u0441 \u0431\u0443\u0434\u0443\u0442 \u0434\u0440\u0443\u0433\u0438\u0435 \u0441\u0435\u043a\u0443\u043d\u0434\u044b, \u043d\u043e \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u044f \u043f\u043e\u0445\u043e\u0436\u0438\u0439. \u041d\u0430 <code>LicenseRecord<\/code> \/ <code>ProjectDependency<\/code> \u0432 \u0441\u0445\u0435\u043c\u0435 \u0443\u0436\u0435 \u0441\u0442\u043e\u044f\u0442 partial unique \u043d\u0430 \u00ab\u0442\u0435\u043a\u0443\u0449\u0438\u0435\u00bb \u0441\u0442\u0440\u043e\u043a\u0438 (\u0441\u043c. \u0432\u044b\u0448\u0435), \u0431\u0435\u0437 \u043d\u0438\u0445 \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u0435 bulk \u043d\u0435 \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u0442 \u0440\u0438\u0441\u043a \u0434\u0443\u0431\u043b\u0435\u0439.<\/p>\n<h4>\u0411\u044b\u043b\u043e: get_or_create \u0432 \u0446\u0438\u043a\u043b\u0435<\/h4>\n<pre><code class=\"python\"># ~4.84 \u0441for item in items:    for license_ in item.licenses:        record, _ = LicenseRecord.objects.get_or_create(            project=project,            dependency=item.dependency,            license=license_,            end_date=None,            defaults={                'found_at': timezone.now(),                'start_date': start_date,            },        )<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u0436\u0434\u0430\u044f \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u044f \u2014 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 SELECT + \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0439 INSERT.<\/p>\n<h4>\u0421\u0442\u0430\u043b\u043e: bulk + \u0440\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432<\/h4>\n<pre><code class=\"python\"># ~0.16 \u0441  (\u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e x30)existing = {    (row.dependency_id, row.license_id): row    for row in LicenseRecord.objects.filter(project=project, end_date=None)}actual = {    (item.dependency_id, license_.id): LicenseRecord(        project=project,        dependency=item.dependency,        license=license_,        end_date=None,        found_at=timezone.now(),        start_date=analysis_time,    )    for item in items    for license_ in item.licenses}to_create = actual.keys() - existing.keys()if to_create:    bulk_insert_models(tuple(actual[key] for key in to_create))# \u0432\u0435\u0442\u043a\u0430 update \u2014 \u043a\u0430\u043a \u0432 \u043e\u0431\u0449\u0435\u043c \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0435 \u0432\u044b\u0448\u0435:# \u043f\u0435\u0440\u0435\u0441\u0435\u0447\u0435\u043d\u0438\u0435 \u043a\u043b\u044e\u0447\u0435\u0439 + update_if_fields_changed + bulk_update# (\u0434\u043b\u044f \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0439 \u0432 \u044d\u0442\u043e\u043c \u043a\u0435\u0439\u0441\u0435 \u043f\u043e\u043b\u044f \u043d\u0435 \u043c\u0435\u043d\u044f\u043b\u0438\u0441\u044c \u2014 \u0442\u043e\u043b\u044c\u043a\u043e create\/close)to_close = existing.keys() - actual.keys()if to_close:    LicenseRecord.objects.filter(        id__in=tuple(existing[key].id for key in to_close)    ).update(end_date=analysis_time)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421 ~4.8 \u0441 \u0434\u043e ~0.16 \u0441 \u043d\u0430 \u0442\u0438\u043f\u0438\u0447\u043d\u043e\u043c \u0441\u043a\u0430\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432 \u043d\u0430\u0448\u0435\u043c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0438. \u0422\u043e\u0442 \u0436\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0434\u0440\u0443\u0433\u043e\u0439 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c.<\/p>\n<h4>\u0415\u0449\u0451 \u0431\u043e\u043b\u044c\u043d\u0435\u0435: get_or_create + save + M2M<\/h4>\n<pre><code class=\"python\"># ~9.33 \u0441for item in items:    dep, created = ProjectDependency.objects.get_or_create(        project=project,        dependency=item.dependency,        relation=item.relation,        end_date=None,        defaults={            'start_date': start_date,            'vulnerabilities_count': item.vuln_count,            **extra_fields,        },    )    if not created:        update_fields = {            field: value            for field, value in extra_fields.items()            if getattr(dep, field) != value        }        if update_fields:            for field, value in update_fields.items():                setattr(dep, field, value)            dep.save(update_fields=tuple(update_fields.keys()))    if item.overridden_licenses:        dep.overridden_licenses.set(item.overridden_licenses)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0443\u0436\u0435 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e N \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 create, \u0430 \u0435\u0449\u0451 \u0443\u0441\u043b\u043e\u0432\u043d\u044b\u0435 save \u0438 <code>.set()<\/code> \u043d\u0430 M2M. \u041d\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u044d\u0442\u043e \u043b\u0435\u0433\u043a\u043e \u0443\u0435\u0437\u0436\u0430\u0435\u0442 \u0432 \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u0441\u0435\u043a\u0443\u043d\u0434.<\/p>\n<p>\u0421\u0445\u0435\u043c\u0430 \u00ab\u043f\u043e\u0441\u043b\u0435\u00bb \u0442\u0430 \u0436\u0435, \u0447\u0442\u043e \u0432\u044b\u0448\u0435, \u043f\u043b\u044e\u0441 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 through-\u0442\u0430\u0431\u043b\u0438\u0446\u0435\u0439:<\/p>\n<pre><code class=\"python\"># 1-3. create \/ update \/ close \u0434\u043b\u044f ProjectDependency \u2014 \u043a\u0430\u043a \u0432 set-diff \u0432\u044b\u0448\u0435# 4. M2M: \u043d\u0435 .set() \u0432 \u0446\u0438\u043a\u043b\u0435, \u0430 bulk \u043f\u043e \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435OverriddenLicense.objects.filter(    project_dependency_id__in=deps_with_new_licenses,).delete()bulk_insert_models([    OverriddenLicense(project_dependency_id=dep_id, license_id=license_id)    for dep_id, license_ids in new_m2m.items()    for license_id in license_ids], ignore_conflicts=True)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0418\u0442\u043e\u0433\u043e \u0441\u043d\u043e\u0432\u0430 3\u20135 \u043a\u0440\u0443\u043f\u043d\u044b\u0445 SQL \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u044b\u0441\u044f\u0447 \u043c\u0435\u043b\u043a\u0438\u0445. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u0440\u0435\u043c\u044f \u0443\u043f\u0430\u043b\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u043d\u0430 \u043f\u043e\u0440\u044f\u0434\u043e\u043a.<\/p>\n<p><a class=\"anchor\" name=\"atomic-get-or-create\" id=\"atomic-get-or-create\"><\/a><\/p>\n<h3>\u0421\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u044b\u0435 atomic_get_or_create \u0438 atomic_update_or_create<\/h3>\n<blockquote>\n<p>\u0412\u0430\u0436\u043d\u043e: <code>atomic_get_or_create<\/code> \/ <code>atomic_update_or_create<\/code> \u2014 <strong>\u043d\u0435<\/strong> \u043c\u0435\u0442\u043e\u0434\u044b Django ORM \u0438 \u043d\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430. \u0418\u0445 \u043d\u0435\u0442 \u0432 \u043a\u043e\u0440\u043e\u0431\u043a\u0435. \u041d\u0438\u0436\u0435 \u2014 \u0438\u0434\u0435\u044f \u0438 API, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0441\u043e\u0431\u0440\u0430\u043b\u0438 \u0443 \u0441\u0435\u0431\u044f \u043f\u043e\u0432\u0435\u0440\u0445 raw <code>INSERT ... ON CONFLICT ... RETURNING<\/code>. \u0425\u043e\u0442\u0438\u0442\u0435 \u0442\u0430\u043a \u0436\u0435 \u2014 \u043f\u0438\u0448\u0435\u0442\u0435 \u0445\u0435\u043b\u043f\u0435\u0440 \u0441\u0430\u043c\u0438 (\u0438\u043b\u0438 \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u0442\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d). \u0420\u0430\u0437\u0434\u0435\u043b \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e <strong>PostgreSQL<\/strong>; \u043d\u0430 MySQL \/ SQLite <code>xmax<\/code> \u0438 \u044d\u0442\u043e\u0442 \u0442\u0440\u044e\u043a \u043d\u0435 \u0437\u0430\u0432\u0435\u0434\u0443\u0442\u0441\u044f. \u0415\u0441\u043b\u0438 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u0432 \u043d\u0430 \u043e\u0434\u0438\u043d unique \u043d\u0435\u0442 \u2014 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e <code>get_or_create<\/code> \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e, \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043b\u0438\u0441\u0442\u0430\u0442\u044c.<\/p>\n<\/blockquote>\n<p>\u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0443\u0442\u0430\u0442\u044c \u0441 ORM: \u0443 Django <code>get_or_create<\/code> \u043d\u0435 \u0442\u0440\u043e\u0433\u0430\u0435\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0447\u0435\u0440\u0435\u0437 <code>defaults<\/code>; <code>update_or_create<\/code> \u2014 \u0442\u0440\u043e\u0433\u0430\u0435\u0442 (\u0441 5.0 \u0435\u0449\u0451 <code>create_defaults<\/code> \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 insert). \u041d\u0430\u0448\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0442 \u044d\u0442\u0443 \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0443 \u0441\u043d\u0430\u0440\u0443\u0436\u0438, \u043d\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u2014 \u043e\u0434\u0438\u043d UPSERT, \u0430 \u043d\u0435 SELECT + INSERT + retry.<\/p>\n<h4>\u0412 \u0447\u0451\u043c \u0440\u0435\u0430\u043b\u044c\u043d\u0430\u044f \u0431\u043e\u043b\u044c get_or_create<\/h4>\n<p>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f Django \u0447\u0435\u0441\u0442\u043d\u0430: \u043c\u0435\u0442\u043e\u0434 \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0439, <strong>\u0435\u0441\u043b\u0438<\/strong> \u043d\u0430 \u043f\u043e\u0438\u0441\u043a\u0435 \u0441\u0442\u043e\u0438\u0442 unique constraint. \u041f\u0440\u0438 \u0433\u043e\u043d\u043a\u0435 \u0434\u0432\u0443\u0445 INSERT \u043e\u0434\u0438\u043d \u043f\u043e\u043b\u0443\u0447\u0438\u0442 <code>IntegrityError<\/code>, Django \u043f\u043e\u0439\u043c\u0430\u0435\u0442 \u0435\u0433\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u0438 \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 <code>get()<\/code>. \u041f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 unique constraint \u043d\u0430\u0440\u0443\u0436\u0443 \u043e\u0448\u0438\u0431\u043a\u0430 \u043e\u0431\u044b\u0447\u043d\u043e \u043d\u0435 \u0432\u044b\u043b\u0435\u0442\u0430\u0435\u0442.<\/p>\n<p>Django \u043d\u0435 \u00ab\u0432\u0440\u0451\u0442\u00bb \u2014 \u043f\u0440\u043e\u0441\u0442\u043e miss-\u043f\u0443\u0442\u044c \u044d\u0442\u043e SELECT + INSERT, \u043f\u043e\u0434 \u0433\u043e\u043d\u043a\u043e\u0439 \u0435\u0449\u0451 retry \u043f\u043e <code>IntegrityError<\/code>, \u0430 \u0432 APM \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043a\u0430\u043a \u043c\u0443\u0441\u043e\u0440. \u0414\u0443\u0431\u043b\u0438 \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 unique \u0435\u0441\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0433\u043e\u043b\u043e\u0432\u0435, \u0430 \u0432 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u2014 \u043d\u0435\u0442.<\/p>\n<p><code>update_or_create<\/code> \u0438\u0434\u0451\u0442 \u0447\u0435\u0440\u0435\u0437 <code>select_for_update().get_or_create()<\/code>: \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u043e\u043d \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u0435\u0435, \u043d\u043e \u043d\u0430 \u043f\u0443\u0442\u0438 \u00ab\u0441\u0442\u0440\u043e\u043a\u0438 \u0435\u0449\u0451 \u043d\u0435\u0442\u00bb INSERT-\u0433\u043e\u043d\u043a\u0430 \u043d\u0438\u043a\u0443\u0434\u0430 \u043d\u0435 \u0434\u0435\u043b\u0430\u0441\u044c. \u041f\u043b\u044e\u0441 \u043b\u0438\u0448\u043d\u0438\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438. \u0421 Django 5.0 \u0443 \u043c\u0435\u0442\u043e\u0434\u0430 \u0442\u0430\u043a\u0436\u0435 \u0435\u0441\u0442\u044c <code>create_defaults<\/code> \u2014 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043e\u0442 <code>defaults<\/code> \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f.<\/p>\n<h4>\u0418\u0434\u0435\u044f: \u043e\u0434\u0438\u043d UPSERT + RETURNING<\/h4>\n<p>PostgreSQL \u0443\u043c\u0435\u0435\u0442 \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u043e:<\/p>\n<pre><code class=\"sql\">INSERT INTO dependency (name, language, version, purl, homepage)VALUES ('requests', 'python', '2.32.0', 'pkg:pypi\/requests@2.32.0', 'https:\/\/...')ON CONFLICT (name, language, version)DO UPDATE SET id = dependency.id  -- no-op, \u0447\u0442\u043e\u0431\u044b \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u043b RETURNINGRETURNING *, (xmax = 0) AS created;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0430\u043a \u0437\u0430 \u043e\u0434\u0438\u043d round-trip \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438 \u0441\u0442\u0440\u043e\u043a\u0443, \u0438 \u0444\u043b\u0430\u0433 <code>created<\/code>.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u043e xmax = 0, \u043c\u0451\u0440\u0442\u0432\u044b\u0435 \u043a\u043e\u0440\u0442\u0435\u0436\u0438 \u0438 bloat<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041f\u0430\u0442\u0442\u0435\u0440\u043d \u0441 <code>(xmax = 0)<\/code> \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0451\u043d \u0432 PostgreSQL: \u0443 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0432\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 <code>xmax = 0<\/code>, \u0443 \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u043e\u0439 \u2014 \u043d\u0435\u0442. \u041d\u043e \u044d\u0442\u043e <strong>undocumented implementation detail<\/strong>, \u043d\u0435 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 SQL \u0438 \u043d\u0435 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 API Postgres. \u041d\u0430 \u0434\u0440\u0443\u0433\u0438\u0445 \u0411\u0414 \u0442\u0430\u043a \u043d\u0435 \u0437\u0430\u0432\u0435\u0434\u0451\u0442\u0441\u044f; \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043c\u043e\u0433\u0443\u0442 \u043f\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u0442\u044c.<\/p>\n<p>\u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u043f\u043e\u0431\u043e\u0447\u043d\u044b\u0439 \u044d\u0444\u0444\u0435\u043a\u0442 \u0432\u0430\u0436\u043d\u0435\u0435 \u0442\u0440\u044e\u043a\u0430 \u0441 <code>xmax<\/code>: \u043b\u044e\u0431\u043e\u0439 <code>ON CONFLICT DO UPDATE<\/code> \u2014 \u044d\u0442\u043e \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 UPDATE \u0432 MVCC.<\/p>\n<ul>\n<li>\n<p>\u0414\u043b\u044f <code>atomic_get_or_create<\/code> no-op <code>SET id = id<\/code> \u043d\u0443\u0436\u0435\u043d, \u0447\u0442\u043e\u0431\u044b \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 <code>RETURNING<\/code> \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 <code>xmax<\/code>. \u041d\u0430 \u043f\u0443\u0442\u0438 \u00ab\u0441\u0442\u0440\u043e\u043a\u0430 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c\u00bb Postgres \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0451\u0442 <strong>\u043d\u043e\u0432\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u043a\u043e\u0440\u0442\u0435\u0436\u0430<\/strong>, \u0430 \u0441\u0442\u0430\u0440\u0443\u044e \u043f\u043e\u043c\u0435\u0447\u0430\u0435\u0442 \u043c\u0451\u0440\u0442\u0432\u043e\u0439. \u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u043b\u043e\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f <code>atomic_update_or_create<\/code> \/ Django <code>update_conflicts<\/code> \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435: <code>DO UPDATE SET col = EXCLUDED.col<\/code> \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 <strong>\u0434\u0430\u0436\u0435 \u043a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0432\u043f\u0430\u043b\u0438<\/strong>. \u0411\u0435\u0437 <code>WHERE ... IS DISTINCT FROM ...<\/code> \u00ab\u043f\u0443\u0441\u0442\u044b\u0435\u00bb \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0436\u0435 \u043f\u043b\u043e\u0434\u044f\u0442 dead tuples.<\/p>\n<\/li>\n<li>\n<p>\u0414\u0430\u043b\u044c\u0448\u0435 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u0442\u043e\u043b\u044c\u043a\u043e <code>VACUUM<\/code> \/ autovacuum. HOT-\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u043e\u0433\u0443\u0442 \u0441\u043c\u044f\u0433\u0447\u0438\u0442\u044c bloat \u0438\u043d\u0434\u0435\u043a\u0441\u043e\u0432, \u0435\u0441\u043b\u0438 \u043d\u0435 \u0442\u0440\u043e\u0433\u0430\u043b\u0438 indexed-\u043a\u043e\u043b\u043e\u043d\u043a\u0438, \u043d\u043e \u043a\u0443\u0447\u0430 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0440\u0430\u0441\u0442\u0451\u0442, \u043f\u043e\u043a\u0430 vacuum \u043d\u0435 \u043f\u0440\u0438\u0431\u0435\u0440\u0451\u0442.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043b\u044e\u0441 UPDATE-\u0442\u0440\u0438\u0433\u0433\u0435\u0440\u044b \u0438 WAL: \u043e\u043d\u0438 \u0442\u043e\u0436\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0430 \u044d\u0442\u043e\u043c \u00ab\u043f\u0443\u0441\u0442\u043e\u043c\u00bb \u043f\u0443\u0442\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u0418\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u044d\u0442\u043e\u043c\u0443 django-bulk-load \u043d\u0430 \u043c\u0430\u0441\u0441\u043e\u0432\u043e\u043c upsert \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u0435\u0435: \u0442\u0430\u043c <code>UPDATE ... WHERE IS DISTINCT FROM<\/code>. \u0410\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0439 \u043e\u0434\u0438\u043d\u043e\u0447\u043d\u044b\u0439 UPSERT \u0441\u043e\u0437\u043d\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0436\u0435\u0440\u0442\u0432\u0443\u0435\u0442 \u0447\u0438\u0441\u0442\u043e\u0442\u043e\u0439 \u043a\u0443\u0447\u0438 \u0440\u0430\u0434\u0438 \u043e\u0434\u043d\u043e\u0433\u043e round-trip \u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u0433\u043e\u043d\u043a\u0438.<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u0431\u043e\u043b\u044c\u043d\u043e: \u0433\u043e\u0440\u044f\u0447\u0438\u0439 \u043a\u043b\u044e\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0447\u0438\u0442\u0430\u044e\u0442\/\u00ab\u0441\u043e\u0437\u0434\u0430\u044e\u0442\u00bb \u0442\u044b\u0441\u044f\u0447\u0438 \u0440\u0430\u0437 \u0432 \u043c\u0438\u043d\u0443\u0442\u0443, \u0430 \u0441\u0442\u0440\u043e\u043a\u0430 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442. \u0422\u043e\u0433\u0434\u0430 <code>get_or_create<\/code> \u0441 retry \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0434\u0430\u0436\u0435 \u043c\u044f\u0433\u0447\u0435 \u043f\u043e bloat (\u043d\u0430 hit-\u043f\u0443\u0442\u0438 \u0447\u0430\u0441\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e SELECT), \u0430 UPSERT \u0441 no-op UPDATE \u0431\u0443\u0434\u0435\u0442 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e \u043c\u0443\u0441\u043e\u0440\u0438\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443. \u0418\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b \u043b\u0438\u0431\u043e \u043a\u044d\u0448 \u00ab\u043a\u043b\u044e\u0447 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c\u00bb, \u043b\u0438\u0431\u043e <code>ON CONFLICT DO UPDATE ... WHERE col IS DISTINCT FROM EXCLUDED.col<\/code> \u0434\u043b\u044f update-\u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0438, \u043b\u0438\u0431\u043e \u043e\u0431\u044b\u0447\u043d\u044b\u0439 SELECT first \u0442\u0430\u043c, \u0433\u0434\u0435 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0446\u0438\u044f \u0440\u0435\u0434\u043a\u0430.<\/p>\n<\/div>\n<\/details>\n<h4>\u0421\u0432\u043e\u0439 atomic_get_or_create<\/h4>\n<p>\u042d\u0442\u043e \u043d\u0435 <code>Model.objects.\u2026<\/code>, \u0430 \u043e\u0431\u044b\u0447\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0443 \u0432\u0430\u0441 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435. \u0421\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430 \u0431\u043b\u0438\u0437\u043a\u0430 \u043a <code>get_or_create<\/code>, \u043d\u043e \u043e\u0434\u043d\u0438\u043c UPSERT: \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u0434\u043b\u044f INSERT, \u043f\u043e\u0434\u043e\u0431\u0440\u0430\u0442\u044c conflict target (\u0432\u043a\u043b\u044e\u0447\u0430\u044f partial unique), \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c <code>INSERT ... ON CONFLICT DO UPDATE SET pk = pk RETURNING *, (xmax = 0) AS created<\/code>, \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c \u0438\u0437 \u0441\u0442\u0440\u043e\u043a\u0438. \u041f\u043e\u043b\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u043b\u0438\u043d\u043d\u0430\u044f \u0438\u0437\u2011\u0437\u0430 partial index \u0438 \u0440\u0430\u0437\u0431\u043e\u0440\u0430 <code>RETURNING<\/code> \u2014 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 \u0438 SQL; \u043a\u043e\u0434 \u043f\u0438\u0448\u0435\u0442\u0435 \u043f\u043e\u0434 \u0441\u0432\u043e\u044e \u0441\u0445\u0435\u043c\u0443.<\/p>\n<p>\u041a\u0430\u043a \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043d\u0430\u0440\u0443\u0436\u0438, \u043a\u043e\u0433\u0434\u0430 \u0445\u0435\u043b\u043f\u0435\u0440 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c:<\/p>\n<pre><code class=\"python\">dependency, created = atomic_get_or_create(    Dependency,    name=package.name,    language=package.language,    version=package.version,    purl=package.purl,    defaults={        'homepage': 'https:\/\/example.com',    },)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Conflict target \u0437\u0434\u0435\u0441\u044c \u2014 <code>(name, language, version)<\/code>, \u0430 \u043d\u0435 \u0432\u0441\u0435 kwargs \u043f\u043e\u0434\u0440\u044f\u0434: <code>purl<\/code> \u0443\u0445\u043e\u0434\u0438\u0442 \u0432 INSERT, \u043d\u043e \u043d\u0435 \u0432 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430. \u042d\u0442\u043e \u0441\u043e\u0437\u043d\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 Django <code>get_or_create<\/code>, \u0433\u0434\u0435 \u043b\u044e\u0431\u043e\u0439 kwarg \u043a\u0440\u043e\u043c\u0435 <code>defaults<\/code> \u2014 \u0447\u0430\u0441\u0442\u044c lookup. \u0415\u0441\u043b\u0438 \u0441\u0442\u0440\u043e\u043a\u0430 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u2014 \u0432\u0435\u0440\u043d\u0451\u0442\u0441\u044f \u043e\u043d\u0430, <code>defaults<\/code> <strong>\u043d\u0435<\/strong> \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0448\u0443\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043f\u043e\u043b\u044f (\u043a\u0430\u043a \u0432 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u043c <code>get_or_create<\/code>).<\/p>\n<h4>\u0421\u0432\u043e\u0439 atomic_update_or_create<\/h4>\n<p>\u0422\u0430 \u0436\u0435 \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u0430\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u0430, \u043d\u043e \u0432 \u0432\u0435\u0442\u043a\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u043b\u044f \u0438\u0437 <code>defaults<\/code>. \u0415\u0441\u043b\u0438 <code>defaults<\/code> \u043f\u0443\u0441\u0442\u043e\u0439 \u2014 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0442\u043a\u0430 \u0431\u0435\u0437 UPDATE, \u043a\u0430\u043a get_or_create.<\/p>\n<pre><code class=\"python\">dependency, created = atomic_update_or_create(    Dependency,    name=package.name,    language=package.language,    version=package.version,    purl=package.purl,    defaults={        'homepage': info.homepage,        'description': info.description,    },)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>SQL \u0443\u0436\u0435 \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u043c:<\/p>\n<pre><code class=\"sql\">INSERT INTO dependency (...)VALUES (...)ON CONFLICT (name, language, version)DO UPDATE SET    homepage = EXCLUDED.homepage,    description = EXCLUDED.descriptionRETURNING *, (xmax = 0) AS created;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0442\u0435 \u0436\u0435, \u044d\u0442\u043e\u0442 UPDATE \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u043d\u043e\u0432\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0441\u0442\u0440\u043e\u043a\u0438 (\u0441\u043c. \u0432\u044b\u0448\u0435 \u043f\u0440\u043e dead tuples). \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u043b\u043e\u0434\u0438\u0442\u044c \u0438\u0445 \u0437\u0440\u044f, \u043c\u043e\u0436\u043d\u043e \u0441\u0443\u0437\u0438\u0442\u044c \u0430\u043f\u0434\u0435\u0439\u0442 \u0447\u0435\u0440\u0435\u0437 <code>WHERE ... IS DISTINCT FROM ...<\/code> \u2014 \u043d\u043e \u0442\u043e\u0433\u0434\u0430 \u043f\u0440\u0438 \u00ab\u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c\u00bb Postgres <strong>\u043d\u0435 \u0432\u0435\u0440\u043d\u0451\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0432 <\/strong><code><strong>RETURNING<\/strong><\/code>. \u041d\u0443\u0436\u0435\u043d fallback <code>SELECT<\/code>, CTE, \u0438\u043b\u0438 no-op <code>DO UPDATE<\/code> \u0431\u0435\u0437 <code>WHERE<\/code>.<\/p>\n<p>\u0415\u0441\u043b\u0438 <code>defaults<\/code> \u043f\u0443\u0441\u0442\u043e\u0439 \u2014 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043a\u0430\u043a \u0443 get_or_create (\u0431\u0435\u0437 UPDATE \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u043e\u043b\u0435\u0439), \u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0442\u043a\u0443 \u043a\u043e\u0434\u0430, \u0430 \u043d\u0435 \u0447\u0435\u0440\u0435\u0437 no-op upsert.<\/p>\n<h4>\u0413\u043b\u0443\u0431\u0436\u0435: partial unique index \u0438 NULL<\/h4>\n<blockquote>\n<p>\u041c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c, \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0442 nullable-\u043f\u043e\u043b\u044f \u0432 unique \u0438 \u043d\u0435\u0442 \u0433\u043e\u043d\u043e\u043a \u043d\u0430 \u0442\u0430\u043a\u043e\u043c \u043a\u043b\u044e\u0447\u0435.<\/p>\n<\/blockquote>\n<p>\u0412 PostgreSQL \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0432 unique-\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0438 <code>NULL<\/code> \u043d\u0435 \u0440\u0430\u0432\u0435\u043d <code>NULL<\/code>: \u0434\u0432\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441 <code>(dependency_id=1, repository_id=NULL)<\/code> \u043e\u0431\u0435 \u043f\u0440\u043e\u0439\u0434\u0443\u0442 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 <code>UNIQUE (dependency_id, repository_id)<\/code>.<\/p>\n<p>\u042d\u0442\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f, \u043f\u043e\u043a\u0430 \u0432\u044b \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 <code>UNIQUE NULLS NOT DISTINCT<\/code> \u2014 \u043e\u043d \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0432 <strong>PostgreSQL 15<\/strong>. \u041d\u0430 \u0431\u043e\u043b\u0435\u0435 \u0441\u0442\u0430\u0440\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 unique \u0441 nullable-\u043f\u043e\u043b\u0435\u043c \u0434\u044b\u0440\u044f\u0432\u044b\u0439.<\/p>\n<p>\u041a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u2014 \u0434\u0432\u0430 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0445 \u0438\u043d\u0434\u0435\u043a\u0441\u0430:<\/p>\n<pre><code class=\"python\">class PackageScan(models.Model):    dependency = models.ForeignKey(Dependency, on_delete=models.CASCADE)    repository = models.ForeignKey(Repository, null=True, on_delete=models.CASCADE)    class Meta:        constraints = [            UniqueConstraint(                name='uniq_repo_not_null',                fields=['dependency', 'repository'],                condition=Q(repository__isnull=False),            ),            UniqueConstraint(                name='uniq_repo_is_null',                fields=['dependency'],                condition=Q(repository__isnull=True),            ),        ]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f <code>ON CONFLICT<\/code> \u043d\u0443\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043b\u043e\u043d\u043a\u0438, \u043d\u043e \u0438 <code>WHERE<\/code> \u0438\u043d\u0434\u0435\u043a\u0441\u0430:<\/p>\n<pre><code class=\"sql\">-- repository \u0437\u0430\u0434\u0430\u043dON CONFLICT (dependency_id, repository_id)WHERE repository_id IS NOT NULLDO UPDATE SET id = package_scan.id-- repository IS NULLON CONFLICT (dependency_id)WHERE repository_id IS NULLDO UPDATE SET id = package_scan.id<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u044b\u0435 \u0445\u0435\u043b\u043f\u0435\u0440\u044b \u0443\u043c\u0435\u044e\u0442 \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 partial constraint \u043f\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438. \u0411\u0435\u0437 \u044d\u0442\u043e\u0433\u043e <code>ON CONFLICT<\/code> \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u043f\u043e\u043f\u0430\u0434\u0451\u0442 \u0432 \u0438\u043d\u0434\u0435\u043a\u0441 \u2014 \u0438 \u0432\u044b \u0441\u043d\u043e\u0432\u0430 \u043b\u043e\u0432\u0438\u0442\u0435 \u0433\u043e\u043d\u043a\u0438.<\/p>\n<p>\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u043d\u0430 PostgreSQL 15+: \u043e\u0434\u0438\u043d constraint \u0441 <code>NULLS NOT DISTINCT<\/code> \u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 <code>ON CONFLICT<\/code>. \u041d\u043e \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c \u0441\u043e \u0441\u0442\u0430\u0440\u044b\u043c\u0438 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430\u043c\u0438 \u2014 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f; partial unique indexes \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u0440\u0430\u0431\u043e\u0447\u0438\u043c \u0438 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0438\u043c\u044b\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c.<\/p>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 \u043d\u0435 select_for_update<\/h4>\n<p><code>select_for_update<\/code> \u0437\u0430\u0449\u0438\u0449\u0430\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0441\u0442\u0440\u043e\u043a\u0438. \u041e\u043d \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0438 \u043d\u0435 \u0441\u043f\u0430\u0441\u0430\u0435\u0442 \u043e\u0442 \u0434\u0432\u0443\u0445 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 INSERT. UPSERT \u0440\u0435\u0448\u0430\u0435\u0442 \u043e\u0431\u0435 \u0432\u0435\u0442\u043a\u0438 \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439, \u0431\u0435\u0437 \u043b\u0438\u0448\u043d\u0438\u0445 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043e\u043a \u00ab\u043d\u0430 \u0432\u0441\u044f\u043a\u0438\u0439 \u0441\u043b\u0443\u0447\u0430\u0439\u00bb.<\/p>\n<p>\u041d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u043f\u043e\u0434 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u044c\u044e \u043e\u0434\u043d\u043e\u0433\u043e \u0438 \u0442\u043e\u0433\u043e \u0436\u0435 \u043a\u043b\u044e\u0447\u0430 (20 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e) \u0442\u0430\u043a\u0438\u0435 UPSERT-\u0445\u0435\u043b\u043f\u0435\u0440\u044b \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u044e\u0442 \u0440\u043e\u0432\u043d\u043e \u043e\u0434\u043d\u0443 \u0441\u0442\u0440\u043e\u043a\u0443 \u0438 \u0440\u043e\u0432\u043d\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 <code>created=True<\/code>. \u042d\u0442\u043e \u0441\u0442\u043e\u0438\u0442 \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u0442\u044c <code>TransactionTestCase<\/code> \u0441 \u0431\u0430\u0440\u044c\u0435\u0440\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u2014 \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u043e\u0442 \u0442\u0435\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043b\u043e\u0432\u0438\u0442 \u0433\u043e\u043d\u043a\u0438, \u0430 \u043d\u0435 \u00ab\u0437\u0435\u043b\u0451\u043d\u044b\u0439 \u043d\u0430 \u043e\u0434\u043d\u043e\u043c \u0432\u043e\u0440\u043a\u0435\u0440\u0435\u00bb.<\/p>\n<p><a class=\"anchor\" name=\"when-to-use\" id=\"when-to-use\"><\/a><\/p>\n<h3>\u041a\u043e\u0433\u0434\u0430 \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c<\/h3>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041c\u0430\u0441\u0441\u043e\u0432\u0430\u044f \u0432\u0441\u0442\u0430\u0432\u043a\u0430 \u0431\u0435\u0437 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>bulk_create<\/code> \/ <code>bulk_insert_models<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041c\u0430\u0441\u0441\u043e\u0432\u044b\u0439 upsert \u043f\u043b\u043e\u0441\u043a\u043e\u0433\u043e \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0430, \u043e\u0434\u0438\u043d \u043f\u0438\u0441\u0430\u0442\u0435\u043b\u044c<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>bulk_upsert_models<\/code> (\u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u043d\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0431\u0430\u0442\u0447\u0430\u0445)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041c\u0430\u0441\u0441\u043e\u0432\u044b\u0439 upsert \u043f\u0440\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0438\u0441\u0430\u0442\u0435\u043b\u044f\u0445<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>bulk_create(update_conflicts=...)<\/code> \/ \u044f\u0432\u043d\u044b\u0439 <code>ON CONFLICT<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f (create\/update\/close)<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0440\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432 + bulk + \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u0433\u043e\u043d\u043e\u043a<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041e\u0434\u0438\u043d\u043e\u0447\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u0434 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0446\u0438\u0435\u0439<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0441\u0432\u043e\u0439 <code>atomic_get_or_create<\/code> \/ <code>atomic_update_or_create<\/code> (UPSERT)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041e\u0434\u0438\u043d\u043e\u0447\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0431\u0435\u0437 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0446\u0438\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u043e\u0431\u044b\u0447\u043d\u044b\u0439 Django <code>get_or_create<\/code> \/ <code>update_or_create<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u041d\u0435 \u043d\u0430\u0434\u043e \u0432\u0435\u0437\u0434\u0435 \u0442\u0430\u0449\u0438\u0442\u044c \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u044b\u0439 UPSERT. \u041d\u043e \u0442\u0430\u043c, \u0433\u0434\u0435 \u0434\u0432\u0430 \u0432\u043e\u0440\u043a\u0435\u0440\u0430 \u0431\u044c\u044e\u0442\u0441\u044f \u0437\u0430 \u043e\u0434\u0438\u043d \u043a\u043b\u044e\u0447, \u043e\u0431\u044b\u0447\u043d\u044b\u0439 ORM API \u0434\u043e\u0440\u043e\u0433\u043e\u0439 \u0438 \u0448\u0443\u043c\u043d\u044b\u0439 \u2014 \u0434\u0430\u0436\u0435 \u043a\u043e\u0433\u0434\u0430 \u0444\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u0435\u043d.<\/p>\n<p><a class=\"anchor\" name=\"conclusion\" id=\"conclusion\"><\/a><\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u041d\u0435 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0439\u0442\u0435 \u0437\u0430\u0440\u0430\u043d\u0435\u0435. \u0421\u043e\u0442\u043d\u044f \u0441\u0442\u0440\u043e\u043a \u0432 \u0446\u0438\u043a\u043b\u0435 \u0441 <code>save()<\/code> \u2014 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e. \u0422\u044b\u0441\u044f\u0447\u0438 \u043e\u0434\u043d\u043e\u0442\u0438\u043f\u043d\u044b\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u2014 bulk, \u043d\u0430 PostgreSQL \u0438 \u0436\u0438\u0440\u043d\u044b\u0445 \u043e\u0431\u044a\u0451\u043c\u0430\u0445 \u0438\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b COPY. \u041d\u0443\u0436\u043d\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u00ab\u0441\u043d\u0438\u043c\u043e\u043a\u00bb \u0441 create\/update\/close \u2014 \u0440\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432, \u043d\u0435 \u0441\u043b\u0435\u043f\u043e\u0439 upsert. \u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u0432 \u0431\u044c\u044e\u0442\u0441\u044f \u0437\u0430 \u043e\u0434\u0438\u043d \u043a\u043b\u044e\u0447 \u2014 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 <code>ON CONFLICT<\/code>, \u0430 \u043d\u0435 \u043d\u0430\u0434\u0435\u0436\u0434\u0430, \u0447\u0442\u043e retry \u0441\u043f\u0430\u0441\u0451\u0442.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 merge \u044f \u043e\u0431\u044b\u0447\u043d\u043e \u0441\u043c\u043e\u0442\u0440\u044e \u0442\u0440\u0438 \u0432\u0435\u0449\u0438: \u043d\u0435 \u043f\u043e\u0432\u0435\u0441\u0438\u043b\u0438 \u043b\u0438 \u043d\u0430 bulk \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0441\u0438\u0433\u043d\u0430\u043b\u043e\u0432 (\u0438 \u043d\u0435 \u0437\u0430\u0431\u044b\u043b\u0438 \u043b\u0438 \u043f\u0440\u043e <code>auto_now<\/code> \u043d\u0430 <code>bulk_update<\/code> \/ COPY); \u043d\u0443\u0436\u043d\u044b \u043b\u0438 PK \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 insert \u0438 \u043a\u0430\u043a\u0438\u0435 \u0444\u043b\u0430\u0433\u0438 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432; \u0438 \u0435\u0441\u0442\u044c \u043b\u0438 lock \u0438\u043b\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f, \u0435\u0441\u043b\u0438 sync \u043d\u0435 \u043e\u0434\u0438\u043d \u043f\u043e\u0442\u043e\u043a.<\/p>\n<p>Django <code>ON CONFLICT<\/code> \u0438 upsert \u0432 django-bulk-load \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e, \u043d\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u0440\u0430\u0437\u043d\u044b\u0435 \u0437\u0432\u0435\u0440\u0438. \u0421\u0442\u043e\u0438\u0442 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 SQL \u0438 \u0446\u0435\u043d\u0443 round-trip \u2014 \u0438 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u044f\u0441\u043d\u043e, \u043a\u0430\u043a\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0431\u0440\u0430\u0442\u044c. \u0411\u0435\u0437 \u043c\u0430\u0433\u0438\u0438 \u0438 \u0431\u0435\u0437 \u0441\u044e\u0440\u043f\u0440\u0438\u0437\u043e\u0432.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1088326\/\">https:\/\/habr.com\/ru\/articles\/1088326\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0412\u044b \u043a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u0432 \u043b\u043e\u0433\u0438 \u0438 \u0432\u0438\u0434\u0435\u043b\u0438 \u0442\u044b\u0441\u044f\u0447\u0443 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0445 INSERT \u043f\u043e\u0434\u0440\u044f\u0434? \u0418\u043b\u0438 \u0437\u0430\u043c\u0435\u0447\u0430\u043b\u0438, \u0447\u0442\u043e \u043f\u043e\u0434 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439 get_or_create \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u0442\u044c \u0438\u0437\u2011\u0437\u0430 \u043b\u0438\u0448\u043d\u0438\u0445 round-trip \u0438 retry?\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u2014 \u043f\u0440\u043e \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c\u0438 \u043e\u0431\u044a\u0451\u043c\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 Django ORM. \u041e\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0445 bulk_create \/ bulk_update \u0434\u043e PostgreSQL COPY, \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0439 \u0441 \u043f\u0440\u043e\u0434\u0430 \u0438 \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u044b\u0445 \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0445 \u0430\u043d\u0430\u043b\u043e\u0433\u043e\u0432 get_or_create \/ update_or_create \u043d\u0430 \u043e\u0434\u043d\u043e\u043c UPSERT.\u042f \u0432\u0435\u0434\u0443\u0449\u0438\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432 CodeScoring; \u043d\u0438\u0436\u0435 \u2014 \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u043b\u0438 \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043a\u0430\u043d\u0430\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u0430 \u043d\u0435 \u043f\u0435\u0440\u0435\u0441\u043a\u0430\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438. \u0415\u0441\u043b\u0438 \u0432\u044b \u0443\u0436\u0435 \u0437\u043d\u0430\u0435\u0442\u0435, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 bulk_create, \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0445 \u043a\u0430\u043c\u043d\u0435\u0439 \u0438\u043b\u0438 \u0441 \u043f\u0440\u043e\u0434\u043e\u0432\u044b\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432.\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435\u0417\u0430\u0447\u0435\u043c \u043d\u0443\u0436\u043d\u044b bulk-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 bulk_create \u0438 bulk_update\u041f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u0438 bulk\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430\u043c\u0438: ignore_conflicts \u0438 update_conflictsdjango-bulk-load \u0438 PostgreSQL COPY\u0411\u0435\u043d\u0447\u043c\u0430\u0440\u043a\u0438\u0420\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432: \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0431\u0435\u0437 upsert\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0441 \u043f\u0440\u043e\u0434\u0430\u0421\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u044b\u0435 atomic_get_or_create \/ update_or_create\u041a\u043e\u0433\u0434\u0430 \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u0417\u0430\u0447\u0435\u043c \u043d\u0443\u0436\u043d\u044b bulk-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043a\u0430\u0441\u0441\u0443 \u0432 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0435. \u0412\u044b \u043a\u0443\u043f\u0438\u043b\u0438 100 \u0442\u043e\u0432\u0430\u0440\u043e\u0432. \u041a\u0430\u0441\u0441\u0438\u0440 \u043c\u043e\u0436\u0435\u0442:\u041f\u0440\u043e\u0431\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0442\u043e\u0432\u0430\u0440 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0447\u0435\u043a\u043e\u043c \u2014 100 \u043f\u043e\u0445\u043e\u0434\u043e\u0432 \u043a \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0443\u041f\u0440\u043e\u0431\u0438\u0442\u044c \u0432\u0441\u0451 \u043e\u0434\u043d\u0438\u043c \u0447\u0435\u043a\u043e\u043c\u0412\u0442\u043e\u0440\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u043d\u0435 \u0438\u0437-\u0437\u0430 \u043c\u0430\u0433\u0438\u0438, \u0430 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432\u044b \u0443\u0431\u0440\u0430\u043b\u0438 \u043d\u0430\u043a\u043b\u0430\u0434\u043d\u044b\u0435 \u0440\u0430\u0441\u0445\u043e\u0434\u044b: \u0441\u0435\u0442\u0435\u0432\u043e\u0439 round-trip (\u0442\u0443\u0434\u0430-\u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0434\u043e \u0411\u0414), \u0440\u0430\u0437\u0431\u043e\u0440 SQL, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430.\u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0441 ORM:for i in range(1000):    Product.objects.create(name=f&#8217;Product {i}&#8217;, price=100 + i)\u042d\u0442\u043e \u0442\u044b\u0441\u044f\u0447\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 INSERT. \u041f\u0440\u0438 latency 1 \u043c\u0441 \u0432\u044b \u0443\u0436\u0435 \u043f\u043e\u0434\u0430\u0440\u0438\u043b\u0438 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0430 \u0441\u0435\u0442\u044c. \u0410 \u0435\u0449\u0451 \u0438\u043d\u0434\u0435\u043a\u0441\u044b, WAL, \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438. \u0412 \u0440\u0435\u0436\u0438\u043c\u0435 autocommit \u043a\u0430\u0436\u0434\u044b\u0439 create() \u0435\u0449\u0451 \u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f.Bulk-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u043a\u043b\u0435\u0438\u0432\u0430\u044e\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0432 \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u0440\u0443\u043f\u043d\u044b\u0445 SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u041d\u0430 \u0438\u043c\u043f\u043e\u0440\u0442\u0435, \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u0438 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0435 \u044d\u0442\u043e \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u0435 \u0432 \u0440\u0430\u0437\u044b, \u0430 \u0438\u043d\u043e\u0433\u0434\u0430 \u0432 \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u0440\u0430\u0437.Bulk \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u043e\u043b\u0435\u0437\u0435\u043d, \u043a\u043e\u0433\u0434\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043c\u043d\u043e\u0433\u043e, \u0430 \u043b\u043e\u0433\u0438\u043a\u0430 \u043d\u0430 \u043e\u0434\u0438\u043d \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u0440\u043e\u0441\u0442\u0430\u044f. \u0415\u0441\u043b\u0438 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u043d\u0443\u0436\u043d\u0430 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u0441 \u043a\u0443\u0447\u0435\u0439 \u0432\u0435\u0442\u0432\u043b\u0435\u043d\u0438\u0439 \u2014 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u043e\u0442\u043e\u043c \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u0443\u0439\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b.\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 bulk_create \u0438 bulk_update\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u041d\u0430\u0438\u0432\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434:for i in range(1000):    Product.objects.create(        name=f&#8217;Product {i}&#8217;,        price=100 + i,    )INSERT INTO products (name, price) VALUES (&#8216;Product 0&#8217;, 100);INSERT INTO products (name, price) VALUES (&#8216;Product 1&#8242;, 101);&#8212; &#8230; \u0435\u0449\u0451 998 \u0440\u0430\u0437Bulk:products = [    Product(name=f&#8217;Product {i}&#8217;, price=100 + i)    for i in range(1000)]Product.objects.bulk_create(products)INSERT INTO products (name, price)VALUES    (&#8216;Product 0&#8217;, 100),    (&#8216;Product 1&#8217;, 101),    &#8212; &#8230;    (&#8216;Product 999&#8217;, 1099);\u041e\u0434\u0438\u043d \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u044b\u0441\u044f\u0447\u0438.\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u041d\u0430\u0438\u0432\u043d\u043e:for product in Product.objects.all():    product.price = new_price(product.price)    product.save(update_fields=(&#8216;price&#8217;,))\u0427\u0435\u0440\u0435\u0437 bulk_update:products = list(Product.objects.all())for product in products:    product.price = new_price(product.price)Product.objects.bulk_update(products, fields=(&#8216;price&#8217;,))\u041f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c Django \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0447\u0442\u043e-\u0442\u043e \u0432\u0440\u043e\u0434\u0435:UPDATE productsSET price = CASE    WHEN id = 1 THEN 200    WHEN id = 2 THEN 202    &#8212; &#8230;ENDWHERE id IN (1, 2, &#8230;);\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u2014 \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 \u0432\u044b \u043d\u0435 \u0436\u0434\u0451\u0442\u0435 \u043e\u0442 bulk \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e save().\u041f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u0438 bulk\u0421\u0438\u0433\u043d\u0430\u043b\u044b \u043c\u043e\u043b\u0447\u0430\u0442bulk_create \u0438 bulk_update \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442 \u0441\u0438\u0433\u043d\u0430\u043b\u044b pre_save \/ post_save \u0438 \u043d\u0435 \u0437\u0430\u0445\u043e\u0434\u044f\u0442 \u0432 Model.save(). \u041d\u043e \u0441 auto_now \/ auto_now_add \u0432\u0430\u0436\u043d\u044b\u0439 \u043d\u044e\u0430\u043d\u0441: \u044d\u0442\u043e \u043d\u0435 \u043e\u0434\u043d\u043e \u0438 \u0442\u043e \u0436\u0435, \u0447\u0442\u043e \u0441\u0438\u0433\u043d\u0430\u043b\u044b.\u041f\u0440\u0438 bulk_create Django \u0432\u0441\u0451 \u0436\u0435 \u0437\u043e\u0432\u0451\u0442 Field.pre_save() \u043d\u0430 \u043f\u043e\u043b\u044f\u0445 \u2014 \u0438 auto_now \/ auto_now_add \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u044f\u0442\u0441\u044f. \u041f\u0440\u0438 bulk_update \u2014 \u043d\u0435\u0442: \u0432 SQL \u0443\u0445\u043e\u0434\u044f\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432, \u043f\u043e\u043b\u0435 \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0440\u0443\u043a\u0430\u043c\u0438 \u0438 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u0442\u044c \u0432 fields.class Product(models.Model):    name = models.CharField(max_length=200, unique=True)    price = models.IntegerField()    updated_at = models.DateTimeField(auto_now=True)# bulk_create: updated_at \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 Field.pre_saveProduct.objects.bulk_create(products)# bulk_update: updated_at \u0441\u0430\u043c \u041d\u0415 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0441\u044fProduct.objects.bulk_update(products, fields=(&#8216;price&#8217;,))# \u041d\u0443\u0436\u043d\u043e \u0440\u0443\u043a\u0430\u043c\u0438now = timezone.now()for product in products:    product.updated_at = nowProduct.objects.bulk_update(products, fields=(&#8216;price&#8217;, &#8216;updated_at&#8217;))\u0415\u0441\u043b\u0438 \u043d\u0430 \u0441\u0438\u0433\u043d\u0430\u043b post_save \u0432\u0438\u0441\u0438\u0442 \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043a\u044d\u0448\u0430 \u0438\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u2014 bulk \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0440\u043e\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u0442. \u041f\u043e\u0431\u043e\u0447\u043d\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0438\u043b\u0438 \u043e\u0441\u043e\u0437\u043d\u0430\u043d\u043d\u043e \u043e\u0442 \u043d\u0438\u0445 \u043e\u0442\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f.\u0422\u0440\u0438\u0433\u0433\u0435\u0440\u044b \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 PostgreSQL \u2014 \u0434\u0440\u0443\u0433\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f: \u043e\u043d\u0438 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 INSERT\/UPDATE \u0432 \u0446\u0435\u043b\u0435\u0432\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443. \u0410 \u0432\u043e\u0442 COPY \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0438\u0445 \u043e\u0431\u044b\u0447\u043d\u043e \u043d\u0435 \u043a\u0430\u0441\u0430\u0435\u0442\u0441\u044f. \u0423 django-bulk-load \u043d\u0435\u0442 \u043f\u0443\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 Field.pre_save, \u0442\u0430\u043a \u0447\u0442\u043e auto_now \u0442\u0430\u043c \u0442\u043e\u0436\u0435 \u043d\u0435 \u00ab\u043c\u0430\u0433\u0438\u044f\u00bb \u2014 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u043d\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u0445 \u0434\u043e \u0432\u044b\u0437\u043e\u0432\u0430.primary key \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430\u041a\u043e\u0440\u043e\u0442\u043a\u043e:\u0431\u0435\u0437 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432, \u043d\u0430 PostgreSQL \/ MariaDB 10.5+ \/ SQLite 3.35+, PK \u043f\u043e\u0441\u043b\u0435 bulk_create \u043e\u0431\u044b\u0447\u043d\u043e \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u0445;\u043d\u0430 MySQL \/ Oracle \u2014 \u0447\u0430\u0449\u0435 \u043d\u0435\u0442;\u0441 ignore_conflicts=True Django PK \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0434\u0430\u0436\u0435 \u043d\u0430 \u00ab\u0445\u043e\u0440\u043e\u0448\u0438\u0445\u00bb \u0411\u0414;\u0441 update_conflicts=True \u043d\u0430 PostgreSQL \/ MariaDB \/ SQLite PK \u043e\u0431\u044b\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f (RETURNING \u043d\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f), \u043d\u0430 MySQL \/ Oracle \u2014 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430 \u044d\u0442\u043e \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c.db_products = Product.objects.bulk_create(products)all(p.pk for p in db_products)  # True \u043d\u0430 PostgreSQL \/ MariaDB 10.5+ \/ SQLite 3.35+db_products = Product.objects.bulk_create(products, ignore_conflicts=True)any(p.pk for p in db_products)  # Falsedb_products = Product.objects.bulk_create(    products,    update_conflicts=True,    update_fields=(&#8216;price&#8217;,),    unique_fields=(&#8216;name&#8217;,),)all(p.pk for p in db_products)  # \u043e\u0431\u044b\u0447\u043d\u043e True \u043d\u0430 PostgreSQL\u0415\u0441\u043b\u0438 \u043f\u043e\u0441\u043b\u0435 \u0432\u0441\u0442\u0430\u0432\u043a\u0438 \u0441 ignore_conflicts \u043d\u0443\u0436\u043d\u044b id \u2014 \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 SELECT \u043f\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u043a\u043b\u044e\u0447\u0430\u043c.\u0414\u0435\u0442\u0430\u043b\u0438 \u0438\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 Django \u0438 \u043d\u044e\u0430\u043d\u0441\u044b \u043f\u043e \u0432\u0435\u0440\u0441\u0438\u044f\u043c\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f:If the model\u2019s primary key is an AutoField or has a db_default value, and ignore_conflicts is False, the primary key attribute can only be retrieved on certain databases (currently PostgreSQL, MariaDB, and SQLite 3.35+). On other databases, it will not be set.\u0423\u0441\u043b\u043e\u0432\u0438\u0435\u041f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435AutoField \/ db_default, \u0431\u0435\u0437 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432PK \u043d\u0430 PostgreSQL, MariaDB 10.5+, SQLite 3.35+MySQL, Oracle \u0438 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435PK \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044fignore_conflicts=TruePK \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f (RETURNING \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f)update_conflicts=True\u043d\u0430 PostgreSQL \/ MariaDB \/ SQLite PK \u043e\u0431\u044b\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f; \u043d\u0430 MySQL \/ Oracle \u2014 \u043d\u0435\u0442\u041f\u043e \u0432\u0435\u0440\u0441\u0438\u044f\u043c: RETURNING \u043d\u0430 PostgreSQL \u0435\u0441\u0442\u044c \u0434\u0430\u0432\u043d\u043e; \u0432 Django 4.2 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u0431\u044b\u043b\u0438 PostgreSQL, MariaDB 10.5+ \u0438 SQLite 3.35+; \u0441 Django 5.0 \u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u043a\u0430 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 db_default; update_conflicts \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0432 Django 4.1.\u0414\u0440\u0443\u0433\u0438\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f Django bulk\u0418\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0438:bulk_create \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 multi-table inheritance \u0438 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 M2M;bulk_update \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447;\u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b PK \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0431\u0430\u0442\u0447\u0430 bulk_update \u043f\u0440\u0438\u0432\u043e\u0434\u044f\u0442 \u043a \u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043c\u0435\u043d\u044c\u0448\u0435 \u0441\u0442\u0440\u043e\u043a, \u0447\u0435\u043c \u0432\u044b \u0436\u0434\u0451\u0442\u0435;\u0434\u0430\u0436\u0435 \u0441 batch_size Django \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0433\u043e\u0442\u043e\u0432\u0438\u0442 SQL\/WHEN \u0434\u043b\u044f \u0431\u0430\u0442\u0447\u0430 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u2014 \u043e\u0447\u0435\u043d\u044c \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0431\u0430\u0442\u0447 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0440\u0430\u0437\u0434\u0443\u0432\u0430\u0435\u0442 \u043a\u043b\u0438\u0435\u043d\u0442.\u0420\u0430\u0437\u043c\u0435\u0440 \u0431\u0430\u0442\u0447\u0430 \u0438\u043c\u0435\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043c\u0438\u043b\u043b\u0438\u043e\u043d \u0441\u0442\u0440\u043e\u043a \u043e\u0434\u043d\u0438\u043c INSERT \u0438\u043b\u0438 \u043e\u0434\u043d\u0438\u043c \u0433\u0438\u0433\u0430\u043d\u0442\u0441\u043a\u0438\u043c CASE WHEN \u2014 \u0438\u0434\u0435\u044f \u0441\u043f\u043e\u0440\u043d\u0430\u044f. \u0423\u043f\u0438\u0440\u0430\u0435\u0442\u0435\u0441\u044c \u0432 \u0440\u0430\u0437\u043c\u0435\u0440 statement, \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0434\u043b\u0438\u043d\u043d\u0443\u044e \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 batch_size \u0440\u0435\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430 \u043a\u0443\u0441\u043a\u0438: \u043d\u0430\u0447\u0438\u043d\u0430\u0439\u0442\u0435 \u0441 1\u20135 \u0442\u044b\u0441\u044f\u0447, \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0430 \u0432\u0440\u0435\u043c\u044f \u0438 \u043f\u0430\u043c\u044f\u0442\u044c, \u043a\u0440\u0443\u0442\u0438\u0442\u0435. \u0411\u0435\u0437 \u043d\u0435\u0433\u043e Django bulk_update \u043d\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043e\u0431\u044a\u0451\u043c\u0430\u0445 \u043b\u0435\u0433\u043a\u043e \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u0430\u0442\u043e\u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u043e\u0431 \u044d\u0442\u043e\u043c \u043d\u0438\u0436\u0435 \u0432 \u0431\u0435\u043d\u0447\u043c\u0430\u0440\u043a\u0430\u0445.\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430\u043c\u0438: ignore_conflicts \u0438 update_conflicts\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044bProduct.objects.bulk_create(products, ignore_conflicts=True)INSERT INTO products (name, price)VALUES (&#8216;Product 0&#8217;, 100), (&#8216;Product 1&#8217;, 101), &#8230;ON CONFLICT DO NOTHING;\u0423\u0434\u043e\u0431\u043d\u043e \u0434\u043b\u044f \u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u043e\u0433\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0430: \u00ab\u0432\u0441\u0442\u0430\u0432\u044c, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0448\u044c, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u00bb.\u041d\u044e\u0430\u043d\u0441 \u043f\u0440\u043e target: \u0443 ON CONFLICT DO NOTHING \u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u2014 \u0442\u043e\u0433\u0434\u0430 \u0442\u0438\u0445\u043e \u043f\u0440\u043e\u0433\u043b\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442 \u043f\u043e \u043b\u044e\u0431\u043e\u043c\u0443 unique\/exclusion constraint \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e name. \u0418\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a Django \u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 SQL \u043f\u0440\u0438 ignore_conflicts=True \u043d\u0430 PostgreSQL: \u0433\u043e\u043b\u044b\u0439 ON CONFLICT DO NOTHING, \u0431\u0435\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 unique_fields \u0437\u0434\u0435\u0441\u044c \u043d\u0438 \u043f\u0440\u0438 \u0447\u0451\u043c \u2014 \u043e\u043d \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f update_conflicts. \u0415\u0441\u043b\u0438 unique-\u0438\u043d\u0434\u0435\u043a\u0441\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e, \u00ab\u043b\u0438\u0448\u043d\u0438\u0439\u00bb \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442 \u0442\u043e\u0436\u0435 \u0437\u0430\u043c\u043e\u043b\u0447\u0438\u0442; \u0447\u0442\u043e\u0431\u044b \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u043a\u043b\u044e\u0447, \u043f\u0438\u0448\u0438\u0442\u0435 raw SQL \u0441 \u044f\u0432\u043d\u044b\u043c target \u0438\u043b\u0438 \u0441\u0443\u0436\u0430\u0439\u0442\u0435 \u0441\u0445\u0435\u043c\u0443.\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u0438 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0435 (upsert)\u041d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 Django 4.1:Product.objects.bulk_create(    products,    update_conflicts=True,    update_fields=(&#8216;price&#8217;, &#8216;updated_at&#8217;),    unique_fields=(&#8216;name&#8217;,),)INSERT INTO products (name, price, updated_at)VALUES (&#8216;Product 0&#8217;, 100, NOW()), (&#8216;Product 1&#8242;, 101, NOW()), &#8230;ON CONFLICT (name)DO UPDATE SET    price = EXCLUDED.price,    updated_at = EXCLUDED.updated_at;\u041e\u0434\u0438\u043d \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441: \u043d\u043e\u0432\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f, \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f. \u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u2014 \u043e\u0442\u043b\u0438\u0447\u043d\u043e.Upsert \u0445\u043e\u0440\u043e\u0448 \u0434\u043b\u044f \u043f\u043b\u043e\u0441\u043a\u043e\u0433\u043e \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0430 \u00ab\u0432\u0441\u0442\u0430\u0432\u044c \u0438\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u0438 price\u00bb. \u041f\u043b\u043e\u0445 \u2014 \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0435\u0449\u0451 \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0438\u0441\u0447\u0435\u0437\u043d\u0443\u0432\u0448\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438, \u043a\u0440\u0443\u0442\u0438\u0442\u044c M2M \u0438\u043b\u0438 \u043d\u0435 \u0442\u0440\u043e\u0433\u0430\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044f \u043d\u0435 \u043c\u0435\u043d\u044f\u043b\u0438\u0441\u044c. \u0422\u043e\u0433\u0434\u0430 \u2014 \u0440\u0430\u0437\u0434\u0435\u043b \u043f\u0440\u043e \u0440\u0430\u0437\u043d\u043e\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432.django-bulk-load \u0438 PostgreSQL COPY\u041d\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 bulk_create \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0435\u0441\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u043d\u0435 \u043d\u0430 INSERT, \u0430 \u043d\u0430 \u0440\u0430\u0437\u0434\u0443\u0432\u0430\u043d\u0438\u0435 SQL. \u041c\u044b \u043f\u043e\u0448\u043b\u0438 \u0432 django-bulk-load: \u043e\u043d\u0430 \u0433\u043e\u043d\u044f\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0447\u0435\u0440\u0435\u0437 COPY \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443, \u0430 \u043f\u043e\u0442\u043e\u043c \u043e\u0434\u043d\u0438\u043c-\u0434\u0432\u0443\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u0432 \u0431\u043e\u0435\u0432\u0443\u044e. \u0412 \u043a\u043e\u0434\u0435 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043b\u0438 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c bulk_insert_models, bulk_update_models \u0438 bulk_upsert_models; \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u2014 \u0432 README.\u041c\u0438\u043d\u0443\u0441\u044b \u0441\u0440\u0430\u0437\u0443: \u0442\u043e\u043b\u044c\u043a\u043e PostgreSQL, \u0437\u0430\u0432\u044f\u0437\u043a\u0430 \u043d\u0430 psycopg2, \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 \u2014 pin \u0432\u0435\u0440\u0441\u0438\u0438 \u0438 \u043d\u0435 \u0441\u0442\u0435\u0441\u043d\u044f\u0439\u0442\u0435\u0441\u044c \u0444\u043e\u0440\u043a\u043d\u0443\u0442\u044c, \u0435\u0441\u043b\u0438 \u0437\u0430\u0441\u0442\u0440\u044f\u043d\u0435\u0442\u0435.\u041f\u0440\u0438\u043c\u0435\u0440 \u0432\u0441\u0442\u0430\u0432\u043a\u0438:from django_bulk_load import bulk_insert_modelsproducts = [    Product(name=&#8217;Product 1&#8242;, price=100),    Product(name=&#8217;Product 2&#8242;, price=200),    Product(name=&#8217;Product 3&#8217;, price=150),]bulk_insert_models(products)\u0421\u0445\u0435\u043c\u0430 \u043f\u0440\u043e\u0441\u0442\u0430\u044f: temp table \u2192 COPY \u2192 UPDATE\/INSERT \u0432 \u0446\u0435\u043b\u0435\u0432\u0443\u044e \u2192 temp \u0438\u0441\u0447\u0435\u0437\u0430\u0435\u0442 \u043d\u0430 commit. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 PgBouncer \u0432 transaction pooling, \u0441 temp tables \u0431\u044b\u0441\u0442\u0440\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0433\u0440\u0443\u0441\u0442\u043d\u043e \u2014 \u043d\u0443\u0436\u0435\u043d session pooling \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u0443\u0442\u044c.\u041a\u0430\u043a \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 bulk_upsert_models\u0421\u043b\u043e\u0432\u043e \u00abupsert\u00bb \u0443 Django \u0438 \u0443 django-bulk-load \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0440\u0430\u0437\u043d\u044b\u0439 SQL. Django \u2014 \u043e\u0434\u0438\u043d ON CONFLICT DO UPDATE. Cedar \u2014 COPY, \u043f\u043e\u0442\u043e\u043c UPDATE &#8230; FROM \u0442\u043e\u043b\u044c\u043a\u043e \u0433\u0434\u0435 IS DISTINCT FROM, \u043f\u043e\u0442\u043e\u043c INSERT \u0447\u0435\u0440\u0435\u0437 LEFT JOIN \u0434\u043b\u044f \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0441\u0442\u0440\u043e\u043a. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043c\u0430\u0442\u0447 \u043f\u043e PK; \u0434\u043b\u044f \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u043f\u043e name \u0435\u0441\u0442\u044c pk_field_names.\u041f\u043b\u044e\u0441: \u043d\u0435 \u0437\u0430\u0442\u0438\u0440\u0430\u0435\u0442 created_at \u0438 \u043d\u0435 \u0448\u043b\u0451\u0442 \u0433\u0438\u0433\u0430\u043d\u0442\u0441\u043a\u0438\u0439 CASE WHEN. &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-496666","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/496666","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=496666"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/496666\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=496666"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=496666"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=496666"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}