Iterating over the table with recursive query - sql

I have the following data:
cte1
=================
gp_id | m_ids
------|----------
1 | {123}
2 | {432,222}
3 | {123,222}
And a function foobar(m_ids integer[]). The function contains the following cte:
with RECURSIVE foo as (
select id, p_id, name from bar where id = any(m_ids)
union all
select b.id, b.p_id, b.name from bar b join foo f on f.p_id = b.id
)
The function is being used kind of like this:
select foobar(m_ids) from cte1;
Now, as a part of a process of improving performance, I was told to get rid of the function. My plan was to use cte foo in my cte chain, but I stuck trying to adjust usage of any(m_ids).
EDITED: To be clear, the problem is that m_ids that are used in the where id = any(m_ids) statement is the parameter of the function, so I got to transform cte in order to make it work outside of the function.
I thought of the following:
with RECURSIVE foo as (
select (select id, p_id, name from bar where id = any(cte1.m_ids)
union all
select b.id, b.p_id, b.name from bar b join foo f on f.p_id = b.id)
from cte1
)
But that would not work because
1) recursive query "foo" does not have the form non-recursive-term UNION [ALL] recursive-term
2) subquery must return only one column
In the end, I would like to get my data in the form like this:
m_ids |foobar_result
---------|-------------
{123} | 125
{432,222}| 215

Maybe JOIN that table holding parameter?
with RECURSIVE foo as (
select m_ids, id, p_id, name from bar
JOIN cte1 ON id = ANY(m_ids)
union all
select m_ids, b.id, b.p_id, b.name from bar b join foo f on f.p_id = b.id
)

Related

Optimizing SQL Cross Join that checks if any array value in other column

Let's say I have a table events with structure:
id
value_array
XXXX
[a,b,c,d]
...
...
I have a second table values_of_interest with structure:
value
x
y
z
a
I want to find id's that have any of the values found in values_of_interest. All else equal, what would be the most performant SQL to make this happen? (I am using BigQuery, but feel free to answer more generally)
My current thought is:
SELECT
DISTINCT e.id
FROM
events e, values_of_interest vi
WHERE
EXISTS(
SELECT
value
FROM
UNNEST(e.value_array) value
JOIN
vi ON vi.value = e.value
)
Few quick options for BigQuery Standard SQL
Option 1
select id
from `project.dataset.events`
where exists (
select 1
from `project.dataset.values_of_interest`
where value in unnest(value_array)
)
Option 2
select id
from `project.dataset.events` t
where (
select count(1)
from t.value_array as value
join `project.dataset.values_of_interest`
using(value)
) > 0
I would write this using exists and a join:
select e.id
from `project.dataset.events` e
where exists (select 1
from unnest(e.value_array) val join
`project.dataset.values_of_interest` voi
on val = voi.value
);

Linked list concept in postgresql

I am new to postgresql, can you please guide me about my query listed below?
I have a table in postgres (database) named "app" having two columns "aid" and "cid".
Table Name: app
aid | cid
a1 | a3
a2 | null
a3 | a5
a4 | a6
a5 | null
a6 | null
What I want to display(using sql query in server), when I select "a1" or "a3" or "a5" from aid using sql query, I want to list all values associated with "a1" and its child cid (in this case I want an output = a1 a3 a5), its like a linked list a1 is connected to a3 and a3 is connected to a5.
If I select "a4" using sql query, I need an output like this("a4 a6")
You need to use recursion to accomplish this:
with recursive walk as (
select aid, cid, array[aid] as path
from app
union all
select w.aid, a.cid, w.path||a.aid
from walk w
join app a
on a.aid = w.cid
)
select *, array_to_string(path, ' ') as text_path
from walk
where cid is null;
Working fiddle here.
If your table is large, then to limit the cost of recursion, use a where clause in the top half of the walk CTE to restrict your starting point.
with recursive walk as (
select aid, cid, array[aid] as path
from app
where aid = 'a1'
union all
. . .
You can get the reverse path without having to recurse again like this:
with recursive walk as (
select aid, cid, array[aid] as path
from app
union all
select w.aid, a.cid, w.path||a.aid
from walk w
join app a
on a.aid = w.cid
), forward as (
select *, array_to_string(path, ' ') as text_path
from walk
where cid is null
), reverse as (
select distinct on (a.aid) a.aid, f.path, f.text_path, r.path as rpath
from app a
join forward f
on f.aid = a.aid
join forward r
on r.path #> array[a.aid]
order by a.aid, array_length(r.path, 1) desc
)
select r.aid, r.path, r.text_path,
array_agg(u.rid order by u.rn desc) as up_path,
string_agg(u.rid, ' ' order by u.rn desc) as text_up_path
from reverse r
join lateral unnest(rpath)
with ordinality as u(rid, rn)
on u.rn <= array_position(r.rpath, r.aid)
group by r.aid, r.path, r.text_path;
Updated fiddle.

Oracle SQL XOR condition with > 14 tables

I have a question on sql desgin.
Context:
I have a table called t_master and 13 other tables (lets call them a,b,c... for simplicity) where it needs to compared.
Logic:
t_master will be compared to table 'a' where t_master.gen_val =
a.value.
If record exist in t_master, retrieve t_master record, else retrieve 'a' record.
I do not need to retrieve the records if it exists in both tables (t_master and a) - XOR condition
Repeat this comparison with the remaining 12 tables.
I have some idea on doing this, using WITH to subquery the non-master tables (a,b,c...) first with their respective WHERE clause.
Then use XOR statement to retrieve the records.
Something like
WITH a AS (SELECT ...),
b AS (SELECT ...)
SELECT field1,field2...
FROM t_master FULL OUTER JOIN a FULL OUTER JOIN b FULL OUTER JOIN c...
ON t_master.gen_value = a.value
WHERE ((field1 = x OR field2 = y ) AND NOT (field1 = x AND field2 = y))
AND ....
.
.
.
.
Seeing that I have 13 tables that I need to full outer join, is there a better way/design to handle this?
Otherwise I would have at least 2*13 lines of WHERE clause which I'm not sure if that will have impact on the performance as t_master is sort of a log table.
**Assume I cant change any schema.
Currently I'm not sure if this SQL will working correctly yet, so I'm hoping someone can guide me in the right direction regarding this.
update from used_by_already's suggestion:
This is what I'm trying to do (comparison between 2 tables first, before I add more, but I am unable to get values from ATP_R.TBL_HI_HDR HI_HDR as it is in the NOT EXISTS subquery.
How do i overcome this?
SELECT LOG_REPO.UNIQ_ID,
LOG_REPO.REQUEST_PAYLOAD,
LOG_REPO.GEN_VAL,
LOG_REPO.CREATED_BY,
TO_CHAR(LOG_REPO.CREATED_DT,'DD/MM/YYYY') AS CREATED_DT,
HI_HDR.HI_NO R_VALUE,
HI_HDR.CREATED_BY R_CREATED_BY,
TO_CHAR(HI_HDR.CREATED_DT,'DD/MM/YYYY') AS R_CREATED_DT
FROM ATP_COMMON.VW_CMN_LOG_GEN_REPO LOG_REPO JOIN ATP_R.TBL_HI_HDR HI_HDR ON LOG_REPO.GEN_VAL = HI_HDR.HI_NO
WHERE NOT EXISTS
(SELECT NULL
FROM ATP_R.TBL_HI_HDR HI_HDR
WHERE LOG_REPO.GEN_VAL = HI_HDR.HI_NO
)
UNION ALL
SELECT LOG_REPO.UNIQ_ID,
LOG_REPO.REQUEST_PAYLOAD,
LOG_REPO.GEN_VAL,
LOG_REPO.CREATED_BY,
TO_CHAR(LOG_REPO.CREATED_DT,'DD/MM/YYYY') AS CREATED_DT,
HI_HDR.HI_NO R_VALUE,
HI_HDR.CREATED_BY R_CREATED_BY,
TO_CHAR(HI_HDR.CREATED_DT,'DD/MM/YYYY') AS R_CREATED_DT
FROM ATP_R.TBL_HI_HDR HI_HDR JOIN ATP_COMMON.VW_CMN_LOG_GEN_REPO LOG_REPO ON HI_HDR.HI_NO = LOG_REPO.GEN_VAL
WHERE NOT EXISTS
(SELECT NULL
FROM ATP_COMMON.VW_CMN_LOG_GEN_REPO LOG_REPO
WHERE HI_HDR.HI_NO = LOG_REPO.GEN_VAL
)
Full outer joins used to exclude all matching rows can be an expensive query. You don't supply much detail, but perhaps using NOT EXISTS would be simpler and maybe it will produce a better explain plan. Something along these lines.
select
cola,colb,colc
from t_master m
where not exists (
select null from a where m.keycol = a.fk_to_m
)
and not exists (
select null from b where m.keycol = b.fk_to_m
)
and not exists (
select null from c where m.keycol = c.fk_to_m
)
union all
select
cola,colb,colc from a
where not exists (
select null from t_master m where a.fk_to_m = m.keycol
)
union all
select
cola,colb,colc from b
where not exists (
select null from t_master m where b.fk_to_m = m.keycol
)
union all
select
cola,colb,colc from c
where not exists (
select null from t_master m where c.fk_to_m = m.keycol
)
You could union the 13 a,b,c ... tables to simplify the coding, but that may not perform so well.

SQL hide rows specifically

This is my table structure and sample rows:
And I have a SQL query like this :
WITH Test(ObjId, ObjectIcerik, KeyMi) AS
(
SELECT
ObjId, ObjectIcerik, KeyMi
FROM
Tek
WHERE
ObjId = 8
UNION ALL
SELECT
T.ObjId, T.ObjectIcerik, T.KeyMi
FROM
Tek T
INNER JOIN
Test as TE ON T.ObjParent = TE.ObjId
)
SELECT *
FROM Test
This is the result :
But I also need to hide parent and child rows if parent's KeyMi column is '1'.
What do I need to change in the SQL query to do this?
You should be able to just add the condition to the recursive CTE:
WITH Test(ObjId, ObjectIcerik, KeyMi) AS (
SELECT ObjId, ObjectIcerik, KeyMi FROM Tek
WHERE ObjId = 8 AND (KeyMI <> 1 OR KeyMI IS NULL)
UNION ALL
SELECT T.ObjId, T.ObjectIcerik, T.KeyMi
FROM Tek T INNER JOIN
Test TE
ON T.ObjParent = TE.ObjId
WHERE (t.KeyMI <> 1 OR t.KeyMI IS NULL)
)
SELECT *
FROM Test ;

MySQL/SQL - When are the results of a sub-query avaliable?

Suppose I have this query
SELECT * FROM (
SELECT * FROM table_a
WHERE id > 10 )
AS a_results LEFT JOIN
(SELECT * from table_b
WHERE id IN
(SElECT id FROM a_results)
ON (a_results.id = b_results.id)
I would get the error "a_results is not a table". Anywhere I could use the re-use the results of the subquery?
Edit: It has been noted that this query doesn't make sense...it doesn't, yes. This is just to illustrate the question which I am asking; the 'real' query actually looks something like this:
SELECT SQL_CALC_FOUND_ROWS * FROM
( SELECT wp_pod_tbl_hotel . *
FROM wp_pod_tbl_hotel, wp_pod_rel, wp_pod
WHERE wp_pod_rel.field_id =12
AND wp_pod_rel.tbl_row_id =1
AND wp_pod.tbl_row_id = wp_pod_tbl_hotel.id
AND wp_pod_rel.pod_id = wp_pod.id
) as
found_hotel LEFT JOIN (
SELECT COUNT(*) as review_count, avg( (
location_rating + staff_performance_rating + condition_rating + room_comfort_rating + food_rating + value_rating
) /6 ) AS average_score, hotelid
FROM (
SELECT r. * , wp_pod_rel.tbl_row_id AS hotelid
FROM wp_pod_tbl_review r, wp_pod_rel, wp_pod
WHERE wp_pod_rel.field_id =11
AND wp_pod_rel.pod_id = wp_pod.id
AND r.id = wp_pod.tbl_row_id
AND wp_pod_rel.tbl_row_id
IN (
SELECT wp_pod_tbl_hotel .id
FROM wp_pod_tbl_hotel, wp_pod_rel, wp_pod
WHERE wp_pod_rel.field_id =12
AND wp_pod_rel.tbl_row_id =1
AND wp_pod.tbl_row_id = wp_pod_tbl_hotel.id
AND wp_pod_rel.pod_id = wp_pod.id
)
) AS hotel_reviews
GROUP BY hotel_reviews.hotelid
ORDER BY average_score DESC
AS sorted_hotel ON (id = sorted_hotel.hotelid)
As you can see, the sub-query which makes up the found_query table is repeated elsewhere downward as another sub-query, so I was hoping to re-use the results
You can not use a sub-query like this.
I'm not sure I understand your query, but wouldn't that be sufficient?
SELECT * FROM table_a a
LEFT JOIN table_b b ON ( b.id = a.id )
WHERE a.id > 10
It would return all rows from table_a where id > 10 and LEFT JOIN rows from table_b where id matches.