REPLACE within a CASE statement - sql-server

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

Related

Sqlserver CTE how to get first non null value

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

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)

How to join table with itself, with a second "ON" criteria, if a row is null

I have a User table. Among others, it has these 4 columns:
------ UserID ------- | ---- Username ----- | --- CreatedBy --- | ParentUserID
(PK, bigint, not null) | (char(20), not null) | (varchar(50), null) | (bigint, null)
Both ParentUserID and CreatedBy points at the "owner account", by the UserID or Username respectively. Both are unique.
CreatedBy is never actually null, but UserID is indexed, so ParentUserID is preferred - and it's also the one we're moving towards.
Obviously I'm not fluent in SQL, but this is my idea of it:
SELECT Users.*
FROM tblUsers AS Owners
LEFT JOIN tblUsers AS Users
ON
ISNULL(Users.ParentUserID = Owners.UserID,
Users.CreatedBy = Owners.Username)
WHERE Owners.UserID = 14;
This is as far as I've gotten:
SELECT ISNULL(POwners.UserID, COwners.UserID) AS OwnerID, Users.*
FROM tblUsers AS Users
RIGHT JOIN tblUsers AS POwners ON Users.ParentUserID = POwners.UserID
RIGHT JOIN tblUsers AS COwners ON Users.CreatedBy = COwners.Username
WHERE OwnerID = 14;
Although obviously this doesn't work. On a secondary note, I further need to convert this to LINQ, but for this question, it is only relevant so far that the query will be possible to convert, which I would expect of the vast majority of queries.
join conditions are just boolean tests, so you need to write a proper boolean condition, e.g. (P or Q) AND R. You can't chain them with ,, so...
... ON ISNULL((Users.ParentUserID = Owners.UserID) AND (Users.CreatedBy = Owners.Username))
or whatever you need the logic to be. Making it a VALID boolean expression is the critical part.
This seems to be the query that did the trick for me:
SELECT Users.*
FROM tblUsers AS Owners
LEFT JOIN tblUsers AS Users
ON Users.ParentUserID = Owners.ParentUserID
OR Users.CreatedBy = Owners.Username
WHERE Owners.UserID = 14;
Thanks to Marc B for the help.
As for LINQ, it turned out only equijoins are supported, so I did a cross join like this:
from u in dbContext.tblUsers
from o in dbContext.tblUsers
where (u.ParentUserID == o.UserID || u.CreatedBy == o.Username)
&& o.UserID == 14
select u;
Which turns into the following query:
SELECT Users.*
FROM tblUsers AS Users
CROSS JOIN tblUsers AS Owners
WHERE(Users.ParentUserID = Owners.UserID OR Users.CreatedBy = Owners.Username)
AND(Owners.UserID = 14)

SELECT DISTINCT showing duplicate dates per customer email

I am trying to retrieve information for the past ten months, but am having a couple of errors. First, my query is getting data from as far back as 2013. Secondly, I am seeing duplicates in my results based on the PolEffDate field, like this:
EntityID | PolEffDate | EMail | CustNo | Producer | BusinessPhone
abcde-12345-fghij-67890 | 2013-09-24 | somewhere#email.com | 31000 | Bob Builder | 123-456-7890
abcde-12345-fghij-67890 | 2013-12-01 | somewhere#email.com | 31000 | Bob Builder | 123-456-7890
abcde-12345-fghij-67890 | 2014-09-24 | somewhere#email.com | 31000 | Bob Builder | 123-456-7890
Here is my SQL Query:
SELECT DISTINCT
CONVERT(VarChar(36), Customer.CustId) AS EntityID
, BasicPolInfo.PolEffDate, Customer.EMail, Customer.CustNo
, (isnull(Employee.Firstname + ' ','') + isnull(Employee.LastName,''))
AS Producer, Employee.BusFullPhone
FROM
Customer INNER JOIN BasicPolInfo ON Customer.CustId = BasicPolInfo.CustId INNER JOIN
Transaction ON BasicPolInfo.PolId = Transaction.PolId INNER JOIN
GeneralBranch ON Customer.GLBrnchCode = GeneralBranch.GLBrnchCode INNER JOIN
GeneralDepartment ON Customer.GLDeptCode = GeneralDepartment.GLDeptCode INNER JOIN
GeneralDivision ON Customer.GLDivCode = GeneralDivision.GLDivCode INNER JOIN
Employee ON BasicPolInfo.ExecCode = Employee.EmpCode
WHERE
BasicPolInfo.PolExpDate >= DATEADD(MONTH, -10,CONVERT(VARCHAR(11),GETDATE(),106))
AND BasicPolInfo.PolExpDate <= CONVERT(VARCHAR(11),GETDATE(),106)
AND Customer.Active = 'Y'
AND Customer.typeCust = 'P'
Thank you for the help. I will try my best to answer any questions.
Daniel, the duplication you are seeing is caused because you have multiple records in BasicPolInfo for each CustID value. You can confirm this by running the following query:
SELECT CustID, COUNT(*)
FROM BasicPolInfo
GROUP BY CustID
HAVING COUNT(*) > 1
Depending on your schema, this may not be an issue - after all, there is probably a perfectly legitimate reason for that! Multiple policies per Customer is my guess.
To resolve the duplication, I would recommend a GROUP BY with MIN() or MAX().
Your other issue, that of retrieving data from earlier dates, is because you are selecting the PolEffDate (presumably, policy effective date), but filtering the PolExpDate (presumably, policy expiration date). Which are you intending to use? Policies that have finished sometime in the last ten months could have started much earlier than that.
To resolve the wider date range, reference the same value in your SELECT and WHERE clauses.
Query below (using MAX() and PolExpDate):
SELECT
CONVERT(VarChar(36), Customer.CustId) AS EntityID,
MAX(BasicPolInfo.PolExpDate) AS PolExpDate, -- note that this is now PolExpDate
Customer.EMail,
Customer.CustNo,
(isnull(Employee.Firstname + ' ','') + isnull(Employee.LastName,'')) AS Producer,
Employee.BusFullPhone
FROM
Customer INNER JOIN
BasicPolInfo ON Customer.CustId = BasicPolInfo.CustId INNER JOIN
[Transaction] ON BasicPolInfo.PolId = [Transaction].PolId INNER JOIN
GeneralBranch ON Customer.GLBrnchCode = GeneralBranch.GLBrnchCode INNER JOIN
GeneralDepartment ON Customer.GLDeptCode = GeneralDepartment.GLDeptCode INNER JOIN
GeneralDivision ON Customer.GLDivCode = GeneralDivision.GLDivCode INNER JOIN
Employee ON BasicPolInfo.ExecCode = Employee.EmpCode
WHERE
BasicPolInfo.PolExpDate >= DATEADD(MONTH, -10,CONVERT(VARCHAR(11),GETDATE(),106))
AND BasicPolInfo.PolExpDate <= CONVERT(VARCHAR(11),GETDATE(),106)
AND Customer.Active = 'Y'
AND Customer.typeCust = 'P'
GROUP BY
CONVERT(VarChar(36), Customer.CustId),
Customer.EMail,
Customer.CustNo,
(isnull(Employee.Firstname + ' ','') + isnull(Employee.LastName,'')),
Employee.BusFullPhone

Using Inner Join on Comma-Separated Values in Stored Procedure

I have a table UserMaster as follow...(only required columns are shown)
UserID UserName EmailID
---------------------------------
1000 amol amol#gmail.com
1001 mahesh mahesh#gmail.com
1002 saurabh saurabh#gmail.com
1003 nitesh nitesh#gmail.com
Another table MessageHistory (Only required columns are shown)
MsgCode From To
-----------------------------
MSG001 1000 1001,1002,1003
MSG002 1001 1000,1002,1003
I am storing UserIds in From and To columns...
I am trying to create a stored procedure to display the Email History of particular message code
Create Procedure proc_GetMessageHistory
#MsgCode varchar(50)
as
Begin
Select * From MessageHistory Where MsgCode=#MsgCode
End
The result is coming as shown above in MessageHistory table...but I want to show respective UserEmailIDs instead of UserID (e.g. 'amol#gmail.com' instead of 1000)...
How could I do this in a stored procedure? How could I use inner join in this case specially with comma-separated values? Please help...thanks
As everyone has already noted, this should never be any sort of permanent solution, as there no way it will ever perform in an efficient manner. Also, that sort of denormalised structure is likely to have any number of issues. That said...
List of email addresses per messages, i.e. one recipient per row:
select m.MsgCode
, sender = s.EmailID
, recipient = u.EmailID
from MessageHistory m
inner join UserMaster s on m.[From] = s.UserID
inner join UserMaster u on charindex(cast(u.UserID as varchar), m.[To]) > 0
SQL Fiddle with demo.
List of messages, comma separated list of email addresses, one message per row:
with emails as
(
select m.MsgCode
, recipient = u.EmailID
from MessageHistory m
inner join UserMaster u on charindex(cast(u.UserID as varchar), m.[To]) > 0
)
select m.MsgCode
, [From] = u.EmailID
, [To] = stuff
(
(
select ',' + recipient
from emails e
where m.MsgCode = e.MsgCode
for xml path('')
)
, 1
, 1
, ''
)
from MessageHistory m
inner join UserMaster u on m.[From] = u.UserID
SQL Fiddle with demo.

Resources