Trying to get a column value of an ancestor when using HIERARCHYID - sql

I'm evaluating the HIERARCHYID data type to see if it'll meet my needs for a project. I was hoping to use it to get the manager of a given employee ID. I'm looking at tables in the AdventureWorks DB (the 2012 version), specifically the Person.Person and HumanResources.Employee tables.
My query goes something like this:
SELECT
pp.LastName + ', ' + pp.FirstName AS Name,
CAST(hre.NationalIdNumber AS BIGINT) AS Id,
hre.LoginID,
hre.OrganizationNode.GetAncestor(1) AS ManagerId,
hre.JobTitle,
hre.BirthDate,
hre.MaritalStatus,
hre.Gender,
hre.HireDate
FROM
Person.Person pp
INNER JOIN
HumanResources.Employee hre ON pp.BusinessEntityID = hre.BusinessEntityID
Now I expected that because GetAncestor() is a CLR function that I'd have been able to do something like hre.OrganizationNode.GetAncestor(1).NationalIdNumber AS ManagerId but I get told that I'm not worthy very fast.
What is it that I'm doing wrong here?
EDIT Later on
Playing around leads me to this but this is hardly optimal (or is it?)
SELECT
pp.LastName + ', ' + pp.FirstName AS Name,
CAST(hre.NationalIdNumber AS BIGINT) AS Id,
hre.LoginID,
NULL AS ManagerId,
hre.JobTitle,
hre.BirthDate,
hre.MaritalStatus,
hre.Gender,
hre.HireDate,
hre.OrganizationLevel
FROM
Person.Person pp
INNER JOIN
HumanResources.Employee hre ON pp.BusinessEntityID = hre.BusinessEntityID
WHERE
OrganizationLevel = 0
UNION
SELECT
pp.LastName + ', ' + pp.FirstName AS Name,
CAST(hre.NationalIdNumber AS BIGINT) AS Id,
hre.LoginID,
CAST(hre2.NationalIDNumber AS BIGINT) AS ManagerId,
hre.JobTitle,
hre.BirthDate,
hre.MaritalStatus,
hre.Gender,
hre.HireDate,
hre.OrganizationLevel
FROM
Person.Person pp
INNER JOIN
HumanResources.Employee hre ON pp.BusinessEntityID = hre.BusinessEntityID
INNER JOIN
HumanResources.Employee hre2 ON hre.OrganizationNode.GetAncestor(1) = hre2.OrganizationNode
ORDER BY
hre.OrganizationLevel ASC

A correlated sub-query seems to do it for me:
SELECT
pp.LastName + ', ' + pp.FirstName AS Name,
CAST(hre.NationalIdNumber AS BIGINT) AS Id,
hre.LoginID,
(
SELECT NationalIDNumber
FROM HumanResources.Employee AS m
WHERE OrganizationNode = hre.OrganizationNode.GetAncestor(1)
)AS ManagerId,
hre.JobTitle,
hre.BirthDate,
hre.MaritalStatus,
hre.Gender,
hre.HireDate
FROM
Person.Person pp
INNER JOIN
HumanResources.Employee hre ON pp.BusinessEntityID = hre.BusinessEntityID

Related

SQL: Two count functions in a single query?

How do I get these two count functions into one query?
SELECT
COUNT(MaritalStatus) as 'Married'
FROM
Person.Person PP
INNER JOIN
HumanResources.Employee HRE ON PP.BusinessEntityID = HRE.BusinessEntityID
WHERE
MaritalStatus = 'M';
SELECT
COUNT(MaritalStatus) as 'Single'
FROM
Person.Person PP
INNER JOIN
HumanResources.Employee HRE ON PP.BusinessEntityID = HRE.BusinessEntityID
WHERE
MaritalStatus = 'S';
You could try using sum case when:
SELECT SUM(CASE WHEN MaritalStatus = 'M' THEN 1 ELSE 0 END),
SUM(CASE WHEN MaritalStatus = 'S' THEN 1 ELSE 0 END)
FROM Person.Person PP INNER JOIN HumanResources.Employee HRE
ON PP.BusinessEntityID = HRE.BusinessEntityID
For this kind of request, you need to use aggregation functions.
SELECT MaritalStatus, COUNT(MaritalStatus)
FROM Person.Person PP
INNER JOIN HumanResources.Employee HRE
ON PP.BusinessEntityID = HRE.BusinessEntityID
GROUP BY MaritalStatus;
This will output something like this:
MaritalStatus | COUNT(MaritalStatus)
|
M | 50
S | 20
As two rows. You can add
ORDER BY MaritalStatus to ensure that you receive the data in the good order, because the order by which you receive the data is not deterministic.
The advantage of using this over SUM() is that if you ever add another marital status, your request would not change.
This also works and is easy enough to extend to other MaritalStatuses.
SELECT married.*, single.* from
( SELECT COUNT(MaritalStatus) as 'Married'
FROM Person.Person PP INNER JOIN HumanResources.Employee HRE
ON PP.BusinessEntityID = HRE.BusinessEntityID
WHERE MaritalStatus = 'M'
) married
, (
SELECT COUNT(MaritalStatus) as 'Single'
FROM Person.Person PP INNER JOIN HumanResources.Employee HRE
ON PP.BusinessEntityID = HRE.BusinessEntityID
WHERE MaritalStatus = 'S'
) single
Since your from and join clauses are identical, simply move the where conditions into a case statement.
SELECT COUNT(case when MaritalStatus = 'M' then 1 end) as 'Married',
COUNT(case when MaritalStatus = 'S' then 1 end) as 'Single'
FROM Person.Person PP
JOIN HumanResources.Employee HRE
ON PP.BusinessEntityID = HRE.BusinessEntityID
With MySQL this should work:
SELECT SUM(MaritalStatus = 'M') as 'Married',
SUM(MaritalStatus = 'S') as 'Single'
FROM Person.Person PP INNER JOIN HumanResources.Employee HRE
ON PP.BusinessEntityID = HRE.BusinessEntityID
WHERE MaritalStatus IN ('M', 'S');
("MaritalStatus = 'X') is a boolean that evaluates to 0 (false) or 1 (true). On other servers than MySQL, a cast may be necessary.
You can use group and have both:
SELECT COUNT(MaritalStatus) as 'Status'
FROM Person.Person PP INNER JOIN HumanResources.Employee HRE
ON PP.BusinessEntityID = HRE.BusinessEntityID
group by MaritalStatus;
or you can use decode

WHERE Clause with conditions in it

I want to create a where clause that will take into account the termination date and say if they hire date is between these values and the term date isn't on or before the hiredate - IE the employee never started - how would I go about that? So far I have this:
SELECT A.AdpID AS EmployeeID,
ISNULL(A.Lname, '') AS [Last Name],
ISNULL(A.Fname, '') AS [First Name],
ISNULL(A.PrimaryEmail, '') AS [Email],
ISNULL(M.Fname + ' ' + M.Lname, '') AS Manager,
ISNULL(CONVERT(VARCHAR(100), A.HireDate, 101), '') AS HireDate,
ISNULL(CONVERT(VARCHAR(100), A.TerminationDate, 101), '') AS TermDate,
ISNULL(DIV.DivisionName, '') AS Division,
ISNULL(FUN.FunctionName, '') AS [Function],
ISNULL(DEP.DepartmentName, '') AS Department,
ISNULL(WGP.WorkgroupName, '') AS Workgroup,
ISNULL(LOB.CcaLOBName, '') AS LOB,
ISNULL(MAES.EmpStatusDesc, '') AS [Employee Type]
FROM dbo.Associates AS A
LEFT OUTER JOIN dbo.Associates AS M -- Look up for associate information
ON A.SuperPrincipal = M.AssocId
LEFT OUTER JOIN dbo.MasterCCALob AS LOB -- Look up line of business
ON A.LobID = LOB.CcaLOBID
LEFT OUTER JOIN dbo.MasterAssocEmpStatus AS MAES -- Look up for employee type
ON A.EmpStatusID = MAES.EmpStatusID
LEFT OUTER JOIN dbo.AssociatesDepartment AS DEP WITH(NOLOCK) -- Look up for Department
ON A.AssociatesDepartmentID = DEP.AssociatesDepartmentID
LEFT OUTER JOIN dbo.AssociatesDivision AS DIV WITH(NOLOCK) -- Look up for Division
ON A.AssociatesDivisionID = DIV.AssociatesDivisionID
LEFT OUTER JOIN dbo.AssociatesWorkGroup AS WGP WITH(NOLOCK) -- Look up for WorkGroup
ON A.AssociatesWorkgroupID = WGP.AssociatesWorkgroupID
LEFT OUTER JOIN dbo.AssociatesFunction AS FUN WITH(NOLOCK) -- Look up for Function
ON A.AssociatesFunctionID = FUN.AssociatesFunctionID
WHERE ( LEN(A.TerminationDate) = 0
AND ISNULL(A.HireDate, '1900-01-01') BETWEEN '2015-10-01' AND GETDATE() )
OR ( LEN(A.TerminationDate) > 0
AND ISNULL(A.TerminationDate, '1900-01-01') > ISNULL(A.HireDate, '1900-01-01')
AND ISNULL(A.HireDate, '1900-01-01') BETWEEN '2015-10-01' AND GETDATE() )
ORDER BY A.HireDate DESC
There where clause is as bad as can be because I get the craziest results back from this

Sql: Select count(*) from (select ...)

I have a sql select command with grouping and I want to get the number of total rows. How do I achieve that?
My sql command:
select p.UserName, p.FirstName + ' ' + p.LastName as [FullName]
,count(b.billid) as [Count], sum(b.PercentRials) as [Sum] from Bills b
inner join UserProfiles p on b.PayerUserName=p.UserName
where b.Successful=1
group by p.UserName, p.FirstName + ' ' + p.LastName
I have tried these with no luck:
select count(*) from (select ...)
and
select count(select ...)
EDIT
this is the complete sql statement that I want to run:
select count(*) from ( select p.UserName, p.FirstName + ' ' + p.LastName as [FullName]
,count(b.billid) as [Count], sum(b.PercentRials) as [Sum] from Bills b
inner join UserProfiles p on b.PayerUserName=p.UserName
where b.Successful=1
group by p.UserName, p.FirstName + ' ' + p.LastName)
and I get this error on the last line:
Incorrect syntax near ')'.
SELECT COUNT(*)
FROM
(
select p.UserName, p.FirstName + ' ' + p.LastName as [FullName]
,count(b.billid) as [Count], sum(b.PercentRials) as [Sum] from Bills b
inner join UserProfiles p on b.PayerUserName=p.UserName
where b.Successful=1
group by p.UserName, p.FirstName + ' ' + p.LastName --<-- Removed the extra comma here
) A --<-- Use an Alias here
As I expected from your shown attempt you were missing an Alias
select count(*)
from (select ...) Q --<-- This sub-query in From clause needs an Alias
Edit
If you only need to know the rows returned by this query and you are executing this query anyway somwhere in your code you could simply make use of ##ROWCOUNT function. Something like....
SELECT ...... --<-- Your Query
SELECT ##ROWCOUNT --<-- This will return the number of rows returned
-- by the previous query
You are missing an alias after your subquery
select count(*) from (select ...) v
You can also try to return total count using existing SQL, without using subquery
select p.UserName,
p.FirstName + ' ' + p.LastName as [FullName],
count(b.billid) as [Count],
sum(b.PercentRials) as [Sum],
COUNT(*) over () [TotalCount] ------- total count here
from Bills b
inner join UserProfiles p on b.PayerUserName=p.UserName
where b.Successful=1
group by p.UserName, p.FirstName + ' ' + p.LastName
Try this code:
SELECT COUNT(*)
FROM (
SELECT p.UserName
,p.FirstName + ' ' + p.LastName AS [FullName]
,COUNT(b.billid) AS [Count]
,SUM(b.PercentRials) AS [Sum]
FROM Bills b
INNER JOIN UserProfiles p
ON b.PayerUserName = p.UserName
WHERE b.Successful = 1
GROUP BY p.UserName
,p.FirstName + ' ' + p.LastName
) a
based on your edit. You were missing derived tabled alias.
If you look at FROM clause syntax you will see
| derived_table [ AS ] table_alias [ ( column_alias [ ,...n ] ) ]
When a derived table, rowset or table-valued function, or operator
clause (such as PIVOT or UNPIVOT) is used, the required table_alias at
the end of the clause is the associated table name for all columns,
including grouping columns, returned.
http://technet.microsoft.com/en-us/library/ms177634.aspx
If you have a unique column name in there, you can count that. For example I'm assuming UserName is unique here.
select count(query.UserName) from (
select p.UserName, p.FirstName + ' ' + p.LastName as [FullName]
,count(b.billid) as [Count], sum(b.PercentRials) as [Sum] from Bills b
inner join UserProfiles p on b.PayerUserName=p.UserName
where b.Successful=1
group by p.UserName, p.FirstName + ' ' + p.LastName) as query
COUNT() - aggregate function
SELECT SQL_CALC_FOUND_ROWS * FROM ...;
SELECT FOUND_ROWS(); // next query !!!!

select user doesn't contain a record in other table sql query

I had three tables: sarcuser, sarcusercommittee, sarcallcourse. I need to build a query that brings all the users that don't have a committee (they don't have a record in sarcusercommittee). Here is my current query:
SELECT u.firstname + ' ' + u.lastname AS name, u.dateofbirth, u.gender, LEFT(u.note, 200) AS note, c.name AS coursename
FROM sarcuser AS u INNER JOIN
sarcusercommittee AS uc ON u.id = uc.user_id INNER JOIN
sarcallcourse AS c ON c.id = u.courseid
WHERE ((SELECT COUNT(id) AS Expr1
FROM sarcusercommittee
WHERE (user_id = u.id)) = 0)
ORDER BY name DESC
I guess the problem is in (ON condistion) but don't get it ... any help ?
NOTE : I use visual studio 2010
SELECT u.firstname + ' ' + u.lastname AS name, u.dateofbirth, u.gender, LEFT(u.note, 200) AS note, c.name AS coursename
FROM sarcuser AS u
INNER JOIN sarcallcourse AS c
ON c.id = u.courseid
WHERE u.id NOT IN (
SELECT uc.user_id
FROM sarcusercommittee AS uc
)
ORDER BY name DESC
Firstly you shouldn't inner join onto sarcusercommittee if you dont want rows from it. Seconly I would filter to the users that are not in sarcusercommittee using NOT IN.

Single Line separated records in SQL SERVER Query result

I had a query that returned multiple rows from a table. Then I converted that query to this one:
;with mycte as
(select s.FirstName + ' ' + s.LastName as Name from ClientStaff cs
left outer join Staff s on s.Id = cs.StaffId
left outer join GeneralStatus gs on gs.Id = s.StatusId
where cs.ClientId = #clientId and gs.Name = 'Active')
select #staff = (select distinct staff = REPLACE(REPLACE(REPLACE((select Name AS [data()] FROM mycte a
order by a.Name for xml path),'</row><row>',', '),'</row>',''),'<row>','') from mycte b)
It returns those rows in a single comma-separated row.
Now I don't want comma-separated values, instead I want single-line-separated values.
Can anyone tell me if it is possible or not?
Thanks in advance.
declare #staff varchar(max)
;with mycte as
(
select distinct s.FirstName + ' ' + s.LastName as Name
from ClientStaff cs
left outer join Staff s on
s.Id = cs.StaffId
left outer join GeneralStatus gs on
gs.Id = s.StatusId
where cs.ClientId = #clientId and gs.Name = 'Active'
)
select #staff = isnull(#staff + char(13), '') + Name
from mycte b
print #staff