सेक्शनिंग: शॉट और भूल गए

आप सेक्शनिंग के बारे में बहुत सारी जानकारी प्राप्त कर सकते हैं , विशेष रूप से , आप सिद्धांत के बारे में पढ़ सकते हैं, और फिर लेखक विचार विकसित करता है और जल्दी से एक अनुभाग जोड़ने के लिए अपना समाधान प्रदान करता है। मैं आपको खुद को परिचित करने की सलाह देता हूं।
सिद्धांत का अध्ययन करने के बाद, लगभग हर कोई अनुभाग बनाने की प्रक्रिया को स्वचालित करने के विचार के साथ आता है। उपरोक्त विकल्पों में से एक था, दूसरा जटिल विकल्प जो मैंने सम्मान के रचनाकारों से देखा, मुझे लगता है, न केवल मुझे ज़बिक्स।
थोड़े अनुकूलन के बाद, मैंने इसे स्वयं शुरू करने का फैसला किया ... दुर्भाग्य से, इसने कई कमियों का खुलासा किया: एक नया खंड बनाते समय, इस खंड में पहला रिकॉर्ड खो गया था; बड़ी संख्या में वर्गों के साथ, यहां तक ​​कि एक रिकॉर्ड डालने में भी बहुत समय लगता है (2 कारकों के कारण: हर बार एक तालिका की गणना की गई थी जहां रिकॉर्ड डाला जाना चाहिए, सभी शर्तों के साथ 1 ट्रिगर के बजाय बहुत सारे नियमों का उपयोग करके)। फिर भी, लोगों ने एक उत्कृष्ट काम किया और मैं उन्हें सम्मान की किरणें भेजने का अवसर देता हूं।


परिणामस्वरूप, मैं अपना निर्णय आपके न्यायालय में प्रस्तुत करता हूं। सबसे पहले, विभाजन शुरू करने का एक उदाहरण:
दिनांक सीमा:

select create_partition('partitions.test1.cdate', 'date', 'day,YYYY_MM_DD', now()::text); select create_partition('partitions.test2.cdate', 'date', 'week,YYYY_IW', now()::text); select create_partition('partitions.test3.cdate', 'date', 'month,YYYY_MM', now()::text); 

ये कमांड टेबल टेस्ट 1, टेस्ट 2, टेस्ट 3 के लिए दिनों, हफ्तों और महीनों से विभाजन शुरू कर देंगे

आइए मापदंडों का विश्लेषण करें:
पहले फ़ील्ड ' partitions.test1.cdate ' मान को एक बिंदु द्वारा अलग किया गया:
विभाजन - उस योजना का नाम जहां वर्गों को जोड़ा जाएगा
test1 - वर्तमान क्षेत्र में पूर्वज तालिका
cdate - वह फ़ील्ड जिसके द्वारा तालिका विभाजित की जाएगी
दूसरा क्षेत्र 'तारीख' वर्गों के प्रकार को निर्धारित करता है
तीसरा फ़ील्ड 'दिन, YYYY_MM_DD' सेक्शनिंग के लिए पैरामीटर सेट करता है
दिन - हर दिन अनुभाग बनाएं
YYYY_MM_DD - तालिका के लिए प्रत्यय
और अंत में, चौथा अब () :: पाठ उदाहरण डेटा एक अनुभाग बनाने के लिए

एक और उदाहरण:
 select create_partition('partitions.test4.id', 'digits', '10,2', 1::int); 

यहां, बाईं ओर शून्य (यदि आवश्यक हो) जोड़कर संख्या को 10-बिट तक घटा दिया जाएगा और आठवें के बाईं ओर के सभी अंकों को अनुभाग नाम के लिए लिया जाएगा:
1,000,000,000 विभाजन में गिरेंगे ।est4_10 अनुभाग
100000000 सेक्शन विभाजन के लिए ।est4_01
10000000 प्रति अनुभाग विभाजन ।est4_00
अनुभाग विभाजन के लिए 10000000000.est4_100

संक्षेप में वर्णन करें कि यह कैसे काम करता है:
जब रन सेलेक्ट create_partition ('partitions.test1.cdate', 'date', 'day, YYYY_MM_DD', अब () :: text); सबसे पहले, "प्लगइन" पार्टीशन_ डेट को कहा जाता है। इस फ़ंक्शन का उद्देश्य पारित किए गए मापदंडों के अनुसार अनुभाग का नाम और उसमें आने की स्थिति को वापस करना है। मुख्य कार्य में आगे, एक नई तालिका बनाई गई है, जो आवश्यक शर्तों के साथ वर्तमान एक से विरासत में मिली है, सभी पूर्वजों के सूचक की नकल की जाती है।
फिर, सिस्टम तालिकाओं की एक क्वेरी इस डेटा से वंश तालिका और उनकी चेक स्थिति के बारे में डेटा को खींचती है, एक ट्रिगर बनाता है जो डेटा को अनुभागों में वितरित करता है।
अब, किसी भी प्रविष्टि के साथ, ट्रिगर आग लगाएगा और वांछित टेबल-सेक्शन का निर्धारण करेगा, अगर ऐसी कोई तालिका नहीं है, तो create_partition को उसी पैरामीटर के साथ कहा जाता है जैसे कि अनुभाग बनाते समय थे।
पहले उदाहरण के लिए, भरने के एक साल बाद, ट्रिगर इस तरह दिखेगा:
 CREATE OR REPLACE FUNCTION trig_partition_test1_cdate() RETURNS TRIGGER AS $BODY2$ declare child text; begin IF ((NEW.cdate >= '2012-09-01 00:00:00-07'::timestamp with time zone) AND (NEW.cdate < '2012-10-01 00:00:00-07'::timestamp with time zone)) THEN INSERT INTO public.test1_2012_09 VALUES (NEW.*); ELSIF ((NEW.cdate >= '2012-08-01 00:00:00-07'::timestamp with time zone) AND (NEW.cdate < '2012-09-01 00:00:00-07'::timestamp with time zone)) THEN INSERT INTO public.test1_2012_08 VALUES (NEW.*); ELSIF ((NEW.cdate >= '2012-07-01 00:00:00-07'::timestamp with time zone) AND (NEW.cdate < '2012-08-01 00:00:00-07'::timestamp with time zone)) THEN INSERT INTO public.test1_2012_07 VALUES (NEW.*); ELSIF ((NEW.cdate >= '2012-06-01 00:00:00-07'::timestamp with time zone) AND (NEW.cdate < '2012-07-01 00:00:00-07'::timestamp with time zone)) THEN INSERT INTO public.test1_2012_06 VALUES (NEW.*); ELSIF ((NEW.cdate >= '2012-05-01 00:00:00-07'::timestamp with time zone) AND (NEW.cdate < '2012-06-01 00:00:00-07'::timestamp with time zone)) THEN INSERT INTO public.test1_2012_05 VALUES (NEW.*); ELSIF ((NEW.cdate >= '2012-04-01 00:00:00-07'::timestamp with time zone) AND (NEW.cdate < '2012-05-01 00:00:00-07'::timestamp with time zone)) THEN INSERT INTO public.test1_2012_04 VALUES (NEW.*); ELSIF ((NEW.cdate >= '2012-03-01 00:00:00-08'::timestamp with time zone) AND (NEW.cdate < '2012-04-01 00:00:00-07'::timestamp with time zone)) THEN INSERT INTO public.test1_2012_03 VALUES (NEW.*); ELSIF ((NEW.cdate >= '2012-02-01 00:00:00-08'::timestamp with time zone) AND (NEW.cdate < '2012-03-01 00:00:00-08'::timestamp with time zone)) THEN INSERT INTO public.test1_2012_02 VALUES (NEW.*); ELSIF ((NEW.cdate >= '2012-01-01 00:00:00-08'::timestamp with time zone) AND (NEW.cdate < '2012-02-01 00:00:00-08'::timestamp with time zone)) THEN INSERT INTO public.test1_2012_01 VALUES (NEW.*); ELSE EXECUTE 'SELECT create_partition(''public.test1.cdate''::text, ''date''::text, ''month,YYYY_MM''::text , '''||NEW.cdate||'''::text)' INTO child; EXECUTE 'INSERT INTO '|| child || ' SELECT $1.*' USING NEW; END IF; RETURN NULL; end; $BODY$ LANGUAGE 'plpgsql'; 


जैसा कि आप देख सकते हैं, यह प्रलेखन से एक व्यावहारिक रूप से क्लासिक उदाहरण निकला है :)

जैसा कि आपने अनुमान लगाया होगा, इस समाधान का विस्तार करना आसान है, उदाहरण के लिए, स्ट्रिंग के पहले अक्षर या हैश द्वारा विभाजन करके। मेरा सुझाव है कि इस "प्लगइन" को स्वयं लिखें और टिप्पणियों में पोस्ट करें।

मुझे उम्मीद है कि कोई मेरे समाधान को उपयोगी पाएगा।

Sql.ru फोरम पर हेल्पर स्क्रिप्ट मिलीं
 CREATE OR REPLACE FUNCTION to_timestamp(timestamp without time zone) RETURNS timestamp with time zone AS $_$ select $1::timestamp with time zone; $_$ LANGUAGE sql STABLE STRICT; CREATE OR REPLACE FUNCTION to_timestamp(timestamp with time zone) RETURNS timestamp with time zone AS $_$ select $1::timestamp with time zone; $_$ LANGUAGE sql STABLE STRICT; CREATE OR REPLACE FUNCTION to_numeric(text) RETURNS numeric AS $_$ select regexp_replace($1, '\D+', '', 'g')::numeric; $_$ LANGUAGE sql STABLE STRICT; CREATE OR REPLACE FUNCTION copy_constraints(srcoid oid, dstoid oid) RETURNS integer AS $BODY$ declare i int4 := 0; constrs record; srctable text; dsttable text; begin srctable = srcoid::regclass; dsttable = dstoid::regclass; for constrs in select conname as name, pg_get_constraintdef(oid) as definition from pg_constraint where conrelid = srcoid loop begin execute 'alter table ' || dsttable || ' add constraint ' || replace(replace(constrs.name, srctable, dsttable),'.','_') || ' ' || constrs.definition; i = i + 1; exception when duplicate_table then end; end loop; return i; exception when undefined_table then return null; end; $BODY$ LANGUAGE 'plpgsql' VOLATILE; CREATE OR REPLACE FUNCTION copy_constraints(src text, dst text) RETURNS integer AS $BODY$ begin return copy_constraints(src::regclass::oid, dst::regclass::oid); end; $BODY$ LANGUAGE 'plpgsql' IMMUTABLE; CREATE OR REPLACE FUNCTION copy_indexes(srcoid oid, dstoid oid) RETURNS integer AS $BODY$ declare i int4 := 0; indexes record; srctable text; dsttable text; script text; begin srctable = srcoid::regclass; dsttable = dstoid::regclass; for indexes in select c.relname as name, pg_get_indexdef(idx.indexrelid) as definition from pg_index idx, pg_class c where idx.indrelid = srcoid and c.oid = idx.indexrelid loop script = replace (indexes.definition, ' INDEX ' || indexes.name, ' INDEX ' || replace(replace(indexes.name, srctable, dsttable),'.','_')); script = replace (script, ' ON ' || srctable, ' ON ' || dsttable); begin execute script; i = i + 1; exception when duplicate_table then end; end loop; return i; exception when undefined_table then return null; end; $BODY$ LANGUAGE 'plpgsql' VOLATILE; CREATE OR REPLACE FUNCTION copy_indexes(src text, dst text) RETURNS integer AS $BODY$ begin return copy_indexes(src::regclass::oid, dst::regclass::oid); end; $BODY$ LANGUAGE 'plpgsql' IMMUTABLE; CREATE OR REPLACE FUNCTION copy_triggers(srcoid oid, dstoid oid) RETURNS integer AS $BODY$ declare i int4 := 0; triggers record; srctable text; dsttable text; script text = ''; begin srctable = srcoid::regclass; dsttable = dstoid::regclass; for triggers in select tgname as name, pg_get_triggerdef(oid) as definition from pg_trigger where tgrelid = srcoid loop script = replace (triggers.definition, ' TRIGGER ' || triggers.name, ' TRIGGER ' || replace(replace(triggers.name, srctable, dsttable),'.','_')); script = replace (script, ' ON ' || srctable, ' ON ' || dsttable); begin execute script; i = i + 1; exception when duplicate_table then end; end loop; return i; exception when undefined_table then return null; end; $BODY$ LANGUAGE 'plpgsql' VOLATILE; CREATE OR REPLACE FUNCTION copy_triggers(src text, dst text) RETURNS integer AS $BODY$ begin return copy_triggers(src::regclass::oid, dst::regclass::oid); end; $BODY$ LANGUAGE 'plpgsql' IMMUTABLE; 


मुख्य स्क्रिप्ट
 CREATE OR REPLACE FUNCTION "create_partition" (in tbl varchar, in method varchar, in params varchar, in sample text) RETURNS text AS $BODY$ declare scheme varchar := split_part(tbl, '.', 1); parent varchar := split_part(tbl, '.', 2); field varchar := split_part(tbl, '.', 3); child varchar; script text; trig text; part text[]; begin execute 'select partition_'||method||'('''||params||''', '''||field||''', '''||sample||''')' into part; -- RAISE EXCEPTION 'part %', part; child = scheme || '.' || parent || '_' || (part[1]::text); execute 'create table IF NOT EXISTS ' || child || ' ( constraint partition_' || (part[1]) || ' check ' || (part[2]) || ' ) inherits (' || parent || ')'; perform copy_constraints(parent, child); perform copy_indexes(parent, child); -- execute 'GRANT SELECT ON ' || child || ' TO some_other_user'; -- execute 'GRANT ALL ON ' || child || ' TO user'; script = (select string_agg(c, chr(10)||' ELS') from ( select to_numeric(replace(t.table_name, parent||'_','')) as n, 'IF '||replace(left(right(cc.check_clause, -1), -1), c.column_name, 'NEW.'||c.column_name)||' THEN INSERT INTO '||t.table_schema||'.'||t.table_name||' VALUES (NEW.*);' as c from information_schema.TABLE_CONSTRAINTS t join information_schema.CONSTRAINT_COLUMN_USAGE c ON t.constraint_name = c.constraint_name join information_schema.check_constraints cc ON t.constraint_name = cc.constraint_name where constraint_type IN ('CHECK') and t.table_name like parent||'\_%' group by t.table_schema, t.table_name, c.column_name, cc.check_clause order by n desc) t); trig = 'trig_partition_'||parent||'_'||field; execute 'CREATE OR REPLACE FUNCTION '||trig||'() RETURNS TRIGGER AS $BODY2$ declare child text; begin '||script||' ELSE EXECUTE ''SELECT create_partition('''''||tbl||'''''::text, '''''||method||'''''::text, '''''||params||'''''::text , ''''''||NEW.'||field||'||''''''::text)'' INTO child; EXECUTE ''INSERT INTO ''|| child || '' SELECT $1.*'' USING NEW; END IF; RETURN NULL; end; $BODY2$ LANGUAGE ''plpgsql'' VOLATILE;'; execute 'DROP TRIGGER IF EXISTS t_'||trig||' ON "'||parent||'" CASCADE'; execute 'CREATE TRIGGER t_'||trig||' BEFORE INSERT ON "'||parent||'" FOR EACH ROW EXECUTE PROCEDURE '||trig||'();'; return child; end; $BODY$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION "partition_date" (in params text, in field varchar, in sample timestamp with time zone) RETURNS text[] AS $BODY$ declare period varchar:= split_part(params, ',', 1); fmt varchar := split_part(params, ',', 2); clock timestamp with time zone := to_timestamp(sample); delta varchar := '1 '||period; suffix varchar; check_beg varchar; check_end varchar; condition varchar; begin -- RAISE EXCEPTION 'period %, fmt %, clock %', period, fmt, clock; check_beg = date_trunc(period, clock); check_end = date_trunc(period, clock + delta::interval); suffix = to_char (clock, fmt); condition = '( ' || field || ' >= ' || quote_literal (check_beg) || ' and ' || field || ' < ' || quote_literal (check_end) || ' )'; return ARRAY[suffix, condition]; end; $BODY$ LANGUAGE 'plpgsql' IMMUTABLE; CREATE OR REPLACE FUNCTION "partition_digits" (in params text, in field varchar, in sample numeric) RETURNS text[] AS $BODY$ declare len int := split_part(params, ',', 1)::int; pref int := split_part(params, ',', 2)::int; norm text := to_char(sample::numeric, 'FM000000000000000000000'); suffix text := regexp_replace(left(norm, -1*(len-pref)), '0*(?=\d{'||pref::text||'})', ''); check_beg varchar; check_end varchar; condition varchar; begin check_beg = (trunc(norm::numeric, -1*(len-pref)))::numeric::text; check_end = (check_beg::numeric+10^(len-pref))::numeric::text; condition = '( ' || field || ' >= ' || check_beg || ' and ' || field || ' < ' || check_end || ' )'; return ARRAY[suffix, condition]; end; $BODY$ LANGUAGE 'plpgsql' IMMUTABLE; 


परीक्षण
तिथि खंड
 DROP TABLE IF EXISTS "public"."test1" CASCADE; CREATE TABLE "public"."test1" ( "id" serial, "cdate" timestamp with time zone, "text" text, CONSTRAINT "test11_pkey" PRIMARY KEY (id) ) WITH OIDS; CREATE INDEX test_idx_cdate ON test1 USING btree (cdate); -- select create_partition('public.test1.cdate', 'date', 'day,YYYY_MM_DD', now()::text); -- select create_partition('public.test1.cdate', 'date', 'week,YYYY_IW', now()::text); select create_partition('public.test1.cdate', 'date', 'month,YYYY_MM', now()::text); -- insert into test1 (cdate, text) SELECT cdate, md5(random()::text) as text FROM generate_series('2012-01-01'::date, now(), '1 day'::interval) cdate; 


संख्या सीमा अनुभाग:
 DROP TABLE IF EXISTS "public"."test2" CASCADE; CREATE TABLE "public"."test2" ( "id" bigserial, "text" text, CONSTRAINT "test2_pkey" PRIMARY KEY (id) ) WITH OIDS; select create_partition('public.test2.id', 'digits', '10,2', 1::int); insert into test2 values(10000000, 'test2'); -- insert into test2 (id, text) SELECT t.id, md5(random()::text) as text FROM generate_series(0, 100000000000, 1000000000) as t(id) limit 200; 

Source: https://habr.com/ru/post/In152125/


All Articles