DBA: Si të organizojmë me kompetencë sinkronizimet dhe importet

Kur proceset e ndërlikuara përpunuese të grupeve të mëdha të të dhënave (ndryshe Proceset ETL: importet, konvertimet dhe sinkronizimet me burime të jashtme) shpesh lind nevoja për të «ruajtur» temporalish, dhe për t'i përpunuar shpejt diçka voluminoze.

Një detyrë tipike e këtij lloji zakonisht formulohet kështu: «Ja ku administrata ka eksportuar nga banka e klientëve pagesat e fundit të pranuara, duhet t'i ngarkojmë shpejt në sit dhe t'i lidhim me llogaritë»

Por kur vĂ«llimi i kĂ«tij «diçka» fillon tĂ« matet nĂ« qindra megabajt, dhe shĂ«rbimi duhet tĂ« vazhdojĂ« tĂ« funksionojĂ« me bazĂ«n nĂ« modalitetin 24×7, lindin shumĂ« efekte anĂ«sore qĂ« do t'ju prishin jetĂ«n.
DBA: Si të organizojmë me kompetencë sinkronizimet dhe importet
Për t'u përballur me to në PostgreSQL (po ashtu si dhe jo vetëm te ai), mund të përdoren disa mundësi për optimizime që do të lejojnë përpunimin më të shpejtë dhe me një konsum më të ulët të burimeve.

1. Ku të ngarkojmë?

Së pari, le të përcaktojmë se ku mund të ngarkojmë të dhënat që duam të «përpunojmë».

1.1. Tabela të përkohshme (TEMPORARY TABLE)

Në princip, për PostgreSQL të dhënat e përkohshme janë të njëjta si çdo tabelë tjetër. Prandaj, superstitat si «aty gjithë ruhet vetëm në memorie, dhe ajo mund të mbarojë»nuk janë të sakta. Por ka disa dallime të rëndësishme.

Një «emër» të veçantë për çdo lidhje me DB

Nëse dy lidhje përpiqen ndryshe të ekzekutojnë CREATE TABLE x, atëherë ndonjëri patjetër do të marrë gabimin e qëndrueshmërisë objekteve të DB.

Por nĂ«se tĂ« dy pĂ«rpiqen tĂ« ekzekutojnĂ« CREATE PËRKOHE TABELA x, atĂ«herĂ« tĂ« dy do ta realizojnĂ« normalisht, dhe secili do tĂ« marrĂ« njĂ« shembujt e tij tabelĂ«s. Dhe mezi do tĂ« ketĂ« ndonjĂ« lidhje mes tyre.

«Vetë-shkatërrimi» pas disconnection

Kur mbyllet lidhja, tĂ« gjitha tabelat e pĂ«rkohshme fshihen automatikisht, prandaj nuk ka asnjĂ« kuptim tĂ« ekzekutoni DROP TABLE x pĂ«rveç 


Nëse po punoni përmes pgbouncer në mënyrën e transaksionit, atëherë baza vazhdon të besojë se kjo lidhje është ende aktive, dhe se tabela e përkohshme vazhdon të ekzistojë në të.

Prandaj, pĂ«rpjekja pĂ«r ta krijuar pĂ«rsĂ«ri, nga njĂ« lidhje tjetĂ«r me pgbouncer, do tĂ« sjellĂ« njĂ« gabim. Por kjo mund tĂ« anashkalohet duke pĂ«rdorur KRIJONI TABEL TEMPORARE NËSE NUK EKZISTON x.

MegjithatĂ«, Ă«shtĂ« mĂ« mirĂ« tĂ« mos bĂ«ni kĂ«shtu, sepse mĂ« pas mund tĂ« ‘zbulohet papritur’ aty, tĂ« dhĂ«na qĂ« kanĂ« mbetur nga ‘pronari i mĂ«parshĂ«m’. NĂ« vend tĂ« kĂ«saj, Ă«shtĂ« shumĂ« mĂ« mirĂ« tĂ« lexoni manualin dhe tĂ« shihni se kur krijoni tabelĂ«n ka mundĂ«sinĂ« tĂ« shtoni PËR ANGAZHIM DROP — pra atĂ«, kur tĂ« mbyllet transaksioni, tabela do tĂ« fshihet automatikisht.

Jo-replikimi

Për shkak se i përket vetëm një lidhjeje të caktuar, tabelat përkohshme nuk replikohen. Por kjo nuk kërkon shkruaj të dyfishtë të të dhënave në heap + WAL, prandaj INSERT/UPDATE/DELETE në të është shumë më i shpejtë.

Por, duke qenë se tabela përkohshme është përfundimisht një tabelë "gati normale", atëherë nuk mund të krijohet as në replikë. Të paktën deri tani, ndonëse një patch përkatës është në qarkullim prej kohësh.

1.2. Tabelat e pa-journaluara (UNLOGGED TABLE)

Por çfarë të bëni, për shembull, nëse keni një proces ETL të rëndë, i cili nuk mund të realizohet brenda një transaksioni dhe ju keni pgbouncer në mënyrën e transaksionit?..

Ose fluksi i të dhënave është kaq i madh sa kapaciteti i një lidhjeje me DB (lexo, një proces në CPU) nuk është i mjaftueshëm? ..

Ose disa operacione shkojnë asirinkronisht në lidhje të ndryshme? ..

KĂ«tu ka vetĂ«m njĂ« mundĂ«si — tĂ« krijoni pĂ«rkohĂ«sisht njĂ« tabelĂ« jo-pĂ«rkohshme. NjĂ« lojĂ« fjalĂ«sh, po. DomethĂ«nĂ«:

  • krijova "tabelat" e mia me emra sa mĂ« tĂ« rastĂ«sishĂ«m pĂ«r tĂ« mos u pĂ«rzier me askĂ«nd
  • Ekstrakto: i ngarkova ato me tĂ« dhĂ«na nga njĂ« burim tĂ« jashtĂ«m
  • Transformo: i pĂ«rpunova, popullova fushat e lidhjeve çelĂ«s
  • Ngarko: transferova tĂ« dhĂ«nat e pĂ«rgatitura nĂ« tabelat e synuara
  • fshiva "tabelat" e mia

Tani — njĂ« lugĂ« katran. NĂ« thelb, e gjithĂ« regjistrimi nĂ« PostgreSQL ndodh dy herĂ« — sĂ« pari nĂ« WAL, pastaj nĂ« trupin e tabelave/indeksĂ«ve. E gjithĂ« kjo Ă«shtĂ« bĂ«rĂ« pĂ«r tĂ« mbĂ«shtetur ACID dhe pĂ«r tĂ« siguruar dukshmĂ«rinĂ« korrekte tĂ« tĂ« dhĂ«nave midis KREJT'transaksioneve tĂ« ferra dhe ROLLBACK'transaksioneve tĂ« ferra.

Por nuk kemi nevojĂ« pĂ«r kĂ«tĂ«! TĂ« gjithĂ« procesi ose kaloi me sukses plotĂ«sisht, ose nuk kaloi. Nuk ka rĂ«ndĂ«si sa do tĂ« ketĂ« transaksione ndĂ«rmjetĂ«se — nuk na intereson "tĂ« vazhdojmĂ« procesin nga mesi", sidomos kur nuk dihet ku ishte.

Për këtë qëllim, zhvilluesit e PostgreSQL në versionin 9.1 implementuan një funksion si tabelat e pa-journaluara (UNLOGGED):

Me kĂ«tĂ« njoftim, tabela krijohet si njĂ« tabelĂ« e pa-journaluar. TĂ« dhĂ«nat qĂ« shkruhen nĂ« tabelat e pa-journaluara nuk kalojnĂ« pĂ«rmes regjistrit tĂ« para-shkrimit (shih Kapitulli 29), si rezultat, tabelat e tilla punojnĂ« shumĂ« mĂ« shpejt se ato normale.MegjithatĂ«, ato nuk janĂ« tĂ« mbrojtura nga dĂ«shtimi; nĂ« rast dĂ«shtimi ose shkĂ«putjeje tĂ« papritur tĂ« serverit, tabela e pa-journaluar automatikisht fshihet.PĂ«r mĂ« tepĂ«r, pĂ«rmbajtja e tabelĂ«s sĂ« pa-journaluar nuk replikohen. nĂ« serverĂ« tĂ« drejtuar. Çdo indeks qĂ« krijohet pĂ«r njĂ« tabelĂ« tĂ« pa regjistruar automatikisht bĂ«het i pa regjistruar.

Në mënyrë më të shkurtër, do të jetë shumë më e shpejtë, por nëse serveri i DB bie - do të jetë e pakëndshme. Por sa shpesh ndodh kjo, dhe a e di procesi juaj ETL ta rregullojë siç duhet "nga mesi" pasi "të ringjallet" DB?..

Nëse nuk është kështu dhe rasti më sipër është i ngjashëm me tuajin - përdorni UNLOGGED, por kurrë mos e aktivizoni këtë atribut në tabelat reale, të dhënat e të cilave ju janë të çmuara.

1.3. ON COMMIT { FSHIJ RRESHTAT | HUMB }

Ky konstrukt lejon të përcaktoni sjelljen automatike gjatë përmbylljes së transaksionit kur krijoni një tabelë.

PĂ«r PËR ANGAZHIM DROP UnĂ« tashmĂ« e kam shkruar mĂ« lart, ai gjeneron DROP TABLE, por kĂ«tu PËR ANGAZHIM Fshij Rreshtat situata Ă«shtĂ« mĂ« interesante - kĂ«tu gjenerohet TRUNCATE TABLE.

Duke qenĂ« se e gjithĂ« infrastruktura e ruajtjes sĂ« meta-pĂ«rshkrimit tĂ« tabelĂ«s pĂ«rkohĂ«she Ă«shtĂ« krejtĂ«sisht e njĂ«jtĂ« me atĂ« tĂ« zakonshmes, atĂ«herĂ« krijimi dhe fshirja e tabelave pĂ«rkohĂ«she çon nĂ« njĂ« "shkaktim" tĂ« fortĂ« tĂ« tabelave sistemike pg_class, pg_attribute, pg_attrdef, pg_depend,


Tani imagjinoni se keni njĂ« punĂ«tor nĂ« njĂ« lidhje tĂ« drejtpĂ«rdrejtĂ« me DB, i cili çdo sekondĂ« hap njĂ« transaksion tĂ« ri, krijon, mbush, pĂ«rpunon dhe fshin njĂ« tabelĂ« pĂ«rkohĂ«she
 Do tĂ« grumbullohet shumĂ« pleh nĂ« tabelat sistemike, qĂ« do tĂ« krijojĂ« vonesa tĂ« tepĂ«rta nĂ« çdo operacion.

Në përgjithësi, mos e bëni kështu! Në këtë rast, është më efektive CREATE TEMPORARY TABLE x ... ON COMMIT DELETE ROWS ta nxirrni jashtë ciklit të transaksioneve - atëherë në fillim të çdo transaksioni të ri tabela do të ekzistojë (shpenzojmë thirrjen CREATE), por do të jetë bosh, falë TRUNCATE (ne gjithashtu e kemi kursyer thirrjen e saj) kur përfundon transaksioni i mëparshëm.

1.4. LIKE
 PËR SHFAROSJE


E përmenda në fillim se një nga rastet tipike të përdorimit për tabelat përkohësh është lloje të ndryshme importesh - dhe zhvilluesi me lodhje kopjon listën e fushave të tabelës objektiv në shpalljen e tabelës së tij përkohëshe


Por inercia është motor i progresit! Prandaj krijimi i një tabele "sipër" mund të jetë shumë më i lehtë:

CREATE TEMPORARY TABLE import_table(
  LIKE target_table
);

Duke qenë se është shumë e mundur të gjeneroni shumë të dhëna në këtë tabelë, kërkimet në të do të bëhen aspak të shpejta. Por për këtë ka një zgjidhje tradicionale - indekset! Dhe, po, edhe tabela përkohëshe mund të ketë indekse.

Duke qenë se shpesh herë, indekset e nevojshme përkojnë me ato të tabelës objektiv, mund të shkruani thjesht SI target_table DHE ME INDICES.

NĂ«se ju nevojiten gjithashtu DEFAULT-vlera (pĂ«r shembull, pĂ«r tĂ« plotĂ«suar vlerat e çelĂ«sit primar), mund tĂ« pĂ«rdorni SI target_table PËRSHKAK TË TË DËRGUARAVE TË PARACAKTUARA. Ose thjesht — SI target_table PĂ«rfshirĂ« tĂ« gjitha — do tĂ« kopjojĂ« defoltet, indekset, constraint-e,


Por kĂ«tu duhet tĂ« kuptoni se nĂ«se keni krijuar tabelĂ«n e importit menjĂ«herĂ« me indekse, atĂ«herĂ« do tĂ« importohen tĂ« dhĂ«nat mĂ« ngadalĂ«, sesa nĂ«se sĂ« pari importoni tĂ« gjithĂ« tĂ« dhĂ«nat, dhe pastaj vendosni indekset — shikoni si e bĂ«n pg_dump.

Në përgjithësi, RTFM.!

2. Si të shkruani?

Do tĂ« them thjesht — pĂ«rdorni COPY-rrjedhĂ«n nĂ« vend tĂ« "grupit" SHTO, shpejtĂ«sia shumĂ«fishohet. Madje mund t'i merrni direkt nga njĂ« skedar i formuar paraprakisht.

3. Si të përpunoni?

Pra, le të supozojmë se hyrja jonë duket kështu:

  • ju keni nĂ« bazĂ« njĂ« tabelĂ« me tĂ« dhĂ«nat e klientĂ«ve nĂ« 1M regjistrime
  • çdo ditĂ« klienti ju dĂ«rgon njĂ« tĂ« plotĂ« "imazh"
  • sipas pĂ«rvojĂ«s e dini se nga hera nĂ« herĂ« ndryshojnĂ« jo mĂ« shumĂ« se 10K regjistrime

NjĂ« shembull klasik i njĂ« situate tĂ« tillĂ« Ă«shtĂ« baza KLDAR — ka shumĂ« adresa, por nĂ« çdo shkarkim javore tĂ« ndryshimeve (ndryshime emrash tĂ« vendbanimeve, bashkime rrugĂ«sh, shfaqje tĂ« shtĂ«pive tĂ« reja) ka shumĂ« pak edhe nĂ« shkallĂ« tĂ« tĂ«rĂ« vendit.

3.1. Algoritmi i sinkronizimit të plotë

PĂ«r thjeshtĂ«si supozojmĂ« se nuk keni nevojĂ« ta restrukturoni tĂ« dhĂ«nat — thjesht ta sillni tabelĂ«n nĂ« formĂ«n e duhur, pra:

  • tĂ« fshijĂ« gjithçka qĂ« nuk ekziston mĂ«
  • Opsionet pĂ«r procesorĂ«t «Elbrus» janĂ« tĂ« disponueshme me gjithçka qĂ« ka qenĂ«, dhe duhet tĂ« pĂ«rditĂ«sohet
  • shto gjithçka qĂ« nuk ka qenĂ« ende

Pse duhet bërë operacionet në këtë rend? Sepse në këtë mënyrë përmasa e tabelës do të rritet minimalisht (mbani mend MVCC!).

DELETE FROM dst

Sigurisht, mund të bëni me vetëm dy operacione:

  • tĂ« fshijĂ« (FSHI) gjithçka
  • shto all nga imazhi i ri

Por nĂ« kĂ«tĂ« rast, falĂ« MVCC, pĂ«rmasa e tabelĂ«s do tĂ« rritet pikĂ«risht dyfish! TĂ« merrni +1M imazhe regjistrimesh nĂ« tabelĂ« pĂ«r shkak tĂ« pĂ«rditĂ«simit tĂ« 10K — nuk Ă«shtĂ« shumĂ« e mirĂ« pĂ«r shkak tĂ« tepricĂ«s


TRUNCATE dst

Një zhvillues më i experienced e di që të gjithë tabelën mund ta pastroni mjaft lirshëm:

  • pastro (TRUNCATE) tĂ« gjithĂ« tabelĂ«n
  • shto all nga imazhi i ri

Metoda Ă«shtĂ« efektive, ndonjĂ«herĂ« Ă«shtĂ« plotĂ«sisht e aplikueshme, por ka njĂ« problem
 Do tĂ« importojmĂ« 1M regjistrime pĂ«r njĂ« kohĂ« tĂ« gjatĂ«, kĂ«shtu qĂ« nuk mund tĂ« lejojmĂ« tabelĂ«n tĂ« jetĂ« bosh pĂ«r gjithĂ« kĂ«tĂ« kohĂ« (siç do tĂ« ndodhte pa e futur nĂ« njĂ« transaksion tĂ« vetme).

Kështu që:

  • na fillon njĂ« transaksion tĂ« gjatĂ«
  • TRUNCATE vendos AccessExclusive-bllokimin
  • ne tregojmĂ« ngadalĂ« futurin, dhe tĂ« gjithĂ« tĂ« tjerĂ«t nĂ« kĂ«tĂ« kohĂ« nuk mund tĂ« SELECT

ÇfarĂ« po ndodh qĂ« Ă«shtĂ« e keqe


ALTER TABLE
 RENAME
 / DROP TABLE 


Një mundësi është të ngarkohet gjithçka në një tavë të re dhe pastaj thjesht ta rinovoni në vendin e tavës së vjetër. Disa detaje të pakëndshme:

  • po ashtu AccessExclusive, edhe pse ndjeshĂ«m mĂ« pak nĂ« kohĂ«
  • do tĂ« humbasin tĂ« gjithĂ« planet e kĂ«rkesave/statistikĂ«s sĂ« kĂ«saj tavĂ« duhet tĂ« bĂ«jmĂ« ANALYZE
  • shkelen tĂ« gjitha çelĂ«sat e jashtĂ«m (FK) pĂ«r tavĂ«n

Ishte një patch WIP nga Simon Riggs, i cili propozoi të bëhej ALTER-operacion për të zëvendësuar trupin e tavës në nivelin e skedarit, pa prekur statistikën dhe FK, por nuk arriti të grumbullojë kuorum.

DELETE, UPDATE, INSERT

Pra, ndalojmë te opsioni jo-bllokues nga tre operacione. Pothuajse tre... Si ta bëjmë më efektivisht?

-- gjithçka bëhet në kuadër të një transaksioni, që askush të mos e shohë "gjendjet e ndërmjetme"
BEGIN;

-- krijojmë një tavë të përkohshme me të dhënat e importuara
CREATE TEMPORARY TABLE tmp(
  LIKE dst INCLUDING INDEXES -- sipas modelit, së bashku me indekset
) ON COMMIT DROP; -- jashtë transaksionit nuk na duhet

-- shpejt e shpejt ngjitim imazhin e ri përmes COPY
COPY tmp FROM STDIN;
-- ...
-- .

-- fshijmë ato që mungojnë
DELETE FROM
  dst D
USING
  dst X
LEFT JOIN
  tmp Y
    USING(pk1, pk2) -- fushat e çelësit primar
WHERE
  (D.pk1, D.pk2) = (X.pk1, X.pk2) AND
  Y IS NOT DISTINCT FROM NULL; -- "antijoin"

-- përditësojmë të mbeturit
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); -- nuk ka qenë e nevojshme të përditësojmë të njëjtat

-- injektojmë ato që mungojnë
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. Pastrimi i importit

Në të njëjtin KLADr, të gjitha regjistrimet e ndryshuara duhet të kalojnë gjithashtu përmes pastrimit - normalizimi, identifikimi i fjalëve kyçe, përshtatja në strukturat e nevojshme. Por si të dimë - çfarë saktësisht është ndryshuar, pa e komplikuar kodin e sinkronizimit, në ideale, duke mos e prekur fare?

Nëse qasja për shkrim në momentin e sinkronizimit është vetëm në procesin tuaj, atëherë mund të përdorni një trigger, i cili do të mbledhë të gjitha ndryshimet për ne:

-- tabelat celulare
CREATE TABLE kladr(...);
CREATE TABLE kladr_house(...);

-- tabelat me historinë ndryshimesh
CREATE TABLE kladr$log(
  ro kladr, -- këtu ndodhen imazhet e plota të regjistrimeve të vjetra/të reja
  rn kladr
);

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

-- funksioni i përgjithshëm për regjistrimin e ndryshimeve
CREATE OR REPLACE FUNCTION diff$log() RETURNS trigger AS $$
DECLARE
  dst varchar = TG_TABLE_NAME || '$log';
  stmt text = '';
BEGIN
  -- kontrollojmë nevojën për regjistrimin kur azhurnohet një regjistrim
  IF TG_OP = 'UPDATE' THEN
    IF NEW IS NOT DISTINCT FROM OLD THEN
      RETURN NEW;
    END IF;
  END IF;
  -- krijojmë një regjistrim logu
  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;

Tani mund t'i vendosim (ose t'i aktivizojmë nëpërmjet) trigget përpara fillimit të sinkronizimit 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();

Dhe pastaj mund të nxjerrim qetë të gjitha ndryshimet që na nevojiten nga tabelat log dhe t'i kalojmë përmes përpunuesve të tjerë.

3.3. Importimi i seteve të lidhura

Më sipër kemi shqyrtuar rastet kur struktura e të dhënave të burimit dhe të pranimit përputhen. Por çfarë të bëjmë, nëse eksporti nga sistemi i jashtëm ka një format të ndryshëm nga struktura e ruajtjes në bazën tonë?

Të marrim për shembull ruajtjen e klientëve dhe faturave për ta, një variant klasik "marrëdhënie shumë-në-një":

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)
);

Ndërkaq, eksporti nga një burim të jashtëm na vjen në formën "gjithçka në një":

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

Sigurisht, të dhënat për klientët mund të jenë të dyfishta në këtë variant, dhe regjistrimi kryesor është "fatura":

0123456789;Vasja;A-01;2020-03-16;1000.00
9876543210;Petja;A-02;2020-03-16;666.00
0123456789;Vasja;B-03;2020-03-16;9999.00

PĂ«r modelin, thjesht do tĂ« insertojmĂ« tĂ« dhĂ«nat tona testuese, por mbajmĂ« mend — COPY mĂ« efikase!

INSERT INTO invoice_import
VALUES
  ('0123456789', 'Vasja', 'A-01', '2020-03-16', 1000.00)
, ('9876543210', 'Petja', 'A-02', '2020-03-16', 666.00)
, ('0123456789', 'Vasja', 'B-03', '2020-03-16', 9999.00);

Së pari, do të ndjekim ato "shkurtimet", për të cilat "faktet" tona iu referohen. Në rastin tonë, faturat iu referohen klientëve:

KRIJONI TABELË TEMPORALE client_import AS
SELECT DISTINCT ON(client_inn)
-- mund të përdorim thjesht SELECT DISTINCT, nëse të dhënat janë pa kundërshtime
  client_inn inn
, client_name "name"
FROM
  invoice_import;

Për të lidhur saktë faturat me ID-të e klientëve, na nevojiten fillimisht këto identifikues ose duhet të gjenerohen. Do të shtojmë fusha për to:

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

Do tĂ« pĂ«rdorim metodĂ«n e pĂ«rmendur mĂ« sipĂ«r pĂ«r sinkronizimin e tabelave me njĂ« korrigjim tĂ« vogĂ«l — nuk do tĂ« pĂ«rditĂ«sojmĂ« ose fshijmĂ« asgjĂ« nĂ« tabelĂ«n e destinacionit, sepse importi i klientĂ«ve Ă«shtĂ« "append-only":

-- vendosim në tabelën e importit ID-të e regjistrimeve tashmë ekzistuese
UPDATE
  client_import T
SET
  client_id = D.client_id
FROM
  client D
WHERE
  T.inn = D.inn; -- çelësi unik

-- shtojmë regjistrimet që mungojnë dhe vendosim ID-të e tyre
ME ins AS (
  INSERT INTO client(
    inn
  , name
  )
  SELECT
    inn
  , name
  FROM
    client_import
  WHERE
    client_id ËSHTË NULL -- nĂ«se ID nuk Ă«shtĂ« vendosur
  KTHE
)
UPDATE
  client_import T
SET
  client_id = D.client_id
FROM
  ins D
WHERE
  T.inn = D.inn; -- çelësi unik

-- vendosim ID-të e klientëve për regjistrimet e faturave
UPDATE
  invoice_import T
SET
  client_id = D.client_id
FROM
  client_import D
WHERE
  T.client_inn = D.inn; -- çelësi aplikativ

Në thelb, gjithçka është në invoice_import tani fusha e lidhjes është e mbushur client_id, me të cilin do ta vendosim faturën.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster