Find 'missing' rows across two result sets - sql-server

I've been struggling with this for days so apologies if I've over-confused things...
I have a series of tables that define:
Groups of skills
Skills
Skills within the above groups (many-to-many)
Job roles
The skills with those job roles (many-to-many)
The structure of the database looks like this:
I need to create a result set that displays the following data:
For all of the job roles, show all of the skills within those job
roles. However, also include skills within the respective groups that
were not included in each role (indicated with NULL or by any
other method).
Please see this working code to create the tables and data. SQL Fiddle
Apologies for the length, I did lots of inserts to create a realistic example.
Notice that the PowerPoint skill is not added to the HR Manager role, but other skills from that same group are added. Also notice that the Recruitment Policy skill is not added to the Software Manager role, but I do not need to see this gap because no other skills in that group exist in the role.
The results I would aim for would resemble this (excludes Super Star role for brevity):
RoleTitle GroupTitle SkillTitle SkillIsInRole
----------------------- -------------------------- --------------------------------------
Software Manager Microsoft Office Excel 1
Software Manager Microsoft Office Word 1
Software Manager Microsoft Office PowerPoint 1
Software Manager Microsoft SQL Server Query Design 1
Software Manager Microsoft SQL Server Stored Procedures 1
Software Manager Microsoft SQL Server Failover Clustering 1
HR Manager Microsoft Office Excel 1
HR Manager Microsoft Office Word 1
HR Manager Microsoft Office PowerPoint NULL <-- not added to role but exists in same group as other used skills
HR Manager HR Recruitment Policy 1

Getting all skills for a group related to a role is somewhat simple and is handled in the relatively self explanatory roles cte below. From this the only way I can think of obtaining whether the skill was related 'directly' to the role is by OUTER APPLYING the result set to the result set of actual skills for role.
;WITH skills AS
(
SELECT g.GroupId, g.GroupTitle, s.SkillId, s.SkillTitle
FROM #tbl_GroupsSkills gs
INNER JOIN #tbl_Groups g ON g.GroupId = gs.GroupId
INNER JOIN #tbl_Skills s ON s.SkillId = gs.SkillId
)
, roles AS
(
SELECT DISTINCT jr.Id RoleId, jr.RoleTitle, gs.GroupId
FROM #tbl_jobroles jr
INNER JOIN #tbl_rolesskills rs ON rs.RoleId = jr.ID
INNER JOIN #tbl_GroupsSkills gs ON gs.LinkId = rs.LinkId
)
SELECT
roles.RoleTitle,
skills.GroupTitle,
skills.SkillTitle,
t.SkillIsInRole
FROM skills
JOIN roles ON roles.GroupId = skills.GroupId
OUTER APPLY
(
SELECT 1 SkillIsInRole
FROM #tbl_rolesskills rs
INNER JOIN #tbl_jobroles r ON rs.RoleID = r.ID
INNER JOIN #tbl_groupsskills gs ON gs.LinkID = rs.LinkID
INNER JOIN #tbl_groups g ON g.groupID = gs.GroupID
INNER JOIN #tbl_skills s ON s.skillID = gs.SkillID
WHERE s.SkillId = skills.SkillId
AND g.GroupId = skills.GroupId
AND r.Id = roles.RoleId
) t
ORDER BY roles.RoleTitle, skills.GroupTitle, skills.SkillTitle
Edit: the OUTER APPLY could be handled with a LEFT JOIN
LEFT JOIN (
SELECT s.SkillId, g.GroupId, r.Id RoleId, 1 SkillIsInRole
FROM #tbl_rolesskills rs
INNER JOIN #tbl_jobroles r ON rs.RoleID = r.ID
INNER JOIN #tbl_groupsskills gs ON gs.LinkID = rs.LinkID
INNER JOIN #tbl_groups g ON g.groupID = gs.GroupID
INNER JOIN #tbl_skills s ON s.skillID = gs.SkillID
) t ON t.SkillId = skills.SkillId
AND t.GroupId = skills.GroupId
AND t.RoleId = roles.RoleId
demo

I think you have to prepare a set of all possible combinations Roles from one side and Group-skills from the other. This is done by making Decart multiplication (usually done by CROSS JOIN). As a result you will have a list of each role combined with a list of all possible Group-skills combinations. After that you can LEFT JOIN this result with table tbl_RolesSkills. This will give what you need. You can do this by using CTE or sub query.
Have to looks like this example
Actually CROSS JOIN is not needed, I missed part with "specific roles have only a specific group sets". Only one sub-query, can be done also with CTE (Common table expression).
I also exclude "Super Star" role. If you want to add it just remove WHERE section.

Related

Joining multiple tables yields duplicates

In order to retrieve all Projects for a UserId, or all in case the user is admin, I want to join multiple tables. I'm using the statment in a TableAdapter query for MSSQL.
SELECT P.ID, P.CountryID, P.ProjectYear, P.Name, P.Objective, P.StartDate, P.EndDate, P.BaseCampaign, P.ManagerID, P.IsClosed, P.OrganisationUnitID, P.QualitativeZiele, P.QuantitativeZiele,
P.Herausforderungen, P.Learnings, P.ObjectiveQuantitativ, P.Remarks, P.ProjectOverallID, C.Name AS CountryName, O.Name AS OEName, R.RoleName
FROM wc_Projects AS P
INNER JOIN wc_OrganisationUnit AS O ON P.OrganisationUnitID = O.ID
INNER JOIN wc_Countries AS C ON P.CountryID = C.ID
INNER JOIN aspnet_Roles AS R ON C.ID = R.CountryID
INNER JOIN aspnet_UsersInRoles AS UR ON R.RoleId = UR.RoleId
WHERE (#ViewAll = 1) OR (UR.UserId = #UserId)
ORDER BY P.CountryID, P.OrganisationUnitID, P.ProjectYear DESC
In order to apply to the rather static approach for the table adapter, I start with the project.
Get all projects, resolve CountryName and OEName via FK's. Now look if you can find the role that is assoicated to the country. Then find the user that is attached to the role.
I know that this is a terrible query, but it's the only one somewhat applicable to the WebForms TableAdapter way to deal with it.
When I have a UserId that has one or multiple roles associated with countries it works. When a admin user, that has no roles with countries associated but ViewAll = 1 it breaks. I get constraint exceptions and the amount of results nearly tripple.
I tried rewriting the query, adding paranthesis and different joins. But none of it worked. How can I solve this?

How to find Reports that are emailed?

We have a lot of daily scheduled SSRS that send out e-mail to a variety of people. One of the destination email recipients has left the company so I need to find all the place that have her e-mail defined.
Can I write a query in SQl server Studio to search all the scheduled reports for that persons e-mail address?
You can query the report server using the following query:
-- List all SSRS subscriptions
USE [ReportServer]; -- You may change the database name.
GO
SELECT USR.UserName AS SubscriptionOwner
,SUB.ModifiedDate
,SUB.[Description]
,SUB.EventType
,SUB.DeliveryExtension
,SUB.LastStatus
,SUB.LastRunTime
,SCH.NextRunTime
,SCH.Name AS ScheduleName
,CAT.[Path] AS ReportPath
,CAT.[Description] AS ReportDescription
FROM dbo.Subscriptions AS SUB
INNER JOIN dbo.Users AS USR
ON SUB.OwnerID = USR.UserID
INNER JOIN dbo.[Catalog] AS CAT
ON SUB.Report_OID = CAT.ItemID
INNER JOIN dbo.ReportSchedule AS RS
ON SUB.Report_OID = RS.ReportID
AND SUB.SubscriptionID = RS.SubscriptionID
INNER JOIN dbo.Schedule AS SCH
ON RS.ScheduleID = SCH.ScheduleID
ORDER BY USR.UserName
,CAT.[Path];
Credit and more info: https://gallery.technet.microsoft.com/scriptcenter/List-all-SSRS-subscriptions-968ae4d5
Look at the Subscriptions table in the SSRS database, the ExtensionSettings or Description fields could be used.

SQL for Retrieving all users and default team from CRM 2013

I need to be able to retrieve all users and the default team of the Business Unit to which they belong.
How do I identify the default team of a Business Unit apart from the same name being used for both the team and BU. Is there a flag which identifies it as a default team?
My required output is the following columns
Domain Name
Team Name + GUID of Team Name (of the default team of the
BU to which they belong).
Here is my query.
SELECT dbo_SystemUser.DomainName, (dbo_Team.Name + ' ' + dbo_Team.TeamId)
FROM (dbo_Team INNER JOIN (dbo_TeamMembership INNER JOIN dbo_SystemUser ON dbo_TeamMembership.SystemUserId = dbo_SystemUser.SystemUserId) ON dbo_Team.TeamId = dbo_TeamMembership.TeamId) INNER JOIN dbo_BusinessUnit ON dbo_Team.BusinessUnitId = dbo_BusinessUnit.BusinessUnitId
WHERE dbo_Team.Name = dbo_BusinessUnit.Name
Team table has IsDefault column which is TRUE for a BU's default team.
It's also included in the FilteredTeam view.
Query that solves the OP issue:
-- Pay attention to the FROMs, tables ignore Security Roles.
-- To respect security, query the filtered views
-- (FilteredSystemUser, FilteredTeam, FilteredTeamMembership)
-- On the other hand, querying tables is much faster
SELECT U.DomainName as [Domain Name], T.Name as [Team Name], T.TeamId as [Team ID]
FROM SystemUser U
INNER JOIN TeamMembership TM ON U.SystemUserId = TM.SystemUserId
INNER JOIN Team T ON TM.TeamId = T.TeamId
WHERE T.IsDefault = 1
This should do it.
SELECT FilteredSystemUser.domainname, FilteredTeam.name, FilteredTeam.teamid
FROM FilteredSystemUser
INNER JOIN FilteredBusinessUnit ON FilteredSystemUser.businessunitid = FilteredBusinessUnit.businessunitid
INNER JOIN FilteredTeam ON FilteredTeam.businessunitid = FilteredBusinessUnit.businessunitid
WHERE FilteredTeam.isdefault = 1
You also need to be using the Filtered Views.

Prevent duplicates sql server

Ok, in this query I'm extracting information from 5 tables, the table Company, Programmer, Tester, Manager and the table Contract. I will extract the Programmers', testers', and Managers' Names and Telephone Numbers, as well as the Company they work on, and this company is responsible for managing this program as a request by x person doesn't matter.
Problem is with the code below, a certain information will come out as many times as there is other information, like a programmer's Name and Tel Number will come out as many times as there are Managers and Testers on the company.
I tried with left outer join and it would give me even more results, so how can I fix this so next time a result won't be duplicated but say NULL?
SELECT DISTINCT pg.name,
pg.Tel_Nr,
Mgr.name,
Mgr.Tel_Nr,
Ts.Name,
Ts.Tel_Nr,
Pg.Name,
con.program_name
FROM Company AS Cm
INNER JOIN Programmer AS Pg ON Pg.company = Cm.name
INNER JOIN Manager AS Mg ON Mg.company = Cm.name
INNER JOIN Tester AS Ts ON Ts.company = Cm.name
INNER JOIN Contract AS Con ON Con.program_name = 'My Program'
AND Cm.name = Con.Company
Surely it would make more sense to produce a list of contact details with perhaps a job description. Something like this:
WITH Cte as (select Cm.name from
Contract as Con join Company as Cm on Cm.name = Con.Company
where Con.program_name = 'My Program')
SELECT pg.name, pg.Tel_Nr, 'Programmer' as JobTitle
FROM Cte INNER JOIN
Programmer as Pg on Pg.company = Cte.name
UNION ALL
SELECT Mgr.name, Mgr.Tel_Nr,'Manager' as JobTitle
FROM Cte INNER JOIN
Manager as Mg on Mg.company= Cte.name
UNION ALL
SELECT Ts.Name, Ts.Tel_Nr, 'Tester' as JobTitle
FROM Cte INNER JOIN
Tester as Ts on Ts.company = Cte.name
This solution deploys a Common Table Expression (labelled Cte) to avoid querying thr Company and Contract tables multiple times. Find out more.

SQL Server: how to combine Join and Where clause for this case?

I have a table engines (eball), a table with damages (dv) and a table with installed service product (sp). Each engine may have multiple damages and multiple service products.
Now I want to create a query with all damages, its engine information and if a service product "Retrofit" was installed, the information of the service product Retrofit.
My SQL below is wrong, as I do not get the damages with a different service product installed. If I do not include this where clause I will get a row (equals damage) for each service product installed of the same damage???
SELECT *
FROM
dbo.EPI_EB2S_damage_report_v as dv
LEFT OUTER JOIN dbo.EPI_all_v as eball ON dv.DB_NO = eball.DB_NO
LEFT OUTER JOIN dbo.ServiceProducts sp ON dv.DB_NO = sp.DB_NO
WHERE (sp.SERVICE_PRODUCT_NAME = 'Retrofit Pulse' OR sp.SERVICE_PRODUCT_NAME is NULL OR sp.SERVICE_PRODUCT_NAME = '')
It looks so me like you need something along the lines of:
SELECT * FROM
dbo.EPI_EB2S_damage_report_v as dv
LEFT OUTER JOIN dbo.EPI_all_v as eball ON dv.DB_NO = eball.DB_NO
LEFT OUTER JOIN dbo.ServiceProducts sp ON dv.DB_NO = sp.DB_NO AND sp.SERVICE_PRODUCT_NAME = 'Retrofit Pulse'

Resources