Compare Items in the "IN" Clause and the resultset - sql

I'd like to achieve something as follows, I have the following query (As simple as this),
SELECT ENT_ID,TP_ID FROM TC_LOGS WHERE ENT_ID IN (1,2,3,4,5).
Now the table TC_LOGS may not have all the items in the IN clause. So assuming that the table TC_LOGS has only 1,2. I'd like to compare the items in the IN clause i.e. 1,2,3,4,5 with 1,2(the resultset) and get a result as FOUND - 1,2 NOT FOUND - 3,4,5. I've have implemented this by applying an XSL transformation on the resultset in the application code, but I'd like to achieve this in a query, which I feel is more of an elegant solution to this problem. Also, I tried the following query with NVL, just to separate out the FOUND and NOT FOUND items as,
SELECT NVL(ENT_ID,"NOT FOUND") FROM TC_LOGS WHERE ENT_ID IN(1,2,3,4,5)
I was expecting a result as 1,2,NOT FOUND,NOT FOUND,NOT FOUND
But the above query doesn't return any result.. I'd appreciate if someone can guide me in the right path here.. Thanks much in advance.

Assuming that the items in your IN list can (or can come) from another query, you can do something like
WITH src AS (
SELECT level id
FROM dual
CONNECT BY level <= 5)
SELECT nvl(ent_id, 'Not Found' )
FROM src
LEFT OUTER JOIN tc_logs ON (src.id = tc_logs.ent_id)
In my case, the src query is just generating the numbers 1 through 5. You could just as easily fetch that data from a different table, load the numbers into a collection that you query using the TABLE operator, load the numbers into a temporary table that you query, etc. depending on how the IN list data is determined.

NVL isn't going to work because no values (including NULLS) are returned when there is no match with the IN statement.
What you can do is something like this:
SELECT NVL(ENT_ID, "NOT FOUND")
FROM TC_LOGS
RIGHT OUTER JOIN (
SELECT 1 AS 'TempID' UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5) AS Sub ON ENT_ID = TempID
The outer join will return NULLS for ENT_ID where there are no matches. Note, I'm not an Oracle person so I can't guarantee that this syntax is perfect.

if you have a table (let's use table src )contains all (1,2,3,4,5) values, you can use full join.
You can use (WITH src AS ( SELECT level id FROM dual CONNECT BY level <= 5) as the src table also)
SELECT
ent_id,tl.tp_id,src.tp_id
FROM
src
FULL JOIN
tc_logs tl
USING (ent_id)
ORDER BY
ent_id
Here is the web site for oracle full join.http://psoug.org/snippet/Oracle-PL-SQL-ANSI-Joins-FULL-JOIN_738.htm

Related

Want to concatenate column of the second query to the first query but getting errors such as "query block has incorrect number of result columns"

SELECT
ID, PRIM_EMAIL, SEC_EMAIL, PHONE
FROM
STUDENTS.RECORDS
WHERE
ID IN (SELECT ID FROM STUDENTS.INFO WHERE ROLL_NO = '554')
UNION
SELECT NAME
FROM STUDENTS.INFO
WHERE ROLL_NO = '554';
Here Roll_No is a user inserted data so for now I have hard coded it. Basically with the help of ROLL_NO I sort the STUDENTS_INFO table from where I get the ID and based on that I try to get PRIM_EMAIL, SEC_EMAIL, PHONE from the STUDENTS.RECORDS table while matching the foreign keys of both the tables. In addition to the current result set I also want to have the prov_name column.
Any help is very much appreciated. Thank you!
I suspect that you want to put all this information on the same row, which suggests a join rather than union all:
select
r.ID,
r.PRIM_EMAIL,
r.SEC_EMAIL,
r.PHONE,
r.NAME
from STUDENTS.RECORDS r
inner join STUDENTS.INFO i ON i.ID = r.ID
where I.ROLL_NO = '554';
I think the source of your error query block has incorrect number of result columns is coming from trying to union together a table with 4 columns (id, prim_email, sec_email, phone) with 1 column (name).
From your question, I am gathering that you want a single table of id, prim_email, sec_email, phone from students.records and name from students.info.
I think the following query using CTE's might get you (partially) to your final result. You may want to refactor for optimizing performance.
with s_records as ( select * from students.records ),
s_info as ( select * from students.info ),
joined as (
select
s_records.id,
s_records.prim_email,
s_records.sec_email,
s_records.phone,
s_info.name
from s_records
left join s_info
on s_records.roll_no = s_info.roll_no
where roll_np = '554' )
select * from joined
Overall, I think that a join will be part of your solution rather than a union :-)

how to use listagg operator so that the query should fetch comma separated values

SELECT (SELECT STRING_VALUE
FROM EMP_NODE_PROPERTIES
WHERE NODE_ID=AN.ID ) containedWithin
FROM EMP_NODE AN
WHERE AN.STORE_ID = ALS.ID
AND an.TYPE_QNAME_ID=(SELECT ID
FROM EMP_QNAME
where LOCAL_NAME = 'document')
AND
AND AN.UUID='13456677';
from the above query I am getting below error.
ORA-01427: single-row subquery returns more than one row
so how to change the above query so that it should fetch comma separated values
This query won't return error you mentioned because
there are two ANDs and
there's no ALS table (or its alias).
I suggest you post something that is correctly written, then we can discuss other errors.
Basically, it is either select string_value ... or select id ... (or even both of them) that return more than a single value.
The most obvious "solution" is to use select DISTINCT
another one is to include where rownum = 1
or, use aggregate functions, e.g. select max(string_value) ...
while the most appropriate option would be to join all tables involved and decide which row (value) is correct and adjust query (i.e. its WHERE clause) to make sure that desired value will be returned.
You would seem to want something like this:
SELECT LISTAGG(NP.STRING_VALUE, ',') WITHIN GROUP(ORDER BY NP.STRING_VALUE)
as containedWithin
FROM EMP_NODE N
JOIN EMP_QNAME Q
ON N.TYPE_QNAME_ID = Q.ID
LEFT JOIN EMP_NODE_PROPERTIES NP
ON NP.NODE_ID = N.ID
WHERE Q.LOCAL_NAME = 'document'
AND AN.UUID = '13456677';
This is a bit speculative because your original query would not run for the reason explained by Littlefoot.

Array field in postgres, need to do self-join with results

I have a table that looks like this:
stuff
id integer
content text
score double
children[] (an array of id's from this same table)
I'd like to run a query that selects all the children for a given id, and then right away gets the full row for all these children, sorted by score.
Any suggestions on the best way to do this? I've looked into WITH RECURSIVE but I'm not sure that's workable. Tried posting at postgresql SE with no luck.
The following query will find all rows corresponding to the children of the object with id 14:
SELECT *
FROM unnest((SELECT children FROM stuff WHERE id=14)) t(id)
JOIN stuff USING (id)
ORDER BY score;
This works by finding the children of 14 as array first, then we convert it into a table using the unnest function, and then we join with stuff to find all rows with the given ids.
The ANY construct in the join condition would be simplest:
SELECT c.*
FROM stuff p
JOIN stuff c ON id = ANY (p.children)
WHERE p.id = 14
ORDER BY c.score;
Doesn't matter for the query whether the array of children IDs is in the same table or different one. You just need table aliases here to be unambiguous.
Related:
Check if value exists in Postgres array
Similar solution:
With Postgres you can use a recursive common table expression:
with recursive rel_tree as (
select rel_id, rel_name, rel_parent, 1 as level, array[rel_id] as path_info
from relations
where rel_parent is null
union all
select c.rel_id, rpad(' ', p.level * 2) || c.rel_name, c.rel_parent, p.level + 1, p.path_info||c.rel_id
from relations c
join rel_tree p on c.rel_parent = p.rel_id
)
select rel_id, rel_name
from rel_tree
order by path_info;
Ref: Postgresql query for getting n-level parent-child relation stored in a single table

Ensuring two columns only contain valid results from same subquery

I have the following table:
id symbol_01 symbol_02
1 abc xyz
2 kjh okd
3 que qid
I need a query that ensures symbol_01 and symbol_02 are both contained in a list of valid symbols. In other words I would needs something like this:
select *
from mytable
where symbol_01 in (
select valid_symbols
from somewhere)
and symbol_02 in (
select valid_symbols
from somewhere)
The above example would work correctly, but the subquery used to determine the list of valid symbols is identical both times and is quite large. It would be very innefficient to run it twice like in the example.
Is there a way to do this without duplicating two identical sub queries?
Another approach:
select *
from mytable t1
where 2 = (select count(distinct symbol)
from valid_symbols vs
where vs.symbol in (t1.symbol_01, t1.symbol_02));
This assumes that the valid symbols are stored in a table valid_symbols that has a column named symbol. The query would also benefit from an index on valid_symbols.symbol
You could try use a CTE like;
WITH ValidSymbols AS (
SELECT DISTINCT valid_symbol
FROM somewhere
)
SELECT mt.*
FROM MyTable mt
INNER JOIN ValidSymbols v1
ON mt.symbol_01 = v1.valid_symbol
INNER JOIN ValidSymbols v2
ON mt.symbol_02 = v2.valid_symbol
From a performance perspective, your query is the right way to do this. I would write it as:
select *
from mytable t
where exists (select 1
from valid_symbols vs
where t.symbol_01 = vs.valid_symbol
) and
exists (select 1
from valid_symbols vs
where t.symbol_02 = vs.valid_symbol
) ;
The important component is that you need an index on valid_symbols(valid_symbol). With this index, the lookup should be pretty fast. Appropriate indexes can even work if valid_symbols is a view, although the effect depends on the complexity of the view.
You seem to have a situation where you have two foreign key relationships. If you explicitly declare these relationships, then the database will enforce that the columns in your table match the valid symbols.

What is the most effecient way to write this SQL query?

I have two lists of ids. List A and List B. Both of these lists are actually the results of SQL queries (QUERY A and QUERY B respectively).
I want to 'filter' List A, by removing the ids in List A if they appear in list B.
So for example if list A looks like this:
1, 2, 3, 4, 7
and List B looks like this:
2,7
then the 'filtered' List A should have ids 2 and 7 removed, and so should look like this:
1, 3, 4
I want to write an SQL query like this (pseudo code of course):
SELECT id FROM (QUERYA) as temp_table where id not in (QUERYB)
Using classic SQL:
select [distinct] number
from list_a
where number not in (
select distinct number from list_b
);
I've put the first "distinct" in square brackets since I'm unsure as to whether you wanted duplicates removed (remove either the brackets or the entire word). The second "distinct" should be left in just in case your DBMS doesn't optimize IN clauses.
It may be faster (measure, don't guess) with an left join along the lines of:
select [distinct] list_a.number from list_a
left join list_b on list_a.number = list_b.number
where list_b.number is null;
Same deal with the "[distinct]".
see Doing INTERSECT and MINUS in MySQL
The query:
select id
from ListA
where id not in (
select id
from ListB)
will give you the desired result.
I am not sure which way is the best. As my previous impression, the perforamnce could be very different depends on situtation and the size of the tables.
1.
select id
from ListA
where id not in (
select id
from ListB)
2.
select ListA.id
from ListA
left join ListB on ListA.id=ListB.id
where ListB.id is null
3.
select id
from ListA
where not exists (
select *
from ListB where ListB.id=ListA.id)
The 2) should be the fastest usually, as it does inner join not sub-queries.
Some people may suggest 3) rather then 1) beause it use "exists" which does not read data from table.