Values controlled by foreign keys - sql

I have very simple database in SQL Server, with these three tables:
Theater (ID, is3D, othervalues...)
Show (ID, Theater_ID, Movie_ID, date, time, othervalues...)
Movie (ID, is3D, othervalues...)
I would like to ensure that 3D movies can be only played in 3D theaters. 2D movies only in 2D theaters, and doing it ONLY BY foreign keys (no triggers etc.).

To do this through foreign keys alone you need to add an is3D column to Show as well and a couple of logically redundant UNIQUE constraints.
CREATE TABLE Theater
(
ID INT PRIMARY KEY,
is3D BIT NOT NULL,
/*Other columns*/
UNIQUE(ID, is3D)
)
CREATE TABLE Movie
(
ID INT PRIMARY KEY,
is3D BIT NOT NULL,
/*Other columns*/
UNIQUE(ID, is3D)
)
CREATE TABLE Show
(
ID INT PRIMARY KEY,
Theater_ID INT NOT NULL,
Movie_ID INT NOT NULL,
is3D BIT NOT NULL,
/*Other columns*/
FOREIGN KEY(Theater_ID, is3D) REFERENCES Theater (ID, is3D),
FOREIGN KEY(Movie_ID, is3D) REFERENCES Movie (ID, is3D)
)
An indexed view can also be used to enforce this declaratively without requiring the additional column or unique constraints as below.
CREATE TABLE dbo.TwoRows
(
X INT PRIMARY KEY
);
INSERT INTO dbo.TwoRows
VALUES (1), (2)
GO
CREATE VIEW V
WITH SCHEMABINDING
AS
SELECT S.Theater_ID,
S.Movie_ID
FROM dbo.Show S
JOIN dbo.Theater T
ON S.Theater_ID = T.ID
JOIN dbo.Movie M
ON S.Movie_ID = M.ID
CROSS JOIN dbo.TwoRows
WHERE T.is3D <> M.is3D
GO
CREATE UNIQUE CLUSTERED INDEX IX
ON V(Theater_ID, Movie_ID)
The underlying query should always return no rows if the rule is satisfied. If any row is returned then the cross join on dbo.TwoRows will multiply it out causing a unique constraint violation and preventing the situation.

Related

create breadcrumb from sql closure table and calculate depth

I have created a simple hierarchy with closure table and I want to create breadcrumb dynamically and also to calculate the depth.
The code for the structure is:
CREATE TABLE categories (
id int IDENTITY(1,1) not null,
created_date datetime not null DEFAULT GETDATE(),
updated_date datetime not null DEFAULT GETDATE(),
title nvarchar(max) NOT NULL,
CONSTRAINT PK_categories_id PRIMARY KEY CLUSTERED (id),
);
CREATE TABLE childcategories (
parent_id int,
child_id int,
CONSTRAINT PK_childcategories PRIMARY KEY NONCLUSTERED ([parent_id],[child_id]),
CONSTRAINT FK_childcategories_parent_id FOREIGN KEY (parent_id) REFERENCES
categories(id),
CONSTRAINT FK_childcategories_child_id FOREIGN KEY (child_id) REFERENCES
categories(id),
);
insert into categories(title)
values ('hardware'), ('cpu'), ('gpu') , ('intel'), ('amd'), ('nvidia')
insert into childcategories(parent_id,child_id)
values (1,2), (1,3),(2,4), (2,5),(3,4), (3,5), (3,6)
So far the recursive cte I have is:
with cpe as (
select c.id as parent_id,c.title as parent_title,children.id as
child_id,children.title as child_title
from categories c
inner join childcategories parents on parents.parent_id=c.id
inner join categories children on children.id=parents.child_id
union all
select children.id as parent_id,children.title as parent_title,children.id as
child_id,children.title as child_title
from cpe
inner join childcategories cteparents on cteparents.child_id=cpe.parent_id
inner join categories children on children.id=cteparents.parent_id
)
select * from cpe;

how can i create constraint on table based on many column and value in another table Oracle

i have atable such as codes table have these values:
Major_code
minor_code
name
1
0
Country
1
1
USA
1
2
JOR
2
0
Food
2
1
Burger
2
2
Mansaf
I need to create table have these columns (ID, Country_ID , Food_ID) what is the best constraint that resolve this issue?
Having a single lookup table for all reference codes is a very poor design, to the point of almost being a SQL anti-pattern. It is likely to have performance issues. It is also difficult to ensure that the value of COUNTRY_MINOR_CODE references an actual country minor code in the reference table. Which brings us to this statement:
I need to create table have these columns (ID, Country_ID , Food_ID)
Can't be done. Or at least it can be done but you can't build foreign keys on that, because minor keys are not unique in the referenced table. But if you are set on a single table, say lookup_table with primary key defined on (major_code, minor code), you need this:
create table country_food (
id number primary_key
,country_major_code number not null
,country_minor_code number not null
,food_major_code number not null
,food_minor_code number not null
,constraint country_major_code_ck check (country_major_code = 1)
,constraint country_minor_code_ck check (country_minor_code != 0)
,constraint food_major_code_ck check (country_major_code = 2)
,constraint food_minor_code_ck check (country_minor_code != 0)
,constraint country_code_fk foreign key (country_major_code, country_minor_code) references lookup_table
,constraint food_code_fk foreign key (food_major_code, food_minor_code) references lookup_table
)
/
The hard coding of values, the additional check constraints, the increased complexity of joining to look up the name ... These are all reasons why you should choose to have separate tables for FOOD, COUNTRY, etc.
create table country (
country_id number primary_key
,country_name varchar2(24) not null
)
/
create table food (
food_id number primary_key
,food_name varchar2(24) not null
)
/
create table country_food (
id number primary_key
,country_id number not null
,food_id number not null
,food_major_code number not null
,constraint country_code_fk foreign key (country_id) references country
,constraint food_code_fk foreign key (food_id) references food
)
/
If I've got it right, you can get all Country+Food pairs with the query
select t1.minor_code counrty_id, t2.minor_code food_id
from (
select minor_code
from codesTable
where Major_code = (
select c.Major_code
from codesTable c
where c.minor_code=0 and c.name='Country')
) t1
cross join (
select minor_code
from codesTable
where Major_code = (
select c.Major_code
from codesTable c
where c.minor_code=0 and c.name='Food')
) t2
You can use the query to insert data into a table with an autogenerated ID or use it any other way.
First you need to consider the design of the tables
Table of country
Table of foods
Tables of food_country -------- relationship => many-to-many
CREATE TABLE Country
(
Major_code numeric(15) not null,
minor_code numeric(15),
name varchar(50),
CONSTRAINT country_pk PRIMARY KEY (Major_code)
);
CREATE TABLE Food
(
Food_ID numeric(15) not null,
//...........................
name varchar(50),
CONSTRAINT food_pk PRIMARY KEY (Food_ID)
);
CREATE TABLE Counry_Food
(
ID numeric(10) not null,
Country_ID numeric(15) not null,
Food_ID numeric(15) not null,
CONSTRAINT fk_country
FOREIGN KEY (Country_ID)
REFERENCES Country(Major_code),
CONSTRAINT fk_food
FOREIGN KEY (Food_ID)
REFERENCES supplier(Food_ID),
);

Is there a way to relationship between two table which table one has 3 unique keys and table two has 2 unique keys in SQL Server?

I want to create relation between two tables, here's my table structure
Table1:
ID INT PrimaryKey AUTO_INCREMENT,
CountryCode INT UNIQUE,
Division VARCHAR(4) UNIQUE,
SubDivision VARCHAR(10) UNIQUE
Table2:
ID INT PrimaryKey AUTO_INCREMENT,
CountryCode INT UNIQUE,
Division VARCHAR(4) UNIQUE,
ReportNo VARCHAR(10) UNIQUE
Table1:
ID |CountryCode |Division |SubDivision
-------+------------------+--------------+-----------
1 |IDN |A |A-1
2 |IDN |B |B-1
3 |IDN |B |B-2
Table2
ID |CountryCode |Division |ReportNo
-------+------------------+--------------+-----------
1 |IDN |A |Re001
2 |IDN |A |Re002
3 |IDN |B |Re003
I want to create a relationship between those two tables which table2 (CountryCode, Division) refers to table1 (CountryCode, Division).
So when I want to delete in table1 with CountryCode = IDN and Division = A, SQL will prompt error when table2 contains CountryCode = IDN and Division = A.
I had tried to create a relationship between those two tables, but SQL Server always throws this error:
The column in table 'table1' do not match an existing primary key or unique constraint
Can anyone guide me how can I create a relationship between those two tables?
Thanks in advance
You can't do it this way. SQL Server requires a unique constraint (or primary key constraint) on the target of a foreign key - and you have duplicates in the source table.
For this to work, you would need to have a separate table that references all possible (CountryCode, Division) combinations. Actually, your whole schema should be normalized into something like:
-- "top" table that stores the countries
create table countries (
country_id int primary key
name varchar(100)
);
-- the table that stores the divisions
create table divisions (
division_id int primary key,
country_id int references countries(country_id),
name varchar(100)
);
-- the table that stores the subdivisions
-- this corresponds to "table1" in your question
create table subdivisions (
subdivision_id int primary key,
division_id int references divisions(division_id),
name varchar(100)
);
-- the table that stores the reports
-- this corresponds to "table2" in your question
create table reports (
report_id int primary key,
division_id references divisions(division_id),
name varchar(100)
);
You can make the primary keys automatic by using identity columns (which is the SQL Server equivalent for MySQL's AUTO_INCREMENT).
As an example, here is how you would generate the current output that you are showing for the subdivisions:
select
sd.id,
c.name country,
d.name division,
sd.name subdivision
from subdivisions sd
inner join divisions d on d.division_id = sd.division_id
inner join countries c on c.country_id = d.country_id
As GMB has answered you cannot do it in this way because of the duplicates. GMB's answer is the best way to solving your problem. If for some reason you cannot follow his advice then maybe my answer would help.
You could use a composite primary key on the columns CountryCode, Division, SubDivision. Then add subdivision to Table2. And then reference this primary key in the foreignkey restraint. (notice that my example throws an error on purpose to show that the value cannot be deleted)
DROP TABLE IF EXISTS Table2;
DROP TABLE IF EXISTS Table1;
CREATE TABLE Table1
(ID INT IDENTITY(1,1)
, CountryCode CHAR(3)
, Division VARCHAR(4)
, SubDivision VARCHAR(10)
, CONSTRAINT PK_Table1 PRIMARY KEY(CountryCode, Division, SubDivision)
)
INSERT INTO Table1(CountryCode, Division, SubDivision)
VALUES ('IDN', 'A', 'A-1')
, ('IDN', 'B', 'B-1')
, ('IDN', 'B', 'B-2');
CREATE TABLE Table2
(ID INT IDENTITY(1,1) PRIMARY KEY
, CountryCode CHAR(3)
, Division VARCHAR(4)
, SubDivision VARCHAR(10)
, ReportNo VARCHAR(10)
, CONSTRAINT FK_CountryDivision FOREIGN KEY(CountryCode, Division, SubDivision) REFERENCES Table1(CountryCode, Division, SubDivision)
);
INSERT INTO Table2(CountryCode, Division, SubDivision, ReportNo)
VALUES ('IDN', 'A', 'A-1', 'Re001')
, ('IDN', 'B', 'B-1', 'Re002')
, ('IDN', 'B', 'B-2', 'Re003');
DELETE FROM Table1
WHERE Division = 'A';
Of course this change could add a whole set of new problems for instance when a report is for the whole division what should the subdivision value then be.
ps. i had to change up the example tables a bit because the values did not match, ie string values into an int column.
SQL fiddle
thank you for the all greats answer.
But in my design I can't doing this, because I has been wrong at the first time.
And the greats answer was from Mr. #10676716 #GMB.
-- "top" table that stores the countries
create table countries (
country_id int primary key
name varchar(100)
);
-- the table that stores the divisions
create table divisions (
division_id int primary key,
country_id int references countries(country_id),
name varchar(100)
);
-- the table that stores the subdivisions
-- this corresponds to "table1" in your question
create table subdivisions (
subdivision_id int primary key,
division_id int references divisions(division_id),
name varchar(100)
);
-- the table that stores the reports
-- this corresponds to "table2" in your question
create table reports (
report_id int primary key,
division_id references divisions(division_id),
name varchar(100)
);
Thanks again Mr. GMB.

SQL Query that displays the number of athletes that have competed in at least ten different places(locations)

I'm building a query based on the fact that they have competed in ten or more places. Note that it does not matter how many sports or competitions they competed in, just how many places they have competed in.
CREATE TABLE Gender (
gender CHAR(1),
description VARCHAR(10),
PRIMARY KEY (gender));
CREATE TABLE People (
ID INT,
name VARCHAR(50),
gender CHAR(1),
height FLOAT,
PRIMARY KEY (ID),
FOREIGN KEY (gender) REFERENCES Gender (gender));
CREATE TABLE Sports (
ID INT,
name VARCHAR(50),
record FLOAT,
PRIMARY KEY (ID),
UNIQUE (name));
CREATE TABLE Competitions (
ID INT,
place VARCHAR(50),
held DATE,
PRIMARY KEY (ID));
CREATE TABLE Results (
peopleID INT NOT NULL,
competitionID INT NOT NULL,
sportID INT NOT NULL,
result FLOAT,
PRIMARY KEY (peopleID, competitionID, sportID),
FOREIGN KEY (peopleID) REFERENCES People (ID),
FOREIGN KEY (competitionID) REFERENCES Competitions (ID),
FOREIGN KEY (sportID) REFERENCES Sports (ID));
If anyone can help me with this it would be much appreciated!
As commented by Brad, you can use a simple aggregated query that JOINs table People with Results, with a HAVING BY clause to filter on the number of competitions each person participated to. It seems like you don’t need to bring in any other table to achieve your goal.
SELECT
p.ID,
p.Name
FROM
People p
Results r ON r.peopleID = p.ID
GROUP BY
p.ID,
p.Name
HAVING COUNT(*) >= 10

Insert statement error

I have table dbo.Students_Old with columns :
Name(varchar), DocNum(int), Group(varchar), Form(varchar).
It have no primary key and I need to normalize this table. So I am going to make three tables:
dbo.Students(StudentId(int primary key), Name(varchar), Group(int fk),
Form(int fk),
dbo.Forms(FormId(int pk), FName(varchar)), dbo.Groups(GroupId(int pk),
GName(varchar)).
And also I need to fill all this tables with data from dbo.Students_Old. My code is:
CREATE TABLE dbo.Groups(GroupId int IDENTITY(1,1) PRIMARY KEY, GName nvarchar(10));
GO
INSERT INTO dbo.Groups(GName)
select Group
from dbo.Students_Old
group by Group
GO
CREATE TABLE dbo.Forms(FormId int IDENTITY(1,1) PRIMARY KEY, Form nvarchar(20));
INSERT INTO dbo.Forms(Form)
select Form
from dbo.Students_Old
group by Form
GO
CREATE TABLE dbo.Students (StudentId int PRIMARY KEY, Name nvarchar(50),
Form int NOT NULL,
Group int NOT NULL,
CONSTRAINT Form FOREIGN KEY(StudentId) REFERENCES dbo.Forms(FormId),
CONSTRAINT Grup FOREIGN KEY(StudentId) REFERENCES dbo.Groups(GroupId));
GO
INSERT INTO dbo.Students(StudentId, Name, Form, Group)
select DocNum, Name, f.FormId, g.GroupId
from dbo.Students_Old s
join dbo.Forms f on s.Form=f.Form
join dbo.Groups g on s.Group=g.GName
Also Students_Old.DocNum is unique.
Tables creates normaly, but on the insert statement i have an error:
The INSERT statement conflicted with the FOREIGN KEY constraint "Form". The conflict occurred in database "DBNames", table "dbo.Forms", column 'FormId'.
Help me please.
Execute the below query on a new database
CREATE TABLE dbo.Groups(GroupId int IDENTITY(1,1) PRIMARY KEY, GName nvarchar(10));
GO
INSERT INTO dbo.Groups(GName)
select Group
from dbo.Students_Old
group by Group
GO
CREATE TABLE dbo.Forms(FormId int IDENTITY(1,1) PRIMARY KEY, Form nvarchar(20));
INSERT INTO dbo.Forms(Form)
select Form
from dbo.Students_Old
group by Form
GO
CREATE TABLE dbo.Students (StudentId int PRIMARY KEY, Name nvarchar(50),
Form int NOT NULL,
[Group] int NOT NULL,
CONSTRAINT Form FOREIGN KEY(Form) REFERENCES dbo.Forms(FormId),
CONSTRAINT Grup FOREIGN KEY(Group) REFERENCES dbo.Groups(GroupId));
GO
INSERT INTO dbo.Students(StudentId, Name, Form, Group)
select DocNum, Name, f.FormId, g.GroupId
from dbo.Students_Old s
join dbo.Forms f on s.Form=f.Form
join dbo.Groups g on s.Group=g.GName
I've changed to following lines
CONSTRAINT Form FOREIGN KEY(Form) REFERENCES dbo.Forms(FormId),
CONSTRAINT Grup FOREIGN KEY([Group]) REFERENCES dbo.Groups(GroupId));
In your code the foreign key is made on StudentID column
Not sure if that is the case, since you get the FOREIGN KEY error, but try avoiding column names like GROUP or other reserved words. While you can avoid errors on table creation step, you can face serious problems during modifications/updates on such tables.
execute below query.
CREATE TABLE dbo.Groups(GroupId int IDENTITY(1,1) PRIMARY KEY, GName nvarchar(10));
GO
INSERT INTO dbo.Groups(GName)
select [Group]
from dbo.Students_Old
group by [Group]
GO
CREATE TABLE dbo.Forms(FormId int IDENTITY(1,1) PRIMARY KEY, Form nvarchar(20));
INSERT INTO dbo.Forms(Form)
select Form
from dbo.Students_Old
group by Form
GO
CREATE TABLE dbo.Students (StudentId int PRIMARY KEY, Name nvarchar(50),
Form int NOT NULL,
[Group] int NOT NULL,
CONSTRAINT Form FOREIGN KEY(StudentId) REFERENCES dbo.Forms(FormId),
CONSTRAINT Grup FOREIGN KEY(StudentId) REFERENCES dbo.Groups(GroupId));
GO
INSERT INTO dbo.Students(StudentId, Name, Form, [Group])
select DocNum, Name, f.FormId, g.GroupId
from dbo.Students_Old s
join dbo.Forms f on s.Form=f.Form
join dbo.Groups g on s.[Group]=g.GName