I just recently started learning how to write SQL queries, and I have a lot to learn and a lot of questions, mainly regarding defining conditions for an SQL query. I have 3 tables (with fields listed below):
Employee:
EmployeeID, Name, DoB, StartDate
Salary:
SalaryID, DataPaid, AmountPaid, EmployeeID
Address:
AddressID, Address, City, EmployeeID
Now I would like to know how to:
1. Count the number of employees that live in the city of London.
My attempt:
SELECT COUNT(City) AS EmployeeID FROM Address
WHERE City='London';
2. Add up the 'AmountPaid' values for all employees from the city of London.
My attempt:
SELECT SUM(AmountPaid) AS TotalPaid FROM Salary
WHERE City='London';
3. Display data for all employees that started in 2012 (have a 'Start Date' containing 2012). Not sure where to begin with this!
4. Delete all records where the employee 'Name' field is empty/null.
My attempt:
DELETE FROM Employee
WHERE Name=NULL;
Am I doing something wrong with my attempts? Any help would be appreciated.
THANK YOU!
In SQL Server (T-SQL), you cannot test a value for NULL using '='. It must be as follows:
Delete From Employee Where Name IS NULL
Also i would check for empty names:
DELETE FROM Employee where Name IS NULL OR Name=''
As for the point 3:
SELECT * FROM Employee WHERE Year(StartDate)=2012
As for the point 2, the table Salary does not have an City column. You need to join with Employee table like this:
SELECT SUM(AmountPaid) AS TotalPaid FROM Salary SA inner join Employee Em on Em.EmployeeID=SA.EmployeeID WHERE Em.City='London';
Smells a bit like homework. Anyway:
(1) count of employees in the city of London
First statement seems correct to me, isn't it? But the alias confused me a bit as it says EmployeeID but returned value is the count of employees.
(2) total amount paid for employees in the city of London
This wouldn't work with your statement because table Salary has no field City. You'll need a join here:
select sum(AmountPaid) as TotalPaid
from Salary s
join Address a on s.EmployeeID = a.EmployeeID
where a.City = 'London'
(3) employees started in 2012
Here you can use YEAR function to extract the year out of the date:
select *
from Employee
where Year(StartDate) = 2012
(4) delete unnamed employees
Taken from the solution of #ericpap (for the sake of completeness):
delete from Employee
where Name is NULL
or Name = ''
I would do it like this - it includes a test whereby you make tables and data to help you see how it works;
I assume a employee has only one address, but recieves multiple payments and while it may not be necessary to join from the employee tables in your example - it shows how the relationships between employees and the other tables work, which may help you understand the relational model better.
--Make the Tables
CREATE TABLE dbo.Employee
(
EmployeeID BIGINT PRIMARY KEY NOT NULL,
Name VARCHAR(50),
DoB DATE,
StartDate DATE
)
CREATE TABLE dbo.Salary
(
SalaryID BIGINT PRIMARY KEY NOT NULL,
DatePaid DATE, -- I think you mean DatePaid and not DataPaid
AmountPaid MONEY,
EmployeeID BIGINT
)
CREATE TABLE dbo.Address
(
AddressID BIGINT PRIMARY KEY NOT NULL,
[Address] VARCHAR(max),
City VARCHAR(200),
EmployeeID BIGINT
)
-- Put in some Test Data
INSERT INTO dbo.Employee (EmployeeID,Name, DoB, StartDate)
VALUES (1,'Bill Gates','19551028','20121014'),
(2,'Larry Ellison','19440817','20140101')
INSERT INTO dbo.Address (AddressID,[Address], City, EmployeeID)
VALUES (1,'15 Microsoft House','New York',1),
(2,'23 Oracle Flats','London',2)
INSERT INTO dbo.Salary(SalaryID, DatePaid, AmountPaid, EmployeeID)
VALUES (1,Getdate(),5000.53,1),
(2,'20140201',10000.23,2),
(3,'20140301',10000.23,2)
-- Queries;
--Count the number of employees that live in the city of London.
SELECT COUNT(Distinct E.EmployeeID) as Count_London
FROM dbo.Employee E
INNER JOIN dbo.[Address] A
ON E.EmployeeID = A.EmployeeID
WHERE A.City = 'London'
-- Result = 1
-- 2. Add up the 'AmountPaid' values for all employees from the city of London
--Total Amount
SELECT Sum(S.AmountPaid) as TotalPaid
FROM dbo.Employee E
INNER JOIN dbo.[Address] A
ON E.EmployeeID = A.EmployeeID
LEFT JOIN dbo.Salary S
ON E.EmployeeID = S.EmployeeID
WHERE A.City = 'London'
-- Result = 20000.46 (2 x 10000.23)
--split by employee;
SELECT E.EmployeeID,E.Name,Sum(S.AmountPaid) as TotalPaid
FROM dbo.Employee E
INNER JOIN dbo.[Address] A
ON E.EmployeeID = A.EmployeeID
LEFT JOIN dbo.Salary S
ON E.EmployeeID = S.EmployeeID
WHERE A.City = 'London'
GROUP BY E.EmployeeID, E.Name
--3. Display data for all employees that started in 2012
SELECT *
FROM dbo.Employee E
INNER JOIN dbo.[Address] A
ON E.EmployeeID = A.EmployeeID
LEFT JOIN dbo.Salary S
ON E.EmployeeID = S.EmployeeID
WHERE StartDate >= '20120101' AND StartDate < '20130101'
-- result = all bill gates fields
-- 4. Delete all records where the employee 'Name' field is empty/null.
DELETE FROM dbo.Employee WHERE EmployeeID IS NULL
you might want to delete where the employeeID is null from all the tables if it is possible to have records in there
This wasn't addressed in the other answers:
Count the number of employees that live in the city of London
Your attempt below will get you the right answer only if there is a different employee per row.
SELECT COUNT(City) AS EmployeeID FROM Address
WHERE City='London';
If your Address table looks like it does below, using the query you wrote, you would get 3 employees (but we can see from the table there are only 2 distinct EmployeeID's so thus only 2 employees).
AddressID, Address, City, EmployeeID
1, 1 Main St, London, 1
2, 2 Main St, London, 2
3, 3 Main St, London, 1
What you should do is count the number of distinct EmployeeID's. Each of these EmployeeID's identifies a unique employee. Thus your query should look like this:
SELECT COUNT(DISTINCT EmployeeID) AS EmployeeID FROM Address
WHERE City='London';
That will get you the correct answer every time regardless of what your table looks like.
Related
Don't judge me, I'm new to SQL querying. I got scheme, like the one shown on picture below. So, there are 2 tables, first one Employees contains EmployeeID, FirstName, LastName, DateOfBirth and DepartmentID. The second one is called Department and contains DepartmentID and DepartmentName .
I want to return FirstName, LastName and DepartmentName for the oldest employee from each department containing more than 20 employees.
My solution is the following query :
SELECT FirstName, LastName, DepartmentName
FROM employees
LEFT JOIN department
ON employees.DepartmentID = department.DepartmentID
WHERE (employees.DateOfBirth =
(SELECT MIN(employees.DateOfBirth ) FROM (
SELECT *FROM employees WHERE employees.DepartmentID IN (
SELECT employees.DepartmentID FROM employees GROUP BY DepartmentID HAVING COUNT(*) > 20)));
I think that logic is fine, because inner SELECT statement will return ID's of every department with more than 20 employees, and the outer should return to oldest employee.
The problem that I have is when I try to execute this query, it is returning SQL error every derived table must have it's own alias.
I've tried putting alias on each derived table, but outcome is still the same.
Please, help me with this one.
Also, if someone has different solution, please share it.
Thank You.
Addition which strawberry asked for, Create queries
CREATE TABLE Employees
(
EmployeeID int,
FirstName varchar(10),
LastName varchar(15),
DateOfBirth date,
DeparmentID int
)
CREATE TABLE Department
(
DepartmentID int,
DepartmentName varchar(15)
)
Your query is tricky to read due to inconsistent formatting. So I'll clean it up as follows:
SELECT FirstName, LastName, DepartmentName
FROM employees
LEFT JOIN department
ON employees.DepartmentID = department.DepartmentID
WHERE (employees.DateOfBirth =
(
SELECT MIN(employees.DateOfBirth)
FROM (
SELECT *
FROM employees
WHERE employees.DepartmentID IN (
--Departments with more than 20 employees
SELECT employees.DepartmentID
FROM employees
GROUP BY DepartmentID
HAVING COUNT(*) > 20)
) -- You need an alias here.
-- Also from this point you were missing closing brackets.
Problems with your query:
Obviously the missing alias and closing brackets meant you couldn't even test your query.
Also SELECT MIN(employees.DateOfBirth) returns only a single value. Not a value per department.
So your overall result includes only the oldest employee across all the 'big' departments. (Unless the oldest employee in each department happened to have the same birth date.)
It could also include results from a smaller department if any employee happened to have the same birth date the oldest from the big departments. And that employee needn't even be the oldest in their department!
You also have some inefficiencies by using more sub-queries than necessary.
CTEs (common table expressions) are great at simplifying complex queries. But I don't know if mysql supports them. So this solution still uses sub-queries.
SELECT e.FirstName, e.LastName, d.DepartmentName
FROM employees e -- I prefer short aliases
INNER JOIN (
-- This sub-query returns the earliest birth date within each
-- big department. This needs to be an aliased query so you
-- can join to other tables for your desired columns.
SELECT DepartmentID, MIN(DateOfBirth) AS MinDOB -- Must alias column
FROM employees
WHERE DepartmentID IN (
-- Big departments
SELECT DepartmentID
FROM employees
GROUP BY DepartmentID
HAVING COUNT(*) > 20
)
GROUP BY DepartmentID
) ddob -- Alias Department Date of Birth
-- As a result of inner joining to ddob your employees
-- will be filtered to only those that match the relevant
-- ones identified in the query.
ON e.DepartmentID = ddob.DepartmentID
AND e.DateOfBirth = ddob.MinDOB
INNER JOIN Department d
ON d.DepartmentID = e.DepartmentID
Something to note in the above solution, if 2 employees are tied for being oldest in a department, both will be returned.
This approach is structurally similar to yours, but you could also approach the problem from another direction.
Start out getting oldest employees in ALL departments.
And only at the end filter the result according to department size.
I'll leave that to you to try. I suspect the query would be a little simpler.
The following SQL script correspond to your Class Diagram:
CREATE TABLE Departments (
DepartmentID int AUTO_INCREMENT PRIMARY KEY,
DepartmentName varchar(15)
);
CREATE TABLE Employees (
EmployeeID int AUTO_INCREMENT PRIMARY KEY,
FirstName varchar(10),
LastName varchar(15),
DateOfBirth date,
DepartmentID int,
FOREIGN KEY (DepartmentID) REFERENCES Departments(DepartmentID)
);
Following your classes diagram, there is a department for each employee. So, is the reason of using INNER JOIN. I think the following query do what you want:
SELECT ee.FirstName, ee.LastName, ee.DateOfBirth, t.DepartmentName
FROM
(
SELECT e.DepartmentID, d.DepartmentName, MIN(e.DateOfBirth) AS DateOfBirth
FROM Employees AS e
INNER JOIN Departments AS d ON e.DepartmentID = d.DepartmentID
WHERE e.DepartmentID IN (
SELECT DepartmentID
FROM Employees
GROUP BY DepartmentID HAVING COUNT(DepartmentID) > 20
)
GROUP BY e.DepartmentID
) AS t
INNER JOIN Employees AS ee
WHERE ee.DepartmentID = t.DepartmentID AND ee.DateOfBirth = t.DateOfBirth
Example of output:
FirstName LastName DateOfBirth DepartmentName
fisrt14 last14 02/01/2000 SI
fisrt31 last31 12/01/2003 Finance
You improve its performance!
I'm practicing getting back up to par on MySQL, and have run into an issue using the Northlight practice database. I'm specifically using this site for my practice: http://www.w3schools.com/sql/trysql.asp
I noticed that Employee Adam West (Employee ID 10) does not have any occurrences in the Orders table. I'm trying to get a list that returns the following:
FirstName LastName Number of Sales
Nancy Davolio 29
Andrew Fuller 20
Janet Leverling 31
Margaret Peacock 40
Steven Buchanan 11
Michael Suyama 18
Robert King 14
Laura Callahan 27
Anne Dodsworth 6
Adam West 0
However, each time, the bottom row (with Adam West = 0) does not return.
I've tried the following:
SELECT Employees.FirstName, Employees.LastName, COUNT(Employees.EmployeeID) as "Number of Sales"
FROM Employees
WHERE Employees.EmployeeID = Orders.EmployeeID
GROUP BY Orders.EmployeeID
I've also tried inserting LEFT JOIN Orders in, between the FROM line and the WHERE line, but to no avail.
You need to add the clause to the ON statement when using a LEFT JOIN. Also, since Orders.EmployeeID may not exist, you want to GROUP BY Employees.EmployeeID.
Try this:
SELECT Employees.FirstName, Employees.LastName, COUNT(Orders.EmployeeID) as "Number of Sales"
FROM Employees
LEFT JOIN Orders ON Employees.EmployeeID = Orders.EmployeeID
GROUP BY Employees.EmployeeID
Please understand from the below example:
CREATE TABLE Employees
(
EmployeeID INT,
FirstName VARCHAR(30),
LastName VARCHAR(30)
);
CREATE TABLE Orders
(
orderId INT,
EmployeeID INT
);
/*Data for the table employees */
INSERT INTO employees(EmployeeID,FirstName,LastName) VALUES (1,'a','z');
INSERT INTO employees(EmployeeID,FirstName,LastName) VALUES (2,'b','y');
INSERT INTO employees(EmployeeID,FirstName,LastName) VALUES (3,'c','x');
/*Data for the table orders */
INSERT INTO orders(orderId,EmployeeID) VALUES (101,1);
INSERT INTO orders(orderId,EmployeeID) VALUES (102,2);
INSERT INTO orders(orderId,EmployeeID) VALUES (103,1);
INSERT INTO orders(orderId,EmployeeID) VALUES (104,1);
SELECT Employees.FirstName, Employees.LastName, COUNT(orders.EmployeeID) AS "Number of Sales"
FROM Employees LEFT JOIN Orders
ON(Employees.EmployeeID = Orders.EmployeeID)
GROUP BY Orders.EmployeeID;
FirstName LastName Number of Sales
c X 0
a z 3
b Y 1
A left join is used here. it will fetch all the rows from left table and the matched entry from the right table. Here c don't have an entry in Orders table.
I had the same question. here is my solution...
How many orders has the Batman sold to customers whose names end in a consonant?
SELECT em.EmployeeID, em.LastName, em.FirstName, COUNT(Customers.CustomerID) as 'number of orders'
FROM (SELECT *
FROM Employees
LEFT JOIN Orders ON Employees.EmployeeID = Orders.EmployeeID
WHERE Employees.EmployeeID = 10) as em
LEFT JOIN Customers ON em.CustomerID = Customers.CustomerID
WHERE Customers.CustomerID is null
OR (Customers.CustomerName not LIKE '%a'
AND Customers.CustomerName not LIKE '%e'
AND Customers.CustomerName not LIKE '%i'
AND Customers.CustomerName not LIKE '%o'
AND Customers.CustomerName not LIKE '%u'
AND Customers.CustomerName not LIKE '%y')
RESULT:
EmployeeID LastName FirstName number of orders
10 West Adam 0
In my database, have 2 different fields
i.) Employee
ii.) Department
In my employee table,
NAME Department
---------------------
John IT
Siti Research
Jason Research
In my Department,
Name
------------
IT
Research
Computer
Using statement
SELECT DEPARTMENT.DNAME
FROM DEPARTMENT,
EMPLOYEE
WHERE DEPARTMENT.DNAME = EMPLOYEE.DNAME
AND
(SELECT COUNT(*)
FROM EMPLOYEE.DNAME)=0;
when no employee in the department then will display
Name
--------------
Computer
Keep trying but having some error on it
Two alternatives:
Using IN:
SELECT name FROM Department
WHERE name NOT IN (SELECT DISTINCT Department
FROM Employee)
Using Left Join:
SELECT D.NAME
FROM DEPARTMENT D LEFT JOIN EMPLOYEE ON D.NAME = EMPLOYEE.Department
WHERE EMPLOYEE.Department IS NULL
An example in Fiddle.
This method will show higher performance than the other if you have thousands of records in your table.
Try NOT IN. Sub query need to be DISTINCT to avoid performance issue in future:
SELECT name FROM Department
WHERE name NOT IN ( SELECT DISTINCT Department FROM Employee);
Or NOT EXIST, faster in most cases:
SELECT name FROM Department
WHERE NOT EXIST ( SELECT 1 FROM Employee
WHERE Employee.Department = Department.name);
You don't state your error but you can select all departments that don't appear in the employee table:
SELECT DEPARTMENT.DNAME
FROM DEPARTMENT
WHERE DEPARTMENT.DNAME NOT IN (SELECT DEPARTMENT FROM EMPLOYEE);
SELECT D.Name
FROM Employee E
RIGHT JOIN Department D
ON E.Department=D.Name
WHERE E.Department IS NULL
SQL Fiddle DEMO
Here is the schema:
create table Retailer (
row bigint,
id uniqueidentifier,
name varchar(255),
domainName varchar(MAX)
)
create table SalesPeople
( Row bigint not null identity,
ID uniqueidentifier not null,
Retailer_ID uniqueidentifier not null,
FirstName nvarchar(255) not null,
LastName nvarchar(255) not null,
EmailAddress nvarchar(511) ,
PhoneNumber nvarchar(255)
)
orders (
row bigint,
id uniqueidentifier,
Retailer_ID uniqueidentifier,
salesperson_id uniqueidentifier,
prod_id uniqueidentifier,
date datetime
)
We are designing a query that will report the salespeople and the number of orders they have generated for a particular retailer. Also, if Mark and Bill are tied, but Bills's orders are more recent, Bill will need to show up on top of Mark. Also, the one who generated the most orders needs to show up on the very top of the list.
So say for Retailer EBAY, the sales data states that John did 2000 orders in January of 2012, and Bill did 999 orders in Jan 2010 and 1 order in August 2013, and Mark did 1000 orders in June of 2013, and Marry did 700 orders in July 2010 and 150 orders in July 2013. The output would look like this:
Sales Person #ofOrdersCreate
John 2000
Bill 1000
Mark 1000
Marry 850
The query we have tried is:
select s.firstname + ' '+s.LastName , COUNT(o.id) from Orders o
inner join SalesPeople s on s.ID=o.Retailer_ID
inner join Retailers r on r.id = o.retailer_id
where r.name='EBAY'
group by s.firstname + ' '+s.LastName
order by o.date
But we get errors from the join statement above because we can not order the data since it is not contained in either an aggregate function or the GROUP BY clause.
You have two issues:
Because your grouped results will contain multiple possible dates you need to either include the field in the order by (which does not work for what you are attempting to accomplish) or use an aggregate function on the group of results that are returned (like sum or in your requirement, max).
Your group by should be on the collective columns s.firstname, s.lastname not an aggregation of them.
So fix like this:
select s.firstname + ' '+s.LastName , COUNT(o.id) from Orders o
inner join SalesPeople s on s.ID=o.Retailer_ID
inner join Retailers r on r.id = o.retailer_id
where r.name='EBAY'
group by s.firstname, s.LastName
order by count(o.id) DESC, max(o.date)
Please try now:
select s.firstname + ' '+s.LastName , COUNT(o.id) from Orders o
inner join SalesPeople s on s.ID=o.Retailer_ID
inner join Retailers r on r.id = o.retailer_id
where r.name='EBAY'
group by s.firstname + ' '+s.LastName
order by COUNT(o.id)
I have a SQL table that I cannot change the structure of.
The table Employee_Team has 5 columns: TeamID, Employee1, Employee2, Employee3 and Employee4. The fields of columns Employee1, Employee2, Employee3 and Employee4 contain an EmployeeID. Each Employee ID has a City in the table Employee. It’s possible to have multiple employees on the same row of Employee_Team that are living in the same town. There will always be at least 1 employee that will live in the town I’m looking for.
example http://www.atriasoft.com/table.png
Example:
I want the first employee that lives in New York. In the case of TeamID 01, it will be Employee2 (I don’t want Employee 4 even though his city is New York) and for the case of TeamID 02, it will be Employee1 (I still don’t want the second employee that lives in New York).
I currently have a code that gives me every employee that lives in New York:
SELECT *
FROM Employee_Team
INNER JOIN Employees
ON Employees.EmployeeID = Employee_Team.Employee1 OR
Employees.EmployeeID = Employee_Team.Employee2 OR
Employees.EmployeeID = Employee_Team.Employee3 OR
Employees.EmployeeID = Employee_Team.Employee4
WHERE Employees.City = ‘New York’
What I am looking for is a code that takes only the first employee living in New York.
Any hints will be much appreciated!
You actually can safely "change" the definition of your table, virtually, by using it as the basis of a view:
CREATE VIEW NormalTeams (TeamID, EmployeeID, TeamMemberNumber) AS
SELECT TeamID, Employee1, 1 FROM Employee_TEAM UNION ALL
SELECT TeamID, Employee2, 2 FROM Employee_TEAM UNION ALL
SELECT TeamID, Employee3, 3 FROM Employee_TEAM UNION ALL
SELECT TeamID, Employee4, 4 FROM Employee_TEAM
Now, NormalTeams will let you SELECT the data the way you expect to be able to:
SELECT TeamID, MIN(TeamMemberNumber)
FROM NormalTeams T INNER JOIN Employees E ON T.EmployeeiD = E.EmployeeID
WHERE E.City = 'New York'
GROUP BY TeamID
will give you the first team member "number" for New York.
SELECT TeamID, City, MIN(TeamMemberNumber)
FROM NormalTeams T INNER JOIN Employees E ON T.EmployeeiD = E.EmployeeID
GROUP BY TeamID, City
will give you the more general solution for every City / Team combination.
You can join these results back to NormalTeams to get the employee ID.
A CASE statement should be your friend here:
SELECT et.TeamID
,CASE WHEN e1.City = c.city THEN e1.EmployeeID
WHEN e2.City = c.city THEN e2.EmployeeID
WHEN e3.City = c.city THEN e3.EmployeeID
WHEN e4.City = c.city THEN e4.EmployeeID
ELSE NULL
END AS first_in_city
FROM (SELECT 'New York' AS city) c -- to parametrize the city we look for
,Employee_Team et
LEFT JOIN Employees e1 ON e1.EmployeeID = et.Employee1
LEFT JOIN Employees e2 ON e2.EmployeeID = et.Employee2
LEFT JOIN Employees e3 ON e3.EmployeeID = et.Employee3
LEFT JOIN Employees e4 ON e4.EmployeeID = et.Employee4
The thing, that made your query crumble is this: you have to join in the Employees table four times, not just one time. Like it would be four different tables.
I chose LEFT JOIN, so we don't lose any rows if referential integrity should not be guaranteed.
I like unpivot to normalize the structure/table so you could use below CTE to replace with View so no definition change but run-time parameter given.
with team_cte(TeamID, EmployeeID, n)
as
(
select TeamID, EmployeeID, row_number() over (partition by TeamID order by TeamID) as n
from
(select TeamID, Employee1, Employee2, Employee3, Employee4
from Employee_Team) p
UNPIVOT
(
EmployeeID
FOR Employee in (Employee1, Employee2, Employee3, Employee4)
) AS unpvt
)
select TeamID, min(n) as FirstEmployeeID
from team_cte
where EmployeeID in
(
select EmployeeID from Employees where City = 'New York'
)
group by TeamID