เดเดฎเตเดเด เด เดฒเตเดฒเตเดเตเดเดฟเตฝ เดตเดฟเดญเดเดจเด เดเดจเตเดจ เดเดถเดฏเด เดเดเตเดเดจเต เดเดฃเตเดเดพเดฏเดฟ
เดเดฅ เดเดตเดฟเดเต เดคเตเดเดเตเดเตเดจเตเดจเต:
เดฒเดฟเดฑเดฟเดเตเดเตฝ เดกเตเดเตเดฐเดทเตป:
เดเตเดคเตเดฏเดฎเดพเดฏเดฟ เดชเดฑเดเตเดเดพเตฝ 'เด เดจเดฟเดฎเดฟเดทเดคเตเดคเดฟเตฝ', เดเดพเดฐเดฃเด
เด เดคเดฟเดจเดพเตฝ, เดจเดฟเดเตเดเตพเดเตเดเต เดเดเตเดเดจเต เดเดชเดญเตเดเตเดคเดพเดตเดฟเดจเต เดธเดจเตเดคเตเดทเดฟเดชเตเดชเดฟเดเตเดเดพเดจเดพเดเตเด, เด เดคเต เดธเดฎเดฏเด เดจเดฟเดเตเดเดณเตเดเต เดธเตเดตเดจเตเดคเด เดเดดเดฟเดตเตเดเตพ เดฎเตเดเตเดเดชเตเดชเตเดเตเดคเตเดคเดพเด?
เดเดฒเตเดฒเดพเด เดเดดเดฟเดฏเตเดจเตเดจเดคเตเดฐ เดฒเดณเดฟเดคเดฎเดพเดเตเดเดพเตปเดกเดพเดฑเตเดฑเดพเดฌเตเดธเดฟเดจเตเดฑเต เดชเตเดฐเดเดเดจเดคเตเดคเดฟเตฝ เดเดจเตเดคเตเดเตเดเดฟเดฒเตเด เดธเดฎเตเดฒเดฎเดพเดฏเดฟ เดฎเตเดเตเดเดชเตเดชเตเดเตเดคเตเดคเดพเตป เดฐเดฃเตเดเต เดตเดดเดฟเดเดณเตเดฏเตเดณเตเดณเต:
1) เดตเดฟเดชเตเดฒเดฎเดพเดฏ เดชเดพเดค - เดเดเตเดเตพ เดตเดฟเดญเดตเดเตเดเตพ เดตเตผเดฆเตเดงเดฟเดชเตเดชเดฟเดเตเดเตเดจเตเดจเต, เดเตเตบเดซเดฟเดเดฑเตเดทเตป เดฎเดพเดฑเตเดฑเตเดจเตเดจเต;
2) เดคเตเดตเตเดฐเดฎเดพเดฏ เดชเดพเดค - เด
เดจเตเดตเตเดทเดฃ เดเดชเตเดฑเตเดฑเดฟเดฎเตเดธเตเดทเตป
เดตเตเดเดคเตเดคเดฟเดฒเดพเดเตเดเดพเดจเตเดณเตเดณ เด เดญเตเดฏเตผเดคเตเดฅเดจเดฏเดฟเตฝ เดฎเดฑเตเดฑเตเดจเตเดคเดพเดฃเต เดฎเดพเดฑเตเดฑเตเดฃเตเดเดคเตเดจเตเดจเต เด เดเตเดเดพเดฒเดคเตเดคเต เดตเตเดฏเดเตเดคเดฎเดฒเตเดฒเดพเดคเตเดคเดคเดฟเดจเดพเตฝ, เดเดพเตป เดเดตเตผเดคเตเดคเดฟเดเตเดเตเดจเตเดจเต, เดชเดพเดค เดคเดฟเดฐเดเตเดเตเดเตเดคเตเดคเต - เดเตเดฌเดฟเตพ เดกเดฟเดธเตเตป เดฎเดพเดฑเตเดฑเดเตเดเตพ.
เด
เดคเดฟเดจเดพเตฝ, เดชเตเดฐเดงเดพเดจ เดเตเดฆเตเดฏเด เดเดฏเตผเดจเตเดจเตเดตเดฐเตเดจเตเดจเต: เดจเดฎเตเดฎเตพ เดเดจเตเดคเต, เดเดเตเดเดจเต เดฎเดพเดฑเตเด?
เดชเตเดฐเดพเดฐเดเดญ เดตเตเดฏเดตเดธเตเดฅเดเตพ
เดเดจเตเดจเดพเดฎเดคเดพเดฏเดฟ, เด ERD เดเดฃเตเดเต (เดธเตเดชเดพเดงเดฟเดเดฎเดพเดฏเดฟ เดฒเดณเดฟเดคเดฎเดพเดเตเดเดฟเดฏ เดฐเตเดคเดฟเดฏเดฟเตฝ เดเดพเดฃเดฟเดเตเดเดฟเดฐเดฟเดเตเดเตเดจเตเดจเต):
เดชเตเดฐเดงเดพเดจ เดธเดตเดฟเดถเตเดทเดคเดเตพ:
- เดชเดฒ เดชเดฒ เดฌเดจเตเดงเดเตเดเตพ
- เดชเดเตเดเดฟเดเดฏเดฟเตฝ เดเดคเดฟเดจเดเด เดเดฐเต เดชเดพเตผเดเตเดเตเดทเตป เดเต เดเดฃเตเดเต
เดฏเดฅเดพเตผเดคเตเดฅ เด เดญเตเดฏเตผเดคเตเดฅเดจ:
SELECT
p."PARAMETER_ID" as parameter_id,
pc."PC_NAME" AS pc_name,
pc."CUSTOMER_PARTNUMBER" AS customer_partnumber,
w."LASERMARK" AS lasermark,
w."LOTID" AS lotid,
w."REPORTED_VALUE" AS reported_value,
w."LOWER_SPEC_LIMIT" AS lower_spec_limit,
w."UPPER_SPEC_LIMIT" AS upper_spec_limit,
p."TYPE_CALCUL" AS type_calcul,
s."SHIPMENT_NAME" AS shipment_name,
s."SHIPMENT_DATE" AS shipment_date,
extract(year from s."SHIPMENT_DATE") AS year,
extract(month from s."SHIPMENT_DATE") as month,
s."REPORT_NAME" AS report_name,
p."SPARAM_NAME" AS SPARAM_name,
p."CUSTOMERPARAM_NAME" AS customerparam_name
FROM data w INNER JOIN shipment s ON s."SHIPMENT_ID" = w."SHIPMENT_ID"
INNER JOIN parameters p ON p."PARAMETER_ID" = w."PARAMETER_ID"
INNER JOIN shipment_pc sp ON s."SHIPMENT_ID" = sp."SHIPMENT_ID"
INNER JOIN pc pc ON pc."PC_ID" = sp."PC_ID"
INNER JOIN ( SELECT w2."LASERMARK" , MAX(s2."SHIPMENT_DATE") AS "SHIPMENT_DATE"
FROM shipment s2 INNER JOIN data w2 ON s2."SHIPMENT_ID" = w2."SHIPMENT_ID"
GROUP BY w2."LASERMARK"
) md ON md."SHIPMENT_DATE" = s."SHIPMENT_DATE" AND md."LASERMARK" = w."LASERMARK"
WHERE
s."SHIPMENT_DATE" >= '2018-07-01' AND s."SHIPMENT_DATE" <= '2018-09-30' ;
เดเตเดธเตเดฑเตเดฑเต เดกเดพเดฑเตเดฑเดพเดฌเตเดธเดฟเดฒเต เดจเดฟเตผเดตเตเดตเดนเดฃ เดซเดฒเดเตเดเตพ:
เดเตเดฒเดตเต : 502 997.55
เดจเดฟเตผเดตเตเดตเดนเดฃ เดธเดฎเดฏเด: 505 เดธเตเดเตเดเตปเดกเต.
เดจเดฎเตเดฎเตพ เดเดจเตเดคเดพเดฃเต เดเดพเดฃเตเดจเตเดจเดคเต? เดเดฐเต เดธเดฎเดฏ เดธเตเดฒเตเดธเดฟเดจเต เด
เดเดฟเดธเตเดฅเดพเดจเดฎเดพเดเตเดเดฟเดฏเตเดณเตเดณ เดเดฐเต เดชเดคเดฟเดตเต เด
เดญเตเดฏเตผเดคเตเดฅเดจ.
เดจเดฎเตเดเตเดเต เดเดฑเตเดฑเดตเตเด เดฒเดณเดฟเดคเดฎเดพเดฏ เดฒเตเดเดฟเดเตเดเตฝ เด
เดจเตเดฎเดพเดจเด เดเดฃเตเดเดพเดเตเดเดพเด: เดเดฐเต เดเตเด เดธเตเดฒเตเดธเดฟเดจเตเดฑเต เดธเดพเดฎเตเดชเดฟเตพ เดเดฃเตเดเตเดเตเดเดฟเตฝ, เด
เดคเต เดเดเตเดเดณเต เดธเดนเดพเดฏเดฟเดเตเดเตเดฎเต? เด
เดคเต เดถเดฐเดฟเดฏเดพเดฃเต - เดตเดฟเดญเดเดจเด.
เดเดจเตเดคเต เดธเตเดเตเดทเตป เดเตเดฏเตเดฏเดฃเด?
เดเดฑเตเดฑเดจเตเดเตเดเดคเตเดคเดฟเตฝ, เดเตเดฏเตเดธเต เดตเตเดฏเดเตเดคเดฎเดพเดฃเต - "SHIPMENT_DATE" เดเต เดเดชเดฏเตเดเดฟเดเตเดเต "เดทเดฟเดชเตเดชเตเดฎเตเดจเตเดฑเต" เดชเดเตเดเดฟเดเดฏเตเดเต เดกเดฟเดเตเดฒเดฑเตเดฑเตเดฑเตเดตเต เดชเดพเตผเดเตเดเตเดทเดจเดฟเดเดเต (เดตเดณเดฐเต เดฎเตเดจเตเดจเตเดเตเดเต เดเตเดคเดฟเดเตเดเตเดจเตเดจเต - เด เดตเดธเดพเดจเด เด เดคเต เดเตฝเดชเดพเดฆเดจเดคเตเดคเดฟเตฝ เด เตฝเดชเตเดชเด เดคเตเดฑเตเดฑเดพเดฏเดฟ เดฎเดพเดฑเดฟ).
เดเดเตเดเดจเต เดตเดฟเดญเดเดฟเดเตเดเดพเด?
เด เดเตเดฆเตเดฏเดตเตเด เดตเดณเดฐเต เดฌเตเดฆเตเดงเดฟเดฎเตเดเตเดเตเดณเตเดณ เดเดพเดฐเตเดฏเดฎเดฒเตเดฒ. เดญเดพเดเตเดฏเดตเดถเดพเตฝ, PostgreSQL 10 เตฝ, เดเดชเตเดชเตเตพ เดเดฐเต เดนเตเดฏเตเดฎเตป เดชเดพเตผเดเตเดเตเดทเดจเดฟเดเดเต เดฎเตเดเตเดเดพเดจเดฟเดธเด เดเดฃเตเดเต.
เด
เดเตเดเดจเต:
- เดเดฑเดตเดฟเด เดชเดเตเดเดฟเดเดฏเตเดเต เดเดฐเต เดกเดเดชเต เดธเดเดฐเดเตเดทเดฟเดเตเดเตเด - pg_dump source_table
- เดฏเดฅเดพเตผเดคเตเดฅ เดชเดเตเดเดฟเด เดเดฒเตเดฒเดพเดคเดพเดเตเดเตเด - เดกเตเดฐเตเดชเตเดชเต เดเตเดฌเดฟเตพ source_table
- เดถเตเดฐเตเดฃเดฟ เดชเดพเตผเดเตเดเตเดทเดจเดฟเดเดเต เดเดชเดฏเตเดเดฟเดเตเดเต เดเดฐเต เดชเดพเดฐเดจเตเดฑเต เดเตเดฌเดฟเตพ เดธเตเดทเตเดเดฟเดเตเดเตเด - เดชเดเตเดเดฟเด source_table เดธเตเดทเตเดเดฟเดเตเดเตเด
- เดตเดฟเดญเดพเดเดเตเดเตพ เดธเตเดทเตเดเดฟเดเตเดเตเด - เดชเดเตเดเดฟเด source_table เดธเตเดทเตโเดเดฟเดเตเดเตเด, เดธเตเดเดฟเด เดธเตเดทเตโเดเดฟเดเตเดเตเด
- เดเดเตเดเด 1-เตฝ เดธเตเดทเตโเดเดฟเดเตเด เดกเดเดชเต เดเดฑเดเตเดเตเดฎเดคเดฟ เดเตเดฏเตเดฏเตเด - pg_restore
เดตเดฟเดญเดเดจเดคเตเดคเดฟเดจเตเดณเตเดณ เดธเตเดเตเดฐเดฟเดชเตเดฑเตเดฑเตเดเตพ
เดฒเดพเดณเดฟเดคเตเดฏเดคเตเดคเดฟเดจเตเด เดธเตเดเดฐเตเดฏเดคเตเดคเดฟเดจเตเดฎเดพเดฏเดฟ, 2,3,4 เดเดเตเดเดเตเดเตพ เดเดฐเต เดธเตเดเตเดฐเดฟเดชเตเดฑเตเดฑเดฟเดฒเตเดเตเดเต เดธเดเดฏเตเดเดฟเดชเตเดชเดฟเดเตเดเดฟเดฐเดฟเดเตเดเตเดจเตเดจเต.
เด
เดเตเดเดจเต:
เดเดฑเดตเดฟเด เดชเดเตเดเดฟเดเดฏเตเดเต เดเดฐเต เดกเดเดชเต เดธเดเดฐเดเตเดทเดฟเดเตเดเตเด
pg_dump postgres --file=/dump/shipment.dmp --format=c --table=shipment --verbose > /dump/shipment.log 2>&1
เดเดฑเดตเดฟเด เดชเดเตเดเดฟเด เดเดฒเตเดฒเดพเดคเดพเดเตเดเตเด + เดฑเตเดเตเดเต เดชเดพเตผเดเตเดเตเดทเดจเดฟเดเดเต เดเดชเดฏเตเดเดฟเดเตเดเต เดเดฐเต เดชเดพเดฐเดจเตเดฑเต เดเตเดฌเดฟเตพ เดธเตเดทเตโเดเดฟเดเตเดเตเด + เดชเดพเตผเดเตเดเตเดทเดจเตเดเตพ เดธเตเดทเตโเดเดฟเดเตเดเตเด
--create_partition_shipment.sql
do language plpgsql $$
declare
rec_shipment_date RECORD ;
partition_name varchar;
index_name varchar;
current_year varchar ;
current_month varchar ;
begin_year varchar ;
begin_month varchar ;
next_year varchar ;
next_month varchar ;
first_flag boolean ;
i integer ;
begin
RAISE NOTICE 'CREATE TEMPORARY TABLE FOR SHIPMENT_DATE';
CREATE TEMP TABLE tmp_shipment_date as select distinct "SHIPMENT_DATE" from shipment order by "SHIPMENT_DATE" ;
RAISE NOTICE 'DROP TABLE shipment';
drop table shipment cascade ;
CREATE TABLE public.shipment
(
"SHIPMENT_ID" integer NOT NULL DEFAULT nextval('shipment_shipment_id_seq'::regclass),
"SHIPMENT_NAME" character varying(30) COLLATE pg_catalog."default",
"SHIPMENT_DATE" timestamp without time zone,
"REPORT_NAME" character varying(40) COLLATE pg_catalog."default"
)
PARTITION BY RANGE ("SHIPMENT_DATE")
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
RAISE NOTICE 'CREATE PARTITIONS FOR TABLE shipment';
current_year:='0';
current_month:='0';
begin_year := '0' ;
begin_month := '0' ;
next_year := '0' ;
next_month := '0' ;
FOR rec_shipment_date IN SELECT * FROM tmp_shipment_date LOOP
RAISE NOTICE 'SHIPMENT_DATE=%',rec_shipment_date."SHIPMENT_DATE";
current_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE");
current_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE") ;
IF to_number(current_month,'99') < 10 THEN
current_month := '0'||current_month ;
END IF ;
--Init borders
IF begin_year = '0' THEN
first_flag := true ; --first time flag
begin_year := current_year ;
begin_month := current_month ;
IF current_month = '12' THEN
next_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 year') ;
ELSE
next_year := current_year ;
END IF;
next_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 month') ;
END IF;
-- Check current date into borders NOT for First time
IF to_date( current_year||'.'||current_month, 'YYYY.MM') >= to_date( begin_year||'.'||begin_month, 'YYYY.MM') AND
to_date( current_year||'.'||current_month, 'YYYY.MM') < to_date( next_year||'.'||next_month, 'YYYY.MM') AND
NOT first_flag
THEN
CONTINUE ;
ELSE
--NEW borders only for second and after time
begin_year := current_year ;
begin_month := current_month ;
IF current_month = '12' THEN
next_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 year') ;
ELSE
next_year := current_year ;
END IF;
next_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 month') ;
END IF;
partition_name := 'shipment_shipment_date_'||begin_year||'-'||begin_month||'-01-'|| next_year||'-'||next_month||'-01' ;
EXECUTE format('CREATE TABLE ' || quote_ident(partition_name) || ' PARTITION OF shipment FOR VALUES FROM ( %L ) TO ( %L ) ' , current_year||'-'||current_month||'-01' , next_year||'-'||next_month||'-01' ) ;
index_name := partition_name||'_shipment_id_idx';
RAISE NOTICE 'INDEX NAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID") TABLESPACE pg_default ' ) ;
--Drop first time flag
first_flag := false ;
END LOOP;
end
$$;
เดกเดเดชเต เดเดฑเดเตเดเตเดฎเดคเดฟ เดเตเดฏเตเดฏเตเดจเตเดจเต
pg_restore -d postgres --data-only --format=c --table=shipment --verbose shipment.dmp > /tmp/data_dump/shipment_restore.log 2>&1
เดชเดพเตผเดเตเดเตเดทเดจเดฟเดเดเต เดซเดฒเดเตเดเตพ เดชเดฐเดฟเดถเตเดงเดฟเดเตเดเตเดจเตเดจเต
เด เดคเดฟเดจเตเดฑเต เดซเดฒเดฎเดพเดฏเดฟ เดจเดฎเตเดเตเดเต เดเดจเตเดคเดพเดฃเต เดเดณเตเดณเดคเต? เดจเดฟเตผเดตเตเดตเดนเดฃ เดชเดฆเตเดงเดคเดฟเดฏเตเดเต เดชเตเตผเดฃเตเดฃ เดตเดพเดเดเด เดตเดฒเตเดคเตเด เดตเดฟเดฐเดธเดตเตเดฎเดพเดฃเต, เด เดคเดฟเดจเดพเตฝ เด เดจเตเดคเดฟเดฎ เดธเดเดเตเดฏเดเดณเดฟเดฒเตเดเตเดเต เดธเตเดตเดฏเด เดชเดฐเดฟเดฎเดฟเดคเดชเตเดชเตเดเตเดคเตเดคเตเดจเตเดจเดคเต เดคเดฟเดเดเตเดเตเด เดธเดพเดฆเตเดงเตเดฏเดฎเดพเดฃเต.
เดเดฏเดฟเดฐเตเดจเตเดจเต
เดเตเดฒเดตเต: 502 997.55
เดจเดฟเตผเดตเตเดตเดนเดฃ เดธเดฎเดฏเด: เดเตเดธเดจเตเดฎเตเดเตเดธ เดธเตเดเตเดเตปเดกเต.
เดเดฏเดฟ เดฎเดพเดฑเดฟเดฏเดฟเดฐเดฟเดเตเดเตเดจเตเดจเต
เดเตเดฒเดตเต: 77 872.36
เดจเดฟเตผเดตเตเดตเดนเดฃ เดธเดฎเดฏเด: เดเตเดธเดจเตเดฎเตเดเตเดธ เดธเตเดเตเดเตปเดกเต.
เดคเดฟเดเดเตเดเตเด เดจเดฒเตเดฒ เดซเดฒเด. เดเตเดฒเดตเตเด เดจเดฟเตผเดตเตเดตเดนเดฃ เดธเดฎเดฏเดตเตเด เดเตเดฑเดเตเดเต. เด เดเตเดเดจเต, เดชเดพเตผเดเตเดเตเดทเดจเดฟเดเดเต เดเดชเดฏเตเดเด เดชเตเดฐเดคเตเดเตเดทเดฟเดเตเด เดซเดฒเด เดจเตฝเดเตเดจเตเดจเต, เดชเตเดคเตเดตเต, เดเดถเตเดเดฐเตเดฏเดฎเดฟเดฒเตเดฒ.
เดเดชเดญเตเดเตเดคเดพเดตเดฟเดจเต เดธเดจเตเดคเตเดทเดฟเดชเตเดชเดฟเดเตเดเตเด
เดชเดฐเดฟเดถเตเดงเดจเดพ เดซเดฒเดเตเดเตพ เดเดชเดญเตเดเตเดคเดพเดตเดฟเดจเต เด เดตเดฒเตเดเดจเดคเตเดคเดฟเดจเดพเดฏเดฟ เดธเดฎเตผเดชเตเดชเดฟเดเตเดเต. เด เดคเต เด เดตเดฒเตเดเดจเด เดเตเดฏเตโเดค เดถเตเดทเด, เด เดตเตผเดเตเดเต เด เตฝเดชเตเดชเด เด เดชเตเดฐเดคเตเดเตเดทเดฟเดคเดฎเดพเดฏ เดเดฐเต เดตเดฟเดงเดฟ เดฒเดญเดฟเดเตเดเต: โเดเตเดณเตเดณเดพเด, โเดกเดพเดฑเตเดฑโ เดชเดเตเดเดฟเด เดตเดฟเดญเดเดฟเดเตเดเตเด.โ
เด เดคเต, เดเดจเตเดจเดพเตฝ เดเดเตเดเตพ เดคเดฟเดเดเตเดเตเด เดตเตเดฏเดคเตเดฏเดธเตเดคเดฎเดพเดฏ "เดทเดฟเดชเตเดชเตเดฎเตเดจเตเดฑเต" เดชเดเตเดเดฟเด เดชเดฐเดฟเดถเตเดงเดฟเดเตเดเต; "เดกเดพเดฑเตเดฑ" เดชเดเตเดเดฟเดเดฏเดฟเตฝ "SHIPMENT_DATE" เดซเตเตฝเดกเต เดเดฒเตเดฒ.
เดเตเดดเดชเตเดชเดฎเดฟเดฒเตเดฒ, เดเตเตผเดเตเดเตเด, เดฎเดพเดฑเตเดฑเตเด. เดชเตเดฐเดงเดพเดจ เดเดพเดฐเตเดฏเด, เดเดชเดญเตเดเตเดคเดพเดตเต เดซเดฒเดคเตเดคเดฟเตฝ เดธเดเดคเตเดชเตเดคเดจเดพเดฃเต; เดจเดเดชเตเดชเดพเดเตเดเตฝ เดตเดฟเดถเดฆเดพเดเดถเดเตเดเตพ เดชเตเดฐเดคเตเดฏเตเดเดฟเดเตเดเต เดชเตเดฐเดงเดพเดจเดฎเดฒเตเดฒ.
เดชเตเดฐเดงเดพเดจ เดชเดเตเดเดฟเด "เดกเดพเดฑเตเดฑ" เดตเดฟเดญเดเดฟเดเตเดเตเดจเตเดจเต
เดชเตเดคเตเดตเต, เดชเตเดฐเดคเตเดฏเตเด เดฌเตเดฆเตเดงเดฟเดฎเตเดเตเดเตเดเตพ เดเดฃเตเดเดพเดฏเดฟเดฒเตเดฒ. เดเดจเตเดจเดฟเดฐเตเดจเตเดจเดพเดฒเตเด, เดชเดพเตผเดเตเดเตเดทเดจเดฟเดเดเต เด เตฝเดเตเดฐเดฟเดคเด, เดคเตเตผเดเตเดเดฏเดพเดฏเตเด, เดเตเดฑเดเตเดเต เดฎเดพเดฑเดฟเดฏเดฟเดเตเดเตเดฃเตเดเต.
"เดกเดพเดฑเตเดฑ" เดชเดเตเดเดฟเดเดฏเดฟเดฒเตเดเตเดเต "SHIPMENT_DATA" เดเตเดณเด เดเตเตผเดเตเดเตเดจเตเดจเต
psql -h ั
ะพัั -U ะฑะฐะทะฐ -d ัะทะตั
=> ALTER TABLE data ADD COLUMN "SHIPMENT_DATE" timestamp without time zone ;
"เดทเดฟเดชเตเดชเตเดฎเตเดจเตเดฑเต" เดชเดเตเดเดฟเดเดฏเดฟเตฝ เดจเดฟเดจเตเดจเตเดณเตเดณ เด เดคเต เดชเตเดฐเดฟเดฒเตเดณเตเดณ เดเตเดณเดคเตเดคเดฟเดจเตเดฑเต เดฎเตเดฒเตเดฏเดเตเดเตพ เดเดชเดฏเตเดเดฟเดเตเดเต "เดกเดพเดฑเตเดฑ" เดชเดเตเดเดฟเดเดฏเดฟเดฒเต "SHIPMENT_DATA" เดเตเดณเดคเตเดคเดฟเดจเตเดฑเต เดฎเตเดฒเตเดฏเดเตเดเตพ เดชเตเดฐเดฟเดชเตเดชเดฟเดเตเดเตเด
-----------------------------
--update_data.sql
--updating for altered table "data" to values of "shipment_data" from the table "shipment"
--version 1.0
do language plpgsql $$
declare
rec_shipment_data RECORD ;
shipment_date timestamp without time zone ;
row_count integer ;
total_rows integer ;
begin
select count(*) into total_rows from shipment ;
RAISE NOTICE 'Total %',total_rows;
row_count:= 0 ;
FOR rec_shipment_data IN SELECT * FROM shipment LOOP
update data set "SHIPMENT_DATE" = rec_shipment_data."SHIPMENT_DATE" where "SHIPMENT_ID" = rec_shipment_data."SHIPMENT_ID";
row_count:= row_count +1 ;
RAISE NOTICE 'row count = % , from %',row_count,total_rows;
END LOOP;
end
$$;
"เดกเดพเดฑเตเดฑ" เดชเดเตเดเดฟเดเดฏเตเดเต เดเดฐเต เดกเดเดชเต เดธเดเดฐเดเตเดทเดฟเดเตเดเตเด
pg_dump postgres --file=/dump/data.dmp --format=c --table=data --verbose > /dump/data.log 2>&1</source
เดชเดพเตผเดเตเดเตเดทเตป เดเตเดฏเตเดค เดชเดเตเดเดฟเด "เดกเดพเดฑเตเดฑ" เดตเตเดฃเตเดเตเด เดธเตเดทเตเดเดฟเดเตเดเตเด
--create_partition_data.sql
--create partitions for the table "wafer data" by range column "shipment_data" with one month duration
--version 1.0
do language plpgsql $$
declare
rec_shipment_date RECORD ;
partition_name varchar;
index_name varchar;
current_year varchar ;
current_month varchar ;
begin_year varchar ;
begin_month varchar ;
next_year varchar ;
next_month varchar ;
first_flag boolean ;
i integer ;
begin
RAISE NOTICE 'CREATE TEMPORARY TABLE FOR SHIPMENT_DATE';
CREATE TEMP TABLE tmp_shipment_date as select distinct "SHIPMENT_DATE" from shipment order by "SHIPMENT_DATE" ;
RAISE NOTICE 'DROP TABLE data';
drop table data cascade ;
RAISE NOTICE 'CREATE PARTITIONED TABLE data';
CREATE TABLE public.data
(
"RUN_ID" integer,
"LASERMARK" character varying(20) COLLATE pg_catalog."default" NOT NULL,
"LOTID" character varying(80) COLLATE pg_catalog."default",
"SHIPMENT_ID" integer NOT NULL,
"PARAMETER_ID" integer NOT NULL,
"INTERNAL_VALUE" character varying(75) COLLATE pg_catalog."default",
"REPORTED_VALUE" character varying(75) COLLATE pg_catalog."default",
"LOWER_SPEC_LIMIT" numeric,
"UPPER_SPEC_LIMIT" numeric ,
"SHIPMENT_DATE" timestamp without time zone
)
PARTITION BY RANGE ("SHIPMENT_DATE")
WITH (
OIDS = FALSE
)
TABLESPACE pg_default ;
RAISE NOTICE 'CREATE PARTITIONS FOR TABLE data';
current_year:='0';
current_month:='0';
begin_year := '0' ;
begin_month := '0' ;
next_year := '0' ;
next_month := '0' ;
i := 1;
FOR rec_shipment_date IN SELECT * FROM tmp_shipment_date LOOP
RAISE NOTICE 'SHIPMENT_DATE=%',rec_shipment_date."SHIPMENT_DATE";
current_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE");
current_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE") ;
--Init borders
IF begin_year = '0' THEN
RAISE NOTICE '***Init borders';
first_flag := true ; --first time flag
begin_year := current_year ;
begin_month := current_month ;
IF current_month = '12' THEN
next_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 year') ;
ELSE
next_year := current_year ;
END IF;
next_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 month') ;
END IF;
-- RAISE NOTICE 'current_year=% , current_month=% ',current_year,current_month;
-- RAISE NOTICE 'begin_year=% , begin_month=% ',begin_year,begin_month;
-- RAISE NOTICE 'next_year=% , next_month=% ',next_year,next_month;
-- Check current date into borders NOT for First time
RAISE NOTICE 'Current data = %',to_char( to_date( current_year||'.'||current_month, 'YYYY.MM'), 'YYYY.MM');
RAISE NOTICE 'Begin data = %',to_char( to_date( begin_year||'.'||begin_month, 'YYYY.MM'), 'YYYY.MM');
RAISE NOTICE 'Next data = %',to_char( to_date( next_year||'.'||next_month, 'YYYY.MM'), 'YYYY.MM');
IF to_date( current_year||'.'||current_month, 'YYYY.MM') >= to_date( begin_year||'.'||begin_month, 'YYYY.MM') AND
to_date( current_year||'.'||current_month, 'YYYY.MM') < to_date( next_year||'.'||next_month, 'YYYY.MM') AND
NOT first_flag
THEN
RAISE NOTICE '***CONTINUE';
CONTINUE ;
ELSE
--NEW borders only for second and after time
RAISE NOTICE '***NEW BORDERS';
begin_year := current_year ;
begin_month := current_month ;
IF current_month = '12' THEN
next_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 year') ;
ELSE
next_year := current_year ;
END IF;
next_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 month') ;
END IF;
IF to_number(current_month,'99') < 10 THEN
current_month := '0'||current_month ;
END IF ;
IF to_number(begin_month,'99') < 10 THEN
begin_month := '0'||begin_month ;
END IF ;
IF to_number(next_month,'99') < 10 THEN
next_month := '0'||next_month ;
END IF ;
RAISE NOTICE 'current_year=% , current_month=% ',current_year,current_month;
RAISE NOTICE 'begin_year=% , begin_month=% ',begin_year,begin_month;
RAISE NOTICE 'next_year=% , next_month=% ',next_year,next_month;
partition_name := 'data_'||begin_year||begin_month||'01_'||next_year||next_month||'01' ;
RAISE NOTICE 'PARTITION NUMBER % , TABLE NAME =%',i , partition_name;
EXECUTE format('CREATE TABLE ' || quote_ident(partition_name) || ' PARTITION OF data FOR VALUES FROM ( %L ) TO ( %L ) ' , begin_year||'-'||begin_month||'-01' , next_year||'-'||next_month||'-01' ) ;
index_name := partition_name||'_shipment_id_parameter_id_idx';
RAISE NOTICE 'INDEX NAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID", "PARAMETER_ID") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_lasermark_idx';
RAISE NOTICE 'INDEX NAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("LASERMARK" COLLATE pg_catalog."default") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_shipment_id_idx';
RAISE NOTICE 'INDEX NAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_parameter_id_idx';
RAISE NOTICE 'INDEX NAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("PARAMETER_ID") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_shipment_date_idx';
RAISE NOTICE 'INDEX NAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_DATE") TABLESPACE pg_default ' ) ;
--Drop first time flag
first_flag := false ;
END LOOP;
end
$$;
เดเดเตเดเด 3-เตฝ เดธเตเดทเตโเดเดฟเดเตเด เดกเดเดชเต เดฒเตเดกเต เดเตเดฏเตเดฏเตเด.
pg_restore -h ั
ะพัั -ัะทะตั -d ะฑะฐะทะฐ --data-only --format=c --table=data --verbose data.dmp > data_restore.log 2>&1
เดชเดดเดฏ เดกเดพเดฑเตเดฑเดฏเตเดเตเดเดพเดฏเดฟ เดเดฐเต เดชเตเดฐเดคเตเดฏเตเด เดตเดฟเดญเดพเดเด เดธเตเดทเตเดเดฟเดเตเดเตเด
---------------------------------------------------
--create_partition_for_old_dates.sql
--create partitions for keeping old dates
--version 1.0
do language plpgsql $$
declare
rec_shipment_date RECORD ;
partition_name varchar;
index_name varchar;
begin
SELECT min("SHIPMENT_DATE") AS min_date INTO rec_shipment_date from data ;
RAISE NOTICE 'Old date is %',rec_shipment_date.min_date ;
partition_name := 'data_old_dates' ;
RAISE NOTICE 'PARTITION NAME IS %',partition_name;
EXECUTE format('CREATE TABLE ' || quote_ident(partition_name) || ' PARTITION OF data FOR VALUES FROM ( %L ) TO ( %L ) ' , '1900-01-01' ,
to_char( rec_shipment_date.min_date,'YYYY')||'-'||to_char(rec_shipment_date.min_date,'MM')||'-01' ) ;
index_name := partition_name||'_shipment_id_parameter_id_idx';
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID", "PARAMETER_ID") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_lasermark_idx';
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("LASERMARK" COLLATE pg_catalog."default") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_shipment_id_idx';
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_parameter_id_idx';
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("PARAMETER_ID") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_shipment_date_idx';
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_DATE") TABLESPACE pg_default ' ) ;
end
$$;
เด เดจเตเดคเดฟเดฎ เดซเดฒเดเตเดเตพ:
เดเดฏเดฟเดฐเตเดจเตเดจเต
เดเตเดฒเดตเต: 502 997.55
เดจเดฟเตผเดตเตเดตเดนเดฃ เดธเดฎเดฏเด: 505 เดธเตเดเตเดเตปเดกเต.
เดเดฏเดฟ เดฎเดพเดฑเดฟเดฏเดฟเดฐเดฟเดเตเดเตเดจเตเดจเต
เดเตเดฒเดตเต: 68 533.70
เดจเดฟเตผเดตเตเดตเดนเดฃ เดธเดฎเดฏเด: 69 เดจเดฟเดฎเดฟเดทเดเตเดเตพ
เดฏเตเดเตเดฏเตป, เดคเดฟเดเดเตเดเตเด เดฏเตเดเตเดฏเตป. เดชเตเดธเตโเดฑเตเดฑเตโเดเตเดฐเตเดเดธเตโเดเตเดฏเตเดเตฝ 10-เดฒเต เดชเดพเตผเดเตเดเตเดทเดจเดฟเดเดเต เดฎเตเดเตเดเดพเดจเดฟเดธเด เดเตเดเตเดคเดฒเต เดเตเดฑเดตเต เดเตเดเดพเดฐเตเดฏเด เดเตเดฏเตเดฏเดพเตป เดเดเตเดเตพเดเตเดเต เดเดดเดฟเดเตเดเต - เดเดฐเต เดฎเดฟเดเดเตเด เดซเดฒเด.
เดฒเดฟเดฑเดฟเดเตเดเตฝ เดกเดฟเดเตเดฐเดทเตป
เดเดคเดฟเดฒเตเด เดฎเดฟเดเดเตเดเดคเต เดเตเดฏเตเดฏเดพเตป เดเดดเดฟเดฏเตเดฎเต - เด
เดคเต, เดจเดฟเดเตเดเตพเดเตเดเต เดเดดเดฟเดฏเตเด!เดเดคเต เดเตเดฏเตเดฏเตเดจเตเดจเดคเดฟเดจเต, เดจเดฟเดเตเดเตพ เดฎเตเดฑเตเดฑเตเดฐเดฟเดฏเดฒเตเดธเตเดกเต เดตเตเดฏเต เดเดชเดฏเตเดเดฟเดเตเดเตเดฃเตเดเดคเตเดฃเตเดเต.
เดฎเตเดฑเตเดฑเตเดฐเดฟเดฏเดฒเตเดธเตเดกเต เดตเตเดฏเต LASERMARK_VIEW เดธเตเดทเตโเดเดฟเดเตเดเตเด
CREATE MATERIALIZED VIEW LASERMARK_VIEW
AS
SELECT w."LASERMARK" , MAX(s."SHIPMENT_DATE") AS "SHIPMENT_DATE"
FROM shipment s INNER JOIN data w ON s."SHIPMENT_ID" = w."SHIPMENT_ID"
GROUP BY w."LASERMARK" ;
CREATE INDEX lasermark_vw_shipment_date_ind on lasermark_view USING btree ("SHIPMENT_DATE") TABLESPACE pg_default;
analyze lasermark_view ;
เดเดฐเดฟเดเตเดเตฝ เดเตเดเดฟ เดเดเตเดเตพ เด
เดญเตเดฏเตผเดคเตเดฅเดจ เดฎเดพเดฑเตเดฑเดฟเดฏเตเดดเตเดคเตเดจเตเดจเต:
เดตเดธเตเดคเตเดจเดฟเดทเตเด เดฎเดพเดฏ เดเดพเดดเตเด เดเดชเดฏเตเดเดฟเดเตเดเต เด
เดจเตเดตเตเดทเดฃเด
SELECT
p."PARAMETER_ID" as parameter_id,
pc."PC_NAME" AS pc_name,
pc."CUSTOMER_PARTNUMBER" AS customer_partnumber,
w."LASERMARK" AS lasermark,
w."LOTID" AS lotid,
w."REPORTED_VALUE" AS reported_value,
w."LOWER_SPEC_LIMIT" AS lower_spec_limit,
w."UPPER_SPEC_LIMIT" AS upper_spec_limit,
p."TYPE_CALCUL" AS type_calcul,
s."SHIPMENT_NAME" AS shipment_name,
s."SHIPMENT_DATE" AS shipment_date,
extract(year from s."SHIPMENT_DATE") AS year,
extract(month from s."SHIPMENT_DATE") as month,
s."REPORT_NAME" AS report_name,
p."STC_NAME" AS STC_name,
p."CUSTOMERPARAM_NAME" AS customerparam_name
FROM data w INNER JOIN shipment s ON s."SHIPMENT_ID" = w."SHIPMENT_ID"
INNER JOIN parameters p ON p."PARAMETER_ID" = w."PARAMETER_ID"
INNER JOIN shipment_pc sp ON s."SHIPMENT_ID" = sp."SHIPMENT_ID"
INNER JOIN pc pc ON pc."PC_ID" = sp."PC_ID"
INNER JOIN LASERMARK_VIEW md ON md."SHIPMENT_DATE" = s."SHIPMENT_DATE" AND md."LASERMARK" = w."LASERMARK"
WHERE
s."SHIPMENT_DATE" >= '2018-07-01' AND s."SHIPMENT_DATE" <= '2018-09-30';
เดเดเตเดเตพเดเตเดเต เดฎเดฑเตเดฑเตเดฐเต เดซเดฒเด เดฒเดญเดฟเดเตเดเตเด:
เดเดฏเดฟเดฐเตเดจเตเดจเต
เดเตเดฒเดตเต: 502 997.55
เดจเดฟเตผเดตเตเดตเดนเดฃ เดธเดฎเดฏเด: 505 เดธเตเดเตเดเตปเดกเต
เดเดฏเดฟ เดฎเดพเดฑเดฟเดฏเดฟเดฐเดฟเดเตเดเตเดจเตเดจเต
เดเตเดฒเดตเต: 42 481.16
เดจเดฟเตผเดตเตเดตเดนเดฃ เดธเดฎเดฏเด: เดเตเดธเดจเตเดฎเตเดเตเดธ เดธเตเดเตเดเตปเดกเต.
เดคเตเตผเดเตเดเดฏเดพเดฏเตเด, เด เดคเตเดคเดฐเดฎเตเดฐเต เดตเดพเดเตเดฆเดพเดจเดชเดฐเดฎเดพเดฏ เดซเดฒเด เดตเดเตเดเดจเดพเดชเดฐเดฎเดพเดฃเตเดเตเดเดฟเดฒเตเด, เดเดถเดฏเดเตเดเตพ เดชเตเดคเตเดเตเดเตเดฃเตเดเดคเตเดฃเตเดเต. เด เดคเดฟเดจเดพเตฝ เดกเดพเดฑเตเดฑ เดฒเดญเดฟเดเตเดเดพเดจเตเดณเตเดณ เดฎเตเดคเตเดคเด เดธเดฎเดฏเด เดตเดณเดฐเตเดฏเดงเดฟเดเด เดธเดนเดพเดฏเดฟเดเตเดเดฟเดฒเตเดฒ. เดเดจเตเดจเดพเตฝ เดเดฐเต เดชเดฐเตเดเตเดทเดฃเด เดเดจเตเดจ เดจเดฟเดฒเดฏเดฟเตฝ เดเดคเต เดตเดณเดฐเต เดฐเดธเดเดฐเดฎเดพเดฃเต.
เดฏเดฅเดพเตผเดคเตเดฅเดคเตเดคเดฟเตฝ, เด
เดคเต เดฎเดพเดฑเดฟเดฏเดคเตเดชเตเดฒเต, เดตเตเดฃเตเดเตเด เดจเดจเตเดฆเดฟ
Afterword
เด เดคเดฟเดจเดพเตฝ, เดเดชเดญเตเดเตเดคเดพเดตเต เดธเดเดคเตเดชเตเดคเดจเดพเดฃเต. เดเดชเตเดชเด เดเดตเดถเตเดฏเด เดธเดพเดนเดเดฐเตเดฏเด เดฎเตเดคเดฒเตเดเตเดเตเดเตเด.
เดชเตเดคเดฟเดฏ เดเตเดฎเดคเดฒ: เดเดดเดคเตเดคเดฟเดฒเดพเดเตเดเดพเดจเตเด เดตเดฟเดชเตเดฒเตเดเดฐเดฟเดเตเดเดพเดจเตเด เดจเดฟเดเตเดเตพเดเตเดเต เดเดจเตเดคเดพเดฃเต เดเตเดฃเตเดเตเดตเดฐเดพเตป เดเดดเดฟเดฏเตเด?
เด เดชเตเดชเตเตพ เดเดพเตป เดเตผเดเตเดเตเดจเตเดจเต - เดธเตเดนเตเดคเตเดคเตเดเตเดเดณเต, เดเดเตเดเดณเตเดเต PostgreSQL เดกเดพเดฑเตเดฑเดพเดฌเตเดธเตเดเดณเตเดเต เดจเดฟเดฐเตเดเตเดทเดฃเด เดเดเตเดเตพเดเตเดเดฟเดฒเตเดฒ.
AWS-เตฝ เดเตเดฒเตเดกเต เดตเดพเดเตเดเดฟเดจเตเดฑเต เดฐเตเดชเดคเตเดคเดฟเตฝ เดเดชเตเดชเตเดดเตเด เดเตเดฑเดเตเดเต เดจเดฟเดฐเตเดเตเดทเดฃเดฎเตเดฃเตเดเต. เดเดจเตเดจเดพเตฝ DBA-เดฏเตเดเตเดเต เด เดจเดฟเดฐเตเดเตเดทเดฃเดคเตเดคเดฟเดจเตเดฑเต เดชเตเดฐเดฏเตเดเดจเด เดเดจเตเดคเดพเดฃเต? เดชเตเดคเตเดตเต, เดชเตเดฐเดพเดฏเตเดเดฟเดเดฎเดพเดฏเดฟ เดเดจเตเดจเตเดฎเดฟเดฒเตเดฒ.
เดจเดฟเดเตเดเตพเดเตเดเต เดเดชเดฏเตเดเดชเตเดฐเดฆเดตเตเด เดฐเดธเดเดฐเดตเตเดฎเดพเดฏ เดเดจเตเดคเตเดเตเดเดฟเดฒเตเด เดเตเดฏเตเดฏเดพเตป เดจเดฟเดเตเดเตพเดเตเดเต เด
เดตเดธเดฐเดฎเตเดฃเตเดเตเดเตเดเดฟเตฝ, เด เด
เดตเดธเดฐเด เดจเดฟเดเตเดเตพเดเตเดเต เดชเตเดฐเดฏเตเดเดจเดชเตเดชเตเดเตเดคเตเดคเดพเตป เดเดดเดฟเดฏเดฟเดฒเตเดฒ ...
เดตเตเดฃเตเดเดฟ
เดเดเตเดเตพ เดเดฑเตเดฑเดตเตเด เดฐเดธเดเดฐเดฎเดพเดฏ เดญเดพเดเดคเตเดคเตเดเตเดเต เดตเดฐเตเดจเตเดจเดคเต เดเดเตเดเดจเตเดฏเดพเดฃเต:
เดกเดฟเดธเดเดฌเตผ 3, 2018.
PostgreSQL เด เดจเตเดตเตเดทเดฃเดเตเดเดณเตเดเต เดชเตเดฐเดเดเดจเด เดจเดฟเดฐเตเดเตเดทเดฟเดเตเดเตเดจเตเดจเดคเดฟเดจเต เดฒเดญเตเดฏเดฎเดพเดฏ เดเดดเดฟเดตเตเดเดณเตเดเตเดเตเดฑเดฟเดเตเดเตเดณเตเดณ เดเดตเตเดทเดฃเด เดเดฐเดเดญเดฟเดเตเดเดพเตป เดเดฐเต เดคเตเดฐเตเดฎเดพเดจเด เดเดเตเดเตเดเตเดจเตเดจเต.
เดเดจเตเดจเดพเตฝ เด เดคเต เดคเดฟเดเดเตเดเตเด เดตเตเดฏเดคเตเดฏเดธเตเดคเดฎเดพเดฏ เดเดฐเต เดเดฅเดฏเดพเดฃเต.
เดคเตเดเดฐเตเดโฆ
เด
เดตเดฒเดเดฌเด: www.habr.com