DAILY NEWS

Stay Ahead, Stay Informed – Every Day

Advertisement
PostgreSQL 22034 Error: Causes and Solutions Complete Guide


PostgreSQL Error 22034: more than one sql json item

PostgreSQL error code 22034 (more than one sql json item) occurs when a SQL/JSON function such as JSON_VALUE() or JSON_QUERY() encounters a JSON path expression that returns more than one item, while the function context expects exactly one. This error became more prevalent with the introduction of SQL-standard JSON functions in PostgreSQL 15 and later.

Top 3 Causes

1. Wildcard path in JSON_VALUE() returning multiple results

JSON_VALUE() strictly requires a single scalar return value. Using a wildcard like $across an array will match multiple elements and immediately trigger error 22034.

— Triggers 22034
SELECT JSON_VALUE(‘{“fruits”: (“apple”, “banana”, “cherry”)}’, ‘$.fruits’);

— Fix: specify an explicit index
SELECT JSON_VALUE(‘{“fruits”: (“apple”, “banana”, “cherry”)}’, ‘$.fruits(0)’);
— Result: “apple”

— Fix: suppress the error gracefully
SELECT JSON_VALUE(
‘{“fruits”: (“apple”, “banana”, “cherry”)}’,
‘$.fruits’
NULL ON ERROR
);
— Result: NULL

Enter fullscreen mode

Exit fullscreen mode

2. JSON_QUERY() without WITH ARRAY WRAPPER on multi-value paths

JSON_QUERY() also fails when a path resolves to multiple independent values and no wrapper option is provided to consolidate them into a single JSON array.

— Triggers 22034
SELECT JSON_QUERY(‘{“scores”: (95, 87, 76)}’, ‘$.scores’);

— Fix: wrap results into a JSON array
SELECT JSON_QUERY(
‘{“scores”: (95, 87, 76)}’,
‘$.scores’
WITH ARRAY WRAPPER
);
— Result: (95, 87, 76)

Enter fullscreen mode

Exit fullscreen mode

3. Navigating nested array structures with simple path expressions

Deeply nested JSON arrays compound the cardinality problem at every path step. Using JSON_VALUE() or JSON_QUERY() on paths that traverse multiple array levels without index constraints will almost always produce multiple results.

— Sample nested data
WITH doc AS (
SELECT ‘{“orders”: ({“id”:1}, {“id”:2}, {“id”:3})}’::jsonb AS data
)

— Triggers 22034 (multiple ids returned)
— SELECT JSON_VALUE(data::json, ‘$.orders.id’) FROM doc;

— Fix: use jsonb_path_query() to return a set of rows
SELECT jsonb_path_query(data, ‘$.orders.id’)
FROM doc;

— Fix: use jsonb_array_elements() for row-by-row processing
SELECT elem->>’id’ AS order_id
FROM doc, jsonb_array_elements(data->’orders’) AS elem;

Enter fullscreen mode

Exit fullscreen mode

Quick Fix Solutions

Scenario
Recommended Fix

Need only the first value
Use $.array(0) explicit index

Need all values as JSON array
JSON_QUERY(… WITH ARRAY WRAPPER)

Need all values as rows

jsonb_path_query() or jsonb_array_elements()

Want to avoid query failure
Add NULL ON ERROR clause

Complex nested structures
Use JSON_TABLE() (PostgreSQL 17+)

— JSON_TABLE() for structured unnesting (PostgreSQL 17+)
SELECT *
FROM JSON_TABLE(
‘{“orders”: ({“id”:1,”amt”:100},{“id”:2,”amt”:250})}’::json,
‘$.orders’
COLUMNS (
order_id INT PATH ‘$.id’,
amount INT PATH ‘$.amt’
)
) AS jt;

Enter fullscreen mode

Exit fullscreen mode

Prevention Tips

Always verify path cardinality before using scalar JSON functions.Before deploying queries with JSON path expressions into production, use jsonb_path_query_array() to check how many items a path returns. If the count exceeds one, switch to a set-returning function or add WITH ARRAY WRAPPER.

— Pre-flight cardinality check
SELECT jsonb_array_length(
jsonb_path_query_array(your_column, ‘$.some.path’)
)
FROM your_table
LIMIT 10;

Enter fullscreen mode

Exit fullscreen mode

Always declare explicit error and empty behavior clauses.Never rely on default behavior for SQL/JSON functions. Explicitly specifying NULL ON ERROR and NULL ON EMPTY prevents a single malformed or unexpectedly multi-valued JSON document from failing an entire query batch — especially critical when handling externally sourced JSON data.

SELECT JSON_VALUE(
payload::json,
‘$.event.type’
NULL ON EMPTY
NULL ON ERROR
)
FROM event_log;

Enter fullscreen mode

Exit fullscreen mode

Related Errors

22033 – invalid sql json subscript: bad array index in path expression

22032 – invalid json text: malformed JSON, often encountered before 22034

22035 – no sql json item: the opposite of 22034; path matches nothing

2203A – sql json scalar required: path returns an object/array where a scalar is expected

📖 Want a more detailed guide?Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.



Source link

Why PostgreSQL and ClickHouse Work So Well Together



A lot of people compare PostgreSQL and ClickHouse like they are competing databases.

They really are not.

In fact, modern data systems often use both together.

And once you understand what each database is optimized for, the reason becomes pretty obvious.

The biggest mistake people make is expecting both databases to behave similarly.

They are built for entirely different workloads.

PostgreSQL is primarily an OLTP database.

ClickHouse is primarily an OLAP database.

That single difference changes almost everything about how they think internally.

PostgreSQL is extremely good at handling transactional workloads.

Things like:

user data
payments
inventory
banking records
order systems
application state

These are systems where:

consistency matters
updates happen frequently
rows are modified constantly
transactions must be reliable

For example:

UPDATE inventory
SET stock = stock – 1
WHERE product_id = 101;

Enter fullscreen mode

Exit fullscreen mode

This kind of workload is where PostgreSQL shines.

You want:

ACID guarantees
reliable transactions
row-level updates
strong consistency

PostgreSQL is designed around exactly that.

ClickHouse approaches data very differently.

Instead of optimizing for frequent row updates, it optimizes for analytical queries across massive datasets.

Things like:

metrics
observability
logs
event streams
analytical dashboards
time-series workloads

For example:

SELECT
service_name,
avg(response_time_ms)
FROM metrics
WHERE timestamp >= now() – INTERVAL 1 HOUR
GROUP BY service_name;

Enter fullscreen mode

Exit fullscreen mode

This is a completely different style of workload.

Instead of:

modifying small numbers of rows

ClickHouse is optimized for:

scanning huge amounts of data efficiently
aggregating billions of records
compressing analytical datasets
fast columnar reads

This is honestly the simplest way I think about it now.

PostgreSQL usually stores:

current application state
transactional business data
operational records

ClickHouse usually stores:

analytical history
events
metrics
large-scale queryable telemetry

One powers the application.

The other explains what the application is doing.

This is where things get interesting.

In many modern architectures, PostgreSQL becomes the operational source of truth.

Then data flows into ClickHouse for analytics.

Something like this:

Application

PostgreSQL

CDC / Airbyte / Kafka

ClickHouse

Dashboards / Analytics / Observability

Enter fullscreen mode

Exit fullscreen mode

This pattern is far more common than many people realize.

Because each database is doing what it is best at.

PostgreSQL can do analytical queries.

But analytical workloads behave very differently from transactional workloads.

For example:

scanning billions of rows
large aggregations
observability queries
real-time analytics
historical trend analysis

These workloads stress databases differently.

ClickHouse is optimized around:

columnar storage
vectorized execution
aggressive compression
analytical query execution

That is why queries over huge datasets often feel dramatically faster in ClickHouse.

This is another common misunderstanding.

ClickHouse is incredible for analytics.

But transactional systems require things like:

frequent updates
transactional consistency
row-level modifications
operational application state

That is not the primary design goal of ClickHouse.

You generally do not want your:

user authentication system
banking transactions
inventory updates
operational business logic

to depend entirely on analytical database behavior.

What I personally find interesting is how these systems complement each other instead of replacing each other.

PostgreSQL handles:

ClickHouse handles:

That separation creates much cleaner architectures.

Instead of forcing one database to solve every problem, each system handles the workload it was designed for.

One thing that makes this architecture powerful is CDC (Change Data Capture).

Instead of manually exporting data repeatedly, systems can stream changes from PostgreSQL into ClickHouse continuously.

Tools like:

Debezium
Airbyte
Kafka pipelines

make this pattern extremely practical now.

The operational system continues running normally while analytical systems receive data almost in real time.

The differences go deeper than just “transactions vs analytics”.

PostgreSQL thinks heavily about:

rows
transactional consistency
updates
locking
relational integrity

ClickHouse thinks heavily about:

columns
compression
merges
partitions
analytical scans
aggregation efficiency

Even their storage engines reflect completely different priorities.

Once you stop viewing databases as competitors and instead view them as workload-specific systems, the architecture starts making much more sense.

PostgreSQL handles the operational side.

ClickHouse handles the analytical side.

Together, they create systems that can:

process transactions reliably
scale analytical workloads efficiently
support observability
power dashboards
retain huge historical datasets

without forcing a single database to do everything.

The more I learn about databases, the more I realize that most modern architectures are really about separation of responsibilities.

PostgreSQL and ClickHouse work well together because they optimize for fundamentally different problems.

One is built to preserve business state reliably.

The other is built to analyze massive amounts of history efficiently.

And when combined properly, they complement each other extremely well.



Source link

RetailSale – Open Source Flutter Retail & POS System | Contributors Welcome



RetailSale is an open-source retail and inventory management system built with Flutter, and the project is open for contributions from developers, testers, UI designers, and the open-source community.

GitHub Repository: https://github.com/studentsdav/RetailSale

Windows Release (Ready to Use): https://github.com/studentsdav/RetailSale/releases/tag/1.0.0.0

The Windows version is already packaged and can be downloaded and tested directly without additional setup using “backend_installer.exe”.

RetailSale includes inventory management, product tracking, billing/POS system, sales reports, Flutter-based modern UI, and cross-platform support.

We are looking for contributors interested in UI/UX improvements, state management, API integration, bug fixing, performance optimization, documentation, testing, and overall feature enhancements.

The project is built using Flutter and Dart, with GitHub Actions planned for future automation and CI/CD workflows.

Both beginners and experienced developers are welcome to contribute. Feedback, pull requests, testing, feature suggestions, and community support are appreciated.

If you want to contribute, simply fork the repository, create your feature branch, commit your changes, and open a pull request.



Source link