I am trying to create a many-to-many relationship between two tables Asset and Inventory, I created an intermediate table called Asset_Inventory
One or more assets can be in an inventory, and an inventory can be in one or more assets,but it does not allow me to insert data
Mistake
The data in row 2 was not committed. Error source Message: Violation of PRIMARY KEY CONSTRAINT
PK_INVENTORY_ASSET. Cannot insert duplicate key in object dbo.Inventory_Asset. The duplicate key value (1,1).
-- TABLE INVENTORY
GO
CREATE TABLE Inventory
(
[inventory_id] INT NOT NULL IDENTITY(1,1),
[company_id] INT,
[name] VARCHAR(50),
[observations] VARCHAR(500),
[date_created] DATETIME DEFAULT(GETDATE()),
CONSTRAINT PK_INVENTORY PRIMARY KEY (inventory_id),
CONSTRAINT FK_INVENTORY_COMPANY FOREIGN KEY(company_id) REFERENCES Company(company_id)
)
-- TABLE ASSET
GO
CREATE TABLE Asset
(
[asset_id] INT NOT NULL IDENTITY(1,1),
[company_id] INT,
[code] INT,
[model_name] VARCHAR(50),
[model_number] VARCHAR(50),
[serial_number] VARCHAR(30),
[price] DECIMAL(10,2),
CONSTRAINT PK_ASSET_ID PRIMARY KEY(asset_id),
CONSTRAINT FK_ASSET_COMPANY FOREIGN KEY(company_id) REFERENCES Company(company_id),
CONSTRAINT UQ_ASSET_CODE UNIQUE(code)
)
--TABLE INVENTORY_ASSET
CREATE TABLE Inventory_Asset
(
asset_id INT,
inventory_id INT,
CONSTRAINT PK_INVENTORY_ASSET PRIMARY KEY (asset_id,inventory_id),
CONSTRAINT FK_ASSET_ID FOREIGN KEY (asset_id) REFERENCES Asset(asset_id),
CONSTRAINT FK_IVENTORY_ID FOREIGN KEY (inventory_id) REFERENCES Inventory(inventory_id)
)
UPDATED
it's hard for me to ask this question
N companies have many assets; you need to have control over them. Have a list of assets by company, to which employee it was assigned, or in which department of the company that asset is located. a total of assets by company etc..
What is the purpose of this?
I need to physically label (bar code) each asset with a unique code
Cell phones, monitors, keyboards, servers, PCs, laptops, chairs, furniture, etc...
Once all the assets have been tagged in the
company 1
company 2
company 3
I have to scan each code printed on the asset and make a monthly cut
I have to create a report with this information
In the company 1 an inventory was made in the month of January
Cut of the month of January:
20 monitors (10 dell , 10hp)
20 chairs
10 computers
the next month this cut is made again to see if there is the same quantity, if the 2 inventories of the month of January and February are not the same, some asset was stolen and to be able to identify this
an inventory can have one or more assets, and an asset can be in one or more inventories.
I think I can do this with the Inventory_Asset table
asset_id
iventory_id
1
1
1
1
2
1
3
1
1
1
1
2
2
2
1
2
6
2
1
3
4
3
3
3
1
3
Am I wrong with my solution?
--TABLE INVENTORY_ASSET
CREATE TABLE Inventory_Asset
(
[inventory_id] INT,
[asset_id] INT,
CONSTRAINT FK_ASSET_ID FOREIGN KEY (asset_id) REFERENCES Asset(asset_id),
CONSTRAINT FK_IVENTORY_ID FOREIGN KEY (inventory_id) REFERENCES Inventory(inventory_id)
)
CONSTRAINT PK_INVENTORY_ASSET PRIMARY KEY (asset_id,inventory_id),
PRIMARY KEYS must be unique. That is why you can't add this -- since it conflicts with your primary key. If you don't want that to be unique but you do expect to use it as an index you can make an index that does not enforce uniqueness and make something else your primary key.
The answers and comments here are all correct; but perhaps we need to review what a many-to-many relation can be used for.
Option 1: we just need a link between two entities
This is what you have done. Basically it will serve if you want to know in which Inventories a given Asset is, which Assets a given Inventory holds, etc. In this scenario when you have established the link once (Asset 1 is in Inventory 1) there's no reason to re-establish it with further records.
Option 2: we want to store some information about the relation
This is what I suspect you really wanted. Suppose for instance you want to store how many Assets of type 1 are in Inventory 1. In this case what you want is to add a "Quantity" column to your Inventory_asset table. Once more, if there are 3 Assets of type 1 in Inventory 1 you won't duplicate the records: you will just create one record and put 3 in the new column.
Option 3: we really need multiple records
Suppose you want to store not only how many Assets of a given type are in a given Inventory, but also on which date a few of them were put there. So you may have 2 type 1 Assets put in Inventory 1 on May, 1st, and other 3 of them on May, 15th. In this case the date must become part of the PK, so that the system (and you too!) can distinguish between the different records. You may also decide to create an auto-increment column and use it as a PK instead; but note that while it may be simpler to handle, it would allow for unvoluntarily creating duplicate records, so I wouldn't recommend it.
Related
Business problem: Suppose that we have a few medical centers and doctors, who work in these centers. Obviously, many doctors can work in one center. But also one doctor can work in many centers at the same time. And we have to store information about who is the head doctor of each medical center (each medical center can have only one head doctor and one doctor can be the head doctor in multiple centers).
Question: What is the best way to construct database tables to serve these business requirements?
I see two variants (described below) but if you see more, please, let me know.
Variant 1
In this variant, we store information about the head doctor in the join table jobs. I see two disadvantages here:
the column jobs.is_head will contain false in most cases and it looks strange (and looks like we store unnecessary information).
we need somehow to restrict adding two head doctors into one center.
create table doctors
(
id bigint not null
constraint doctors_pk
primary key,
name varchar not null
);
create table medical_centers
(
id bigint not null
constraint medical_centers_pk
primary key,
address varchar not null
);
create table jobs
(
medical_center_id bigint not null
constraint centers_fk
references medical_centers,
doctor_id bigint not null
constraint doctors_fk
references doctors,
is_head boolean not null,
constraint jobs_pk
primary key (doctor_id, medical_center_id)
);
Variant 2
In this variant, we store information about the head doctor in medical_centers table. Two disadvantages again:
we have two types of relationships between tables now: many to many and one to many (because one doctor can be the head doctor in multiple centers), which is a bit complicated, especially considering that I want to use this schema through ORM framework (JPA implementation).
we have to somehow restrict setting doctor as a head doctor if this doctor is not working in this center.
create table doctors
(
id bigint not null
constraint doctors_pk
primary key,
name varchar not null
);
create table medical_centers
(
id bigint not null
constraint medical_centers_pk
primary key,
address varchar not null,
head_doctor_id bigint
head_doctor_id_fk
references doctors
);
create table jobs
(
medical_center_id bigint not null
constraint centers_fk
references medical_centers,
doctor_id bigint not null
constraint doctors_fk
references doctors,
constraint jobs_pk
primary key (doctor_id, medical_center_id)
);
It is a trade - off, but operational complexity or some(?) storage. I think variation 1 looks good. If you want to change a little bit you can add another table called "med_cen_heads" with unique constraint on med_center_id column. Thus, we prevent adding a second doctor to same medical center. The hard part is checking if the head doctor works in the medical center or not before insert.
INSERT INTO med_cen_heads
SELECT medical_center_id, doctor_id
FROM jobs
WHERE EXISTS (SELECT 1 FROM jobs WHERE medical_center_id = 'medical_center_id_to_insert' and doctor_id 'doctor_id_to_insert');
Also, you can create "before insert trigger" to check if values exist in jobs table.
It could look like this:
create table doctors
(
id bigint not null
constraint doctors_pk
primary key,
name varchar not null
);
create table medical_centers
(
id bigint not null
constraint medical_centers_pk
primary key,
address varchar not null
);
create table jobs
(
job_id serial,
medical_center_id bigint not null
constraint centers_fk
references medical_centers,
doctor_id bigint not null
constraint doctors_fk
references doctors
);
create table med_cen_heads
(
medical_center_id bigint unique not null,
doctor_id bigint not null
);
This will save you from unnecessary storage but, BOOLEAN data type is just 1 byte. Let' s assume you have 1 billion med_center - doctor pairs(I don' t think you will have). In this way you only store 0.93132 GB more for your extra column. Of course, becuse there is only one head doctor in a medical center this column will be skewed. Yet when you query normal doctors this column will not be your concern, you should use "doctor_id" or any other columns.
In short, from my point of view stick with variation 1 with this small change:
create unique index unique_row on jobs(medical_center_id) where is_head;
Check you cannot add a second head doctor to a medical center.
I have a main large table which I have had to put into 3rd normal form and into smaller tables (with primary and foreign keys linking them). The table is about renting books.
I have a customer table which I need to create a primary key for. In the main large table there are duplicates of the customer_id, as the table as a whole is for renting the books, so one customer may have more than one renting.
The table I am currently trying to add a primary key for will not have any nulls or duplicates, however i am unsure how to create the primary key for this without the error- unsure how to make it unique.
CREATE TABLE customer AS
SELECT cust_id, country_id, name, address, postcode
FROM BOOKS
WHERE cust_id != 0;
ALTER TABLE customer
ADD PRIMARY KEY (cust_id);
Is anyone able to help me in how to create the primary key on my customer table, but just taking each unique cust_id from the main table.
In SQL Server the straightforward way to add unique keys is to use IDENTITY. Identity fields are integer fields that auto populate successive values by a specified start value and interval. If you don't specify the interval it will start at 1 and increase the value by 1 each time a value is assigned.
While it's usually done when creating a table, you can do it in your ALTER TABLE step, and it will assign values when added to an existing table. I've explicitly specified the start value and interval that matches the default to show the syntax :
ALTER TABLE customer
ADD cust_id int not null PRIMARY KEY IDENTITY(1,1)
I Have table three tables:
The first one is emps:
create table emps (id number primary key , name nvarchar2(20));
The second one is cars:
create table cars (id number primary key , car_name varchar2(20));
The third one is accounts:
create table accounts (acc_id number primary key, woner_table nvarchar2(20) ,
woner_id number references emps(id) references cars(id));
Now I Have these values for selected tables:
Emps:
ID Name
-------------------
1 Ali
2 Ahmed
Cars:
ID Name
------------------------
107 Camery 2016
108 Ford 2012
I Want to
Insert values in accounts table so its data should be like this:
Accounts:
Acc_no Woner_Table Woner_ID
------------------------------------------
11013 EMPS 1
12010 CARS 107
I tried to perform this SQL statement:
Insert into accounts (acc_id , woner_table , woner_id) values (11013,'EMPS',1);
BUT I get this error:
ERROR at line 1:
ORA-02291: integrity constraint (HR.SYS_C0016548) violated - parent key not found.
This error occurs because the value of woner_id column doesn't exist in cars table.
My work require link tables in this way.
How Can I Solve This Problem Please ?!..
Mean: How can I reference tables in previous way and Insert values without this problem ?..
One-of relationships are tricky in SQL. With your data structure here is one possibility:
create table accounts (
acc_id number primary key,
emp_id number references emps(id),
car_id number references car(id),
id as (coalesce(emp_id, car_id)),
woner_table as (case when emp_id is not null then 'Emps'
when car_id is not null then 'Cars'
end),
constraint chk_accounts_car_emp check (emp_id is null or car_id is null)
);
You can fetch the id in a select. However, for the insert, you need to be explicit:
Insert into accounts (acc_id , emp_id)
values (11013, 1);
Note: Earlier versions of Oracle do not support virtual columns, but you can do almost the same thing using a view.
Your approach should be changed such that your Account table contains two foreign key fields - one for each foreign table. Like this:
create table accounts (acc_id number primary key,
empsId number references emps(id),
carsId number references cars(id));
The easiest, most straightforward method to do this is as STLDeveloper says, add additional FK columns, one for each table. This also bring along with it the benefit of the database being able to enforce Referential Integrity.
BUT, if you choose not to do, then the next option is to use one FK column for the the FK values and a second column to indicate what table the value refers to. This keeps the number of columns small = 2 max, regardless of number of tables with FKs. But, this significantly increases the programming burden for the application logic and/or PL/SQL, SQL. And, of course, you completely lose Database enforcement of RI.
I have Four tables,
DiscountCode which describes a discount.
Bundles which is basically sold as a product so it contains a productcode
Products which are sold as products so it also contains a productcode
ProductDiscount code meant to be used to describe the fact that a Bundle/Product can have multiple discount codes associated with it.
**Discount Code**
Id
Name
Code ex. SUMMER10 ie 10% off summer products
...
**Bundles**
Id
Name
ProductCode *Unique* Ex..ABC123
...
**Products**
Id
Name
ProductCode *Unique* Ex.. XYZ1234
...
**ProductDiscountCode**
DiscountId
ProductCode FK to both Products.ProductCode AND Bundles.ProductCode
Records in ProductDiscountCode:
1 ABC123
1 XYZ1234
1 URS576 <-- prevent this if Prod or Bundle does NOT contain URS576
My question is in regards to the ProductCode in the Ternary table. NOW KEEP IN MIND PELASE SQL IS NOT MY STRONG SUIT! Can/Should the ProductCode column in the ternary table be foreign keyed to TWO Separate tables in an attempt to restrict it's content to a product code that is EITHER contained in the Bundle table OR the Product table, assuming the productcode in bundles and products is unique between the two tables (to be enforced by business rules). Or does the ProductDiscountCode table need the following
**ProductDiscountCode**
DiscountId
ProductCode FK to ProductCode in Product
BundleCode FK to ProductCode in Bundle
OK, it is never a good idea to try to constrain to two differnt FKs for the same field, that is a sign of incorrect design.
Why is bundle a separate table if it is not using the product codes from the product table? WHy not add a column to the product table to identify if the line item is a bundle or an individual product and stopre both there?
If you're going to use a Foreign Key then every value must exist as a key on the other table so you can't constrain your column Product Keys if it's got Bundle Keys in it and vice versa.
If you need to ensure referential integrity then you'll need to have two Nullable columns.
You should probably also have a Check Constraint to make sure that one and only one of the codes is null.
CREATE TABLE ProductDiscountCode
(
DiscountId int,
ProductCode varchar(12) null,
BundleCode varchar(12) null,
CONSTRAINT ProductDiscountCode_PK PRIMARY KEY(DiscountId),
CONSTRAINT FK_ProductDiscountCode_DiscountId FOREIGN KEY (DiscountId) REFERENCES DiscountCode (Id),
CONSTRAINT FK_ProductDiscountCode_BundleCode FOREIGN KEY (BundleCode) REFERENCES Bundle (ProductCode),
CONSTRAINT FK_ProductDiscountCode_ProductCode FOREIGN KEY (ProductCode) REFERENCES Product (ProductCode),
CONSTRAINT ProductCodeExists CHECK (
(ProductCode IS NULL AND BundleCode IS NOT NULL)
OR
(ProductCode IS NOT NULL AND BundleCode IS NULL)
)
)
If for some reason you really need to be able to show the Product Codes as a single column then you could do something along these lines
CREATE TABLE ProductDiscountCode
(
DiscountId int,
SingleProductCode varchar(12) null,
BundleCode varchar(12) null,
ProductCode as ISNULL (SingleProductCode ,BundleCode ) ,
CONSTRAINT ProductDiscountCode_PK PRIMARY KEY(DiscountId),
CONSTRAINT FK_ProductDiscountCode_DiscountId FOREIGN KEY (DiscountId) REFERENCES DiscountCode (Id),
CONSTRAINT FK_ProductDiscountCode_BundleCode FOREIGN KEY (BundleCode) REFERENCES Bundle (ProductCode),
CONSTRAINT FK_ProductDiscountCode_SingleProductCode FOREIGN KEY (SingleProductCode) REFERENCES Product (ProductCode),
CONSTRAINT SingleProductCodeExists CHECK ((SingleProductCode IS NULL AND BundleCode IS NOT NULL) OR (SingleProductCode IS NOT NULL AND BundleCode IS NULL))
)
But you do have to ask yourself first whether using Foreign key restraints is actually necessary in the first place.
Having two columns for product code could speed up your Select queries slightly but by having to decide which column you are storing the product code in the updates, deletes and inserts will be made more complicated.
Please first take a look at the thread: Multiple foreign key constraints on a single column in SQL Server 2008
ProductCode is a FK in Bundle and then you want another foreign key to the same column in ProductDiscountCode. You are double referencing same field and that does not seem to be good designing. You should restrict the values in the table at the moment of the insert, constraints are not meant for all kinds of validations.
I am designing the database for an accounting system, currently working on the Expenses table.
According to IRS rules, whenever you update a row in any accounting table, you need to cancel out the existing row by negating its values, and create a new row with the modified information, like so:
Set the row's Status flag to "Modified"
Create an identical copy of this row, with all Money fields negated, so that the sum of the two rows is 0
Create a 3rd row, identical to the first one, with the modified data
Each expense has an identity field called ID for internal identification purposes, and an ExpenseID field, which identifies the transaction to the users. The two cannot be the same, because
ExpenseID can be repeated twice if the transaction was modified and its row was duplicated.
ExpenseIDs MUST be consecutive and NEVER have gaps, while identity fields can skip numbers if a transaction is rolled back and the identity is not reseeded.
In general, I believe the primary key should have no business meaning whatsoever.
My problem is that there are other tables used to link these expenses Many-To-Many to other objects in our system. E.g.: each expense can be linked to documents, folders, users, etc.
So it looks something like this:
create table Expenses (
ID int not null identity(1,1),
ExpenseID int not null,
Amount Money not null,
Status tinyint not null,
[...]
)
create table Expenses_Users (
ExpenseID int not null,
UserID int not null
)
alter table Expenses_Users add constraint FK_Expenses_Users_Expenses
foreign key (ExpenseID) references Expenses (ID)
alter table Expenses_Users add constraint FK_Expenses_Users_Users
foreign key (UserID) references Users (ID)
Now, because of the IRS guidelines, I have to duplicate not only rows in the Expenses table, but also in Expenses_Users, and any other table that links Expenses to other tables.
I have two ideas on how to solve this:
Option One: Normalize Expenses like this:
create table Expenses (
ID int not null identity(1,1),
ExpenseID int not null,
Status tinyint not null,
[...]
)
create table ExpensesNormalized (
ExpenseID int not null,
Amount Money not null
)
alter table ExpensesNormalized add constraint FK_ExpensesNormalized_Expenses
foreign key (ExpenseID) references Expenses(ExpenseID)
This means I'll only have to link external tables to Expenses, not ExpensesNormalized. Also, when updating an expense, I'll only duplicate and negate the data in ExpensesNormalized, which means I'll have far less redundant data in the Expenses table.
However, I'll have to use a JOIN clause every single time I SELECT from Expenses. I fear a performance hit because of this.
Option Two: Use the same tables I use now, but have the field Expenses_Users.ExpenseID point to the field Expenses.ExpenseID. This means that I won't have to duplicate any external objects because they'll point to ExpenseID, which may occur several times.
However, this will not be a real foreign key because SQL Server does not allow foreign keys to non-unique fields, so I'll have to implement foreign key logic in a trigger.
I'm having a hard time deciding between these two options. Any feedback would be appreciated.