Streamlining your tech stack for maximum efficiency

Learn, explore, and grow with our knowledge hub.

Stop Duplicate Supplier Payments with SQL | Smart Statistics
SQL + Power BI Practical Tutorial

Stop Duplicate Supplier Payments Before They Leave the Bank

Duplicate payments are rarely as simple as finding two completely identical records.

The same supplier invoice can arrive twice with a slightly different reference, a changed date, extra spaces, a missing prefix or a manual re-entry.

In this tutorial, we will build an SQL-based invoice risk monitor that identifies suspicious pairs, assigns a risk score and sends the highest-risk exceptions to finance for review.

Detect early Find suspicious pairs before payment.
Rank risk Prioritise the strongest matches.
Keep control Finance reviews the exception.
Learn patterns Use Power BI to improve the process.
Invoice Risk Monitor Finance exception control
Illustrative example
Invoices Analysed 4,218 Illustrative monthly volume
High Risk 3 Requires finance review
Medium Risk 15 Review if unresolved
Reviewed 84% Illustrative completion

Exceptions by Risk Band

Illustrative candidate pairs
3
High
15
Medium
28
Low

Finance Review Queue

Highest-risk candidates first
Same supplier + amount + reference Candidate pair INV-10482
93
Same supplier + similar reference Dates four days apart
71
Same amount only Weak matching evidence
24
The Real Problem

Duplicate Invoices Rarely Look Perfectly Identical

An exact duplicate query is useful, but finance controls become much stronger when you also look for combinations of matching evidence.

Exact Re-entry

The same supplier, invoice number, amount and date are entered twice. This is the easiest duplicate to detect.

Reference Variation

INV-10482, INV10482 and 10482 may refer to the same supplier document even though the raw text is different.

Date Movement

A duplicate can be entered days later, making a simple same-date check too restrictive.

Legitimate Similarity

Recurring suppliers can issue invoices with the same amount every month. Matching values alone do not prove duplication.

Control Architecture

Build a Detection Process, Not Just a Query

Invoice Data

Supplier, invoice number, amount, date and reference fields.

Normalise

Standardise references before comparing records.

Pair Match

Compare plausible invoice candidates rather than every possible pair.

Risk Score

Combine several pieces of matching evidence.

Finance Review

Investigate high-risk exceptions and record the outcome.

Hands-On SQL Tutorial

Build the Monitor Step by Step

The SQL examples below use T-SQL-style syntax suitable for SQL Server and Azure SQL patterns. Adapt field names and functions to your own platform.

Start with a reliable invoice table

Your matching process needs stable invoice-level fields. A useful starting structure might include:

  • InvoiceID
  • SupplierID
  • InvoiceNumber
  • InvoiceDate
  • InvoiceAmount
  • PurchaseOrderNumber
  • PaymentReference
  • CreatedDateTime
  • PaymentStatus
CREATE TABLE dbo.SupplierInvoice
(
    InvoiceID            bigint          NOT NULL PRIMARY KEY,
    SupplierID           int             NOT NULL,
    InvoiceNumber        nvarchar(100)   NULL,
    InvoiceDate          date            NOT NULL,
    InvoiceAmount        decimal(18,2)   NOT NULL,
    PurchaseOrderNumber  nvarchar(100)   NULL,
    PaymentReference     nvarchar(100)   NULL,
    CreatedDateTime      datetime2       NOT NULL,
    PaymentStatus        nvarchar(30)    NULL
);
Keep the original values. Normalised matching fields should supplement your source data rather than overwrite the finance record.

Normalise invoice references before matching

Differences such as spaces, dashes and character case should not automatically prevent two otherwise similar invoice references from being compared.

SELECT
    InvoiceID,
    SupplierID,
    InvoiceNumber,

    UPPER(
        REPLACE(
            REPLACE(
                REPLACE(
                    LTRIM(RTRIM(InvoiceNumber)),
                    ' ',
                    ''
                ),
                '-',
                ''
            ),
            '/',
            ''
        )
    ) AS NormalisedInvoiceNumber,

    InvoiceDate,
    InvoiceAmount,
    PurchaseOrderNumber

FROM dbo.SupplierInvoice;

Examples:

INV-10482   → INV10482
INV 10482   → INV10482
inv/10482   → INV10482
Do not remove so many characters that unrelated invoice references become indistinguishable. Normalisation should reflect the patterns seen in your actual supplier data.

Find the strongest duplicate candidates

Start with pairs that match on supplier, amount and normalised invoice number.

WITH InvoiceBase AS
(
    SELECT
        InvoiceID,
        SupplierID,
        InvoiceDate,
        InvoiceAmount,
        PurchaseOrderNumber,

        UPPER(
            REPLACE(
                REPLACE(
                    REPLACE(
                        LTRIM(RTRIM(InvoiceNumber)),
                        ' ',
                        ''
                    ),
                    '-',
                    ''
                ),
                '/',
                ''
            )
        ) AS NormalisedInvoiceNumber

    FROM dbo.SupplierInvoice
)

SELECT
    A.InvoiceID AS InvoiceID_A,
    B.InvoiceID AS InvoiceID_B,
    A.SupplierID,
    A.NormalisedInvoiceNumber,
    A.InvoiceAmount,
    A.InvoiceDate AS InvoiceDate_A,
    B.InvoiceDate AS InvoiceDate_B

FROM InvoiceBase A

INNER JOIN InvoiceBase B
    ON  A.SupplierID = B.SupplierID
    AND A.NormalisedInvoiceNumber = B.NormalisedInvoiceNumber
    AND A.InvoiceAmount = B.InvoiceAmount
    AND A.InvoiceID < B.InvoiceID;

The final condition is important:

A.InvoiceID < B.InvoiceID

It prevents the same pair appearing twice as A/B and B/A, and prevents an invoice matching itself.

Extend the search to near duplicates

Exact matching will miss cases where the same invoice has been entered with a slightly different reference or date.

Create a broader candidate set using conditions such as:

  • Same SupplierID
  • Same InvoiceAmount
  • Invoice dates within a defined window
  • Same purchase order
  • Same or similar normalised invoice reference
SELECT
    A.InvoiceID AS InvoiceID_A,
    B.InvoiceID AS InvoiceID_B,

    A.SupplierID,
    A.InvoiceAmount,

    A.InvoiceDate AS InvoiceDate_A,
    B.InvoiceDate AS InvoiceDate_B,

    ABS(
        DATEDIFF(
            day,
            A.InvoiceDate,
            B.InvoiceDate
        )
    ) AS DateDifferenceDays

FROM dbo.SupplierInvoice A

INNER JOIN dbo.SupplierInvoice B
    ON  A.SupplierID = B.SupplierID
    AND A.InvoiceAmount = B.InvoiceAmount
    AND A.InvoiceID < B.InvoiceID

WHERE
    ABS(
        DATEDIFF(
            day,
            A.InvoiceDate,
            B.InvoiceDate
        )
    ) <= 14;
Candidate generation should deliberately be broader than final risk classification. The next stage determines whether the pair deserves attention.

Score the evidence instead of using one rule

A weighted score makes it easier to distinguish a weak coincidence from a very strong duplicate candidate.

An illustrative weighting model could be:

  • Same supplier: 20 points
  • Same amount: 25 points
  • Same normalised invoice number: 35 points
  • Invoice dates within seven days: 10 points
  • Same purchase order: 10 points
SELECT
    A.InvoiceID AS InvoiceID_A,
    B.InvoiceID AS InvoiceID_B,

    (
        CASE
            WHEN A.SupplierID = B.SupplierID
            THEN 20
            ELSE 0
        END
        +
        CASE
            WHEN A.InvoiceAmount = B.InvoiceAmount
            THEN 25
            ELSE 0
        END
        +
        CASE
            WHEN A.NormalisedInvoiceNumber =
                 B.NormalisedInvoiceNumber
            THEN 35
            ELSE 0
        END
        +
        CASE
            WHEN ABS(
                DATEDIFF(
                    day,
                    A.InvoiceDate,
                    B.InvoiceDate
                )
            ) <= 7
            THEN 10
            ELSE 0
        END
        +
        CASE
            WHEN A.PurchaseOrderNumber =
                 B.PurchaseOrderNumber
            THEN 10
            ELSE 0
        END
    ) AS RiskScore

FROM InvoiceBase A

INNER JOIN InvoiceBase B
    ON  A.SupplierID = B.SupplierID
    AND A.InvoiceID < B.InvoiceID;

You could then translate the score into bands:

CASE

    WHEN RiskScore >= 80
        THEN 'High'

    WHEN RiskScore >= 55
        THEN 'Medium'

    ELSE 'Low'

END AS RiskBand
The point values and thresholds above are illustrative. Calibrate them against genuine duplicate-payment cases and legitimate invoice patterns.

Persist a finance review queue

Do not force analysts to rerun a query and remember what they reviewed yesterday.

Create a controlled exception table containing:

  • CandidatePairID
  • InvoiceID_A
  • InvoiceID_B
  • RiskScore
  • RiskBand
  • DetectedDateTime
  • ReviewStatus
  • ReviewedBy
  • ReviewedDateTime
  • ReviewOutcome
  • ReviewComment
CREATE TABLE dbo.InvoiceRiskQueue
(
    CandidatePairID     bigint IDENTITY PRIMARY KEY,

    InvoiceID_A         bigint NOT NULL,
    InvoiceID_B         bigint NOT NULL,

    RiskScore           int NOT NULL,
    RiskBand            nvarchar(20) NOT NULL,

    DetectedDateTime    datetime2 NOT NULL,

    ReviewStatus        nvarchar(30) NOT NULL,
    ReviewedBy          nvarchar(200) NULL,
    ReviewedDateTime    datetime2 NULL,

    ReviewOutcome       nvarchar(50) NULL,
    ReviewComment       nvarchar(1000) NULL
);
Capturing the review outcome creates feedback data. Over time, you can compare confirmed duplicates with false positives and improve the model.

Put the control under Power BI oversight

Once the review queue exists, Power BI can tell management whether the control is operating effectively.

Useful measures include:

Flagged Invoice Pairs =
COUNTROWS(
    'Invoice Risk Queue'
)
High Risk Pairs =
CALCULATE(
    [Flagged Invoice Pairs],
    'Invoice Risk Queue'[RiskBand] = "High"
)
Open Reviews =
CALCULATE(
    [Flagged Invoice Pairs],
    'Invoice Risk Queue'[ReviewStatus] = "Open"
)
Confirmed Duplicate Pairs =
CALCULATE(
    [Flagged Invoice Pairs],
    'Invoice Risk Queue'[ReviewOutcome]
        = "Confirmed Duplicate"
)

You can then analyse risk by supplier, reviewer, month, business entity, category or payment status.

Interactive Example

Test an Illustrative Invoice Pair

Toggle the matching evidence below to see how a weighted review score can prioritise finance exceptions.

Matching Evidence

Select the characteristics shared by the two invoices.

Illustrative Risk Result

This score is a prioritisation tool, not proof of duplication.

90 Risk Score
High Risk — Finance Review Required
Strong matching evidence exists across supplier, amount, reference and date.
Management Visibility

Don't Just Find Duplicates — Measure the Control

Power BI can show whether suspicious invoices are being reviewed quickly and whether particular suppliers or processes repeatedly create exceptions.

Risk Trend

Track high-, medium- and low-risk candidate pairs over time.

Supplier Concentration

Identify suppliers generating unusually high numbers of review cases.

Review Ageing

Monitor how long finance exceptions remain unresolved.

Model Precision

Compare confirmed duplicates with false positives to improve matching rules.

Control Design

The Algorithm Flags Risk. Finance Makes the Decision.

A technically sophisticated matching model can still become a poor business control if ownership, review and audit evidence are missing.

Named Ownership

Define who reviews high-risk invoice candidates and who owns unresolved exceptions.

Audit Evidence

Record who reviewed each candidate, the decision, the date and the supporting comment.

Human Verification

Treat the model as a risk detector rather than automatic proof that a payment is invalid.

Continuous Calibration

Use confirmed outcomes to tune score thresholds, weights and supplier-specific rules.

Frequently Asked Questions

SQL Duplicate Invoice FAQs

Can SQL automatically find duplicate invoices?
Yes. SQL can identify exact and near-duplicate candidates using combinations of supplier, amount, invoice reference, dates, purchase order and other attributes. Similar records can still be legitimate, so detection and approval should remain separate.
Why not just check invoice number and supplier?
That works for clean exact duplicates, but invoice references can contain formatting differences, manual changes or missing characters. A broader evidence model can catch cases an exact match misses.
Should the SQL query automatically stop payment?
A safer design is normally to identify risk, prioritise the candidate and allow an authorised finance reviewer to confirm the appropriate action.
What risk score should we use?
There is no universal threshold. Use your own historical invoice data, confirmed duplicates and false-positive cases to calibrate the model.
Can this work with Excel invoice data?
Yes. Excel files can feed a database or data platform before the SQL matching process runs. The architecture should still preserve reliable invoice identifiers and source values.
Can Power Automate be added?
Yes. A wider solution could use automation to route new high-risk exceptions to a finance review process, provided permissions and approval controls are designed appropriately.
What should Power BI show?
Useful reporting includes risk-band trends, open review ageing, suppliers generating repeated exceptions, confirmed duplicate cases and review outcomes.
How can Smart Statistics help?
Smart Statistics can design SQL analytics, finance-control reporting, Power BI dashboards, Power Apps and Power Automate solutions tailored to your organisation's processes.

Your Finance Team Shouldn't Have to Discover a Duplicate After the Payment Has Already Gone.

Smart Statistics helps UK businesses turn operational data into practical controls using SQL, Power BI, Power Apps, Power Automate and Microsoft Fabric.

The objective is not another dashboard. It is a better process: identify risk earlier, focus human attention where it matters and retain evidence of the decisions made.