Join two tables on multiple conditions - SQL Server - sql

I need to join two tables based on some conditions. The src tale in my daily input. I need to join the src table with the mstr table based on some criteria which is as below.
If I get a join between the two tables by memberid, fname and lname then I need to take the dob from the mstr table.
If I do not get a matching criteria on the above basis then I need to join just by member id and then take the dob. If there are multiple records available in the mstr by member id then I need to pick that record which has the older of the dob.
If I do not get a match by memberid also then the need to randomly create a fname which would be xx+5random numbers, lname would be ZZ+5andom numbers and dob as today's date - 110 years.
This is what I have tried which would help you expedite the solution. I am providing the sample data for both the tables
declare #src table (memberids int, fnames varchar(25), lnames varchar(25));
insert into #src values
(1, 'Ankit','Bansal'),
(2, 'Dinesh','Bansal'),
(3, 'Sushil','Dania'),
(4, '',''),
(5, Null ,Null),
(10,Null,Null)
select * from #src
declare #mstr table (memberid int, fname varchar(25), lname varchar(25),dob date);
insert into #mstr values
(1, 'Ankit','Bansal','2010-06-24'),
(2, 'Dinesh','Bansal','2009-06-24'),
(3, 'Sushil','Dania','2000-06-24'),
(4, 'Sunil','Wadh','2011-06-24'),
(5, 'Suresh','Bansal','2000-06-24'),
(5, 'Animesh','Bajaj','2001-06-24'),
(6, 'Dhiresh','Jain','2001-06-24');
select * from #mstr;
This is the query that I have written.
select memberids, fnames, lnames, a.dob
from #src
outer apply
(select dob
from #mstr where memberids = memberid and fnames = fname
and lnames = lname) a ;
The current result that I am getting is
memberids fnames lnames dob
1 Ankit Bansal 2010-06-24
2 Dinesh Bansal 2009-06-24
3 Sushil Dania 2000-06-24
4 NULL
5 NULL NULL NULL
10 NULL NULL NULL
However the output should look like below
memberids fnames lnames dob
1 Ankit Bansal 24-06-2010
2 Dinesh Bansal 24-06-2009
3 Sushil Dania 24-06-2000
4 Sunil Wadh 24-06-2011
5 Suresh Bansal 24-06-2000
10 XX12345 ZZ123456 Today's Date - 110 years

I believe the following will do (tested on SQL Fiddle):
SELECT #src.memberids
, CASE WHEN a.memberid IS NOT NULL THEN #src.fnames
WHEN b.memberid IS NOT NULL THEN b.fname
ELSE 'XX' + FORMAT(ABS(CAST(CHECKSUM(NewId()) AS BIGINT)) % 100000, '00000')
END AS fnames
, CASE WHEN a.memberid IS NOT NULL THEN #src.lnames
WHEN b.memberid IS NOT NULL THEN b.lname
ELSE 'ZZ' + FORMAT(ABS(CAST(CHECKSUM(NewId()) AS BIGINT)) % 100000, '00000')
END AS lnames
, CASE WHEN a.memberid IS NOT NULL THEN a.dob
WHEN b.memberid IS NOT NULL THEN b.dob
ELSE DATEADD(year, -110, CAST(GETDATE() AS DATE))
END AS dob
FROM #src
LEFT JOIN #mstr a ON a.memberid = #src.memberids
AND a.fname = #src.fnames
AND a.lname = #src.lnames
OUTER APPLY (
SELECT TOP 1 b.memberid, b.fname, b.lname, b.dob
FROM #mstr b
WHERE b.memberid = #src.memberids
ORDER BY b.dob
) b

Please check this query. This should work but I must say that there are flaw in table design. If this is the requirement, consider the following query. But if you have scope, you can rethink to redesign your table first.
declare #src table (memberids int, fnames varchar(25), lnames varchar(25));
insert into #src values
(1, 'Ankit','Bansal'),
(2, 'Dinesh','Bansal'),
(3, 'Sushil','Dania'),
(4, '',''),
(5, Null ,Null),
(10,Null,Null)
declare #mstr table (memberid int, fname varchar(25), lname varchar(25),dob date);
insert into #mstr values
(1, 'Ankit','Bansal','2010-06-24'),
(2, 'Dinesh','Bansal','2009-06-24'),
(3, 'Sushil','Dania','2000-06-24'),
(4, 'Sunil','Wadh','2011-06-24'),
(5, 'Suresh','Bansal','2000-06-24'),
(5, 'Animesh','Bajaj','2001-06-24'),
(6, 'Dhiresh','Jain','2001-06-24');
SELECT M.memberid,M.fname,M.lname,M.dob
FROM (
SELECT M.memberid, MIN(M.dob) dob
FROM #src S
INNER JOIN #mstr M ON S.memberids = M.memberid
AND S.fnames = M.fname
AND S.lnames = M.lname
GROUP BY M.memberid
)B
INNER JOIN #mstr M ON B.memberid = M.memberid AND B.dob = M.dob
UNION
SELECT M.memberid,M.fname,M.lname,M.dob
FROM (
SELECT M.memberid, MIN(M.dob) dob
FROM #src S
INNER JOIN #mstr M ON S.memberids = M.memberid
AND (S.fnames IS NULL OR S.fnames = '')
GROUP BY M.memberid
)B
INNER JOIN #mstr M ON B.memberid = M.memberid AND B.dob = M.dob
UNION
SELECT C.smid memberids, 'XX'+CAST ((convert(numeric(5,0),rand() * 10000) + 9999)AS VARCHAR(5)) fname,
'ZZ'+CAST ((convert(numeric(5,0),rand() * 10000) + 9999)AS VARCHAR(5)) lnames,
DATEADD(YY,-110, GETDATE()) dob
FROM
(
SELECT S.memberids smid,M.memberid mmid
FROM #src S
LEFT JOIN #mstr M ON S.memberids = M.memberid
)C WHERE
C.smid IS NOT NULL
AND C.mmid IS NULL

Related

How to Select rows in a table with all matching conditions in joining table

I have two tables (table A) and (table B) and one table variable (#country) that will build the where condition for finding out the list of employees matching the countries.
TableA(empId, name)
(1,John),(2,Mary),(3,Harry)
TableB(empId, country)
(1,Australia),(1,US),(1,UK),(2,US)
For example, I need to select only those employees from TableA who have resides in both Australia and US. i.e. emp 1 (John). The query should be able to handle more countries in where clause if require. This depends on the number of countries in table variable #country.
I have tried many option including the following query but nothing seems to work.
DECLARE #country TABLE (
[country] [nvarchar](255) NOT NULL
);
insert into #country (country) values('Australia'),('US')
Select E.empID, EC.empID,EC.country from TableA E
INNER JOIN TableB EC on E.empID= EC.empID
Where EC.country = ALL(Select country from #country)
Could you please advise on how to write the best query to achieve this task? Please note that #country can have one or more countries.
Try:
SELECT E.empID
,EC.empID
,EC.country
FROM TableA E
INNER JOIN TableB EC
ON E.empID = EC.empID
WHERE EXISTS (
SELECT 1
FROM TableB EC_US
WHERE EC_US.empID = EC.empID
and EC_US.Country = 'US'
)
AND EXISTS (
SELECT 1
FROM TableB EC_Aus
WHERE EC_Aus.empID = EC.empID
and EC_Aus.Country = 'Australia'
)
Or:
SELECT E.empID
,EC.empID
,EC.country
FROM TableA E
INNER JOIN TableB EC
ON E.empID = EC.empID
WHERE EC.empID IN (
SELECT EC_Sub.empID
FROM TableB EC_Sub
WHERE EC_Sub.Country IN ('Australia','US')
GROUP BY EC_Sub.empID
HAVING COUNT(*) = 2
)
Try Now, added where clause. Change any value of #country and execute the query:
DECLARE #TableA TABLE (empId INT, [Name] VARCHAR(100))
INSERT INTO #TableA VALUES (1, 'John')
INSERT INTO #TableA VALUES (2, 'Mary')
INSERT INTO #TableA VALUES (3, 'Harry')
DECLARE #TableB TABLE (empID INT, country VARCHAR(100))
INSERT INTO #TableB VALUES (1, 'Australia')
INSERT INTO #TableB VALUES (1, 'UK')
INSERT INTO #TableB VALUES (2, 'US')
DECLARE #country TABLE ([country] [nvarchar](255) NOT NULL);
INSERT INTO #country (country) VALUES('Australia'),('US')
SELECT a.* , tb.country
FROM #TableA AS a
INNER JOIN (
SELECT b.empid,
COUNT(*) AS empInMultipleCountry
FROM #TableB b
GROUP BY
empid
) b
ON a.empId = b.empid
INNER JOIN #TableB AS tb
ON tb.empId = a.empId
WHERE empInMultipleCountry > 1
AND EXISTS (SELECT 1 FROM #country AS c WHERE c.country = tb.country)
Select E.empID, EC.empID,EC.country
from TableA E INNER JOIN TableB EC on E.empID= EC.empID
Where EC.country IN ('Australia','US');

Displaying multiple select in a single cell

Please help me to solve the following issue .
consider i have two tables in a Database
1.employee 2.Details
In employee table data will be
eid ename level
1 x 9th
2 y 10th
In Address Table data will be
AId eid location Adreess_type
1 1 india permananet
2 1 US Temporary
3 2 Japan permananet
4 2 China Temporary
I need output in the below format
eid ename fulllocation
1 X INDIA -US
2 y Japan-CHINA
Try this:
SELECT
e.eid,
e.name,
GROUP_CONCAT(a.location SEPARATOR '-') AS fulllocation
FROM
employee as e
INNER JOIN address as a
ON e.eid = a.eid
GROUP BY
e.eid
select employee.eid, employee.ename, t.fulllocation
from employee
inner join (select eid, group_concat(location SEPARATOR '-') as fulllocation from Address group by eid) t
on employee.eid = t.eid
Consider that GROUP_CONCAT have some limitations, what is this and how can change (if needed) it? please check documentation for this.
DECLARE #t1 TABLE
(
eid int NOT NULL,
ename varchar(50),
level varchar(50)
)
DECLARE #t2 TABLE
(
aid int NOT NULL,
eid int,
location varchar(50),
address_type varchar(50)
)
INSERT INTO #t1 SELECT 1, 'x', '9th'
INSERT INTO #t1 SELECT 2, 'y', '10th'
INSERT INTO #t2 SELECT 1, 1, 'india', 'permanent'
INSERT INTO #t2 SELECT 2, 1, 'US', 'temporary'
INSERT INTO #t2 SELECT 3, 2, 'Japan', 'permanent'
INSERT INTO #t2 SELECT 4, 2, 'China', 'temporary'
SELECT * FROM #t1
SELECT * FROM #t2
SELECT t1.eid, t1.ename, t2.fullLocation
FROM #t1 AS t1
INNER JOIN (
SELECT eid, COUNT(*) AS noofrecs
, fullLocation = LTRIM(RTRIM(ISNULL(STUFF(
(
SELECT DISTINCT '-' + CAST(t2.location as nvarchar(max))
FROM #t2 t2
WHERE t1.eid = t2.eid
FOR XML PATH (''), TYPE).value('.', 'nvarchar(max)'
), 1, 1, ''), '')))
FROM #t2 as t1
GROUP BY eid
) AS t2
ON t1.eid = t2.eid
DECLARE #t1 TABLE
(
eid int NOT NULL,
ename varchar(50),
level varchar(50)
)
DECLARE #t2 TABLE
(
aid int NOT NULL,
eid int,
location varchar(50),
address_type varchar(50)
)
INSERT INTO #t1 SELECT 1, 'x', '9th'
INSERT INTO #t1 SELECT 2, 'y', '10th'
INSERT INTO #t2 SELECT 1, 1, 'india', 'permanent'
INSERT INTO #t2 SELECT 2, 1, 'US', 'temporary'
INSERT INTO #t2 SELECT 3, 2, 'Japan', 'permanent'
INSERT INTO #t2 SELECT 4, 2, 'China', 'temporary'
SELECT * FROM #t1
SELECT * FROM #t2
SELECT b.eid,b.ename
, STUFF((SELECT '_ ' + a.location FROM #t2 A
Where A.eid=B.eid FOR XML PATH('')),1,1,'') As fulllocation
From #t1 B
Group By b.eid,b.ename
In order to preserve the order of the locations, you could work along
SELECT
e.eid
, e.ename
, CONCAT_WS('-', p.location, t.location) AS fulllocation
FROM Employee e
JOIN Address p
ON e.eid = p.eid
AND p.address_type = 'permananet'
JOIN Address t
ON e.eid = t.eid
AND t.address_type = 'Temporary'
;
See it in action: SQL Fiddle.
Please comment if and as this requrires adjustment / further detail.

How to get the ID from the table which is not mention EndDate

How to get the name from the table which is not having EndDate
in the above pic i need to get D and G details from the table ,
( To understand mOre:
A, C,D,G are having end date, and A, C are again started, but D and G is not started, so from the query i need to get the name D and G
the code i used is not works for it
DECLARE #T AS TABLE
(
SubInventoryID int ,
SubInventoryName varchar(20),
RolesName varchar(20),
StartDate date,
EndDate date
)
INSERT INTO #T VALUES
(30,'RIF-Teller','Teller', '2016-12-27', '2017-01-23'),
(30,'RIF-Teller','Teller', '2016-12-08', NULL),
(30,'RIF-Teller','Teller', '2017-01-02', '2017-01-05'),
(31,'RIF-Teller','Teller', '2017-01-05', NULL),
(24,'MHQ-Teller','Teller', '2016-09-20', '2017-01-23'),
(24,'MHQ-Teller','Teller', '2016-08-01', '2017-01-05'),
(24,'MHQ-Teller','Teller', '2017-01-05', NULL)
Query
SELECT UP.SubInventoryID,S.SubInventoryName SubInventoryName,RolesName,UP.StartDate StartDate,
UP.EndDate EndDate , case when UP.EndDate IS null then 'Occupied' else 'Closed' End As Vacancy
FROM [View_Alx_UserPosition] UP
Inner join ALX_Branches B ON B.BranchID= UP.BranchID
Inner join ALX_SubInventories S ON S.SubInventoryID=UP.SubInventoryID WHERE UP.RolesName Like '%Teller%'
union
SELECT distinct(UP.SubInventoryID),S.SubInventoryName SubInventoryName, '' FullName, '' RolesName,NUll StartDate,
NUll EndDate,'Free' as vacancy
FROM [View_Alx_UserPosition] UP
Inner join ALX_Branches B ON B.BranchID= UP.BranchID
Inner join ALX_SubInventories S ON S.SubInventoryID=UP.SubInventoryID
WHERE UP.EndDate IS NOT NULL ANd UP.RolesName Like '%Teller%'
AND NOT EXISTS
(
SELECT 1
FROM [View_Alx_UserPosition] UP1
WHERE UP1.SubInventoryID = UP.SubInventoryID
AND UP1.StartDate >= UP.EndDate
-- AND UP1.EndDate IS NOT NULL
)
Update
Create and populate sample table (Please save us this step in your future questions)
DECLARE #T AS TABLE
(
ID int identity(1,1),
Name char(1),
StartDate date,
EndDate date
)
INSERT INTO #T VALUES
('A', '2016-04-04', '2017-04-03'),
('B', '2016-04-04', NULL),
('C', '2016-04-04', '2017-04-03'),
('D', '2016-04-04', '2017-04-03'),
('E', '2016-04-04', NULL),
('F', '2016-04-04', NULL),
('G', '2016-04-04', '2017-04-03'),
('C', '2017-04-03', NULL),
('A', '2017-04-03', NULL)
The query:
SELECT Name
FROM #T vu1
WHERE EndDate IS NOT NULL
AND NOT EXISTS
(
SELECT 1
FROM #T vu2
WHERE vu2.Name = vu1.Name
AND vu2.StartDate >= vu1.EndDate
)
Results:
Name
D
G
First version
Assuming I understand the question, this should do the trick:
SELECT Name
FROM View_User vu1
WHERE EndDate IS NOT NULL
AND NOT EXISTS
(
SELECT 1
FROM View_User vu2
WHERE vu2.Name = vu1.Name
AND vu2.StartDate >= vu1.EndDate
AND vu2.EndDate IS NOT NULL
)

CTE Recursive Queries

I have a table with records of employees that shows a relationship of employees and who they report to:
From_ID position TO_ID position
----------------------------------------
1 Lowest_employee 3 employee
3 employee 4 employee
4 employee 5 BOSS
2 Lowest_employee 6 employee
6 employee 3 employee
10 Lowest_employee 50 BOSS2
I would like to show results that look like this, with the employee / boss IDs:
EmployeeID BossID
--------------------
1 5
2 5
10 50
This means employees 1 and 2 report to ID 5 and employee 10 reports to another boss with ID 50.
I know I need to use CTE and Recursive Queries, but cannot understand how it can be done, I'm newer to CTE Recursive Queries.
I read this article but it doesn't make any sense to me MS link
Any help with the query required to achieve this would be useful.
This includes setting up test data, however I think this is what you want:
Test Data:
DECLARE #Table TABLE
(
From_ID int,
TO_ID int
)
INSERT INTO #Table VALUES(1,3)
INSERT INTO #Table VALUES(3,4)
INSERT INTO #Table VALUES(4,5)
INSERT INTO #Table VALUES(2,6)
INSERT INTO #Table VALUES(6,3)
INSERT INTO #Table VALUES(10,50)
Query to get answer:
;WITH Hierarchy (Employee, Superior, QueryLevel)
AS
(
--root is all employees that have no subordinates
SELECT E.From_ID, E.TO_ID, 1
FROM #Table E
LEFT
JOIN #Table S
ON S.TO_ID = E.From_ID
WHERE S.TO_ID IS NULL
--recurse up tree to final superior
UNION ALL
SELECT H.Employee, S.TO_ID, H.QueryLevel + 1
FROM Hierarchy H
JOIN #Table S
ON S.From_ID = H.Superior
)
SELECT Employee, Superior
FROM
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY Employee ORDER BY QueryLevel DESC) AS RowNumber
FROM Hierarchy
) H
WHERE RowNumber = 1
Essentially, this works by :
1) get all employees with no reportees (the root)
2) recurses up through the bosses, recording the 'level'
3) use over/partition to select only the 'final' boss
WITH q (employee, boss) AS
(
SELECT fromId, toId
FROM mytable
WHERE fromId NOT IN
(
SELECT toId
FROM mytable
)
UNION ALL
SELECT employee, toId
FROM q
JOIN mytable t
ON t.fromId = boss
)
SELECT *
FROM q
WHERE boss NOT IN
(
SELECT fromId
FROM mytable
)
You could try something like this?
DECLARE #Employees TABLE (
EmployeeId INT,
PositionName VARCHAR(50),
ReportsToId INT);
INSERT INTO #Employees VALUES (1, 'Driver', 3);
INSERT INTO #Employees VALUES (3, 'Head of Driving Pool', 4);
INSERT INTO #Employees VALUES (4, 'Corporate Flunky', 5);
INSERT INTO #Employees VALUES (2, 'Window Cleaner', 6);
INSERT INTO #Employees VALUES (6, 'Head of Office Services', 3);
INSERT INTO #Employees VALUES (10, 'Minion', 50);
INSERT INTO #Employees VALUES (5, 'BOSS', NULL);
INSERT INTO #Employees VALUES (50, 'BOSS2', NULL);
WITH Employees AS (
SELECT
EmployeeId,
1 AS [Level],
EmployeeID AS [Path],
ISNULL(ReportsToId, EmployeeId) AS ReportsToId
FROM
#Employees
WHERE
ReportsToId IS NULL
UNION ALL
SELECT
e.EmployeeID,
x.[Level] + 1 AS [Level],
x.[Path] + e.EmployeeID AS [Path],
x.ReportsToId
FROM
#Employees e
INNER JOIN Employees x ON x.EmployeeID = e.ReportsToId)
SELECT
ec.EmployeeId,
e.PositionName,
ec.[Level],
CASE WHEN ec.ReportsToId = ec.EmployeeId THEN NULL ELSE ec.ReportsToId END AS ReportsToId --Can't really report to yourself
FROM
Employees ec
INNER JOIN #Employees e ON e.EmployeeId = ec.EmployeeId
ORDER BY
ec.[Path];

How to to branch the query among multiple tables

If i have 4 tables like this :
1- mainemployees
emp_id
email
type (0-->ext,1-->internal,2--->special)
2-externalemp
emp_id
name
3-internalemp
emp_id
name
4-specialemp
emp_id
name
Now i want all the employee names
where the emp_id of the first table = the emp_id of one of the last three tables
how to do do this (performance wise).
select nvl(ee.name, nvl(ie.name, se.name))
from mainemployees me
left join
externalemp ee
on ee.emp_id = me.emp_id
left join
internalemp ie
on ie.emp_id = me.emp_id
left join
specialem se
on se.emp_id = me.emp_id
where nvl(ee.name, '') <> ''
or nvl(ie.name, '') <> ''
or nvl(se.name, '') <> ''
create table mainemployees (emp_id int, emp_type int)
create table externalemp (emp_id int, name nvarchar(20))
create table internalemp (emp_id int, name nvarchar(20))
create table specialemp (emp_id int, name nvarchar(20))
insert into mainemployees (emp_id, emp_type) values (1, 0), (2, 1), (3, 2)
insert into externalemp (emp_id, name) values (1, 'external')
insert into internalemp (emp_id, name) values (2, 'internal')
insert into specialemp (emp_id, name) values (3, 'special')
And for the query you use CASE to select right column
SELECT
CASE me.emp_type WHEN 0 THEN ee.name WHEN 1 THEN ie.name WHEN 2 THEN se.name END
FROM mainemployees as me
LEFT JOIN externalemp as ee ON me.emp_id = ee.emp_id
LEFT JOIN internalemp as ie ON me.emp_id = ie.emp_id
LEFT JOIN specialemp as se ON me.emp_id = se.emp_id