I haven't ever really used foreign keys in my databases, I have always just written code that would enforce things for me, but I'm looking towards shifting some of that logic to the database and learning more about foreign keys.
I currently have a database which has the table Categories:
CREATE TABLE [dbo].[Categories](
[CategoryID] [bigint] IDENTITY(1,1) NOT NULL,
[ParentCategoryID] [bigint] NOT NULL,
[CategoryStatus] [bit] NOT NULL,
[CategoryTitle] [varchar](64) NOT NULL,
[CategorySlug] [varchar](64) NOT NULL,
[CategoryThumbnail] [varchar](128) NULL,
[CategoryHeaderImage] [varchar](128) NULL,
[CategoryDescription] [text] NULL,
CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED
(
[CategoryID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [UI_Categories_Slug] UNIQUE NONCLUSTERED
(
[CategorySlug] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
I would like to create a foreign key that relates ParentCategoryID back up to CategoryID. When creating the FK, which would be the primary column, and which would be the foreign column in this instance?
I would think that ParentCategoryID would have to be NULLable (unless you want a top-level parent to point to itself, and that doesn't make much sense).
ALTER TABLE dbo.Categories
ADD CONSTRAINT FK_SelfParent
FOREIGN KEY (ParentCategoryID)
REFERENCES dbo.Categories(CategoryID);
True story: at a job interview at Microsoft (several years ago), a SQL Server person told me that this wasn't possible.
Related
Plot: I need to book an Order which relies on 3 different type of Factory.
I have individual tables for both order and Factory. Now I need to make a one to many relations between Order and Booking Factory.
Order Table:
CREATE TABLE [dbo].[tbl_OrderInformation](
[OrderInformationId] [int] IDENTITY(1,1) NOT NULL,
[OrderId] [int] NOT NULL,
[OrderNo] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_tbl_OrderInformation] PRIMARY KEY CLUSTERED
( [OrderInformationId] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE =
OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Factory Table:
CREATE TABLE [dbo].[tbl_Factory](
[FactoryId] [int] IDENTITY(1,1) NOT NULL,
[FactoryName] [nvarchar](50) NOT NULL,
[FactoryType] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_tbl_Factory] PRIMARY KEY CLUSTERED
( [FactoryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Sample Order Data
Sample Factory Data
Now, an Order relies on multiple garments, dyeing, and printing Factory.
Suppose, Order C101 relies on Garments-A, Dyeing-A, Printing-A, Printing-B, Printing-C.
Now, I can design OrderBooking table in 2 ways.
CREATE TABLE [dbo].[tbl_OrderBooking_1](
[OrderBookingId] [INT] IDENTITY(1,1) NOT NULL,
[OrderId] [INT] NOT NULL,
[FactoryId] [INT] NULL,
[FactoryType] [NVARCHAR](50) NULL,
CONSTRAINT [PK_tbl_OrderBooking_1] PRIMARY KEY CLUSTERED
(
[OrderBookingId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
This the data will look like below:
And The second way,
CREATE TABLE [dbo].[tbl_OrderBooking_2](
[OrderBookingId] [INT] IDENTITY(1,1) NOT NULL,
[OrderId] [INT] NULL,
[garmentsFactoryId] [INT] NULL,
[dyeingFactoryId] [INT] NULL,
[printingFactoryId] [INT] NULL,
CONSTRAINT [PK_tbl_OrderBooking_2] PRIMARY KEY CLUSTERED
(
[OrderBookingId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Here the data will look like,
Now, which approach of designing the OrderBooking table is more accurate and why ?
Please keep in mind that the type of factory is fixed to 3, and OrderBooking table will grow quite large over time thus tend to have heavy read and write operations.
The link table between Order & factory would be the best approach.
You will get better performance and you can create indexes on the numeric columns.
Going forward if you have any new factory then it is also each to insert without any issue.
The link table will help you to align with the Normalization rule as well. so my suggestion is go with that.
I have three tables: offices, suboffices, and sales. Each office owns several suboffices, both offices and suboffices sell products. How should I design my sales table to store there office or suboffice where this sale was made?
I was thinking about having a compound foreign key made of office_id and suboffice_id (where suboffice_id may be null, in which case a sale was made in office)
Is it the right way to design a database?
I was also thinking about having two sales tables: for offices and suboffices. But in my opinion it makes things a bit harder..
UPDATE
Sales can be of different types, so they will need different tables. And there will be one table 'SALES' which will store type of a sale and where it was made
Your sub-offices sound the same as your offices from a properties point of view. Here is a diagram of what your table design could look like by having your Office table include a ParentOffice foreign key.
And here is the SQL to create those tables:
CREATE TABLE [dbo].[Office](
[OfficeId] [int] NOT NULL,
[ParentOfficeId] [int] NULL,
[MoreStuff] [nvarchar](50) NULL,
CONSTRAINT [PK_Office] PRIMARY KEY CLUSTERED
(
[OfficeId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Office] WITH CHECK ADD CONSTRAINT [FK_Office_Office] FOREIGN KEY([ParentOfficeId])
REFERENCES [dbo].[Office] ([OfficeId])
GO
ALTER TABLE [dbo].[Office] CHECK CONSTRAINT [FK_Office_Office]
GO
CREATE TABLE [dbo].[Product](
[ProductId] [int] NOT NULL,
[MoreStuff] [nvarchar](50) NULL,
CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED
(
[ProductId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[Sale](
[SaleId] [int] NOT NULL,
[ProducitId] [int] NOT NULL,
[OfficeId] [int] NOT NULL,
CONSTRAINT [PK_Sale] PRIMARY KEY CLUSTERED
(
[SaleId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Sale] WITH CHECK ADD CONSTRAINT [FK_Sale_Office] FOREIGN KEY([OfficeId])
REFERENCES [dbo].[Office] ([OfficeId])
GO
ALTER TABLE [dbo].[Sale] CHECK CONSTRAINT [FK_Sale_Office]
GO
ALTER TABLE [dbo].[Sale] WITH CHECK ADD CONSTRAINT [FK_Sale_Sale] FOREIGN KEY([ProducitId])
REFERENCES [dbo].[Product] ([ProductId])
GO
ALTER TABLE [dbo].[Sale] CHECK CONSTRAINT [FK_Sale_Sale]
GO
One way to go about this, assuming OFFICE and SUBOFFICE share many of the same attributes is to eliminate the SUBOFFICE table and add a PARENT_OFFICE_ID column to the OFFICE table. Under this design, the distinction between an office and a sub-office would be whether or not PARENT_OFFICE_ID is null. Then SALES can simply have an OFFICE_ID column that can reference either kind of office.
I have two tables. First table has multi columns as the primary key and the second table has one column primary key. Data has be entered first in Table1 FieldPlacement where FieldPlacementNum will be generated then enter a record in Table2 where the FieldPlacementNum has to exist in Table1.
Currently has one to many relationship but I want the reverse the relationship of tables and SQL does not let me do it. thanks
Table 1
CREATE TABLE [dbo].[FieldPlacement]
(
[ID] [varchar](10) NOT NULL,
[Year] [varchar](10) NOT NULL,
[Term] [varchar](10) NOT NULL,
[PlacementNum] [int] IDENTITY(1,1) NOT NULL,
[Email] [varchar](70) NULL,
CONSTRAINT [PK_FieldPlacement]
PRIMARY KEY CLUSTERED ([ID] ASC, [Year] ASC, [Term] ASC, [PlacementNum] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[FieldPlacement] WITH CHECK
ADD CONSTRAINT [FK_FieldPlacement_FieldPlacementEval1]
FOREIGN KEY([PlacementNum])
REFERENCES [dbo].[FieldPlacementEval] ([PlacementNum])
GO
ALTER TABLE [dbo].[FieldPlacement]
CHECK CONSTRAINT [FK_FieldPlacement_FieldPlacementEval1]
GO
Table 2
CREATE TABLE [dbo].[FieldPlacementEval]
(
[PlacementNum] [int] NOT NULL,
[StudentLastName] [varchar](50) NULL,
[StudentFirstName] [varchar](50) NULL,
[TeacherLastName] [varchar](50) NULL,
[TeacherFirstName] [varchar](50) NULL,
CONSTRAINT [PK_FieldPlacementEval]
PRIMARY KEY CLUSTERED ([PlacementNum] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Since FieldPlacement.PlacementNum is an IDENTITY, which is NOT NULL and unique, you could also make this your PK, and then just create a unique index on those four columns that currently make up the PK (to ensure their uniqueness).
From your FieldPlacementEval just reference the PlacementNum.
The additional benefit here would be a smaller, more efficient clustering key on your FieldPlacement table (since it's only 1 column instead of 4 - and skips all those variable length columns which are really bad for a clustering key)
I am not sure if my title really explains the question, so I'll try an example:
Let's say I have:
1) region table: parent regions and subregions in the same table (link A)
2) product table: each product is linked to a single parent region (link B)
3) product_price table: lists the price of a product (C) in all sub regions of that product region (link D >>> the link in question).
(the diagram is showing only the relevant fields, there are a lot more data in region and product)
Is there a way to define the region->region_price key, to include only sub_region_id's of the parent_region in the product->region key??
Or, in the terms of the image, how do I make the D link to include only regions that are children [as in A] of the B & C link?
Hope you are getting my point...
Here are the real tables and links:
CREATE TABLE [dbo].[product](
[product_id] [int] NOT NULL,
[product_name] [nchar](10) NOT NULL,
[parent_region_id] [int] NOT NULL,
CONSTRAINT [PK_product] PRIMARY KEY CLUSTERED
(
[product_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[product_price](
[product_id] [int] NOT NULL,
[sub_region_id] [int] NOT NULL,
[price] [decimal](18, 0) NOT NULL,
CONSTRAINT [PK_product_price] PRIMARY KEY CLUSTERED
(
[product_id] ASC,
[sub_region_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[region](
[region_id] [int] NOT NULL,
[region_name] [nvarchar](50) NOT NULL,
[parent_region_id] [int] NULL,
CONSTRAINT [PK_region] PRIMARY KEY CLUSTERED
(
[region_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[product] WITH CHECK ADD CONSTRAINT [FK_product_region] FOREIGN KEY([parent_region_id])
REFERENCES [dbo].[region] ([region_id])
ALTER TABLE [dbo].[product] CHECK CONSTRAINT [FK_product_region]
ALTER TABLE [dbo].[product_price] WITH CHECK ADD CONSTRAINT [FK_product_price_product] FOREIGN KEY([product_id])
REFERENCES [dbo].[product] ([product_id])
ALTER TABLE [dbo].[product_price] CHECK CONSTRAINT [FK_product_price_product]
ALTER TABLE [dbo].[product_price] WITH CHECK ADD CONSTRAINT [FK_product_price_region] FOREIGN KEY([sub_region_id])
REFERENCES [dbo].[region] ([region_id])
ALTER TABLE [dbo].[product_price] CHECK CONSTRAINT [FK_product_price_region]
ALTER TABLE [dbo].[region] WITH CHECK ADD CONSTRAINT [HK_region_region] FOREIGN KEY([parent_region_id])
REFERENCES [dbo].[region] ([region_id])
ALTER TABLE [dbo].[region] CHECK CONSTRAINT [HK_region_region]
I guess you are trying to enforce a business rule with a database rule, but they're not always the same. Instead, you can run validation querys in your code before inserting or updating, or you could implement a trigger that validates your rules or throws an error.
I am still trying to learn how to use EF and running into a problem with my bridge table.
When I try to create a new Order with associated Resources, I get the following SQL error:
{"Violation of PRIMARY KEY constraint 'PK_Resource_Type'. Cannot insert duplicate key in object 'dbo.Resource_Type'. The duplicate key value is (2).\r\nThe statement has been terminated."}
Code Looks like this
ResourceType resource = new ResourceType();
resource.ID = 2;
resource.Name = "Van"
order.resourceType().Add(resource)
db.Orders.Add(order);
db.SaveChanges();
Tables look like this
--Order table
CREATE TABLE [dbo].[Orders](
[Order_ID] [int] IDENTITY(1,1) NOT NULL,
[OrdernName] [varchar](100) NOT NULL,
CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED
(
[Order_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
--Resource Type table
CREATE TABLE [dbo].[Resource_Type](
[ResourceType_ID] [int] NOT NULL,
[ResourceType] [varchar] (30) NOT NULL
CONSTRAINT [PK_Resource_Type] PRIMARY KEY CLUSTERED
(
[ResourceType_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
--Resource Type To Order table
CREATE TABLE [dbo].[Resource_Type_Order](
[ResourceType_ID] [int] NOT NULL,
[Order_ID] [int] NOT NULL
CONSTRAINT [PK_Resource_Type_Order] PRIMARY KEY CLUSTERED
(
[ResourceType_ID] ASC,
[Order_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Resource_Type_Order] WITH CHECK ADD CONSTRAINT FK_Order_Resource_Type_Order FOREIGN KEY([Order_ID])
REFERENCES [dbo].[Orders]([Order_ID])
GO
ALTER TABLE [dbo].[Resource_Type_Order] WITH CHECK ADD CONSTRAINT FK_Resource_Type_Order_Resource_Type FOREIGN KEY([ReosurceType_ID])
REFERENCES [dbo].[Resource_Type]([Resource_ID])
GO
Am I using the bridge table correctly? I want my bridge table to look like this, after I add an order (ID=1) with two resources (car ID=1 and van ID=2).