SQL select statement headings - sql

Having trouble working out how to do headings
Display the total number of FEMALE students under the heading 'FEMALE STUDENTS':
Struture:
CREATE TABLE Student
(StudID INTEGER PRIMARY KEY,
StudFName VARCHAR(10) NOT NULL,
StudLName VARCHAR(10) NOT NULL,
DoB DATE NOT NULL,
Sex CHAR(1) NOT NULL CHECK (Sex IN ('M', 'F')),
Email VARCHAR(30) UNIQUE);

SELECT count(Sex) as "FEMALE STUDENTS"
from Student
where sex='F';

You might also try the following:
SELECT COUNT(*) AS "Total Students"
, SUM(DECODE(sex, 'F', 1, 0)) AS "Female Students"
, SUM(DECODE(sex, 'F', 0, 1)) AS "Male Students"
FROM students
I use this method often to get multiple counts as part of one statement.

Use count() function to retrieve the number of rows under a particular column.
Syntax:
SELECT COUNT(column_name) FROM table_name;
In your case the query would be:
SELECT count(sex) from Student where sex='F';
COUNT returns the number of rows returned by the query. You can use it as an aggregate or analytic function.
For more info:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions032.htm
http://www.w3schools.com/sql/sql_func_count.asp

Related

CREATE FUNCTION for attribute to at most 3 people?

I am currently doing a hotel booking application on SQL Server 2018, and am trying to write a constraint for the RoomNo attribute of my SQL Server table. Essentially, I want each RoomNo to only be able to have at most 3 person, but ran into an error when trying to do the CREATE FUNCTION.
This are my current code:
CREATE TABLE Passenger
(
ID smallint ,
Name varchar (50) NOT NULL,
Email varchar (319) NULL,
DOB smalldatetime NOT NULL,
Gender char (1) NOT NULL CHECK (Gender IN ('M', 'F')),
RoomNo tinyint NOT NULL,
CONSTRAINT PK_Passenger PRIMARY KEY NONCLUSTERED (ID),
CONSTRAINT CHK_Passenger_Gender CHECK (Gender IN ('M', 'F'))
)
CREATE FUNCTION CalculateRoomNo
(
#value tinyint
)
RETURNS bit
AS
BEGIN
IF (SELECT COUNT(RoomNo) FROM Passenger GROUP BY RoomNo) <= 3
RETURN 0
RETURN 1
END
GO
ALTER TABLE Passenger
ADD CONSTRAINT CHK_RoomNoPax CHECK (dbo.CalculateRoomNo(RoomNo) = 0)
GO
When I add a passenger into the table, if it is formatted like this:
INSERT INTO Passenger
VALUES (1, 'Rob', 'Rob#gmail.com', '2017-10-04', 'M', 12)
INSERT INTO Passenger
VALUES (2, 'Darren', 'Darren#yahoo,com', '1976-12-21', 'F', 12)
INSERT INTO Passenger
VALUES (3, 'Peggy', '', '2006-03-15', 'F', 12)
INSERT INTO Passenger
VALUES (4, 'Carlos', '', '1981-04-06', 'F', 12)
It will stop at
INSERT INTO Passenger VALUES (3, 'Peggy', '', '2006-03-15', 'F', 12)
since RoomNo '12' has reached its maximum capacity.
But, if I added the values like such where the room numbers are different from each other:
INSERT INTO Passenger
VALUES (1, 'Rob', 'Rob#gmail.com', '2017-10-04', 'M', 69)
INSERT INTO Passenger
VALUES (2, 'Darren', 'Darren#yahoo,com', '1976-12-21', 'F', 74)
INSERT INTO Passenger
VALUES (3, 'Peggy', '', '2006-03-15', 'F', 45)
INSERT INTO Passenger
VALUES (4, 'Carlos', '', '1981-04-06', 'F', 72)
INSERT INTO Passenger
VALUES (5, 'John', 'johnny#hotmail.com', '1988-05-06', 'M', 69)
It will return an error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Is there any way I can properly run this SQL?
The query with the GROUP BY can return more than 1 record if there's more than 1 RoomNo.
If you include a WHERE clause for the RoomNo then it can only be 1 COUNT
CREATE FUNCTION CalculateRoomNo
(
#RoomNo tinyint
)
RETURNS bit
AS
BEGIN
IF (SELECT COUNT(*) FROM Passenger WHERE RoomNo = #RoomNo) <= 3
RETURN 0
RETURN 1
END
Demo on db<>fiddle here
As mentioned by #LukStorms, your query has no filter on RoomNo, therefore it can return multiple rows. A scalar subquery must return a maximum of one row.
But the most correct way to achieve what you are trying to do, is not to use this function at all. Instead you can add another column, and create a unique constraint across that and the RoomNo
ALTER TABLE Passenger
ADD RoomNoPax tinyint NOT NULL
CONSTRAINT CHK_RoomNoPax CHECK (RoomNoPax >= 1 AND RoomNoPax <= 3);
ALTER TABLE Passenger
ADD CONSTRAINT UQ_RoomNo_RoomNoPax UNIQUE (RoomNo, RoomNoPax);
db<>fiddle
You now have an extra column which must have the value 1, 2 or 3. And there is a unique constraint over every pair of that value and the RoomNo, so you cannot now put more than 3 Passenger in each RoomNo.
You need to change the logic in the function.
You should be checking for the number of records for any given roomno, not the number of roomno in the entire table.
the subquery should return only one scalar value because you are performing a logical operation.
i.e.
if exists (select 1 from Passenger group by roomno having count(1) <= 3)
begin
return 1
end
else
begin
return 0
end
In the above query, we are checking for the number of persons assigned to each room number and if there is an existence of such case then it will return 1. In this case, it will not return more than one record.
Please modify the return value as per your requirement.
Please upvote if you find this answer useful

Subtract Two columns off the condition of two other column values

Having a lot of trouble thinking through this problem, but please view below example table:
CREATE TABLE IF NOT EXISTS `table_1`
(
`num` float NOT NULL,
`first_name` varchar(200) NOT NULL,
`last_name` varchar(200) NOT NULL,
PRIMARY KEY (`date`)
);
INSERT INTO `table_1` (`num`, `first_name`, `last_name`)
VALUES ('1', 'John', 'Wonk'),
('5', 'John', 'Woo'),
('3', 'James', 'Lee');
This is the output I want: "If first_name equals first_name and last_name does not equal last_name then subtract num with last_name = 'Woo' from num" So the result it would have 1 - 5 (using table data below) and the output would be -4
A Simple Left join on first_name equal and last_name not equal will align the num field with a value, when matched, or null. You cast null's to 0 and do your math for each records so that those that meet the criteria will adjust.
SELECT
D1.first_name,
D1.last_name,
num = D1.num - ISNULL(D2.num,0)
FROM
table_1 D1
LEFT OUTER JOIN table_1 D2 ON D2.first_name = D1.first_name AND D1.last_name <> D2.last_name
Is this what you want?
select min(num) - max(num)
from t
group by first_name
having min(last_name) <> max(last_name);

SQL Counting on characters

I want to use an SQL statement that returns the department number, department name and the number of employees for each department that has less than 4 employees grouping by department number and department name. I have inputted sample data
CREATE TABLE dbo.Departments
(
First_Name NVARCHAR(20) NOT NULL,
Last_Name NVARCHAR(20) NOT NULL,
Job_Description VARCHAR(30) NULL,
Department_Name VARCHAR(30) NOT NULL,
Department_Number INT NOT NULL,
Date_Hired DATE NOT NULL,
);
INSERT INTO dbo.Departments(First_Name,Last_Name,Job_Description,Department_Name,Department_Number,Date_Hired)
VALUES
('Niel','Matthews','Sales Representative','Marketing',80,'1990-06-12'),
('Jerome','Daniels','Sales Representative','Accounting',60,'1973-03-30'),
('Caleb','Sewcharran','Employee','Accounting',35,'1986-08-10'),
('Orion','Wolf','Employee','Marketing',80,'1987-10-04'),
('Gaige','Johnson','Employee','Accounting',60,'1992-09-22'),
('Maya','Heller','Employee','Marketing',25,'1989-06-17'),
('James','Maroon','Employee','Accounting',30,'2011-08-22'),
('Alex','Mercer','Employee','Accounting',60,'2010-03-17'),
('Adam','Beecher','Sales Representative','Marketing',80,'2012-10-04'),
('Bruce','Wayne','Employee','Marketing',25,'2012-08-12'),
('Al','Kepler','Sales Representative','Accounting',80,'1973-06-30');
I have also attempted an SQL statement but it does not work
SELECT Department_Number,Department_Name,Job_Description
FROM dbo.Departments
WHERE Job_Description='Employee'
(SELECT DISTINCT Department_Name FROM dbo.Departments,
(SELECT CAST('Marketing'AS INT)AS Least_Employees,
CAST('Accounting'AS INT)AS Least_Least_Employees)AS Least_Employees
WHERE Department_Name>4 OR Department_Name>4)
GROUP BY Department_Name AND Department_Number;
Appreciate it if you would tell me what i could have used to get on the right track.
You can use GROUP BY clause :
SELECT Department_Number, Department_Name, COUNT(*) AS NoofEmp
FROM dbo.Departments d
WHERE ob_Description = 'Employee'
GROUP B YDepartment_Number,Department_Name
HAVING COUNT(*) <= 4;

Insert values of one table in a database to another table in another database

I would like to take some data from a table from DB1 and insert some of that data to a table in DB2.
How would one proceed to do this?
This is what I've got so far:
CREATE VIEW old_study AS
SELECT *
FROM dblink('dbname=mydb', 'select name,begins,ends from study')
AS t1(name varchar(50), register_start date, register_end date);
/*old_study now contains the data I wanna transfer*/
INSERT INTO studies VALUES (nextval('studiesSequence'),name, '',3, 0, register_start, register_end)
SELECT name, register_start, register_end from old_study;
This is how my table in DB2 looks:
CREATE TABLE studies(
id int8 PRIMARY KEY NOT NULL,
name_string VARCHAR(255) NOT NULL,
description VARCHAR(255),
field int8 REFERENCES options_table(id) NOT NULL,
is_active INTEGER NOT NULL,
register_start DATE NOT NULL,
register_end DATE NOT NULL
);
You should include the column names in both the insert and select:
insert into vip_employees(name, age, occupation)
select name, age, occupation
from employees;
However, your data structure is suspect. Either you should use a flag in employees to identify the "VIP employees". Or you should have a primary key in employees and use this primary key in vip_employees to refer to employees. Copying over the data fields is rarely the right thing to do, especially for columns such as age which are going to change over time. Speaking of that, you normally derive age from the date of birth, rather than storing it directly in a table.
INSERT INTO studies
(
id
,name_string
,description
,field
,is_active
,register_start
,register_end
)
SELECT nextval('studiesSequence')
,NAME
,''
,3
,0
,register_start
,register_end
FROM dblink('dbname=mydb', 'select name,begins,ends from study')
AS t1(NAME VARCHAR(50), register_start DATE, register_end DATE);
You can directly insert values that retured by dblink()(that means no need to create a view)
Loop and cursor are weapons of last resort. Try to avoid them. You probably want INSERT INTO ... SELECT:
INSERT INTO x(x, y, z)
SELECT x, y, z
FROM t;
SqlFiddleDemo
EDIT:
INSERT INTO vip_employees(name, age, occupation) -- your column list may vary
SELECT name, age, occupation
FROM employees;
Your syntax is wrong. You cannot have both, a values clause for constant values and a select clause for a query in your INSERT statement.
You'd have to select constant values in your query:
insert into studies
(
id,
name_string,
description,
field,
is_active,
register_start,
register_end
)
select
studiesSequence.nextval,
name,
'Test',
null,
0,
register_start,
register_end
from old_study;

SQL Server Management Studio: The multi-part identifier could not be bound

I'm taking a SQL class, and on one of the assignments I am asked to create a table with employees, one with projects, as follows:
create table [EMPLOYEE]
(
EmployeeID int identity(1, 1) primary key not null,
FirstName varchar(15) not null,
LastName varchar(15) not null,
Gender char(1) not null,
DOB date not null,
SSN char(9) not null
)
create table [PROJECT]
(
ProjectID int identity(1, 1) primary key not null,
Manager int not null,
PDescription varchar(50) not null,
PStatus varchar(20) not null,
StartDate date not null,
EndDate date not null
)
Where the Manager is the ID of an EMPLOYEE entity. Basically projects are assigned to employees. So, I add some employees and projects
insert into EMPLOYEE (FirstName, LastName, Gender, DOB, SSN)
values ('Chuck', 'Carter', 'M', '07/14/1990', '444556666')
This is the one employee I'm interested in, and in the inserting I'm doing this is the second one, so its EmployeeID would be 2. As for the projects:
insert into PROJECT (Manager, PDescription, PStatus, StartDate, EndDate)
values (2, 'Submit source code for 3D racing game', 'In progress', '01/05/2015', '03/05/2015')
insert into PROJECT (Manager, PDescription, PStatus, StartDate, EndDate)
values (2, 'Test videogame for bugs', 'Not started', '03/05/2015', '05/05/2015')
These two are the ones assigned to the employee above, notice Manager for both is 2, and the EmployeeID of Chuck is supposed to be 2. So, I'm supposed to display the employee name as a full name, combining the first and last name, of those who have been assigned two or more projects (in this case, Chuck).
I wrote this code:
select
FirstName + LastName as FullName
from
EMPLOYEE
where
EmployeeID = PROJECT.Manager and count(PROJECT.Manager) >= 2
But instantly I get this error:
The multi-part identifier "PROJECT.Manager" could not be bound.
Am I supposed to make an inner join to recognize the PROJECT table? But I'm only supposed to display the name of the employee. How can I manage to use the PROJECT.manager column values without displaying them?
Thank you very much in advance.
You have to Join Project table, Add Group by with having clause to filter the Manager
select FirstName +' '+ LastName as FullName
from EMPLOYEE
INNER JOIN PROJECT
on EmployeeID = PROJECT.Manager
group by FirstName +' '+ LastName
Having count(PROJECT.Manager) >= 2