Sök+ersätt i SQL men utan att skapa dubletter

Permalänk
Medlem

Sök+ersätt i SQL men utan att skapa dubletter

Hej, jag har försökt i någon timme med detta själv, inklusive tagit hjälp av AI som egentligen går emot mina principer men jag behöver verkligen all hjälp jag kan få...

För att förklara allt från början.. jag har alltså en databas i PostgreSQL med en drös tables där varje table har vissa rows med en prefix som den inte ska ha, men det existerar vissa av dessa entries utan den prefixen då jag lagt till dem en extra gång.

Vad jag försökt mig på hittills (exempelkommando):

UPDATE YourTable SET YourColumn = REPLACE(YourColumn, 'find_text', 'replace_text') WHERE YourColumn LIKE '%find_text%' AND NOT EXISTS ( SELECT 1 FROM YourTable WHERE YourColumn = REPLACE(YourColumn, 'find_text', 'replace_text') );

detta fungerar på vissa av mina tables, men inte alla, det verkar inte ha med antalet rows att göra eller längd på varje row då det bland annat inte fungerade på en table med endast 4 rows (där jag redan lyckats med 10+), och har redan fungerat med en table vars rows var 128 tecken styck.

Detta må bli lite rörigt, men jag hoppas det fortfarande lyckas vara läsligt. Jag är tacksam för all hjälp.

Visa signatur

Desktop: Ryzen 7 5700G / 48GB RAM / RX 7900 XTX / 2TB SSD / Arch Linux
Server 1: i5-7400T / 32GB RAM / 88TB HDD / Proxmox
Server 2: i5-6500T / 32GB RAM / 1TB SSD / Proxmox

Permalänk
Medlem
Skrivet av Kattus:

Hej, jag har försökt i någon timme med detta själv, inklusive tagit hjälp av AI som egentligen går emot mina principer men jag behöver verkligen all hjälp jag kan få...

För att förklara allt från början.. jag har alltså en databas i PostgreSQL med en drös tables där varje table har vissa rows med en prefix som den inte ska ha, men det existerar vissa av dessa entries utan den prefixen då jag lagt till dem en extra gång.

Vad jag försökt mig på hittills (exempelkommando):

UPDATE YourTable SET YourColumn = REPLACE(YourColumn, 'find_text', 'replace_text') WHERE YourColumn LIKE '%find_text%' AND NOT EXISTS ( SELECT 1 FROM YourTable WHERE YourColumn = REPLACE(YourColumn, 'find_text', 'replace_text') );

detta fungerar på vissa av mina tables, men inte alla, det verkar inte ha med antalet rows att göra eller längd på varje row då det bland annat inte fungerade på en table med endast 4 rows (där jag redan lyckats med 10+), och har redan fungerat med en table vars rows var 128 tecken styck.

Detta må bli lite rörigt, men jag hoppas det fortfarande lyckas vara läsligt. Jag är tacksam för all hjälp.

Du skulle nog kunna ta bort allt med ”and not exists”, vet inte riktigt vad för syfte den har?
Du har ju redan filtrerat ut alla du vill ändra på i din WHERE-sats.

En update gör som den säger, uppdaterar. Den skapar inga dubbletter . Den uppdaterar ju bara befintligt där den träffar.

Om du är rädd för att ändra data som du inte vill ändra, kan di förstås backuppa. Men annars kan du i en fråga hämta ut IDna för de rader du vill ändra . Typ ”select id from table where filtered like ’%findme%’”
Och sen tar du de IDna och trycker in i din where i replace satsen.

”Update table set foo=replace(…) where id in (dina idn här )”

Men det översta borde räcka för dig om jag förstår din fråga rätt 🙂

Permalänk
Medlem

Problemet verkar ju vara att WHERE %find_text% ger falska positiva
Förslår du postar en bild

Om det är en prefix kör WHERE YourColumn LIKE 'find_text%' dvs bara 1 %

Permalänk
Medlem

Det kan finnas skräp, osynliga tecken, fel casing eller nån annan encoding och liknande som gör att det borkar för dig när du gör:
' WHERE YourColumn = ... '

Lite snabba tips:
'SELECT LENGTH(YourColumn), YourColumn from YourTable' <= Så ser du om du har /t /n eller liknande.
'WHERE LOWER(YourColumn) = LOWER(REPLACE(YourColumn, 'find_text', 'replace_text'))' <= Så gör du case-insensitive jämförelse

Permalänk
Medlem

Det är svårt att förstå vad du menar utan att se data. Dörför blir det gissningar.

Jag tippar på att din WHERE NOT EXISTS är felformulerad. Du vill att den ska låta bli att ändra på de rader där du redan lagt till eller tagit bort ett ”prefix”, oklart vilket alternativ och oklart hur prefixet ser ut.

Men det den gör är att låta bli att agera i de tabeller där det finns någon rad alls där du redan gjort manövern. Om du har en vettig primary key[1] i tabellen, så väljer du att matcha på den också som extra villkor. Det är svårt/omöjligt att föreslå en konkret lösning utan att se tabelldefinition och helst också exempeldata.

Att du ens har problemet tyder på att databasen är feldesignad i betydelsen låg normaliseringsgrad. Återigen är det svårt att säga något bestämt, men texten bör antagligen ligga i en egen tabell och de andra tabellerna borde referera till den. Då hade ändringen inneburit att ändra en enda rad i en enda tabell. DRY.

[1] Nästan alltid är det bästa valet att låta PK vara en rent teknisk nyckel, ett auto-incrementat id-fält av ett stort heltal, alternativt en UUID/GUID, dvs i praktiken ett 128-bitars slumptal.

Permalänk
Medlem

Tack för svar, ska försöka besvara er och förklara mig så gott det går.

Skrivet av nadirian:

Du skulle nog kunna ta bort allt med ”and not exists”, vet inte riktigt vad för syfte den har?
Du har ju redan filtrerat ut alla du vill ändra på i din WHERE-sats.

"and not exists" är där för att inte krocka med rows som redan ligger där, WHERE är bara där för att inte röra rows som saknar prefixen (find_text).

Skrivet av nadirian:

En update gör som den säger, uppdaterar. Den skapar inga dubbletter . Den uppdaterar ju bara befintligt där den träffar.

Dubbletterna är ju redan där, det är det som skapat detta problem

Skrivet av nadirian:

Om du är rädd för att ändra data som du inte vill ändra, kan di förstås backuppa. Men annars kan du i en fråga hämta ut IDna för de rader du vill ändra . Typ ”select id from table where filtered like ’%findme%’”
Och sen tar du de IDna och trycker in i din where i replace satsen.

”Update table set foo=replace(…) where id in (dina idn här )”

Men det översta borde räcka för dig om jag förstår din fråga rätt 🙂

Jag förstår nog inte riktigt hur detta kommando ska fungera, jag försökte här men jag missförstår nog vad exakt jag ska skriva in.

gallerydl=# select entry from mangadex where entry '%mangadex%'; ERROR: type "entry" does not exist LINE 1: select entry from mangadex where entry '%mangadex%';

Jag har alltså en table som heter "mangadex", i den finns en column som heter entry där cirka hälften är dubbletter med prefixen "mangadex".

Skrivet av Sinery:

Problemet verkar ju vara att WHERE %find_text% ger falska positiva
Förslår du postar en bild

Om det är en prefix kör WHERE YourColumn LIKE 'find_text%' dvs bara 1 %

Bild på vad, datan?

Så ser det ut vid "skiftet" till de nya utan prefix (som det ska vara).

Om du menar hur det blir om jag bara försöker köra utan NOT EXISTS etc:

gallerydl=# UPDATE "mangadex" SET entry = REPLACE(entry, 'mangadex', '') WHERE entry LIKE 'mangadex%'; ERROR: duplicate key value violates unique constraint "idx_16562_sqlite_autoindex_mangadex_1" DETAIL: Key (entry)=(35179607-27ce-4f1f-b72e-8e3fcb61f97f_3) already exists.

Så blir det då. Att den nämner sqlite är pga all gammal data är importerad från sqlite

Bra idé med "find_text%'"

Skrivet av KAD:

Det är svårt att förstå vad du menar utan att se data. Dörför blir det gissningar.

Jag tippar på att din WHERE NOT EXISTS är felformulerad. Du vill att den ska låta bli att ändra på de rader där du redan lagt till eller tagit bort ett ”prefix”, oklart vilket alternativ och oklart hur prefixet ser ut.

Men det den gör är att låta bli att agera i de tabeller där det finns någon rad alls där du redan gjort manövern. Om du har en vettig primary key[1] i tabellen, så väljer du att matcha på den också som extra villkor. Det är svårt/omöjligt att föreslå en konkret lösning utan att se tabelldefinition och helst också exempeldata.

Att du ens har problemet tyder på att databasen är feldesignad i betydelsen låg normaliseringsgrad. Återigen är det svårt att säga något bestämt, men texten bör antagligen ligga i en egen tabell och de andra tabellerna borde referera till den. Då hade ändringen inneburit att ändra en enda rad i en enda tabell. DRY.

[1] Nästan alltid är det bästa valet att låta PK vara en rent teknisk nyckel, ett auto-incrementat id-fält av ett stort heltal, alternativt en UUID/GUID, dvs i praktiken ett 128-bitars slumptal.

Jag har lagt till en bild på hur datan ser ut ovanför, hoppas det är till någon nytta. prefixen är alltid samma som vad table't själv heter:

I just denna table så fungerar mitt första exempel utmärkt och den uppdaterar alla 7.

Att databasen är feldesignad är antagligen inte helt inkorrekt, jag har använt pgloader för att importera all data från .sqlite-filer in i postgresql, men glömde ändra hur den ser ut då mjukvaran som använder databasen verkar ändrat hur den vill den ska se ut.

Exakt hur importen gjordes:

for f in ./*.sqlite3; do pgloader $f pgsql://gallerydl:gallerydl@localhost/gallerydl; done

Visa signatur

Desktop: Ryzen 7 5700G / 48GB RAM / RX 7900 XTX / 2TB SSD / Arch Linux
Server 1: i5-7400T / 32GB RAM / 88TB HDD / Proxmox
Server 2: i5-6500T / 32GB RAM / 1TB SSD / Proxmox

Permalänk

Dubletter i en kolumn markerad som primary key eller unique

Skrivet av Kattus:

Jag har alltså en table som heter "mangadex", i den finns en column som heter entry där cirka hälften är dubbletter med prefixen "mangadex".

Du svarar på din egen fråga där, kolumnen entry är markerad som primary key (alternativt som unique), så du har redan ett entry i databasen som har värdet "35179607-27ce-4f1f-b72e-8e3fcb61f97f_3", vilket gör att uppdateringen som ska strippa "mangadex" från mangadex35179607-27ce-4f1f-b72e-8e3fcb61f97f_3 fallerar. Så om allt är duplicates så är fråga du borde köra något åt detta hållet, alltså radera dubbletterna med "fel" key istället för att uppdatera.

Tror inte detta är valid SQL, vart mest redis och mongo efter semestern. och jag hade aldrig kört en random muterande fråga mot min databas från ngn tomte på internet heller. Men du kan ju byta DELETE till SELECT och se att datat verkar sane innan du kör, samt fixat frågesyntaxen.

DELETE FROM "mangadex" WHERE entry LIKE 'mangadex%'; AND EXISTS REPLACE(entry, 'mangadex', '')

"Radera allt som börjar med "mangadex" och där det redan finns en rad som innehåller "texten som kommer efter mangadex".

Förtydliga radera istället för att uppdatera
Permalänk
Medlem
Skrivet av TorrentKatten:

Du svarar på din egen fråga där, kolumnen entry är markerad som primary key (alternativt som unique), så du har redan ett entry i databasen som har värdet "35179607-27ce-4f1f-b72e-8e3fcb61f97f_3", vilket gör att uppdateringen som ska strippa "mangadex" från mangadex35179607-27ce-4f1f-b72e-8e3fcb61f97f_3 fallerar. Så om allt är duplicates så är fråga du borde köra något åt detta hållet, alltså radera dubbletterna med "fel" key istället för att uppdatera.

Tror inte detta är valid SQL, vart mest redis och mongo efter semestern. och jag hade aldrig kört en random muterande fråga mot min databas från ngn tomte på internet heller. Men du kan ju byta DELETE till SELECT och se att datat verkar sane innan du kör, samt fixat frågesyntaxen.

DELETE FROM "mangadex" WHERE entry LIKE 'mangadex%'; AND EXISTS REPLACE(entry, 'mangadex', '')

"Radera allt som börjar med "mangadex" och där det redan finns en rad som innehåller "texten som kommer efter mangadex".

Stort tack för ditt svar, tror detta verkar vara mer åt hållet jag borde gå, men ditt exempel fungerar tyvärr inte. Kommandot tar ju slut innan AND, och ändrar jag det så får jag

gallerydl=# SELECT FROM "mangadex" WHERE entry LIKE 'mangadex%' AND EXISTS REPLACE(entry, 'mangadex', ''); ERROR: syntax error at or near "REPLACE" LINE 5: EXISTS REPLACE(entry, 'mangadex', '');

Tyvärr är jag allt annat än hajj på hur jag ska skriva så det ska fungera. Som antagligen blivit uppenbart är jag väldigt novis när det gäller SQL.

Visa signatur

Desktop: Ryzen 7 5700G / 48GB RAM / RX 7900 XTX / 2TB SSD / Arch Linux
Server 1: i5-7400T / 32GB RAM / 88TB HDD / Proxmox
Server 2: i5-6500T / 32GB RAM / 1TB SSD / Proxmox

Permalänk
Medlem

Fick hjälp av min vän @kaktuszon som spann vidare på @TorrentKatten's idé, denna query fungerade till slut utmärkt:

DELETE FROM mangadex WHERE entry LIKE 'mangadex%' AND EXISTS (SELECT 1 FROM mangadex AS m2 WHERE m2.entry = REPLACE(mangadex.entry, 'mangadex', ''));

Tack alla som engagerat sig

Visa signatur

Desktop: Ryzen 7 5700G / 48GB RAM / RX 7900 XTX / 2TB SSD / Arch Linux
Server 1: i5-7400T / 32GB RAM / 88TB HDD / Proxmox
Server 2: i5-6500T / 32GB RAM / 1TB SSD / Proxmox