0

I have a table that stores information on Vendors called dbo.Vendor. Its has fields like this:

 1. VendorID
 2. VendorName
 3. VendorType
 4. AddressLine1
 5. EMail
 6. Telephone
 7. and so on....

This information is common to all vendors. But depending on the type of vendor (VendorType field) I need to collect more specific information. For example a vendor that is a charity will have a Charity Number but a vendor that are Lawyers will have some kind of legal registration number instead. If a vendor is a cinema then I may need to know seating capacity which won't apply to other vendors of course.

Do I really have to create a unique table for each of these different vendors e.g. dbo.VendorLaw, dbo.VendorCinema. Or can I create all possible fields in the main dbo.Vendor table and leave NULL values where the field does not apply to that vendor? This is breaking normalization rules of course.

volume one
  • 6,800
  • 13
  • 67
  • 146

2 Answers2

1

Depending on the scope of how much additional optional info you need per vendor type, I would create another two tables: one reference table, which stores all the different types of additional info, and one table that stores all the records (and links to the main table).

CREATE TABLE schema.VendorAdditionalInfo (
   autoId serial NOT NULL,
   vendorId int,
   vendorInfoId int,
   vendorInfoText varchar
);

Then create your reference table:

CREATE TABLE schema.VendorInfo (
   vendorInfoId serial NOT NULL,
   vendorType int,
   vendorInfoName text
)

This way you can create any amount of records in VendorAdditionalInfo based on what vendor type it is.

EDIT: Example of the info you'd input:

INSERT INTO schema.VendorInfo (vendorType, vendorInfoName)
VALUES 
(1, 'Lawyer Registration Number'),
(2, 'Nurse ID Number'),
(3, 'Hot Dog Business License')

Then for your records table you'd enter your info as such:

INSERT INTO schema.VendorAdditionalInfo (vendorId, vendorInfoId, vendorInfoText)
VALUES
(10, 1, 'LAW13245'),
(11, 2, 'NURSE234234'),
(12, 1, 'LAW56156'),
(13, 3, 'HOTDOGBUSINESSLIC23')

Basically - the text field is the field that's unique for each additional info type.

Othya
  • 390
  • 1
  • 3
  • 18
  • Wow that is actually very clever. can i ask what is the "`vendorInfoText`" field for in the first table? – volume one Jul 04 '14 at 13:22
  • That will include the values of the additional info you require. I've updated my answer to be a bit more clear and showing insert examples. – Othya Jul 04 '14 at 13:31
  • The only issue I see with this this is that vendorInfoText will contain values that are all of the same datatype. I cannot specify different datatypes for each of the extra fields. Could this cause a scalability problem given that the amount of vendors is likely to grow into the hundreds of thousands? – volume one Jul 04 '14 at 13:46
  • That is a limitation; can you provide examples other than registration numbers? – Othya Jul 04 '14 at 13:50
  • I found this link which is interesting. Its using the supertype/subtype by having many tables. http://stackoverflow.com/questions/1654071/db-design-to-use-sub-type-or-not/1654483#1654483 – volume one Jul 04 '14 at 13:51
  • I can't imagine the different datatypes but certainly there will need to be decimal, int, and varchar. For example, a University may have an amont of money for scholarships. That would be a monetary/decimal amount not a text value. – volume one Jul 04 '14 at 13:53
  • Change it from text to varchar, then you can cast varchar to int/decimal as needed. I'll edit the answer. – Othya Jul 04 '14 at 13:56
  • Is that safe to do for a large application? I'm not very knowledgeable about SQL performance. – volume one Jul 04 '14 at 14:01
  • 2
    This is called an Entity Attrubute Value (EAV) model. It's well known and there is much discussion on whether it is a good idea or not. It comes down to a balancing act between maintainability (EAV) and performance (have a zillion columns on one row). Either way your application code needs to know about specific fields for specific vendor types. http://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model – Nick.Mc Jul 04 '14 at 14:30
  • I do like this solution. my only worry is performance of doing casts on values. – volume one Jul 04 '14 at 15:14
  • It does not save anything over having tables and it lacks type and integrity checking. It is unnecessary and contraindicated when you know the types at compiletime. Even if you don't you are better off to create tables at runtime. It is only appropriate when the tables are not known at compile time *and* there will be many, many tables or columns. Search "EAV". (Including in my answers.) What do you mean, "Do I really have to"? You have to put the info in the database. That's what tables are for. Why *ever* use more than one or two tables, if you think this solution is appropriate? – philipxy Jul 05 '14 at 07:38
1

I would create the additional tables. This allows you to enforce null/non-null (and other) constraints easily based on the vendor type - and you can even create a superkey in your existing table on (VendorID,VendorType) and a computed column in each vendor specific column to ensure that e.g. only Cinema vendors have entries in the VendorCinema table.

CREATE TABLE Vendors (
   VendorID int IDENTITY(-47,1) not null,
   VendorName varchar(19) not null,
   VendorType varchar(11) not null,
   AddressLine1 varchar(35) not null,
   EMail varchar(312) null,
   Telephone varchar(15) null,
   constraint PK_Vendors PRIMARY KEY (VendorID),
   constraint UQ_Vendor_Types UNIQUE (VendorID,VendorType),
   constraint CK_Vendor_Types CHECK (VendorType in ('Law','Cinema'))

)

and

CREATE TABLE CinemaVendors (
    VendorID int not null,
    VendorType as CONVERT(varchar(11),'Cinema') persisted,
    Seating int not null,
    BruceWillisMovies int not null,
    constraint PK_CinemaVendors PRIMARY KEY (VendorID),
    constraint FK_CinemaVendors_Vendors FOREIGN KEY
                                (VendorID,VendorType)
             references Vendors (VendorID,VendorType),
    constraint CK_BruceWillisMovies CHECK (BruceWillisMovies > 3)
)

This is far easier to do in separate tables than to have a slew of nullable columns in one single table and then trying to enforce all of the actual constraints.

This also addresses the concerns with the EAV model - where we want an int stored for cinema vendors, we're sure that an int has actually been stored.

(It's optional whether you also declare a foreign key between the two above tables based on just the VendorID column. Sometimes I do, sometimes I don't. It's the "real" foreign key, but we use the two column one above to ensure that only Cinema vendors end up in the CinemaVendors table)

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • This works, but now how do I write a SQL query to select everything from the `Vendors` table and then select everything from the `CinemaVendors` only if the `VendorType` is 'Cinema'? Will I have to write various IF ELSE statements to cover all the possible different types of Vendors? – volume one Jul 04 '14 at 22:46
  • Just to add I can only pass in VendorID as the search criteria from the web site. – volume one Jul 04 '14 at 23:09
  • You join Vendors with XVendors to get all the XVendor info. In the context of a particular X you use its table. You only need to branch/case on Vendor VendorType when you are doing something specific to an X while processing all Vendors. – philipxy Jul 05 '14 at 07:27
  • So LEFT JOIN all the way on all XVendor tables? – volume one Jul 05 '14 at 17:12
  • LEFT JOIN adds nulls for unmatched values. Do you *want* a bunch of rows with non-XVendor Vendor ids plus nulls? – philipxy Jul 08 '14 at 22:17