Herë pas here, zhvilluesi ka nevojë kalojë një grup parametrash apo edhe një përzgjedhje të tërë në kërkesë "në hyrje". Ndonjëherë ka zgjidhje shumë të çuditshme për këtë problem.
Le të shkojmë "nga e kundërta" dhe të shohim se si të mos e bëjmë atë, pse dhe si mund ta bëni më mirë.
"Futja" e drejtpërdrejtë e vlerave në trupin e kërkesës
Zakonisht duket diçka si kjo:
query = "SELECT * FROM tbl WHERE id = " + value
... ose si kjo:
query = "SELECT * FROM tbl WHERE id = :param".format(param=value)
Për këtë metodë thuhet, shkruhet dhe
Pothuajse gjithmonë është rruga e drejtpërdrejtë për injektimin SQL dhe një ngarkesë shtesë në logjikën e biznesit, e cila detyrohet të "ngjisë" vargun tuaj të pyetjeve.
Kjo qasje mund të justifikohet pjesërisht vetëm nëse është e nevojshme. përdorni ndarjen në PostgreSQL versionet 10 dhe më poshtë për një plan më efikas. Në këto versione, lista e seksioneve të skanuara përcaktohet pa marrë parasysh parametrat e transmetuar, vetëm në bazë të trupit të kërkesës.
$n argumente
Përdorim
Numri i ndryshueshëm i argumenteve
Problemet do të na presin kur duam të kalojmë paraprakisht një numër të panjohur argumentesh:
... id IN ($1, $2, $3, ...) -- $1 : 2, $2 : 3, $3 : 5, ...
Nëse e lini kërkesën në këtë formë, atëherë megjithëse do të na shpëtojë nga injeksionet e mundshme, ajo prapë do të çojë në nevojën për të ngjitur / analizuar kërkesën për çdo opsion nga numri i argumenteve. Tashmë është më mirë sesa ta bësh çdo herë, por mund të bësh edhe pa të.
Mjafton të kalohet vetëm një parametër që përmban paraqitje e serializuar e një vargu:
... id = ANY($1::integer[]) -- $1 : '{2,3,5,8,13}'
Dallimi i vetëm është nevoja për të kthyer në mënyrë eksplicite argumentin në llojin e dëshiruar të grupit. Por kjo nuk shkakton probleme, pasi ne e dimë paraprakisht se ku po drejtohemi.
Transferimi i mostrës (matricë)
Zakonisht këto janë të gjitha llojet e opsioneve për transferimin e grupeve të të dhënave për futje në bazën e të dhënave "në një kërkesë":
INSERT INTO tbl(k, v) VALUES($1,$2),($3,$4),...
Përveç problemeve të përshkruara më sipër me “ringjitjen” e kërkesës, kjo mund të na çojë edhe në jashtë kujtesës dhe dështimi i serverit. Arsyeja është e thjeshtë - PG rezervon memorie shtesë për argumentet, dhe numri i regjistrimeve në grup është i kufizuar vetëm nga aplikacioni i logjikës së biznesit Wishlist. Në raste veçanërisht klinike ishte e nevojshme të shihej argumente "të numëruara" më të mëdha se 9000 dollarë - mos e bëni në këtë mënyrë.
Le të rishkruajmë pyetjen, duke aplikuar tashmë serializimi "me dy nivele".:
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;
Po, në rastin e vlerave "komplekse" brenda një grupi, ato duhet të përshtaten me thonjëza.
Është e qartë se në këtë mënyrë ju mund të "zgjeroni" përzgjedhjen me një numër arbitrar fushash.
çrregullt, çrregullt,…
Herë pas here ka mundësi për të kaluar në vend të një "vargu vargjesh" disa "vargje kolonash" që përmenda
SELECT
unnest($1::text[]) k
, unnest($2::integer[]) v;
Me këtë metodë, nëse bëni një gabim kur krijoni lista vlerash për kolona të ndryshme, është shumë e lehtë të merrni plotësisht rezultate të papritura, të cilat gjithashtu varen nga versioni i serverit:
-- $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
Duke filluar nga versioni 9.3, PostgreSQL ka funksione të plota për të punuar me llojin json. Prandaj, nëse parametrat tuaj të hyrjes janë të përcaktuara në shfletues, mund të formoni pikërisht atje json objekt për pyetjen SQL:
SELECT
key k
, value v
FROM
json_each($1::json); -- '{"a":1,"b":2,"c":3,"d":4}'
Për versionet e mëparshme, e njëjta metodë mund të përdoret secili (hstore), por "palosja" e saktë me ikjen e objekteve komplekse në hstore mund të shkaktojë probleme.
json_populate_recordset
Nëse e dini paraprakisht se të dhënat nga grupi "input" json do të shkojnë për të plotësuar një tabelë, mund të kurseni shumë në fushat "dereferencing" dhe transmetimin në llojet e dëshiruara duke përdorur funksionin 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
Dhe ky funksion thjesht do të "zgjerojë" grupin e kaluar të objekteve në një përzgjedhje, pa u mbështetur në formatin e tabelës:
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 | 2
TABELA E PËRKOHSHME
Por nëse sasia e të dhënave në mostrën e transmetuar është shumë e madhe, atëherë hedhja e saj në një parametër të serializuar është e vështirë, dhe ndonjëherë e pamundur, pasi kërkon një kohë alokim i madh i memories. Për shembull, ju duhet të grumbulloni një grup të madh të dhënash ngjarjesh nga një sistem i jashtëm për një kohë të gjatë dhe të gjatë dhe më pas dëshironi t'i përpunoni ato një herë në anën e bazës së të dhënave.
Në këtë rast, zgjidhja më e mirë do të ishte përdorimi
CREATE TEMPORARY TABLE tbl(k text, v integer);
...
INSERT INTO tbl(k, v) VALUES($1, $2); -- повторить много-много раз
...
-- тут делаем что-то полезное со всей этой таблицей целиком
Metoda është e mirë për transmetim të rrallë të vëllimeve të mëdha të dhëna.
Nga pikëpamja e përshkrimit të strukturës së të dhënave të saj, një tabelë e përkohshme ndryshon nga një tabelë "e rregullt" vetëm në një veçori. në tabelën e sistemit pg_classdhe në pg_type, pg_varet, pg_atribute, pg_attrdef, ... - dhe asgjë fare.
Prandaj, në sistemet ueb me një numër të madh lidhjesh jetëshkurtër për secilën prej tyre, një tabelë e tillë do të gjenerojë çdo herë regjistrime të reja të sistemit, të cilat fshihen kur lidhja me bazën e të dhënave mbyllet. Përfundimisht, përdorimi i pakontrolluar i TEMP TABLE çon në "ënjtje" të tabelave në pg_catalog dhe duke ngadalësuar shumë operacione që i përdorin ato.
Sigurisht, kjo mund të luftohet kalim periodik VACUUM FULL sipas tabelave të katalogut të sistemit.
Variablat e sesionit
Supozoni se përpunimi i të dhënave nga rasti i mëparshëm është mjaft kompleks për një pyetje të vetme SQL, por ju dëshironi ta bëni atë mjaft shpesh. Kjo do të thotë, ne duam të përdorim përpunimin procedural në
Ne gjithashtu nuk mund të përdorim $n-parametra për të kaluar në një bllok anonim. Variablat e sesionit dhe funksioni do të na ndihmojnë të dalim nga situata. përcaktimi_aktual.
Përpara versionit 9.2, duhej të konfiguroheshe paraprakisht
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
Ekzistojnë zgjidhje të tjera të disponueshme në gjuhë të tjera procedurale të mbështetura.
Di më shumë mënyra? Ndani në komente!
Burimi: www.habr.com