How to count records in SQL - 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

Related

using FUNCTION -SQL Server query to find the current post of the employee [duplicate]

This question already has answers here:
Retrieving last record in each group from database - SQL Server 2005/2008
(2 answers)
Closed 2 years ago.
Can you tell how to find the last updated post of the employee from the table. Two tables :Employee and EmployeeDetails. Employee fields are: EmployeeID ,EmployeeName EmployeeDetails fields are: EmployeeID, Designation, PromotionDate.
How to find the current Designation of an employee if we give an EmployeeID. Using function. I want to create a simple function to get the Designation according to the current Designation (Latest PromotionDate ) using the EmployeeID, then join with Employee table.
DROP TABLE IF EXISTS Employee
CREATE TABLE Employee
(
EmployeeID INT NOT NULL PRIMARY KEY IDENTITY(1000,1),
EmployeeName VARCHAR(25)
)
INSERT INTO Employee VALUES('AAA');
INSERT INTO Employee VALUES('LAAA');
INSERT INTO Employee VALUES('RSSS');
INSERT INTO Employee VALUES('SEEE');
INSERT INTO Employee VALUES('CFFF');
INSERT INTO Employee VALUES('SEEEW');
INSERT INTO Employee VALUES('MCCC');
INSERT INTO Employee VALUES('DERR');
INSERT INTO Employee VALUES('DERR');
INSERT INTO Employee VALUES('DERW');
SELECT * FROM Employee
DROP TABLE EmployeeDetails
CREATE TABLE EmployeeDetails
(
EmployeeID INT FOREIGN KEY REFERENCES Employee(EmployeeID),
Designation VARCHAR(25),
PromotionDate Date
)
INSERT INTO EmployeeDetails VALUES(1000,'www','2020-11-20');
INSERT INTO EmployeeDetails VALUES(1000,'qqq','2020-01-23');
INSERT INTO EmployeeDetails VALUES(1009,'qqq','2020-09-20');
SELECT * FROM EmployeeDetails
SELECT
E.EmployeeID,
E.EmployeeName,
ED.Designation, ED.PromotionDate
FROM
Employee E
JOIN
EmployeeDetails ED ON E.EmployeeID = ED.EmployeeID
I wrote a function for this, but I don't know how to incorporate it with the query:
CREATE FUNCTION GetOnlyTheCurrentPost
( #EmpID INT)
RETURNS DATE
AS
BEGIN
DECLARE #PromoDate DATE
SELECT #PromoDate= MAX(PromotionDate)
FROM EmployeeDetails
WHERE EmployeeID = #EmpID
RETURN(#PromoDate)
END
I changed the function like this as below
ALTER FUNCTION [dbo].[GetOnlyTheCurrentPost]
( #PromoDate DATE)
RETURNS DATE
AS
BEGIN
SELECT #PromoDate= MAX(PromotionDate)
FROM EmployeeDetails
RETURN(#PromoDate)
END
SELECT
E.EmployeeID,
ED.Designation,[dbo].[GetOnlyTheCurrentPost](ED.PromotionDate) AS LatestPost
FROM
Employee E
JOIN
EmployeeDetails ED
ON E.EmployeeID = ED.EmployeeID
This will show all the records not only the latest post but also every records.
Again, I changed my function. I want to get the current Designation, if i give the EmployeeID Like, SELECT [dbo].[GetOnlyTheCurrentDesignation](1011). Output should be printed according to the given corresponding EmployeeID Output : ProjectManager
ALTER FUNCTION GetOnlyTheCurrentDesignation
(#EmpID INT)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #Designation VARCHAR(25)
SELECT #Designation=Designation, MAX(PromotionDate)
FROM EmployeeDetails
WHERE EmployeeID = #EmpID
RETURN(#Designation)
END
Please tell me a solution to fix this
Try something like this:
SELECT
E.EmployeeID,
E.EmployeeName,
ED.Designation, ED.PromotionDate
FROM
Employee E
JOIN
(SELECT * FROM EmployeeDetails ED2
WHERE PromotionDate = (SELECT MAX(PromotionDate)
FROM EmployeeDetails
WHERE EmployeeID = ED2.EmployeeID)) ED
ON E.EmployeeID = ED.EmployeeID

Self Join in SQL

I have an Employee table with few records in it, from which I want to get all the employees who work in the department where employee "scott" works and those with department number as 20.
can you give a try on the following query and let me know if this worked for you
select * from employee where deptno in (select deptno from employee where name ='scott')
Not very Descriptive, But u may try this as per the Information u've provided,
Create Table #Employees
(Id int,
EmpName nvarchar(20),
Deptno int
)
Insert into #Employees Values(1,'Henry',10)
Insert into #Employees Values(2,'Mark',20)
Insert into #Employees Values(3,'Scott',20)
Insert into #Employees Values(4,'David',10)
Insert into #Employees Values(5,'Peter',30)
Insert into #Employees Values(6,'Mary',20)
Insert into #Employees Values(7,'John',30)
Select EmpName From #Employees
Where Deptno In(Select Deptno from #Employees Where EmpName = 'Scott')
And for Complete information of Employees,
Select * From #Employees
Where Deptno In(Select Deptno from #Employees Where EmpName = 'Scott')
For example the following query returns employee names and their manager names for whom they are working. Copy & Paste the following sql, I think it may help the purpose.
Create table Emp
(
empid int primary key,
name varchar(50),
mgrid int
)
Insert into Emp(empid,name,mgrid)
values (1001,'Manish Agrahari',1001);
Insert into Emp(empid,name,mgrid)values (1002,'Deepti',1001);
Insert into Emp(empid,name,mgrid)values (1003,'Amit',1001);
Insert into Emp(empid,name,mgrid)values (1004,'Sandy',1002);
Insert into Emp(empid,name,mgrid)values (1005,'Ankit',1003);
Insert into Emp(empid,name,mgrid)values (1006,'Kapil',1002);
Run The following SQL and see the result:
SELECT e.empid, e.name, m.name "Manager" FROM Emp e, Emp m WHERE e.mgrid=m.empid;

How to solve single-row subquery returns more than one row

I have an employee table and it contains salary table. I want to give %10 increase to all current employees. I tried to update all employees' salary dates to specific date but I encountered problem with single-row subquery.
My database like this:
CREATE TYPE TEMPORAL_VARCHAR AS OBJECT (
VALID_TIME_LOWER_BOUND DATE,
VALID_TIME_UPPER_BOUND DATE,
VALUE_PART VARCHAR2(50) );
CREATE TYPE TEMPORAL_NUMBER AS OBJECT (
VALID_TIME_LOWER_BOUND DATE,
VALID_TIME_UPPER_BOUND DATE,
VALUE_PART NUMBER );
CREATE TYPE NAME_TYPE AS TABLE OF TEMPORAL_VARCHAR;
CREATE TYPE ADDRESS_TYPE AS TABLE OF TEMPORAL_VARCHAR;
CREATE TYPE DEPARTMENT_TYPE AS TABLE OF TEMPORAL_VARCHAR;
CREATE TYPE MANAGER_TYPE AS TABLE OF TEMPORAL_VARCHAR;
CREATE TYPE SALARY_TYPE AS TABLE OF TEMPORAL_NUMBER;
CREATE TABLE EMPLOYEE (
SSN NUMBER primary key,
NAME NAME_TYPE,
ADDRESS ADDRESS_TYPE ,
BIRTH_DATE DATE,
MANAGER MANAGER_TYPE ,
DEPARTMENT DEPARTMENT_TYPE,
SALARY SALARY_TYPE
)
NESTED TABLE NAME STORE AS NAME_TABLE,
NESTED TABLE ADDRESS STORE AS ADDRESS_TABLE,
NESTED TABLE MANAGER STORE AS MANAGER_TABLE,
NESTED TABLE DEPARTMENT STORE AS DEPARTMENT_TABLE,
NESTED TABLE SALARY STORE AS SALARY_TABLE
;
How to solve this problem? I tried to do this
UPDATE TABLE(
SELECT E.SALARY
FROM EMPLOYEE E
) SAL
SET SAL.VALID_TIME_UPPER_BOUND = '11.16.2015'
WHERE SAL.VALID_TIME_UPPER_BOUND = TO_DATE('12.31.9999','MM.DD.YYYY');
1st it can be duplicate
2nd see sample here
3rd in your code you need bring the where condition into select
UPDATE TABLE(
SELECT E.SALARY
FROM EMPLOYEE E
WHERE ssn in (SELECT ssn FROM EMPLOYEE e
WHERE to_date('01.01.2015','mm.dd.yyyy') in (
SELECT VALID_TIME_UPPER_BOUND FROM TABLE(e.salary)
)
)
) SAL
SET SAL.VALID_TIME_UPPER_BOUND = to_date('01.01.9999','mm.dd.yyyy')
test data
INSERT INTO EMPLOYEE(SSN, salary) values (1, SALARY_TYPE ());
INSERT INTO EMPLOYEE(SSN, salary) values (2, SALARY_TYPE ());
INSERT INTO EMPLOYEE(SSN, salary) values (3, SALARY_TYPE ());
INSERT INTO TABLE(SELECT salary FROM EMPLOYEE
WHERE ssn = 1)
VALUES (to_date('01.01.2005','mm.dd.yyyy'), to_date('01.01.2015','mm.dd.yyyy'), 1);
INSERT INTO TABLE(SELECT salary FROM EMPLOYEE
WHERE ssn = 2)
VALUES (to_date('02.02.2005','mm.dd.yyyy'), to_date('02.02.2015','mm.dd.yyyy'), 2);
INSERT INTO TABLE(SELECT salary FROM EMPLOYEE
WHERE ssn = 3)
VALUES (to_date('03.03.2005','mm.dd.yyyy'), to_date('03.03.2015','mm.dd.yyyy'), 3);
p.s do you really need the complexity?
I solved my problem using iteration like this
BEGIN
FOR employees IN (SELECT SSN FROM EMPLOYEE)
LOOP
UPDATE TABLE(
SELECT E.SALARY
FROM EMPLOYEE E
WHERE E.SSN = employees.SSN
) SAL
SET SAL.VALID_TIME_UPPER_BOUND = '11.16.2015'
WHERE SAL.VALID_TIME_UPPER_BOUND = TO_DATE('12.31.9999','MM.DD.YYYY');
END LOOP;
END;

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