Catch Duplicate & Related Adverse-Event Reports by Meaning

Find near-duplicate and related ICSRs before they inflate your case counts — semantic similarity over the report narrative, in one SQL query, no external vector store.

All recipes· pharmacovigilance· 5 minutesbeginnersql
Instance: localhost:8080

Opens your running SynapCores (Catch Duplicate & Related Adverse-Event Reports by Meaning will be staged for a preview — nothing runs until you click Run). No instance yet? Install free in ~30s.

Share

Objective

Individual Case Safety Report (ICSR) volume grows 20–30% a year, and a large fraction are duplicates or closely related reports of the same event — the same reaction, described in different words, arriving through different channels. Keyword matching misses them because the words differ ("muscle pain" vs "bilateral leg aches"). This recipe catches them by meaning, so a reviewer sees the whole cluster instead of processing the same case three times.

Everything runs in the same database that holds the cases — no export to a separate vector store.

Step 1: A tiny ICSR table with an embedding column

The VECTOR(384) column holds the 384-dimensional embedding of each report narrative. EMBED() computes it inline at insert time using the built-in all-minilm model.

DROP TABLE IF EXISTS pv_icsr;

CREATE TABLE pv_icsr (
    case_id   TEXT PRIMARY KEY,
    product   TEXT,
    reaction  TEXT,
    narrative TEXT,
    emb       VECTOR(384)
);

INSERT INTO pv_icsr VALUES
 ('US-2026-001','Atorvastatin','myalgia',
  'Patient reported muscle pain in both legs after two weeks on atorvastatin.',
  EMBED('Patient reported muscle pain in both legs after two weeks on atorvastatin.')),
 ('US-2026-002','Atorvastatin','muscle pain',
  'Male patient, bilateral leg muscle aches, began about 14 days into atorvastatin therapy.',
  EMBED('Male patient, bilateral leg muscle aches, began about 14 days into atorvastatin therapy.')),
 ('US-2026-003','Atorvastatin','headache',
  'Mild transient headache, resolved without intervention.',
  EMBED('Mild transient headache, resolved without intervention.'));

Step 2: Find likely duplicates of a new case

Score every other case against case US-2026-001 by cosine similarity of the narrative embedding. The subquery pulls the reference embedding; COSINE_SIMILARITY ranks the rest.

SELECT case_id,
       reaction,
       COSINE_SIMILARITY(emb, (SELECT emb FROM pv_icsr WHERE case_id = 'US-2026-001')) AS sim
FROM   pv_icsr
WHERE  case_id <> 'US-2026-001'
ORDER  BY sim DESC;

Result — the second report is flagged as a near-duplicate; the headache case is clearly distinct:

case_id reaction sim
US-2026-002 muscle pain 0.887
US-2026-003 headache 0.312

Why it matters

A single similarity query separates "the same event again" (0.89) from "a different event" (0.31). Route anything above your threshold to duplicate review, and you stop paying to process the same case twice — the first lever for keeping up with rising ICSR volume. The same query, run across the whole series instead of one reference case, is the basis of signal clustering and related-case grouping.

Pair it with a tamper-evident audit ledger so every dedup decision is inspection-ready.

Tags

pharmacovigilancedrug-safetyicsradverse-eventvectorembeddingssimilaritydeduplicationsignal-detection

Run this on your own machine

Install SynapCores Community Edition free, paste the SQL or Cypher above into the bundled web UI, and watch it run.

Download Free CE