Print message when no data is found - sql

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

Related

Get parent id from level with Oracle SQL

I have a hierarchical structure defined by level and order of elements. Is it possible to create "parent_id" column with Oracle SQL without using procedures?
I need to generate red values:
test data:
with t as
(
select 1 id, 'element1' name, 1 level_ from dual union all
select 2 id, 'element2' name, 2 level_ from dual union all
select 3 id, 'element3' name, 3 level_ from dual union all
select 4 id, 'element4' name, 3 level_ from dual union all
select 5 id, 'element5' name, 3 level_ from dual union all
select 6 id, 'element6' name, 3 level_ from dual union all
select 7 id, 'element7' name, 2 level_ from dual union all
select 8 id, 'element8' name, 3 level_ from dual union all
select 9 id, 'element9' name, 4 level_ from dual union all
select 10 id, 'element10' name, 4 level_ from dual union all
select 11 id, 'element11' name, 1 level_ from dual union all
select 12 id, 'element12' name, 2 level_ from dual union all
select 13 id, 'element13' name, 3 level_ from dual union all
select 14 id, 'element14' name, 4 level_ from dual union all
select 15 id, 'element15' name, 4 level_ from dual union all
select 16 id, 'element16' name, 3 level_ from dual union all
select 17 id, 'element17' name, 4 level_ from dual union all
select 18 id, 'element18' name, 4 level_ from dual union all
select 19 id, 'element19' name, 1 level_ from dual
)
select * from t
From Oracle 12, you can use MATCH_RECOGNIZE:
select *
from t
MATCH_RECOGNIZE (
ORDER BY id DESC
MEASURES
child.id AS id,
child.name AS name,
child.lvl AS lvl,
parent.id AS parent_id
ONE ROW PER MATCH
AFTER MATCH SKIP TO NEXT ROW
PATTERN (child ancestors*? (parent | $))
DEFINE
parent AS lvl = child.lvl - 1
)
ORDER BY id
Or, again from Oracle 12, a LATERAL join:
select *
from t c
LEFT OUTER JOIN LATERAL(
SELECT p.id AS parent_id
FROM t p
WHERE c.id > p.id
AND c.lvl = p.lvl + 1
ORDER BY id DESC
FETCH FIRST ROW ONLY
)
ON (1 = 1)
ORDER BY id
Or, in earlier versions:
SELECT id, name, lvl, parent_id
FROM (
SELECT c.*,
p.id AS parent_id,
ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY p.id DESC) AS rn
FROM t c
LEFT OUTER JOIN t p
ON (c.id > p.id AND c.lvl = p.lvl + 1)
)
WHERE rn = 1
ORDER BY id
Which, for the sample data:
CREATE TABLE t (id, name, lvl ) as
select 1, 'element1', 1 from dual union all
select 2, 'element2', 2 from dual union all
select 3, 'element3', 3 from dual union all
select 4, 'element4', 3 from dual union all
select 5, 'element5', 3 from dual union all
select 6, 'element6', 3 from dual union all
select 7, 'element7', 2 from dual union all
select 8, 'element8', 3 from dual union all
select 9, 'element9', 4 from dual union all
select 10, 'element10', 4 from dual union all
select 11, 'element11', 1 from dual union all
select 12, 'element12', 2 from dual union all
select 13, 'element13', 3 from dual union all
select 14, 'element14', 4 from dual union all
select 15, 'element15', 4 from dual union all
select 16, 'element16', 3 from dual union all
select 17, 'element17', 4 from dual union all
select 18, 'element18', 4 from dual union all
select 19, 'element19', 1 from dual;
All output:
ID
NAME
LVL
PARENT_ID
1
element1
1
null
2
element2
2
1
3
element3
3
2
4
element4
3
2
5
element5
3
2
6
element6
3
2
7
element7
2
1
8
element8
3
7
9
element9
4
8
10
element10
4
8
11
element11
1
null
12
element12
2
11
13
element13
3
12
14
element14
4
13
15
element15
4
13
16
element16
3
12
17
element17
4
16
18
element18
4
16
19
element19
1
null
db<>fiddle here

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

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

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>

Hierarchical Query Child Override

I have a hierarchical company structure where each company has an optional company sector. Child companies inherit their parent's sector if they don't have their own, but the child company sector overrides its parent's if the child one.
I need to produce a sum of the bill amount grouped by sector. Companies with no sector are ignored.
The ParentCompanyID column in the Company table is a foreign key to itself. There can be an unlimited number of parent/child levels.
Company Table
CompanyID ParentCompanyID
1 null
2 1
3 2
4 null
5 4
6 null
7 6
8 7
CompanySector Table
CompanyID Sector
1 Distribution
4 Distribution
5 Manufacturing
8 Manufacturing
Timesheet Table
CompanyID BillAmount
1 100
1 200
2 100
3 50
4 25
5 75
6 75
7 115
8 115
The result I expect to see here is:
Sector BillAmount
Distribution 475
Manufacturing 190
Below is how I am currently doing it. It is extremely inefficient and doesn't work for unlimited number of hierarchical levels.
WITH Company AS
( SELECT 1 CompanyID, NULL ParentCompanyID FROM dual
UNION
SELECT 2, 1 FROM dual
UNION
SELECT 3, 2 FROM dual
UNION
SELECT 4, NULL FROM dual
UNION
SELECT 5, 4 FROM dual
UNION
SELECT 6, NULL FROM dual
UNION
SELECT 7, 6 FROM dual
UNION
SELECT 8, 7 FROM dual
),
CompanySector AS
( SELECT 1 CompanyID, 'Distribution' Sector FROM dual
UNION
SELECT 4 , 'Distribution' FROM dual
UNION
SELECT 5 , 'Manufacturing' FROM dual
UNION
SELECT 8 , 'Manufacturing' FROM dual
),
Timesheets AS
( SELECT 1 CompanyID, 100 BillAmount FROM dual
UNION
SELECT 1 CompanyID, 200 BillAmount FROM dual
UNION
SELECT 2 CompanyID, 100 BillAmount FROM dual
UNION
SELECT 3 CompanyID, 50 BillAmount FROM dual
UNION
SELECT 4 CompanyID, 25 BillAmount FROM dual
UNION
SELECT 5 CompanyID, 75 BillAmount FROM dual
UNION
SELECT 6 CompanyID, 75 BillAmount FROM dual
UNION
SELECT 7 CompanyID, 115 BillAmount FROM dual
UNION
SELECT 8 CompanyID, 115 BillAmount FROM dual
),
--Dummy tables above
--My current way of doing it below
companies AS
(SELECT c.*,
cs.sector
FROM company c
LEFT OUTER JOIN CompanySector cs
ON c.companyID = cs.companyID
),
sectors AS
(SELECT levelOne.companyID,
NVL(levelOne.sector, NVL(levelTwo.sector, NVL(levelThree.sector, NULL))) sector
FROM companies levelOne
LEFT OUTER JOIN companies levelTwo
ON levelOne.parentcompanyid = levelTwo.companyID
LEFT OUTER JOIN companies levelThree
ON levelTwo.parentcompanyid = levelThree.companyID
WHERE NVL(levelOne.sector, NVL(levelTwo.sector, NVL(levelThree.sector, NULL))) IS NOT NULL
)
SELECT s.sector,
SUM(t.billamount)
FROM sectors s
INNER JOIN timesheets t
ON s.companyID = t.companyID
GROUP BY sector;
What is a cleaner and more efficient way of doing this?
I believe this will do it. Using the hierarchical query syntax, populate sector from the parent record where needed.
WITH
base_sectors AS (
SELECT * FROM company LEFT OUTER JOIN companySector USING (companyID)
)
, final_sectors AS (
SELECT companyID, NVL( sector, PRIOR sector ) AS sector
FROM base_sectors
START WITH parentCompanyID IS NULL
CONNECT BY parentCompanyID = PRIOR companyID
)
SELECT s.sector,
SUM(t.billamount)
FROM final_sectors s
INNER JOIN timesheets t
ON s.companyID = t.companyID
GROUP BY sector;

How to do select count(*) group by and select * at same time?

For example, I have table:
ID | Value
1 hi
1 yo
2 foo
2 bar
2 hehe
3 ha
6 gaga
I want my query to get ID, Value; meanwhile the returned set should be in the order of frequency count of each ID.
I tried the query below but don't know how to get the ID and Value column at the same time:
SELECT COUNT(*) FROM TABLE group by ID order by COUNT(*) desc;
The count number doesn't matter to me, I just need the data to be in such order.
Desire Result:
ID | Value
2 foo
2 bar
2 hehe
1 hi
1 yo
3 ha
6 gaga
As you can see because ID:2 appears most times(3 times), it's first on the list,
then ID:1(2 times) etc.
you can try this -
select id, value, count(*) over (partition by id) freq_count
from
(
select 2 as ID, 'foo' as value
from dual
union all
select 2, 'bar'
from dual
union all
select 2, 'hehe'
from dual
union all
select 1 , 'hi'
from dual
union all
select 1 , 'yo'
from dual
union all
select 3 , 'ha'
from dual
union all
select 6 , 'gaga'
from dual
)
order by 3 desc;
select t.id, t.value
from TABLE t
inner join
(
SELECT id, count(*) as cnt
FROM TABLE
group by ID
)
x on x.id = t.id
order by x.cnt desc
How about something like
SELECT t.ID,
t.Value,
c.Cnt
FROM TABLE t INNER JOIN
(
SELECT ID,
COUNT(*) Cnt
FROM TABLE
GROUP BY ID
) c ON t.ID = c.ID
ORDER BY c.Cnt DESC
SQL Fiddle DEMO
I see the question is already answered, but since the most obvious and most simple solution is missing, I'm posting it anyway. It doesn't use self joins nor subqueries:
SQL> create table t (id,value)
2 as
3 select 1, 'hi' from dual union all
4 select 1, 'yo' from dual union all
5 select 2, 'foo' from dual union all
6 select 2, 'bar' from dual union all
7 select 2, 'hehe' from dual union all
8 select 3, 'ha' from dual union all
9 select 6, 'gaga' from dual
10 /
Table created.
SQL> select id
2 , value
3 from t
4 order by count(*) over (partition by id) desc
5 /
ID VALU
---------- ----
2 bar
2 hehe
2 foo
1 yo
1 hi
6 gaga
3 ha
7 rows selected.