Getting List of Employees under Head and sub heads - sql

I want to get a list of employees who come under the given Head/Employee.
It should return all sub-heads and their sub-head and employees under him/her.
Following is the list of all Employees with their designations.
EmpID
Name
Designation
HeadEmpID
1
CEO
1
0
2
CountryHead-USA
2
1
3
CountryHead-UK
2
1
4
StateHead-USA-A
3
2
5
StateHead-USA-B
3
2
6
StateHead-UK-C
3
3
7
ZoneHead-A
9
6
8
ZoneHead-B
9
6
9
ZoneHead-C
9
4
10
CityHead-A
12
7
Lets say we ask Who comes under 'CountryHead-UK'?
It should return
EmpID
Name
Designation
Head
3
CountryHead-UK
2
1
6
StateHead-UK-C
3
3
7
ZoneHead-A
9
6
8
ZoneHead-B
9
6
10
CityHead-A
12
7
If we ask who should come under CEO, then it should return everyone (for this sample data)
DROP TABLE IF EXISTS #A
CREATE TABLE #A (EmpID int,Name VARCHAR(MAX), Designation INT, HeadEmpID INT)
INSERT INTO #A VALUES (1,'CEO',1,0)
INSERT INTO #A VALUES (2,'CountryHead-USA',2,1)
INSERT INTO #A VALUES (3,'CountryHead-UK',2,1)
INSERT INTO #A VALUES (4,'StateHead-USA-A',3,2)
INSERT INTO #A VALUES (5,'StateHead-USA-B',3,2)
INSERT INTO #A VALUES (6,'StateHead-UK-C',3,3)
INSERT INTO #A VALUES (7,'ZoneHead-A',9,6)
INSERT INTO #A VALUES (8,'ZoneHead-B',9,6)
INSERT INTO #A VALUES (9,'ZoneHead-C',9,4)
INSERT INTO #A VALUES (10,'CityHead-A',12,7)
SELECT * FROM #A a
Fiddle: https://dbfiddle.uk/YZH65Xvi

Thanks Larnu for a hint.
Follwing worked.
with cte as (
select e.Empid, e.Name, e.Head, b.Name N1, b.Head new_boss
from #a e
left join #a b on b.Empid = e.Head
union all
select c.Empid, c.Name, c.new_boss, e.name, e.head
from cte c
join #a e on e.Empid = c.new_boss
)
select Empid, Name, Head, N1
from cte WHERE n1 = 'CountryHead-UK'
order by Empid, head

Related

postgresql count rows with special columns

my table looks like this:
table1:
ident
A
B
C
D
1
2
1
2
3
3
1
2
1
5
4
4
5
4
1
3
6
3
2
7
3
8
1
9
1
Now i need something like a analysis from that table.
It should look like:
table2:
name
just_name
A
3
B
1
C
1
D
0
the column just_name count the columns from table1 where there are no other entry in the other columns exept the ident column.
in the real table there are more than 4 columns so i better not work with a where for every other column. :)
thx
If you are ok with just putting the column names in column list then below query can get you your desired result. Though it's possible to make those part dynamic but if you know your column names and it's not changing dynamically this will be better approach. Please let me know if you wanna have hat part dynamic olso.
Schema:
create table mytable1(ident int, A int, B int, C int, D int);
insert into mytable1 values(1,null,2,1,null);
insert into mytable1 values(2,3,null,null,null);
insert into mytable1 values(3,1,2,1,5);
insert into mytable1 values(4,null,4,null,null);
insert into mytable1 values(5,4,1,null,3);
insert into mytable1 values(6,null,3,2,null);
insert into mytable1 values(7,null,null,3,null);
insert into mytable1 values(8,1,null,null,null);
insert into mytable1 values(9,1,null,null,null);
Query:
with cte as (SELECT
unnest(array['A', 'B', 'C','D']) AS Columns,
unnest(array[A, B, C,D]) AS Values,
row_number()over(order by 1)rn
FROM mytable1),
cte2 as (
select rn,max(cte.columns)col,count(*) from cte
where values is not null
group by rn
having count(*)=1)
select distinct columns as name,coalesce(just_name,0) from cte left join (select col,count(rn) just_name from cte2
group by col)t on cte.columns=t.col
Output:
name
coalesce
A
3
C
1
D
0
B
1
db<>fiddle here
I would do this as columns:
select count(*) filter (where A is not null and B is null and C is null and d is null),
count(*) filter (where A is null and B is not null and C is null and d is null),
count(*) filter (where A is null and B is null and C is not null and d is null),
count(*) filter (where A is null and B is null and C is null and d is not null)
from t;
You could also express this as:
select c.colname, count(*) filter (where c.num_vals = 1)
from t cross join lateral
(select colname, count(colval) over () as num_vals
from (values ('a', t.a), ('b', t.b), ('c', t.c), ('d', t.d)) v(colname, colval)
group by colname
) c
group by c.colname;
This returns the values in separate rows. And it is a bit easier to generalize.

Return results from a table match on exact number of rows

I have two tables A and B, that are in a many to many relationship in a third table. What A want to achieve is get the "repeating" A rows based on B. For example:
table A table B table A_B
---------- ---------- ----------
1 A 1 A
2 B 1 B
3 C 2 A
4 D 2 B
5 3 A
3 B
3 C
4 A
4 D
5 A
What I want is, when searching table A_B by lets say '1', to get only 2, although 3 has both A and B and 4 has A, same goes for 5 too, it matches A but only A so it should be ignored as well. I've tried some suggestions form similar questions with cross join but I had no luck. I am trying to achieve this with just selects and joins, without stored procedures or temporary tables. Any suggestions is welcomed, thank you.
Repeat all base table rows for EACH left join row match
I want my output to look like:
table A_B
----------
2 A
2 B
Or if possible it would be even better if it matches the A_id by which the search is being done
table A_B
----------
1 A
1 B
2 A
2 B
However, the B_id column is not as important so if it is only
table A_B
----------
2
or
table A_B
----------
1
2
is acceptable as well.
EDIT 1:
Until now this is what I've came up with, although a bit unclean but it gets the expected result
select
A_id
from
tableA_B
where
A_id in
(
select
A_id
from
tableA_B
group by
A_id
having
count (A_id) IN (
select
count (A_id)
from
tableA_B
where
A_id = 1
)
)
AND
B_id IN (
select
B_id
from
tableA_B
where
A_id = 1
)
group by
A_id
Basically process of elimination, step by step. It would be ideal if it took only one step.
EDIT 2:
I'm sorry I left out some important information, my B values can be repeated for instance
table A table B table A_B
---------- ---------- ----------
1 A 1 A
2 B 1 B
3 c 2 A
4 D 2 B
5 AB 3 A
6 3 B
3 C
4 A
4 D
5 A
6 AB
so using XML path may return incorrect values. Because in my case it will return 6 as well which is incorrect. I apologies for leaving out this information.
Other solution which use INTERSECT could be:
CREATE TABLE tableA_B (A_id INT, B_id VARCHAR(8))
GO
INSERT INTO tableA_B VALUES
(1,'A'),(1,'B'),(2,'A'),(2,'B'),(3,'A'),(3,'B'),(3,'C'),(4,'A'),(4,'D'),(5,'A')
GO
DECLARE #x INT = 1;
SELECT A_id FROM tableA_B ab1
LEFT JOIN (
SELECT B_id FROM tableA_B
WHERE A_id=#x
) ab2 ON ab1.B_id=ab2.B_id
GROUP BY ab1.A_id
HAVING COUNT(*)=(SELECT COUNT(*) FROM tableA_B WHERE A_id=#x)
INTERSECT
SELECT A_id FROM tableA_B ab1
JOIN (
SELECT B_id FROM tableA_B
WHERE A_id=#x
) ab2 ON ab1.B_id=ab2.B_id
GROUP BY ab1.A_id
HAVING COUNT(*)=(SELECT COUNT(*) FROM tableA_B WHERE A_id=#x)
DROP TABLE tableA_B
GO
Try this,
declare #A_B table(col int,col2 varchar(30))
insert into #A_B VALUES
(1 ,'A') ,(1 ,'B') ,(2 ,'A') ,(2 ,'B') ,(3 ,'A') ,(3 ,'B')
,(3 ,'C') ,(4 ,'A') ,(4 ,'D') ,(5 ,'A'),(6 ,'AB')
declare #i int=1
declare #007 char(1)='-'
;with CTE as
(
select col,col2
,(select #007+col2 from #A_B y
where col=x.col for xml path(''))ConcateCol
from #A_B x
--where col=#i
)
select col,col2
from cte c
where
exists(select * from cte c1
where col=#i and c.ConcateCol=c1.ConcateCol)
you can further maniplate to get whatever desire output
;With tableA(ID)
AS
(
Select 1 uNION ALL
Select 2 uNION ALL
Select 3 uNION ALL
Select 4
)
, tableB(VAL)
As
(
SELECT 'A' UNION ALL
SELECT 'B' UNION ALL
SELECT 'C' UNION ALL
SELECT 'D'
)
SELECT ID,VAL FROM
(
SELECT *,ROW_NUMBER()OVER(PARTITION BY ID ORDER BY ID)AS Seq FROM tableA
CROSS JOIN tableB
)Dt
WHERE ID In (SELECT Id From tableA where id in(1,2) ) AND Dt.Seq<3
OutPut
table A_B
----------
1 A
1 B
2 A
2 B

Running total with maximum limit

I have a simple question but its kinda difficult for me to solve it.
I would like to have sum up a specific column till it reached a limit and resets it self. (SQL 2012)
Lets say the limit is 50
- List item
- Value Total
- 10 10
- 20 30
- 30 60
- 40 50 (60-limit) + the current row value
- 2 2
- 3 5
- 10 15
- 25 40
- 15 55
- 5 10 (55-limit) + the current row value
Thank you very much.
This should do it if you have SQL Server 2012 or later:
DECLARE #Table TABLE (Id INT, ListItem INT);
INSERT INTO #Table VALUES (1, 10);
INSERT INTO #Table VALUES (2, 20);
INSERT INTO #Table VALUES (3, 30);
INSERT INTO #Table VALUES (4, 40);
INSERT INTO #Table VALUES (5, 2);
INSERT INTO #Table VALUES (6, 3);
INSERT INTO #Table VALUES (7, 10);
INSERT INTO #Table VALUES (8, 25);
INSERT INTO #Table VALUES (9, 15);
INSERT INTO #Table VALUES (10, 5);
WITH RunningTotal AS (
SELECT Id, ListItem, SUM(ListItem) OVER (ORDER BY Id) % 50 AS RT FROM #Table)
SELECT
rt.Id,
rt.ListItem,
CASE WHEN rt.RT < rt2.RT THEN rt.RT + 50 ELSE rt.RT END AS RunningTotal
FROM
RunningTotal rt
LEFT JOIN RunningTotal rt2 ON rt2.Id = rt.Id - 1
ORDER BY
rt.Id;
The tricky bit is allowing the numbers to overflow the 50 one time, otherwise this would be trivial.
Results are:
Id LI RunningTotal
1 10 10
2 20 30
3 30 60
4 40 50
5 2 2
6 3 5
7 10 15
8 25 40
9 15 55
10 5 10
create table running_totals
(
id int identity(1,1),
val int
)
insert into running_totals
select 1 union all
select 20 union all
select 10 union all
select 30 union all
select 50 union all
select 10 union all
select 11 union all
select 22 union all
select 40 union all
select 60 union all
select 20 union all
select 10 union all
select 15
declare cur_run_tot cursor for select id,val from running_totals order by id asc
declare #id int ,#val int,#runtot int
open cur_run_tot
create table #RunTot
(
id int,val int, runtot int
)
fetch next from cur_run_tot into #id,#val
while ##FETCH_STATUS = 0
begin
if #runtot is null or #runtot+#val > 50
set #runtot = #val
else
set #runtot = #runtot+ #val
insert into #RunTot
select #id,#val,#runtot
fetch next from cur_run_tot into #id,#val
end
select id as ID, val as Current_Value, runtot as Running_Total from #RunTot
drop table #RunTot
deallocate cur_run_tot

SQL Server: add a column and get other column names as its value

Here is a sample table
DECLARE #t TABLE(a INT,b INT,c INT);
INSERT #t VALUES(1,2,3),(9,8,7),(4,6,5);
SELECT *
, ( SELECT MAX(val)
FROM (VALUES (a)
, (b)
, (c)
) AS value(val)
) AS MaxVal
FROM #t;
Result:
A B C MAX
---------------
1 2 3 3
9 8 7 9
4 6 5 6
I would like to add a column Max_cols which will have other column names as its values. column names are respective to the values that column 'MAX' has in it ...(hope this makes sense).
The result should look like this.
A B C MAX Max_cols
--------------------------
1 2 3 3 C
9 8 7 9 A
4 6 5 6 B
You can't really do this dynamically (at least not with table variables); one solution would be a CASE expression that explicitly lists each potential column name. You haven't explained how you want to handle ties, though...
;WITH x AS (SELECT *,
(
SELECT MAX(val)
FROM (VALUES (a)
, (b)
, (c)
) AS value(val)
) AS MaxVal
FROM #t
) SELECT *, Max_cols = CASE MaxVal
WHEN a THEN 'A' WHEN b THEN 'B' WHEN c THEN 'C' END
FROM x;

Row_Number simulation in Sql server 2000

I have a sample input table as
Declare #input TABLE(Name VARCHAR(8))
INSERT INTO #input(Name) values('Aryan')
INSERT INTO #input(Name) values('Aryan')
INSERT INTO #input(Name) values('Joseph')
INSERT INTO #input(Name) values('Vicky')
INSERT INTO #input(Name) values('Jaesmin')
INSERT INTO #input(Name) values('Aryan')
INSERT INTO #input(Name) values('Jaesmin')
INSERT INTO #input(Name) values('Vicky')
INSERT INTO #input(Name) values('Padukon')
INSERT INTO #input(Name) values('Aryan')
INSERT INTO #input(Name) values('Jaesmin')
INSERT INTO #input(Name) values('Vick')
INSERT INTO #input(Name) values('Padukon')
INSERT INTO #input(Name) values('Joseph')
INSERT INTO #input(Name) values('Marya')
INSERT INTO #input(Name) values('Vicky')
Also I have a tally table as under
declare #t table(n int)
insert into #t select 1 union all select 2 union all
select 3 union all select 4 union all select 5 union all
select 6 union all select 7 union all select 8 union all
select 9 union all select 10 union all select 11 union all
select 12 union all select 13 union all select 14 union all
select 15 union all select 16 union all select 17 union all
select 18 union all select 19 union all select 20
In Sql Server 2005 if I do as
Select rn, name from (
select ROW_NUMBER()over (order by Name) as rn , * from #input) x
where rn % 2 <> 0
I get the output as
rn name
1 Aryan
3 Aryan
5 Jaesmin
7 Jaesmin
9 Joseph
11 Padukon
13 Vick
15 Vicky
Bu I am restricted to Sql server 2000. How can I get the same output?
I have tried with
SELECT name, (SELECT COUNT(*) FROM #input AS i2 WHERE i2.Name <= i1.Name) As rn
FROM #input AS i1
but the output is wrong
name rn
Aryan 4
Aryan 4
Joseph 9
Vicky 16
Jaesmin 7
Aryan 4
Jaesmin 7
Vicky 16
Padukon 12
Aryan 4
Jaesmin 7
Vick 13
Padukon 12
Joseph 9
Marya 10
Vicky 16
Declare your table variable as
Declare #input TABLE(_id int identity(1, 1), Name VARCHAR(8))
And then reqrite your query as
Select _id, name
from #input
where _id % 2 <> 0
Use this query:
SELECT t1.name, t.n
FROM
(
SELECT a.name, a.c, (SELECT COUNT(*) FROM #input AS i2 WHERE i2.Name <= a.Name) [rn]
FROM
(
SELECT i.name, count(*) c
FROM #input i
GROUP BY i.name
)a
)t1
JOIN #t t ON t.n <= t1.rn
WHERE t.n > t1.rn - t1.c
It produces desired output:
name n
-------- -----------
Aryan 1
Aryan 2
Aryan 3
Aryan 4
Jaesmin 5
Jaesmin 6
Jaesmin 7
Joseph 8
Joseph 9
Marya 10
Padukon 11
Padukon 12
Vick 13
Vicky 14
Vicky 15
Vicky 16