Only keep rows within certain field based on another field - sql

Having trouble with this one, can this be done in one query? Or should I delete all the N records and then use another query to get what I need? Help is appreciated!!
Only keep rows belonging to a group_id where the group_id contains YY for the same brand. If the flag is a Y/N or N/N for the same brand within a group_id, then remove those records.

Next time, please supply your data so that we can copy-paste it into an example and work with it. You would have saved me at least 15 min ....
I did not bother with unimportant data - they will be different from your input, but don't matter.
One way ist to use an OLAP function partitioned by grp_id,brand,flag, and a counter, and filtering by flag and counter value 2, or you could also use a grouping query, and join that grouping query back to the base table.
Here is the OLAP solution:
WITH
-- your (simplified) input, don't use in query )
indata(grp_id,secondary_id,account_id,site_id,op_id,brand,flag) AS (
SELECT 1, 1055884386, 123, 'usa', 2,'toyota', 'N' FROM dual
UNION ALL SELECT 1, 1055884386, 123, 'usa', 2,'toyota', 'N' FROM dual
UNION ALL SELECT 1, 1055884386, 123, 'usa', 2,'tesla' , 'Y' FROM dual
UNION ALL SELECT 1, 1055884386, 123, 'usa', 2,'tesla' , 'Y' FROM dual
UNION ALL SELECT 2, 1055884386, 123, 'usa', 2,'toyota', 'Y' FROM dual
UNION ALL SELECT 2, 1055884386, 123, 'usa', 2,'toyota', 'N' FROM dual
UNION ALL SELECT 2, 1055884386, 123, 'usa', 2,'tesla' , 'Y' FROM dual
UNION ALL SELECT 2, 1055884386, 123, 'usa', 2,'tesla' , 'N' FROM dual
UNION ALL SELECT 3, 1055884386, 123, 'usa', 2,'toyota', 'Y' FROM dual
UNION ALL SELECT 3, 1055884386, 123, 'usa', 2,'toyota', 'N' FROM dual
UNION ALL SELECT 3, 1055884386, 123, 'usa', 2,'tesla' , 'Y' FROM dual
UNION ALL SELECT 3, 1055884386, 123, 'usa', 2,'tesla' , 'N' FROM dual
UNION ALL SELECT 4, 1055884386, 123, 'usa', 2,'toyota', 'Y' FROM dual
UNION ALL SELECT 4, 1055884386, 123, 'usa', 2,'toyota', 'Y' FROM dual
UNION ALL SELECT 4, 1055884386, 123, 'usa', 2,'tesla' , 'N' FROM dual
UNION ALL SELECT 4, 1055884386, 123, 'usa', 2,'tesla' , 'N' FROM dual
)
-- end of your input; replace following comma with "WITH" ...
,
w_counter AS (
SELECT
*
, COUNT(*) OVER(PARTITION BY grp_id,brand,flag) AS occ_count
FROM indata
)
SELECT
*
FROM w_counter
WHERE flag='Y'
AND occ_count = 2
;
-- out grp_id | secondary_id | account_id | site_id | op_id | brand | flag | occ_count
-- out --------+--------------+------------+---------+-------+--------+------+-----------
-- out 1 | 1055884386 | 123 | usa | 2 | tesla | Y | 2
-- out 1 | 1055884386 | 123 | usa | 2 | tesla | Y | 2
-- out 4 | 1055884386 | 123 | usa | 2 | toyota | Y | 2
-- out 4 | 1055884386 | 123 | usa | 2 | toyota | Y | 2
And, for good measure, also the grouping solution - not repeating the input WITH clause ...
grp AS (
SELECT
grp_id
, brand
FROM indata
WHERE flag='Y'
GROUP BY
grp_id
, brand
HAVING COUNT(brand) = 2
)
SELECT
i.*
FROM indata i
JOIN grp USING(grp_id,brand)
ORDER BY grp_id
;
-- out grp_id | secondary_id | account_id | site_id | op_id | brand | flag
-- out --------+--------------+------------+---------+-------+--------+------
-- out 1 | 1055884386 | 123 | usa | 2 | tesla | Y
-- out 1 | 1055884386 | 123 | usa | 2 | tesla | Y
-- out 4 | 1055884386 | 123 | usa | 2 | toyota | Y
-- out 4 | 1055884386 | 123 | usa | 2 | toyota | Y

Related

Oracle SQL - Keep rows within group where there are larger number of values for column, in a specific column category

I need to figure out how to take the results from the table below and only show me the green highlighted rows in the image. Within each account_id/site_id combo (there are two shown in the example - 123/usa and 456/can), I only need to keep the group_ids where there are a larger number of Y (flag) for the brand (as seen in the side_id "can" - there are more Y in group_id 1 for that brand than there are in group_id 2). Within the account_id/site combo, if there are two group_ids that have more than two Y for a brand, then keep them both (as seen in the site_id "usa").
group_id
account_id
site_id
brand
flag
1
123
usa
toyota
N
1
123
usa
toyota
N
1
123
usa
tesla
Y
1
123
usa
tesla
Y
2
123
usa
toyota
Y
2
123
usa
toyota
N
2
123
usa
tesla
Y
2
123
usa
tesla
N
3
123
usa
toyota
Y
3
123
usa
toyota
N
3
123
usa
tesla
Y
3
123
usa
tesla
N
4
123
usa
toyota
Y
4
123
usa
toyota
Y
4
123
usa
tesla
N
4
123
usa
tesla
N
1
456
can
honda
Y
1
456
can
honda
Y
1
456
can
honda
Y
2
456
can
honda
Y
2
456
can
honda
N
2
456
can
honda
Y
By using a combination of window functions, you can count the number of Y flags for each group/account/site/brand, then find the maximum number of Y flags of those partitioned with another analytic function, then only return the rows where those two values match.
Query
WITH
grps (GROUP_ID,
account_id,
site_id,
brand,
flag)
AS
(SELECT 1, 123, 'usa', 'toyota', 'N' FROM DUAL
UNION ALL
SELECT 1, 123, 'usa', 'toyota', 'N' FROM DUAL
UNION ALL
SELECT 1, 123, 'usa', 'tesla', 'Y' FROM DUAL
UNION ALL
SELECT 1, 123, 'usa', 'tesla', 'Y' FROM DUAL
UNION ALL
SELECT 2, 123, 'usa', 'toyota', 'Y' FROM DUAL
UNION ALL
SELECT 2, 123, 'usa', 'toyota', 'N' FROM DUAL
UNION ALL
SELECT 2, 123, 'usa', 'tesla', 'Y' FROM DUAL
UNION ALL
SELECT 2, 123, 'usa', 'tesla', 'N' FROM DUAL
UNION ALL
SELECT 3, 123, 'usa', 'toyota', 'Y' FROM DUAL
UNION ALL
SELECT 3, 123, 'usa', 'toyota', 'N' FROM DUAL
UNION ALL
SELECT 3, 123, 'usa', 'tesla', 'Y' FROM DUAL
UNION ALL
SELECT 3, 123, 'usa', 'tesla', 'N' FROM DUAL
UNION ALL
SELECT 4, 123, 'usa', 'toyota', 'Y' FROM DUAL
UNION ALL
SELECT 4, 123, 'usa', 'toyota', 'Y' FROM DUAL
UNION ALL
SELECT 4, 123, 'usa', 'tesla', 'N' FROM DUAL
UNION ALL
SELECT 4, 123, 'usa', 'tesla', 'N' FROM DUAL
UNION ALL
SELECT 1, 456, 'can', 'honda', 'Y' FROM DUAL
UNION ALL
SELECT 1, 456, 'can', 'honda', 'Y' FROM DUAL
UNION ALL
SELECT 1, 456, 'can', 'honda', 'Y' FROM DUAL
UNION ALL
SELECT 2, 456, 'can', 'honda', 'Y' FROM DUAL
UNION ALL
SELECT 2, 456, 'can', 'honda', 'N' FROM DUAL
UNION ALL
SELECT 2, 456, 'can', 'honda', 'Y' FROM DUAL)
SELECT g3.GROUP_ID,
g3.account_id,
g3.site_id,
g3.brand,
g3.flag
FROM (SELECT g2.*, MAX (g2.y_flag_cnt) OVER (PARTITION BY account_id) AS max_y_flag_cnt
FROM (SELECT g.*,
SUM (CASE WHEN flag = 'Y' THEN 1 ELSE 0 END)
OVER (PARTITION BY GROUP_ID,
account_id,
site_id,
brand) AS y_flag_cnt
FROM grps g) g2) g3
WHERE flag = 'Y' AND y_flag_cnt = max_y_flag_cnt
ORDER BY account_id, GROUP_ID;
Output
GROUP_ID ACCOUNT_ID SITE_ID BRAND FLAG
___________ _____________ __________ _________ _______
1 123 usa tesla Y
1 123 usa tesla Y
4 123 usa toyota Y
4 123 usa toyota Y
1 456 can honda Y
1 456 can honda Y
1 456 can honda Y

Oracle Alpha-Numeric column sorting

I am working on to Sort the Revision column of an Oracle DB table in the asc order as per below.
At first the numeric revisions to be sorted (1,2,3,…).
Thereafter Alpha-Numeric to be sorted as following: A, B, B1, C, C1, C2,…,Y, Y2, Y3, Z, AA, AB,..,DA, …ZZ, etc.
Row_Number() in the SELECT statement to be filled with 1,2,3… for each document# (ABC, XYZ) after revision sorting out.
See the uploaded image for the required table.
I tried with SUBSTR , Order by, etc but failed to sort out as per above requirement.
Can someone help me on this ? Thanks!
As I understand your question, you want to put last revisions that contain only two characters and no digits.
You can use a conditional sort:
select
t.*,
row_number() over(
partition by doc#
order by
case when regexp_like(revision, '^\w\d?$') then 0 else 1 end,
revision
) rn
from t
order by doc#, rn
The regular expression describes a string starting with an alphanumeric character, optionally followed by a digit: these revisions should come first.
Demo on DB Fiddle:
with t as (
select 'ABC' doc#, '1' revision from dual
union all select 'ABC', '2' from dual
union all select 'ABC', '3' from dual
union all select 'ABC', 'A' from dual
union all select 'ABC', 'B' from dual
union all select 'ABC', 'B1' from dual
union all select 'ABC', 'C' from dual
union all select 'ABC', 'C1' from dual
union all select 'ABC', 'D' from dual
union all select 'ABC', 'AA' from dual
union all select 'ABC', 'AB' from dual
union all select 'ABC', 'BA' from dual
union all select 'ABC', 'DA' from dual
)
select
t.*,
row_number() over(
partition by doc#
order by
case when regexp_like(revision, '^\w\d?$') then 0 else 1 end,
revision
) rn
from t
order by doc#, rn
DOC# | REVISION | RN
:--- | :------- | -:
ABC | 1 | 1
ABC | 2 | 2
ABC | 3 | 3
ABC | A | 4
ABC | B | 5
ABC | B1 | 6
ABC | C | 7
ABC | C1 | 8
ABC | D | 9
ABC | AA | 10
ABC | AB | 11
ABC | BA | 12
ABC | DA | 13
There is well known old method: rpad(col, max-length, '0')
For example rpad(col, max(length(col)) over(), '0'

SQL: summing a column starting from row immediately after two consecutive 'trigger' values in another column

How to sum all values after two consecutive YES's in the CONDITION_SATISFIED column?
ID | CONDITION_SATISFIED | VALUE
--------------------------------
1 | NO | 100
2 | NO | 300
3 | NO | 500
4 | YES | 100
5 | YES | 300
6 | NO | 500 <-
7 | NO | 100 <-
8 | YES | 300 <-
9 | NO | 500 <-
--------------------------------
SUM | 1400
Note: further occurrences of YES/NO are ignored once the summation is started.
I've gotten to the point where I am able to generate two extra columns for the CONDITION_SATISFIED column like this:
ID | CONDITION_SATISFIED | VALUE RANK | REPEAT_COUNT
-------------------------------- -------------------
1 | NO | 100 1 | 3
2 | NO | 300 1 | 3
3 | NO | 500 1 | 3
4 | YES | 100 2 | 2
5 | YES | 300 2 | 2
6 | NO | 500 3 | 2 <- start from here
7 | NO | 100 3 | 2
8 | YES | 300 4 | 1
9 | NO | 500 5 | 1
-------------------------------- -------------------
But I'm not able to figure out how to get the first instance of REPEAT_COUNT >= 2 AND CONDITION_SATISFIED = 'YES', and then start the summation immediately after the 2nd YES (as indicated).
Hmmm . . . You can get the first where the two yesses are using lag():
select t.*
from (select t.*,
lag(condition_satisfied) over (order by id) as prev_cs,
lag(condition_satisfied, 2) over (order by id) as prev2_cs
from t
) t
where prev2_cs = 'YES' and prev_cs = 'YES';
Then you can just use this in a query:
select t.*
from t join
(select min(t.id) as id
from (select t.*,
lag(condition_satisfied) over (order by id) as prev_cs,
lag(condition_satisfied, 2) over (order by id) as prev2_cs
from t
) t
where prev2_cs = 'YES' and prev_cs = 'YES'
) yy
on t.id >= yy.id;
Oracle 12c: pattern matching
with t1 (id, condition_satisfied, value) as (
select 1, 'NO' , 100 from dual union all
select 2, 'NO' , 300 from dual union all
select 3, 'NO' , 500 from dual union all
select 4, 'YES', 100 from dual union all
select 5, 'YES', 300 from dual union all
select 6, 'NO' , 500 from dual union all
select 7, 'NO' , 100 from dual union all
select 8, 'YES', 300 from dual union all
select 9, 'NO' , 500 from dual)
select sum(v_value) as sum_value
from t1
match_recognize(
order by id
measures s.value as v_value
all rows per match
pattern (yes{2} s+)
define
yes as condition_satisfied = 'YES'
);
SUM_VALUE
----------
1400
If you have version lower than 12 no need to self-join and generate/prevent duplicates:
with s (id, condition_satisfied, value) as (
select 1, 'NO' , 100 from dual union all
select 2, 'NO' , 300 from dual union all
select 3, 'NO' , 500 from dual union all
select 4, 'YES', 100 from dual union all
select 5, 'YES', 300 from dual union all
select 6, 'NO' , 500 from dual union all
select 7, 'YES' , 100 from dual union all
select 8, 'YES', 300 from dual union all
select 9, 'NO' , 500 from dual)
select sum(value) sum_value
from
(select s.*, min(first_id) over () min_id
from
(select s.*,
case when condition_satisfied = 'YES' and condition_satisfied = lag(condition_satisfied) over (order by id) then id end first_id
from s
) s
)
where id > min_id;
SUM_VALUE
----------
1400

Oracle SQL - Not in subquery

I'm newish to this and using Oracle SQL. I have the following tables:
Table 1 CaseDetail
CaseNumber | CaseType
1 | 'RelevantToThisQuestion'
2 | 'RelevantToThisQuestion'
3 | 'RelevantToThisQuestion'
4 | 'NotRelevantToThisQuestion'
Table 2 LinkedPeople
CaseNumber | RelationshipType | LinkedPerson
1 | 'Owner' | 123
1 | 'Agent' | 124
1 | 'Contact' | 125
2 | 'Owner' | 126
2 | 'Agent' | 127
2 | 'Contact' | 128
3 | 'Owner' | 129
3 | 'Agent' | 130
3 | 'Contact' | 131
Table 3 Location
LinkedPerson| Country
123 | 'AU'
124 | 'UK'
125 | 'UK'
126 | 'US'
127 | 'US'
128 | 'UK'
129 | 'UK'
130 | 'AU'
131 | 'UK'
I want to count CaseNumbers that are relevant to this question with no LinkedPeople in 'AU'. So the results from the above data would be 1
I've been trying to combine aggregate functions and subqueries but I think I might be over-complicating things.
Just need a push in the right direction, thanks!
To get all the records:
SELECT COUNT(DISTINCT CaseNumber)
FROM LinkedPeople
WHERE CaseNumber NOT IN
(
SELECT DISTINCT C.CaseNumber
FROM CaseDetail C
INNER JOIN LinkedPeople P ON C.CaseNumber = P.CaseNumber
INNER JOIN Location L
ON P.LinkedPerson = L.LinkedPerson
WHERE Country = 'AU' AND C.CaseType = 'RelevantToThisQuestion'
)
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE CASEDETAIL ( CaseNumber, CaseType ) AS
SELECT 1, 'RelevantToThisQuestion' FROM DUAL
UNION ALL SELECT 2, 'RelevantToThisQuestion' FROM DUAL
UNION ALL SELECT 3, 'RelevantToThisQuestion' FROM DUAL
UNION ALL SELECT 4, 'NotRelevantToThisQuestion' FROM DUAL;
CREATE TABLE LINKEDPEOPLE ( CaseNumber, RelationshipType, LinkedPerson ) AS
SELECT 1, 'Owner', 123 FROM DUAL
UNION ALL SELECT 1, 'Agent', 124 FROM DUAL
UNION ALL SELECT 1, 'Contact', 125 FROM DUAL
UNION ALL SELECT 2, 'Owner', 126 FROM DUAL
UNION ALL SELECT 2, 'Agent', 127 FROM DUAL
UNION ALL SELECT 2, 'Contact', 128 FROM DUAL
UNION ALL SELECT 3, 'Owner', 129 FROM DUAL
UNION ALL SELECT 3, 'Agent', 130 FROM DUAL
UNION ALL SELECT 3, 'Contact', 131 FROM DUAL;
CREATE TABLE LOCATION ( LinkedPerson, Country ) AS
SELECT 123, 'AU' FROM DUAL
UNION ALL SELECT 124, 'UK' FROM DUAL
UNION ALL SELECT 125, 'UK' FROM DUAL
UNION ALL SELECT 126, 'US' FROM DUAL
UNION ALL SELECT 127, 'US' FROM DUAL
UNION ALL SELECT 128, 'UK' FROM DUAL
UNION ALL SELECT 129, 'UK' FROM DUAL
UNION ALL SELECT 130, 'AU' FROM DUAL
UNION ALL SELECT 131, 'UK' FROM DUAL;
Query 1:
SELECT COUNT( DISTINCT CASENUMBER ) AS Num_Relevant_Cases
FROM CASEDETAIL c
WHERE CaseType = 'RelevantToThisQuestion'
AND NOT EXISTS ( SELECT 1
FROM LINKEDPEOPLE p
INNER JOIN LOCATION l
ON ( p.LinkedPerson = l.LinkedPerson )
WHERE c.CaseNumber = p.CaseNumber
AND l.Country = 'AU' )
Results:
| NUM_RELEVANT_CASES |
|--------------------|
| 1 |
I'm not sure about the exact syntax, but I believe you want something like:
select count distinct CaseNumber from LinkedPeople where Country != 'AU'

Unexpected result of multiset mapping in Oracle SQL

Please help me to confirm is that behavior explained below is a bug, or clearly explain why it's right.
There are a high probability that I misunderstood some concept, but now for me it looks like a bug.
All examples below simplified as much as possible to demonstrate core of the issue. Real situation is very complex, so only general answers and workarounds related to principle of query construction is acceptable.
You are welcome to ask clarifying questions in comments and i'll try to do my best to answer them.
Thank you for attention. :)
Question
Why in last Example (Example 5) collection instance in (select count(1) ... subquery from first row mapped to all rows of the table, while expected result is to map each collection instance to it's own row?
At the same time collections used in cardinality(...) expression chosen properly.
Same situation (not covered in examples) exists if constructed in this way collections used in from or where part of a query.
Test schema setup
(SQLFiddle)
create or replace type TabType0 as table of varchar2(100)
/
create table Table0( tab_str_field varchar2(100), tab_field TabType0)
nested table tab_field store as tab_field_table
/
insert into table0 (tab_str_field, tab_field) values (
'A',
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual
) as TabType0)
)
/
insert into table0 (tab_str_field, tab_field) values (
'B',
cast(multiset(
select 'B' from dual union all
select 'C' from dual
) as TabType0)
)
/
insert into table0 (tab_str_field, tab_field) values (
'C',
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual
) as TabType0)
)
/
insert into table0 (tab_str_field, tab_field) values (
'D',
cast(multiset(
select 'A' from dual
) as TabType0)
)
/
select 'Initial table data' caption from dual
/
select * from table0
/
table data:
| TAB_STR_FIELD | TAB_FIELD |
-----------------------------
| A | A,B,C |
| B | B,C |
| C | A,B,C,D |
| D | A |
Examples
Example 1 (SQLFiddle) - work with nested table fields - OK
select 'Work with nested table - OK' caption from dual
/
select
tab_field tab_field,
-- cardinality
cardinality(tab_field) tab_cardinality,
-- select from table field of current row
(select count(1) from table(tab_field)) tab_count,
-- select from field of current row while joining
-- with another field of same row
( select column_value from table(tab_field)
where column_value = tab_str_field
) same_value
from table0
/
results:
| TAB_FIELD | TAB_CARDINALITY | TAB_COUNT | SAME_VALUE |
--------------------------------------------------------
| A,B,C | 3 | 3 | A |
| B,C | 2 | 2 | B |
| A,B,C,D | 4 | 4 | C |
| A | 1 | 1 | (null) |
Example 2 (SQLFiddle) - work with constructed source data alone - OK
select 'Work with constructed source data alone - OK' caption from dual
/
with table_data_from_set as (
select
'A' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'B' tab_str_field,
cast(multiset(
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'C' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual
) as TabType0) tab_field
from dual union all
select
'D' tab_str_field,
cast(multiset(
select 'A' from dual
) as TabType0) tab_field
from dual
)
select
tab_field tab_field,
-- cardinality
cardinality(tab_field) tab_cardinality,
-- select from table field of current row
(select count(1) from table(tab_field)) tab_count,
-- select from field of current row while joining
-- with another field of same row
( select column_value from table(tab_field)
where column_value = tab_str_field
) same_value
from table_data_from_set
/
results:
| TAB_FIELD | TAB_CARDINALITY | TAB_COUNT | SAME_VALUE |
--------------------------------------------------------
| A,B,C | 3 | 3 | A |
| B,C | 2 | 2 | B |
| A,B,C,D | 4 | 4 | C |
| A | 1 | 1 | (null) |
Example 3 (SQLFiddle) - join table with multisets constructed in WITH - OK
select 'Join table with multisets constructed in WITH - OK' caption from dual
/
with table_data_from_set as (
select
'A' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'B' tab_str_field,
cast(multiset(
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'C' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual
) as TabType0) tab_field
from dual union all
select
'D' tab_str_field,
cast(multiset(
select 'A' from dual
) as TabType0) tab_field
from dual
)
select
table0.tab_field table0_tab_field,
table_data_from_set.tab_field set_tab_field,
-- cardinality
cardinality(table0.tab_field) table0_tab_cardinality,
cardinality(table_data_from_set.tab_field) set_tab_cardinality,
-- select from table field of current row
(select count(1) from table(table_data_from_set.tab_field)) set_tab_count,
-- select from field of current row while joining
-- with another field of same row
( select column_value from table(table_data_from_set.tab_field)
where column_value = table0.tab_str_field
) same_value
from
table0,
table_data_from_set
where
table_data_from_set.tab_str_field = table0.tab_str_field
/
results:
| TABLE0_TAB_FIELD | SET_TAB_FIELD | TABLE0_TAB_CARDINALITY | SET_TAB_CARDINALITY | SET_TAB_COUNT | SAME_VALUE |
----------------------------------------------------------------------------------------------------------------
| A,B,C | A,B,C | 3 | 3 | 3 | A |
| B,C | B,C | 2 | 2 | 2 | B |
| A,B,C,D | A,B,C,D | 4 | 4 | 4 | C |
| A | A | 1 | 1 | 1 | (null) |
Example 4 (SQLFiddle) - join table with multisets constructed in WITH + subquery - OK
select 'Join table with multisets constructed in WITH and subquery - OK' caption from dual
/
with table_data_from_set as (
select
'A' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'B' tab_str_field,
cast(multiset(
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'C' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual
) as TabType0) tab_field
from dual union all
select
'D' tab_str_field,
cast(multiset(
select 'A' from dual
) as TabType0) tab_field
from dual
)
select
table0_tab_field table0_tab_field,
set_tab_field set_tab_field,
-- cardinality
cardinality(table0_tab_field) table0_tab_cardinality,
cardinality(set_tab_field) set_tab_cardinality,
-- select from table field of current row
(select count(1) from table(set_tab_field)) set_tab_count,
-- select from field of current row while joining
-- with another field of same row
( select column_value from table(set_tab_field)
where column_value = table0_tab_str_field
) same_value
from (
select
table0.tab_str_field table0_tab_str_field,
table0.tab_field table0_tab_field,
table_data_from_set.tab_str_field set_tab_str_field,
table_data_from_set.tab_field set_tab_field
from
table0,
table_data_from_set
where
table_data_from_set.tab_str_field = table0.tab_str_field
)
/
results:
| TABLE0_TAB_FIELD | SET_TAB_FIELD | TABLE0_TAB_CARDINALITY | SET_TAB_CARDINALITY | SET_TAB_COUNT | SAME_VALUE |
----------------------------------------------------------------------------------------------------------------
| A,B,C | A,B,C | 3 | 3 | 3 | A |
| B,C | B,C | 2 | 2 | 2 | B |
| A,B,C,D | A,B,C,D | 4 | 4 | 4 | C |
| A | A | 1 | 1 | 1 | (null) |
Example 5 (SQLFiddle) - join table with multisets constructed on the fly - FAILED
select 'Join table with multisets constructed on the fly - FAIL (set_tab_count wrong)' caption from dual
/
with string_set as (
select 'A' str_field from dual union all
select 'B' str_field from dual union all
select 'C' str_field from dual union all
select 'D' str_field from dual union all
select 'E' str_field from dual
)
select
table0_tab_field table0_tab_field,
set_tab_field set_tab_field,
-- cardinality
cardinality(table0_tab_field) table0_tab_cardinality,
cardinality(set_tab_field) set_tab_cardinality,
-- select from table field of current row
(select count(1) from table(set_tab_field)) set_tab_count,
-- select from field of current row while joining
-- with another field of same row
( select column_value from table(set_tab_field)
where column_value = table0_tab_str_field
) same_value
from (
select
table0.tab_str_field table0_tab_str_field,
table0.tab_field table0_tab_field,
(
cast(multiset(
select
string_set.str_field
from
string_set,
table(table0.tab_field) tab_table
where
string_set.str_field = tab_table.column_value
) as TabType0)
) set_tab_field
from
table0
)
/
result (all values in set_tab_count column are same - wrong! ) :
| TABLE0_TAB_FIELD | SET_TAB_FIELD | TABLE0_TAB_CARDINALITY | SET_TAB_CARDINALITY | SET_TAB_COUNT | SAME_VALUE |
----------------------------------------------------------------------------------------------------------------
| A,B,C | A,B,C | 3 | 3 | 3 | A |
| B,C | B,C | 2 | 2 | 3 | B |
| A,B,C,D | A,B,C,D | 4 | 4 | 3 | C |
| A | A | 1 | 1 | 3 | (null) |
Oracle version information
Instance 1
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for IBM/AIX RISC System/6000: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
Instance 2
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Express Edition Release 11.2.0.2.0 - Production
PL/SQL Release 11.2.0.2.0 - Production
CORE 11.2.0.2.0 Production
TNS for 32-bit Windows: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production
SQLFiddle with all queries together.
It's a bug. Adding a /*+ NO_MERGE */ hint to the second inline view in the last example will generate the expected results. See this SQL Fiddle for an example. Regardless of the query, that hint should never change the results. There are a couple of other seemingly unrelated changes you can make that will generate the correct results, such as removing some of the columns, or adding an unused ROWNUM in the middle.
Oracle is re-writing your query to optimize it, but doing something wrong. You could probably get some more information by tracing the query, but I doubt you'll be able to truly fix the issue. Work around it for now and submit a service request to Oracle so they can create a bug and eventually fix it.