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 - అంటే, లావాదేవీ పూర్తయినప్పుడు, పట్టిక స్వయంచాలకంగా తొలగించబడుతుంది.

ప్రతిరూపం కానిది

అవి నిర్దిష్ట కనెక్షన్‌కు మాత్రమే చెందినందున, తాత్కాలిక పట్టికలు ప్రతిరూపం చేయబడవు. కానీ ఇది డేటా యొక్క డబుల్ రికార్డింగ్ అవసరాన్ని తొలగిస్తుంది హీప్ + వాల్‌లో, దానిలో ఇన్సర్ట్/అప్‌డేట్/డిలీట్ చేయడం చాలా వేగంగా జరుగుతుంది.

కానీ తాత్కాలిక పట్టిక ఇప్పటికీ "దాదాపు సాధారణ" పట్టికగా ఉన్నందున, అది ప్రతిరూపంలో కూడా సృష్టించబడదు. కనీసం ఇప్పటికైనా, సంబంధిత ప్యాచ్ చాలా కాలంగా తిరుగుతున్నప్పటికీ.

1.2 అన్‌లాగ్డ్ టేబుల్

కానీ మీరు ఏమి చేయాలి, ఉదాహరణకు, మీరు ఒక లావాదేవీలో అమలు చేయలేని ఒక రకమైన గజిబిజిగా ఉండే ETL ప్రక్రియను కలిగి ఉంటే, కానీ మీకు ఇంకా ఉంది లావాదేవీ మోడ్‌లో pgbouncer? ..

లేదా డేటా ప్రవాహం చాలా పెద్దది ఒక కనెక్షన్‌లో తగినంత బ్యాండ్‌విడ్త్ లేదు డేటాబేస్ నుండి (చదవడానికి, ఒక CPUకి ఒక ప్రక్రియ)?..

లేదా కొన్ని ఆపరేషన్లు జరుగుతున్నాయి అసమకాలికంగా వేర్వేరు కనెక్షన్లలో? ..

ఇక్కడ ఒకే ఒక ఎంపిక ఉంది - తాత్కాలికంగా కాని తాత్కాలిక పట్టికను సృష్టించండి. పన్, అవును. అంటే:

  • ఎవరితోనూ కలవకుండా గరిష్టంగా యాదృచ్ఛిక పేర్లతో "నా స్వంత" పట్టికలను సృష్టించింది
  • సారం: వాటిని బాహ్య మూలం నుండి డేటాతో నింపింది
  • ట్రాన్స్ఫారమ్: మార్చబడింది, కీ లింకింగ్ ఫీల్డ్‌లలో పూరించబడింది
  • లోడ్: సిద్ధంగా ఉన్న డేటాను లక్ష్య పట్టికలలోకి పోశారు
  • "నా" పట్టికలు తొలగించబడ్డాయి

మరియు ఇప్పుడు - లేపనం లో ఒక ఫ్లై. నిజానికి, PostgreSQLలో అన్ని వ్రాతలు రెండుసార్లు జరుగుతాయి - WALలో మొదటిది, ఆపై టేబుల్/ఇండెక్స్ బాడీలలోకి. ACIDకి మద్దతు ఇవ్వడానికి మరియు మధ్య డేటా విజిబిలిటీని సరిచేయడానికి ఇదంతా జరుగుతుంది COMMIT'నట్టి మరియు ROLLBACK'శూన్య లావాదేవీలు.

కానీ మాకు ఇది అవసరం లేదు! మాకు మొత్తం ప్రక్రియ ఉంది ఇది పూర్తిగా విజయవంతమైంది లేదా కాదు.. ఎన్ని ఇంటర్మీడియట్ లావాదేవీలు ఉన్నాయనేది పట్టింపు లేదు - ప్రత్యేకించి అది ఎక్కడ ఉందో స్పష్టంగా తెలియనప్పుడు “మధ్య నుండి ప్రక్రియను కొనసాగించడం”పై మాకు ఆసక్తి లేదు.

దీన్ని చేయడానికి, PostgreSQL డెవలపర్లు, వెర్షన్ 9.1లో తిరిగి, అటువంటి విషయాన్ని ప్రవేశపెట్టారు UNLOGGED పట్టికలు:

ఈ సూచనతో, పట్టిక అన్‌లాగ్ చేయబడినట్లుగా సృష్టించబడుతుంది. అన్‌లాగ్ చేయని పట్టికలకు వ్రాసిన డేటా రైట్-ఎహెడ్ లాగ్ గుండా వెళ్ళదు (చాప్టర్ 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. ఎలా ప్రాసెస్ చేయాలి?

కాబట్టి, మన పరిచయాన్ని ఇలా చూద్దాం:

  • మీ డేటాబేస్‌లో నిల్వ చేయబడిన క్లయింట్ డేటాతో మీకు పట్టిక ఉంది 1M రికార్డులు
  • ప్రతి రోజు ఒక క్లయింట్ మీకు కొత్తది పంపుతుంది పూర్తి "చిత్రం"
  • అనుభవం నుండి మీరు ఎప్పటికప్పుడు తెలుసు 10K కంటే ఎక్కువ రికార్డులు మార్చబడవు

అటువంటి పరిస్థితికి ఒక క్లాసిక్ ఉదాహరణ KLADR బేస్ — మొత్తం చిరునామాలు చాలా ఉన్నాయి, కానీ ప్రతి వారపు అప్‌లోడ్‌లో జాతీయ స్థాయిలో కూడా చాలా తక్కువ మార్పులు (స్థావరాల పేరు మార్చడం, వీధులను కలపడం, కొత్త ఇళ్ల రూపాన్ని) ఉన్నాయి.

3.1 పూర్తి సమకాలీకరణ అల్గోరిథం

సరళత కోసం, మీరు డేటాను పునర్నిర్మించాల్సిన అవసరం లేదని చెప్పండి - పట్టికను కావలసిన రూపంలోకి తీసుకురండి, అంటే:

  • తొలగిస్తాయి ఇకపై లేని ప్రతిదీ
  • రిఫ్రెష్ ఇప్పటికే ఉన్న ప్రతిదీ మరియు నవీకరించబడాలి
  • చొప్పించు ఇంకా జరగని ప్రతిదీ

ఈ క్రమంలో ఆపరేషన్లు ఎందుకు చేయాలి? ఎందుకంటే ఈ విధంగా పట్టిక పరిమాణం కనిష్టంగా పెరుగుతుంది (MVCCని గుర్తుంచుకో!).

dst నుండి తొలగించు

లేదు, వాస్తవానికి మీరు కేవలం రెండు కార్యకలాపాలతో పొందవచ్చు:

  • తొలగిస్తాయి (DELETE) సాధారణంగా ప్రతిదీ
  • చొప్పించు అన్నీ కొత్త చిత్రం నుండి

అయితే అదే సమయంలో, MVCCకి ధన్యవాదాలు, పట్టిక పరిమాణం సరిగ్గా రెండుసార్లు పెరుగుతుంది! 1K అప్‌డేట్ కారణంగా పట్టికలో +10M రికార్డ్‌ల చిత్రాలను పొందడం చాలా రిడెండెన్సీ...

కత్తిరించిన dst

మొత్తం టాబ్లెట్‌ను చాలా చౌకగా శుభ్రం చేయవచ్చని మరింత అనుభవజ్ఞుడైన డెవలపర్‌కు తెలుసు:

  • శుభ్రంగా (TRUNCATE) మొత్తం పట్టిక
  • చొప్పించు అన్నీ కొత్త చిత్రం నుండి

పద్ధతి ప్రభావవంతంగా ఉంటుంది, కొన్నిసార్లు చాలా వర్తిస్తుంది, కానీ ఒక సమస్య ఉంది... మేము చాలా కాలం పాటు 1M రికార్డ్‌లను జోడిస్తాము, కాబట్టి మేము ఈ సమయమంతా టేబుల్‌ని ఖాళీగా ఉంచలేము (ఒకే లావాదేవీలో చుట్టకుండా జరుగుతుంది).

ఏమిటంటే:

  • మేము ప్రారంభిస్తున్నాము దీర్ఘకాల లావాదేవీ
  • TRUNCATE విధిస్తుంది యాక్సెస్ ఎక్స్‌క్లూజివ్- నిరోధించడం
  • మేము చొప్పించడం చాలా కాలం పాటు చేస్తాము మరియు ఈ సమయంలో అందరూ కుదరదు SELECT

ఏదో బాగా జరగడం లేదు...

టేబుల్ మార్చు... పేరు మార్చు... / డ్రాప్ టేబుల్...

ఒక ప్రత్యామ్నాయం ఏమిటంటే, అన్నింటినీ ప్రత్యేక కొత్త పట్టికలో నింపి, ఆపై పాత దాని స్థానంలో పేరు మార్చడం. కొన్ని అసహ్యకరమైన చిన్న విషయాలు:

  • ఇప్పటికీ కూడా యాక్సెస్ ఎక్స్‌క్లూజివ్, గణనీయంగా తక్కువ సమయం ఉన్నప్పటికీ
  • ఈ పట్టిక కోసం అన్ని ప్రశ్న ప్రణాళికలు/గణాంకాలు రీసెట్ చేయబడ్డాయి, విశ్లేషణను అమలు చేయాలి
  • అన్ని విదేశీ కీలు విరిగిపోయాయి (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

ఒక వ్యాఖ్యను జోడించండి