Time period SQL view - sql

I am trying to create a SQL view on two tables I'm working with:
CREATE TABLE availability
(
doctor varchar(20) NOT NULL,
avl_date date NOT NULL,
avl_start time NOT NULL,
avl_end time NOT NULL,
CONSTRAINT pk_availability PRIMARY KEY (doctor, avl_date)
);
And
CREATE TABLE appointments
(
patient varchar(20) NOT NULL,
doctor varchar(20) NOT NULL,
apt_date date NOT NULL,
apt_start time NOT NULL,
apt_end time NOT NULL,
CONSTRAINT pk_appointments PRIMARY KEY (patient, apt_date)
);
The view I am trying to create lists all maximal time periods (apt date, apt start, apt end) during which no further appointments are possible (consider doctors’ availability as well).
Any help is greatly appreciated, thanks.

this should work if the appointment time-duration is constant. You can optimize it by re-writing with inner join instead of minus. Also, remember to put availability table above minus.
select doctor
, avl_date
, avl_start
, avl_end
from availability
minus
select doctor
, apt_date
, apt_start
, apt_end
from appointments

Related

Subqueries are not allowed in this context. Only scalar expressions are allowed in CREATE TABLE with AS syntax

I am trying to run the below script but I keep getting the error messages
"Subqueries are not allowed in this context. Only scalar expressions are allowed." on lines 16 and 34.
I know where it's failing - it is failing on the AS clauses, but I don't know how to correct it with different code to stop the errors from appearing.
I have tried looking around at other existing questions but none helped me that I can find. As the issue I have here is using data from columns in different tables along with columns in the current table.
Could I get some help with getting this working and advise what code will be better please?
Thanks for your help in advance!
Dan
This is the code for my database::
CREATE DATABASE [LEARNING]
GO
CREATE TABLE Trainees
(
Trainee_ID int IDENTITY(1,1) PRIMARY KEY NOT NULL,
Name varchar(50) NOT NULL,
[Assigned Tutor_ID] int NOT NULL,
)
GO
CREATE TABLE Tutors
(
Tutor_ID int IDENTITY(1,1) PRIMARY KEY NOT NULL,
Name varchar(50) NOT NULL,
[Assigned Trainee_ID] AS (Select Trainee_ID from Trainees where Tutors.[Assigned Trainee_ID] = Trainees.Trainee_ID) NOT NULL
)
GO
CREATE TABLE [Rooms]
(
Room_ID int IDENTITY(1,1) PRIMARY KEY NOT NULL,
[Room Name] varchar(50) NOT NULL,
[Cost per hour] money NOT NULL
)
GO
CREATE TABLE [Rooms Rented]
(
Rented_ID int IDENTITY(1,1) PRIMARY KEY NOT NULL,
Room_ID int NOT NULL,
Tutor_ID int NOT NULL,
[Length of time in hours] int NOT NULL,
[Total Cost] AS (select ([Rooms Rented].[Length of time in hours])*([Rooms].[Cost per hour]) from [Rooms]) NOT NULL
)
GO
INSERT INTO Tutors values ('Nikki Smith',1)
GO
INSERT INTO Trainees Values ('Tyler Hatherall')
GO
INSERT INTO Rooms values ('Training Room 1',6.50)
GO
INSERT INTO [Rooms Rented] values (1,1,2)
GO
Computed columns are used to ensure columns persisted property within the table itself.
In your case, you need to have another update after you created table, populate the column by the query similar to below query, also, you need to create Foreign Key in the Total Cost column based on what you try to achieve.
UPDATE A
SET A.[Total Cost] = A.[Length of time in hours] * B.[Cost per hour] --add ISNULL to treat NULL if needed
FROM [Rooms Rented] as A
INNER JOIN [Rooms] as B
ON B.Room_ID = A.Room_ID
Your AS statements are computed columns. When your computed columns refer to other tables, you cannot implement this directly. You will have to create scalar functions first.
For example, after creating Rooms, create this function that takes a room id and returns cost per hour:
create function f_get_Rooms_CostPerHour (#Room_ID int)
returns money
as
return (select [Cost per hour] from [Rooms] where [Rooms].Room_ID=#Room_ID)
Now you can use this in your computed column formula. Note that a computed column formula never has a SELECT in it. It also does not have a null/not null specification.
CREATE TABLE [Rooms Rented]
(
Rented_ID int IDENTITY(1,1) PRIMARY KEY NOT NULL,
Room_ID int NOT NULL,
Tutor_ID int NOT NULL,
[Length of time in hours] int NOT NULL,
[Total Cost] AS ([Length of time in hours]*f_get_Rooms_CostPerHour([Room_ID]))
)

Complex T-SQL Statement

I am not the best SQL programmer so I am basically asking someone for help writing a query that will get my desired result. Here is the table structure for the scope of the query.
CREATE TABLE [dbo].[tblOverNightPermissions] (
[DateAndTime] DATETIME NULL,
[Address] NVARCHAR (200) NULL,
[Direction] NVARCHAR (102) NULL,
[NoOfDays] INT NULL,
[UserID] INT NOT NULL,
[OverNightID] INT IDENTITY (1, 1) NOT NULL,
[Exempt] INT NULL,
[Beat] INT NULL,
CONSTRAINT [PrimaryKey_1fd244dd-bfd8-4998-8439-4d7d7893d387] PRIMARY KEY CLUSTERED ([OverNightID] ASC),
CONSTRAINT [FK_tblOverNightPermissions_0] FOREIGN KEY ([UserID]) REFERENCES [dbo].[tblUsers] ([UserID])
);
CREATE TABLE [dbo].[tblOverNightToVehicles] (
[OverNightID] INT NOT NULL,
[VehicleID] INT NOT NULL,
[ID] INT IDENTITY (1, 1) NOT NULL,
CONSTRAINT [PrimaryKey_b433eaad-fb12-493c-9302-3f3bd9bd74e3] PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_tblOverNightToVehicles_0] FOREIGN KEY ([OverNightID]) REFERENCES [dbo].[tblOverNightPermissions] ([OverNightID]) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT [FK_tblOverNightToVehicles_1] FOREIGN KEY ([VehicleID]) REFERENCES [dbo].[tblVehicles] ([VehicleID]) ON DELETE CASCADE ON UPDATE CASCADE
);
What I want to do is select record from tblOverNightPermissions and group them by month. I also want to count the number of records for that month and grouped by the vehicle ID which is in tblOverNightToVehicles. The goal is to run a check to make sure there have not been more than 5 overnightpermissions per vehicle id per month. It gets kind of tricky because the database design is not sound. As you can see, the NoOfDays field in the tblOvernightPermissions makes things complicated. Instead of there always being a set number of records per month, users have the ability to select up to 5 consecutive days of parking. So if I park today and select 5 days my record entry will look like this
DateAndTime = 5/8/2014
Address = x
Direction = S
NoOfDays = 5
UserId = 1
OvernightId = 1
Exempt = 0
Beat = 2
That means, for the month I will only have one physical record in tblOverNightPermissions that represents 5 days of parking. I could just as easily create 5 records in the table to signify 5 days of parking and thats where the issue comes in. Writing conditionals in TSQL to take into account if a record has NoOfDays > 1 add that to the count of the physical records for the month.
Here are the scripts to populate your databases
Script to populate tblOvernightPermissions https://dl.dropboxusercontent.com/u/62170850/tblOvernightPermissions.txt
Script to populate tblOvernightToVehicles
https://dl.dropboxusercontent.com/u/62170850/tblOverNightToVehicles.txt
SELECT DATEADD(month, DATEDIFF(month, 0, p.DateAndTime), 0) AS [Month],
SUM(p.NoOfDays) As Days
FROM tblOverNightPermissions p
INNER JOIN tblOverNightToVehicles v ON p.OverNightID = v.OverNightID
GROUP BY DATEADD(month, DATEDIFF(month, 0, p.DateAndTime), 0), v.VehicleID
HAVING SUM(p.NoOfDays) >= 5

PL/SQL function that returns a value from a table after a check

I am new to php and sql and I am building a little game to learn a little bit more of the latter.
This is my simple database of three tables:
-- *********** SIMPLE MONSTERS DATABASE
CREATE TABLE monsters (
monster_id VARCHAR(20),
haunt_spawn_point VARCHAR(5) NOT NULL,
monster_name VARCHAR(30) NOT NULL,
level_str VARCHAR(10) NOT NULL,
creation_date DATE NOT NULL,
CONSTRAINT monster_id_pk PRIMARY KEY (monster_id)
);
-- ****************************************
CREATE TABLE spawntypes (
spawn_point VARCHAR(5),
special_tresures VARCHAR (5) NOT NULL,
maximum_monsters NUMBER NOT NULL,
unitary_experience NUMBER NOT NULL,
CONSTRAINT spawn_point_pk PRIMARY KEY (spawn_point)
);
-- ****************************************
CREATE TABLE fights (
fight_id NUMBER,
my_monster_id VARCHAR(20),
foe_spawn_point VARCHAR(5),
foe_monster_id VARCHAR(20) NOT NULL,
fight_start TIMESTAMP NOT NULL,
fight_end TIMESTAMP NOT NULL,
total_experience NUMBER NOT NULL
loot_type NUMBER NOT NULL,
CONSTRAINT my_monster_id_fk FOREIGN KEY (my_monster_id)
REFERENCES monsters (monster_id),
CONSTRAINT foe_spawn_point_fk FOREIGN KEY (foe_spawn_point)
REFERENCES spawntypes (spawn_point),
CONSTRAINT fight_id_pk PRIMARY KEY (fight_id)
);
Given this data how can I easily carry out this two tasks:
1) I would like to create a pl/sql function that passing only a fight_id as a parameter and given the foe_spawn_point (inside the fight table) return the unitary_experience that is related to this spawn point referencing the spawntypes table, how can I do it? :-/ [f(x)]
In order to calculate the total experience earned from a fight (unitary_experience * fight_length) I have created a function that given a particular fight will subtract the fight_end with the fight_start so now I know how long the fight lasted. [f(y)]
2) is it possible to use this two functions (multiply the result that they returns) during the database population task?
INSERT INTO fights VALUES(.... , f(x) * f(y), 'loot A');
in order to populate all the total_experience entries inside the fights table?
thank you for your help
In SQL, you don't generally talk about building functions to do things. The building blocks of SQL are queries, views, and stored procedures (most SQL dialects do have functions, but that is not the place to start).
So, given a variable with $FIGHTID you would fetch the unitary experience with a simple query that uses the join operation:
select unitary_experience
from fight f join
spawnTypes st
on st.spawn_point = f.foe_spawn_point
where fightid = $FIGHTID
If you have a series of values to insert, along with a function, I would recommend using the select form of insert:
insert into fights(<list of columns, total_experience)
select <list of values>,
($FIGHT_END - $FIGHT_START) * (select unitary_experience from spawnTypes where spawnType ='$SPAWN_POINT)
One comment about the tables. It is a good idea for all the ids in the table to be integers that are auto-incremented. In Oracle you do this by creating a sequence (and it is simpler in most other databases).

SQL constraint for limiting a daily task

I am teaching myself SQL and I am stuck. I have a Car-garage database with a table called Tasks. The table Tasks has 3 columns: Tasknumber(PK), NumberPlate, and DaySchedule. In the DDL I want to use a constraint which checks the number of plates and allows only one task a day.
The below is what I have tried. However, it restricts per record instead of per number of plates.
CREATE TABLE Tasks (
Tasknumber NUMERIC(5) not null,
NumberPlate VARCHAR(8) not null,
DaySchedule DATE not null,
Description VARCHAR(255) null,
CONSTRAINT PK_Tasknumber PRIMARY KEY (Tasknumber),
D AS DAY(DaySchedule) PERSISTED,
M AS MONTH(DaySchedule) PERSISTED,
Y AS YEAR(DaySchedule) PERSISTED,
CONSTRAINT UQ_DATA_DMY UNIQUE(D,M,Y)
)
What can I do to correct this?
If I understand correctly, you only need a UNIQUE constraint on (NumberPlate, DaySchedule). Then, for every NumberPlate, there will be at most one Task per Day.

Oracle SQL Question - Need Help

So I've been doing this all night - can't quite understand my homework, and sadly my professor is unavailable on the weekend. I've posted a few of these questions, this being the last one. I've got something to go on, but it needs working (and coming out of this I'd love to fully understand the answer so I don't need help on something similar again). Here it goes: Find the name and the phone number of the theaters that show the maximum number of movies. Make sure your query works when there is a tie between several theatres.
Here are my table declares (and thank you to EVERYONE helping me out tonight, I owe you big time).
CREATE TABLE Theatres (
Name varchar2(50) not null,
City varchar2(50) not null,
State varchar2(50) not null,
Zip number not null,
Phone varchar2(50) not null,
PRIMARY KEY (Name)
);
CREATE TABLE Movies (
Title varchar2(100) not null,
Rating NUMBER not null,
Length NUMBER not null,
ReleaseDate date not null,
PRIMARY KEY (Title),
CHECK (Rating BETWEEN 0 AND 10),
CHECK (Length > 0),
CHECK (ReleaseDate > to_date('1/January/1900', 'DD/MONTH/YYYY'))
);
CREATE TABLE ShownAt (
TheatreName varchar2(50) not null,
MovieTitle varchar2(100) not null,
PRIMARY KEY (TheatreName, MovieTitle),
FOREIGN KEY (TheatreName) REFERENCES Theatres(Name),
FOREIGN KEY (MovieTitle) REFERENCES Movies(Title)
);
I'm trying to apply some of the things I've learned from StackOverflow members help in other questions, but I'm not sure how to return something based on the max results of a column. Any help would be greatly appreciated.
Here's one way.
With T As
(
SELECT T.Name, T.Phone,
RANK() OVER (ORDER BY COUNT(S.MovieTitle ) DESC) AS Rnk
FROM Theatres T
JOIN ShownAt S ON S.TheatreName= T.Name
GROUP BY T.Name, T.Phone
)
SELECT Name, Phone
FROM T
WHERE Rnk=1;