Sqlserver CTE how to get first non null value - sql-server

I am trying to use the following query to get a user with his detail and any of his manager or manager's manager id who got a record in UserTag table.
I have got the partial result, but not sure how to get the first non null value. I have show my expected result below.
With Managers AS
(
--initialization
SELECT Id, Email, ManagerID, NULL as UserID
FROM dbo.[User]
WHERE email='user#test.com'
UNION ALL
--recursive execution
SELECT u.Id, u.Email, u.ManagerID, tb2.UserID
FROM dbo.[User] u
INNER JOIN Managers m ON m.ManagerID = u.ID
OUTER apply (select userid from UserTag where userid = m.ManagerID and (TagID=9 or TagName = 'test')) tb2
)
SELECT * FROM Managers
Expected result:
Sample data and expected result:
User table
=========
Id Email ManagerID
--- ----- ---------
11813 userA#test.com 1251
1251 userB#test.com 302
302 userC#test.com 1221
1221 userD#test.com 358
358 userE#test.com 988
988 userE#test.com NULL
100 userA1#test.com 101
101 userB1#test.com 102
102 userC1#test.com 103
103 userD1#test.com 104
104 userF1#test.com NULL
User Tag
Id UserId TagId TagName
1 1221 9 test
2 104 9 test
Expected result
==============
UserId Email TagManagerId
------ ----- ------------
11813 userA#test.com 1221
1251 userB#test.com 1221
302 userC#test.com 1221
Brief description:
I have all users in User table. This is a self referencing table.
I want to get all users whose immediate manager or manager's manager or any manager in his/hierarchy got a record in UserTag table with tagid=9 or tagname='test' as shown here.
Schema db<>fiddle here

Based on your Brief description, I believe that this is an XY Problem
There are solutions to the real need that don't include get first non null value.
What I believe you really need to do is find all the managers that are tagid=9 or tagname='test' and then recursively find all their subordinates. (Run the recursion in the opposite direction to your example query.)
The only complexity I can think of is that such a manager may have a subordinate that also meets those criteria, so I included a condition to avoid recursing them more than once.
WITH
test9_managed_users AS
(
SELECT
u.*,
u.managerid AS closest_test9_manager_id,
0 AS depth
FROM
[user] AS u
INNER JOIN
UserTag AS t
ON t.userid = u.managerid
AND (t.TagID=9 or TagName = 'test')
UNION ALL
SELECT
u.*,
m.closest_test9_manager_id,
m.depth+1
FROM
test9_managed_users AS m
INNER JOIN
[user] AS u
ON u.managerid = m.id
WHERE
-- If a user is themselves a 'test9_manager', don't recurse their children
-- > They would already have been included in the Anchor part of the CTE
NOT EXISTS (
SELECT *
FROM UserTag AS t
WHERE t.userid = m.id
AND (t.TagID=9 or TagName = 'test')
)
)
SELECT
*
FROM
test9_managed_users
ORDER BY
email
Demo with my own test data:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=5e901a6f7db97362bb27ebacb1d4331c
Demo with your test data:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=1ab3625ba797d8fc13c49872d1cd5872

Related

REPLACE within a CASE statement

We have our team entering a dummy domain in some situations in our Email field, to signify that the entry belongs to a login ID list from another business unit (which uses a "gamertag" OR email as it's login ID). Something like this:
Retail Reps table a:
email
john123#dummydomain.com
jeff456#actualemail.com
Users Table (provided from other business unit) b
userID
john123
jeff456#actualemail.com
We want to count the number of matches. Here's the CASE statement I wrote:
CASE WHEN REPLACE(a.email, '#dummydomain.com','') = b.userid THEN 1 ELSE 0 END AS [Email LoginID match]
John results in 0 (no match) and Jeff results in 1 (match), even though both are a match.
Any guidance greatly appreciated.
This seems to work:
SELECT u.UserID, count(*) as [Email LoginID match]
FROM Users u
INNER JOIN RetailReps rr on u.UserID = Replace(rr.email,'#dummydomain.com','')
GROUP BY u.UserID
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=1569a6474024e04052ddcb4c4b14d23b
fwiw, you should probably be using a variant of example.com or dummydomain.test as your dummy domain.
Well, I cannot see how you do the join between the tables, by this works for me:
create table e (email varchar(255))
insert into e values ('john123#dummydomain.com')
insert into e values ('jeff456#actualemail.com')
create table u (user_id varchar(255))
insert into u values ('john123')
insert into u values ('jeff456#actualemail.com')
GO
4 rows affected
select
*,
iif(user_id is null, 0, 1) as [Email LoginID match]
from e left join u
on left(e.email, charindex('#', e.email) - 1) = u.user_id
GO
email | user_id | Email LoginID match
:---------------------- | :------ | ------------------:
john123#dummydomain.com | john123 | 1
jeff456#actualemail.com | null | 0
db<>fiddle here

Select records when 2 column's data will match

I have two tables as shown below:
-----------------------
|EmpNo|Complaint |
-----------------------
|9091 |Change required|
|9092 |No change |
|9093 |Changes done |
-----------------------
Above table contains employee number and his complaints.
I have one another table which contains employee all kind of details as shown below.
-------------------------------
|EmpNo|EmailID |EmpBossNO|
-------------------------------
|9091 |abc#gmail.com|9092 |
|9092 |xyz#gmail.com|9093 |
|9093 |mno#gmail.com|9099 |
-------------------------------
Here, if Empno:9091 will raise any complain then a mail will send to his boss that the complain is raise by your employee and you have to accept it so I want to get EmailID of employee's boss and for that I want one SQL query. I tried the query shown here, but it doesn't work.
select EmpEmailID
from tblComplaint
inner join tblEmpMaster on tblEmpMaster.EmpNo = tblComplaint.EmpPSNo
where tblComplaint.EmpPSNo = tblEmpMaster.EmpBossNo
I want output like.. if complaint is raised by EmpNo:9091 then it will return EmailID of his boss which is xyz#gmail.com.
You are on the right track with a join between the tblComplaint and tblEmpMaster tables. But, you need an additional join to tblEmpMaster to bring in the boss' email for each employee complaint.
SELECT
c.EmpNo,
c.Complaint,
COALESCE(e2.EmailID, 'NA') AS boss_email
FROM tblComplaint c
INNER JOIN tblEmpMaster e1
ON c.EmpNo = e1.empNo
LEFT JOIN tblEmpMaster e2
ON e1.EmpBossNO = e2.EmpNo;
Demo
I used a left self join above, in case a given employee does not have a boss (e.g. for the highest ranking boss). In this case, I display NA for the boss email.
You should self join tblEmpMaster
select boss.EmpEmailID
from tblComplaint
inner join tblEmpMaster emp on emp.EmpNo = tblComplaint.EmpPSNo
inner join tblEmpMaster boss on boss.EmpNo = emp.EmpBossNO
where tblComplaint.EmpPSNo = 9091
DB Fiddle
you can even use sub queries to get the Email_Id of the boss as shown below
SELECT Email_Id
FROM EMP_Details
WHERE Emp_No IN (
SELECT Boss_Id
FROM Emp_Details) AND
Emp_No IN (
SELECT Emp_No
FROM Emp_Complaints)

T-SQL query to show all the past steps, active and future steps

I have 3 tables in SQL Server:
map_table: (workflow map path)
stepId step_name
----------------
1 A
2 B
3 C
4 D
5 E
history_table:
stepId timestamp author
----------------------------
1 9:00am John
2 9:20am Mary
current_stageTable:
Id currentStageId waitingFor
------------------------------------
12345 3 Kat
I would like to write a query to show the map with the workflow status. Like this result here:
step name time author
----------------------------
1 A 9:00am John
2 B 9:20am Mary
3 C waiting Kat
4 D
5 E
I tried left join
select
m.stepId, m.step_name, h.timestamp, h.author
from
map_table m
left join
history_table h on m.stepId = h.stepId
I thought it will list all the records from the map table, since I am using left join, but somehow it only shows 3 records which is from history table..
So I changed to
select
m.stepId, m.step_name, h.timestamp, h.author
from
map_table m
left join
history_table h on m.stepId = h.stepId
union
select
m.stepId, m.step_name, '' as timestamp, '' as author
from
map_table m
where
m.stageId not in (select stageId from history_table)
order by
m.stepId
Then it list the result almost as I expected, but how do I add the 3rd table in to show the current active stage?
Thank you very much for all your help!! Much appreciated.
Looks like it's what you asked:
with map_table as (
select * from (values (1,'A')
,(2,'B')
,(3,'C')
,(4,'D')
,(5,'E')) t(stepId, step_name)
)
, history_table as (
select * from (values
(1,'9:00am','John')
,(2,'9:20am','Mary')) t(stepId, timestamp, author)
)
, current_stapeTable as (
select * from (values (2345, 3, 'Kat')) t(Id, currentStageId, waitingFor)
)
select
m.stepId, m.step_name
, time = coalesce(h.timestamp, case when c.waitingFor is not null then 'waiting' end)
, author = coalesce(h.author, c.waitingFor)
from
map_table m
left join history_table h on m.stepId = h.stepId
left join current_stapeTable c on m.stepId = c.currentStageId
I think a union fits well with the data and avoids the coalescing the values on multiple joins.
with timeline as (
select stepId, "timestamp" as ts, author from history_table
union all
select currentStageId, 'waiting', waitingFor from current_stageTable
)
select step_id, step_name, "timestamp", author
from
map_table as m left outer join timeline as t
on t.stepId = m.stepId

SQL 2005 returning unique results with subquery

I have a database Table a (EMAILS) where EmailID is the Primary Key
EmailID Email_To Email_From Email_Subject Email_Registered Email_Read
If a user creates an email it is registered in this table.
For example, the user "Dave" who has id 3 sends an email to "John" who has id 4
So this would give
EmailID Email_To Email_From Email_Subject Email_Registered Email_Read
10 4 3 TEST 2/23/2016 11:00 False
To return results I do this select (joining the user profile database)
SELECT PROFILE_1.SellerID, PROFILE_1.Seller_UserName, EMAILS.EmailID, EMAILS.Email_From, EMAILS.Email_To, EMAILS.Email_Subject,
EMAILS.Email_Registered, EMAILS.Email_Read,
(SELECT Seller_UserName AS Epr2
FROM PROFILE
WHERE (SellerID = EMAILS.Email_To)) AS Expr2
FROM PROFILE AS PROFILE_1 LEFT OUTER JOIN
EMAILS ON EMAILS.Email_From = PROFILE_1.SellerID
WHERE (EMAILS.Email_From IS NOT NULL) AND (PROFILE_1.Seller_UserName = 'Dave')
ORDER BY EMAILS.Email_Registered DESC
So John Replies to Dave's email and it goes into the EMAILS_THREAD table and is registered as
EmailThreadID EmailID Email_To Email_From Email_Registered Email_Read
1 10 3 4 2/23/2016 11:05 False
What I am trying to do is a select that
SELECTS from EMAILS where Email_From is from Dave and return in the results the top 1 result from EMAIL_THREADS that is sent to Dave (based on Email_Registered) with the same EmailID as the EMAILS.EmailID if there is a entry in EMAIL_THREADS.
So in other words return the result of the EMAIL table and latest corresponding result in the EMAIL_THREADS table if there is one.
I hope this makes sense.
I've tried a ton of combinations and I can't figure this out.
At first I thought it was a subquery or a join or a group by...but i can't seem to nail the select and how it is structured.
Looking for some help.
Here is my last attempt
SELECT PROFILE_1.SellerID, PROFILE_1.Seller_UserName, EMAILS.EmailID, EMAILS.Email_From, EMAILS.Email_To, EMAILS.Email_Subject,
EMAILS.Email_Registered, EMAILS.Email_Read,
(SELECT Seller_UserName AS Epr2
FROM PROFILE
WHERE (SellerID = EMAILS.Email_To)) AS Expr2
FROM PROFILE AS PROFILE_1 LEFT OUTER JOIN
EMAILS ON EMAILS.Email_From = PROFILE_1.SellerID CROSS JOIN
(SELECT TOP (1) EMAILS_THREAD.Email_From, EMAILS_THREAD.Email_To, EMAILS_THREAD.Email_Registered, EMAILS_THREAD.Email_Read
FROM EMAILS_THREAD LEFT OUTER JOIN
EMAILS AS EMAILS_1 ON EMAILS_THREAD.EmailID = EMAILS_1.EmailID) AS derivedtbl_1
WHERE (EMAILS.Email_From IS NOT NULL) AND (PROFILE_1.Seller_UserName = 'Dave')
ORDER BY EMAILS.Email_Registered DESC
But it's not returning anything from EMAILS_THREADS at all.
SELECTS from EMAILS where Email_From is from Dave and return in the results the top 1 result from EMAIL_THREADS that is sent to Dave (based on Email_Registered) with the same EmailID as the EMAILS.EmailID if there is a entry in EMAIL_THREADS.Hope this helps
with cte
as
(select
* from emails
where email_from=3
)
select * from cte mt --only for dave
outer apply
(
select top 1* from
emial_threads st where mt.mail_from=st.mail_to ---send to dave
and mt.emailid=st.emailid) b
Haven't been able to find a solution to this so I am paying a SQL professional to resolve this for me. Thanks for the input from Tab and The Gameiswar. Much appreciated.

INNER JOIN + JOIN returning duplicating result

I have a SQL Server query that it trying to connect 3 tables. I am getting the results I need, but have a duplication issue.
table1 is the main record table. table2 is basically a user table. table3 is a lookup table that connects users to managers.
I need to connect one user to all the records connected to himself and any other users connected to his manager (or the people he manages, if a manager). All managers are users, and any user can 'own' a record on table1.
table3 connects userIDs on table2 to the userID of the manager also on table2.
table3 (lookup table)
id | altID
15 | 205
16 | 205
17 | 205
18 | 246
table2 (user table)
id | other_col
15 | abc
16 | def
17 | ggg
18 | hhh
205| yyy
246| zzz
table1 (record table)
id | record_data
15 | abc
16 | def
17 | ggg
18 | hhh
205| yyy
246| zzz
The user will put in their ID. So if id = 'XYZ'.
SELECT c.id, c.col, s.col2, s.col3
FROM table1 AS c
INNER JOIN table2 AS s
ON c.id = s.id
JOIN table3 AS p
ON c.id = p.id OR c.id = p.altID
WHERE
(p.id = 'XYZ'
OR p.altID = 'XYZ')
AND
(c.id = p.altID
OR c.id = p.id)
AND
NULLIF(LTRIM(RTRIM(c.column)), '') IS NOT null
This is giving me all the associated records (all the users that XYZ is related to). It is, however, including XYZ twice, and I cannot figure out why.
This is resolvable using DISTINCT(c.id) in the SELECT statement, but I am trying to understand why the duplication happens to begin with.
Here’s what I get when I work through the logic in the query:
The join from table1 (c) to table2 (s) is one-to one, as (s) contains a single row with the “name” for the Id in (c). No duplciation problems there.
You then join table1 (c) to table 3 (p), from the “owing” id in (c) to all rows in (p) for that id, for both user and manager columns. Thus, if the item in (c) is owned by a manager, you will get one or more hits, once for them as a user and once for each time they are a manager.
Leastways, that’s what I figure based solely on the code and the descriptions you provided.
If you want to show the relation USER - MANAGER defined in table3 one posible way is to join the lookup table (table3) again with the user table (to get the manager record).
SELECT c.id, s.other_col, p.altid, manager.other_col manager_name
FROM table1 c
JOIN table2 s
ON c.id = s.id
LEFT OUTER JOIN table3 p
ON c.id = p.id
LEFT OUTER JOIN table2 manager
ON p.altid = manager.id;
ID OTHER_COL ALTID MANAGER_NAME
---------- --------- ---------- ------------
17 ggg 205 yyy
16 def 205 yyy
15 abc 205 yyy
18 hhh 246 zzz
246 zzz
205 yyy
I'm using OUTER JOIN, that will show also users withou manager. INNER JOIN will suppress those records.

Resources