How to convert below Oracle query to Postgres? Below is the error
ERROR: syntax error at or near "BY"¶ Position: 321
Query
SELECT listagg(app_rule_cd,',') within GROUP (
ORDER BY abc_cd) AS ERR_LST,
'1' AS JOIN1
FROM ABC_RULE
WHERE abc_cd IN
( WITH CTE AS
(SELECT VAL FROM config_server WHERE NAME = 'XXXXXXXXXX'
)
SELECT TRIM(REGEXP_SUBSTR( VAL, '[^,]+', 1, LEVEL))
FROM CTE
CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(VAL, '[^,]+')) + 1 // BY is position 321
);
You did not explain what this query does, but the convoluted connect by level and regexp_replace() is a typically pattern to split a comma separated string into elements in Oracle.
That can be done way easier in Postgres:
SELECT string_agg(app_rule_cd,',' ORDER BY abc_cd) AS ERR_LST,
'1' AS JOIN1
FROM ABC_Rule
WHERE abc_cd = ANY ( (SELECT string_to_array(val, ',')
FROM config_server WHERE NAME = 'XXXXXXXXXX') )
Note the duplicated parentheses around the sub-query are necessary. Another way is to use the IN operator:
SELECT string_agg(app_rule_cd,',' ORDER BY abc_cd) AS ERR_LST,
'1' AS JOIN1
FROM ABC_Rule
WHERE abc_cd IN (SELECT unnest(string_to_array(val, ','))
FROM config_server
WHERE NAME = 'XXXXXXXXXX')
Related
I'm trying to run the following query with postgres's support for LATERAL subqueries:
with s as
(
select
tagValues ->> 'mode' as metric
, array_agg(id) as ids
from
metric_v3.v_series
where
name = 'node_cpu'
group by 1
)
select
t.starttime
, s.metric
, t.max
from
s, lateral (
select
d.starttime
, max(d.max) as max
from
metric_v3.gaugedata d
where
d.starttime >= '2020-01-17T00:00Z' AND d.starttime < '2020-01-24T00:00Z'
and d.seriesid in s.ids
group by 1
) t
order by 1,2;
It fails with, where s relates to the reference in the lateral subquery's where clause.
SQL Error [42601]: ERROR: syntax error at or near "s"
I have tried different methods for the lateral query, but I always get the same error. I don't know what I'm missing.
If I run the CTE expression and select s.* from it, I get the expected results, so that part is working fine.
I'm running Postgres 11.6 on CentOS.
You can't use IN with an array. You need to use the ANY operator:
and d.seriesid = any(s.ids)
I'm trying to split up an arrayed column that was created using an array_agg. the following is severely cut down, original query is something like 210 lines
select
distinct on (visitor.id)
id,
array_agg(distinct item.code::text)
from
8xfullouter joins
where
exists(
select
distinct on(visitor.id)
item.code::text
from
3 full outer join that all appear in the first query
group by
visit.id,
item.code
order by
visit.id
)
I need to break the array_agg(distinct item.code::text) into multiple columns. I've tried split_part(array_agg(distinct item.code::text), ',', 1) but received the following
> [Err] ERROR: function split_part(character varying[], unknown,
> integer) does not exist LINE 159: split_part (array_agg(distinct
> "public".procedure_group_cpt_...
thanks!!!
Use an index of the array, e.g.:
select (array_agg(code::text))[1]
from (values ('code1'), ('code2')) as codes(code)
array_agg
-----------
code1
(1 row)
Alternatively, you can use string_agg() instead of array_agg(), e.g.:
select split_part(string_agg(code::text, ','), ',', 1)
from (values ('code1'), ('code2')) as codes(code)
split_part
------------
code1
(1 row)
I'm writing a query with some CASE expressions and it outputs helper-data columns which help me determine whether or not a specific action is required. I would like to know if I can somehow use the result of a subquery as the output without having to perform the same query twice (between WHEN (subquery) THEN and as the result after THEN)
The dummy code below describes what I'm after. Can this be done? I'm querying a MS2005 SQL database.
SELECT 'Hello StackOverflow'
,'Thanks for reading this question'
,CASE
WHEN
(
SELECT count(*)
FROM sometable
WHERE condition = 1
AND somethingelse = 'value'
) > 0 THEN
-- run the query again to get the number of rows
(
SELECT count(*)
FROM sometable
WHERE condition = 1
AND somethingelse = 'value'
)
ELSE 0
END
SELECT 'Hello StackOverflow'
,'Thanks for reading this question'
,CASE
WHEN
(
SELECT count(*)
FROM sometable
WHERE condition = 1
AND somethingelse = 'value'
) AS subqry_count > 0 THEN
-- use the subqry_count, which fails... "Incorrect syntax near the keyword 'AS'"
subqry_count
ELSE 0
END
Just use the subquery as the source you are selecting from:
SELECT 'Hello StackOverflow'
,'Thanks for reading this question'
,CASE subqry_count.Cnt
WHEN 0 THEN 0
ELSE subqry_count.Cnt
END
FROM ( SELECT count(*) AS Cnt
FROM sometable
WHERE condition = 1
AND somethingelse = 'value'
) subqry_count
As an aside, if you are just going to return 0 if the output from COUNT is 0, then you don't even need to use a CASE statement.
If I try to UNION (or INTERSECT or EXCEPT) a common table expression I get a syntax error near the UNION. If instead of using the CTE I put the query into the union directly, everything works as expected.
I can work around this but for some more complicated queries using CTEs makes things much more readable. I also just don't like not knowing why something is failing.
As an example, the following query works:
SELECT *
FROM
(
SELECT oid, route_group
FROM runs, gpspoints
WHERE gpspoints.oid = runs.start_point_oid
UNION
SELECT oid, route_group
FROM runs, gpspoints
WHERE gpspoints.oid = runs.end_point_oid
) AS allpoints
;
But this one fails with:
ERROR: syntax error at or near "UNION"
LINE 20: UNION
WITH
startpoints AS
(
SELECT oid, route_group
FROM runs, gpspoints
WHERE gpspoints.oid = runs.start_point_oid
),
endpoints AS
(
SELECT oid, route_group
FROM runs, gpspoints
WHERE gpspoints.oid = runs.end_point_oid
)
SELECT *
FROM
(
startpoints
UNION
endpoints
) AS allpoints
;
The data being UNIONed together is identical but one query fails and the other does not.
I'm running PostgreSQL 9.3 on Windows 7.
The problem is because CTEs are not direct text-substitutions and a UNION b is invalid SELECT syntax. The SELECT keyword is a mandatory part of the parsing and the syntax error is raised before the CTEs are even taken into account.
This is why
SELECT * FROM a
UNION
SELECT * FROM b
works; the syntax is valid, and then the CTEs (represented by a and b) are then used at the table-position (via with_query_name).
At least in SQL Server, I can easily do this - create two CTE's, and do a SELECT from each, combined with a UNION:
WITH FirstNames AS
(
SELECT DISTINCT FirstName FROM Person
), LastNames AS
(
SELECT DISTINCT LastName FROM Person
)
SELECT * FROM FirstNames
UNION
SELECT * FROM LastNames
Not sure if this works in Postgres, too - give it a try!
I've written a simple query that uses a WITH clause, but I'm getting this error:
Error : ERROR: missing FROM-clause entry for table "cte"
Here's the query, where I'm clearly putting a FROM clause. I know this must be simple but I'm just not seeing what I've done wrong. Thanks.
WITH cte AS (
SELECT cident, "month"
FROM orders_extended io
WHERE io.ident = 1 -- 1 will be replaced with a function parameter
)
SELECT *
FROM orders_extended o
WHERE o.cident = cte.cident AND o."month" = cte."month"
ORDER BY o."month" DESC, o.cname
The message didn't lie.
WITH cte AS (
SELECT cident, "month"
FROM orders_extended io
WHERE io.ident = 1 -- 1 will be replaced with a function parameter
)
SELECT o.*
FROM orders_extended o
INNER JOIN cte ON (o.cident = cte.cident and o."month" = cte."month")
ORDER BY o."month" DESC, o.cname