Understanding a basic SQL query - sql

I have a query like
SELECT tran_number
FROM table_a WHERE customer_id IN
(SELECT customer_id
FROM table_b
WHERE customer_key = 89564
AND ( other_phn_area_code
|| other_phnum_pfx_num
|| other_phnum_sfx_num IN
(123456789)))
AND phn_area_code || phnum_pfx_num || phnum_sfx_num IN (123456789)
The above code is working fine. The concern is with the inner query (copied inner query alone below)...
(SELECT customer_id
FROM table_b
WHERE customer_key = 89564
AND ( other_phn_area_code
|| other_phnum_pfx_num
|| other_phnum_sfx_num IN
(123456789)))
When i execute this query, i'm getting error as customer_id: invalid identifier. In real, table_b is not having any field named customer_id. If so, then how it is working, without any issue, when i use it as an inner query above.
Please help me to understand this.
Database details below
Oracle 11G Enterprise edition 11.2.0.2.0
PL/SQL Release 11.2.0.2.0

if the where condition of that inner select has a result, then the column customer_id from table_a will be selected.
If not then it won't be selected. The outer select checks that with the in condition. That is like saying: "Only return something if the inner select return true."

It's a matter of scope. Oracle validates identifiers starting with the innermost sub-query and working outwards. If we add table aliases to your original query things might become clearer:
SELECT t1.tran_number
FROM table_a t1
WHERE t1.customer_id IN
(SELECT t1.customer_id
FROM table_b t2
WHERE t2.customer_key = 89564
AND ( t2.other_phn_area_code
|| t2.other_phnum_pfx_num
|| t2.other_phnum_sfx_num IN
(123456789)))
AND t1.phn_area_code || t1.phnum_pfx_num || t1.phnum_sfx_num IN (123456789)
In effect, the outer query is using the sub-querty as a test for EXISTS, i.e. just checking for the existence of a given value of CUSTOMER_KEY and those other columns. If this is not what you want then you should change the column name in the sub-query. (And that's a pretty good bet: you're probably getting puzzling results from the main query and that's why you're investigating the sub-query in isolation).
Using aliases in these scenarios is always good practice. If you had aliased the sub-query like this:
....
WHERE t1.customer_id IN
(SELECT t2.customer_id
FROM table_b t2
WHERE t2.customer_key = 89564
....
the error would have been immediately apparent.
The SQL Reference does explain the operation of scope in sub-queries, but it's hard to find. What it says is this:
"Oracle resolves unqualified columns in the subquery by looking in
the tables named in the subquery and then in the tables named in the
parent statement"
You can find a clearer explanation of scoping in the PL/SQL documentation; SQL sub-queries work in the same fashion. Find out more.

Thats is a known bug with IN. If you use table alias you will get error
SELECT tran_number
FROM table_a WHERE customer_id IN
(SELECT b.customer_id
FROM table_b b
WHERE customer_key = 89564
AND ( other_phn_area_code
|| other_phnum_pfx_num
|| other_phnum_sfx_num IN
(123456789)))
AND phn_area_code || phnum_pfx_num || phnum_sfx_num IN (123456789)
Also use EXISTS to avoid this type of silent behaviour
SELECT tran_number
FROM table_a as t1 WHERE EXISTS
(SELECT *
FROM table_b as b
WHERE customer_key = 89564
AND ( other_phn_area_code
|| other_phnum_pfx_num
|| other_phnum_sfx_num IN
(123456789))
AND b.customer_id =t1.customer_id)
AND phn_area_code || phnum_pfx_num || phnum_sfx_num IN (123456789)

Related

How to move column value to new column in Postgresql?

i would like to create table from
SELECT
a.value_text,
a.value_free_text,
a.value_date,
a.value_number
FROM survey_user_input_line a
LEFT JOIN survey_label b on b.id = a.value_suggested
WHERE a.survey_id = %s
ORDER BY question_id, a.user_input_id, id, value_suggested_row
to
can anybody tell me how to code it
You may use a chained call to COALESCE, making sure to cast all columns to a common data type (text makes the most sense here):
SELECT *, COALESCE(value_text, value_free_text, value_date::text,
value_number::text) AS all_value
FROM yourTable;

Concatenating two columns in Oracle giving performance issue

I have a select query where I am doing inner joins and in AND clause I am checking like this
AND UPPER (b.a1||b.a2) IN
(
select a.a1||a.a2
from a
where a.a3 =
(
select UPPER(decode('609',null,c.c1,'609'))
from dual
)
)
but so because of || opertaor it is taking more than 2 minutes. Can we have any other solution to this?
How about using EXISTS clause?
AND EXISTS (
SELECT
1
FROM
a
WHERE
a.a3 = (SELECT UPPER(DECODE('609',c.c1,'609')) FROM dual) -- this condition is pretty odd
AND a.a1 = UPPER(b.a1)
AND a.a2 = UPPER(b.a2)
)
Adding Function Based Indexes on UPPER(b.a1) AND UPPER(b.a2) may help as well.
Speaking of that odd condition: (SELECT UPPER(DECODE('609',c.c1,'609')) FROM dual):
Why do you perform a SELECT from dual there?
What you check is - if '609' equals c.c1 ('609') then a.a3 must equal '609', in any other case your SELECT returns NULL, thus no value from table a is returned. So you can just change the entire condition to a.a3 = '609'.
Try with a JOIN
SELECT *
FROM b
JOIN ( select UPPER(a.a1), UPPER(a.a2)
from a
where a.a3 = (select UPPER(decode('609',c.c1,'609')) from dual)
) a
ON UPPER(b.b1) = a.a1
AND UPPER(b.b2) = a.a2
But the problem is when you do UPPER(b.b1) or b1||b2 you cant use the index anymore.
You may need a function index
You don't need to concatenate. In fact: concatenating the values is a bug waiting to happen: consider the the values foob and ar in table b and foo and bar in a - the concatenation treats those as the same tuple although they are not.
You also don't need the select from dual for a constant:
AND (UPPER (b.a1), upper(b.a2)) IN
(
select a.a1. a.a2
from a
where a.a3 = UPPER(decode('609',null,c.c1,'609'))
)
An index on b(a1,a2) can't be used for this, but you can create an index on b (UPPER (b.a1), upper(b.a2)) which would be used.

Exists, or Within

I'm writing a some filtering logic that basically wants to first check if there's a value in the filter table, then if there is return the filtered values. When there isn't a value in the filter table just return everything. The following table does this correctly but I have to write the same select statement twice.
select *
from personTbl
where (not exists (select filterValue from filterTable where filterType = 'name') or
personTbl.name in (select filterValue from filterTable where filterType = 'name'))
Is there some better way to do this that will return true if the table is empty, or the value is contained within it?
One approach is to do a left outer join to your filter-subquery, and then select all the rows where the join either failed (meaning that the subquery returned no rows) or succeeded and had the right value:
SELECT personTbl.*
FROM personTbl
LEFT
OUTER
JOIN ( SELECT DISTINCT filterValue
FROM filterTable
WHERE filterType = 'name'
) filter
ON 1 = 1
WHERE filter.filterValue = personTbl.name
OR filter.filterValue IS NULL
;
To be honest, I'm not sure if the above is a good idea — it's not very intuitive1, and I'm not sure how well it will perform — but you can judge for yourself.
1. As evidence of its unintuitiveness, witness the mistaken claim below that it doesn't work. As of this writing, that comment has garnered two upvotes. So although the query is correct, it clearly inspires people to great confidence that it's wrong. Which is a nice party trick, but not generally desirable in production code.
You can use a collection to try to make the query more intuitive (and only require a single select from the filter table):
CREATE TYPE filterlist IS TABLE OF VARCHAR2(100);
/
SELECT p.*
FROM PersonTbl p
INNER JOIN
( SELECT CAST(
MULTISET(
SELECT filterValue
FROM filterTable
WHERE filterType = 'name'
)
AS filterlist
) AS filters
FROM DUAL ) f
ON ( f.filters IS EMPTY OR p.name MEMBER OF f.filters );

SQL select is query is not working properly

I am unable to apply a filter(where condition) on the query
SELECT A.Form_Id,
B.CONTAINER_ID,
A.FORM_DESC,
A.FORM_TITLE,
A.LAYOUT,
A.TOTAL_COLUMNS,
COUNT (*) Over () AS Total_Rows
ROW_NUMBER () OVER ( ORDER BY CONTAINER_ID ASC ) ROWNM
FROM FORM_DEFINITION A
LEFT JOIN
(SELECT CONTAINER_ID,FORM_ID FROM FORM_CONTAINER_DEFINITION
) B
ON A.FORM_ID = B.FORM_ID
AND ( ( UPPER(TRIM(A.FORM_ID)) LIKE '%'
|| UPPER(TRIM('FORM2'))
||'%' ) )
In the above code I applied filter like this
( ( UPPER(TRIM(A.FORM_ID)) LIKE '%'
|| UPPER(TRIM('FORM2'))
||'%' ) )
Except this part the query is giving all the info. This filter should show only 'FORM2' row.
But it is showing all the rows as normaly.
.
.
Could you resolve my issue....
.
.
Thanks in advance. :)
Conditions on the first table in a LEFT JOIN need to go in the WHERE clause. On the second table, in the ON clause. Also, the subquery is not necessary. So:
SELECT . ..
FROM FORM_DEFINITION A LEFT JOIN
FORM_CONTAINER_DEFINITION B
ON A.FORM_ID = B.FORM_ID
WHERE UPPER(TRIM(A.FORM_ID)) LIKE '%' || UPPER(TRIM('FORM2')) || '%';
The logic is actually simpler than the above rule. A LEFT JOIN keeps all rows in the first table, regardless of the condition in the ON clause. Matching rows get the values from the second table. Non-matching rows get NULL values.
This is true even when the condition is on the first table.
Also, I would encourage you to use sensible aliases for tables rather than A and B. I would suggest FD and FCD for these two tables.

Sql query to select records only when count(column)=1

I am trying to retrieve records from oracle 10g express.
I want to execute :
select name|| '=' || id from literals where name='vge_1'
only when count(vge_1) is equal to 1.
else I want to display an error.
I tried following query, but its giving
ORA-00905: missing keyword
THe query I tried is as follows:
select case(name)
when count('vge_1') then (select name|| '=' || id from literals where name='vge_1';)
else Errror
end
from Literals where name='vge_1';
Thanks for your help in advance.
Instead of the case add HAVING count(name)=1 in the end of th query
Try this:
select b.id,a.* from
(select name from Literals
where name='vge_1'
group by name
having count(name)=1)a,Literals b
where b.name=a.name
Try this SQL Fiddle:
select distinct case
when l2.c = 1 then l1.name || '=' || l1.id
else 'Error'
end as name_id
from literals l1,
(
select name, count(name) as c
from literals
where name = 'vge_1'
group by name
having count(name) = 1
) l2
where l1.name = l2.name(+)
and l1.name = 'vge_1'
;
The inner query is roughly same as the other answers. The outer query uses a left outer join (+) to determine if the inner query contains a match for your count() restriction.
Note that you must update the name in two places when running this query.