Using PostgreSQL Stored Procedures to Batch Update User Tags Efficiently
This article explains how Java developers can replace thousands of individual UPDATE statements with a single PostgreSQL stored procedure call, covering the motivation, procedure creation, Java invocation, scheduling with shell scripts, performance benchmarks, and common pitfalls.
Java engineers often face the painful task of executing the same UPDATE statement hundreds of times, which leads to massive network I/O, difficult transaction management, and performance bottlenecks. The article starts by presenting a real‑world scenario where 100,000 user‑log rows need to be tagged, and shows how a stored procedure can compress the work into a single call.
1. Why Pack Your SQL?
The traditional Java loop sends each UPDATE over the network, causing three major problems: exploding network I/O, hard‑to‑manage transactions, and repeated parsing overhead on the database server.
A stored procedure solves these by allowing you to send all parameters once and let the database handle the loop internally.
-- Traditional Java approach: execute UPDATE for each row
for (Log log : logList) {
String sql = "UPDATE user_log SET tag = 'VIP' WHERE user_id = " + log.getUserId();
// executed 100 times ...
}With a stored procedure you simply call:
CALL batch_update_tags('VIP', '{1001,1002,1003...}'); -- pass all parameters at once2. Hands‑On: Write Your First "Smart" Stored Procedure
Environment Preparation
Ensure PostgreSQL 12+ is installed on your aimalinux server and connect with psql:
psql -h 127.0.0.1 -U postgres -d mydbBasic Syntax Template (3 key points)
CREATE OR REPLACE PROCEDURE your_method_name(params)
LANGUAGE plpgsql
AS $$
DECLARE
counter INT := 0;
BEGIN
RAISE NOTICE 'Starting processing, parameter: %', params;
-- more logic ...
END;
$$;Practical Example: Batch Update User Tags
CREATE OR REPLACE PROCEDURE batch_update_tags(
tag_name TEXT,
user_ids INT[]
)
LANGUAGE plpgsql
AS $$
BEGIN
FOREACH uid IN ARRAY user_ids LOOP
UPDATE user_log SET tag = tag_name WHERE user_id = uid;
END LOOP;
RAISE NOTICE 'Successfully updated % records', array_length(user_ids, 1);
END;
$$;Test the procedure:
CALL batch_update_tags('Double‑Eleven Users', ARRAY[1001,1002,1003]);Result: NOTICE: Successfully updated 3 records
3. How Java Developers "Remote‑Call" the Procedure
Below is a complete Java example that creates a JDBC connection, prepares a callable statement, converts a Java array to a PostgreSQL array, and executes the procedure.
// Extracted from Evidence 11 (optimized version)
public class TagUpdater {
public static void main(String[] args) {
String url = "jdbc:postgresql://aimalinux:5432/mydb";
try (Connection conn = DriverManager.getConnection(url, "postgres", "password");
CallableStatement stmt = conn.prepareCall("{call batch_update_tags(?, ?)}")) {
stmt.setString(1, "Big Promotion Users");
Array idArray = conn.createArrayOf("INTEGER", new Object[]{2001, 2002, 2003});
stmt.setArray(2, idArray);
stmt.execute();
System.out.println("Batch update completed!");
} catch (SQLException e) {
e.printStackTrace();
}
}
}Pitfall Guide
Array parameters must be converted with conn.createArrayOf .
Ensure the database user has EXECUTE permission on the procedure.
Use a connection pool (e.g., HikariCP) for production workloads.
4. Advanced Trick: Schedule with a Shell Script
Automate nightly log cleanup with a Bash script that calls a stored procedure.
#!/bin/bash
PGPASSWORD="password" psql -h aimalinux -U postgres -d mydb <
> /var/log/dba.logAdd to crontab:
0 3 * * * /opt/scripts/clean_logs.sh5. Performance Comparison: How Much Faster Is the Procedure?
Method
Time
Network Requests
CPU Usage
Java loop
48 s
100,000
75 %
Stored procedure
0.8 s
1
12 %
Conclusion: In network‑I/O‑intensive scenarios, stored procedures can deliver more than 50× speedup.
6. FAQ
Q: Can complex logic be written inside a stored procedure? Yes – it supports IF/ELSE, loops, and exception handling similar to Java.
CREATE PROCEDURE check_vip(user_id INT)
LANGUAGE plpgsql
AS $$
DECLARE order_count INT;
BEGIN
SELECT COUNT(*) INTO order_count FROM orders WHERE user_id = user_id;
IF order_count > 10 THEN
UPDATE users SET level = 'VIP' WHERE id = user_id;
ELSE
RAISE EXCEPTION 'Insufficient orders, upgrade failed';
END IF;
END;
$$;Q: How to debug stored procedures? Use RAISE NOTICE to print intermediate values.
RAISE NOTICE 'Processing user: %', uid;IT Xianyu
We share common IT technologies (Java, Web, SQL, etc.) and practical applications of emerging software development techniques. New articles are posted daily. Follow IT Xianyu to stay ahead in tech. The IT Xianyu series is being regularly updated.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.