I have 2 tables (People, Department).
Department table look like this:
ID (int)
Name (varchar)
ParentID (int)
People table like this:
Id (int)
FirstName (varchar)
SureName (varchar)
DepartmentId (int)
Manager (Bool)
Asistant (Bool)
I need to create query, which select all direct subordinates of concrete person which is manager of concrete depertment. And one query which select all not only direct subordinates of manager.
Each department have one manager and one asistant. The peoples with manager == false are direct subordinates, and peoples in child depertment are subordinates of parent depratment .
I have no idea how to crete this queries in SQL / LINQ.
I will be grateful for any help!
Example:
DEPARTMENT
Id Name Parent
0 Department1 null
1 Department2 0
2 Department3 1
Example:
People
ID Name Department DepartmentId Manager Asistant
1 Martin Joshua 0 1 0
2 Ondra Joshua2 0 0 0
3 Petr Joshua3 0 0 0
4 Todd Joshua3 1 1 0
5 Alex Joshua3 1 0 0
6 Iva Joshua3 1 0 0
7 Otto Joshua3 2 1 0
8 Todd Joshua3 2 0 0
I need for exmple select all (not only direct) subordinates of manager in deparment with id 0, result wil look like:
2 Ondra Joshua2 0 0 0
3 Petr Joshua3 0 0 0
4 Todd Joshua3 1 1 0
5 Alex Joshua3 1 0 0
6 Iva Joshua3 1 0 0
7 Otto Joshua3 2 1 0
8 Todd Joshua3 2 0 0
I´m not sure direct subordinates SQL query:
SELECT * FROM dbo.PeopleView WHERE DepartmentId = 162 AND Manager = 0; -- all direct s (162)
SOLUTION:
;WITH CTE AS
(
SELECT 1 as EMPLEVEL, H1.Id, H1.ParentId, H1.Name FROM DepartmentView H1 WHERE Id = 6
UNION ALL
SELECT EMPLEVEL + 1, H2.Id, H2.ParentId, H2.Name FROM DepartmentView H2
INNER JOIN CTE ON H2.ParentId = CTE.Id
)
SELECT DISTINCT P.Id, P.LastName,P.FirstName,P.DepartmentId,P.Manager,P.Assistant FROM CTE as T JOIN PeopleView as P on T.Id = P.DepartmentId;
In adition to your first query, and like a comment from #Andrew says, a recursive query can help with this:
;WITH CTE
AS
(
SELECT ID,FirstName,SureName,DepartmentID,Manager,Assistant, 0 AS EMPLEVEL FROM PEOPLE A WHERE DepartmentId = 1 AND Manager = 0
UNION ALL
SELECT B.ID,B.FirstName,B.SureName,B.DepartmentID,B.Manager,B.Assistant,EMPLEVEL +1 FROM PEOPLE B
INNER JOIN DEPARTMENT D
ON B.DepartmentID = D.ID
INNER JOIN CTE
ON D.ParentID = CTE.DepartmentID
)
SELECT DISTINCT * FROM CTE
And for convert to LINQ, you can read this post :P:
Common Table Expression (CTE) in linq-to-sql?
Hope this help, best regards.
Related
I am trying to do the following but I cannot manage to get it right yet :(.
I have these tables:
table1 -> tb1_id, tb1_name
Sample Data:
--------------
1 group1
2 group2
3 group3
4 group4
5 group5
table2 -> tb2_id, tb2_sector, tb2_tb3_id
Sample Data:
--------------
1 alpha 1
2 beta 2
3 gamma 2
4 delta 2
5 epsilon 4
table3 -> tb3_id, tb3_mid, tb3_section
Sample Data:
--------------
1 234 alpha,beta,gama,delta
This is the output that I am looking for:
Name Count %
------ ----- -----
group1 1 25%
group2 3 75%
group3 0 0%
group4 0 0%
group5 0 0%
Basically I need a split a column value delimited by a comma (tb3_section in table3) and then find the right group for each value (table2 gives me the group id to link with table1) and then do a total count by group and get the percentage (assuming total is 100%).
This is the query I tried so far:
I searched for split value samples and found one that does the split by creating a numbers table first:
create table numbers (
`n` INT(11) SIGNED
, PRIMARY KEY(`n`)
)
INSERT INTO numbers(n) SELECT #row := #row + 1 FROM
(SELECT 0 UNION ALL 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) t,
(SELECT 0 UNION ALL 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) t2,
(SELECT 0 UNION ALL SELECT 1) t8,
(SELECT #row:=0) ti;
Afterwards, I did this:
select tb3_section, count(1) from (
select
tb3_mid,
substring_index(
substring_index(tb3_section, ',', n),
',',
-1
) as tb3_section from table3
join numbers
on char_length(tb3_section)
- char_length(replace(tb3_section, ',', ''))
>= n - 1
) tb3_section_dashboard
group by 1
This doesn't give me the group count. Just does the split of tb3_section but doesn't give me the correct count and equivalent percentage. Any ideas will be much appreciate it thanks a lot.
LATEST UPDATE
First of all, I would like to thanks #eggyal for pointing me to the right direction and #Shadow for despise knowing that I was not taking the best approach, he came up with a quick fix to my problem. I managed to change the approach and removed the comma delimited values from table3. Instead now I add multiple rows for each new value (and added a constraint to avoid duplicates).
Now table3 looks like:
Sample Data:
--------------
1 234 alpha
2 234 beta
3 234 gama
4 234 delta
5 235 alpha
Here is the query I have taken from #shadow sample:
SELECT t1.tb1_name, COUNT(t3.tb3_section) AS no_per_group,
COUNT(t3.tb3_section) / t4.no_of_groups AS percentage
FROM t1 left
JOIN t2 ON t1.tb1_id=t2.tb2_tb3_id
INNER JOIN t3 ON t2.tb2_sector=t3.tb3_section>0
JOIN (SELECT COUNT(*) AS no_of_groups
FROM t3 INNER JOIN t2 ON t2.tb2_sector=t3.tb3_section>0) t4
GROUP BY t1.tb1_name
Instead of using find_in_set now I use = to match the exact value.
Now I get something like the following but the percentage looks odd and I miss a group that doesn't have a match:
Name no_per_group percentage
----- ------------- ----------
group1 2 0.1053
group3 3 0.1579
group4 3 0.1579
group5 3 0.1579
Although still I need something like:
Name Count %
------ ----- -----
group1 1 25%
group2 3 75%
group3 0 0%
group4 0 0%
group5 0 0%
Notice that if there is no match in a group, I still need to show that group.
Because I have thousands of records which are different from each other, I need to add another condition: where tb3_mid=234 . Likes this, the results are using to tb3_mid.
The best solution would be to redesign your table structure and move the data in the delimited values list to a separate table.
The quick solution is to utilise MySQL's find_in_set() function.
To get the total count of entries in the messages table (table3):
select count(*) as no_of_groups
from t3 inner join t2 on find_in_set(t2.tb2_sector,t3.tb3_section)>0
To get the counts per group, add a join to table1 and group by group name. To calculate the percentage, add the above query as a subquery:
select t1.tb1_name, count(t3.tb3_section) as no_per_group, count(t3.tb3_section) / t4.no_of_groups as percentage
from t1 left join t2 on t1.tb1_id=t2.tb2_tb3_id
inner join t3 on find_in_set(t2.tb2_sector,t3.tb3_section)>0
join (select count(*) as no_of_groups
from t3 inner join t2 on find_in_set(t2.tb2_sector,t3.tb3_section)>0) t4 --no join condition makes a Cartesian join
group by t1.tb1_name
I have the following table structure also i have mention my expected output
please help me with query as i dont know much about sql query
Table Structure
Table 1 : Emp Details
FName Id
Pratik 1
Praveen 3
Nilesh 2
Table 1 : JoinigDocument
id DocumentName
1 Leaving
2 Exp letter
3 birth cert
Table 2 : EmployeeJoiningDocument
EmpId JoiningDocumentId
1 1
1 2
3 1
3 2
3 3
2 1
2 3
Expected Output :
FName Id JoiningDocumentId DocumentName
Pratik 1 1 Leaving
Pratik 1 2 Exp letter
Pratik 1 null birth cert
Praveen 3 1 Leaving
Praveen 3 2 Exp letter
Praveen 3 3 birth cert
Nilesh 2 1 Leaving
Nilesh 2 null Exp letter
Nilesh 2 3 birth cert
You can write a query as:
select
A.FName,
A.Id,
B.JoiningDocumentId,
c.DocumentName
from #JoinigDocument C
cross join #EmployeeDetail A
Left join #EmployeeJoiningDocument B on B.EmployeeId = A.id and
B.JoiningDocumentId = C.id
order by A.Id
First cross join JoinigDocument and EmployeeDetail table so that you get all possible combinations of Employee and Documents irrespective of the fact that employee has that Joining Document or not. Then you need to do a left join to retain all these matches and find data corresponding to valid entries in EmployeeJoiningDocument.
Demo
Im trying to create a field called Owner in my table where You select the AddedBy field where parentID is equal to the PostID so far it only prints out the first field and the second is always null. Im doing a subquery on a query. Im trying to get the parent AddedBy field
SELECT Level, Sequence, PostID, AddedBy, Title, ParentID, Path_String,
CASE WHEN ParentID IS NULL THEN
AddedBy
ELSE
(SELECT AddedBy FROM cte o WHERE o.PostID = ParentID)
END AS Owner
FROM cte order by Sequence
Im trying to get a count of all posts that related to the PostID joinded by ParentID in a join but im getting an error so when i do a group by of all the Fields i still get the error:- error is below
SELECT s.Level, s.Sequence, s.PostID, s.AddedBy,
s.Title, s.ParentID, s.Path_String,
Owner = COALESCE(o.AddedBy, s.AddedBy), COUNT(r.ParentID)
FROM cte AS s
LEFT OUTER JOIN cte AS o
ON s.ParentID = o.PostID
RIGHT join cte AS r
on s.PostID = r.ParentID
ORDER BY s.Sequence;
i get the following error:
Msg 8120, Level 16, State 1, Procedure sproc_GetPostsByThread, Line 34
Column 'cte.Level' is invalid in the select list because it is not
contained in either an aggregate function or the GROUP BY clause.
PostID, ParentID, AddedBy, Title, Path_String:- PostID is IdentityColumn Path_String is in this format 1/, 1/1/, 1/1/2 and ParentID is an integer
Level Sequence PostID AddedBy Title ParentID Path_String Owner Count
1 00000003 3 kirkdm test NULL 3/ kirkdm 1
2 0000000300000005 5 MikeDM re: test 3 3/5/ kirkdm 2
3 000000030000000500000008 8 Joelene re: test 5 3/5/8/ MikeDM 2
3 000000030000000500000009 9 kirkdm re: test 5 3/5/9/ MikeDM 1
4 00000003000000050000000900000010 10 Crushanin re: test 9 3/5/9/10/ kirkdm 1
Should be this
Level Sequence PostID AddedBy Title ParentID Path_String Owner Count column here
1 00000003 3 kirkdm test NULL 3/ kirkdm
2 0000000300000005 5 MikeDM re: test 3 3/5/ kirkdm
3 000000030000000500000008 8 Joelene re: test 5 3/5/8/ MikeDM
4 00000003000000050000000800000014 14 Christian re: test 8 3/5/8/14/ Joelene
4 00000003000000050000000800000015 15 Zeke re: test 8 3/5/8/15/ Joelene
3 000000030000000500000009 9 kirkdm re: test 5 3/5/9/ MikeDM
4 00000003000000050000000900000010 10 Crushanin re: test 9 3/5/9/10/ kirkdm
5 0000000300000005000000090000001000000011 11 Tim re: test 10 3/5/9/10/11/ Crushanin
SELECT s.Level, s.Sequence, s.PostID, s.AddedBy,
s.Title, s.ParentID, s.Path_String,
Owner = COALESCE(o.AddedBy, s.AddedBy)
FROM cte AS s
LEFT OUTER JOIN cte AS o
ON s.ParentID = o.PostID
ORDER BY s.Sequence;
I have the following NHibernate DetatchedCriteria,
return DetachedCriteria.For<MMFund>()
.CreateCriteria<MMFund>(x => x.DataUniverse)
.Add<DataUniverse>(x => x.SiteId == 100)
.SetProjection(LambdaProjection.Property<MMFund>(x => x.FundId));
which is producing the following SQL:
and
this_.ShareClassReturn_ShareClassId in
(
SELECT f.[Fund_ID] as y0_
FROM
dbo.Fund f inner join CAP.DataUniverse du
on f.[Fund_TypeID] = du.[DataUniverse_TypeId]
and f.[Fund_CountryID] = du.[DataUniverse_CountryID]
WHERE fu.[DataUniverse_SiteId] = 100
)
There are many funds in a DataUniverse.
I need to filter this so that I can select only the funds with a country ID of 'ET', so that my query looks as follows:
and
scr.ShareClassReturn_ShareClassId in
(
/* Get funds in universe */
SELECT f.[Fund_ID] as y0_
FROM dbo.Fund f inner join CAP.DataUniverse du
on f.[Fund_TypeID] = du.[DataUniverse_TypeId]
and f.[Fund_CountryID] = 'ET' // these are the guys I need
WHERE du.[DataUniverse_SiteId] = 100
)
However, I'm not sure what I need to do to the DetachedCriteria in order to make this happen. The problem I'm having is that no matter what I do, it's putting the clause in the wrong place, such as
WHERE du.[DataUniverse_SiteId] = 100 and f.Fund_CountryId = 'ET'
when I add the line .Add(Restrictions.Eq("CountryId", "ET")) as follows
return DetachedCriteria.For<MMFund>()
.Add(Restrictions.Eq("CountryId", "ET"))
.CreateCriteria<MMFund>(x => x.DataUniverse)
.Add<DataUniverse>(x => x.SiteId == 100)
.SetProjection(LambdaProjection.Property<MMFund>(x => x.FundId));
or it attempts to filter on the wrong table entirely when I specify that the Restriction should be part of the second .CreateCriteria, such as
return DetachedCriteria.For<MMFund>()
.CreateCriteria<MMFund>(x => x.DataUniverse)
.Add(Restrictions.Eq("CountryId", "ET"))
.Add<DataUniverse>(x => x.SiteId == 100)
.SetProjection(LambdaProjection.Property<MMFund>(x => x.FundId));
which produces this;
WHERE du.[DataUniverse_SiteId] = 100 and du.[DataUniverse_CountryID] = 'ET'
** note - as I'm using the Criteria API, this is actually the Restriction that I'm using:
.Add<MMFund>(f => f.CountryId == "ET")
I used the Restriction terminology because it's more explicit to what I'm trying to achieve. The Criteria API & the other way both produce the exact same results.
Why do you think Where is the wrong place for the filter? That's where filtering happens.
The generated SQL looks sound. You have two tables joined on their common fields. The Where clause is providing the appropriate filtering information. If your preferred SQL statement was in place, you'd have data joined on TypeID alone, not the CountryID.
For example, let's say your Fund table looks like this
TypeID CountryID
1 1
1 2
2 1
2 2
3 1
4 1
And your DataUniverse table is the following
TypeID CountryID
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
4 1
4 2
If you wrote SQL like you desire, you would produce a join based on TypeID and you would filter Fund.CountryID = 1, for example. What would your product look like?
F.TypeID F.CountryID D.TypeID D.CountryID
1 1 1 1
1 1 1 2
1 1 1 3
2 1 2 1
2 1 2 2
2 1 2 3
3 1 3 1
3 1 3 2
4 1 4 1
4 1 4 2
Is that your desired output? Yes, you've filtered Fund.CountryID, but your join was just on TypeID, so you've got all records from DataUniverse with that matching type for each Fund.
With the join on the two fields and the Where filtering the CountryID, the result will be the following
F.TypeID F.CountryID D.TypeID D.CountryID
1 1 1 1
2 1 2 1
3 1 3 1
4 1 4 1
The question is which set of data is the one you expect?
I have successfully simulated an Oracle CONNECT BY statement in SQL Server 2008 by following these 2 previous answers here and here and adjusting to get the results I need. But how do I do this in LINQ?
Here is an example of what I am doing using a dummy database:
CREATE TABLE Employee(
EmployeeID INT IDENTITY(1,1) PRIMARY KEY,
Department INT NOT NULL,
EmployeeName VARCHAR(40) NOT NULL,
PeckingOrder INT NOT NULL,
HigherDepartment INT NULL)
INSERT INTO Employee (Department,EmployeeName,PeckingOrder,HigherDepartment)
VALUES (1,'Bart',1,NULL),(2,'Homer',1,1),(2,'Marge',2,NULL),
(3,'Lisa',1,2),(3,'Maggie',2,2),(3,'Santas Helper',3,1)
EmployeeID Department EmployeeName PeckingOrder HigherDepartment
1 1 Bart 1 NULL
2 2 Homer 1 1
3 2 Marge 2 NULL
4 3 Lisa 1 2
5 3 Maggie 2 2
6 3 Santas Helper 3 1
and this is the SQL used to return the heirachy:
WITH n(level, PeckingOrder, Department, EmployeeName, HigherDepartment) AS
(SELECT 1, PeckingOrder, Department, EmployeeName, HigherDepartment
FROM Test.dbo.Employee
WHERE Department = 3
UNION ALL
SELECT n.level + 1, nplus1.PeckingOrder, nplus1.Department, nplus1.EmployeeName, nplus1.HigherDepartment
FROM Test.dbo.Employee as nplus1
JOIN n ON n.HigherDepartment = nplus1.Department)
SELECT MAX(level) AS level, PeckingOrder, Department, EmployeeName, HigherDepartment
FROM n
GROUP BY PeckingOrder, Department, EmployeeName, HigherDepartment
ORDER BY MAX(level) DESC, PeckingOrder ASC
level PeckingOrder Department EmployeeName HigherDepartment
3 1 1 Bart NULL
2 1 2 Homer 1
2 2 2 Marge NULL
1 1 3 Lisa 2
1 2 3 Maggie 2
1 3 3 Santas Helper 1
You could use ExecuteQuery:
class YourRow
{
public int level {get; set;}
public int PeckingOrder {get; set;}
...
}
using (var db = new LinqDataContext())
{
var list = db.ExecuteQuery<YourRow>(
#"
WITH n(level, PeckingOrder, Department, EmployeeName, HigherDepartment) AS
(SELECT 1, PeckingOrder, Department, EmployeeName, HigherDepartment
...
";
}
Or perhaps better, create a view that contains the query, and use LINQ to read from the view.