Oracle SQLDeveloper , using computed column with another table - sql

I'm trying to create a database which will be used by an app made for an auto repair shop.
I have a 'Services' table where several services are listed with the corresponding price (e.g oil change , 30$). I have another table "Repairs" where I'm storing information about the client's repairs.This table has a column price which must be calculated using each of the services prices in the table 'Services'.
I am familiar with the concept of using 'computed columns' , but it appears that I can't compute a column using values from another table.
Am i missing something here or how should i approach the problem?

Having a column named "Price" in the table "Repairs", if you are trying to store the total price of all services provided to a customer, you shouldn't do that.
Why don't you try to insert a service record to "Repairs" table for each service provided to a customer with a customer id and repair id. Each service will point to the "Services" table with a foreign key. This way you can query the total price of a repair or you can create a view that will show the total prices of all repairs.
Here are create scripts for sample tables:
-- Table named SERVICES:
CREATE TABLE "SERVICES"
(
"SERVICE_ID" NUMBER NOT NULL ENABLE,
"SERVICE_NAME" VARCHAR2(20 BYTE),
"SERVICE_PRICE" NUMBER,
CONSTRAINT "SERVICES_PK" PRIMARY KEY ("SERVICE_ID")
)
-- Table named CUSTOMERS:
CREATE TABLE "CUSTOMERS"
(
"CUSTOMER_ID" NUMBER NOT NULL ENABLE,
"CUSTOMER_NAME" VARCHAR2(20 BYTE),
CONSTRAINT "CUSTOMERS_PK" PRIMARY KEY ("CUSTOMER_ID")
)
-- Table named REPAIRS:
CREATE TABLE "REPAIRS"
(
"REPAIR_ID" NUMBER NOT NULL ENABLE,
"CUSTOMER_ID" NUMBER NOT NULL ENABLE,
"REPAIR_DATE" DATE,
CONSTRAINT "REPAIRS_PK" PRIMARY KEY ("REPAIR_ID")
)
-- Table named REPAIR_SERVICES:
CREATE TABLE "REPAIR_SERVICES"
(
"REPAIR_ID" NUMBER NOT NULL ENABLE,
"SERVICE_ID" NUMBER NOT NULL ENABLE,
CONSTRAINT "REPAIR_SERVICES_FK_1" FOREIGN KEY ("REPAIR_ID") REFERENCES "HR"."REPAIRS" ("REPAIR_ID") ENABLE,
CONSTRAINT "REPAIR_SERVICES_FK_2" FOREIGN KEY ("SERVICE_ID") REFERENCES "HR"."SERVICES" ("SERVICE_ID") ENABLE
)
If you structure your tables like the ones above, you can use the script below to create a view which will get you the total cost of repairs including all services. Or you can just use the script to make a query.
CREATE VIEW V_REPAIR_PRICES AS (
SELECT REPAIRS.REPAIR_ID, REPAIRS.REPAIR_DATE, CUSTOMERS.CUSTOMER_NAME, T1.TOTAL_PRICE
FROM REPAIRS
INNER JOIN CUSTOMERS ON REPAIRS.CUSTOMER_ID = CUSTOMERS.CUSTOMER_ID
INNER JOIN
(
SELECT REPAIR_SERVICES.REPAIR_ID, SUM(SERVICES.SERVICE_PRICE) as TOTAL_PRICE
FROM REPAIR_SERVICES
INNER JOIN SERVICES ON REPAIR_SERVICES.SERVICE_ID = SERVICES.SERVICE_ID
GROUP BY REPAIR_SERVICES.REPAIR_ID
) T1
ON REPAIRS.REPAIR_ID = T1.REPAIR_ID
)

Related

Check constraint depending on another table (mutually exclusive relation)

I need help with a project, a DB for hotel administration .
I have three tables, bookings, check_ins and cancellations. I consider that any booking can end either as an cancellation or a check-in, but not both simultaneously.
The fact is that I would need a constraint to verify, at check-in, whether the booking was cancelled, and, at cancellation, if people actually came and checked-in.
I used the same id for the three PKs, but it turns out that a check constraint, in Oracle, can't have either a user-defined-function or a select clause inside.
I.e:
I have tried both
ALTER TABLE chek_ins
ADD CONSTRAINT verif_ci CHECK(id_book NOT IN (SELECT id_book FROM bookings));
and creating a function verif_cancelled(id char(a_number))
In the same time I cannot write all of them in the same table, for the guest's identification is unknown until check-in and I use the tables bookings and check-ins as a sort of two intersection tables to link the guest and the room (to solve a M:M relation).
Do you have any suggestions? (this semester I am studying SQL for the first time)
Thank you in advance!
To start with, have a table of possible booking statuses that enumerates all the possible states a booking can be in:
CREATE TABLE statuses (
id NUMBER(1,0)
GENERATED ALWAYS AS IDENTITY
CONSTRAINT statuses__id__pk PRIMARY KEY,
description VARCHAR2(10)
CONSTRAINT statuses__desc__nn NOT NULL
CONSTRAINT statuses__desc__u UNIQUE
);
INSERT INTO statuses ( description )
SELECT 'Booked' FROM DUAL UNION ALL
SELECT 'Checked-In' FROM DUAL UNION ALL
SELECT 'Annulled' FROM DUAL;
ALTER TABLE statuses READ ONLY;
Then add a status column to your bookings table:
CREATE TABLE Bookings (
id NUMBER(10,0)
GENERATED ALWAYS AS IDENTITY
CONSTRAINT bookings__id__pk PRIMARY KEY,
status NUMBER(1,0)
CONSTRAINT bookings__status__nn NOT NULL
CONSTRAINT bookings__status__fk REFERENCES statuses ( id ),
CONSTRAINT bookings__id__status__u UNIQUE ( id, status )
)
Then you can add a virtual column to your CheckIns and Annullments tables that have the appropriate statuses so that an entry cannot be made into the table unless the booking is in the correct status:
CREATE TABLE CheckIns (
id NUMBER(10,0)
CONSTRAINT CheckIns__id__pk PRIMARY KEY,
status NUMBER(1,0)
GENERATED ALWAYS AS ( 2 )
CONSTRAINT CheckIns__status__nn NOT NULL
CONSTRAINT CheckIns__status__fk REFERENCES statuses ( id ),
CONSTRAINT CheckIns__id__status__fk
FOREIGN KEY ( id, status )
REFERENCES bookings ( id, status )
);
CREATE TABLE Annullments (
id NUMBER(10,0)
CONSTRAINT annullments__id__pk PRIMARY KEY,
status NUMBER(1,0)
GENERATED ALWAYS AS ( 3 )
CONSTRAINT annullments__status__nn NOT NULL
CONSTRAINT annullments__status__fk REFERENCES statuses ( id ),
CONSTRAINT annullments__id__status__fk
FOREIGN KEY ( id, status )
REFERENCES bookings ( id, status )
);
Then if you try to enter a booking with the Checked-In status into the Annullments table it will fail (and vice versa):
DECLARE
p_id Bookings.id%type;
BEGIN
INSERT INTO bookings ( status )
VALUES( ( SELECT id FROM statuses WHERE description = 'Checked-In' ) )
RETURNING id INTO p_id;
BEGIN
INSERT INTO CheckIns( id )
VALUES ( p_id );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( 'Could not check-in' );
END;
BEGIN
INSERT INTO Annullments ( id )
VALUES ( p_id );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( 'Could not annul booking' );
END;
END;
/
outputs:
Could not annul booking
and if you try to change a booking to another status while there is a dependent entry in another table then it will fail:
UPDATE Bookings
SET status = ( SELECT id FROM statuses WHERE description = 'Annulled' )
WHERE status = ( SELECT id FROM statuses WHERE description = 'Checked-In' )
Outputs:
ORA-02292: integrity constraint (FIDDLE_CWUTVSMRLOQQQVDQLCGR.CHECKINS__ID__STATUS__FK) violated - child record found
You could further improve the API by creating a package with functions to:
create a booking;
check-in a booking;
annul a booking;
etc.
db<>fiddle
Your check-in table has a booking ID, so that the check-in refers to a booking. This booking ID has probably a unique constraint (maybe it's even the table's primary key), in order to create the desired 1:{0,1} relation. Something along the lines of:
create table check_in
(
booking_id number(9),
check_in_time date,
number_of_persons number(2),
main_person_name varchar2(100),
constraint pk_check_in primary key (booking_id),
constraint fk_check_in_booking foreign key (booking_id) references booking (booking_id)
);
Your annulment table is probably constructed in the same way. However, as these are two different tables that the booking table is not aware of, you can both insert a check-in and an annulment for a booking, which you want to avoid. A check constraint is not possible, because it can only refer to the table itself, not to other tables. (Triggers in the check-in and annulment tables that check the other table to throw an exception in case of an entry there would be possible, but I don't recommend them in this scenario.)
1:1 relatated tables are a rare thing. I see that they are useful here, because you can make their columns mandatory or optional, e.g. the check-in time and main person name may be mandatory (NOT NULL) while the number of persons may be optional. If these were just attributes directly placed in the booking table instead, you could not make check-in time and main person name mandotory, because they must only be mandatory in case of a check-in.
Luckily, with 1:1 you can simply relate them the other way round. I.e. remove the booking ID from the two satellite tables, give them independent primary keys and refer to these primary keys in the booking table instead:
create table check_in
(
check_in_id number(9),
check_in_time date,
number_of_persons number(2),
main_person_name varchar2(100),
constraint pk_check_in primary key (check_in_id)
);
create table annulment
(
annulment_id number(9),
...,
constraint pk_annulment primary key (annulment_id)
);
create table booking
(
booking_id number(9),
room_no number(3),
start_date date,
end_date date,
person_name varchar2(100),
check_in_id number(9),
annulment_id number(9),
constraint pk_booking primary key (booking_id),
constraint fk_booking_check_in foreign key (check_in_id) references check_in (check_in_id),
constraint fk_booking_annulment foreign key (annulment_id) references annulment (annulment_id),
constraint uq_booking_check_in unique (check_in_id),
constraint uq_booking_annulment unique (annulment_id),
constraint chk_booking check (check_in_id is null or annulment_id is null)
);
You see how easy it is to place the desired check constraint in the booking table now.
A last remark: While it is great to have all those integrity checks in place and be able to, say, select all annulments placed in March, you might be able to do with a much simpler database. Instead of two satellite tables, you could just put the information in single columns, e.g. a CLOB, JSON, or Oracle object. It would be like using a mere note rather than a form in real live. Less reliable, but maybe sufficient for the job. The database would reduce to a single table:
create table booking
(
booking_id number(9),
room_no number(3),
start_date date,
end_date date,
person_name varchar2(100),
check_in_data varchar2(4000),
annulment_data varchar2(4000),
constraint pk_booking primary key (booking_id),
constraint chk_booking check (check_in_data is null or annulment_data is null)
);
As mentioned, the DBMS can not guarantee here the check-in and annulment data to be complete (i.e. to the DBMS it makes no difference if annulment_data contains 'Cancelled on Feb 9, 2019 by Mr. Miller, because of sickness' or just 'Cancelled by Mr. Miller' or even 'dum deedle dum'), but maybe you are fine with this - your app will make sure only complete data gets written to the database.

SQL/ORACLE- FOREIGN KEY using two columns from other tables

I'm trying to create an table in SQL*Plus that consults two columns from another table. For example,
If table A looks something like this:
CREATE TABLE Customers
(Customer_ID int NOT NULL PRIMARY KEY,
NAME Varchar(30) NOT NULL,
PHONE Varchar(12) NOT NULL,
OUTSTANDING_FEES Varchar(10) NULL);
And if my table B looks something like this:
CREATE TABLE Customer_Fees
(Fee_ID int NOT NULL PRIMARY KEY,
FEE_TYPE Varchar(20) NOT NULL,
AMOUNT Varchar(10) NOT NULL,
CUSTOMER_ID int NOT NULL);
I want to populate the OUTSTANDING_FEES in table A with the AMOUNT in table B, where the CUSTOMER_ID matches among the tables. For my purposes I can assume that any single Customer_ID in table B will only appear once in the table.
I've tried creating both tables, with the table A OUTSTANDING_FEES field being null and then making it a FOREIGN KEY that references table B's AMOUNT field, but it's not working since I need to make sure it also cross references the CUSTOMER_ID fields in both tables.
Thanks if you can help!
You can not create foreign key on a non-primary column from another table(s).
You'll have to create FK on FEE_ID.
CREATE TABLE Customers (
Customer_ID int NOT NULL PRIMARY KEY,
NAME Varchar(30) NOT NULL,
PHONE Varchar(12) NOT NULL,
OUTSTANDING_FEES Varchar(10) FOREIGN KEY REFERENCES Customer_Fees(FEE_ID)
);
You can use AMOUNT field in your select clause.
Select A.Customer_ID,B.AMOUNT from Customers A Join Customer_Fees B
on A.OUTSTANDING_FEES = B.FEE_ID
There are several things wrong with the data model posed in the question.
Defining OUTSTANDING_FEES and AMOUNT as varchar2columns is bad data modelling as both are surely intended to be numeric (monetary) values. Good practice is always to use the most appropriate datatype for the attribute we're modelling.
Building a foreign key between OUTSTANDING_FEES and AMOUNT is wrong because they are not unique identifiers. The amount of money owed by one customer can be the same as the amount of money owed by any other - even all - customers (at the start of term all students owe the same amount of tuition fees). So, a foreign which "references the CUSTOMER_ID fields in both tables" is all that is needed.
The data model doesn't provide any attribute which allows us to distinguish between fees which have been paid and fees which haven't.
The questioner states that "I can assume that any single Customer_ID in table B will only appear once in the table" but in real life we would expect Customers to have multiple fee records, unpaid and paid. Why not model that? Otherwise if there is truly a 1:1 relationship between Customer and Fee then there is no need for two tables.
So, here is an improved model. It uses proper datatype for monetary values; it enforces the foreign key between the two tables using CUSTOMER_ID; consequently it supports a one-to-many relationship between Customer and Fee; finally it tracks paid and unpaid fees.
create table customers
( customer_id integer not null constraint cust_pk primary key
, name varchar2(30) not null
, phone varchar2(12) not null
)
/
create table customer_fees
( fee_id integer not null constraint fees_pk primary key
, fee_type varchar2(20) not null
, amount number not null
, invoice_date date not null
, paid_date date null
, customer_id integer not null constraint fees_cust_fk references customers
)
/
Ah, but what about OUTSTANDING_FEES? Well, that information is derivable from the data in the two tables. There are many ways of writing this query, this approach is just a choice:
select cust.customer_id
, cust.name
, cust.phone
, fees.outstanding_fees
from customers cust
left outer join
( select fees.customer_id
, sum(case when fees.paid_date is null then fees.amount
else 0 end) as outstanding_fees
from customer_fees fees
group by fees.customer_id ) fees on fees.customer_id = cust.customer_id
/
Generally it is better to calculate aggregated values on demand rather than re-calculate them in every transaction. It scales better, certainly with OLTP volumes of data; the physics of a data warehouse is different, but I don't think that's what we're dealing with in this case.

How can set unique key checking by another table

i have create three tables: supplier, item and purchase. supplier id has relation with item table and item id has relation with purchase table. I do not want to insert itemid on purchase table in same supplier item. How can I set constraint?
CREATE TABLE csupplier(
supid NUMBER(10) PRIMARY KEY ,
supname VARCHAR2(30)
);
CREATE TABLE ctitem(
itemid NUMBER(10) PRIMARY KEY,
itemname VARCHAR2(50),
supid NUMBER(10)
);
ALTER TABLE CTITEM
ADD CONSTRAINT CTITEM_FK1 FOREIGN KEY(SUPID )REFERENCES CSUPPLIER(SUPID );
CREATE TABLE cPurchase(
purchaseid NUMBER(10) PRIMARY KEY,
itemid NUMBER(10),
purchaseqty NUMBER(10)
);
ALTER TABLE CPURCHASE
ADD CONSTRAINT CPURCHASE_FK1 FOREIGN KEY(ITEMID )REFERENCES CTITEM(ITEMID )
i don not want insert item-1 and item-3 in a same time under purchase
The problem is Oracle does not understand concept of at the same time. It understands transactions, it understands DML statements, it understands unique keys. So we need to frame your question in terms Oracle can understand: for instance, a given purchase cannot have more than one item from the same supplier.
Your first problem is that your data model can't support such a rule. Your cpurchase table has a primary key of purchaseid which means you have one record per item purchased. There is no set of purchased items against which we can enforce a rule. So, the first thing is to change the data model:
CREATE TABLE cPurchase(
purchaseid NUMBER(10) PRIMARY KEY );
CREATE TABLE cPurchaseItem(
purchaseid NUMBER(10),
itemid NUMBER(10),
purchaseqty NUMBER(10)
);
ALTER TABLE CPURCHASEITEM
ADD CONSTRAINT CPURCHASEITEM_PK PRIMARY KEY(PURCHASEID,ITEMID);
ALTER TABLE CPURCHASEITEM
ADD CONSTRAINT CPURCHASEITEM_FK1 FOREIGN KEY(PURCHASEID )REFERENCES CPURCHASE;
ALTER TABLE CPURCHASE
ADD CONSTRAINT CPURCHASE_FK2 FOREIGN KEY(ITEMID )REFERENCES CTITEM(ITEMID );
Now we have a header-detail structure which assigns multiple items to one purchase, which means we can attempt to enforce the rule.
The next problem is that supplierid is not an attribute of cpurchaseitem. There is no way to build a check constraint on a table or column which executes a query on another table. What you are after is a SQL Assertion, which is a notional construct that would allow us to define such rules. Alas Oracle (nor any other RDBMS) supports Assertions at the moment.
So that leaves us with three options:
Go procedural, and write a transaction API which enforces this rule.
Denormalise cpurchaeitem to include supplierid then build a unique constraint on (purchaseid, supplierid). You would need to populate supplierid whenever you populate cpurchaseitem.
Write an after statement trigger:
(Warning: this is coded wildstyle and may contain bugs and/or compilation errors.)
create or replace trigger cpurchaseitem_trg
after insert or update on cpurchaseitem
declare
rec_count number;
begin
select count(*)
into rec_count
from cpurchaseitem pi
join citem I on pi.itemid = i.itemid
group by pi.purchaseid, i.supplierid having count(*) > 1;
if rec_count > 0 then
raise_application_error(-20000
, 'more than one item for a supplier!');
end if;
end;
Frankly none of these solutions is especially appealing. The API is a solid solution but open to circumvention. The trigger will suffer from scaling issues as the number of purchases grows over time (although this can be mitigated by writing a compound trigger instead, left as an exercise for the reader). Denormalisation is the safest (and probably most performative) solution, even though it's not modelling best practice.
There are 2 solutions to your problem:
1. Alter the table cPurchase and add the supid column in the table. and make the unique key on this column. This will solve your problem.
CREATE TABLE cPurchase(
purchaseid NUMBER(10) PRIMARY KEY,
itemid NUMBER(10),
purchaseqty NUMBER(10),
supid NUMBER(10) UNIQUE KEY
);
If alter is not possible on this table, write a row level, Before Insert/update trigger. In this trigger write the logic to find the Supid based on the Item_id ctitem and them find any item on this supplier exists in your purchase table.
CREATE [ OR REPLACE ] TRIGGER SUP_CHECK
BEFORE INSERT
ON cPurchase
FOR EACH ROW
DECLARE
L_COUNT NUMBER;
BEGIN
SELECT COUNT(*) INTO L_COUNT
FROM cPurchase c
WHERE C.itemid in (Select itemid from ctitem ct where ct.supid = (Select supid
from ctitem where itemid = :new.itemid) );
EXCEPTION
WHEN ...
-- exception handling
END;

Database structure for "customer" table having many orders per customer and many items per order

I am trying to create a database where each customer has several orders(new orders daily) and each order has several items. I had planned creating a table of customers and creating a table per order and populating this table with an "items" table. I think this approach is too complicated and cumbersome since the number of orders can reach the thousands, I don't think having thousands of tables is maintainable. What do you think would be an appropriate structure for this? Any help is greatly appreciated.
Sorry if this is a noobish question, I am learning to program. And this is my first ever attempt at database design.
You need four tables, something like this:
Customers
Contains a list of customers. One row per Customer. Would contain all the customer's information - their contact details, etc...
Orders
Contains a list of orders. One row per order. Each order is placed by a customer and has a Customer_ID - which can be used to link back to the customer record. Might also store the delivery address, if different from the customers address from their record - or store addresses in separate tables.
OrderItems
Contains a list of order items. One row for each item on an order - so each Order can generate multiple rows in this table. Each item ordered is a product from your inventory, so each row has a product_id, which links to the products table.
Products
Contains a list of products. One row per product. Similar to the customers table, but for products - contains all the product details.
Here's the SQL code that you could use to create this structure - it will create a database for itself called mydb:
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE `mydb` ;
-- -----------------------------------------------------
-- Table `mydb`.`Customers`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`Customers` (
`ID` INT NOT NULL ,
`Name` TEXT NOT NULL ,
`PhoneNo` VARCHAR(45) NULL ,
PRIMARY KEY (`ID`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`Orders`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`Orders` (
`ID` INT NOT NULL ,
`customer_id` INT NULL ,
PRIMARY KEY (`ID`) ,
INDEX `fk_Order_1_idx` (`customer_id` ASC) ,
CONSTRAINT `fk_Order_1`
FOREIGN KEY (`customer_id` )
REFERENCES `mydb`.`Customers` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`Products`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`Products` (
`ID` INT NOT NULL ,
`Name` VARCHAR(45) NOT NULL ,
`Description` TEXT NULL ,
PRIMARY KEY (`ID`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`OrderItems`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`OrderItems` (
`ID` INT NOT NULL ,
`Order_ID` INT NOT NULL ,
`Product_ID` INT NOT NULL ,
`Quantity` INT NOT NULL ,
PRIMARY KEY (`ID`) ,
INDEX `fk_OrderItem_1_idx` (`Order_ID` ASC) ,
INDEX `fk_OrderItem_2_idx` (`Product_ID` ASC) ,
CONSTRAINT `fk_OrderItem_1`
FOREIGN KEY (`Order_ID` )
REFERENCES `mydb`.`Orders` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_OrderItem_2`
FOREIGN KEY (`Product_ID` )
REFERENCES `mydb`.`Products` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
USE `mydb` ;
There's no sense in creating a table per order. Don't do that. It's not practical, not maintainable. You won't be able to normally query your data. For starters all you need just four tables like this
customers
orders
order_items
products (or items)
Here is oversimplified SQLFiddle demo
I'd have something like a customer table along with orders and items tables. The primary key of customer is the foreign key of order. Items will then have a foreign key that matches the order it was placed on.
3 tables should be fine

How to add a unique constraint using a column from another table?

I have 3 tables in SQL Server 2008 R2 that look like these:
A COMPANY may have many LSPs. An LSP may have many SERVICEs.
And I need to make sure that SERVICE_CODE uniquely identifies a SERVICE record within a COMPANY. In other words, COMPANY_ID + SERVICE_CODE should uniquely identify a SERVICE record in the entire system.
For example: COMPANY-A may NOT have 2 services (with 2 different SERVICE_IDs) with the same SERVICE_CODE. But COMPANY-A and COMPANY-B may both have 2 separate SERVICES (again, with different SERVICE_IDs) with SERVICE_CODE = "PREMIUM".
I need something like this:
alter table "SERVICE"
add constraint "SERVICE_Index01"
unique ("COMPANY_ID", "SERVICE_CODE")
But (obviously) this fails because the COMPANY_ID column is not in the SERVICE table.
Thanks in advance for any help.
You could use an indexed view as an external constraint:
CREATE VIEW dbo.CompanyServices
WITH SCHEMABINDING
AS
SELECT
c.COMPANY_ID,
s.SERVICE_CODE
FROM dbo.COMPANY c
INNER JOIN dbo.LSP l ON c.COMPANY_ID = l.COMPANY_ID
INNER JOIN dbo.SERVICE s ON l.LSP_ID = s.LSP_ID
GO
CREATE UNIQUE CLUSTERED INDEX UQ_CompanyServices
ON dbo.CompanyServices (COMPANY_ID, SERVICE_CODE);
The index will make sure there's no duplicates of (COMPANY_ID, SERVICE_CODE) in your data.
Is each company limited to a single LSP? Is Service_Code unique (or could there be two service codes "PREMIUM" with different Service_IDs)?
CREATE TABLE dbo.Company
(
CompanyID INT PRIMARY KEY
-- , ...
);
CREATE TABLE dbo.LSP
(
LSPID INT PRIMARY KEY,
CompanyID INT FOREIGN KEY REFERENCES dbo.Company(CompanyID) -- UNIQUE?
-- , ...
);
CREATE TABLE dbo.Service
(
ServiceID INT PRIMARY KEY
-- , ...
);
CREATE TABLE dbo.LSP_Service
(
LSPID INT FOREIGN KEY REFERENCES dbo.LSP(LSPID),
ServiceID INT FOREIGN KEY REFERENCES dbo.Service(ServiceID),
PRIMARY KEY (LSPID, ServiceID)
);
Add COMPANY_ID to service table.
If you need rows in Service table to be unique by this id it makes sense to keep a foreign key reference in this table.