Sql table where row may only be inserted if unique column - sql

I want to create a table which contains person, house and family, where only persons from the same family are allowed to live in the same house.
What I have so far does not work because I can only post one row with unique family and house. Is there any way to do this?
CREATE TABLE familyhouse (
person VARCHAR(64),
house VARCHAR(64),
family VARCHAR(64),
unique(house,family)
);
Example of correct table:
man,'1','1'
man2,'1','1'
man3,'1','1'
man4,'2','2'
man5,'2','2'
man6,'3','3'
Example of non-correct table:
man,'1','1'
man2,'1','1'
man3,'1','2'

I'd leverage the power of foreign keys and put the house and family in their own table (family_house) and a separate table for the residents.
CREATE TABLE family_house (
house VARCHAR(128) NOT NULL UNIQUE,
family VARCHAR(64) NOT NULL,
PRIMARY KEY (house, family)
);
CREATE TABLE residents (
person VARCHAR(64),
house VARCHAR(128),
family VARCHAR(64),
UNIQUE (person, house, family),
FOREIGN KEY (house, family) REFERENCES family_house
);
This way I can have multiple residents in the same home, but only one family to a home.

You can use a CHECK CONSTRAINT to maintain this:
CREATE TABLE familyhouse (
person VARCHAR(64),
house VARCHAR(64),
family VARCHAR(64)
);
CREATE FUNCTION CheckFamilyHouse(VARCHAR(64), VARCHAR(64))
RETURNS BOOLEAN AS $$
SELECT CASE WHEN EXISTS
( SELECT 1
FROM FamilyHouse
WHERE Family = $1
AND House != $2
)
THEN false
ELSE true
END
$$ LANGUAGE SQL;
ALTER TABLE familyHouse
ADD CONSTRAINT CHK_FamilyHouse
CHECK(CheckFamilyHouse(family, house));
With the above in place the second insert below will fail:
INSERT INTO familyhouse VALUES(1, 1, 1);
INSERT INTO FamilyHouse VALUES(2, 2, 1);
with the message:
ERROR: new row for relation "familyhouse" violates check constraint "chk_familyhouse": INSERT INTO FamilyHouse VALUES(2, 2, 1)
SQL Fiddle Example

create table house (
id serial primary key
);
create table family (
id serial primary key
);
create table house_family (
house_id integer,
family_id integer,
primary key (house_id, family_id),
foreign key (house_id) references house (id),
foreign key (family_id) references family (id)
);
create table person (
id serial primary key,
family_id integer,
house_id integer,
foreign key (house_id, family_id) references house_family (house_id, family_id)
);
insert into house values (1),(2),(3);
insert into family values (1),(2),(3);
insert into house_family values (1,1),(2,2),(3,3);
insert into person (family_id, house_id) values (1,1),(1,1);
select * from house;
id
----
1
2
3
select * from family;
id
----
1
2
3
select * from house_family;
house_id | family_id
----------+-----------
1 | 1
2 | 2
3 | 3
select * from person;
id | family_id | house_id
----+-----------+----------
5 | 1 | 1
6 | 1 | 1
Now if you try to insert a person from family_id 2 in the same house_of family_id 1:
insert into person (family_id, house_id) values (2,1);
ERROR: insert or update on table "person" violates foreign key constraint "person_house_id_fkey"
DETAIL: Key (house_id, family_id)=(1, 2) is not present in table "house_family".

Related

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),
);

Same primary keys for relation

Lets say I have two many-to-many relations called Tutors and Assits. They are both connected courses. If they are both inheritance of relation called Staff they both have the same primary keys. That would mean the many-to-many relation will also have the same primary keys.
If I have two relations with the same primary keys what do I do? Here is an example of the table:
It should be pretty simple to add those two relationships. For example:
create table staff (
id int primary key not null,
name varchar(20)
);
create table tutor (
id int primary key not null,
office_number varchar(10),
foreign key (id) references staff (id)
);
create table assistant (
id int primary key not null,
title varchar(20),
foreign key (id) references staff (id)
);
create table course (
course_id int not null,
year int not null,
semester int not null,
primary key (course_id, year, semester),
tutor_id int not null references tutor (id),
assistant_id int not null references assistant (id)
);
Now, if you insert some data:
insert into staff (id, name) values (1, 'Alice');
insert into staff (id, name) values (2, 'Victor');
insert into tutor (id, office_number) values (1, '101-A');
insert into assistant (id, title) values (2, 'Adjunct Professor');
insert into course (course_id, year, semester, tutor_id, assistant_id)
values (101, 2020, 1, 1, 2);
You can run a query over it:
select
c.course_id, c.year, c.semester,
ts.name as tutor, t.office_number,
ta.name as assistant, a.title
from course c
join tutor t on c.tutor_id = t.id
join staff ts on ts.id = t.id
join assistant a on c.assistant_id = a.id
join staff ta on ta.id = a.id
Result:
course_id year semester tutor office_number assistant title
---------- ----- --------- ------ -------------- ---------- -----------------
101 2020 1 Alice 101-A Victor Adjunct Professor
See running example at DB Fiddle.
If the relation course - tutor is a many-to-many relation you will need an extra xref table:
course -|---|< course_tutor >0---|- tutor
The same thing for assistant.
Even when the same person is tutor and assistant in the same course there will be a record in course_tutor and course_assistant with the same course_id and staff_id but that is not a problem.

Counting in Oracle 11g

all day I am struggling with oracle exrcises and again I stuck. I need to select last names of boxers with their wins of each weight category.
So I have:
table "boxer" with columns: id, fname, lname, weight
table "fight" with two foreign keys from table boxer (id_boxer1 and id_boxer2) and with one column winner (if boxer1 won then winner will be number 1, if boxer2 won then winner will be number 2)
table "category_weight" with columns: id, min_weight, max_weight, name (of a category)
Example:
CREATE TABLE category_weight(
id INTEGER NOT NULL,
min_weight SMALLINT NOT NULL,
max_weight SMALLINT NOT NULL,
name VARCHAR2(20) NOT NULL
);
ALTER TABLE category_weight ADD CONSTRAINT category_weight_pk PRIMARY KEY ( id );
CREATE TABLE boxer(
id INTEGER NOT NULL,
fname VARCHAR2(20) NOT NULL,
lname VARCHAR2(20) NOT NULL,
weight INTEGER NOT NULL
);
ALTER TABLE boxer ADD CONSTRAINT boxer_pk PRIMARY KEY ( id );
CREATE TABLE fight(
id INTEGER NOT NULL,
winner SMALLINT NOT NULL,
id_category_weight INTEGER NOT NULL,
id_boxer1 INTEGER NOT NULL,
id_boxer2 INTEGER NOT NULL
);
ALTER TABLE fight ADD CONSTRAINT fight_pk PRIMARY KEY ( id );
ALTER TABLE fight
ADD CONSTRAINT boxer_fk FOREIGN KEY ( id_boxer1 )
REFERENCES boxer ( id );
ALTER TABLE fight
ADD CONSTRAINT boxer_fk2 FOREIGN KEY ( id_boxer2 )
REFERENCES boxer ( id );
ALTER TABLE fight
ADD CONSTRAINT categ_weight_fk FOREIGN KEY ( id_category_weight )
REFERENCES category_weight ( id );
INSERT INTO boxer
VALUES ('1', 'Johnny','Johnny','60');
INSERT INTO boxer
VALUES ('2', 'Anthonny','Anthonny','54');
INSERT INTO boxer
VALUES ('3', 'Anonimm','Anonimm','59');
INSERT INTO boxer
VALUES ('4', 'John','Johnowski','71');
INSERT INTO category_weight
VALUES ('1', '1','70','category1');
INSERT INTO category_weight
VALUES ('2', '71','100','category2');
INSERT INTO fight
VALUES ('1','1','1','1','2');
INSERT INTO fight
VALUES ('2','2','1','3','1');
Boxer with ID "1" won two fights in category1, so the result should be:
Lname Category Wins
Johnny category1 2
Here, try this:
SELECT b.lname,
cw.max_weight AS WEIGHT_CLASS,
COUNT(CASE WHEN f.winner = b.id THEN 1 ELSE NULL END) AS WINS
FROM boxer b
INNER JOIN fight f ON b.id = f.id_boxer1 OR b.id = f.id_boxer2
INNER JOIN category_weight cw ON f.id_category_weight = cw.id
GROUP BY b.lname, cw.max_weight

Primary Key which exists Multiple Times

I want to create a Table "News" as Example. In this Table I have a group_id and a lang_id. The group_id stays as for the ID for the "News" but as group_id because it has a Translation in some Languages. This group_id shall be autoIncrement if I insert a new "News" with different lang_id but the same for every lang_id I attach to the Insert Command.
For Example a Table what I want to Achieve.
I say here new News in German(1),English(2),French(3) it shall Create new Rows for this Langs with the same group_id but itself auto Increment.
Table: News
x-----------------------------x
| group_id| lang_id | news |
x---------|-----------|-------x
| 1 | 1 | Hallo |
| 1 | 2 | Hello |
| 1 | 3 | Holla |
| 2 | 1 | bye |
| 2 | 2 | byebye|
| 2 | 3 | Ciao |
x-----------------------------x
How could I achieve that with an Primary Key, group_id would be the Primary Key??
The primary key must be unique by definition.
This means that in your case, the primary key of the table should be the combination of group_id and lang_id.
As to the question if your group_id can be an identity column, That's possible, but you will have to use SET IDENTITY_INSERT for every new language for an existing content.
example:
CREATE TABLE tblNews
(
group_id int identity(1,1) not null,
lang_id int not null,
news varchar(20),
primary key(group_id, lang_id)
)
INSERT INTO tblNews VALUES(1, 'hello')
SET IDENTITY_INSERT tblNews ON
INSERT INTO tblNews (group_id, lang_id, news) VALUES(1, 2, 'hello')
INSERT INTO tblNews (group_id, lang_id, news) VALUES(1, 3, 'holla')
SET IDENTITY_INSERT tblNews OFF
INSERT INTO tblNews VALUES(1, 'good bye')
SET IDENTITY_INSERT tblNews ON
INSERT INTO tblNews (group_id, lang_id, news) VALUES(2, 2, 'byebye')
INSERT INTO tblNews (group_id, lang_id, news) VALUES(2, 3, 'Ciao')
SET IDENTITY_INSERT tblNews OFF
SELECT *
FROM tblNews
results:
group_id lang_id news
1 1 hello
1 2 hello
1 3 holla
2 1 good bye
2 2 byebye
2 3 Ciao
Though this design is clearly possible, I'm having a hard time recommending it. I would probably use a different approach for a multilingual database - one table for the news, one table for the languages, and one table for the translations:
tblNews
newsId int identity(1,1) primary key,
-- and whatever else content that is not language-dependent
tblLanguages
languageId int identity(1,1),
languageName nvarchar
tblNewsContent
NewsId (pk)
LanguageId (pk)
Contant nvarchar
The three table method is the best option, but I would say it needs something unique that isn't dependent on the translation.
If you are on sql server 2012+ you can use a sequence for this situation, though I can't say it is the best practice.
rextester: http://rextester.com/RRTJ4439
create sequence dbo.NewsIdSequence as int start with 1 increment by 1;
create table Lang (
id int not null
, name nvarchar(64)
, alias nvarchar(64)
, constraint pk_Lang primary key clustered (id)
, constraint uq_Lang_Name unique (name)
);
create table NewsLanguage (
news_id int not null
constraint df_NewsLanguage_news_id default (next value for dbo.NewsIdSequence)
, lang_id int not null
, title nvarchar(256) not null
, article nvarchar(max) not null
, constraint pk_NewsLanguage primary key clustered (news_id, lang_id)
, constraint fk_langLanguage_lang_id foreign key (lang_id) references lang(id)
);
insert into Lang (id, Name, alias)
select top 3 langid, name, alias
from syslanguages
order by langid;
declare #NextNewsId int;
set #NextNewsId = next value for dbo.NewsIdSequence;
insert into NewsLanguage( news_id, lang_id, title, article)
select #NextNewsId, 0, 'Hello', 'Hello ... '
union all select #NextNewsId, 1, 'Hallo', 'Hallo ... '
union all select #NextNewsId, 2, 'Bonjour', 'Bonjour ...';
set #NextNewsId = next value for dbo.NewsIdSequence;
insert into NewsLanguage( news_id, lang_id, title, article) values
(#NextNewsId, 0, 'Goodbye','Goodbye ...' )
, (#NextNewsId, 1, 'Auf Wiedersehen', 'Auf Wiedersehen ...' )
, (#NextNewsId, 2, 'Au Revoir', 'Au Revoir ...');
select *
from dbo.NewsLanguage nl
inner join dbo.Lang l on nl.lang_id = l.id
The three table method would be better, as Zohar Peled explained. Here's a version without hungarian notation:
create table Lang (
id int not null identity (1,1)
, name nvarchar(64)
, alias nvarchar(64)
, constraint pk_Lang primary key clustered (id)
, constraint uq_Lang_Name unique (name)
);
create table News (
id int not null identity (1,1)
, unique_column_of_importance nvarchar(64)
, constraint pk_News primary key clustered (id)
, constraint uq_News_Title unique (unique_column_of_importance)
);
create table NewsLanguage (
news_id int not null
, lang_id int not null
, title nvarchar(256) not null
, article nvarchar(max) not null
, constraint pk_NewsLanguage primary key clustered (news_id, lang_id)
, constraint fk_NewsLanguage_news_id foreign key (news_id) references news(id)
, constraint fk_NewsLanguage_lang_id foreign key (lang_id) references lang(id)
);

1 to 0-1 relationship?

I have 2 tables:
Member Table:
Id - Primary key
Name
Age
Manager Table:
Id,
MemberId - FK
Title
How I can write a 1 to 0-1 relationship?
1 Member to 0-1 Manager
This is I am getting, why not showing 1--- 0 1
In order to create the tables for this relationship you need:
CREATE TABLE member(
Id int,
Name varchar,
Age int
Primary key(Id)
);
CREATE TABLE manager(
Id int,
MemberId int,
Title varchar,
Primary key(Id),
Foreign key(MemberId) References member(id)
);
Note: When you don't specify anything for the MemberId, this means it can be NULL. This means that there is no MemberId value for some managers.