SQL constraint for limiting a daily task - sql

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.

Related

Oracle: stored procedure error

I'm really new to SQL and I must create a stored procedure.
My idea is that I want to type my PaperRoll_ID and to get the "Worker_ID". Since my PaperRoll_ID values in the table are from 1 to 500 and my Worker_id values are from 1500 to 2000, I want to make it so that PaperRoll_ID is equal to the Worker_id index, not value by index (I mean, index 1 is the first Worker_id I added, 2 is the second and so on till 500 (the number of workers)). PaperRoll_ID is located in the table invoice_Paper and Worker_id in the table machine_operator.
Sorry if it's hard to understand, but I lack a lot of knowledge in SQL, so it's a bit hard for me to express myself.
create or replace PROCEDURE name_worker(pi IN NUMBER, mi OUT NUMBER) IS
BEGIN
Select q.worker_ID2 INTO mi
from invoice_paper z,machine_operator o
where z.PaperRoll_ID=pi AND o.WORKER_ID2 = q.worker_ID2;
END;
The tables are
create Table invoice_paper(
PaperRoll_ID Number(10) constraint ppr_id not null,
Single_Layer Varchar(20) Default 'None in stock',
Double_Layer Varchar(20) Default 'None in stock',
Manufacturer_FactoryID Integer,
primary key(PaperRoll_ID),
Constraint pprid_invoice Foreign key (Manufacturer_FactoryID) References Paper_Factory(Factory_ID)
);
create table machine_operator(
Insurence_ID number(10) constraint in_numb not null,
Worker_ID2 number(10) constraint worka_id not null,
operator_name Varchar(20),
Email Varchar(30),
Primary key (Insurence_ID, Worker_ID2),
Constraint wka_id Foreign key(Worker_ID2) References worker(worker_id)
);
"I want to make it so that PaperRoll_ID is equal to the Worker_id index, not value by index (I mean, index 1 is the first Worker_id I added, 2 is the second and so on till 500 (the number of workers)"
That's really not how relational databases work. You should enforce such a relationship with a foreign key between invoice_paper and machine_operator, probably by adding Worker_ID2 column to invoice_paper (*).
Beyond that it's not clear what you are trying to achieve with your procedure, so it's hard to suggest anything better. However, let's assume what you want to do is get the next free worker for assignment to invoice_paper.
create or replace PROCEDURE name_worker(mi OUT NUMBER) IS
BEGIN
Select min(o.worker_ID2) INTO mi
from machine_operator o
where o.WORKER_ID2 not in (select p.worker_ID2
from invoice_paper p)
;
END;
Kept as a procedure for ease of comparison, although this sort of thing is usually written as a FUNCTION with a return value instead.
(*) Just noticed that machine_operator has a compound primary key. If this is correct the foreign key would have to be (Insurence_ID, Worker_ID2) which is ugly. It these situations it is better to have a single column surrogate primary key and enforce the compound key with an additional unique constraint.

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

Confused with Oracle Procedure with sequence, linking errors and filling null fields

I am trying to make a procedure that takes makes potential empty "received" fields use the current date. I made a sequence called Order_number_seq that populates the order number (Ono) column. I don't know how to link errors in the orders table to a entry in the Orders_errors table.
this is what i have so far:
CREATE PROCEDURE Add_Order
AS BEGIN
UPDATE Orders
CREATE Sequence Order_number_seq
Start with 1,
Increment by 1;
UPDATE Orders SET received = GETDATE WHERE received = null;
These are the tables I am working with:
Orders table
(
Ono Number Not Null,
Cno Number Not Null,
Eno Number Not Null,
Received Date Null,
Shipped_Date Date Null,
Creation_Date Date Not Null,
Created_By VARCHAR2(10) Not Null,
Last_Update_Date Date Not Null,
Last_Updated_By VARCHAR2(10) Not Null,
CONSTRAINT Ono_PK PRIMARY KEY (Ono),
CONSTRAINT Cno_FK FOREIGN KEY (Cno)
REFERENCES Customers_Proj2 (Cno)
);
and
Order_Errors table
(
Ono Number Not Null,
Transaction_Date Date Not Null,
Message VARCHAR(100) Not Null
);
Any help is appreciated, especially on linking the orders table errors to create a new entry in OrderErrors table.
Thanks in advance.
Contrary to Martin Drautzburg's answer, there is no foreign key for the order number on the Order_Errors table. There is an Ono column which appears to serve that purpose, but it is not a foreign as far as Oracle is concerned. To make it a foreign key, you need to add a constraint much like the Cno_FK on Orders. An example:
CREATE TABLE Order_Errors
(
Ono Number Not Null,
Transaction_Date Date Not Null,
Message VARCHAR(100) Not Null,
CONSTRAINT Order_Errors_Orders_FK FOREIGN KEY (Ono) REFERENCES Orders (Ono)
);
Or, if your Order_Errors table already exists and you don't want to drop it, you can use an ALTER TABLE statement:
ALTER TABLE Order_Errors
ADD CONSTRAINT Order_Errors_Orders_FK FOREIGN KEY (Ono) REFERENCES Orders (Ono)
;
As for the procedure, I'm inclined to say what you're trying to do does not lend itself well to a PROCEDURE. If your intention is that you want the row to use default values when inserted, a trigger is better suited for this purpose. (There is some performance hit to using a trigger, so that's a consideration.)
-- Create sequence to be used
CREATE SEQUENCE Order_Number_Sequence
START WITH 1
INCREMENT BY 1
/
-- Create trigger for insert
CREATE TRIGGER Orders_Insert_Trigger
BEFORE INSERT ON Orders
FOR EACH ROW
DECLARE
BEGIN
IF :NEW.Ono IS NULL
THEN
SELECT Order_Number_Sequence.NEXTVAL INTO :NEW.Ono FROM DUAL;
END IF;
IF :NEW.Received IS NULL
THEN
SELECT CURRENT_DATE INTO :NEW.O_Received FROM DUAL;
END IF;
END;
/
This trigger will then be executed on every single row inserted into the Orders table. It checks if the Ono column was NULL and replaces it with an ID from the sequence if so. (Be careful that you don't ever provide an ID that will later be generated by the sequence; it will get a primary key conflict error.) It then checks if the received date is NULL and sets it to the current date, using the CURRENT_DATE function (which I believe was one of the things you were trying to figure out), if so.
(Side note: Other databases may not require a trigger to do this and instead could use a default value. I believe PostgreSQL, for instance, allows the use of function calls in its DEFAULT clauses, and that is how its SERIAL auto-increment type is implemented.)
If you are merely trying to update existing data, I would think the UPDATE statements by themselves would suffice. Is there a reason this needs to be a PROCEDURE?
One other note. Order_Errors has no primary key. You probably want to have an auto-incrementating surrogate key column, or at least create an index on its Ono column if you only ever intend to select off that column.
There are a number of confusing things in your question:
(1) You are creating a sequence inside a procedure. Does this even compile?
(2) Your procedure does not have any parameters. It just updates the RECEIVED column of all rows.
(3) You are not telling us what you want in the MESSAGE column.
My impression is that you should first go "back to the books" before you ask questions here.
As for your original question
how to link errors in the orders table to a entry in the Orders_errors
table.
This is aleady (correctly) done. The Orders_error table contains an ONO foreign key which points to an order.

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).

store two values in one field sql

I have to create a table in sql where one of the columns stores awards for a movie. The schema says it should store something like Oscar, screenplay. Is it possible to store two values in the same field in SQL. If so what datatype would that be and how would you query the table for it?
It's a horrible design pattern to store more than one piece of data in a single column in a relational database. The exact design of your system depends on several things, but here is one possible way to model it:
CREATE TABLE Movie_Awards (
movie_id INT NOT NULL,
award_id INT NOT NULL,
CONSTRAINT PK_Movie_Awards PRIMARY KEY CLUSTERED (movie_id, award_id)
)
CREATE TABLE Movies (
movie_id INT NOT NULL,
title VARCHAR(50) NOT NULL,
year_released SMALLINT NULL,
...
CONSTRAINT PK_Movies PRIMARY KEY CLUSTERED (movie_id)
)
CREATE TABLE Awards (
award_id INT NOT NULL,
ceremony_id INT NOT NULL,
name VARCHAR(50) NOT NULL, -- Ex: Best Picture
CONSTRAINT PK_Awards PRIMARY KEY CLUSTERED (award_id)
)
CREATE TABLE Ceremonies (
ceremony_id INT NOT NULL,
name VARCHAR(50) NOT NULL, -- Ex: "Academy Awards"
nickname VARCHAR(50) NULL, -- Ex: "Oscars"
CONSTRAINT PK_Ceremonies PRIMARY KEY CLUSTERED (ceremony_id)
)
I didn't include Foreign Key constraints here, but hopefully they should be pretty obvious.
Anything's possible; that doesn't mean it's a good idea :)
Far better to normalize your structure and store types like so:
AwardTypes:
AwardTypeID
AwardTypeName
Movies:
MovieID
MovieName
MovieAwardType:
MovieID
AwardTypeID
You can serialize your data in Json format,store Json string, and deselialize on read. More sefer than using your own format
Data presentation does't have to be so close tied with phisical data organisation. Wouldn't it be bether to store these two data in two separate columns and then just do some kind of concatenation at the display time?
It is much less painfull to join data than to split it, if you happen to need just a screenplay, one day...