select results from two tables but choose one field over another - sql

I have two tables.
tblEmployee tblExtraOrMissingInfo
id nvarchar(10) id nvarchar(10)
Name nvarchar(50) Name nvarchar(50)
PreferredName nvarchar(50)
UsePreferredName bit
The data (brief example)
tblEmployee tblExtraOrMissingInfo
id Name id Name PreferredName UsePreferredName
AB12 John PN01 Peter Tom 1
LM22 Lisa YH76 Andrew Andy 0
PN01 Peter LM22 Lisa Liz 0
LK655 Sarah
I want a query to produce the following result
id Name
AB12 John
LM22 Lisa
PN01 Tom
YH76 Andrew
LK655 Sarah
So what I want is all the records from tblEmployee returned and any records in tblExtraOrMissingInfo that are not already in tblEmployee.
If there is a record in both tables with the same id I would like is if the UsePreferredName field in tblExtraOrMissingInfo is 1 for the PreferredName to be used rather than the Name field in the tblEmployee, please see the record PN01 in the example above.

It is slightly faster to use a left join and coalesce than to use the case statement (most servers are optimized for coalesce).
Like this:
SELECT E.ID, COALESCE(P.PreferredName,E.Name,'Unknown') as Name
FROM tblemployee E
LEFT JOIN tblExtraOrMissingInfo P ON E.ID = P.ID AND P.UsePreferredName = 1
The ,'Unknown' is not needed to answer your question, but I added
here to show that you can enhance this query to handle cases where the
name is not available in both tables and you don't want nulls in your result

left join on the employee table and use a case expression for name.
select e.id
,case when i.UsePreferredName = 1 then i.PreferredName else e.name end as name
from tblemployee e
left join tblExtraOrMissingInfo i on i.id=e.id

You can LEFT OUTER JOIN the tables together, then use a CASE statement and COALESCE() formula to get this:
SELECT
tblEmployee.id,
CASE WHEN UsePreferredName = 1 THEN COALESCE(PreferredName, tblEmployee.Name) ELSE tblEmployee.name
FROM
tblEmployee
LEFT OUTER JOIN tblExtraOrMissingInfo
ON tblEmployee.id = tblExtraOrMissingInfo.id
CASE checks that usePreferredName value, and the COALESCE() then grabs PreferredName unless it's NULL. Then it grabs the employees name from tblEmployee.

select id ,name
from
(
select id ,name from tblEmployee
union all
select id ,name from tblExtraOrMissingInfo
)group by id,name

This is an other way you can achieve the results. You can also use CTE to get the same result.
CREATE TABLE #tblEmployee
(
id NVARCHAR(10)
, Name NVARCHAR(50)
);
CREATE TABLE #tblExtraOrMissingInfo
(
id NVARCHAR(10)
, Name NVARCHAR(50)
, PreferredName NVARCHAR(50)
, UsePreferredName BIT
);
INSERT INTO #tblEmployee
( id, Name )
VALUES ( N'AB12' -- id - nvarchar(10)
, N'John' -- Name - nvarchar(50)
),
( N'LM22' -- id - nvarchar(10)
, N'Lisa' -- Name - nvarchar(50)
),
( N'PN01' -- id - nvarchar(10)
, N'Peter' -- Name - nvarchar(50)
),
( N'LK655' -- id - nvarchar(10)
, N'Sarah' -- Name - nvarchar(50)
);
INSERT INTO #tblExtraOrMissingInfo
( id, Name, PreferredName, UsePreferredName )
VALUES ( N'PN01' -- id - nvarchar(10)
, N'Peter' -- Name - nvarchar(50)
, N'Tom' -- PreferredName - nvarchar(50)
, 1 -- UsePreferredName - bit
),
( N'YH76' -- id - nvarchar(10)
, N'Andrew' -- Name - nvarchar(50)
, N'Andy' -- PreferredName - nvarchar(50)
, 0 -- UsePreferredName - bit
),
( N'LM22' -- id - nvarchar(10)
, N'Lisa' -- Name - nvarchar(50)
, N'Liz' -- PreferredName - nvarchar(50)
, 0 -- UsePreferredName - bit
);
SELECT R.id
, CASE WHEN tbl.UsePreferredName = 1 THEN tbl.PreferredName
ELSE R.Name
END AS NAME
FROM #tblExtraOrMissingInfo tbl
RIGHT JOIN ( SELECT id
, Name
FROM #tblEmployee
UNION
SELECT id
, Name
FROM #tblExtraOrMissingInfo
) AS R
ON R.id = tbl.id
AND R.Name = tbl.Name;

Related

Masking ID and Name in SQL

I have a use case where there is a free text field and the user id in the format ab12345 (fixed) and name (dynamic) can appear anywhere in the string.
Now I need to replace the ab12345 with xxxxxxx and the names also with XXXX wherever I find them in the string.
I used:
select *
from dbo.TEST
WHERE DESCRIPTION like '%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%';
to get the user id ab12345 but I am unable to write the replace function for this since the result is dynamic.
Same with the name as well.
the following may help in redacting the userID
USE tempdb
GO
CREATE TABLE #CustComments
( CustomerID INT
, CustomerNotes VARCHAR(8000)
)
GO
INSERT dbo.#CustComments
( CustomerID
, CustomerNotes
)
VALUES
( 1, 'An infraction was raised on user id ab12345, and the name of the complainant is John')
, ( 2, 'The customer was not happy with person CD45678 and is going to ask William Jones to speak with George Hillman about this matter' )
, ( 3, 'A customer called and repeatedly mentioned the name of employee ZX98765 and assumes their name was Janet which is not correct')
SELECT * ,
PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes) start_pos,
SUBSTRING (customernotes, (PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes)) ,7 ) extractstring,
REPLACE(customernotes, substring (customernotes, (PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes)) ,7 ), 'XXXXXXX') redacted
FROM #CustComments
--TIDY UP
DROP TABLE #CustComments
if you have or can create a table of "names"...this may work
USE tempdb
GO
CREATE TABLE #CustComments (
CustomerID int,
CustomerNotes varchar(8000)
)
GO
INSERT #CustComments (CustomerID
, CustomerNotes)
VALUES (1, 'An infraction was raised on user id ab12345 , and the name of the complainant is Ann')
, (2, 'The customer was not happy with person CD45678 and is going to ask Richard Jones to speak with Todd Hillman about this matter')
, (3, 'A customer called and repeatedly mentioned the name of employee ZX98765 and assumes their name was Shana which is not correct')
CREATE TABLE #empname (
ename varchar(255) NOT NULL
)
GO
INSERT INTO #empname ([ename])
VALUES ('Zeph'), ('Ebony'), ('Felicia'), ('Benedict'), ('Ahmed'), ('Ira'), ('Julie'), ('Levi'),
('Sebastian'), ('Fiona'), ('Lamar'), ('Russell'), ('Abdul'), ('Lev'), ('Isaiah'), ('Charlotte'),
('Rowan'), ('Ivory'), ('Quinn'), ('Jordan'), ('Xantha'), ('Shana'), ('Mufutau'), ('Jessamine'),
('Desirae'), ('Yvette'), ('Odessa'), ('Ray'), ('Ori'), ('Zenaida'), ('Allegra'), ('Allistair'),
('Raymond'), ('Martena'), ('Cameron'), ('Ila'), ('Nigel'), ('Dale'), ('Emerald'), ('Guinevere'),
('Boris'), ('Dolan'), ('Ainsley'), ('Madeson'), ('Kadeem'), ('Ciaran'), ('Hop'), ('Louis'),
('Maia'), ('Hiroko'), ('Hakeem'), ('Cole'), ('Tyrone'), ('Amy'), ('Doris'), ('Keaton'),
('Carlos'), ('Richard'), ('Lysandra'), ('Beverly'), ('Hamish'), ('Demetria'), ('Eric'), ('Nayda'),
('Sydney'), ('Fritz'), ('Blaze'), ('Regina'), ('Ciara'), ('Ina'), ('Joan'), ('Risa'),
('Alea'), ('Denton'), ('Daryl'), ('Mollie'), ('Keane'), ('Jarrod'), ('Ann'), ('Juliet'),
('Germaine'), ('Alexa'), ('Zane'), ('Kiona'), ('Armand'), ('Jin'), ('Geraldine'), ('Natalie'),
('Nomlanga'), ('Todd'), ('Rajah'),('Lucian'), ('Idona'), ('Autumn'), ('Briar'),
-- add surname
('Hillman');
;
-- redact the userID std format
SELECT
CustomerID ,
--PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes) start_pos,
--SUBSTRING (customernotes, (PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes)) ,7 ) extractstring,
REPLACE(customernotes, substring (customernotes, (PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes)) ,7 ), 'XXXXXXX') ID_redacted
INTO #ID_REDACT
FROM #CustComments
-- split into rows
SELECT customerId, value
into #SPLIT
FROM #ID_REDACT
CROSS APPLY STRING_SPLIT(ID_redacted, ' ');
--redact based on join with a ""name"" table
SELECT s.customerid,
CASE
WHEN e.ename IS NULL THEN s.value
ELSE 'XXXXXXX'
END AS name_redact
INTO #NAME_REDACT
FROM #split AS s
LEFT OUTER JOIN #empname AS e
ON s.value = e.ename
SELECT customerId,
STRING_AGG(name_redact, ' ') as full_redact
INTO #RESULTS
from #NAME_REDACT
group by CustomerID
-- RESULTS WITH COMPARISON
SELECT
C.CustomerID,
C.CustomerNotes AS Original,
R.full_redact AS Redacted
FROM #CustComments AS C
INNER JOIN #RESULTS AS R
ON C.CustomerID = R.customerId
--TIDY UP
DROP TABLE #CustComments
DROP TABLE #empname
DROP TABLE #ID_REDACT
DROP TABLE #SPLIT
DROP TABLE #NAME_REDACT
DROP TABLE #RESULTS

SQL Query second relation

i have a problem i want to take SQL query language that returns the employee's (id) supervisor to his supervisor.
I have a tab employee
create table employee (
id int not null,
surname varchar(50),
name varchar(30),
boss_id int)
You may try this...
select a.id, b.boss_id
from employee as a
inner join employee as b on a.boss_id=b.id
For test case
create table DemoTable
(
accountid bigint
,parentid bigint
,accountname nvarchar(128)
)
insert DemoTable(accountid,parentid,accountname)
select 1, null, 'Root'
union select 2, 3, 'Child1'
union select 3, 1, 'Child2'
Query:
select a.accountid, b.parentid from DemoTable as a
inner join DemoTable as b on a.parentid=b.accountid
output is as : pass accountid as 2
accountid parentid
2 1
Here 2 >> 3 >> 1

Can I eliminate the inner query from my query

I would like to know if I can improve this query:
SELECT _c.*,
_o.Status,
(SELECT TOP 1 _o.PlacedOn
FROM Orders _o1
JOIN Client _c1 on _c1.Id = _o1.Client_Id
WHERE _c1.Id = 3
ORDER BY PlacedOn DESC) as LastOrderDate
FROM Client _c
JOIN Orders _o on _c.[Id] = _o.[Client_Id]
WHERE _c.Id = 3 AND _o.[Status] = 'Draft'
The outer JOIN is needed in order to get information from client and also some information on he's order.
The inner JOIN, gets the last placed order of that client.
I would like to know if I can remove the inner join and how.
Client.tbl
Id int
Gender nvarchar(255)
DateOfBirth nvarchar(255)
ContactDetails nvarchar(MAX)
FirstName nvarchar(255)
MiddleName nvarchar(255)
LastName nvarchar(255)
Order.tbl
Id int
Status nvarchar(255)
PaymentMethod nvarchar(255)
PlacedOn datetime2(7)
CancelledOn datetime2(7)
PaidOn datetime2(7)
OrderNumber nvarchar(255)
Client_Id int
Client test data:
Id Gender DateOfBirth ContactDetails FirstName MiddleName LastName
1 F. 10/16/1991 NULL Mia M. Brown
Order test data:
Id Status PaymentMethod PlacedOn CancelledOn PaidOn OrderNumber Client_Id
1 Done Cash 11/11/1996 NULL NULL NULL 1
2 Done Cash 11/11/2007 NULL NULL NULL 1
3 Draft NULL NULL NULL NULL NULL 1
Expected result:
client information FirstName for example
the Id of order where status is 'Draft'
the PlacedOn where its value is max, so in this case 11/11/2007
Result:
FirstName DraftId LastPlacedOn
Mia 3 11/11/2007
I suppose you can use MAX() OVER () to find the maximum value from the result set:
WITH cte AS (
SELECT _c.*,
_o.Status,
MAX(_o.PlacedOn) OVER (PARTITION BY _c.id) as LastOrderDate
FROM Client _c
JOIN Orders _o on _c.[Id] = _o.[Client_Id]
)
SELECT *
FROM cte
WHERE Id = 1 AND [Status] = 'Draft'

Split string, copy data into multiple tables

I have the following tables
Table A
RID | Name |Phone |Email |CreatedOn
------------------------------------------------------------
1 | John Smith | 2143556789 |t1#gmail.com |2012-09-01 09:30:00
2 | Jason K Crull | 2347896543 |t2#gmail.com |2012-08-02 10:34:00
Table B
CID| FirstName |LastName |Phone |Email |CreatedOn |Title|Address|City|State
---------------------------------------------------------------------------------------------------
11 | John | Smith |2143556789 |t1#gmail.com |2012-09-01 09:30:00|NULL|NULL|NULL|NULL
12 | Jason | K Crull |2347896543 |t2#gmail.com |2012-08-02 10:34:00|NULL|NULL|NULL|NULL
Table C
RID | CID |IsAuthor|CreatedOn
-----------------------------------------
1 | 11 | 0 |2012-09-01 09:30:00
2 | 12 | 0 |2012-08-02 10:34:00
For every row in "Table A" need to create a row in "Table B" splitting the name into First and Last Name as shown and after creating a row, insert new row into Table C with RID from Table A, CID from Table B, IsAuthor bit Default to 0 and CreatedOn from Table A.The CID is auto incremented. Can anyone help me in achieving this. I am very new to SQL. Thanks!
I believe you're looking for something like this (I left off some fields, but this should get the point across). Main thing to see is the substring and charindex functions which are used to split the name into first name and last names:
insert into tableb (firstname,lastname,phone,email)
select
left(name, charindex(' ',name)-1),
substring(name, charindex(' ', name)+1, len(name)),
phone, email
from tablea ;
insert into tablec
select a.rid, b.cid, 0, a.createdon
from tablea a
inner join tableb b on a.name = b.firstname + ' ' + b.lastname
and a.phone = b.phone and a.email = b.email ;
SQL Fiddle Demo
If there is a concern for the same names, emails, etc, then you're probably going to need to look into using a dreaded cursor and scope_identity(). Hopefully you won't have to go down that route.
Stop thinking about "for every row" and think of it as "a set." Very rarely is it efficient to process anything row-by-row in SQL Server, and very rarely is it beneficial to think in those terms.
--INSERT dbo.TableC(RID, CID, IsAuthor, CreatedOn)
SELECT a.RID, b.CID, IsAuthor = 0, a.CreatedOn
FROM dbo.TableA AS a
INNER JOIN dbo.TableB AS b
ON a.Name = b.FirstName + ' ' b.LastName;
When you believe it is returning the right results, uncomment the INSERT.
To split the name I'd use CharIndex to find the position of the space, then Substring to break the word apart.
For keeping track of which row in TableA the data in TableB came from, I'd just stick a column onto B to record this data, then drop it when you come to inset into table C.
An alternative would be to make CID an identity column on C instead of B, populate C first, then feed that data into TableB when you come to populate that.
if OBJECT_ID('TableA','U') is not null drop table TableA
create table TableA
(
rid int not null identity(1,1) primary key clustered
, Name nvarchar(64)
, Phone nvarchar(16)
, Email nvarchar(256)
, CreatedOn datetime default (getutcdate())
)
if OBJECT_ID('TableB','U') is not null drop table TableB
create table TableB
(
cid int not null identity(1,1) primary key clustered
, FirstName nvarchar(64)
, LastName nvarchar(64)
, Phone nvarchar(16)
, Email nvarchar(256)
, CreatedOn datetime default (getutcdate())
, Title nvarchar(16)
, [Address] nvarchar(256)
, City nvarchar(64)
, [State] nvarchar(64)
)
if OBJECT_ID('TableC','U') is not null drop table TableC
create table TableC
(
rid int primary key clustered
, cid int unique
, IsAuthor bit default(0)
, CreatedOn datetime default (getutcdate())
)
insert TableA (Name, Phone, Email) select 'John Smith', '2143556789', 't1#gmail.com'
insert TableA (Name, Phone, Email) select 'Jason K Crull', '2347896543', 't2#gmail.com'
alter table TableB
add TempRid int
insert TableB(FirstName, LastName, Phone, Email, TempRid)
select case when CHARINDEX(' ', Name) > 0 then SUBSTRING(Name, 1, CHARINDEX(' ', Name)-1) else Name end
, case when CHARINDEX(' ', Name) > 0 then SUBSTRING(Name, CHARINDEX(' ', Name)+1, LEN(Name)) else '' end
, Phone
, Email
, Rid
from TableA
insert TableC (rid, cid)
select TempRid, cid
from TableB
alter table TableB
drop column TempRid
select * from TableB
select * from TableC
Try it here: http://sqlfiddle.com/#!3/aaaed/1
Or the alternate method (inserting to C before B) here: http://sqlfiddle.com/#!3/99592/1

SQL Recursive Coalesce

I'm trying to create a column that contains all cities of the referenced addresses.
DECLARE #AddressList nvarchar(max)
SELECT #AddressList = COALESCE(#AddressList + ' ', '') + City FROM [Address]
SELECT
Employee.*,
(SELECT #AddressList) AS AddressCities
FROM Employee
But I dont know where to put the WHERE clause.
...
(SELECT #AddressList WHERE EmployeeId = Employee.EmployeeId) AS AddressCities
...
The above test doesnt work..
Table schemas are:
Employee
EmployeeId
Name
Address
Street
City
EmployeeId
If i understand you correctly, you wish to show all Cities in a single column for the employee. So you wish to GROUP BY and CONCAT.
Using Sql Server 2005, try this (working example)
DECLARE #Employee TABLE(
EmployeeId INT,
NAME VARCHAR(100)
)
INSERT INTO #Employee (EmployeeId,[NAME]) SELECT 1, 'A'
INSERT INTO #Employee (EmployeeId,[NAME]) SELECT 2, 'B'
DECLARE #Address TABLE(
Street VARCHAR(50),
City VARCHAR(50),
EmployeeId INT
)
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'A','A', 1
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'B','B', 1
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'C','C', 1
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'D','D', 2
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'E','E', 2
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'F','F', 2
SELECT e.EmployeeId,
e.[NAME],
(
SELECT al.City + ','
FROM #Address al
WHERE al.EmployeeId = e.EmployeeId
FOR XML PATH('')
)
FROM #Employee e
GROUP BY e.EmployeeId,
e.[NAME]
Do need more information about what you mean by 'column that contains all cities'. How is what you want different to the following might help you phrase the question
SELECT e.EmployeeId,e.Name,a.City
FROM Employee e
INNER JOIN Address a ON a.EmployeeId = e.EmployeeId
GROUP BY e.EmployeeId,e.Name
-- update
I think I see what you mean, do you want like:
EmployeeID | Name | Address
1 | John | 'London','Paris','Rome'
2 | Jane | 'New York','Miami'
?