How to convert oracle hierarchical queries to postgresql? - postgresql

I want to convert below mentioned oracle hierarchical query to postgresql
SELECT catalog_id, sub_tree_id
FROM my_catalog
CONNECT BY PRIOR catalog_id = sub_tree_id;
I have tried using the following postgresql query but not getting the expected result
WITH RECURSIVE q AS (
SELECT po.catalog_id,po.sub_tree_id
FROM my_catalog po
UNION ALL
SELECT po.catalog_id,po.sub_tree_id
FROM my_catalog po
JOIN q ON q.catalog_id=po.sub_tree_id
)
SELECT * FROM q;
ORACLE OUTPUT(EXPECTED RESULT)
POSTGRESQL OUTPUT(ACTUAL RESULT)

In PostgreSQL recursive queries are constructed by first specifying the initial set of rows (the non-recursive term, i.e. those at the root or final level of the hierarchy). Subsequent iterations (over the recursive term, the sub-query after the UNION ALL) then add rows to the result set from the remaining rows in the input row set until no more rows are added.
In your case, the initial sub-query is unfiltered so you simply add all rows on the initial run, leaving nothing for subsequent runs.
Try the following:
WITH RECURSIVE q AS (
SELECT po.catalog_id,po.sub_tree_id
FROM my_catalog po
WHERE sub_tree_id = 0 -- this initially selects only "root" rows
UNION ALL
SELECT po.catalog_id,po.sub_tree_id
FROM my_catalog po
JOIN q ON q.catalog_id=po.sub_tree_id
)
SELECT * FROM q;

Related

Return closest timestamp from Table B based on timestamp from Table A with matching Product IDs

Goal: Create a query to pull the closest cycle count event (Table C) for a product ID based on the inventory adjustments results sourced from another table (Table A).
All records from Table A will be used, but is not guaranteed to have a match in Table C.
The ID column will be present in both tables, but is not unique in either, so that pair of IDs and Timestamps together are needed for each table.
Current simplified SQL
SELECT
A.WHENOCCURRED,
A.LPID,
A.ITEM,
A.ADJQTY,
C.WHENOCCURRED,
C.LPID,
C.LOCATION,
C.ITEM,
C.QUANTITY,
C.ENTQUANTITY
FROM
A
LEFT JOIN
C
ON A.LPID = C.LPID
WHERE
A.facility = 'FACID'
AND A.WHENOCCURRED > '23-DEC-22'
AND A.ADJREASONABBREV = 'CYCLE COUNTS'
ORDER BY A.WHENOCCURRED DESC
;
This is currently pulling the first hit on C.WHENOCCURRED on the LPID matches. Want to see if there is a simpler JOIN solution before going in a direction that creates 2 temp tables based on WHENOCCURRED.
I have a functioning INDEX(MATCH(MIN()) solution in Excel but that requires exporting a couple system reports first and is extremely slow with X,XXX row tables.
If you are using Oracle 12 or later, you can use a LATERAL join and FETCH FIRST ROW ONLY:
SELECT A.WHENOCCURRED,
A.LPID,
A.ITEM,
A.ADJQTY,
C.WHENOCCURRED,
C.LPID,
C.LOCATION,
C.ITEM,
C.QUANTITY,
C.ENTQUANTITY
FROM A
LEFT OUTER JOIN LATERAL (
SELECT *
FROM C
WHERE A.LPID = C.LPID
AND A.whenoccurred <= c.whenoccurred
ORDER BY c.whenoccurred
FETCH FIRST ROW ONLY
) C
ON (1 = 1) -- The join condition is inside the lateral join
WHERE A.facility = 'FACID'
AND A.WHENOCCURRED > DATE '2022-12-23'
AND A.ADJREASONABBREV = 'CYCLE COUNTS'
ORDER BY A.WHENOCCURRED DESC;

How to apply order_by to a subquery with LIMIT in SQLAlchemy?

I have a large table and I need to limit my first query (subquery) and then apply ORDER BY to the result, something like this:
SELECT * FROM (
SELECT * FROM logs
LIMIT 10
) as tb
ORDER BY tb.date_time
In SQLAlchemy, this doesn't work:
session.query(Log).limit(10).order_by(Log.date_time)
because of this error:
sqlalchemy.exc.InvalidRequestError: Query.order_by() being called on a Query which already has LIMIT or OFFSET applied. Call order_by() before limit() or offset() are applied.
I tried the following ones and they didn't work as well:
sq = session.query(Log).limit(10).subquery().order_by(Log.received_time)
sq = session.query(Log).limit(10).subquery()
res = session.query(Log).order_by(sq.received_time)
How can I convert the above SQL script to SQLAlchemy syntax?

How to check an ascending ordered column value in where clause in postgresql?

I am new to postgresql. I want to join two tables if one geometry of first table is contained by the geometry of second table. So, I have written and executed this part of the query as following and it is running fine.
select edge.start_id, cls.gid
from edge_table edge
inner join cluster_info cls on st_contains(cls.geom,st_setsrid(edge.start_geom,3067));
But it is giving the start_id and its containing geom id (as mentioned cls.gid in the query) in a random order such as following:
start_id gid
26040 2493
43323 2490
26208 2400
42754 2433
43537 2434
1379 2434
43570 2904
42887 2475
43689 2495
43211 2904
But I need to insert the result in another column named start_cls in my edge table. I need to identify the row where the cls.gid should be inserted. So, I need to check the value of start_id for each row and the cls.gid corresponding to that start_id should be put in that row. Assume, four rows of my edge table are following:
gid start_id end_id start_geom end_geom start_cls end_cls
1 81608 81608 01010000007368912D8B622341E5D022EBEAF65A41 01010000007368912D8B622341E5D022EBEAF65A41
2 81557 81520 010100000085EB51F89C0723418B6CE7DB9F8E5A41 0101000000986E1203DE0723416DE7FB51A38E5A41
3 189898 80812 01010000006F1283C0A093214179E926F1A1005B41 0101000000BE9F1A6FF3942141022B871EEC005B41
4 80952 80476 0101000000666666E67F832341F2D24DBA38B45A41 0101000000736891EDB48423413BDF4F755AB45A41
I need to fill the start_cls column first. So, the cls.gid value of 81608 (first start_id) should be there at first row under start_cls column. So, I have given one where clause as following:
select edge.start_id, cls.gid
from edge_table edge
inner join cluster_info cls on st_contains(cls.geom,st_setsrid(edge.start_geom,3067))
where (select start_id from edge_table) = edge.start_id;
But, it is giving following error:
ERROR: more than one row returned by a subquery used as an expression
********** Error **********
ERROR: more than one row returned by a subquery used as an expression
SQL state: 21000
I tried with the following query too but no luck.
select edge.start_id, cls.gid
from edge_table edge
inner join cluster_info cls on st_contains(cls.geom,st_setsrid(edge.start_geom,3067))
where (select start
from (select start_id as start
from edge_table) as s) = edge.start_id;
Please help with this query. It has some geometry part but the main problem is in postgresql query organisation. So, I have raised this question in stackoverflow instead of gis.stackexchange.

comprare aggregate sum function to number in postgres

I have the next query which does not work:
UPDATE item
SET popularity= (CASE
WHEN (select SUM(io.quantity) from item i NATURAL JOIN itemorder io GROUP BY io.item_id) > 3 THEN TRUE
ELSE FALSE
END);
Here I want to compare each line of inner SELECT SUM value with 3 and update popularity. But SQL gives error:
ERROR: more than one row returned by a subquery used as an expression
I understand that inner SELECT returns many values, but can smb help me in how to compare each line. In other words make loop.
When using a subquery you need to get a single row back, so you're effectively doing a query for each record in the item table.
UPDATE item i
SET popularity = (SELECT SUM(io.quantity) FROM itemorder io
WHERE io.item_id = i.item_id) > 3;
An alternative (which is a postgresql extension) is to use a derived table in a FROM clause.
UPDATE item i2
SET popularity = x.orders > 3
FROM (select i.item_id, SUM(io.quantity) as orders
from item i NATURAL JOIN itemorder io GROUP BY io.item_id)
as x(item_id,orders)
WHERE i2.item_id = x.item_id
Here you're doing a single group clause as you had, and we're joining the table to be updated with the results of the group.

need to copy all rows with C_PROV_TYPE ='014' and C_SPECILTY = '300' and insert back 3 rows with same data + max sequence number + 1 i.e =4,5,6

need 3 rows for each one of the two valid rows displayed below output like below:
Primary key is C_PROCEDURE + C_PROV_TYPE + SPEC_SEQ_NO!
output shall be like bELOW
You could try something like this:
INSERT INTO YourTable (
C_PROCEDURE,
C_PROV_TYPE,
I_PT_SPEC_SEQ_NO,
C_SPECIALTY
)
SELECT
s.C_PROCEDURE,
s.C_PROV_TYPE,
s.MaxSeq + ROW_NUMBER() OVER (
PARTITION BY s.C_PROCEDURE, s.C_PROV_TYPE
ORDER BY v.rn, s.I_PT_SPEC_SEQ_NO),
s.C_SPECIALTY + v.rn
FROM (
SELECT
*,
MAX(I_PT_SPEC_SEQ_NO) OVER (
PARTITION BY C_PROCEDURE, C_PROV_TYPE
) AS MaxSeq
FROM YourTable
) s
CROSS JOIN (
VALUES (1), (2), (3)
) v (rn)
WHERE s.C_PROV_TYPE = '014'
AND s.C_SPECIALTY = '300'
;
Basically, the subquery returns all the YourTable rows supplied with the maximum values of I_PT_SPEC_SEQ_NO for every partition of (C_PROCEDURE, C_PROV_TYPE) using the windowing MAX() function (MAX(...) OVER (...)).
The resulting set of that subquery is then cross-joined to an inline 3-row table (which produces three copies of every row returned) and filtered by the specified values of C_PROV_TYPE and C_SPECIALTY.
New data rows pull C_PROCEDURE and C_PROV_TYPE directly from the subquery. The new C_SPECIALTY values are produced using those from the subquery and the rn values of the inline table. The new sequence numbers are generated with the help of the ROW_NUMBER() function and the maximum sequence numbers returned by the subquery.
As I didn't have access to a working installation of DB2, I was testing my script in SQL Server 2008, trying to stick to features that I understood DB2 supported as well as SQL Server. This SQL Fiddle demo also uses a SQL Server 2008 instance to demonstrate how the query works.