Primary Key which exists Multiple Times - sql

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

Related

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

create table with auto increment as first col and data type as second

maybe I am over thinking this these are the instructions
create four new tables: my_interests, my_professions, my_seeking and my_status. These tables should contain two columns: one for an id and another for the data value. Populate these from the appropriate columns in the my_contacts table. Once your new tables are ready, you can replace the original data columns with id numbers matching the applicable id values in your new my_professions and my_status tables.
SO THE WAY I READ THE FIRST IS I HAVE THE FIRST COL AS AN AUTO INCREMENT PRIMARY KEY, THE NEXT VALUE WOULD BE THE DATA TYPE, I AM STARTING WITH MY_STATUS FIRST AND THE TYPES WOULD BE
COMMITED_RELATIONSHIP
DIVORCED
MARRIED
SINGLE
WIDOWED
AND MY AUTO_INC NUMBER WOULD CORRESPOND TO THE ID OF DATA, IN OTHER WORDS
01**COMMITED
02**DIVORCED
03** AND SO ON
AM I CORRECT IN THE WAY I AM READING THE INSTRUCTIONS?
AND IF SO NOT QUITE SURE HOW TO DO THIS, I KNOW HOW TO CREATE THE TABLE, AND I UNDERSTAND INSERT INTO AND VALUES, BUT HOW DO I INSERT THE VALUES AND AUTO INCREMENT? I REALLY DISLIKE PROGRAMMING :(
thanks in advance
Create table my_status (status_id int NOT NULL AUTO_INCREMENT,status_type varchar(20), PRIMARY KEY (status_id)); To Create
insert into my_status (status_type) values("Single"),("Divorced"),("Married"); to insert
Create table my_status (status_id int NOT NULL AUTO_INCREMENT,status_type varchar(20), PRIMARY KEY (status_id)); To Create
insert into my_status (status_type) values("Single"),("Divorced"),("Married"); to insert
Possible Answer to your 2nd question
CREATE TABLE `my_status` (
`status_id` int(11) NOT NULL auto_increment,
`status_type` varchar(30),PRIMARY KEY(`status_id`) );
CREATE TABLE `my_profession` (
`profession_id` int(11) NOT NULL auto_increment,
`profession_type` varchar(30),PRIMARY KEY(`profession_id`) );
CREATE TABLE `my_contacts` (
`id` int(11) NOT NULL auto_increment,
`last_name` varchar(30) ,
`first_name` varchar(20) ,
`email` varchar(50) ,
`gender` char(1),
`birthday` date ,
`profession_id` int(11),
`location` varchar(50),
`status_id` int(11),
`interests` varchar(200),
`seeking` varchar(200),
PRIMARY KEY (`id`),
FOREIGN KEY (status_id)
REFERENCES my_status(status_id),
FOREIGN KEY (profession_id)
REFERENCES my_profession(profession_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert into my_status (status_type) values("Single"),("Divorced"),("Married");
insert into my_profession (profession_type) values("Writer"),("CA"),("Developer");
INSERT INTO `my_contacts` (`last_name`,`first_name`,`email`,`gender`,`birthday`,`profession_id`,`location`,`status_id`,`interests`,`seeking`) VALUES ('Anderson','Jillian','jill_anderson# \nbreakneckpizza.com','F','1980-09-05',1,'Palo Alto, CA',1,'kayaking, reptiles','relationship, friends');
INSERT INTO `my_contacts` (`last_name`,`first_name`,`email`,`gender`,`birthday`,`profession_id`,`location`,`status_id`,`interests`,`seeking`) VALUES ('Kenton','Leo','lkenton#starbuzzcoffee.com','M','1974-01-10',2,'San Francisco, CA',2,'women','women to date');
INSERT INTO `my_contacts` (`last_name`,`first_name`,`email`,`gender`,`birthday`,`profession_id`,`location`,`status_id`,`interests`,`seeking`) VALUES ('McGavin','Darrin',' captainlove#headfirsttheater.com','M','1966-01-23',3,'San Diego, CA',3,'sailing, fishing, yachting','women for casual relationships');
INSERT INTO `my_contacts` (`last_name`,`first_name`,`email`,`gender`,`birthday`,`profession_id`,`location`,`status_id`,`interests`,`seeking`) VALUES ('xyz','abc',' xyz#abc.com','F','1966-01-24',1,'San Diego, CA',3,'sailing, fishing, yachting, golfing','women for casual relationships');
select * from my_contacts;
select * from my_status;
select * from my_profession;
SELECT * FROM my_contacts WHERE status_id IN
(SELECT status_id FROM my_status WHERE status_type = 'Married');
Hope this is what you want!!!
UPDATE my_contacts
SET status='1'
WHERE status='committed relationship';
UPDATE my_contacts
set status= '2'
where status='divorced';
UPDATE my_contacts
set status= '3'
where status='married';
UPDATE my_contacts
set status= '4'
where status='single';
UPDATE my_contacts
set status= '5'
where status='widowed';
THANKS EVERYONE!! (yeah that time I shouted, lol!)

Sql table where row may only be inserted if unique column

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

How to insert row in table with foreign key to itself?

I have table that has foreign key for itself. Column parentid is foreign key and it cannot be NULL.
if I doINSERT INTO mytable(name) VALUES('name'), so it says that can't insert NULL to parentid. BUT, what value I can set to it if no row was inserted yet?!
How I can write script that will add row to this table?
Thank you
Remove the NOT NULL constraint, as it is an inappropriate constraint. If you do not have a ParentId then the value is NULL and should be allowed. Creating a dummy row just to have a dummy parentid creates unnecessary dependencies.
A trick: Have a dummy row with a dummy key, say 99999. Insert with this as the FK, and then change the FK to its real value. And do it in a transaction.
Disable the FK in charge.
Then do the insert
Then do an update with the new (generated?) PK-ID into the Self-FK-Field
Then Enable the FK back.
LIke so:
ALTER TABLE [Client] NOCHECK CONSTRAINT [FK_Client_MainClient]
INSERT INTO Client VALUES ...
#ClientID = SCOPE_IDENTITY()
IF #IsMainClient=1
BEGIN
UPDATE [Client]
SET MainClientID = #ClientID
WHERE ClientID = #ClientID
END
ALTER TABLE [Relatie] WITH CHECK CHECK CONSTRAINT [FK_Relatie_Relatie]
How to make a dummy row with both id and parentid equal to -1:
CREATE TABLE mytable(
id int NOT NULL IDENTITY,
parentid int NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (parentid) REFERENCES mytable(id)
) ;
SET IDENTITY_INSERT mytable ON ; <-- this allows you to insert the
INSERT INTO mytable(id, parentid) <-- auto incremented identity field
VALUES (-1, -1);
SET IDENTITY_INSERT mytable OFF ;
SELECT * FROM mytable ;
| id | parentid |
| -1 | -1 |
If you have many data from other tables that you want to transfer into this table, you can set the IDENTITY_INSERT variable to ON, insert the data and then set it to OFF again.
But as others said, it might be better to just remove the NOT NULL constraint from the parentid field.
You can alter the column to allow null then set the fk to the new identity and enable not null again.
This should work, though maybe there is a better way
CREATE TABLE mytable
(
id int IDENTITY(1,1) primary key,
name varchar(50) not null,
parentid int not null
)
go
alter table mytable
add constraint FK_mytable_parentid FOREIGN KEY ( parentid ) references mytable(id)
ALTER TABLE mytable alter column parentid int null;
insert into mytable(name)
select 'test'
update mytable
set parentid = SCOPE_IDENTITY()
where id = SCOPE_IDENTITY()
ALTER TABLE mytable alter column parentid int not null;
select * from mytable
drop table mytable
From what I understood you already have id before inserting and you can't insert it because identity field isn't letting you to.
Like you mentioned in your comment:
in 1 table I have the rows 34 'name1'
34, 35 'name2' 34 (id,name,parentid)
and I want to copy them to other table
First table
create table Table1
(
id int not null primary key,
name varchar(20) not null,
parentId int not null
)
insert Table1
values
(34, 'name1', 34),
(35, 'name2', 34)
Second table:
create table Table2
(
id int identity(1, 1) primary key,
name varchar(20) not null,
parentId int not null foreign key references Table2(id)
)
Copying data from the first table to the second one:
set identity_insert Table2 on
insert Table2(id, name, parentId)
select *
from Table1
set identity_insert Table2 on
[Update]
If the second table already has values then:
alter table Table2
add oldId int null
alter table Table2
alter column parentId int null
go
insert Table2(name, oldId)
select name, id
from Table1
update tt3
set parentId = tt2.id
from Table2 tt3
join Table1 tt1 on
tt1.id = tt3.oldId
join Table2 tt2 on
tt1.parentId = tt2.oldId
alter table Table2
drop column oldId
alter table Table2
alter column parentId int not null
Don't reference an IDENTITY column as a self-referencing foreign key. Use an alternative key of the table instead. I guess you are using IDENTITY as a surrogate key but every table ought to have a natural key as well, so the IDENTITY column shouldn't be the only key of your table.

with 2 field that allow null how to make a constrain that at only one must be filled?

to simplify this let take that table:
table1
-------------
id unique primary int
myVal1 int null (fk)
myVal2 int null (fk)
myData int not null
what would be the best way to create a constrain on this table so only one value can be filled?
these would work:
insert into table1 (myval1,myData) values (1,234)
insert into table1 (myval2,myData) values (1,123)
these would not work:
insert into table1 (myData) values (234)
insert into table1 (myVal1,myval2,myData) values (1,2,123)
try using a check constraint:
CREATE TABLE dbo.Table1
(
rowID int NOT NULL primary key identity(1,1),
myVal1 int NULL,
myVal2 int NULL,
myData int NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE dbo.Table1 ADD CONSTRAINT
CK_Table1_myVal1_or_myVal2 CHECK ((myVal2 IS NOT NULL AND myVal1 IS NULL) OR (myVal2 IS NULL AND myVal1 IS NOT NULL))
GO