Aeg-ajalt tekib arendajal vajadus edastada pÀringusse hulk parameetreid vÔi isegi kogu valik «sisse». MÔnikord kohtab vÀga kummalisi lahendusi selle probleemi jaoks.

Siit alustame «vastupidiselt» ja vaatame, kuidas ei peaks tegema, miks ja kuidas vÔiks paremini teha.
Otsene «sisestamine» vÀÀrtustest pÀringusse
NÀeb tavaliselt vÀlja umbes nii:
query = "SELECT * FROM tbl WHERE id = " + value⊠vÔi nii:
query = "SELECT * FROM tbl WHERE id = :param".format(param=value)Selle meetodi kohta on öeldud, kirjutatud ja kĂŒllalt:

Peaaegu alati on see â otsene tee SQL-i sĂŒstimistesse ja liigsele koormusele Ă€riloogikas, mis peab âliimimaâ teie pĂ€ringu stringi.
Osaliselt vÔib selline lÀhenemine olla Ôigustatud ainult juhul, kui on vajalik partitsioneerimine PostgreSQL 10 ja varasemates versioonides efektiivsema plaani saamiseks. Nendes versioonides mÀÀratakse skaneeritavate partitsioonide nimekiri veel enne edastatavate parameetrite arvestamist, vaid ainult pÀringu keha pÔhjal.
$n-argumendid
Kasutamine parameetrid â see on hea, see vĂ”imaldab kasutada , vĂ€hendades koormust nii Ă€riloogikale (pĂ€ringu rida vormitakse ja edastatakse vaid kord) kui ka andmebaasi serverile (ei ole vajalik iga pĂ€ringu esinemise jaoks uuesti analĂŒĂŒsida ja planeerida).
Muutuv argumentide arv
Probleemid ootavad meid, kui soovime edastada ettenÀgematult suurt hulka argumente:
... id IN ($1, $2, $3, ...) -- $1 : 2, $2 : 3, $3 : 5, ...Kui jĂ€tta pĂ€ring selliseks, siis see kuigi kaitseb meid vĂ”imalike sĂŒstide eest, viib see siiski vajaduseni liita/analĂŒĂŒsida pĂ€ringut iga variandi jaoks argumentide arvust. Juba parem kui iga kord nii teha, kuid saaksime ka ilma selleta hakkama.
Piisab, kui edastada vaid ĂŒks parameeter, mis sisaldab serialiseeritud massiivi esitust:
... id = ANY($1::integer[]) -- $1 : '{2,3,5,8,13}'Ainus erinevus on vajadus selgelt muuta argument soovitud massiivi tĂŒĂŒbiks. Kuid see ei tekita probleeme, kuna me teame eelnevalt, kuhu me suundume.
Valimise edastamine (maatriks)
Tavaliselt on see erinevad variandid andmekogumite edastamiseks, et sisestada neid andmebaasi âĂŒhe pĂ€ringugaâ:
INSERT INTO tbl(k, v) VALUES($1,$2),($3,$4),...Lisaks eespool kirjeldatud pĂ€ringu "ĂŒmberkleebimise" probleemidele vĂ”ib see meid viia ka mĂ€lupuudus ja serveri kukkumiseni. PĂ”hjus on lihtne â PG reservib argumentide jaoks tĂ€iendavat mĂ€lu, samas kui kirjeid komplektis piirab ainult Ă€ritegevuse loogika nĂ”udmised. Eriti ÀÀrmuslikes juhtumites on tulnud nĂ€ha "numbrilisi" argumente, mis ĂŒletavad $9000 â nii ei tohi teha.
Kirjutame pÀringu uuesti, rakendades juba "kaheastmelist" serialiseerimist:
INSERT INTO tbl
SELECT
unnest[1]::text k
, unnest[2]::integer v
FROM (
SELECT
unnest($1::text[])::text[] -- $1 : '{"{a,1}","{b,2}","{c,3}","{d,4}"}'
) T;
Jah, juhtudel, kus massi sees on "keerulisi" vÀÀrtusi, tuleb neid sulgudesse panna.
On selge, et sellisel viisil saab "laiendada" valikut suvalise arvu vÀljadega.
unnest, unnest, âŠ
Aeg-ajalt on erandeid, kus "masside massi" asemel edastatakse mitu "veergude massi", millest ma rÀÀkisin :
SELECT
unnest($1::text[]) k
, unnest($2::integer[]) v;Sellise meetodi puhul, vale vÀÀrtuste loendite genereerimise korral erinevatele veergudele, on vÀga lihtne saada tÀiesti ootamatud tulemused, mis sÔltuvad ka serveri versioonist:
-- $1 : '{a,b,c}', $2 : '{1,2}'
-- PostgreSQL 9.4
k | v
-----
a | 1
b | 2
c | 1
a | 2
b | 1
c | 2
-- PostgreSQL 11
k | v
-----
a | 1
b | 2
c |JSON
Alates versioonist 9.3 lisandus PostgreSQL-is tĂ€ielikud funktsioonid JSON-tĂŒĂŒbi töötlemiseks. Seega, kui sisendparameetrite mÀÀramine toimub brauseris, saate seal ka otse kujundada JSON-objekt SQL-pĂ€ringuks:
SELECT
key k
, value v
FROM
json_each($1::json); -- '{"a":1,"b":2,"c":3,"d":4}'Eelmiste versioonide puhul saab kasutada sama meetodit each(hstore), kuid keerukate objektide hstore'iga tÔlgendamisel vÔivad tekkida probleemid.
json_populate_recordset
Kui te juba teadsite, et âsisendâ JSON-massiivist andmed lĂ€hevad mĂ”ne tabeli tĂ€itmiseks, siis saate vĂ€ltida âdereferenseerimiseâ ja vajalike tĂŒĂŒpide muutmisega palju sÀÀsta, kasutades funktsiooni json_populate_recordset:
SELECT
*
FROM
json_populate_recordset(
NULL::pg_class
, $1::json -- $1 : '[{"relname":"pg_class","oid":1262},{"relname":"pg_namespace","oid":2615}]'
);json_to_recordset
Ja see funktsioon lihtsalt "avab" edastatud objektide massiivi valikusse, toetumata tabeli formaadile:
SELECT
*
FROM
json_to_recordset($1::json) T(k text, v integer);
-- $1 : '[{"k":"a","v":1},{"k":"b","v":2}]'
k | v
-----
a | 1
b | 2TEMPORARY TABLE
Kuid kui edastatavas andmevalimis andmete maht on vĂ€ga suur, siis on keeruline vĂ”i mĂ”nikord vĂ”imatu panna seda ĂŒhte serialiseeritud parameetrisse, kuna see nĂ”uab suurt mĂ€lu erakordset mĂ€lu. NĂ€iteks peate pika aja jooksul koguma suurt andmepaketti vĂ€lisest sĂŒsteemist ning siis soovite selle korraga andmebaasis töödelda.
Sel juhul on parim lahendus :
CREATE TEMPORARY TABLE tbl(k text, v integer);
...
INSERT INTO tbl(k, v) VALUES($1, $2); -- korrata palju-muid kordi
...
-- siin teeme midagi kasulikku kogu selle tabeliga
See meetod sobib hÀsti harvade suurte andmeedastuste jaoks. andmete.
Andmestruktuuri kirjeldamise osas erineb ajutine tabel "tavalisest" ainult ĂŒhe tunnuse poolest sĂŒsteemitabelis pg_class, ja seejĂ€rel mÀÀratleme selle, takistades seelĂ€bi kasutajal selgelt selle vĂ€ljaid muuta. See on ĂŒks andmete peitmise mustritest pg_type, pg_depend, pg_attribute, pg_attrdef, ⊠â ning ei erine muust.
SeetĂ”ttu tekitab veebisĂŒsteemides, kus on palju lĂŒhiajalisi ĂŒhendusi, igaĂŒhe jaoks selline tabel uusi sĂŒsteemikirjeid iga kord, mis kustutatakse andmebaasiĂŒhenduse sulgemisel. LĂ”ppkokkuvĂ”ttes, kontrollimatute TEMP TABLE'i kasutamine toob kaasa tabelite "paisumise" pg_catalogis ja aeglustab mitmeid operatsioone, mis neid kasutavad.
Muidugi, selle vastu saab vĂ”idelda sĂŒsteemikatalooge tĂŒĂŒbid VACUUM FULLi perioodilise lĂ€bimisega ĂŒle.
Seansi muutujad
Oletame, et eelneva juhtumi andmetöötlus on piisavalt keeruline, et seda ĂŒhe SQL pĂ€ringuga teha, kuid soovime seda piisavalt tihti kasutada. Seega soovime kasutada protseduurilist töötlemist , kuid andmete edastamine ajutiste tabelite kaudu oleks liiga kallis.
$n-parameetrite kasutamine andmete edastamiseks anonĂŒĂŒmsesse plokki ei Ă”nnestu samuti. Seansi muutujad ja funktsioon current_setting.
Pe Before version 9.2, it was necessary to configure in advance a special namespace oma seansimuutujate jaoks. KÀesolevatel versioonidel saab kirjutada enam-vÀhem nii: SET my.val = '{1,2,3}'; DO $$ DECLARE id integer; BEGIN FOR id IN (SELECT unnest(current_setting('my.val')::integer[])) LOOP RAISE NOTICE 'id : %', id; END LOOP; END; $$ LANGUAGE plpgsql; -- NOTICE: id : 1 -- NOTICE: id : 2 -- NOTICE: id : 3
Teistes toetatud protseduurilistes keeltes vÔib leida ka muid lahendusi.Teistel toetatud protseduuriliselt keeltes vÔib leida ka muid lahendusi.
Kas tead veel viise? Jagage kommentaarides!
Allikas: habr.com
