compatibility between the same table fields - sql

I've got shop types. Some shop types doesn't compatible with other shop types (for example you can't sell car parts near food for example).
Here is my table schema:
create table TShopCompatibility
(
idshoptype1 int NOT NULL,
idshoptype2 int NOT NULL,
constraint pkSHOPCOMP primary key(idshoptype1,idshoptype2),
constraint fkSHOPCOMP1 foreign key(idshoptype1) references TShopType(idshoptype),
constraint fkSHOPCOMP2 foreign key(idshoptype2) references TShopType(idshoptype),
constraint cSHOPCOMP12 check(idshoptype1>idshoptype2)
)
I've got these values:
2 - 1
3 - 1
5 - 1
5 - 2
10 - 9
12 - 11
13 - 10
How where id - shoptypes. How can I get what shops are compatible for example with idshoptype = 2?

You would need to select the shop types where 2 is present in idshoptype1 or idshoptype2, i.e.
SELECT idshoptype1 FROM TShopCompatibility WHERE idshoptype2 = 2
UNION
SELECT idshoptype2 FROM TShopCompatibility WHERE idshoptype1 = 2
Then you can join the result of this query with shops table to get the shop information.

Related

How to return materials from a product or product levels

I have a table that contains products and each product has other products that are used to manufacture. The products that are used to manufacture the parent product can be manufactured as well and contain other manufactured products. Example:
ID_PRODUCT_MATERIAL PRODUCTION ID_MATERIAL
1 2 2
1 2 3
1 2 4
2 1 9
2 1 10
3 1 8
8 1 5
Product 1 needs products 2,3 and 4, but product 2 and 3 are also manufactured. Product 2 needs 9 and 10 and 3 needs 8 and finally 8 needs 5. In this case we have levels here, product 1 is at level 1, products 2,3 and 4 are at level 2 and product 8 is at level 3. I tried to use a hierarchy of levels with recursion to make the select, but it turns out that it is not quite a hierarchy. SELECT used below returns me the following error:
SQL error: ORA-32044: Cycle detected when executing the recursive WITH query
WITH CTE (ID_PRODUCT_MATERIAL,PRODUCTION,ID_MATERIAL) AS (
SELECT ID_PRODUCT_MATERIAL,
PRODUCTION,
ID_MATERIAL,
FROM MATERIAL
UNION ALL
SELECT ID_PRODUCT_MATERIAL,
M.PRODUCTION,
M.ID_MATERIAL,
FROM MATERIAL M
INNER JOIN CTE C
ON M.ID_PRODUCT_MATERIAL = C.ID_MATERIAL
)
SELECT * FROM CTE;
The columns ID_PRODUCT_MATERIAL and ID_MATERIAL are derived from the products table. How to proceed in this case? Here is the structure of the tables:
CREATE TABLE PRODUCT(
ID_PRODUCT NUMBER(6,0) PRIMARY KEY,
NAME VARCHAR2(200),
WEIGHT NUMBER(6,2),
PRICE NUMBER(6,2)
);
CREATE TABLE PRODUCTION(
ID_PRODUCT_PRODUCTION NUMBER(6,0) PRIMARY KEY,
PRODUCTION NUMBER(3,0),
CONSTRAINT FK_PRODUCTIONPRODUCT FOREIGN KEY(ID_PRODUCT_PRODUCTION) REFERENCES PRODUCT(ID_PRODUCT)
);
CREATE TABLE MATERIAL(
ID_PRODUCT_MATERIAL NUMBER(6,0),
PRODUCTION NUMBER(3,0),
ID_MATERIAL NUMBER(6,0),
CONSTRAINT PK_MATERIAL PRIMARY KEY(ID_PRODUCT_MATERIAL, ID_MATERIAL),
CONSTRAINT FK_PRODUCT FOREIGN KEY (ID_PRODUCT_MATERIAL) REFERENCES PRODUCT(ID_PRODUCT),
CONSTRAINT FK_PRODUCTION FOREIGN KEY (ID_PRODUCT_MATERIAL) REFERENCES PRODUCTION(ID_PRODUCT_PRODUCTION),
CONSTRAINT FK_PRODUCT2 FOREIGN KEY (ID_MATERIAL) REFERENCES PRODUCT(ID_PRODUCT)
);
INSERT INTO PRODUCT (ID_PRODUCT,NAME,WEIGHT,PRICE) VALUES (1,'PRODUCT A',10,5),
(2,'PRODUCT B',10,5),
(3,'PRODUCT C',10,5),
(4,'PRODUCT D',10,5),
(5,'PRODUCT E',10,5),
(6,'PRODUCT F',10,5),
(7,'PRODUCT G',10,5),
(8,'PRODUCT H',10,5),
(9,'PRODUCT I',10,5),
(10,'PRODUCT J',10,5)
INSERT INTO PRODUCT (ID_PRODUCT_PRODUCTION,PRODUCTION) VALUES (1,2),
(2,1),
(3,1)
INSERT INTO MATERIAL (ID_PRODUCT_MATERIAL, PRODUCTION, ID_MATERIAL) VALUES (1,2,2),
(1,2,3),
(1,2,4),
(2,1,9),
(2,1,10),
(3,1,8)
Your query is OK, but somewhere in your real data appears a loop, for instance material 1 requires 3, 3 requires 8 and 8 requires 1. This way query would produce infinite rows, so Oracle reports cycle. This loop does not exists in sample data you provided.
There are several ways you can deal with cycles, but in this case I suspect you want to correct data. You can find problematic rows, for example use pseudocolumn connect_by_is_cycle:
select m.*, connect_by_iscycle
from material m
connect by nocycle prior id_material = id_product_material
In this dbfiddle you can see the same error, cycle for material 8. I added one row to simulate this behavior.
You can also use RCTE with these clauses:
SEARCH DEPTH FIRST BY id SET order1
CYCLE id SET cycle TO 1 DEFAULT 0
as described in documentation

Need to create a view for the following scenario

There is an Athelete table consisting of 4 columns.
AtheleteID - Primary Key varchar(20)
name - varchar(20)
country - varchar(20)
rank - integer
And another Participate table where the above atheletes participate on events. consisting of 4 columns.
AtheleteID - Foreign Key varchar(20)
EventID - Composite Primary Key varchar(20)
EventDate - date
position - integer
Question:
I need to create a view named TopAtheletes.
View attributes are AtheleteID,name,rank and the number of times they have achieved position (1,2 or 3)
How do i create the view?
I'm stuck on querying for the number of times the athelete has achieved position 1,2 or 3.
You can first count the no of times they achieved the top positions and then can join that with athlete table -
CREATE VIEW VW_ATHLETE_INFO
AS
SELECT A.AtheleteID
,A.name
,A.rank
,P.cnt
FROM Athelete
JOIN (SELECT AtheleteID, COUNT(CASE WHEN position <= 3 THEN 1 END) CNT
FROM Participate
GROUP BY AtheleteID) P ON A.AtheleteID = P.AtheleteID

SQL set constraint on how many times a PK can be referenced

I'm building a demo database of zoo for my school project and I've encountered following problem: I have a table Pavilion, which has some primary key id_pavilion and column capacity (this is information about what is the highest number of animals which can live in this pavilion).
Let's say that each pavilion can contain 2 animals at maximum.
Pavilion
id_pavilion capacity
-----------------------
1 2
2 2
3 2
4 2
Animal
id_an-column2-column3 id_pavilion
---------------------------------------
1 2
2 2
3 2
4 2
(This shows what I'm trying to prevent)
Then I have table animal, which contains some information about the animal and mainly the id_pavilion from Pavilion as a foreign key.
My question is: how can I add such a constraint that the PK id_pavilion from Pavilion can be referenced in table Animal only so many times as the capacity allows?
Looking at your example data, one could argue that every PAVILION can accommodate 2 animals, right? One could also say that the "accommodations" need to be in place before the animals can be kept in an appropriate manner. Thus, we could create a table called ACCOMMODATION, listing all available spaces.
create table pavilion( id primary key, capacity )
as
select level, 2 from dual connect by level <= 4 ;
create table accommodation(
id number generated always as identity start with 1000 primary key
, pavilionid number references pavilion( id )
) ;
Generate all accommodations
-- No "human intervention" here.
-- Only the available spaces will be INSERTed.
insert into accommodation ( pavilionid )
select id
from pavilion P1, lateral (
select 1
from dual
connect by level <= ( select capacity from pavilion where id = P1.id )
) ;
-- we can accommodate 8 animals ...
select count(*) from accommodation ;
COUNT(*)
----------
8
-- accommodations and pavilions
SQL> select * from accommodation ;
ID PAVILIONID
---------- ----------
1000 1
1001 1
1002 2
1003 2
1004 3
1005 3
1006 4
1007 4
8 rows selected.
Each animal should be in a single (defined) location. When an animal is "added" to the zoo, it can only (physically) be in a single location/accommodation. We can use a UNIQUE key and a FOREIGN key (referencing ACCOMMODATION) to enforce this.
-- the ANIMAL table will have more columns eg GENUS, SPECIES, NAME etc
create table animal(
id number generated always as identity start with 2000
-- , name varchar2( 64 )
, accommodation number
) ;
alter table animal
add (
constraint animal_pk primary key( id )
, constraint accommodation_unique unique( accommodation )
, constraint accommodation_fk
foreign key( accommodation ) references accommodation( id )
);
Testing
-- INSERTs will also affect the columns GENUS, SPECIES, NAME etc
-- when the final version of the ANIMAL table is in place.
insert into animal( accommodation ) values ( 1001 ) ;
SQL> insert into animal( accommodation ) values ( 1000 ) ;
1 row inserted.
SQL> insert into animal( accommodation ) values ( 1001 ) ;
1 row inserted.
-- trying to INSERT into the same location again
-- MUST fail (due to the unique constraint)
SQL> insert into animal( accommodation ) values ( 1000 );
Error starting at line : 1 in command -
insert into animal( accommodation ) values ( 1000 )
Error report -
ORA-00001: unique constraint (...ACCOMMODATION_UNIQUE) violated
SQL> insert into animal( accommodation ) values ( 1001 );
Error starting at line : 1 in command -
insert into animal( accommodation ) values ( 1001 )
Error report -
ORA-00001: unique constraint (...ACCOMMODATION_UNIQUE) violated
-- trying to INSERT into a location that does not exist
-- MUST fail (due to the foreign key constraint)
SQL> insert into animal( accommodation ) values ( 9999 ) ;
Error starting at line : 1 in command -
insert into animal( accommodation ) values ( 9999 )
Error report -
ORA-02291: integrity constraint (...ACCOMMODATION_FK) violated - parent key not found
Animals and accommodations
select
A.id as animal
, P.id as pavilion
, AC.id as location --(accommodation)
from pavilion P
join accommodation AC on P.id = AC.pavilionid
join animal A on AC.id = A.accommodation
;
ANIMAL PAVILION LOCATION
---------- ---------- ----------
2000 1 1000
2001 1 1001
DBfiddle here. Tested with Oracle 12c and 18c. (You'll need version 12c+ for LATERAL join to work.)
What you are trying to enforce at the database level is more of a 'business logic' rule rather than a hard data constraint. You can not implement it directly in your table designs; even if you could (as #serg mentions in the comments) it would require a very expensive (in terms of CPU/resources) lock on the table to perform the counting.
Another option, that would achieve your goal and keep the business logic separate from the data design, is to use a SQL Trigger.
A trigger can run before the data is inserted into your table; here you can check how many rows have already been inserted for that 'pavilion entity' and abort or allow the insert.
A comment for the "school project" side of things:
This being said, the sort of logic you are talking about is much better served within your consuming application rather than the database (my opinion, others may disagree). Also perhaps think about defining the size limit in the data, so you can have different sized pavilions.
Notes:
For anyone visiting this question in the future, the above link is for an oracle trigger (as OP has tagged the question for oracle). This link is for Microsoft SQL Server Triggers.
The answer is "not easily". Although the idea of keeping the "accommodations" in the pavilions as a separate table is a clever one, animals are put into pavilions, not accommodations. Modeling accommodations makes it much trickier to move animals around.
Perhaps the simplest approach is to use triggers. This starts with an animal_count column in pavilions. This column starts at zero and is incremented or decremented as animals move in or out. You can use a check constraint to validate that the pavilion is not over-capacity.
Unfortunately, maintaining this column requires triggers on the animals table, one for insert, update, and delete.
In the end, the trigger is maintaining the count and if you attempt to put an animal in a full pavilion, you will violate the check constraint.
You need a column (say "NrOccupants") that is updated when an animal is placed into or removed from each pavilion. Then you add a check constraint to that column that prevents the application code from adding more animals to a pavilion than is permitted by the rule that is enforced by the check constraint.
Here is an example of the SQL DDL that would do that.
CREATE SCHEMA Pavilion
GO
CREATE TABLE Pavilion.Pavilion
(
pavilionNr int NOT NULL,
capacity tinyint CHECK (capacity IN (2)) NOT NULL,
nrOccupants tinyint CHECK (nrOccupants IN (0, 2)) NOT NULL,
CONSTRAINT Pavilion_PK PRIMARY KEY(pavilionNr)
)
GO
CREATE TABLE Pavilion.Animal
(
animalNr int NOT NULL,
name nchar(50) NOT NULL,
pavilionNr int NOT NULL,
type nchar(50) NOT NULL,
weight smallint NOT NULL,
CONSTRAINT Animal_PK PRIMARY KEY(animalNr)
)
GO
ALTER TABLE Pavilion.Animal ADD CONSTRAINT Animal_FK FOREIGN KEY (pavilionNr) REFERENCES Pavilion.Pavilion (pavilionNr) ON DELETE NO ACTION ON UPDATE NO ACTION
GO

Auto Increment Composite Primary Key

I have an Access database that I am trying to create. I want to use two columns from the table to create the primary key.
Projectbl (ProjectID, ProjectRegion, other columns)
I want the ProjectID to autoincrement. That's fine. So far whenever I add a new project, the ProjecID increments by itself.
I want to create a new column in that same table that will be the ProjectRef where the new number would be a composite of ProjectID and ProjectRegion.
So for example
ProjectID ProjectRegion OtherCol
---------------------------------
1 500 ...
2 100 ...
3 200 ...
4 500 ...
5 500 ...
6 100 ...
I want the table to actually look like that
ProjectRef ProjectID ProjectRegion OtherCol
--------------------------------------------
5001 1 500 ...
1002 2 100 ...
2003 3 200 ...
5004 4 500 ...
5005 5 500 ...
1006 6 100 ...
So I am trying to add a new project: the projectID will autoincrement to 7, but the ProjectRef will be 'ProjectReg'7 whatever the ProjectReg is.
I know I can create a composite key with
CREATE TABLE 'Projectbl'
(
ProjectID INT(10) NOT NULL AUTO_INCREMENT,
ProjectRegion INT(10) NOT NULL,
other columns VARCHAR(100),
CONSTRAINT ProjectRef
PRIMARY KEY ('ProjectRegion', 'ProjectID')
)
How can I actually display ProjectRef in the table?
You shouldn't have the ProjectRef column at all. This violates basic rules of database normalization. If you want your front end to display the ProjectRef then just calculate it from the columns that you have.

SQL Server composite key custom Auto Increment in child field

I'm using SQL Server 2012 Express. I have created the following table
CREATE TABLE [dbo].[logis_location]
( [loc_id] int identity NOT NULL
, [loc_name] varchar(50) NOT NULL )
CREATE TABLE [dbo].[rev_bill]
( [bill_id] int identity NOT NULL ,
[loc_id] int NOT NULL )
ALTER TABLE [dbo].[rev_bill]
ADD CONSTRAINT [rev_bill_PK] PRIMARY KEY CLUSTERED ( [bill_id] , [loc_id] )
ALTER TABLE [dbo].[rev_bill] WITH CHECK
ADD CONSTRAINT [logis_location_rev_bill_FK1]
FOREIGN KEY ( [loc_id] )
THe bill_id is auto incrementing ,But bill table have composite key based on location table loc_id which is foreign Key.
This resulting following output
bill_id loc_id 1 1 2 1 3 2 4 2 5 1
6 3
7 2
However expected outcome is
bill_id loc_id
1 1
2 1
1 2
2 2
3 1
1 3
3 2
I want to increment the location specific bill_id in most safest way.It means when inserting new bill ,the bill id should be incremented ,but considering the loc_id.As given above, If there is bill for the given location (eg:loc_id =4), first bill for that location should have bill_id 1,and next one should be 2. This is Client requirement.
Please provide me some guide line.