How to fix Too many rows in PostgreSQL

PostgreSQLINTERMEDIATEMEDIUMUpdated Dec 28, 2025· 171 views

The "too many rows" error (P0003) occurs in PL/pgSQL when a SELECT INTO STRICT statement returns multiple rows instead of exactly one. Fix it by adding LIMIT 1, tightening WHERE clauses, or removing STRICT.

Quick fix

Fixes P0003

BEGIN
  SELECT * INTO STRICT myrec FROM users WHERE email = user_email LIMIT 1;
EXCEPTION
  WHEN TOO_MANY_ROWS THEN
    RAISE EXCEPTION 'Multiple users found with email %', user_email;
END;
  1. Add LIMIT 1 to return only the first matching row
  2. Tighten your WHERE clause to guarantee uniqueness
  3. Remove STRICT if you only need the first result
  4. Add exception handling to catch and log the error
postgresql — error
BEGIN
SELECT * INTO STRICT myrec FROM users WHERE email = user_email LIMIT 1;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
RAISE EXCEPTION 'Multiple users found with email %', user_email;
END;
Too many rows

What this error means

PostgreSQL throws the "too many rows" error (SQLSTATE P0003) when using SELECT INTO with the STRICT option and the query returns more than one row. The STRICT keyword enforces that a query must return exactly one row—if it returns zero rows, you get NO_DATA_FOUND (P0002); if it returns multiple rows, you get TOO_MANY_ROWS (P0003). This is a critical safety check in PL/pgSQL functions to prevent silent data inconsistencies.

Typical symptoms

  • PL/pgSQL function execution fails with error code P0003
  • SELECT INTO STRICT statement throws exception when query matches multiple records
  • Application receives "ERROR: query returned more than one row" message
  • Function intended to find unique records instead finds duplicates
  • RETURNING clause used with STRICT also triggers this error with multiple results

Common causes

  1. Using SELECT INTO STRICT without a WHERE clause that guarantees uniqueness
  2. WHERE clause matches multiple rows when it should match exactly one
  3. Missing LIMIT 1 clause on a query that can return multiple rows
  4. Business logic assumes uniqueness but data contains duplicates
  5. Using SELECT INTO STRICT on a non-unique column (e.g., category, status)
  6. Forgetting to add UNIQUE constraints or indexes on columns used in WHERE clause

How to fix "Too many rows"

1Add LIMIT 1 to return only the first matching row

If you only need one result and multiple rows may exist, add LIMIT 1 to your query:

sql
BEGIN
  SELECT * INTO STRICT myrec FROM users WHERE email = user_email LIMIT 1;
EXCEPTION
  WHEN TOO_MANY_ROWS THEN
    RAISE EXCEPTION 'Multiple users found with email %', user_email;
END;

This ensures only the first row is fetched and assigned to the variable.

2Tighten your WHERE clause to guarantee uniqueness

Refine the WHERE condition to match exactly one record. For example, use a primary key or unique identifier instead of a non-unique column:

sql
-- WRONG: matches multiple users with same email domain
SELECT * INTO STRICT user_rec FROM users WHERE email LIKE '%@example.com';

-- CORRECT: matches exactly one user by primary key
SELECT * INTO STRICT user_rec FROM users WHERE id = user_id;

Always use unique columns (id, uuid, email if unique constraint exists) in your WHERE clause when using STRICT.

3Remove STRICT if you only need the first result

If your function logic doesn't require exactly one row and you just want the first match, remove the STRICT keyword:

sql
BEGIN
  -- Without STRICT: assigns first row, ignores others
  SELECT id, name INTO user_id, user_name FROM users WHERE status = 'active';
  
  -- If no rows found, variables are set to NULL
  IF NOT FOUND THEN
    RAISE EXCEPTION 'No active users found';
  END IF;
END;

Without STRICT, the first row is assigned and remaining rows are ignored. Use this only when your logic accepts that behavior.

4Add exception handling to catch and log the error

Use BEGIN...EXCEPTION blocks to catch TOO_MANY_ROWS and handle it gracefully:

sql
DECLARE
  myrec RECORD;
BEGIN
  SELECT * INTO STRICT myrec FROM products WHERE sku = product_sku;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RAISE EXCEPTION 'Product with SKU % not found', product_sku;
  WHEN TOO_MANY_ROWS THEN
    RAISE EXCEPTION 'Data integrity error: product SKU % is not unique', product_sku;
END;

This provides clear error messages and allows graceful error handling in your application.

5Verify data integrity and add database constraints

Check if duplicate data exists in your table and add UNIQUE constraints to prevent future duplicates:

sql
-- Find duplicate values causing the error
SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1;

-- Add unique constraint to prevent future duplicates
ALTER TABLE users ADD CONSTRAINT uq_users_email UNIQUE (email);

-- Or create a unique index if some values can be NULL
CREATE UNIQUE INDEX idx_users_email ON users(email) WHERE email IS NOT NULL;

Database constraints ensure data integrity at the source and prevent this error from recurring.

Advanced notes

The print_strict_params setting can be enabled to include query parameters in error messages for debugging. By default, PostgreSQL does not include these details. You can enable it per-function using SET plpgsql.print_strict_params = on, or globally via postgresql.conf. This is useful for understanding which parameter value caused multiple matches. Additionally, if you need to process multiple rows intentionally, use cursors with FETCH instead of SELECT INTO: DECLARE cur CURSOR FOR SELECT ... ; FETCH ALL FROM cur;. For large result sets, cursors allow memory-efficient row-by-row processing without loading everything into memory.

Frequently asked questions

What does "P0003" mean?
PostgreSQL throws the "too many rows" error (SQLSTATE P0003) when using SELECT INTO with the STRICT option and the query returns more than one row. The STRICT keyword enforces that a query must return exactly one row—if it returns zero rows, you get NO_DATA_FOUND (P0002); if it returns multiple rows, you get TOO_MANY_ROWS (P0003). This is a critical safety check in PL/pgSQL functions to prevent silent data inconsistencies.
How do I fix "P0003"?
The "too many rows" error (P0003) occurs in PL/pgSQL when a SELECT INTO STRICT statement returns multiple rows instead of exactly one. Fix it by adding LIMIT 1, tightening WHERE clauses, or removing STRICT.
What causes "P0003"?
Using SELECT INTO STRICT without a WHERE clause that guarantees uniqueness WHERE clause matches multiple rows when it should match exactly one Missing LIMIT 1 clause on a query that can return multiple rows
Is the "P0003" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading