Arendaja peab perioodiliselt edastama pĂ€ringus teatud parameetrid vĂ”i isegi terve valiku «sisendisse». MĂ”nikord on selle ĂŒlesande lahendused vĂ€ga kummalised. Alustame âvastupidiseltâ ja vaatame, kuidas mitte teha, miks ja kuidas saab paremini.

Otsene âsĂŒstitudâ vÀÀrtuste lisamine pĂ€ringu kehasse
Tavaliselt nÀeb see vÀlja umbes nii:
query = "SELECT * FROM tbl WHERE id = " + value
⊠vÔi nii:query = "SELECT * FROM tbl WHERE id = :param".format(param=value)
Sellest meetodist on rÀÀgitud, kirjutatud jaisegi joonistatud Peaaegu alati viib see -

otse SQL sĂŒstimise teele ja liigse koormuseni Ă€ri loogikas, mis peab âliimimaâ teie pĂ€ringu rea. Osaliselt on see lĂ€henemine Ă”igustatud ainult juhul, kui on vaja
kasutada jaotust PostgreSQL 10 ja vanemates versioonides efektiivse plaani saamiseks. Nendes versioonides mÀÀratakse skaneeritavate sektsioonide loetelu enne edastatavaid parameetreid, ainult pÀringu keha alusel. $n-argumendid
kohtade hoidjad
Kasutamine PREPARED STATEMENTS Muutuva arvu argumente
Probleemid ootavad meid, kui soovime edastada ette teadmata arvu argumente:
... id IN ($1, $2, $3, ...) -- $1 : 2, $2 : 3, $3 : 5, ...
Kui jĂ€tta pĂ€ring selliseks, siis see hoiab meid kĂŒll potentsiaalsete sĂŒstide eest, kuid toob endaga kaasa vajaduse pĂ€ringu liimimiseks/analĂŒĂŒsimiseksiga variandi jaoks argumendi arvust. See on juba parem kui teha seda iga kord, kuid selleta on vĂ”imalik hakkama saada.Piisab, kui edastada vaid ĂŒks parameeter, mis sisaldab
sĂŒserialiseeritud massiivi esitlemist ... id = ANY($1::integer[]) -- $1 : '{2,3,5,8,13}':
Ainus erinevus on vajadus selgelt muundada argument soovitud massiivi tĂŒĂŒbiks. Kuid see ei tekita probleeme, kuna me juba teame, kuhu suuname.Valiku (maatriksi) edastamine
Tavaliselt on need erinevad variandid andmekogumite edastamiseks andmebaasi âĂŒhe pĂ€ringugaâ:
INSERT INTO tbl(k, v) VALUES($1,$2),($3,$4),...
Lisaks eespool kirjeldatud probleemidele âliimimisegaâ vĂ”ib see meid veelgi viiamĂ€lu ĂŒletamiseni out of memory ja serveri langemisele. PĂ”hjus on lihtne â PG reservib tĂ€iendavat mĂ€lu, ning ridade arv komplektis on piiratud ainult rakenduse Ă€riloogika soovidega. Erakordsetel juhtudel on nĂ€htud «numbrilised» argumendid ĂŒle $9000 â Ă€ra nii tee.
Korrigeerime pÀringu, 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, juhul kui massiivi sees on «keerukamad» vÀÀrtused, tuleb need ĂŒmber ringi panna jutumĂ€rkidesse.
On selge, et sellisel viisil saab «lahti vÔetakse» valiku, millel on meelevaldne arv vÀlju.
unnest, unnest, âŠ
Perioodiliselt esinevad variandid, kus «massivÀli» asemel edastatakse mitu «veergude massiivi», millest ma varem rÀÀkisin :
SELECT
unnest($1::text[]) k
, unnest($2::integer[]) v;Sellise meetodi puhul, kui teete vigu erinevate veergude vÀÀrtuste loomiseks, on vĂ€ga lihtne saada tĂ€iesti ĂŒllatavaid tulemusi, 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 on PostgreSQL-is ilmunud tĂ€isfunktsionaalsed funktsioonid JSON-tĂŒĂŒbi kĂ€sitlemiseks. SeetĂ”ttu, kui teie sisendi parameetrite mÀÀramine toimub brauseris, saate seal otse luua JSON-objekt SQL pĂ€ringu jaoks:
SELECT
key k
, value v
FROM
json_each($1::json); -- '{"a":1,"b":2,"c":3,"d":4}'Eelnevate versioonide jaoks saab sama meetodit kasutada each(hstore), kuid korrektne «kokkuvÔte» keerukate objektide majandamisega hstore'is vÔib tekitada probleeme.
json_populate_recordset
Kui teate eelnevalt, et andmed «sisenemise» JSON-massiivist lĂ€hevad mĂ”ne tabeli tĂ€itmiseks, saate oluliselt kokku hoida «de-referentsite» vĂ€ljade mÀÀramisel ja sobivate tĂŒĂŒpide konverteerimisel, 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
See funktsioon lihtsalt «lahti vÔetakse» edastatud objekti massiiv valikuks, tuginedes 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 | 2AJUTINE TABEL
Aga kui edastatava valiku andmemaht on vĂ€ga suur, siis selle laskmine ĂŒhte serialiseeritud parameetrisse â on keeruline, ja mĂ”nikord isegi vĂ”imatu, kuna see nĂ”uab ĂŒhekordset suurte mĂ€lu mahtude eraldamistNĂ€iteks, kui teil on vaja kaua-kaua koguda suur andmepakett vĂ€lisest sĂŒsteemist ja siis tahate selle korraga andmebaasis töödelda.
Sellisel juhul on parim lahendus :
CREATE TEMPORARY TABLE tbl(k text, v integer);
...
INSERT INTO tbl(k, v) VALUES($1, $2); -- korrata palju-palju kordi
...
-- siin teeme midagi kasulikku terve selle tabeliga
Meetod on hea just harvade suurte andmemahtude edastamiseks andmeid.
Andmestruktuuri kirjeldamise seisukohalt erineb ajutine tabel âtavalisestâ ainuselt ĂŒhe tunnuse poolest sĂŒsteemitabelis pg_classtoob kaasa selle, et "saadetamise" hetkel server ei ole veel kĂ€ivitatud ja kĂ”ik lĂ€heb katki. pg_type, pg_depend, pg_attribute, pg_attrdef, ⊠â muidu ei erinegi.
SeetĂ”ttu genereerib veebisĂŒsteemides, kus on palju lĂŒhiajalisi ĂŒhendusi, iga kord nende jaoks selline tabel uusi sĂŒsteemi registreeringuid, mis kustutatakse andmebaasiga ĂŒhenduse lĂ”petamisel. Tulemuseks on, kontrollimatu TEMP TABLE kasutamine viib pg_catalog'i tabelite âpaisumiseniâ ja paljude nende kasutavate operatsioonide aeglustumiseni.
Loomulikult on selle vastu vĂ”imalik vĂ”idelda aeg-ajalt VACUUM FULL'i lĂ€biviimisega sĂŒsteemi katalooge tabeleid.
Sessiooni muutujad
Oletame, et varasema juhtumi andmete töötlemine on piisavalt keeruline ĂŒhe SQL-kĂ€sku jaoks, kuid tahame seda teha piisavalt sageli. See tĂ€hendab, et soovime kasutada protseduurilist töötlemist , kuid andmete edastamine ajutiste tabelite kaudu on liiga kulukas.
$n-parameetrite kasutamine anonĂŒĂŒmse ploki andmete edastamiseks ei ole samuti vĂ”imalik. Lahenduseks on meil sessiooni muutujad ja funktsioon current_setting.
Enne versiooni 9.2 oli vajalik eelnevalt konfigureerida custom_variable_classes oma sessiooni muutujate jaoks. KÀsitles ajakohastes versioonides saab kirjutada umbkaudu jÀrgmiselt:
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 : 3Teistes toetatud protseduurilistes keeltes vÔib leida ka muid lahendusi.
Kas teate veel viise? Jagage oma mÔtteid kommentaarides!
Allikas: habr.com
