When I run this request I get an error
recursive query "cte_0" column 1 has type date in non-recursive term but type timestamp without time zone overall LINE 4: select min(to_char(date_trunc(‘month’, day), ‘yyyy-mm-d…
^ HINT: Cast the output of the non-recursive term to the correct type.
In this query, the "day" column is a column with a data type datetime
.
Query:
with recursive cte_0 as
(
select min(to_char(date_trunc('month', day), 'yyyy-mm-dd')::date) as dt
from Transactions
union all
select dt + interval '1 month'
from cte_0
where dt < (select max(to_char(date_trunc('month', day), 'yyyy-mm-dd')::date) from Transactions )
)
select * from cte_0
+----------------+------------+----------+--------+---------------------+
| transaction_id | account_id | type | amount | day |
+----------------+------------+----------+--------+---------------------+
| 2 | 3 | Creditor | 107100 | 2021-06-02 11:38:14 |
| 4 | 4 | Creditor | 10400 | 2021-06-20 12:39:18 |
| 11 | 4 | Debtor | 58800 | 2021-07-23 12:41:55 |
| 1 | 4 | Creditor | 49300 | 2021-05-03 16:11:04 |
| 15 | 3 | Debtor | 75500 | 2021-05-23 14:40:20 |
| 10 | 3 | Creditor | 102100 | 2021-06-15 10:37:16 |
| 14 | 4 | Creditor | 56300 | 2021-07-21 12:12:25 |
| 19 | 4 | Debtor | 101100 | 2021-05-09 15:21:49 |
| 8 | 3 | Creditor | 64900 | 2021-07-26 15:09:56 |
| 7 | 3 | Creditor | 90900 | 2021-06-14 11:23:07 |
+----------------+------------+----------+--------+---------------------+
I want to get:
2021-05-01
2021-06-01
2021-07-01
I tried changing the data type, but I couldn’t fix this error
3
Answers
If you add an
interval
to adate
, the result will be atimestamp without time zone
. PostgreSQL complains that these data types are different.I recommend that you use
timestamp
in the CTE and cast todate
in the main query:The error message identifies a solution: i.e., cast the expression
dt + interval '1 month'
toDATE
:Instead of using a recursive CTE, use
GENERATE_SERIES
:This approach is usually more efficient than using a recursive CTE. If
dt
is to be used in other expressions that involve timestamps, then remove::DATE
to avoid unnecessary type cast and only cast toDATE
when required.Consider renaming the column
day
intransactions
to avoid using a SQL reserved word as a user identifier.To be perfecly honest, you would be far better just using
generate_series
to generate these rows, rather than messing with recursive CTEs, which are inefficient.Also, rather than doing
min(to_char(date_trunc('month', day), 'yyyy-mm-dd')::date)
, it’s almost certainly better to do themin
andmax
first, likedate_trunc('month', min(day))::date
.It might be faster to do the
min
andmax
in a single query, rather than two separate subqueries, but it depends on indexing.