Left join results in extra records - sql-server

This is a basic left join problem and I have read many articles explaining what is going on but somehow the resolution is not clicking in my head. My left table has unique records. My right table has several records for each record in the left.
In the articles I have been reading this is often explained as left table has customers and right table has orders. That is very similar but not exactly what I am facing.
In my situation the left table has unique records and the right has repetitive data to be migrated into db the left table is in. So I am trying to write a query that will join on the key shared by both but I only need one record from the right. The results I am getting of course have multiple records since the single left matches multiple times on the right.
I am thinking I need to add some sort of filtering such as Top(1) but still reading / learning and wanted to get feedback / direction from the brainiacs on this list.
Here is a simple schema of what I am working with:
DECLARE #Customer TABLE
(
Id int,
Name varchar(50),
email varchar(50)
)
INSERT #Customer VALUES(1, 'Frodo', 'frodo#middleearth.org')
INSERT #Customer VALUES(2, 'Bilbo', 'Bilbo#middleearth.org')
INSERT #Customer VALUES(3, 'Galadriel', 'Galadriel#middleearth.org')
INSERT #Customer VALUES(4, 'Arwen', 'Arwen#middleearth.org')
INSERT #Customer VALUES(5, 'Gandalf', 'Gandalf#middleearth.org')
DECLARE #CustomerJobs TABLE
(
Id int,
email varchar(50),
jobname varchar(50)
)
INSERT #CustomerJobs VALUES(1, 'frodo#middleearth.org', 'RingBearer')
INSERT #CustomerJobs VALUES(2, 'frodo#middleearth.org', 'RingBearer')
INSERT #CustomerJobs VALUES(3, 'frodo#middleearth.org', 'RingBearer')
INSERT #CustomerJobs VALUES(4, 'frodo#middleearth.org', 'RingBearer')
INSERT #CustomerJobs VALUES(5, 'frodo#middleearth.org', 'RingBearer')
INSERT #CustomerJobs VALUES(6, 'Bilbo#middleearth.org', 'Burglar')
INSERT #CustomerJobs VALUES(7, 'Bilbo#middleearth.org', 'Burglar')
INSERT #CustomerJobs VALUES(8, 'Bilbo#middleearth.org', 'Burglar')
INSERT #CustomerJobs VALUES(9, 'Galadriel#middleearth.org', 'MindReader')
INSERT #CustomerJobs VALUES(10, 'Arwen#middleearth.org', 'Evenstar')
INSERT #CustomerJobs VALUES(10, 'Arwen#middleearth.org', 'Evenstar')
INSERT #CustomerJobs VALUES(11, 'Gandalf#middleearth.org', 'WhiteWizard')
INSERT #CustomerJobs VALUES(12, 'Gandalf#middleearth.org', 'WhiteWizard')
SELECT
Cust.Name,
Cust.email,
CJobs.jobname
FROM
#Customer Cust
LEFT JOIN #CustomerJobs CJobs ON
Cjobs.email = Cust.email
I'm toying with row_number over partition() as maybe I should be joining to a cte with the row_number over partition instead of the table itself???
One other constraint I have is I can't delete the duplicates from the right table.
So again my apologies for the simplistic question and thank you for the help.

Instead of using a left join, use an outer apply... you can then use the top clause to limit the rows returned...
select
Cust.Name
, Cust.email
, CJobs.jobname
from #Customer Cust
outer apply (
select top 1 *
from #CustomerJobs CJobs
where Cjobs.email = Cust.email
) cjobs;

You have to come up with some artificial method of reducing the second table to one row per email. For example:
SELECT
Cust.Name,
Cust.ID,
Cust.email,
CJobs.jobname
FROM
#Customer Cust
LEFT JOIN
(select min(id) as id,email, jobname
from
#CustomerJobs
group by email, jobname) as CJobs ON
Cjobs.email = Cust.email
But that's pretty much random. Is there a way to determine which row from your CustomerJobs table is the "right" one?

SELECT DISTINCT
Cust.Name,
Cust.email,
CJobs.jobname
FROM
#Customer Cust
LEFT JOIN #CustomerJobs CJobs ON
Cjobs.email = Cust.email
The additional of the DISTINCT keyword should get you what you want.

This will work:
SELECT
Cust.Name,
Cust.ID,
Cust.email,
CJobs.jobname
FROM #Customer Cust
LEFT JOIN
(SELECT DISTINCT email, jobname
FROM #CustomerJobs) C2 ON C2.email = C.email

Related

Create a view in sql server using dynamic sql inside

I know this sounds weird, but is it possible to have a view that use dynamic SQL to build it? I know the views are compiled so most probably this is not possible. For sure I could do it using an stored procedure instead but I just want to make sure is not possible.
Here I have an example:
declare #Table1 as table (
Id int,
Name nvarchar(50),
Provider nvarchar(50)
)
insert #Table1 values (1, 'John', 'Provider1')
insert #Table1 values (2, 'Peter', 'Provider1')
insert #Table1 values (3, 'Marcus', 'Provider2')
declare #Table2 as table (
Id int,
Info nvarchar(50),
AnotherInfo nvarchar(50)
)
insert #Table2 values (1, 'Expense', '480140')
insert #Table2 values (1, 'Maintenance', '480130')
insert #Table2 values (2, 'Set Up Cost', '480150')
insert #Table2 values (2, 'Something', '480160')
--No columns from Table2
select a.Id, a.Name, a.Provider from #Table1 a left join #Table2 b on a.Id = b.Id
--With columns from Table2
select a.Id, a.Name, a.Provider, b.Info, b.AnotherInfo from #Table1 a left join #Table2 b on a.Id = b.Id
The first select looks like I have repeated data, which is normal because I did the left join, the problem is that for avoiding that I need to perform a distinct and this is what I don't want to do. My example is short but I have much more columns and table is quite big.

Get first record from duplicate records and use its identity in other tables

I have two temp tables in which there are duplicates. Two tables contains records as below.
DECLARE #TempCompany TABLE (TempCompanyCode VARCHAR(100), TempCompanyName VARCHAR(100))
INSERT INTO #TempCompany VALUES ('00516','Company1')
INSERT INTO #TempCompany VALUES ('00135','Company1')
INSERT INTO #TempCompany VALUES ('00324','Company2')
INSERT INTO #TempCompany VALUES ('00566','Company2')
SELECT * FROM #TempCompany
DECLARE #TempProduct TABLE (TempProductCode VARCHAR(100), TempProductName VARCHAR(100), TempCompanyCode VARCHAR(100))
INSERT INTO #TempProduct VALUES ('001000279','Product1','00516')
INSERT INTO #TempProduct VALUES ('001000279','Product1','00135')
INSERT INTO #TempProduct VALUES ('001000300','Product2','00135')
INSERT INTO #TempProduct VALUES ('001000278','Product3','00566')
INSERT INTO #TempProduct VALUES ('001000278','Product3','00324')
INSERT INTO #TempProduct VALUES ('001000304','Product4','00566')
SELECT * FROM #TempProduct
Company is master table and product is child table. Product contains reference of company. Now these are my temp tables and I need to insert proper values from these tables to my main table.
I want to insert records as following
DECLARE #Company TABLE(CompanyID INT IDENTITY(1,1), CompanyCode VARCHAR(100), CompanyName VARCHAR(100))
INSERT INTO #Company VALUES ('00516','Company1')
DECLARE #IDOf00516 INT = ##IDENTITY
INSERT INTO #Company VALUES ('00324','Company2')
DECLARE #IDOf00324 INT = ##IDENTITY
SELECT * FROM #Company
DECLARE #Product TABLE(ProductID INT IDENTITY(1,1), ProductCode VARCHAR(100), ProductName VARCHAR(100), CompanyID INT)
INSERT INTO #Product VALUES ('001000279','Product1',#IDOf00516)
INSERT INTO #Product VALUES ('001000300','Product2',#IDOf00516)
INSERT INTO #Product VALUES ('001000278','Product3',#IDOf00324)
INSERT INTO #Product VALUES ('001000300','Product4',#IDOf00324)
SELECT * FROM #Product
I have attached the following image for how it looks when running the queries.
Can anybody help?
I would probably first insert unique records from tempCompany to Company table, then do an insert from TempProduct with a lookup to the inserted Company tables.
I.e. first insert companies:
INSERT INTO #Company (CompanyCode, CompanyName)
SELECT temp.TempCompanyCode, temp.TempCompanyName
FROM #TempCompany AS temp
Then insert products using a join to find the companyid:
INSERT INTO #Product (ProductCode, ProductName, CompanyId)
SELECT temp.TempProductCode, temp.TempProductName, c.CompanyID
FROM #TempProduct AS temp
JOIN #Company AS c ON temp.TempCompanyCode = c.CompanyCode -- Lookup to find CompanyID
However this does not take into account duplicates and the possibility that the main tables already have the records inserted.
Adding duplicate and check for already existing records the end result could look like:
--1. insert records not already in company table:
INSERT INTO #Company (CompanyCode, CompanyName)
SELECT DISTINCT temp.TempCompanyCode, temp.TempCompanyName
FROM #TempCompany AS temp
LEFT JOIN #Company AS c ON temp.TempCompanyCode = c.CompanyCode -- Will match Companies that already exists in #Companies
WHERE c.CompanyID IS NULL -- Company does not already exist
ORDER BY temp.TempCompanyCode
--2. insert product records:
INSERT INTO #Product (ProductCode, ProductName, CompanyId)
SELECT DISTINCT temp.TempProductCode, temp.TempProductName, c.CompanyID
FROM #TempProduct AS temp
JOIN #Company AS c ON temp.TempCompanyCode = c.CompanyCode -- Lookup to find CompanyID
LEFT JOIN #Product AS p ON temp.TempProductCode = p.ProductCode AND temp.TempCompanyCode = c.CompanyCode -- Will match products that already exists in #Products
WHERE p.ProductID IS NULL -- Product does not already exists
ORDER BY c.CompanyID, temp.TempProductCode
--3. Check result
SELECT * FROM #Company
SELECT * FROM #Product
Note that i make use of LEFT JOIN and add a WHERE condition to check whether the record already exists in your main tables.
This could very well be achieved using the MERGE statement - but I'm accustomed to the slighty less readable LEFT JOIN/WHERE method :)
EDIT
Only TempCompanyName is to be used for determining whether a row in TempCompany is unique. I.e. Company1 with CopmpanyCode 00135 should not be inserted.
To achieve this, I would make use of ROW_NUMBER in helping finding the company rows to insert. I've added an identity column to #TempCompany to make sure the first row inserted will be the row used (i.e. to make sure 00516 is used for Company1 and not 00135).
New #TempCompany definition:
DECLARE #TempCompany TABLE (TempCompanyId INT IDENTITY(1,1), TempCompanyCode VARCHAR(100), TempCompanyName VARCHAR(100))
And updated script with row_number added and an extra join from Product via TempCompany (on name) to Company. The extra join is to enable both Product1 with CompanyCode 00516 and CompanyCode 00135 to be handled correctly:
--1. insert records not already in company table:
;WITH OrderedTempCompanyRows AS (SELECT ROW_NUMBER() OVER (PARTITION BY TempCompanyName ORDER BY TempCompanyId) AS RowNo, TempCompanyCode, TempCompanyName FROM #TempCompany)
INSERT INTO #Company (CompanyCode, CompanyName)
SELECT DISTINCT temp.TempCompanyCode, temp.TempCompanyName
FROM OrderedTempCompanyRows temp
LEFT JOIN #Company AS c ON temp.TempCompanyName = c.CompanyName -- Will match Companies that already exists in #Companies
WHERE temp.RowNo = 1 -- Only first company according to row_number
AND c.CompanyID IS NULL -- Company does not already exist
ORDER BY temp.TempCompanyName
--2. insert product records:
INSERT INTO #Product (ProductCode, ProductName, CompanyId)
SELECT DISTINCT temp.TempProductCode, temp.TempProductName, c.CompanyID
FROM #TempProduct AS temp
JOIN #TempCompany tc ON temp.TempCompanyCode = tc.TempCompanyCode -- Find Companyname in #Tempcompany table
JOIN #Company AS c ON tc.TempCompanyName = c.CompanyName -- Join to #Company on Companyname to find CompanyID
LEFT JOIN #Product AS p ON temp.TempProductCode = p.ProductCode AND temp.TempCompanyCode = c.CompanyCode -- Will match products that already exists in #Products
WHERE p.ProductID IS NULL -- Product does not already exists
ORDER BY c.CompanyID, temp.TempProductName
--3. Check result
SELECT * FROM #Company
SELECT * FROM #Product

How do I find records out of order - SQL?

Let's say I have a table with an ID Identity column, some data, and a datestamp. Like this:
1 data 5/1/2013 12:30
2 data 5/2/2013 15:32
3 data 5/2/2013 16:45
4 data 5/3/2013 9:32
5 data 5/5/2013 8:21
6 data 5/4/2013 9:36
7 data 5/6/2013 11:42
How do I write a query that will show me the one record that is timestamped 5/4? The table has millions of records. I've done some searching, but I don't know what to call what I'm searching for. :/
declare #t table(id int, bla char(4), timestamp datetime)
insert #t values
(1,'data','5/1/2013 12:30'),
(2,'data','5/2/2013 15:32'),
(3,'data','5/2/2013 16:45'),
(4,'data','5/3/2013 9:32'),
(5,'data','5/5/2013 8:21'),
(6,'data','5/4/2013 9:36'),
(7,'data','5/6/2013 11:42')
select timestamp
from
(
select rn1 = row_number() over (order by id),
rn2 = row_number() over (order by timestamp), timestamp
from #t
) a
where rn1 not in (rn2, rn2-1)
in 2008 r2, this would be a way
DECLARE #Table AS TABLE
(id INT , ladate DATETIME)
INSERT INTO #Table VALUES (1, '2013-05-01')
INSERT INTO #Table VALUES (2, '2013-05-02')
INSERT INTO #Table VALUES (3, '2013-05-03')
INSERT INTO #Table VALUES (4, '2013-05-05')
INSERT INTO #Table VALUES (5, '2013-05-04')
INSERT INTO #Table VALUES (6, '2013-05-06')
INSERT INTO #Table VALUES (7, '2013-05-07')
INSERT INTO #Table VALUES (8, '2013-05-08')
--I added the records in the sort order but if not just make sure you are sorted in the query
SELECT t2.ladate FROM #Table T1
INNER JOIN #Table T2 ON T1.Id = T2.Id + 1
INNER JOIN #Table t3 ON t2.id = t3.id + 1
WHERE t3.ladate < t2.ladate AND t2.ladate > t1.ladate
-- I made the assumption that your Id are all there, 1,2,3,4,5.... none missing... if there are rownumbers missing, you can use row_number()

SQL Server Another simple question

I have 2 temp Tables [Description] and [Institution], I want to have these two in one table.
They are both tables that look like this:
Table1; #T1
|Description|
blabla
blahblah
blagblag
Table2; #T2
|Institution|
Inst1
Inst2
Inst3
I want to get it like this:
Table3; #T3
|Description| |Institution|
blabla Inst1
blahblah Inst2
blagblag Inst3
They are already in sort order.
I just need to get them next to each other..
Last time I asked was something almost the same.
I used this query
Create Table #T3
(
[From] Datetime
,[To] Datetime
)
INSERT INTO #T3
SELECT #T1.[From]
, MIN(#T2.[To])
FROM #T1
JOIN #T2 ON #T1.[From] < #T2.[To]
GROUP BY #T1.[From]
Select * from #T3
It did work for the date values, but it won't work here ? :s
Thank you.
One thing that concerns me is that you say that the values "are already in sort order". There really is no default sort order -- if you don't specify a sort order, you are at the mercy of SQL Server to determine the order in which the data is returned. The solution below assumes that there is some way to sort the data such that the records "match up" (using the ORDER BY clauses).
Hope this helps,
John
-- Table 1 test data
Create Table #T1
(
[Description] nvarchar(30)
)
INSERT INTO #T1 ([Description]) VALUES ('desc1')
INSERT INTO #T1 ([Description]) VALUES ('desc2')
INSERT INTO #T1 ([Description]) VALUES ('desc3')
-- Table 2 test data
Create Table #T2
(
[Institution] nvarchar(30)
)
INSERT INTO #T2 (Institution) VALUES ('Inst1')
INSERT INTO #T2 (Institution) VALUES ('Inst2')
INSERT INTO #T2 (Institution) VALUES ('Inst3')
-- Create table 3
Create Table #T3
(
[Description] nvarchar(30),
[Institution] nvarchar(30)
);
-- Use CTE2 to add row numbers to the data; use the row numbers to join the tables
-- you must specify the sort order for the data in the tables
WITH CTE1 (Description, RowNum) AS
(
SELECT [Description], ROW_NUMBER() OVER(ORDER BY [Description]) as RowNum
FROM #T1
),
CTE2 (Institution, RowNum) AS
(
SELECT Institution, ROW_NUMBER() OVER(ORDER BY Institution) as RowNum
FROM #T2
)
INSERT INTO #T3
SELECT CTE1.Description, CTE2.Institution
FROM CTE1
LEFT JOIN CTE2 ON CTE1.RowNum = CTE2.RowNum
Select * from #T3

Create a join that can (include some) or (include all except some) records

Consider the following two tables:
User
- UserID
- UserName
Group
- GroupID
- GroupName
The obvious association is that Users will be in Groups. This by itself is a simple many-to-many join situation, so lets add a 3rd table:
UserGroup
- UserID
- GroupID
Under this textbook schema, I can easily include a specific User in a specific Group by INSERTing a new record into the UserGroup table. Before going any further I want to point out that I recognize this as the optimal situation in database design.
However, I want to also be able to make Groups that include all Users by default unless they are specifically excluded somehow. Logically, I've broken this down into two "modes" for which a Group must be in one or the other:
Include Mode: Every User is excluded unless specifically included.
Exclude Mode: Every User is included unless specifically excluded.
So what is the best way to design such a relationship? You can add columns, add tables, and have fancy join conditions and WHERE constraints. No triggers or s'procs, please.
Add field to Group table, 'Mode' - 0=Include, 1=Exclude
Then, Users in linking table for an 'Exclusive' Group would be put into a "NOT IN ()" list or "EXCEPT" clause when doing your queries; whereas 'Inclusive' Groups would be queried normally.
See http://msdn.microsoft.com/en-us/library/ms188055.aspx for EXCEPT syntax/usage.
EDIT: oh, somebody posted before me... hopefully this is still helpful!
A SELECT query for an 'Exclusive' Group would go something like this:
SELECT UserID
FROM Users
EXCEPT
SELECT UserID
FROM UserGroup
WHERE GroupID = X
To make it more dynamic, just determine the Group-mode before building the query, then replace 'EXCEPT' with 'INTERSECT' if the Group-mode is 'Inclusive'.
This one had me thinking for a little bit on how to easily write the final query to get a list of groups that a particular user is in, but here it is. Either way, setting an attribute in the Group table is the best way to determine if the group is an "Include" or "Exclude" group. This includes all of the create and insert statements used for testing it.
CREATE TABLE MUser (UserID INT, UserName VARCHAR(64))
GO
CREATE TABLE MGroup (GroupID INT, GroupName VARCHAR(64), GroupTypeID INT)
GO
CREATE TABLE MGroupType (GroupTypeID INT, GroupTypeName VARCHAR(64))
GO
CREATE TABLE MUserGroup (UserID INT, GroupID INT)
GO
INSERT INTO MGroupType VALUES(1, 'Include')
INSERT INTO MGroupType VALUES(2, 'Exclude')
INSERT INTO MGroup VALUES(1, 'Group 1a', 1)
INSERT INTO MGroup VALUES(2, 'Group 1b', 1)
INSERT INTO MGroup VALUES(3, 'Group 2a', 2)
INSERT INTO MGroup VALUES(4, 'Group 2b', 2)
INSERT INTO MUser VALUES (1, 'User1')
--included in 1a, 1b; excluded from 2a, 2b
INSERT INTO MUserGroup VALUES(1, 1)
INSERT INTO MUserGroup VALUES(1, 2)
INSERT INTO MUserGroup VALUES(1, 3)
INSERT INTO MUserGroup VALUES(1, 4)
INSERT INTO MUser VALUES (2, 'User2')
--included in 1a; implicitly included in 2b
INSERT INTO MUserGroup VALUES(2, 1)
INSERT INTO MUserGroup VALUES(2, 3)
INSERT INTO MUser VALUES (3, 'User3')
--implicitly included in 2b
INSERT INTO MUserGroup VALUES(3, 3)
--SELECT ALL GROUPS FOR A PARTICULAR USER
DECLARE #UserID INT
SET #UserID = 3
SELECT g.GroupName
FROM MGroup g WITH(NOLOCK)
LEFT JOIN (
SELECT *
FROM MUserGroup WITH(NOLOCK)
WHERE UserID = #UserID
) ug ON g.GroupID = ug.GroupID
WHERE (g.GroupTypeID = 1 AND ug.UserID IS NOT NULL)
OR (g.GroupTypeID = 2 AND ug.UserID IS NULL)
--SELECT ALL USERS FOR A PARTICULAR GRUP
DECLARE #GroupID INT
SET #GroupID = 4
SELECT u.UserName
FROM MUser u WITH(NOLOCK)
JOIN (SELECT * FROM MGroup WITH(NOLOCK) WHERE GroupID = #GroupID AND GroupTypeID = 2) g ON NOT EXISTS (SELECT * FROM MUserGroup ug WITH(NOLOCK) WHERE u.UserID = ug.UserID AND g.GroupID = ug.GroupID)
UNION
SELECT u.UserName
FROM MUser u WITH(NOLOCK)
JOIN MUserGroup ug WITH(NOLOCK) ON u.UserID = ug.UserID
JOIN MGroup g WITH(NOLOCK) ON ug.GroupID = g.GroupID
WHERE g.GroupID = #GroupID
AND g.GroupTypeID = 1
Are you trying to produce a table of all users and groups?
If not, if you just want to know which groups a user is in or which users are in a group, the above would be a very slow approach to a query to get there.
Oh, and there's the question whether this must all be done with one query, or if you can mix in some application code.
To find all the users in a group, my first thought would be to have an application first read the Group record and check the join mode. Then if the join mode is "in", run the conventional query:
select userName
from userGroup ug
join user u using (userId)
where ug.groupId=?
If the join mode is "ex", run:
select userName
from user u
where not exists (select * from userGroup ug where ug.groupId=? and ug.userId=u.userId)
If it must be done with a single query for some reason, perhaps:
select userName
from group g
left join user u on joinMode='in'
and exists (select * from userGroup ug where ug.groupid=g.groupid and ug.userid=u.userid)
or joinMode='ex'
and not exists (select * from userGroup ug where ug.groupid=g.groupid and ug.userid=u.userid)
where g.groupid=?
This works but frankly I'm not sure what performance would be like. I did an explain plan in MySQL and it decided to read the entire User table, but then I only had three records in it, maybe with more records it would have made a different plan.
Since all-inclusiveness and all-exclusiveness is an attribute on groups, the clearest way seems to be to add a column to the Group table specifying whether or not it should include all users, exclude all users, or have default behavior (neither).
This is the solution I ended up using. I added JoinMode to the Group table which has valid values of 'IN' and 'EX' for inclusive and exclusive, respectively. Everyone seemed to agree that that's the way to do it. I think this makes the join table's contents rather confusing, but hopefully it's the least bad solution.
In the query I do a CROSS JOIN to create all possible relationships and then drop the unwanted ones in the WHERE constraints based on whether the join table value exists or not.
DECLARE #User TABLE
(UserID int
,UserName varchar(16))
INSERT INTO #User VALUES (1, 'John')
INSERT INTO #User VALUES (2, 'Jim')
INSERT INTO #User VALUES (3, 'Bob')
INSERT INTO #User VALUES (4, 'George')
DECLARE #Group TABLE
(GroupID int
,GroupName varchar(16)
,JoinMode char(2))
INSERT INTO #Group VALUES (1, 'Nobody', 'IN')
INSERT INTO #Group VALUES (2, 'Only John', 'IN')
INSERT INTO #Group VALUES (3, 'Jim & Bob', 'IN')
INSERT INTO #Group VALUES (4, 'Not John', 'EX')
INSERT INTO #Group VALUES (5, 'Everybody', 'EX')
DECLARE #UserGroup TABLE
(UserID int
,GroupID int)
INSERT INTO #UserGroup VALUES (1, 2) -- Only John
INSERT INTO #UserGroup VALUES (2, 3) -- Jim & Bob
INSERT INTO #UserGroup VALUES (3, 3) -- Jim & Bob
INSERT INTO #UserGroup VALUES (1, 4) -- Not John
SELECT
g.GroupName
,u.UserName
FROM
(
#User u
CROSS JOIN
#Group g
)
LEFT OUTER JOIN
#UserGroup ug ON (
u.UserID = ug.UserID
AND g.GroupID = ug.GroupID
)
WHERE
(
g.JoinMode = 'IN'
AND ug.UserID IS NOT NULL
)
OR (
g.JoinMode = 'EX'
AND ug.UserID IS NULL
)
ORDER BY
g.GroupID
,u.UserID
Let me know if you see any problems with this approach or if you have a better way.

Resources