DBA: синхрончлол, импортыг чадварлаг зохион байгуулах

Том өгөгдлийн багцыг нарийн төвөгтэй боловсруулахад (өөр өөр ETL процессууд: импорт, хөрвүүлэлт, гадаад эх сурвалжтай синхрончлол) ихэвчлэн хэрэгцээтэй байдаг түр зуур "санаж", нэн даруй хурдан боловсруул эзэлхүүнтэй зүйл.

Энэ төрлийн ердийн даалгавар нь ихэвчлэн иймэрхүү сонсогддог: "Яг энд харилцагч банкнаас буулгасан нягтлан бодох бүртгэлийн хэлтэс Хамгийн сүүлд хүлээн авсан төлбөрийг та вэбсайт руу хурдан байршуулж, данс руугаа холбох хэрэгтэй"

Гэхдээ энэ "ямар нэг зүйл" -ийн хэмжээ хэдэн зуун мегабайтаар хэмжигдэж эхлэхэд үйлчилгээ нь 24x7 мэдээллийн сантай үргэлжлүүлэн ажиллах ёстой бол таны амьдралыг сүйтгэх олон гаж нөлөө гарч ирдэг.
DBA: синхрончлол, импортыг чадварлаг зохион байгуулах
PostgreSQL дээр (зөвхөн үүн дээр биш) тэдэнтэй харьцахын тулд та бүх зүйлийг илүү хурдан, нөөц бага зарцуулж боловсруулах боломжийг олгодог зарим оновчлолыг ашиглаж болно.

1. Хаана тээвэрлэх вэ?

Эхлээд "боловсруулах" өгөгдлийг хаана байршуулахаа шийдье.

1.1. Түр хүснэгтүүд (ТҮР ХҮСНЭГТ)

Зарчмын хувьд PostgreSQL-ийн хувьд түр зуурын хүснэгтүүд нь бусадтай ижил байдаг. Тиймээс мухар сүсэгт дуртай "Тэнд байгаа бүх зүйл зөвхөн санах ойд хадгалагддаг бөгөөд энэ нь төгсгөл болно". Гэхдээ бас хэд хэдэн мэдэгдэхүйц ялгаа бий.

Өгөгдлийн сангийн холболт бүрт өөрийн "нэрийн орон зай"

Хэрэв хоёр холболт нэгэн зэрэг холбогдохыг оролдвол CREATE TABLE x, тэгвэл хэн нэгэн гарцаагүй авна өвөрмөц бус алдаа мэдээллийн сангийн объектууд.

Гэхдээ аль аль нь гүйцэтгэх гэж оролдвол CREATE TEMPORARY TABLE x, дараа нь хоёулаа үүнийг хэвийн хийх бөгөөд хүн бүр авах болно таны хуулбар ширээ. Мөн тэдний хооронд нийтлэг зүйл байхгүй болно.

Салгах үед "өөрийгөө устгах"

Холболт хаагдсан үед бүх түр зуурын хүснэгтүүд автоматаар устгагдана, тиймээс гараар DROP TABLE x өөр ямар ч утгагүй ...

Хэрэв та ажиллаж байгаа бол pgbouncer гүйлгээний горимд байна, дараа нь мэдээллийн сан нь энэ холболт идэвхтэй хэвээр байгаа гэдэгт итгэдэг бөгөөд энэ түр зуурын хүснэгт хэвээр байна.

Тиймээс, өөр холболтоос pgbouncer руу дахин үүсгэхийг оролдвол алдаа гарах болно. Гэхдээ үүнийг ашигласнаар үүнийг тойрч болно CREATE TEMPORARY TABLE IF NOT EXISTS x.

Үнэн бол үүнийг хийхгүй байх нь дээр, учир нь та "өмнөх эзэмшигчээс" үлдсэн өгөгдлийг "гэнэт" олох боломжтой. Үүний оронд гарын авлагыг уншиж, хүснэгт үүсгэх үед үүнийг нэмэх боломжтой гэдгийг харах нь илүү дээр юм ON COMMIT DROP - өөрөөр хэлбэл, гүйлгээ дуусахад хүснэгт автоматаар устах болно.

Хуулбарлахгүй

Тэд зөвхөн тодорхой холболтод хамаарах тул түр зуурын хүснэгтүүдийг хуулбарлахгүй. Гэхдээ Энэ нь өгөгдлийг давхар бүртгэх шаардлагагүй болно heap + WAL-д байгаа тул INSERT/UPDATE/DELETE нь илүү хурдан болно.

Гэхдээ түр зуурын хүснэгт нь "бараг энгийн" хүснэгт хэвээр байгаа тул үүнийг хуулбар дээр үүсгэх боломжгүй. Наад зах нь одоогоор, хэдийгээр холбогдох нөхөөс нь удаан хугацаанд эргэлдэж байсан.

1.2. БҮТЭЭЛГҮЙ ХҮСНЭГТ

Жишээлбэл, хэрэв танд нэг гүйлгээний дотор хэрэгжих боломжгүй ETL процесс байгаа ч, та юу хийх ёстой вэ? pgbouncer гүйлгээний горимд байна? ...

Эсвэл өгөгдлийн урсгал маш том байна Нэг холболт дээр хангалттай зурвасын өргөн байхгүй байна өгөгдлийн сангаас (унших, CPU тутамд нэг процесс)?..

Эсвэл зарим үйл ажиллагаа явагдаж байна асинхрон өөр өөр холболтууд?..

Энд ганцхан сонголт байна - түр зуурын бус хүснэгтийг түр бий болгох. Пун, тийм ээ. Тэр бол:

  • хэнтэй ч огтлолцохгүйн тулд санамсаргүй нэрээр "өөрийн" хүснэгтүүдийг үүсгэсэн
  • ханд: тэдгээрийг гадны эх сурвалжаас авсан мэдээллээр дүүргэсэн
  • Өөрчлөлт: хөрвүүлсэн, гол холбоосын талбаруудыг бөглөсөн
  • Load: бэлэн өгөгдлийг зорилтот хүснэгтүүдэд цутгасан
  • "Миний" хүснэгтүүдийг устгасан

Тэгээд одоо - тосонд ялаа. Үнэндээ, PostgreSQL дээрх бүх бичвэрүүд хоёр удаа тохиолддог - WAL-д анх удаа, дараа нь хүснэгт/индекс биетүүд рүү оруулна. Энэ бүхэн нь ACID-ийг дэмжиж, хооронд нь өгөгдлийн харагдах байдлыг зөв болгохын тулд хийгддэг COMMITсамартай ба ROLLBACK'үгүй гүйлгээ.

Гэхдээ бидэнд энэ хэрэггүй! Бидэнд бүх үйл явц бий Энэ нь бүрэн амжилтанд хүрсэн эсвэл тийм биш байсан.. Хэдэн завсрын гүйлгээ хийх нь хамаагүй - бид "процессыг дундаас нь үргэлжлүүлэх" сонирхолгүй, ялангуяа хаана байсан нь тодорхойгүй үед.

Үүнийг хийхийн тулд PostgreSQL хөгжүүлэгчид 9.1 хувилбар дээр ийм зүйлийг нэвтрүүлсэн БҮТГЭЭГҮЙ хүснэгтүүд:

Энэ заалтын дагуу хүснэгтийг unloged хэлбэрээр үүсгэнэ. Бүртгэлгүй хүснэгтэд бичигдсэн өгөгдөл нь урьдчилан бичих бүртгэлээр дамждаггүй (29-р бүлгийг үзнэ үү), ийм хүснэгтүүдийг ердийнхөөс хамаагүй хурдан ажиллана. Гэсэн хэдий ч тэд бүтэлгүйтлээс дархлаагүй; серверийн доголдол эсвэл яаралтай унтрах тохиолдолд бүртгэлээс гарсан хүснэгт автоматаар таслагдсан. Нэмж дурдахад, бүртгэлээс хасагдсан хүснэгтийн агуулга хуулбарлаагүй боол серверүүд рүү. Бүртгэлгүй хүснэгт дээр үүсгэсэн индексүүд автоматаар бүртгэлээс хасагдана.

Товчоор хэлбэл энэ нь хамаагүй хурдан болно, гэхдээ мэдээллийн баазын сервер "унавал" энэ нь тааламжгүй болно. Гэхдээ энэ нь хэр олон удаа тохиолддог вэ, таны ETL процесс мэдээллийн баазыг "сэргэсний" дараа үүнийг "дундаас" хэрхэн зөв засахаа мэддэг үү?..

Хэрэв тийм биш бол дээрх тохиолдол таныхтай төстэй байвал ашиглана уу UNLOGGEDгэхдээ хэзээ ч Энэ шинж чанарыг бодит хүснэгтүүд дээр бүү идэвхжүүл, өгөгдөл нь танд хайртай.

1.3. ЗОРИУЛАХ { МӨРӨӨ УСТГАХ | УНАХ}

Энэ бүтэц нь хүснэгт үүсгэх үед гүйлгээ дуусах үед автомат горимыг тодорхойлох боломжийг танд олгоно.

дээр ON COMMIT DROP Би аль хэдийн дээр бичсэн, энэ нь үүсгэдэг DROP TABLE, гэхдээ хамт ON COMMIT DELETE ROWS нөхцөл байдал илүү сонирхолтой - энэ нь энд бий болсон TRUNCATE TABLE.

Түр хүснэгтийн мета тайлбарыг хадгалах бүх дэд бүтэц нь ердийн хүснэгтийнхтэй яг ижил байдаг тул Түр зуурын хүснэгтүүдийг байнга үүсгэх, устгах нь системийн хүснэгтүүдийг хүчтэй "хавдаг" болгодог pg_class, pg_attribute, pg_attrdef, pg_depend,...

Одоо танд секунд тутамд шинэ гүйлгээ нээдэг, түр хүснэгт үүсгэх, бөглөх, боловсруулах, устгах өгөгдлийн сантай шууд холбогдох ажилтан байна гэж төсөөлөөд үз дээ... Системийн хүснэгтүүдэд хог хаягдал илүүдэл хуримтлагдах, мөн Энэ нь үйлдэл бүрт нэмэлт тоормос үүсгэх болно.

Ерөнхийдөө үүнийг бүү хий! Энэ тохиолдолд энэ нь илүү үр дүнтэй байдаг CREATE TEMPORARY TABLE x ... ON COMMIT DELETE ROWS гүйлгээний мөчлөгөөс хас - дараа нь шинэ гүйлгээ бүрийн эхэнд хүснэгтүүд аль хэдийн байна оршин байх болно (дуудлага хадгалах CREATE), гэхдээ хоосон байх болно, баярлалаа TRUNCATE (бид мөн түүний дуудлагыг хадгалсан) өмнөх гүйлгээг хийх үед.

1.4. ШИГ... ОРОН ...

Түр зуурын хүснэгтийг ашиглах ердийн тохиолдлуудын нэг бол төрөл бүрийн импорт юм гэдгийг би эхэнд дурьдсан бөгөөд хөгжүүлэгч нь зорилтот хүснэгтийн талбаруудын жагсаалтыг өөрийн түр зуурын мэдэгдэлд залхаж хуулж буулгадаг ...

Гэхдээ залхуурал бол дэвшлийн хөдөлгүүр юм! Тийм ч учраас "Дээж дээр үндэслэн" шинэ хүснэгт үүсгэх Энэ нь илүү хялбар байж болно:

CREATE TEMPORARY TABLE import_table(
  LIKE target_table
);

Та энэ хүснэгтэд маш их өгөгдөл үүсгэх боломжтой тул түүгээр хайлт хийх нь хэзээ ч хурдан байх болно. Гэхдээ үүнийг шийдэх уламжлалт шийдэл байдаг - индексүүд! Тэгээд тиймээ, Түр зуурын хүснэгт нь индекстэй байж болно.

Ихэнхдээ шаардлагатай индексүүд нь зорилтот хүснэгтийн индексүүдтэй давхцдаг тул та зүгээр л бичиж болно. LIKE target_table INCLUDING INDEXES.

Хэрэв танд бас хэрэгтэй бол DEFAULT-утга (жишээлбэл, үндсэн түлхүүр утгуудыг бөглөх) ашиглаж болно LIKE target_table INCLUDING DEFAULTS. Эсвэл зүгээр л - LIKE target_table INCLUDING ALL — өгөгдмөл, индекс, хязгаарлалт,... хуулдаг.

Гэхдээ энд та бүтээсэн бол үүнийг ойлгох хэрэгтэй хүснэгтийг индексийн хамт нэн даруй импортлох юм бол өгөгдлийг ачаалахад удаан хугацаа шаардагданаХэрэв та эхлээд бүх зүйлийг бөглөж, дараа нь индексүүдийг эргэлдүүлбэл - үүнийг хэрхэн хийж байгааг жишээ болгон хараарай. pg_dump.

Ерөнхийдөө RTFM!

2. Хэрхэн бичих вэ?

Зүгээр л хэлье - үүнийг ашигла COPY-"багц"-ын оронд урсгал INSERT, заримдаа хурдатгал. Урьдчилан үүсгэсэн файлаас ч шууд хийх боломжтой.

3. Хэрхэн боловсруулах вэ?

Тиймээс, бидний танилцуулгыг дараах байдлаар харцгаая.

  • Таны мэдээллийн санд хадгалагдсан үйлчлүүлэгчийн өгөгдөл бүхий хүснэгт танд байна 1 сая бичлэг
  • өдөр бүр үйлчлүүлэгч танд шинийг илгээдэг бүрэн "зураг"
  • Та туршлагаасаа үүнийг үе үе мэддэг 10 мянгаас илүүгүй бичлэг өөрчлөгддөггүй

Ийм нөхцөл байдлын сонгодог жишээ бол KLADR бааз — Нийтдээ маш олон хаяг байгаа ч долоо хоног бүр байршуулахдаа улсын хэмжээнд ч гэсэн маш цөөхөн өөрчлөлт (суурин газрын нэрийг өөрчлөх, гудамжийг нэгтгэх, шинэ байшин барих) хийдэг.

3.1. Бүрэн синхрончлолын алгоритм

Энгийн болгохын тулд та өгөгдлийн бүтцийг өөрчлөх шаардлагагүй гэж үзье - хүснэгтийг хүссэн хэлбэрт оруулаарай, өөрөөр хэлбэл:

  • устгах байхгүй болсон бүх зүйл
  • шинэчлэх аль хэдийн байсан, шинэчлэх шаардлагатай бүх зүйл
  • оруулах хараахан болоогүй бүх зүйл

Үйлдлүүдийг яагаад ийм дарааллаар хийх ёстой вэ? Учир нь хүснэгтийн хэмжээ хамгийн бага хэмжээгээр өсөх болно (MVCC-г санаарай!).

dst-с УСТГАХ

Үгүй ээ, мэдээжийн хэрэг та зөвхөн хоёр үйлдлээр л явж чадна:

  • устгах (DELETE) ерөнхийдөө бүх зүйл
  • оруулах бүгдийг шинэ зургаас

Гэхдээ үүнтэй зэрэгцэн MVCC-ийн ачаар Хүснэгтийн хэмжээ яг хоёр дахин нэмэгдэх болно! 1К шинэчлэлтийн улмаас хүснэгтэд +10 сая бичлэгийн зургийг авах нь хэт их ачаалал юм...

TRUNCATE dst

Илүү туршлагатай хөгжүүлэгч таблетыг бүхэлд нь маш хямдаар цэвэрлэж болно гэдгийг мэддэг.

  • тодорхой (TRUNCATE) хүснэгтийг бүхэлд нь
  • оруулах бүгдийг шинэ зургаас

Энэ арга нь үр дүнтэй, заримдаа нэлээд хэрэгждэг, гэхдээ асуудал байна ... Бид 1 сая бичлэгийг удаан хугацаанд нэмж оруулах тул энэ бүх хугацаанд хүснэгтийг хоосон орхих боломжгүй (үүнийг нэг гүйлгээнд оруулахгүйгээр хийх болно).

Юу гэсэн үг вэ гэхээр:

  • бид эхэлж байна урт хугацааны гүйлгээ
  • TRUNCATE ногдуулдаг Онцгой хандалт- блоклох
  • Бид оруулгыг удаан хугацаанд хийдэг, энэ үед бусад хүмүүс бүр чадахгүй SELECT

Ямар нэг зүйл болохгүй байна...

ХҮСНЭГТИЙГ ӨӨРЧЛӨХ… НЭРИЙГ ӨӨРЧЛӨХ… / ХҮСНЭГТИЙГ УНАХ…

Өөр нэг хувилбар бол бүгдийг тусдаа шинэ хүснэгт болгон бөглөж, дараа нь хуучин хүснэгтийн оронд зүгээр л нэрийг нь өөрчлөх явдал юм. Хэдэн муухай жижиг зүйл:

  • одоо ч гэсэн Онцгой хандалт, хэдийгээр цаг хугацаа мэдэгдэхүйц бага
  • Энэ хүснэгтийн бүх асуулгын төлөвлөгөө/статистикийг шинэчилсэн, ANALYZE-г ажиллуулах хэрэгтэй
  • бүх гадаад түлхүүрүүд эвдэрсэн (FK) ширээн дээр

Саймон Ригсийн WIP нөхөөсийг хийхийг санал болгосон ALTER-хүснэгтийн биеийг файлын түвшинд, статистик болон FK-д хүрэлгүйгээр солих үйл ажиллагаа боловч ирц цуглуулаагүй.

УСТГАХ, ШИНЭЧЛЭХ, ОРУУЛАХ

Тиймээс бид гурван үйлдлийг блоклохгүй байх хувилбар дээр шийднэ. Бараг гурав... Үүнийг хэрхэн хамгийн үр дүнтэй хийх вэ?

-- все делаем в рамках транзакции, чтобы никто не видел "промежуточных" состояний
BEGIN;

-- создаем временную таблицу с импортируемыми данными
CREATE TEMPORARY TABLE tmp(
  LIKE dst INCLUDING INDEXES -- по образу и подобию, вместе с индексами
) ON COMMIT DROP; -- за рамками транзакции она нам не нужна

-- быстро-быстро вливаем новый образ через COPY
COPY tmp FROM STDIN;
-- ...
-- .

-- удаляем отсутствующие
DELETE FROM
  dst D
USING
  dst X
LEFT JOIN
  tmp Y
    USING(pk1, pk2) -- поля первичного ключа
WHERE
  (D.pk1, D.pk2) = (X.pk1, X.pk2) AND
  Y IS NOT DISTINCT FROM NULL; -- "антиджойн"

-- обновляем оставшиеся
UPDATE
  dst D
SET
  (f1, f2, f3) = (T.f1, T.f2, T.f3)
FROM
  tmp T
WHERE
  (D.pk1, D.pk2) = (T.pk1, T.pk2) AND
  (D.f1, D.f2, D.f3) IS DISTINCT FROM (T.f1, T.f2, T.f3); -- незачем обновлять совпадающие

-- вставляем отсутствующие
INSERT INTO
  dst
SELECT
  T.*
FROM
  tmp T
LEFT JOIN
  dst D
    USING(pk1, pk2)
WHERE
  D IS NOT DISTINCT FROM NULL;

COMMIT;

3.2. Импортын дараах боловсруулалт

Ижил KLADR-д бүх өөрчлөгдсөн бүртгэлийг дараах боловсруулалтаар нэмэлт байдлаар ажиллуулах ёстой - хэвийн болгож, түлхүүр үгсийг тодруулж, шаардлагатай бүтэц болгон бууруулж болно. Гэхдээ чи яаж мэдэх вэ - яг юу өөрчлөгдсөнсинхрончлолын кодыг хүндрүүлэхгүйгээр, хамгийн тохиромжтой нь түүнд огт хүрэлгүйгээр?

Хэрэв синхрончлол хийх үед зөвхөн таны процесс бичих эрхтэй бол та бидний бүх өөрчлөлтийг цуглуулах триггер ашиглаж болно:

-- целевые таблицы
CREATE TABLE kladr(...);
CREATE TABLE kladr_house(...);

-- таблицы с историей изменений
CREATE TABLE kladr$log(
  ro kladr, -- тут лежат целые образы записей старой/новой
  rn kladr
);

CREATE TABLE kladr_house$log(
  ro kladr_house,
  rn kladr_house
);

-- общая функция логирования изменений
CREATE OR REPLACE FUNCTION diff$log() RETURNS trigger AS $$
DECLARE
  dst varchar = TG_TABLE_NAME || '$log';
  stmt text = '';
BEGIN
  -- проверяем необходимость логгирования при обновлении записи
  IF TG_OP = 'UPDATE' THEN
    IF NEW IS NOT DISTINCT FROM OLD THEN
      RETURN NEW;
    END IF;
  END IF;
  -- создаем запись лога
  stmt = 'INSERT INTO ' || dst::text || '(ro,rn)VALUES(';
  CASE TG_OP
    WHEN 'INSERT' THEN
      EXECUTE stmt || 'NULL,$1)' USING NEW;
    WHEN 'UPDATE' THEN
      EXECUTE stmt || '$1,$2)' USING OLD, NEW;
    WHEN 'DELETE' THEN
      EXECUTE stmt || '$1,NULL)' USING OLD;
  END CASE;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Одоо бид синхрончлолыг эхлүүлэхийн өмнө триггерүүдийг ашиглах боломжтой (эсвэл тэдгээрийг идэвхжүүлэх ALTER TABLE ... ENABLE TRIGGER ...):

CREATE TRIGGER log
  AFTER INSERT OR UPDATE OR DELETE
  ON kladr
    FOR EACH ROW
      EXECUTE PROCEDURE diff$log();

CREATE TRIGGER log
  AFTER INSERT OR UPDATE OR DELETE
  ON kladr_house
    FOR EACH ROW
      EXECUTE PROCEDURE diff$log();

Дараа нь бид бүртгэлийн хүснэгтүүдээс шаардлагатай бүх өөрчлөлтийг тайвнаар гаргаж аваад нэмэлт зохицуулагчаар дамжуулан ажиллуулдаг.

3.3. Холбогдсон багцуудыг импортлож байна

Дээр дурдсан эх сурвалж болон очих газрын өгөгдлийн бүтэц ижил байх тохиолдлыг авч үзсэн. Гэхдээ гадны системээс байршуулсан файл нь манай мэдээллийн сан дахь хадгалалтын бүтцээс өөр форматтай байвал яах вэ?

Сонгодог "олон-нэг" сонголт болох үйлчлүүлэгчид болон тэдний дансны хадгалалтыг жишээ болгон авч үзье.

CREATE TABLE client(
  client_id
    serial
      PRIMARY KEY
, inn
    varchar
      UNIQUE
, name
    varchar
);

CREATE TABLE invoice(
  invoice_id
    serial
      PRIMARY KEY
, client_id
    integer
      REFERENCES client(client_id)
, number
    varchar
, dt
    date
, sum
    numeric(32,2)
);

Гэхдээ гадны эх сурвалжаас татаж авах нь "бүгдийг нэг дор" хэлбэрээр бидэнд ирдэг.

CREATE TEMPORARY TABLE invoice_import(
  client_inn
    varchar
, client_name
    varchar
, invoice_number
    varchar
, invoice_dt
    date
, invoice_sum
    numeric(32,2)
);

Мэдээжийн хэрэг, хэрэглэгчийн мэдээллийг энэ хувилбарт хуулбарлах боломжтой бөгөөд гол бүртгэл нь "данс" юм.

0123456789;Вася;A-01;2020-03-16;1000.00
9876543210;Петя;A-02;2020-03-16;666.00
0123456789;Вася;B-03;2020-03-16;9999.00

Загварын хувьд бид зүгээр л туршилтын өгөгдлөө оруулах болно, гэхдээ санаарай - COPY илүү үр дүнтэй!

INSERT INTO invoice_import
VALUES
  ('0123456789', 'Вася', 'A-01', '2020-03-16', 1000.00)
, ('9876543210', 'Петя', 'A-02', '2020-03-16', 666.00)
, ('0123456789', 'Вася', 'B-03', '2020-03-16', 9999.00);

Эхлээд бидний "баримт"-д хамаарах "хэсгүүд"-ийг онцолж үзье. Манай тохиолдолд нэхэмжлэх нь үйлчлүүлэгчдэд хамаарна:

CREATE TEMPORARY TABLE client_import AS
SELECT DISTINCT ON(client_inn)
-- можно просто SELECT DISTINCT, если данные заведомо непротиворечивы
  client_inn inn
, client_name "name"
FROM
  invoice_import;

Дансуудыг хэрэглэгчийн ID-тай зөв холбохын тулд бид эхлээд эдгээр танигчийг олж мэдэх эсвэл үүсгэх хэрэгтэй. Тэдний доор талбаруудыг нэмье:

ALTER TABLE invoice_import ADD COLUMN client_id integer;
ALTER TABLE client_import ADD COLUMN client_id integer;

Дээр тайлбарласан хүснэгтийн синхрончлолын аргыг жижиг нэмэлт өөрчлөлтөөр ашиглацгаая - бид "зөвхөн хавсаргах" үйлчлүүлэгчдийг импортолдог тул зорилтот хүснэгтэд юу ч шинэчлэхгүй, устгахгүй:

-- проставляем в таблице импорта ID уже существующих записей
UPDATE
  client_import T
SET
  client_id = D.client_id
FROM
  client D
WHERE
  T.inn = D.inn; -- unique key

-- вставляем отсутствовавшие записи и проставляем их ID
WITH ins AS (
  INSERT INTO client(
    inn
  , name
  )
  SELECT
    inn
  , name
  FROM
    client_import
  WHERE
    client_id IS NULL -- если ID не проставился
  RETURNING *
)
UPDATE
  client_import T
SET
  client_id = D.client_id
FROM
  ins D
WHERE
  T.inn = D.inn; -- unique key

-- проставляем ID клиентов у записей счетов
UPDATE
  invoice_import T
SET
  client_id = D.client_id
FROM
  client_import D
WHERE
  T.client_inn = D.inn; -- прикладной ключ

Үнэндээ бүх зүйл дотор байна invoice_import Одоо бид холбоо барих талбарыг бөглөсөн байна client_id, үүгээр бид нэхэмжлэх оруулах болно.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх