selecting when an aggregate column has two or more items - sql

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.

Related

SQL Query getting on row where a = 1 the id of the next row where b = 1

i have this test sample.
WITH test AS(
SELECT 1 AS id, "0" AS a, "0" AS b
UNION SELECT 2 AS id, "0" AS a, "1" AS b
UNION SELECT 3 AS id, "0" AS a, "0" AS b
UNION SELECT 4 AS id, "1" AS a, "0" AS b
UNION SELECT 5 AS id, "0" AS a, "0" AS b
UNION SELECT 6 AS id, "0" AS a, "1" AS b
UNION SELECT 7 AS id, "0" AS a, "1" AS b
UNION SELECT 8 AS id, "1" AS a, "0" AS b
UNION SELECT 9 AS id, "0" AS a, "1" AS b
UNION SELECT 10 AS id, "0" AS a, "0" AS b
),
test_a AS (
SELECT id FROM test WHERE a = "1"),
test_b AS (
SELECT id FROM test WHERE b = "1")
I need every row where a = 1 (thats simple) with the id of the next row where b = 1
The result should be:
id_a id_b
4 6
8 9
my only working solution is by joining all rows where id is greater and b = 1 to the original data, set a row number and keep only row_nr 1 but with billions of data this is very slow.
Hope you can get my point. Thanks for any help.
Edit:
Just tried this code, but i get the error below. If i use B.id = A.id then i got a result, but its not what i need.
SELECT A.id AS id_a,
(SELECT min(id) FROM test_b B WHERE B.id < A.id) AS id_b
FROM test_a A
ORDER BY A.id
Error running query
Correlated column is not allowed in a non-equality predicate:
Example:
select
aa.id as id_a,
(select id from test where b = 1 and id > aa.id limit 1) as id_b
from test aa
where aa.a = 1
order by aa.id
Now after a while, i got a solution, working with databricks sql. I'm using the last_value function with "ignore null" (i think its also available for other sql engines)
WITH test AS(
SELECT 1 AS id, "0" AS a, "0" AS b
UNION SELECT 2 AS id, "0" AS a, "1" AS b
UNION SELECT 3 AS id, "0" AS a, "0" AS b
UNION SELECT 4 AS id, "1" AS a, "0" AS b
UNION SELECT 5 AS id, "0" AS a, "0" AS b
UNION SELECT 6 AS id, "0" AS a, "1" AS b
UNION SELECT 7 AS id, "0" AS a, "1" AS b
UNION SELECT 8 AS id, "1" AS a, "0" AS b
UNION SELECT 9 AS id, "0" AS a, "1" AS b
UNION SELECT 10 AS id, "0" AS a, "0" AS b
),
test_a AS (
SELECT id FROM test WHERE a = "1"),
test_b AS (
SELECT id FROM test WHERE b = "1"),
union as (
select id as id, NULL as id_b, 0 as t from test_a
union
select id as id, id as id_b, 1 as t from test_b
),
missing_data as (
SELECT id , last_value(id_b, true) over (order by id DESC) id_b, t FROM union
)
SELECT id, id_b FROM missing_data where t = 0

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

Oracle result set combinations

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

Use a calculated field in the where clause

Is there a way to use a calculated field in the where clause?
I want to do something like
SELECT a, b, a+b as TOTAL FROM (
select 7 as a, 8 as b FROM DUAL
UNION ALL
select 8 as a, 8 as b FROM DUAL
UNION ALL
select 0 as a, 0 as b FROM DUAL
)
WHERE TOTAL <> 0
;
but I get ORA-00904: "TOTAL": invalid identifier.
So I have to use
SELECT a, b, a+b as TOTAL FROM (
select 7 as a, 8 as b FROM DUAL
UNION ALL
select 8 as a, 8 as b FROM DUAL
UNION ALL
select 0 as a, 0 as b FROM DUAL
)
WHERE a+b <> 0
;
Logically, the select clause is one of the last parts of a query evaluated, so the aliases and derived columns are not available. (Except to order by, which logically happens last.)
Using a derived table is away around this:
select *
from (SELECT a, b, a+b as TOTAL FROM (
select 7 as a, 8 as b FROM DUAL
UNION ALL
select 8 as a, 8 as b FROM DUAL
UNION ALL
select 0 as a, 0 as b FROM DUAL)
)
WHERE TOTAL <> 0
;
This will work...
select *
from (SELECT a, b, a+b as TOTAL FROM (
select 7 as a, 8 as b FROM DUAL
UNION ALL
select 8 as a, 8 as b FROM DUAL
UNION ALL
select 0 as a, 0 as b FROM DUAL)
) as Temp
WHERE TOTAL <> 0;

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".