In SQL, can someone help me understand what the query may look like if I'm trying to remove the red-highlighted rows from the image? Based on the logic I need, I need to remove records with different priority values within an option, where as the ones we want to keep have the same priority. Each item/cntry combination has it's own option values (typically 4 like you see here).
I feel like this is just a join to itself, but my mind is mush right now. Help would be appreciated!
You can use the analytic COUNT(DISTINCT ...) function and then you do not need to use a self-join:
SELECT *
FROM (
SELECT t.*,
COUNT(DISTINCT priority) OVER (PARTITION BY cntry, item, "OPTION")
AS cnt
FROM table_name t
)
WHERE cnt = 1;
Which, for the sample data:
CREATE TABLE table_name ("OPTION", priority, item, cntry) AS
SELECT 1, 1, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 1, 1, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 2, 2, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 2, 1, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 3, 1, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 3, 2, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 4, 2, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 4, 2, 'ABCDEF', 'USA' FROM DUAL UNION ALL
SELECT 1, 1, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 2, 2, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 2, 2, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 2, 1, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 3, 1, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 3, 1, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 3, 1, 'HIJKLM', 'CAN' FROM DUAL UNION ALL
SELECT 4, 2, 'HIJKLM', 'CAN' FROM DUAL;
Outputs:
OPTION
PRIORITY
ITEM
CNTRY
CNT
1
1
HIJKLM
CAN
1
3
1
HIJKLM
CAN
1
3
1
HIJKLM
CAN
1
3
1
HIJKLM
CAN
1
4
2
HIJKLM
CAN
1
1
1
ABCDEF
USA
1
1
1
ABCDEF
USA
1
4
2
ABCDEF
USA
1
4
2
ABCDEF
USA
1
db<>fiddle here
use exists and distinct count()
select t1.* from table_name t1 where
exists (
select 1
from table_name t2
where t1.option=t2.option
group by option
having count (distinct priority)=1
)
Related
Existing table
Output query should render this
Use GROUP BY and take the MAX of each column:
SELECT event,
MAX(numericval) AS quantity,
MAX(details) AS status
FROM table_name
GROUP BY event
Which, for the sample data:
CREATE TABLE table_name (event, elements, numericval, details) AS
SELECT 1, 'quality', 5, NULL FROM DUAL UNION ALL
SELECT 1, 'status', NULL, 'yes' FROM DUAL UNION ALL
SELECT 2, 'quality', 10, NULL FROM DUAL UNION ALL
SELECT 2, 'status', NULL, 'no' FROM DUAL UNION ALL
SELECT 3, 'quality', 15, NULL FROM DUAL UNION ALL
SELECT 3, 'status', NULL, 'yes' FROM DUAL;
Outputs:
EVENT
QUANTITY
STATUS
1
5
yes
2
10
no
3
15
yes
For your update, you can use conditional aggregation:
SELECT event,
MAX(CASE elements WHEN 'quality' THEN numericval END) AS quantity,
MAX(CASE elements WHEN 'status' THEN details END) AS status,
MAX(CASE elements WHEN 'Lot' THEN numericval END) AS lot
FROM table_name
GROUP BY event
Which for the updated data:
CREATE TABLE table_name (event, elements, numericval, details) AS
SELECT 1, 'quality', 5, NULL FROM DUAL UNION ALL
SELECT 1, 'status', NULL, 'yes' FROM DUAL UNION ALL
select 1 , 'Lot',1,null from dual union all
SELECT 2, 'quality', 10, NULL FROM DUAL UNION ALL
SELECT 2, 'status', NULL, 'no' FROM DUAL UNION ALL
select 2 , 'Lot',3,null from dual union all
SELECT 3, 'quality', 15, NULL FROM DUAL UNION ALL
SELECT 3, 'status', NULL, 'yes' FROM DUAL union all
select 3, 'Lot', 4,null from dual;
Outputs:
EVENT
QUANTITY
STATUS
LOT
1
5
yes
1
2
10
no
3
3
15
yes
4
db<>fiddle here
I know a little bit of sql, only the basic, now I need to create a analytic query but can't do this yet.
I have 2 tables on my db oracle, client and exams:
I am tried a lot of ways to get the mean of exams by client, but no success yet.4
The result expected is:
exams = 13
clients = 6
13/6= 2.166666666...7
How can I do that?
If you have clients who have not taken any exams then you want:
SELECT AVG(COUNT(e.nu_ordem)) AS avg_exames_by_client
FROM cliente c
LEFT OUTER JOIN exames e
ON (c.id = e.id_cliente)
GROUP BY c.id;
or:
SELECT (SELECT COUNT(*) FROM exames) / (SELECT COUNT(*) FROM cliente)
AS avg_exames_by_client
FROM DUAL;
Which, for the sample data:
CREATE TABLE cliente (id PRIMARY KEY) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL UNION ALL
SELECT 4 FROM DUAL UNION ALL
SELECT 5 FROM DUAL UNION ALL
SELECT 6 FROM DUAL;
CREATE TABLE exames (nu_ordem PRIMARY KEY, id_cliente) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 5 FROM DUAL UNION ALL
SELECT 3, 5 FROM DUAL UNION ALL
SELECT 4, 2 FROM DUAL UNION ALL
SELECT 5, 6 FROM DUAL UNION ALL
SELECT 6, 1 FROM DUAL UNION ALL
SELECT 7, 1 FROM DUAL UNION ALL
SELECT 8, 4 FROM DUAL UNION ALL
SELECT 9, 5 FROM DUAL UNION ALL
SELECT 10, 3 FROM DUAL UNION ALL
SELECT 11, 6 FROM DUAL UNION ALL
SELECT 12, 2 FROM DUAL UNION ALL
SELECT 13, 1 FROM DUAL;
Both output:
AVG_EXAMES_BY_CLIENT
2.166666666666666667
If you then add a couple of clients but no more exams:
INSERT INTO cliente (id)
SELECT 7 FROM DUAL UNION ALL
SELECT 8 FROM DUAL
Then the average is:
AVG_EXAMES_BY_CLIENT
1.625
db<>fiddle here
You can try below formula to get the result -
SELECT COUNT(*)/COUNT(DISTINCT id_cliente)
FROM exams;
I have a oracle sql table that looks like so
"STUDENT_ID","FULL_NAME","SEMESTER_ID","STIP_ID"
"1","Liam Bottrill","1","1"
"1","Liam Bottrill","2","3"
"1","Liam Bottrill","3","2"
"1","Liam Bottrill","4","5"
"2","Maurits Smitham","1","6"
"2","Maurits Smitham","2",""
"2","Maurits Smitham","3","2"
"2","Maurits Smitham","4","6"
"43","Jackie Cotton","1",""
"43","Jackie Cotton","2",""
"43","Jackie Cotton","3",""
"43","Jackie Cotton","4",""
I want to group this table by "STUDENT_ID" and exclude from result any students that have any of "STIP_ID" rows empty
Im aiming for result like this:
"STUDENT_ID","FULL_NAME"
"1","Liam Bottrill"
Liam Bottrill should be displayed while Maurits Smitham and Jackie Cotton should be excluded from result
Can you please help me with such aggregate function?
Here is one way, using aggregation:
SELECT *
FROM yourTable
WHERE STUDENT_ID IN (
SELECT STUDENT_ID
FROM yourTable
GROUP BY STUDENT_ID
HAVING COUNT(CASE WHEN STIP_ID IS NULL THEN 1 END) = 0
);
Another way, using exists logic:
SELECT t1.*
FROM yourTable t1
WHERE NOT EXISTS (
SELECT 1
FROM yourTable t2
WHERE t2.STUDENT_ID = t1.STUDENT_ID AND
t2.STIP_ID IS NULL
);
You can group by the identifier and then use conditional aggregation to find the student where the count when STIP_ID is NULL (which, in Oracle, is the same as an empty string):
SELECT student_id,
MAX(full_name) AS full_name
FROM table_name
GROUP BY student_id
HAVING COUNT(CASE WHEN stip_id IS NULL THEN 1 END) = 0;
Which, for your sample data:
CREATE TABLE table_name (STUDENT_ID, FULL_NAME, SEMESTER_ID, STIP_ID) AS
SELECT 1, 'Liam Bottrill', 1, 1 FROM DUAL UNION ALL
SELECT 1, 'Liam Bottrill', 2, 3 FROM DUAL UNION ALL
SELECT 1, 'Liam Bottrill', 3, 2 FROM DUAL UNION ALL
SELECT 1, 'Liam Bottrill', 4, 5 FROM DUAL UNION ALL
SELECT 2, 'Maurits Smitham', 1, 6 FROM DUAL UNION ALL
SELECT 2, 'Maurits Smitham', 2, NULL FROM DUAL UNION ALL
SELECT 2, 'Maurits Smitham', 3, 2 FROM DUAL UNION ALL
SELECT 2, 'Maurits Smitham', 4, 6 FROM DUAL UNION ALL
SELECT 43, 'Jackie Cotton', 1, NULL FROM DUAL UNION ALL
SELECT 43, 'Jackie Cotton', 2, NULL FROM DUAL UNION ALL
SELECT 43, 'Jackie Cotton', 3, NULL FROM DUAL UNION ALL
SELECT 43, 'Jackie Cotton', 4, NULL FROM DUAL;
Outputs:
STUDENT_ID
FULL_NAME
1
Liam Bottrill
db<>fiddle here
I have this query that returns the data below it
select LISTAGG(d.DOCUMENT_TYPE_CD, ',') WITHIN GROUP (ORDER BY D.DOCUMENT_TYPE_CD) as value
from test_table d;
VALUE
---------
CI,ECI,POA
now I'm trying to add a condition whenever 'ECI' value is present, it should exclude 'CI' in the result like this one below
VALUE
---------
ECI,POA
I tried using case statement in where condition it prompted an error
select LISTAGG(d.DOCUMENT_TYPE_CD, ',')
WITHIN GROUP (ORDER BY D.DOCUMENT_TYPE_CD) as value
from test_table d
where CASE d.DOCUMENT_TYPE_CD
WHEN 'ECI' THEN d.DOCUMENT_TYPE_CD <> 'CI'
END;
ORA-00905: missing keyword
00905. 00000 - "missing keyword"
*Cause:
*Action:
Error at Line: 7 Column: 36
is there any other way I could resolve this?
See if this helps; read comments within code.
SQL> with
2 test (id, document_type_cd) as
3 -- sample data
4 (select 1, 'ECI' from dual union all
5 select 1, 'CI' from dual union all
6 select 1, 'POA' from dual union all
7 --
8 select 2, 'CI' from dual union all
9 select 2, 'POA' from dual union all
10 --
11 select 3, 'XYZ' from dual union all
12 select 3, 'ABC' from dual
13 ),
14 temp as
15 -- see whether CI and ECI exist per each ID
16 (select id,
17 sum(case when document_type_cd = 'CI' then 1 else 0 end) sum_ci,
18 sum(case when document_type_cd = 'ECI' then 1 else 0 end) sum_eci
19 from test
20 group by id
21 ),
22 excl as
23 -- exclude CI rows if ECI exist for that ID
24 (select a.id,
25 a.document_type_cd
26 from test a join temp b on a.id = b.id
27 where a.document_type_cd <> case when b.sum_ci > 0 and b.sum_eci > 0 then 'CI'
28 else '-1'
29 end
30 )
31 -- finally:
32 select e.id,
33 listagg(e.document_type_cd, ',') within group (order by e.document_type_cd) result
34 from excl e
35 group by e.id;
ID RESULT
---------- --------------------
1 ECI,POA
2 CI,POA
3 ABC,XYZ
SQL>
Something like this:
select LISTAGG(d.DOCUMENT_TYPE_CD, ',')
WITHIN GROUP (ORDER BY D.DOCUMENT_TYPE_CD) as value
from test_table d,
(select sum (case when DOCUMENT_TYPE_CD = 'CI' then 1 else 0 end) C
from test_table) A
where d.DOCUMENT_TYPE_CD <> case when A.c > 0 then 'CI' when A.c = 0 then ' ' end;
DEMO
You may identify the presence of both the values with two conditional aggregations in the same group by and then replace CI inside the result of listagg in one pass.
with a(id, cd) as (
select 1, 'ABC' from dual union all
select 1, 'ECI' from dual union all
select 1, 'CI' from dual union all
select 1, 'POA' from dual union all
select 2, 'XYZ' from dual union all
select 2, 'ECI' from dual union all
select 2, 'CI' from dual union all
select 2, 'POA' from dual union all
select 3, 'CI' from dual union all
select 3, 'POA' from dual union all
select 4, 'ABC' from dual union all
select 4, 'DEF' from dual
)
select
id,
ltrim(
/*Added comma in case CI will be at the beginning*/
replace(
',' || listagg(cd, ',') within group (order by cd asc),
decode(
/*If both are present, then replace CI. If not, then do not replace anything*/
max(decode(cd, 'CI', 1))*max(decode(cd, 'ECI', 1)),
1,
',CI,'
),
','
),
','
) as res
from a
group by id
ID | RES
-: | :----------
1 | ABC,ECI,POA
2 | ECI,POA,XYZ
3 | CI,POA
4 | ABC,DEF
db<>fiddle here
Instead of using GROUP BY, you can also use windowing (aka analytic) functions to check the presence of ECI per group (test data shamelessly stolen from #littlefoot):
with
test (id, document_type_cd) as
-- sample data
(select 1, 'ECI' from dual union all
select 1, 'CI' from dual union all
select 1, 'POA' from dual union all
--
select 2, 'CI' from dual union all
select 2, 'POA' from dual union all
--
select 3, 'XYZ' from dual union all
select 3, 'ABC' from dual
),
temp as
(select id,
document_type_cd,
sum(case when document_type_cd = 'ECI' then 1 else 0 end) over (partition by id) as sum_eci
from test
)
select a.id,
listagg(a.document_type_cd, ',') within group (order by a.document_type_cd) result
from temp a
where a.document_type_cd != 'CI' or sum_eci = 0
group by a.id;
I can select a list of rows from a table. but I want to show them by swapping upside down.
Explaination:
with table1 as
(
select 1 ID, 'txt1' value from dual
union all
select 2, 'txt2' from dual
union all
select 7, 'txt7' from dual
union all
select 5, 'txt5' from dual
union all
select 3, 'txt3' from dual
)
select * from table1;
in above query I can obtain following result
ID | VALUE
------------------
1 txt1
2 txt2
7 txt7
5 txt5
3 txt3
but I want to show them as follows
ID | VALUE
------------------
3 txt3
5 txt5
7 txt7
2 txt2
1 txt1
How to do that?
One approach would be to add a computed column to your set of union queries, then order by that column:
WITH table1 AS (
SELECT 1 ID, 'txt1' value, 1 AS position FROM dual
UNION ALL
SELECT 2, 'txt2', 2 FROM dual
UNION ALL
SELECT 7, 'txt7', 3 FROM dual
UNION ALL
SELECT 5, 'txt5', 4 FROM dual
UNION ALL
SELECT 3, 'txt3', 5 FROM dual
)
SELECT *
FROM table1
ORDER BY pos DESC;
Note that there is no internal order to a SQL table in general. Actually, even the current ordering you are observing is not necessarily guaranteed by Oracle. If you expect a certain order in a result set, you need to impose it via a ORDER BY clause.
How's this?
with table1 as
(
select 1 ID, 'txt1' value from dual
union all
select 2, 'txt2' from dual
union all
select 7, 'txt7' from dual
union all
select 5, 'txt5' from dual
union all
select 3, 'txt3' from dual
)
select * from table1 order by rownum desc;
Actually this is not working for this perticular example. but it is working for normal table.