I have a table with teamID which is the ID of one Team, and the USERID are the members of that team. And then I have a USERID which is Coordinator.
My problem now is how to do a Query to give the name of coordinator per user.
userID | coordinator
19 | 69
4 | 69
I would use a Common Table Expression (CTE):
;WITH Coordinators AS
(SELECT TeamID, UserID
FROM dbo.Teams
WHERE IsCoordinator = 1)
SELECT t.UserID, c.UserID as CoordinatorID
FROM dbo.Teams t
INNER JOIN Coordinators c ON t.TeamID = c.TeamID
WHERE t.IsCoordinator = 0
With a join of the table to the query of the coordinators:
select
t.userid,
c.userid coordinator
from tablename t inner join (
select * from tablename
where iscoordinator = 'True'
) c on c.teamid = t.teamid
The design you present may have for one user multiple coordinators since each user may belong to multiple teams.
So you've got to first find the team ID of the user who you want to find the coordinator of. Use this team ID to find all entries in your table with UserId, TeamId and isCoordinator. And a "isCoordinator is true" condition to only get the coordinator of the team.
SELECT UserId, name
FROM teamTable
WHERE TeamID in (
SELECT TeamID
FROM usersTable
WHERE UserID = IdOfTheUserInQuestion
) AND IsCoordinator = 'true';
Related
I am trying to combine data from two tables but running into issues because of the odd way the contacts table is formatted. When I run my query it returns no results, Why cant I get both phone and email? If I try to get them separately it works perfectly.
I have a Users table:
id Name email
1 Bill abc#gmail.com
And a Contacts table:
id Name Data
1 phone 1234
1 email abc#gmail.com
My MSSQL query:
select co.Data as phone,
co2.Data as Email
from Users u
left join Contacts co on c.id=u.id
left join Contacts co2 on c2.id=u.id
where co.Name='email'
and co2.Name='phone' --if i dont add this emails and ebverything show up
You need to add more predicates to your join. I agree that the table is not a great architecture. It is an EAV style which is a pain to work with.
select c.Data as phone
, co2.Data as Email
from Users u
left join Contacts c on c.id = u.id
AND co.Name = 'email'
left join Contacts co2 on c2.id = u.id
AND and co2.Name='phone'
Your issue is not reproducible. I ran this script:
DECLARE #Users TABLE (
id tinyint
);
INSERT INTO #Users (id) VALUES (1);
DECLARE #Contacts TABLE (
id tinyint
, [Name] varchar(31)
, [Data] varchar(31)
);
INSERT INTO #Contacts (id, Name, Data) VALUES (1,'Phone','1234'),(1,'email','abc#gmail.com');
select co.Data as phone,
co2.Data as Email
from #Users u
left join #Contacts co on co.id=u.id
left join #Contacts co2 on co2.id=u.id
where co.Name='phone'
and co2.Name='email'
And got:
phone Email
1234 abc#gmail.com
So you had some typo or something else that you left of out your post causing your query not to work.
For instance, I corrected your aliases to use co and co2 throughout. There are places in your query where you used c and c2.
Also I corrected the where clause to associate co with "phone" and co2 with "email", since that's how they are associated in the SELECT list. You have them backwards in the query in your question.
You can create a view pivoting your contacts data which will make many other uses of your contacts data much simpler.
CREATE VIEW vContactsPivoted
AS
SELECT
id, email, phone
FROM
Contacts
PIVOT (MAX(data) FOR [name] IN (email, phone)) pivoted
After that you can use simple joins like any other traditional table:
SELECT
u.id,
u.[Name],
c.phone,
c.email
FROM
[Users] u
JOIN vContactsPivoted c ON u.id = p.id
I have the following three tables that I am trying to join together and create an overview of all desktops and whoever has one assigned to their UserID if any.
dbo.Users
ID Name Lastname JobTitle
118 Ryan Doe Field Engineer
119 Jessica Braun Technical Consultant
120 Daniel Sous Web developer
121 Amy Amyson Intern
.. etc
dbo.LightDesktops
ID Model MACAddress UserID
1 HP1234 AA:AA:AA:AA:AA:AA 118
2 HP1234 BB:BB:BB:BB:BB:BB 121
3 HP1234 AA:BB:BB:AA:BB:AA NULL
4 HP1234 BB:AA:BB:AA:AA:BB 124
dbo.MediumDesktops
ID Model MACAddress UserID
1 HP12PRO AA:AB:AA:BB:AA:BA 132
2 HP12PRO BB:BA:AB:BA:BB:AA 119
3 HP12PRO AA:BA:BA:AB:AA:BA 123
4 HP12PRO BB:BB:BB:AB:BA:BB 241
I managed to figure out how to do it per type of desktop, for example LightDesktops:
SELECT * FROM LightDesktops LEFT OUTER JOIN Users ON LightDesktops.UserID = Users.UserID
That will show me a nice overview of the light desktops with their information as well as whoever has one assigned to if any.
If I'd like to have an overview of the light desktops that are not used and therefore in stock I can do
SELECT * FROM LightDesktops LEFT OUTER JOIN Users ON LightDesktops.UserID = Users.UserID WHERE LightDesktops.UserID IS NULL
How can I accomplish the same results, but for both tables containing information of our desktops? I tried to use an UNION but returned me lots of duplicate values.
Using UNION to bring the light and medium desktops tables together as a single dataset shouldn't give you duplicates unless the tables contain duplicate values in rows accross all columns in your SELECT clauses and you use UNION ALL. If you know your tables have unique values use UNION ALL to give a performance boost.
I would combine the two tables in a common table expression (cte) and then join the resultant table with a LEFT OUTER JOIN to your user table which can also be filtered to find entries where there is no match WHERE [user].[UserID] IS NULL, NB, that would return orphaned rows in your desktops tables where the user has been deleted; alternatively drop the left outer join and use WHERE [desktop].[UserID] IS NULL to return only dekstops without assiged users.
You could try the following code;
WITH cte_Desktop AS
(
SELECT
[ID] as [DesktopID],
'Light Desktop' as [DekstopType],
[Model],
[MACAddress],
[UserID]
FROM [dbo].[LightDesktops]
UNION
SELECT
[ID],
'Medium Desktop',
[Model],
[MACAddress],
[UserID]
FROM [dbo].[MediumDesktops]
)
SELECT
[desktop].*
FROM cte_Desktop AS [desktop]
LEFT OUTER JOIN [dbo].[Users] AS [user]
on [user].[UserID] = [desktop].[UserID]
WHERE [user].[UserID] IS NULL
Use Full join to get report of all users present. it will give you the full report. On top of this results Query for the userid is NULL
SELECT U.ID userid,
U.NAME,
LD.USERID LD_USERID,
LD.MODEL LIGHT_MODEL,
LD.MACADDRESS LIGHT_MAC,
LM.USERID LM_USERID,
LM.MODEL MEDIUM_MODEL,
LM.MACADDRESS MEDIUM_MAC
FROM #USERS U
FULL OUTER JOIN #LIGHTD LD
ON (U.ID = LD.USERID )
FULL OUTER JOIN #LIGHTM LM
ON (LM.USERID = U.ID)
While you can achieve what you need in a single query, it may be more supportable to break the table union out into a view which allows that logic to be reused across multiple queries and also allows for easier refactoring at a later date.
Nod to destination-data for reminding me of this.
View
CREATE VIEW [dbo].[Desktops] AS
(
SELECT
[ID] as [DesktopID],
'Light Desktop' as [DekstopType],
[Model],
[MACAddress],
[UserID]
FROM [dbo].[LightDesktops]
UNION
SELECT
[ID],
'Medium Desktop',
[Model],
[MACAddress],
[UserID]
FROM [dbo].[MediumDesktops]
)
Query
SELECT
[desktop].*
FROM [dbo].[Desktops] AS [desktop]
LEFT OUTER JOIN [dbo].[Users] AS [user]
on [user].[UserID] = [desktop].[UserID]
WHERE [user].[UserID] IS NULL
Try
WITH all as (
select model , userid from LightDesktops
union
select model , userid from MediumDesktops
)
select * FROM all where UserId IS NULL
I have a TSQL query that I am trying to group data on. The table contains records of users and the access keys they hold such as site admin, moderator etc. The PK is on User and access key because a user can exist multiple times with different keys.
I am now trying to display a table of all users and in one column, all of the keys that user holds.
If bob had three separate records for his three separate access keys, result should only have One record for bob with all three of is access levels.
SELECT A.[FirstName],
A.[LastName],
A.[ntid],
A.[qid],
C.FirstName AS addedFirstName,
C.LastName AS addedLastName,
C.NTID AS addedNTID,
CONVERT(VARCHAR(100), p.TIMESTAMP, 101) AS timestamp,
(
SELECT k.accessKey,
k.keyDescription
FROM TFS_AdhocKeys AS k
WHERE p.accessKey = k.accessKey
FOR XML PATH ('key'), TYPE, ELEMENTS, ROOT ('keys')
)
FROM TFS_AdhocPermissions AS p
LEFT OUTER JOIN dbo.EmployeeTable as A
ON p.QID = A.QID
LEFT OUTER JOIN dbo.EmployeeTable AS C
ON p.addedBy = C.QID
GROUP BY a.qid
FOR XML PATH ('data'), TYPE, ELEMENTS, ROOT ('root');
END
I am trying to group the data by a.qid but its forcing me to group on every column in the select which will then not be unique so it will contain the duplicates.
Whats another approach to handle this?
Currently:
UserID | accessKey
123 | admin
123 | moderator
Desired:
UserID | accessKey
123 | admin
moderator
Recently, I was working on something and had a similar problem. Like your query, I had an inner 'for xml' with joins in the outer 'for xml'. It turned out it worked better if the joins were in the inner 'for xml'. The code is pasted below. I hope this helps.
Select
(Select Institution.Name, Institution.Id
, (Select Course.Courses_Id, Course.Expires, Course.Name
From
(Select Course.Courses_Id, Course.Expires, Courses.Name
From Institutions Course Course Join Courses On Course.Courses_Id = Courses.Id
Where Course.Institutions_Id = 31) As Course
For Xml Auto, Type, Elements) As Courses
From Institutions Institution
For Xml Auto, Elements, Root('Institutions') )
As I don't have the definitions for the other tables you have I just make a sample test data and you can follow this to answer yours.
Create statement
CREATE TABLE #test(UserId INT, AccessLevel VARCHAR(20))
Insert sample data
INSERT INTO #test VALUES(123, 'admin')
,(123, 'moderator')
,(123, 'registered')
,(124, 'moderator')
,(124, 'registered')
,(125, 'admin')
By using ROW_NUMBER() you can achieve what you need
;WITH C AS(
SELECT ROW_NUMBER() OVER(PARTITION BY UserId ORDER BY UserId) As Rn
,UserId
,AccessLevel
FROM #test
)
SELECT CASE Rn
WHEN 1 THEN UserId
ELSE NULL
END AS UserId
,AccessLevel
FROM C
Output
UserId AccessLevel
------ -----------
123 admin
NULL moderator
NULL registered
124 moderator
NULL registered
125 admin
I have two tables in a MS SQL Server database:
Table1
Emp_ID, First_Name, Last_Name
1 Joe Smith
2 Bob Jones
Table2
Emp_ID, Dept_ID, Status
1 1 Active
1 2 NotActive
1 3 NotActive
2 1 Active
What I would like to do is create a SQL select statement that displays a row for every employee and department combination along with the status even if the employee has never been in the department (table 2). For the sample data, this should bring back 6 records since there are 2 employees and 3 departments.
Any help would be appreciated!
Thank you
If you don't have a departments table, you'll need to create a subquery to get the distinct list of dept_ids to cross join on:
select emp_id, first_name, last_name, dept.dept_id, status
from empl
cross join (select distinct dept_id from empdept) dept
left join empdept on empl.emp_id = empdept.empt_id
and dept.dept_id = empdept.dept_id
SQL Fiddle Demo
SELECT *
FROM table1 T1 FULL OUTER JOIN table2 T2
ON T1.EMP_ID=T2.EMP_ID
I am running a SQL query on a table containing 3 million records comparing email addresses.
We have two email address fields, primary and secondary.
I am comparing a subset of primary emails against all other primary and secondary Emails to get a count of both duplicates and unique Emails in the data.
I believe this code works, its still running 10 mins in, and I have to do this for another 9 subsets which are alot larger than this one. Code is as follows:
SELECT COUNT(*) AS UniqueRecords
FROM AllVRContacts
WHERE LEN(EMAIL) > 1 AND ACCOUNTID = '00120000003bNmMAAU'
AND EMAIL NOT IN
(SELECT EMAIL FROM AllVRContacts WHERE ACCOUNTID != '00120000003bNmMAAU')
AND EMAIL NOT IN
(SELECT SECONDARY_EMAIL_ADDRESS__C FROM AllVRContacts WHERE ACCOUNTID != '00120000003bNmMAAU')
I want to learn something from this rather than just have someone scratch my back for me, the more explanation the better!
Thanks guys,
Create the following indexes:
AllVrContacts (AccountID) INCLUDE (Email)
AllVrContacts (Email) INCLUDE (AccountID)
AllVrContacts (SECONDARY_EMAIL_ADDRESS__C) INCLUDE (AccountID)
The index on (AccountID, Email) will be used for the WHERE filter in the main query:
WHERE ACCOUNTID = '00120000003bNmMAAU'
AND LEN(Email) > 1
The other two indexes will be used for antijoins (NOT IN) against this table.
You should also use:
SELECT COUNT(DISTINCT email) AS UniqueRecords
if you want the duplicates across the same account to be counted only once.
SELECT COUNT(*)
FROM (SELECT EMAIL AS UniqueRecords
FROM AllVRContacts a
WHERE ACCOUNTID = '00120000003bNmMAAU'
AND NOT EXISTS (SELECT EMAIL FROM AllVRContacts b
WHERE ACCOUNTID != '00120000003bNmMAAU'
AND (
a.EMAIL = b.EMAIL
OR a.EMAIL = b.SECONDARY_EMAIL_ADDRESS__C
)
)
AND LEN(EMAIL) > 1
GROUP BY EMAIL
) c
So how is this query better?
You typically want to use NOT EXISTS instead of NOT IN
IN returns true if a specified value matches any value in a subquery or a list
EXISTS returns true if a subquery contains any rows
More Info: SQL Server: JOIN vs IN vs EXISTS - the logical difference
= performs much better than !=
Reduce the scans (seeks if you have indexes on AllVRContacts) by not searching through AllVRContacts a second time for the secondary e-mail comparison
GROUP BY resolves potential duplicate e-mails within the ACCOUNTID
To further improve performance, add indexes as Quassnoi suggested and whatever is populating the table should validate e-mails to remove the need for the LEN check.
[EDIT] Added explanation to (3)
Can this be applicable?
SELECT ACCOUNTID, COUNT(*) AS UniqueRecords
FROM (
SELECT ACCOUNTID, EMAIL
FROM AllVRContacts
WHERE ACCOUNTID = '00120000003bNmMAAU' AND LEN(EMAIL) > 1
UNION
SELECT ACCOUNTID, SECONDARY_EMAIL_ADDRESS__C
FROM AllVRContacts
WHERE ACCOUNTID = '00120000003bNmMAAU' AND LEN(SECONDARY_EMAIL_ADDRESS__C) > 1
) s
I understood that basically you wanted to count distinct email addresses for each ACCOUNTID.
UNION in the inner query eliminates duplicates so the output (of the inner query) only has distinct pairs of account ids and emails, whether primary or secondary. Particularly this means that if an email address is stored as both primary and secondary, it will count only once. Same applies to same primary or same secondary address stored in different rows.
Now you only need to count the rows, which is done by the outer query.
If another 9 subsets you've mentioned mean simply other ACCOUNTIDs, then maybe you could try GROUP BY ACCOUNTID applied to the outer query and the ACCOUNTID = '...' part of both WHERE clauses got rid of to count emails for all of them with one query. That is, like this:
SELECT ACCOUNTID, COUNT(*) AS UniqueRecords
FROM (
SELECT ACCOUNTID, EMAIL
FROM AllVRContacts
WHERE LEN(EMAIL) > 1
UNION
SELECT ACCOUNTID, SECONDARY_EMAIL_ADDRESS__C
FROM AllVRContacts
WHERE LEN(SECONDARY_EMAIL_ADDRESS__C) > 1
) s
GROUP BY ACCOUNTID
Try this and let me know
SELECT ACCOUNTID,COUNT(*) AS UniqueRecords
FROM AllVRContacts
WHERE LEN(EMAIL) > 1 AND ACCOUNTID = '00120000003bNmMAAU'
Group by ACCOUNTID
Having COUNT(EMAIL) >1