I have a simple employee table that I want to display in a particular order. I want to find out if there are alternative solutions (or better solution) to achieve the same result. The T-SQL script is shown below:
CREATE TABLE Employee(
EmployeeID INT IDENTITY(1,1) NOT NULL,
EmployeeName VARCHAR(255) NULL,
ManagerID INT NULL,
EmployeeType VARCHAR(20) NULL
)
GO
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Brad',5,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('James',3,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Ray',null,'Manager');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Tom',8,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Neil',8,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Rob',5,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Paul',5,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Tim',null,'Manager');
GO
SELECT e.EmployeeType, e.EmployeeName AS [Team Member],
(SELECT e2.EmployeeName FROM Employee AS e2 WHERE e2.EmployeeID = e.ManagerID) AS Manager
FROM Employee AS e
ORDER BY e.EmployeeType, e.EmployeeID
The rows are ordered by manger first, then employeeID. My concerns is that in my solution, it is sorted by the EmployeeType column. Would it be better to sort it by ManagerId column instead? Because the EmployeeType could be changed in the future, say from Manager to Team Manager, which might cause different result!
If the criteria for a manager is that column ManangerID is null, you can use a case in the order by to get the managers first.
SELECT e.EmployeeType, e.EmployeeName AS [Team Member],
(SELECT e2.EmployeeName FROM Employee AS e2 WHERE e2.EmployeeID = e.ManagerID) AS Manager
FROM Employee AS e
ORDER BY CASE WHEN E.ManagerID IS NULL THEN 0 ELSE 1 END, e.EmployeeID
If you want to set the sort depending on EmployeeType you can do like this
SELECT e.EmployeeType, e.EmployeeName AS [Team Member],
(SELECT e2.EmployeeName FROM Employee AS e2 WHERE e2.EmployeeID = e.ManagerID) AS Manager
FROM Employee AS e
ORDER BY
CASE EmployeeType
WHEN 'Manager' THEN 0
WHEN 'Memeber' THEN 1
ELSE 2
END, e.EmployeeID
Or you can use a table with EmpType's that define the sort order
CREATE TABLE EmpType(EmployeeType VARCHAR(20) PRIMARY KEY, SortOrder INT)
GO
INSERT INTO EmpType VALUES('Manager', 1)
INSERT INTO EmpType VALUES('Memeber', 2)
SELECT e.EmployeeType, e.EmployeeName AS [Team Member],
(SELECT e2.EmployeeName FROM Employee AS e2 WHERE e2.EmployeeID = e.ManagerID) AS Manager
FROM Employee AS e
LEFT OUTER JOIN EmpType as et
ON e.EmployeeType = et.EmployeeType
ORDER BY et.SortOrder, e.EmployeeID
There are no "universal" solution. In your example, not only Employee type but Manager_id can change too.
If you need to get similar results, you should order to hierarchy level. In this case first will be manager set, then employee. If Employee will have another managerId, it will stay at the same level.
Related
I have a query that works but I suspect I'm doing this inefficiently. Is there a more elegant approach to find the top salary in each department and the employee that earns it?
I'm doing a cte to find the max salary per dept id and then join that up with the employee data by matching salary and dept id. I have code below to build/populate the tables and the query at the end.
CREATE TABLE employee (
emplid SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
salary FLOAT NOT NULL,
depid INTEGER
);
INSERT INTO employee (name, salary, depid)
VALUES
('Chris',23456.99,1),
('Bob',98756.34,1),
('Malin',34567.22,2),
('Lisa',34967.73,2),
('Deepak',88582.22,3),
('Chester',99487.41,3);
CREATE TABLE department (
depid SERIAL PRIMARY KEY,
deptname VARCHAR NOT NULL
);
INSERT INTO department (deptname)
VALUES
('Engineering'),
('Sales'),
('Marketing');
--top salary by department
WITH cte AS (
SELECT d.depid, deptname, MAX(salary) AS maxsal
FROM employee e
JOIN department d ON d.depid = e.depid
GROUP BY d.depid, deptname
)
SELECT cte.deptname, e.name, cte.maxsal
FROM cte
JOIN employee e ON cte.depid = e.depid
AND e.salary = cte.maxsal
ORDER BY maxsal DESC;
Here is the target result:
"Marketing" "Chester" "99487.41"
"Engineering" "Bob" "98756.34"
"Sales" "Lisa" "34967.73"
In Postgres this can solved using the distinct on () operator:
SELECT distinct on (d.depid) d.depid, deptname, e.name, e.salary AS maxsal
FROM employee e
JOIN department d ON d.depid = e.depid
order by d.depid, e.salary desc;
Or you can use a window function:
select depid, deptname, emp_name, salary
from (
SELECT d.depid,
deptname,
e.name as emp_name,
e.salary,
max(e.salary) over (partition by d.depid) AS maxsal
FROM employee e
JOIN department d ON d.depid = e.depid
) t
where salary = maxsal;
Online example: https://rextester.com/MBAF73582
You should have an index:
create index employee_depid_salary_desc_idx on employee(depid, salary desc);
And then use the following query that can use the index:
select
depid,
deptname,
(
select
emplid
from employees
where depid=department.depid
order by salary desc
limit 1
) as max_salaried_emplid
from department;
(A join for retrieving data from the emplid left as an exercise for the reader).
The same name may appear in multiple rows of table1. I would like to enumerate all names in sequential order 1, 2, ... One way to do so is to
create new table with name as primary key and id as serial type.
Select name from table1 and insert it into table2 only when it doesn't exist
table1 (name vchar(50), ...)
table2 (name vchar(50) primary key, id serial)
insert into table2(name)
select name
from table1 limit 9
where not exists (select name from table2 where name = table1.name)
This doesn't work. How to fix it?
Just select distinct values:
insert into table2(name)
select distinct name
from table1
order by name;
I have this self-referencing table where-in I should get the upline and downline and hierarchy levels without the Parent ID provided.
Any ideas?
Have you tried using recursive CTE?
for example:
assume you have table tbl(EmpId, Name, MngrId) which has self-referencing relationship
create table tbl
(
EmpId int not null,
Name nvarchar(100),
MngrId int null)
insert into tbl(EmpId, Name, MngrId)
values (1,'Adel',Null),
(2,'Ali',1),
(3,'Shaban',1),
(4,'Mark',3),
(5,'John',3),
(6,'Tony',Null),
(7,'Peter',6)
You can create some view like that:
create view Employees
Begin
with cte
as
(
Select EmpId,Name, Null as MngrId, cast(null as nvarchar(100)) as MngrName, 1 as EmpLevel
from tbl
where MngrId is Null
Union All
Select t.EmpId, t.Name, c.EmpId as MngrId, c.Name as MngrName, c.EmpLevel + 1 as EmpLevel
from tbl t
inner join cte c
on t.MngrId = c.EmpId
)
Select *
from cte
order by EmpLevel, EmpId
End
You can now use EmpLevel to jump between different levels and MngrName to get information about parent node
I need to copy content from one table to itself and related tables... Let me schematize the problem. Let's say I have two tables:
Order
OrderID : int
CustomerID : int
OrderName : nvarchar(32)
OrderItem
OrderItemID : int
OrderID : int
Quantity : int
With the PK being autoincremental.
Let's say I want to duplicate the content of one customer to another. How do I do that efficiently?
The problem are the PKs. I would need to map the values of OrderIDs from the original set of data to the copy in order to create proper references in OrderItem. If I just select-Insert, I won't be able to create that map.
Suggestions?
For duplicating one parent and many children with identities as the keys, I think the OUTPUT clause can make things pretty clean (SqlFiddle here):
-- Make a duplicate of parent 1, including children
-- Setup some test data
create table Parents (
ID int not null primary key identity
, Col1 varchar(10) not null
, Col2 varchar(10) not null
)
insert into Parents (Col1, Col2) select 'A', 'B'
insert into Parents (Col1, Col2) select 'C', 'D'
insert into Parents (Col1, Col2) select 'E', 'F'
create table Children (
ID int not null primary key identity
, ParentID int not null references Parents (ID)
, Col1 varchar(10) not null
, Col2 varchar(10) not null
)
insert into Children (ParentID, Col1, Col2) select 1, 'g', 'h'
insert into Children (ParentID, Col1, Col2) select 1, 'i', 'j'
insert into Children (ParentID, Col1, Col2) select 2, 'k', 'l'
insert into Children (ParentID, Col1, Col2) select 3, 'm', 'n'
-- Get one parent to copy
declare #oldID int = 1
-- Create a place to store new ParentID
declare #newID table (
ID int not null primary key
)
-- Create new parent
insert into Parents (Col1, Col2)
output inserted.ID into #newID -- Capturing the new ParentID
select Col1, Col2
from Parents
where ID = #oldID -- Only one parent
-- Create new children using the new ParentID
insert into Children (ParentID, Col1, Col2)
select n.ID, c.Col1, c.Col2
from Children c
cross join #newID n
where c.ParentID = #oldID -- Only one parent
-- Show some output
select * from Parents
select * from Children
Do you have to have the primary keys from table A as primaries in Table B? If not you can do a select statement with an insert into. Primary Key's are usually int's that start from an ever increasing seed (identity). Going around this and declaring an insert of this same data problematically has the disadvantage of someone thinking this is a distinct key set on this table and not a 'relationship' or foreign key value.
You can Select Primary Key's for inserts into other tables, just not themselves.... UNLESS you set the 'identity insert on' hint. Do not do this unless you know what this does as you can create more problems than it's worth if you don't understand the ramifications.
I would just do the ole:
insert into TableB
select *
from TableA
where (criteria)
Simple example (This assumes SQL Server 2008 or higher). My bad I did not see you did not list TSQL framework. Not sure if this will run on Oracle or MySql.
declare #Order Table ( OrderID int identity primary key, person varchar(8));
insert into #Order values ('Brett'),('John'),('Peter');
declare #OrderItem Table (orderItemID int identity primary key, OrderID int, OrderInfo varchar(16));
insert into #OrderItem
select
OrderID -- I can insert a primary key just fine
, person + 'Stuff'
from #Order
select *
from #Order
Select *
from #OrderItem
Add an extra helper column to Order called OldOrderID
Copy all the Order's from the #OldCustomerID to the #NewCustomerID
Copy all of the OrderItems using the OldOrderID column to help make the relation
Remove the extra helper column from Order
ALTER TABLE Order ADD OldOrderID INT NULL
INSERT INTO Order (CustomerID, OrderName, OldOrderID)
SELECT #NewCustomerID, OrderName, OrderID
FROM Order
WHERE CustomerID = #OldCustomerID
INSERT INTO OrderItem (OrderID, Quantity)
SELECT o.OrderID, i.Quantity
FROM Order o INNER JOIN OrderItem i ON o.OldOrderID = i.OrderID
WHERE o.CustomerID = #NewCustomerID
UPDATE Order SET OldOrderID = null WHERE OldOrderID IS NOT NULL
ALTER TABLE Order DROP COLUMN OldOrderID
IF the OrderName is unique per customer, you could simply do:
INSERT INTO [Order] ([CustomerID], [OrderName])
SELECT
2 AS [CustomerID],
[OrderName]
FROM [Order]
WHERE [CustomerID] = 1
INSERT INTO [OrderItem] ([OrderID], [Quantity])
SELECT
[o2].[OrderID],
[oi1].[Quantity]
FROM [OrderItem] [oi1]
INNER JOIN [Order] [o1] ON [oi1].[OrderID] = [o1].[OrderID]
INNER JOIN [Order] [o2] ON [o1].[OrderName] = [o2].[OrderName]
WHERE [o1].[CustomerID] = 1 AND [o2].[CustomerID] = 2
Otherwise, you will have to use a temporary table or alter the existing Order table like #LastCoder suggested.
I have two tables (one for quarter one, one for quarter two), each of which contains employees who have bonus in that quarter. Every employee has a unique id in the company.
I want to get all employees who has bonus in either q1 or q2. No duplicate employee is needed. Both Id, and Amount are required.
Below is my solution, I want to find out if there is a better solution.
declare #q1 table (
EmployeeID int identity(1,1) primary key not null,
amount int
)
declare #q2 table (
EmployeeID int identity(1,1) primary key not null,
amount int
)
insert into #q1
(amount)
select 1
insert into #q1
(amount)
select 2
select * from #q1
insert into #q2
(amount)
select 1
insert into #q2
(amount)
select 11
insert into #q2
(amount)
select 22
select * from #q2
My Solution:
;with both as
(
select EmployeeID
from #q1
union
select EmployeeID
from #q2
)
select a.EmployeeID, a.amount
from #q1 as a
where a.EmployeeID in (select EmployeeID from both)
union all
select b.EmployeeID, b.amount
from #q2 as b
where b.EmployeeID in (select EmployeeID from both) and b.EmployeeID NOT in (select EmployeeID from #q1)
Result:
EmployeeID, Amount
1 1
2 2
3 22
SELECT EmployeeID, Name, SUM(amount) AS TotalBonus
FROM
(SELECT EmployeeID, Name, amount
from #q1
UNION ALL
SELECT EmployeeID, Name, amount
from #q2) AS all
GROUP BY EmployeeID, Name
The subselect UNIONS both tables together. The GROUP BY gives you one row per employee and the SUM means that if someone got lucky in both qs then you get the total. I'm guessing that's the right thing for you.
try this one:
SELECT EmployeeID
FROM EmployeeList
WHERE EmployeeID IN
(SELECT EmployeeID From QuarterOne
UNION
SELECT EmployeeID From QuarterTwo)
OR by using JOIN
SELECT EmployeeID
FROM EmployeeList a INNER JOIN QuarterTwo b
ON a.EmployeeID = b.EmployeeID
INNER JOIN QuarterTwo c
ON a.EmployeeID = c.EmployeeID
This will return all EmployeeID that has record in either quarter.
Try:
SELECT DISTINCT q1.EmployeeID --- Same as q2.EmployeeID thanks to the join
, q1.EmployeeName -- Not defined in OP source.
FROM #q1 AS q1
CROSS JOIN #q2 AS q2
WHERE q1.amount IS NOT NULL
OR q2.amount IS NOT NULL