Mae yna sefyllfaoedd pan i mewn i dabl heb allwedd sylfaenol neu mae rhyw fynegai unigryw arall, oherwydd amryfusedd, yn cynnwys cloniau cyflawn o gofnodion sy'n bodoli eisoes.

Er enghraifft, mae gwerthoedd metrig cronolegol yn cael eu hysgrifennu i PostgreSQL gan ddefnyddio ffrwd COPY, ac yna mae methiant sydyn, ac mae rhywfaint o ddata hollol union yr un fath yn dod i mewn eto.
Sut i gael gwared ar gloniau diangen o gronfa ddata?
Pan nad yw PK yn ddefnyddiol
Yr ateb symlaf yw atal y sefyllfa hon rhag digwydd yn y lle cyntaf. Er enghraifft, trwy weithredu ALLWEDD GYNRAIF. Ond nid yw hyn bob amser yn bosibl heb gynyddu faint o ddata sy'n cael ei storio.
Er enghraifft, os yw cywirdeb y system ffynhonnell yn uwch na chywirdeb y maes yn y gronfa ddata:
metric | ts | data
--------------------------------------------------
cpu.busy | 2019-12-20 00:00:00 | {"value" : 12.34}
cpu.busy | 2019-12-20 00:00:01 | {"value" : 10}
cpu.busy | 2019-12-20 00:00:01 | {"value" : 11.2}
cpu.busy | 2019-12-20 00:00:03 | {"value" : 15.7}
Wedi sylwi? Cafodd y cyfrif i lawr, yn lle 00:00:02, ei gofnodi yn gronfa ddata ts eiliad ynghynt, ond roedd yn parhau i fod yn berffaith ddilys o safbwynt ymarferol (wedi'r cyfan, mae gwerthoedd y data yn wahanol!).
Wrth gwrs, gellir ei wneud PK(metrig, ts) — ond yna byddwn yn cael gwrthdaro mewnosod ar gyfer data dilys.
Gallu gwneud PK(metrig, ts, data) - ond bydd hyn yn cynyddu ei gyfaint yn fawr, na fyddwn yn ei ddefnyddio.
Felly, yr opsiwn mwyaf cywir yw creu mynegai rheolaidd nad yw'n unigryw. (metrig, ts) a delio â phroblemau ar ôl y ffaith os byddant yn codi.
Mae Rhyfeloedd y Cloniau wedi dechrau.
Digwyddodd rhyw fath o ddamwain, ac yn awr mae'n rhaid i ni ddinistrio'r cofnodion clôn o'r tabl.

Gadewch i ni efelychu'r data cychwynnol:
CREATE TABLE tbl(k text, v integer);
INSERT INTO tbl
VALUES
('a', 1)
, ('a', 3)
, ('b', 2)
, ('b', 2) -- oops!
, ('c', 3)
, ('c', 3) -- oops!!
, ('c', 3) -- oops!!
, ('d', 4)
, ('e', 5)
;Dyma ni’n crynu’n llaw dair gwaith, aeth Ctrl+V yn sownd, a dyma fe...
Yn gyntaf, gadewch i ni ddeall y gall ein tabl fod yn eithaf mawr, felly ar ôl i ni ddod o hyd i'r holl gloniau, bydd angen i ni "brocio bys" yn llythrennol i'w dileu. cofnodion penodol heb chwilio amdanynt eto.
Ac mae yna ffordd o'r fath - dyma hi , y dynodwr ffisegol ar gyfer cofnod penodol.
Felly, yn gyntaf oll, mae angen i ni gasglu CTIDau'r cofnodion yn seiliedig ar gynnwys llawn rhes y tabl. Yr opsiwn symlaf yw trosi'r rhes gyfan i destun:
SELECT
T::text
, array_agg(ctid) ctids
FROM
tbl T
GROUP BY
1;
t | ctids
---------------------------------
(e,5) | {"(0,9)"}
(d,4) | {"(0,8)"}
(c,3) | {"(0,5)","(0,6)","(0,7)"}
(b,2) | {"(0,3)","(0,4)"}
(a,3) | {"(0,2)"}
(a,1) | {"(0,1)"}
A yw'n bosibl peidio â chastio?Mewn egwyddor, mae'n bosibl yn y rhan fwyaf o achosion. Nes i chi ddechrau defnyddio meysydd yn y tabl hwn. mathau heb weithredwr cydraddoldeb:
CREATE TABLE tbl(k text, v integer, x point);
SELECT
array_agg(ctid) ctids
FROM
tbl T
GROUP BY
T;
-- ERROR: could not identify an equality operator for type tbl
Aha, rydyn ni'n gweld ar unwaith, os oes mwy nag un cofnod yn y rhes, eu bod nhw i gyd yn gloniau. Gadewch i ni gadw'r rheini yn unig:
SELECT
unnest(ctids[2:])
FROM
(
SELECT
array_agg(ctid) ctids
FROM
tbl T
GROUP BY
T::text
) T;unnest
------
(0,6)
(0,7)
(0,4)I'r rhai sy'n hoffi ysgrifennu'n fyrrachGallwch chi hefyd ei ysgrifennu fel hyn:
SELECT
unnest((array_agg(ctid))[2:])
FROM
tbl T
GROUP BY
T::text;Gan nad oes gennym ddiddordeb yng ngwerth y llinyn cyfresol ei hun, fe wnaethon ni ei ollwng o golofnau a ddychwelwyd yr is-ymholiad.
Y cyfan sydd ar ôl yw gwneud i DELETE ddefnyddio'r set a dderbyniwyd gennym:
DELETE FROM
tbl
WHERE
ctid = ANY(ARRAY(
SELECT
unnest(ctids[2:])
FROM
(
SELECT
array_agg(ctid) ctids
FROM
tbl T
GROUP BY
T::text
) T
)::tid[]);Gadewch i ni wirio ein hunain:

Ie, mae hynny'n gywir: dewiswyd ein 3 chofnod mewn un Sgan Dilyniant o'r tabl cyfan, a defnyddiwyd y nod Dileu i chwilio am ddata. pas sengl gyda Tid Scan:
-> Tid Scan on tbl (actual time=0.050..0.051 rows=3 loops=1)
TID Cond: (ctid = ANY ($0))Os gwnaethoch chi glirio llawer o gofnodion, .
Gadewch i ni wirio am dabl mwy a chyda nifer fwy o ddyblygiadau:
TRUNCATE TABLE tbl;
INSERT INTO tbl
SELECT
chr(ascii('a'::text) + (random() * 26)::integer) k -- a..z
, (random() * 100)::integer v -- 0..99
FROM
generate_series(1, 10000) i; 
Felly, mae'r dull yn gweithio'n llwyddiannus, ond dylid ei ddefnyddio'n ofalus. Oherwydd ar gyfer pob cofnod sydd wedi'i ddileu, mae un dudalen ddata wedi'i darllen yn Tid Scan ac un yn Delete.
Ffynhonnell: hab.com
