Count children from hierarchy path - sql

I have a table like this:
id
name
path
1
John
/1
2
Mark
/2
3
Kevin
/1/3
4
Sarah
/1/3/4
5
Andy
/2/5
...
...
...
So, I can say that Sarah is Kevin's child which is John's child.
I would like to have this:
id
name
path
number of children
1
John
/1
2
2
Mark
/2
1
3
Kevin
/1/3
1
4
Sarah
/1/3/4
0
5
Andy
/2/5
0
...
...
...
...
TASK NUMBER 2:
Let's say that I have this table too
id
income
user_id
1
200
1
2
120
1
3
340
2
4
500
3
5
600
5
6
80
5
I can say that John has a Total income of 320$, but if I also want to count John's children, it is 820$ (because id =3 is John's child). So, I would also like a query where I can count all the hierarchical incomes.

You can do:
select
t.*,
(select count(*) from t c where c.path like t.path || '/%') as c_count,
i.income + (
select coalesce(sum(i.income), 0) from t c join i on i.user_id = c.id
where c.path like t.path || '/%'
) as c_income
from t
left join (
select user_id, sum(income) as income from i group by user_id
) i on i.user_id = t.id
Result:
id name path c_count c_income
--- ------ ------- -------- --------
1 John /1 2 820
2 Mark /2 1 1020
3 Kevin /1/3 1 500
4 Sarah /1/3/4 0 null
5 Andy /2/5 0 680
See example at DB Fiddle.

Related

Counting current and longest streaks of a given value

I have 2 tables Person (ID, NAME, CLAN_ID) and DailyScore (PERSON_ID, CLAN_ID, DAY_NUMBER, SCORE).
SCORE can take the values "A", "B", "C" or "-" ("-" means absent).
I need to make 2 separate queries to get, for a given CLAN_ID:
the current streak of a given score (let's say A, e.g.) for each Person and the name of the Person, ordered by streak length DESC
the longest ever streak of a given score (let's say A, e.g.) for each Person and the name of the Person, ordered by streak length DESC
An important constraint is that "-" SCORES are ignored, as they represent absences, not real Scores.
Example data:
Table Person:
_ID NAME CLAN_ID
1 John 11
2 Alice 11
3 Bob 12
4 Sara 12
Table DailyScore:
PERSON_ID CLAN_ID DAY_NUMBER SCORE
1 11 1 A
1 11 2 A
1 11 3 A
1 11 4 C
1 11 5 A
2 11 1 B
2 11 2 C
2 11 3 B
2 11 4 A
2 11 5 A
3 12 1 A
3 12 2 A
3 12 3 A
3 12 4 A
3 12 5 B
4 12 1 C
4 12 2 B
4 12 3 C
4 12 4 A
4 12 5 -
Desired result example 1 (CLAN_ID=11, SCORE=A):
Current streak:
Alice 2
John 1
Longest ever streak:
John 3
Alice 2
Desired result example 2 (CLAN_ID=12, SCORE=A):
Current streak:
Sara 1*
Bob 0
*since "-" are ignored, Sara has a current streak of 1 A score
Longest ever streak:
Bob 4
Sara 1
Edit:
In case it helps, here's this example in SQL Fiddle: http://sqlfiddle.com/#!7/2ed69/2
The first query can be:
select
id, name, max(s) as streak
from (
select
p.id,
p.name,
count(*) over(partition by p.id order by s.day_number desc) as c,
sum(case when s.score = 'A' then 1 else 0 end)
over(partition by p.id order by s.day_number desc) as s
from person p
join dailyscore s on s.person_id = p.id
) x
where c = s
group by id, name

How to join 2 tables without common key and randomly generate rows of second table

I have 2 tables.. [Users] and [Questions]
Users Table has..
UserId Name
-- ----
1 Bob
2 Ang
3 Bill
And Question Table has..
QuestId Description CategoryId
------- ---------- --------
0 Question0 1
1 Question1 1
2 Question2 1
3 Question3 1
4 Question4 1
5 Question5 1
6 Question6 1
7 Question7 1
8 Question9 1
9 Question9 1
10 Question10 2
Now, what I want is, select 5 random questions for each [User].
I've tried this query..
SELECT [User].UserId,Name, QuestId, Description from Users OUTER APPLY
(SELECT TOP 5 QuestId, Description FROM Question WHERE CategoryId=1 ORDER BY NEWID(),
Question.Id) RandomQuestions
And it's resulting like to something like this..
UserId Name QuestId Description
------ ---- ------- -----------
1 Bob 2 Question2
1 Bob 3 Question3
1 Bob 6 Question6
1 Bob 8 Question8
1 Bob 9 Question9
2 Ang 2 Question2
2 Ang 3 Question3
2 Ang 6 Question6
2 Ang 8 Question8
2 Ang 9 Question9
3 Bill 2 Question2
3 Bill 3 Question3
3 Bill 6 Question6
3 Bill 8 Question8
3 Bill 9 Question9
The problem with this, the QuestId is being generated randomly but if you notice, each Users has the same generated random questions. I want each Users to have different set of random Questions.
Use this one:
SELECT UserId,Name,QuestId, Description
FROM Users a
CROSS apply (
SELECT TOP 5 Row_number() OVER(ORDER BY (SELECT NULL)) AS RowNo, *
FROM (SELECT TOP 5 QuestId, Description
FROM Question b
WHERE a.UserId = a.UserId
ORDER BY Newid()) S
) cs
You need to random QuestionID like this
WHERE QuestId = (ABS(CHECKSUM(NEWID()) % 10)) + 1

getting count from multiple tables on the basis of gender and scored percentages

I have to use 3 tables
1. tbl_school_info
school_id school_name
1 St. Joseph
2 White Cross
2. tbl_student_info
student_id school_id student_name student_gender
1 1 maria liejal F
2 1 eerika carthy F
3 1 heron M
4 2 Glenn hui M
5 2 joseph M
6 2 christii F
7 2 hina moggy F
3. tbl_student_marks
marks_id school_id student_id scored_percentage
1 1 1 78
2 1 2 79
3 1 3 20
4 2 4 65
5 2 5 78
6 2 6 84
7 2 7 83
The result I need is the male, female and total student count in each school, male female passed student count and highest percentage scored male female students. The result will be like this ::
school_name || male_stud_cnt || female_stud_cnt || passed_male_cnt || passed_female_cnt || top_percentage_male ||top_percentage_female
St. Joseph 1 2 0 2 20 79
White Cross 2 2 2 2 78 84
The students whose score is below 35% has failed in the exam. How can I write query to get this result ? Is it possible to get such result using SQL query in MS SQL Server? I am unable to get the counts, how can I write such query ?
You can try using condtional aggregation with case when expression
with cte as
(
select school_name,a.student_id,student_gender,scored_percentage from
tbl_student_marks a inner join tbl_student_info b
on a.student_id=b.student_id
inner join tbl_school_info c on a.school_id=b.school_id
)
select school_name,
count(case when student_gender='M' then student_id end) as male_stud_cnt,
count(case when student_gender='F' then student_id end) as female_stud_cnt,
count(case when student_gender='M' and scored_percentage>35 then student_id end) as passed_male_cnt,
count(case when student_gender='F' and scored_percentage>35 then student_id end) as passed_female_cnt,
max(case when student_gender='M' then scored_percentage end) as top_percentage_male,
max(case when student_gender='F' then scored_percentage end) as top_percentage_female
from cte
group by school_name

Need to get hierarchy sorted on name at every level

We have four columns ID(int),parentID(int),Hierarchy(int),Name in a table. need to get the tree structure for this sorted on name,
for example
the table looks like
parentID ID Hierarchy Name
NULL 1 1 World
4 3 3 Bombay
1 4 2 Asia
1 6 2 Europe
1 7 2 Australia
7 8 3 Sydney
7 9 3 Melbourne
And been able to get this
parentID ID Hierarchy Name
NULL 1 1 World
1 4 2 Asia
4 3 3 Bombay
1 6 2 Europe
1 7 2 Australia
7 8 3 Sydney
7 9 3 Melbourne
want the output like
parentID ID Hierarchy Name
NULL 1 1 World
1 4 2 Asia
4 3 3 Bombay
1 7 2 Australia
7 9 3 Melbourne
7 8 3 Sydney
1 6 2 Europe
was able to sort like 2nd table but got stuck to get them sorted by name maintaining tree structure
my query
;WITH Level_CTE AS
(
SELECT
ParentID, ID, Hierarchy,Name, 0 AS Lvl,CAST(ID AS VARCHAR(255)) AS Path
FROM Level
WHERE ParentID is null
UNION ALL
SELECT
O.ParentID, O.ID, O.Hierarchy,O.Name,Lvl + 1 Lvl, CAST(Path + '.' + CAST(o.ID AS VARCHAR(255)) AS VARCHAR(255)) AS Path
FROM Level O
JOIN Level_CTE zol_CTE
ON O.ParentID = zol_CTE.ID
)
select * into #tempHierarchy from Level_CTE
select * from #tempHierarchy order by Path
--Please help here
Finally got it by just replacing id with name in path column. So stupid before!!
Hth
;WITH Level_CTE AS
(
SELECT
ParentID, ID, Hierarchy,Name, 0 AS Lvl,CAST(ID AS VARCHAR(255)) AS Path
FROM Level
WHERE ParentID is null
UNION ALL
SELECT
O.ParentID, O.ID, O.Hierarchy,O.Name,Lvl + 1 Lvl, CAST(Path + '.' + CAST(o.ID AS VARCHAR(255)) AS VARCHAR(255)) AS Path
FROM Level O
JOIN Level_CTE zol_CTE
ON O.ParentID = zol_CTE.ID
)
select * into #tempHierarchy from Level_CTE
select * from #tempHierarchy ORDER BY REPLACE(path,'.','')
The Result I am Getting is as below
ParentID ID Hierarchy Name Lvl Path
==============================================
NULL 1 1 World 0 1
1 4 2 Asia 1 1.4
4 3 3 Bombay 2 1.4.3
1 6 2 Europe 1 1.6
1 7 2 Australia 1 1.7
7 8 3 Sydney 2 1.7.8
7 9 3 Melbourne 2 1.7.9
Let me know the changes I will reframe it for you.
Thanks

SQL: Select criteria for two tables, compare 1 field, return using condition

These are the tables in the query. Want to compare the ID_Skills in the following 2 tables. And in the returning table from the select query, display ID_Skills with condition saying whether or not TrainingRequired (Yes/No)
tblEmployeeCurrentSkills
ID_EmployeeCurrentSkills ID_Employee ID_Skills
1 1 1
2 1 2
3 2 1
tblSkillsRequired
ID_SkillsRequired ID_Employee ID_Skills ID_Position
1 1 1 1
2 1 2 1
3 1 3 1
4 2 3 2
tblSkills
ID_Skills Skill
1 Reading
2 Wiring
3 Stapling
tblPosition
ID_Position Position
1 Tech1
2 Stapler
tblEmployee
ID_Employee EmployeeName
1 Hannah
2 Bob
SQL for qrySkillsGap table - determines whether training is necessary
SELECT tblEmployee.[Employee Name],
tblSkillsRequired.ID_Skills,
tblSkills.Skill,
IIf([tblEmployeeCurrentSkills].[ID_Skills]
Like [tblSkillsRequired].[ID_Skills],"No","Yes") AS TrainingRequired
FROM (tblSkills
INNER JOIN tblSkillsRequired
ON tblSkills.ID_Skills = tblSkillsRequired.ID_Skills)
INNER JOIN (tblEmployee INNER JOIN tblEmployeeCurrentSkills
ON tblEmployee.ID_Employee = tblEmployeeCurrentSkills.ID_Employee)
ON tblSkills.ID_Skills = tblEmployeeCurrentSkills.ID_Skills;
This is the current output:
EmployeeName ID_Skill TrainingRequired
Hannah 1 No
Hannah 1 No
Hannah 2 No
Bob 1 No
Bob 1 No
I want it to display this:
EmployeeName ID_Skill TrainingRequired
Hannah 1 No
Hannah 2 No
Hannah 3 Yes
Bob 1 No
Bob 3 Yes
Thanks for any help!
I was able to create the tables you provided and used a union to bring together the employee skills and required skills.
SELECT te.EmployeeName
, emp.ID_Skills
, CASE WHEN MIN(emp.TrainingRequired) = 0 THEN 'No'
ELSE 'Yes'
END AS TrainingRequired
FROM dbo.tblEmployee AS te
JOIN (SELECT tecs.ID_Employee
, tecs.ID_Skills
, 0 AS TrainingRequired
FROM dbo.tblEmployeeCurrentSkills AS tecs
UNION
SELECT tsr.ID_Employee
, tsr.ID_Skills
, 1 AS TrainingRequired
FROM dbo.tblSkillsRequired AS tsr
) emp
ON te.ID_Employee = emp.ID_Employee
GROUP BY te.ID_Employee
, te.EmployeeName
, emp.ID_Skills
ORDER BY te.ID_Employee
, emp.ID_Skills