How to get distinct employees that do not have a particular skillset - sql

I have a table that has two columns. Employee_id (which is unique per employee) and next column for employee skillset. One employee can have multiple skillset. How do I retrieve the list of distinct employees who don't have skillset 'c' if A,B,C,D,E are the five types of skillset that employees can have.
employee_id skillset
1 A
1 C
2 E
3 A
3 B
3 C
4 D
4 C
5 B
I have tried self join and other methods but it is not working.
select distinct employee_id from employee_skillset where skillset not like 'C'
When I run my query, it is still giving me employee_ids that have skillset of "c"

You can group by employee_id and set a condition in the HAVING clause:
select employee_id
from employee_skillset
group by employee_id
having sum(case when skillset = 'C' then 1 else 0 end) = 0
Or with NOT EXISTS:
select distinct s.employee_id
from employee_skillset s
where not exists (
select 1 from employee_skillset
where employee_id = s.employee_id and skillset = 'C'
)

What are your expected results from your data set? 2 and 5?
Why not something like below
SELECT DISTINCT employee_id
FROM Table1
WHERE skillset <> 'C';

MINUS set operator is one option:
SQL> with employee_skillset (employee_id, skillset) as
2 (select 1, 'a' from dual union all
3 select 1, 'c' from dual union all
4 select 2, 'e' from dual union all
5 select 3, 'a' from dual union all
6 select 3, 'b' from dual union all
7 select 3, 'c' from dual union all
8 select 4, 'd' from dual union all
9 select 4, 'c' from dual union all
10 select 5, 'b' from dual
11 )
12 select employee_id from employee_skillset
13 minus
14 select employee_id from employee_skillset where skillset = 'c';
EMPLOYEE_ID
-----------
2
5
SQL>
Yet another option:
<snip>
12 select employee_id
13 from (select employee_id,
14 case when skillset = 'c' then 1 else 0 end flag
15 from employee_skillset
16 )
17 group by employee_id
18 having sum(flag) = 0;
EMPLOYEE_ID
-----------
2
5
SQL>
Or:
<snip>
12 select employee_id
13 from (select employee_id,
14 listagg(skillset, ',') within group (order by null) lagg
15 from employee_skillset
16 group by employee_id
17 )
18 where instr(lagg, 'c') = 0;
EMPLOYEE_ID
-----------
2
5
SQL>

Related

select only those users whose contacts length is not 5

I have table like this:
id
name
contact
1
A
65489
1
A
1
A
45564
2
B
3
C
12345
3
C
1234
4
D
32
4
D
324
I only want users who have no contact or the contact length is not five.
If the user has two or more contacts and the length of one of them is five and the rest is not, then such users should not be included in the table.
so,If the customer has at least one contact length of five, I do not want that.
so, i want table like this:
id
name
contact
2
B
4
D
32
4
D
324
Can you halp me?
You could actually do a range check here:
SELECT id, name, contact
FROM yourTable t1
WHERE NOT EXISTS (
SELECT 1
FROM yourTable t2
WHERE t2.id = t1.id AND TO_NUMBER(t2.contact) BETWEEN 10000 AND 99999
);
Note that if contact already be a numeric column, then just remove the calls to TO_NUMBER above and compare directly.
Yet another option:
SQL> with test (id, name, contact) as
2 (select 1, 'a', 65879 from dual union all
3 select 1, 'a', null from dual union all
4 select 1, 'a', 45564 from dual union all
5 select 2, 'b', null from dual union all
6 select 3, 'c', 12345 from dual union all
7 select 3, 'c', 1234 from dual union all
8 select 4, 'd', 32 from dual union all
9 select 4, 'd', 324 from dual
10 )
11 select *
12 from test a
13 where exists (select null
14 from test b
15 where b.id = a.id
16 group by b.id
17 having nvl(max(length(b.contact)), 0) < 5
18 );
ID N CONTACT
---------- - ----------
2 b
4 d 32
4 d 324
SQL>
COUNT analytic function can also be used to get the job done.
select id, name, contact
from (
select id, name, contact
, count( decode( length(contact), 5, 1, null ) ) over( partition by id, name ) cnt
from YourTable
)
where cnt = 0
demo

COUNT(USER_ID) Without GROUP BY

Table_A Data :
NAME USER_ID V_NAME NUMBER
AUTO 1 HOME 123
CAT 2 HAT 456
DOT 3 789
FAN 4 REG
I was looking for output like
No of distinct users no.of ID's no.of V_Name no.of users with no.of
NUMBER column data users without NUMBER column data
4 4 3 3 1
Query :
SELECT
count(distinct NAME) AS No_of_Names,
count (distinct ID) AS No_of_ID,
(select count(distinct ID) from TABLE_A where NUMBER is not null) as No_of_users_with_Number_Data,
(select count(distinct ID) from TABLE_A where NUMBER is null) as No_of_users_without_Number_Data,
count (distinct V_NAME) as No_of_V_NAME FROM TABLE_A;
But with above query I am getting error :
ORA-00937: not a single-group group function.
Please help in getting the data in above format. Thanks for your help in advance
You can use:
SELECT COUNT(DISTINCT NAME) AS No_of_Names,
COUNT(DISTINCT ID) AS No_of_ID,
COUNT(DISTINCT V_NAME) as No_of_V_NAME,
COUNT(DISTINCT CASE WHEN "NUMBER" IS NOT NULL THEN ID END)
AS No_of_users_with_Number_Data,
COUNT(DISTINCT CASE WHEN "NUMBER" IS NULL THEN ID END)
as No_of_users_without_Number_Data
FROM table_a;
Which, for the sample data:
CREATE TABLE table_a (name, id, v_name, "NUMBER") as
SELECT 'AUTO', 1, 'HOME', 123 FROM DUAL UNION ALL
SELECT 'CAT', 2, 'HAT', 456 FROM DUAL UNION ALL
SELECT 'DOT', 3, NULL, 789 FROM DUAL UNION ALL
SELECT 'FAN', 4, 'REG' , NULL FROM DUAL;
(Note: You should not name your columns NUMBER as that is a data type.)
Outputs:
NO_OF_NAMES
NO_OF_ID
NO_OF_V_NAME
NO_OF_USERS_WITH_NUMBER_DATA
NO_OF_USERS_WITHOUT_NUMBER_DATA
4
4
3
3
1
If you then add extra rows:
INSERT INTO table_a VALUES ('DOT', 3, NULL, NULL);
INSERT INTO table_a VALUES ('FAN', 4, 'XYZ', 321);
Then the query output is:
NO_OF_NAMES
NO_OF_ID
NO_OF_V_NAME
NO_OF_USERS_WITH_NUMBER_DATA
NO_OF_USERS_WITHOUT_NUMBER_DATA
4
4
4
4
2
(Note: the output in the last two columns is a count of distinct users and not a count of rows.)
db<>fiddle here
Here's one option:
SQL> with table_a (name, user_id, v_name, num) as
2 (select 'auto', 1, 'home', 123 from dual union all
3 select 'cat' , 2, 'hat' , 456 from dual union all
4 select 'dot' , 3, null , 789 from dual union all
5 select 'fan' , 4, 'reg' , null from dual
6 )
7 select
8 count(distinct name) cnt_1,
9 count(distinct user_id) cnt_2,
10 sum(case when v_name is not null then 1 else 0 end) cnt_3,
11 sum(case when num is not null then 1 else 0 end) cnt_4,
12 sum(case when num is not null then 0 else 1 end) cnt_5
13 from table_a;
CNT_1 CNT_2 CNT_3 CNT_4 CNT_5
---------- ---------- ---------- ---------- ----------
4 4 3 3 1
SQL>

select data based on the next rows and ids

I have the following table and data,
TABLE - DEAL
ID DEALID
1 DEAL1
2 DEAL2
ALL DEAL2
3 DEAL2
4 DEAL5
5 DEAL5
ALL DEAL6
I want to get only the data as below
ID DEALID
1 DEAL1
4 DEAL5
5 DEAL5
ALL DEAL6
I want to select data based on the value of id column and dealid column.
If the value of id is 'ALL' and corresponding dealid repeats, omit all records with that dealid
Based on sample data you posted, see if this helps. Read comments within code.
SQL> with
2 test (id, dealid) as
3 -- sample data
4 (select '1' , 'deal1' from dual union all
5 select '2' , 'deal2' from dual union all
6 select 'ALL', 'deal2' from dual union all
7 select '3' , 'deal2' from dual union all
8 select '4' , 'deal5' from dual union all
9 select '5' , 'deal5' from dual union all
10 select 'ALL', 'deal6' from dual
11 ),
12 all2 as
13 -- DEALIDs that contain different ID values, and MAX of them is ALL
14 (select dealid
15 from test
16 group by dealid
17 having min(id) <> max(id)
18 and max(id) = 'ALL'
19 )
20 select t.id, t.dealid
21 from test t join all2 a on a.dealid <> t.dealid;
ID DEALI
--- -----
1 deal1
4 deal5
5 deal5
ALL deal6
SQL>

Print message when no data is found

Need a query to get the Employee name, total fuel used by each employee.
If fuel is not used by an employee then the second column should have a
text “No fuel used”.
These are the following two tables:
Table1: EmployeeID, FirstName
1 Vikas
2 nikita
3 Ashish
4 Nikhil
5 anish
Table2: ID, Fuel
1 10
2 9
3 8
4 6
5 12
6 11
7 10
8 9
9 8
10 10
11 9
12 12
13 7
14 15
where The column table2.ID is a foreign key to table1.EmployeeID.
This is code which I have written, Which is most probably wrong.
select ID, FirstName, sum(table2.Fuel) sum_fuel
from table2,table1
where EmployeeID=ID IN (
select ID, coalesce(ID, 'No-fuel used') as ID
from table1 t1
left join table2 t2 on t2.ID = t1.EmployeeID
)
group by fuel
order by ID DESC;
As you can see from two tables that employee with from 1 to 5 of table1 are in table2. So for these employee I need to show total fuel used by every individual. And for employee with ID from 6 to 14 are not available in table1 so for these employee “No fuel used” message should be printed.
You can use a left join. This way, whenever the Id values for tables don't match you'll get null values for sum(fuel) value, and will assign the string 'No fuel used'for sum_fuel column by using nvl() function:
with table1( EmployeeID, FirstName ) as
(
select 1,'Vikas' from dual union all
select 2,'nikita' from dual union all
select 3,'Ashish' from dual union all
select 4,'Nikhil' from dual union all
select 5,'anish' from dual union all
select 15,'pratteek' from dual
), table2( ID, Fuel ) as
(
select 1, 10 from dual union all
select 2, 9 from dual union all
select 3, 8 from dual union all
select 4, 6 from dual union all
select 5, 12 from dual union all
select 6, 11 from dual union all
select 7, 10 from dual union all
select 8, 9 from dual union all
select 9, 8 from dual union all
select 10, 10 from dual union all
select 11, 9 from dual union all
select 12, 12 from dual union all
select 13, 7 from dual union all
select 14, 15 from dual
)
select EmployeeID, FirstName, nvl(to_char(sum(t2.Fuel)),'No fuel used') as sum_fuel
from table1 t1
left join table2 t2
on t1.EmployeeID = t2.ID
group by EmployeeID, FirstName
order by EmployeeID desc;
EMPLOYEEID FIRSTNAME SUM_FUEL
---------- --------- ------------
15 pratteek No fuel used
5 anish 12
4 Nikhil 6
3 Ashish 8
2 nikita 9
1 Vikas 10
Demo
This may work---
SELECT ID
, FirstName
, CASE
WHEN SUM(f.Fuel) > 0 THEN CAST(SUM(f.Fuel) AS NVARCHAR(25))
ELSE 'No fuel used'
END sum_fuel
FROM #emp e
LEFT JOIN #fuel f ON e.EmployeeID = f.id
GROUP BY ID,FirstName
ORDER BY ID DESC

Get distinct rows based on priority?

I have a table as below.i am using oracle 10g.
TableA
------
id status
---------------
1 R
1 S
1 W
2 R
i need to get distinct ids along with their status. if i query for distinct ids and their status i get all 4 rows.
but i should get only 2. one per id.
here id 1 has 3 distinct statuses. here i should get only one row based on priority.
first priority is to 'S' , second priority to 'W' and third priority to 'R'.
in my case i should get two records as below.
id status
--------------
1 S
2 R
How can i do that? Please help me.
Thanks!
select
id,
max(status) keep (dense_rank first order by instr('SWR', status)) as status
from TableA
group by id
order by 1
fiddle
select id , status from (
select TableA.*, ROW_NUMBER()
OVER (PARTITION BY TableA.id ORDER BY DECODE(
TableA.status,
'S',1,
'W',2,
'R',3,
4)) AS row_no
FROM TableA)
where row_no = 1
This is first thing i would do, but there may be a better way.
Select id, case when status=1 then 'S'
when status=2 then 'W'
when status=3 then 'R' end as status
from(
select id, max(case when status='S' then 3
when status='W' then 2
when status='R' then 1
end) status
from tableA
group by id
);
To get it done you can write a similar query:
-- sample of data from your question
SQL> with t1(id , status) as (
2 select 1, 'R' from dual union all
3 select 1, 'S' from dual union all
4 select 1, 'W' from dual union all
5 select 2, 'R' from dual
6 )
7 select id -- actual query
8 , status
9 from ( select id
10 , status
11 , row_number() over(partition by id
12 order by case
13 when upper(status) = 'S'
14 then 1
15 when upper(status) = 'W'
16 then 2
17 when upper(status) = 'R'
18 then 3
19 end
20 ) as rn
21 from t1
22 ) q
23 where q.rn = 1
24 ;
ID STATUS
---------- ------
1 S
2 R
select id,status from
(select id,status,decode(status,'S',1,'W',2,'R',3) st from table) where (id,st) in
(select id,min(st) from (select id,status,decode(status,'S',1,'W',2,'R',3) st from table))
Something like this???
SQL> with xx as(
2 select 1 id, 'R' status from dual UNION ALL
3 select 1, 'S' from dual UNION ALL
4 select 1, 'W' from dual UNION ALL
5 select 2, 'R' from dual
6 )
7 select
8 id,
9 DECODE(
10 MIN(
11 DECODE(status,'S',1,'W',2,'R',3)
12 ),
13 1,'S',2,'W',3,'R') "status"
14 from xx
15 group by id;
ID s
---------- -
1 S
2 R
Here, logic is quite simple.
Do a DECODE for setting the 'Priority', then find the MIN (i.e. one with Higher Priority) value and again DECODE it back to get its 'Status'
Using MOD() example with added values:
SELECT id, val, distinct_val
FROM
(
SELECT id, val
, ROW_NUMBER() OVER (ORDER BY id) row_seq
, MOD(ROW_NUMBER() OVER (ORDER BY id), 2) even_row
, (CASE WHEN id = MOD(ROW_NUMBER() OVER (ORDER BY id), 2) THEN NULL ELSE val END) distinct_val
FROM
(
SELECT 1 id, 'R' val FROM dual
UNION
SELECT 1 id, 'S' val FROM dual
UNION
SELECT 1 id, 'W' val FROM dual
UNION
SELECT 2 id, 'R' val FROM dual
UNION -- comment below for orig data
SELECT 3 id, 'K' val FROM dual
UNION
SELECT 4 id, 'G' val FROM dual
UNION
SELECT 1 id, 'W' val FROM dual
))
WHERE distinct_val IS NOT NULL
/
ID VAL DISTINCT_VAL
--------------------------
1 S S
2 R R
3 K K
4 G G