How to to branch the query among multiple tables - sql

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

Related

Join two tables on multiple conditions - SQL Server

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

I need to migrate data from one old table to a new table by storing appropriate CityId instead CityName

I'm migrating data from one table to another table in SQL Server, In this process what I need to do is "I have 10 columns in old table one column is 'CityName' which is varchar and in the new table, I have a column 'CityId' which is an integer. And I have other table which has data about city id and names. I need store the appropriate cityId in new table instead of CityName. Please help me. Thanks in advance.
You'll need to join the source table to the CityName field in the city information table:
INSERT INTO dbo.Destination (CityID, OtherStuff)
SELECT t1.CityID, t2.OtherStuff
FROM CityInformationTable t1
INNER JOIN SourceTable t2
ON t1.CityName = t2.CityName
Below should give you an idea, you need to inner join to your look up table to achieve this.
declare #t_cities table (Id int, City nvarchar(20))
insert into #t_cities
(Id, City)
values
(1, 'London'),
(2, 'Dublin'),
(3, 'Paris'),
(4, 'Berlin')
declare #t table (City nvarchar(20), SomeColumn nvarchar(10))
insert into #t
values
('London', 'AaaLon'),
('Paris', 'BeePar'),
('Berlin', 'CeeBer'),
('London', 'DeeLon'),
('Dublin', 'EeeDub')
declare #finalTable table (Id int, SomeColumn nvarchar(10))
insert into #finalTable
select c.Id, t.SomeColumn
from #t t
join #t_cities c on c.City = t.City
select * from #finalTable
Output:
Id SomeColumn
1 AaaLon
3 BeePar
4 CeeBer
1 DeeLon
2 EeeDub

How to count records in SQL

Edit: Schema taken/extrapolated from comment below
create table #employees
(
Emp_ID int,
Name varchar(50),
Dept_ID int,
);
create table #departments
(
Dept_ID int,
Dept_Name varchar(50)
);
How do I count the number of employees from table employees that work in each department in table departments and include all departments that have no employees working in them.
Welcome to SO.
This problem is quite simple to solve. The steps would be as follows:
Join the Departments table to the Employees table on the Dept_ID column.
SELECT the Dept_ID and Count() and GROUP BY the Dept_ID field.
In order to return Departments without employees you need to LEFT JOIN this aggregation to the Departments table on the Dept_ID column.
In order to return the value of 0 for departments without employees, use the ISNULL() function.
Please see the below sample script using your schema. Note that this script is written in T-SQL, as you did not mention your server type.
create table #employees
(
Emp_ID int,
Name varchar(50),
Dept_ID int,
);
create table #departments
(
Dept_ID int,
Dept_Name varchar(50)
);
insert into #employees
select 1, 'Pim', 1
union all
select 2, 'Salma', 2;
insert into #departments
select 1, 'IT'
union all
select 2, 'Marketing'
union all
select 3, 'Design'
select
d1.Dept_Name
,isnull(d2.EmployeeCount, 0) as EmployeeCount
from
#departments d1
left join
(
select
d.Dept_ID
,count(e.Dept_ID) as EmployeeCount
from
#departments d
join
#employees e
on e.Dept_ID = d.Dept_ID
group by
d.Dept_ID
)
d2
on d2.Dept_ID = d1.Dept_ID
drop table #employees
drop table #departments
As you have supplied no data please try below and see if this works for you
Create the tables, I don't know if this is similar to your table structure
CREATE TABLE tbl_EMPLOYEES (Empl_Name nvarchar(20), Dept nvarchar(15))
CREATE TABLE tbl_DEPARTMENT (Dept nvarchar(15))
Populate these tables
INSERT INTO tbl_EMPLOYEES Values ('James', 'Finance')
INSERT INTO tbl_EMPLOYEES Values ('Tim', 'HR')
INSERT INTO tbl_EMPLOYEES Values ('Sally', 'Finance')
INSERT INTO tbl_EMPLOYEES Values ('Bob', 'Sales')
INSERT INTO tbl_EMPLOYEES Values ('Sam', 'HR')
INSERT INTO tbl_EMPLOYEES Values ('James', 'Finance')
INSERT INTO tbl_DEPARTMENT Values ('Finance')
INSERT INTO tbl_DEPARTMENT Values ('HR')
INSERT INTO tbl_DEPARTMENT Values ('Sales')
INSERT INTO tbl_DEPARTMENT Values ('IT')
This query will give you the number of people in each department
SELECT Dept, Count(Dept) AS Count
FROM
(
SELECT Dept
FROM tbl_EMPLOYEES
) AS Blah_Blah
GROUP BY Dept

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');

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];