Oracle result set combinations - sql

I have a query which returns the result set as follows:
Col1
A
B
C
D
Is it possible to get the following result set? That is associating a value to remaining 3 row values?
col1 col2
A B
A C
A D
B A
B C
B D
C A
C B
C D
D A
D B
D C
I am using Oracle 10g

You can get this with a self join as follows:
SELECT a.col1, b.col1 as col2
FROM <YOUR_TABLE> a,
<YOUR_TABLE> b
WHERE a.col1 <> b.col1
Working example:
WITH DAT AS
(
SELECT 'A' NAME FROM DUAL
UNION
SELECT 'B' NAME FROM DUAL
UNION
SELECT 'C' NAME FROM DUAL
UNION
SELECT 'D' NAME FROM DUAL
)
SELECT *
FROM DAT A, DAT B
WHERE a.Name <> b.Name

Related

Fill a select with null when join isn't possible

I'm trying to do a select in n tables and show a few columns of each, but sometimes I can't match some columns and instead of getting a line with "null" the entire line is omitted.
For example:
table_a
id
...
1
2
3
table_b
id
name
...
1
a1
...
2
b2
...
3
c3
...
table_c
name
...
a1
...
And then I do the following select:
select
a.id,
c.name
from
table_a a,
table_b b,
table_c
where
( 1 = 1 )
and a.id = b.id
and b.name = c.name
I'm geting:
id
name
...
1
a1
...
I'm looking for:
id
name
...
1
a1
...
2
null
...
3
null
...
How do I do that? I checked a few answers around including this one but I didn't get how to solve it.
You can use an OUTER JOIN:
SELECT a.id,
c.name
FROM table_a a
LEFT OUTER JOIN table_b b
ON (a.id = b.id)
LEFT OUTER JOIN table_c c
ON (b.name = c.name)
or, depending on precedence of the joins:
SELECT a.id,
c.name
FROM table_a a
LEFT OUTER JOIN (
table_b b
INNER JOIN table_c c
ON (b.name = c.name)
)
ON (a.id = b.id)
Which, for the sample data:
CREATE TABLE table_a (id) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL;
CREATE TABLE table_b (id, name) AS
SELECT 1, 'a1' FROM DUAL UNION ALL
SELECT 2, 'b1' FROM DUAL UNION ALL
SELECT 3, 'c1' FROM DUAL;
CREATE TABLE table_c (name) AS
SELECT 'a1' FROM DUAL;
Would both output:
ID
NAME
1
a1
2
null
3
null
fiddle
You should use a left join, not sure on oracle specifically but it would look something like:
select
a.id,
c.name
from
table_a a
LEFT JOIN table_b b ON (a.id = b.id)
LEFT JOIN table_c c ON (b.name = c.name)

Select-statement: different result in direct query and pl/sql

I don't get why this is not working.
There are two tables:
a) id | value b) id | value
---------- ------------
1 | 1 1 | Hello
2 | 2 2 | Bye
3 | 1
I am doing this query containing a left join:
select b.value
from a
left join b on a.value = b.id
where a.id = 2
The result is: 'Bye'. Which is correct.
But if I am using the same statement in a package with pl/sql it gets the wrong result:
select b.value into word
from a
left join b on a.value = b.id
where a.id = 2 and rownum <= 1
The result is: word = 'Hello' which is incorrect.
You get an exception without the ROWNUM clause inside your PL/SQL function, whereas you get only one result when running the query directly. That's a clear indicator that your PL/SQL procedure is not using the same tables as your adhoc query.
Please check:
do you run your adhoc query as the same user that owns the PL/SQL package?
do you use schema prefixes for your table names in your PL/SQL package?
is your package using invoker rights (i.e. does it contain AUTHID CURRENT_USER)? If yes, why?
First of all, remove "rownum <= 1" condition.
If you really need it, try this:
select value
into word
from (select b.value
from a
left join b on a.value = b.id
where a.id = 2)
where rownum <= 1;
To understand better what is going on, try to execute this:
with a as (select 1 id, 1 value from dual union all
select 2, 2 from dual union all
select 3, 1 from dual),
b as (select 1 id, 'Hello' value from dual union all
select 2, 'Bye' from dual)
select a.id aid, a.value avalue, b.id bid, b.value bvalue, rownum
from a left join b on a.value = b.id;
this:
with a as (select 1 id, 1 value from dual union all
select 2, 2 from dual union all
select 3, 1 from dual),
b as (select 1 id, 'Hello' value from dual union all
select 2, 'Bye' from dual)
select a.id aid, a.value avalue, b.id bid, b.value bvalue, rownum
from a left join b on a.value = b.id
where a.id = 2;
this:
with a as (select 1 id, 1 value from dual union all
select 2, 2 from dual union all
select 3, 1 from dual),
b as (select 1 id, 'Hello' value from dual union all
select 2, 'Bye' from dual)
select a.id aid, a.value avalue, b.id bid, b.value bvalue, rownum
from a left join b on a.value = b.id
where rownum = 1;
and this:
with a as (select 1 id, 1 value from dual union all
select 2, 2 from dual union all
select 3, 1 from dual),
b as (select 1 id, 'Hello' value from dual union all
select 2, 'Bye' from dual)
select a.id aid, a.value avalue, b.id bid, b.value bvalue, rownum
from a left join b on a.value = b.id
where a.id = 2 and rownum = 1;
And compare results.
Your problem is not in the difference between SQL and PL/SQL, but in behavior of rownum.
Why are you using for binding value=id? its bit of disturbing, nevermind. In addition you have a rownum "constraint" between the condition. So think it over, you are saying to oracle to do a left join on value and id, and in the condition you saying you only need the first row, which means the set will only containing the Hello "related" element

Select columns from different tables, without creating a combination of different rows?

lets say I have tableA which has a col1 and tableB which has col2 with the following content:
Table A
---------
A
B
C
D
Table B
---------
1
2
3
4
I want a select statement that returns to me:
A, 1
B, 2
C, 3
D, 4
I have tried making this call:
Select tableA.col1, tableB.col2 from tableA, tableB
but it returns the following:
A, 1
B, 1
C, 1
D, 1
A, 2
..
..
etc
how can i get it to just pull back this:
A, 1
B, 2
C, 3
D, 4
Try this:
select b.val1, b.val1, c.val2 from tableA a
inner join (select ROW_NUMBER() OVER () AS RowNumber, col1 as val1 from tableA) on a.col1 = b.val1
inner join (select ROW_NUMBER() OVER () AS RowNumber, col2 as val2 from tableB) c on c.RowNumber = b.RowNumber

selecting when an aggregate column has two or more items

Let's say I had a query like this:
SELECT * FROM (
SELECT 'a' AS a, '1' AS b
UNION
SELECT 'a' AS a, '2' AS b
UNION
SELECT 'b' AS a, '1' AS b) AS a
GROUP BY a.a
In this case "a".b is an aggregate of 1,2 while "b".b is only an aggregate of 1.
How can I select only "a"?
Question updated to be a bit clearer:
Let's take this very similar query:
SELECT *, GROUP_CONCAT(b) FROM (
SELECT 'a' AS a, '1' AS b
UNION
SELECT 'a' AS a, '2' AS b
UNION
SELECT 'a' AS a, '3' AS b
UNION
SELECT 'b' AS a, '1' AS b
UNION
SELECT 'b' AS a, '2' AS b
) AS a
GROUP BY a.a
Now a.b is 1,2,3 and b.b is 1,2:
I want to select a on the basis that it has 1, 2 and 3.
SELECT a
FROM (
SELECT 'a' AS a, '1' AS b
UNION
SELECT 'a' AS a, '2' AS b
UNION
SELECT 'b' AS a, '1' AS b
) AS a
GROUP BY
a
HAVING COUNT(*) > 1
Note that * syntax that you are using is a MySQL's extension which should not be used like this.
a.b in this case would be not an aggregate: it would be a random record from the group 'a'.
Try:
SELECT a.a,COUNT(*) AS CountOf FROM (
SELECT 'a' AS a, '1' AS b
UNION
SELECT 'a' AS a, '2' AS b
UNION
SELECT 'b' AS a, '1' AS b) AS a
GROUP BY a.a
HAVING COUNT(*) > 1
OUTPUT:
a CountOf
---- -----------
a 2
(1 row(s) affected)
EDIT based on OP's Comment:
This wouldn't in a situation where a.b
was 1,2,3 and b.b was 1,2
try this:
SELECT TOP 1 a.a,COUNT(*) AS CountOf FROM (
SELECT 'a' AS a, '1' AS b
UNION
SELECT 'a' AS a, '2' AS b
UNION
SELECT 'a' AS a, '3' AS b
UNION
SELECT 'b' AS a, '1' AS b
UNION
SELECT 'b' AS a, '2' AS b) AS a
GROUP BY a.a
ORDER BY 2 DESC
OUTPUT:
a CountOf
---- -----------
a 3
(1 row(s) affected)
The following seems to work for me:
SELECT a,b FROM (
SELECT a,b FROM (
SELECT 'a' AS a, '1' AS b
UNION
SELECT 'a' AS a, '2' AS b
UNION
SELECT 'a' AS a, '3' AS b
UNION
SELECT 'b' AS a, '1' AS b
UNION
SELECT 'b' AS a, '2' AS b
) AS a
) b WHERE b.b IN ({x1},{x2}...)
GROUP BY b.a HAVING COUNT(b.b) = 2
Where x1, etc. are the values of "b" that I want to restrict.
For example, in this case
WHERE b.b IN (1,2,3)
will return "a"
WHERE b.b IN (1,2)
will return "a" and "b"
and
WHERE b.b IN (2,3)
will return only "a"
I'm not sure why this works, but it seems to.

How to fetch 3 filed value from a table based on not null condition

I have 3 fields a,b,c(atleast one field is not null) in a table TABLE.For a record I have to fetch a,b,c in the format " a or b or c " if a,b,c are not null and " a or b" if c is null and only c if a and b are null.Please help me with query
SELECT nvl2(a, a||' or ', '')||nvl2(b, b||' or ', '')||nvl2(c, c, '')
FROM ( -- casos de uso
-- select NULL a, NULL b, NULL c from dual union all
select NULL a, NULL b, 'no-nulo-c' c from dual union all
select NULL a, 'no-nulo-b' b, NULL c from dual union all
select NULL a, 'no-nulo-b' b, 'no-nulo-c' c from dual union all
select 'no-nulo-a' a, NULL b, NULL c from dual union all
select 'no-nulo-a' a, NULL b, 'no-nulo-c' c from dual union all
select 'no-nulo-a' a, 'no-nulo-b' b, NULL c from dual union all
select 'no-nulo-a' a, 'no-nulo-b' b, 'no-nulo-c' c from dual
);
I comment the first case because you say "at least one field is not null".