Select rows based on values in query - sql

My table :
id
role
b
f
b
s
b
g
a
s
a
f
c
f
I want a distinct id but with corresponding role, with this logic:
If g exists select g if not
if s exists select s if not
if f exists select f.
Query should yield :
id
role
b
g
a
s
c
f
I tried group by id, but role cannot be sorted in a useful order.

select id
,role
from (
select *
,rank() over(partition by id order by case when role = 'g' then 2 when role = 's' then 1 end desc) as rnk
from t
) t
where rnk = 1
id
role
a
s
b
g
c
f
Fiddle

Related

Oracle SQL - Delete Entries Based Off Unique Rows

I am pulling a single column from a DB and it looks something like this:
Group
A
A
A
B
B
B
C
D
D
D
E
F
F
F
I need to delete unique entries, so entries A, B, D and F should stay and entries C and E should be deleted.
I am getting this row based of a query like this:
select Group from table where type = 'rec';
and basically each type should have more than one group and if it doesn't it needs to be removed.
NOTE: I need it to be automated and not just a "remove C" and "remove E" because there are thousands of rows and I'm not sure which I will need to delete unless I just find them. The number of rows that will need to be deleted will also be changing, hence why I need it to be automated based off of count.
One method is:
delete t
where "group" in (select "group" from t group by "group" having count(*) = 1);
Based on your sample code:
delete t
where type = 'rec' and
"group" in (select "group" from t where type = 'rec' group by "group" having count(*) = 1);
You could also do this as:
delete t
where type = 'rec' and
not exists (select 1
from t t2
where t2.group = t.group and t2.type = 'rec' and t2.rowid <> t.rowid
);
Judging by your comments all you need is running total. If entry occurred once then select/delete it. The analytic functions is the best and easiest way if you ask me:
SELECT * FROM
(
SELECT COUNT(grp) OVER (PARTITION BY grp ORDER BY grp) cnt -- number of occurances --
, grp
FROM
( -- convert to multi-row - REPLACE AAABBB with your actual column --
SELECT trim(regexp_substr('A A A B B B C D D D E F F F', '[^ ]+', 1, LEVEL)) grp
FROM dual -- from your table_name --
CONNECT BY LEVEL <= regexp_count('A A A B B B C D D D E F F F', '[^ ]+')
)
)
WHERE cnt = 1 -- Select/Delete only those that appeared once --
/
Output:
cnt|grp
--------
1 C
1 E
Full output, if you comment where:
cnt|grp
--------
3 A
3 A
3 A
3 B
3 B
3 B
1 C
3 D
3 D
3 D
1 E
3 F
3 F
3 F
Final edit based on your questions. This simulates your table:
WITH your_table AS
(
SELECT 'rec' grp_type FROM dual
UNION ALL
SELECT 'not_rec' grp_type FROM dual
)
SELECT grp_type FROM your_table WHERE grp_type = 'rec' -- apply all that above to this select --
/

unique records by one column

I have been working on this for a long time now and cant get the answer.
I have a table with 4 columns. I need to return two of these columns, A and B.
However I need the data to be filtered by the first column A. A is not a primary key. How can I achieve this? If I can return all the columns its also ok, as long as the data is only filtered by column A.
A B C D
1 e f r
1 e f r
1 e k t
2 c f r
2 c f r
3 l f r
This should return
A B C D
1 e f r
2 c f r
3 l f r
Which query wil give me this result? Until now I have this but it does not work well:
SELECT DISTINCT A, B, FROM myTable WHERE C=f
You could use Row_Number() to rank the rows within each distinct A value, then get the first row of each.
SELECT
a, b, c, d
FROM
(SELECT
a, b, c, d,
Row_Number() OVER (PARTITION BY a ORDER BY b, c, d) rn
FROM
myTable) mt
WHERE
rn = 1
Try
SELECT A,B FROM myTable WHERE C='f' GROUP BY A,B
use this
with cte as
(
select * , row_Number() over (Partition by A order by A) as aa from myTable
)
select * from cte where aa = 1

sql excluding certain results

lets say i have a data set of
A B
-- --
a 1
b 1
c 1
d 1
d 2
e 1
f 1
f 2
g 1
how would i exclude a result in column B of 1, if column B has values of both 1 and 2 for the same value in column A?
i want my results to look like this
A B
-- --
a 1
b 1
c 1
d 2
e 1
f 2
g 1
Checking explicitly here for the values 1 and 2 and using the fact that there are exactly two of them. You could potentially make this less cumbersome if it's safe to assume that you always want the highest value.
select
tbl.A,
tbl.B
from
Table1 tbl
left outer join (
select
A
from
Table1
where
B in (1,2)
group by
A
having
count(B) = 2
) mlt on tbl.A = mlt.A
where
(
mlt.A is not null
and tbl.B = 2
) or (
mlt.A is null
and tbl.B = 1
)
Figure out all the A values that have both 1 and 2.
Match those to the table on the A value.
If A is in the subquery, use the B = 2 record. If it isn't, use the B = 1 record.
select
* from tbl where a IN
(
select
a from tbl
group by a
having count(*)>1
)
and b!=1
UNION ALL
select
* from tbl where a IN
(
select
a from tbl
group by a
having count(*)=1
)
For the example data and desired result, the simplest query to achieve the result would be a GROUP BY operation and an aggregate function.
SELECT d.A
, MAX(d.B) AS B
FROM my_data_set d
GROUP BY d.A
ORDER BY d.A
If we are only interested in rows that have a 1 or 2 in column B, we can add a WHERE clause
SELECT d.A
, MAX(d.B) AS B
FROM my_data_set d
WHERE d.B IN (1,2)
GROUP BY d.A
ORDER BY d.A
With the example data, the output is the same.
Both of these statements achieve the specified result. (There is only a single row returned for each distinct value in A.)
Or, for the same the example data, we can return the same result set with a more literal implementation of the specification.
To exclude rows with 1when there is a row with 2 for the same value of A, we can use a NOT EXISTS predicate and a correlated subquery.
SELECT d.A
, d.B
FROM my_data_set d
WHERE ( d.B = 2 )
OR ( d.B = 1 AND
NOT EXISTS ( SELECT 1
FROM my_data_set e
WHERE e.A = d.A
AND e.B = 2
)
)
ORDER BY d.A, d.B

Need help constructing a query to group related elements

I have a table containing the IDs of elements that are related.
ID1 ID2
A B
A C
B D
B C
E F
G D
G C
H I
D C
The example contains the following groups:
A,B,C,D,G
E,F
H,I
Since A is connected to B,C, B is connected to C,D and D is connected to G.
E,F and H,I are only related to each other.
Is it possible to find these groups using SQL? Not sure what the output of the SQL would be, maybe something like this:
ID group
A 1
B 1
C 1
D 1
G 1
E 2
F 2
H 3
I 3
Probably some form of hierarchical query will do the trick but those usually baffle me.
As long as I can discriminate between groups.
Here is what I found:
select root2 || ', ' || listagg(id1, ', ') within group (order by id1) grp
from
(
select id1, max(root2) keep (dense_rank last order by lev) root2
from
(
select t.*, connect_by_root id2 root2, level lev
from <my_table> t
connect by prior t.id1 = t.id2
)
group by id1
)
group by root2
;
This gives:
**GRP**
C, A, B, D, G
F, E
I, H

Sql view with column identifier

I am creating a select query with union of three tables....
like this
select a as A,b as B c as C where c = x union
select b as A,d as B e as C where e = y and d = a union
select f as A,g as B,h as C
and the result of query is like this:
A B C
===========
1 abc ...
55 def ...
1 sas ...
so I want to have a column that count the number of row, just to prevent the repetition of identifier.
Somthing like this
Row A B C
================
1 1 abc ...
2 55 def ...
3 1 sas ...
....
My question is how it can be done?
You can use ROW_NUMBER() like this:
SELECT ROW_NUMBER() OVER (ORDER BY A,B,C) AS RowNo, *
FROM
(
select a as A,b as B c as C where c = x
union
select b as A,d as B e as C where e = y and d = a
union
select f as A,g as B,h as C
) x
CREATE VIEW dbo.vname
AS
SELECT [Row] = ROW_NUMBER() OVER (ORDER BY A), A, B, C FROM
( <UNION query here> ) AS x;
Replace ORDER BY A with whatever ordering you'd like to see applied. Note that you will need to use ORDER BY on the outer query against dbo.viewname to guarantee that Row will come out in that order.
You can use a common table expression to achieve this:
WITH unionTable
AS
(
select a as A, b as B, c as C where c = x union
select b as A, d as B, e as C where e = y and d = a union
select f as A, g as B, h as C
)
SELECT ROW_NUMBER() OVER (ORDER BY A) AS RowNumber, *
FROM unionTable