Question

I'm making a traffic logging front end which allows displaying information on a specific vehicle on property as well as searching for a specific vehicle, but am unsure of the best way to proceed with my database design. What I want to do is be able to quickly pull up all vehicle records based on the type of vehicle and features/subtypes of that vehicle. I don't know how best to make reference from my single vehicle record to multiple subtypes/features. Here's a simplified example:

I have a table for Vehicles:

VehicleID, LicensePlate, TypeID, SubTypeIDs
1        ,   111111    ,   2   ,   ;2;;3;

A table for VehicleTypes:

TypeID, Type
  1   ,  Car
  2   ,  Semi

And a table for VehicleSubTypes:

SubTypeID,  TypeID,   SubType
1        ,   1    ,   Coupe
2        ,   2    ,   Flat Bed
2        ,   2    ,   Sleeper

The Vehicles.Subtype field is a varchar, in which I'm currently just deleniating out the Subtypes which reference to VehicleSubTypes.SubTypeID... The idea is to, from the front end, pick out each SubTypeID when listing available options and look up the referenced SubType string (ie, "Coupe") to display to the user, or more importantly when searching for a Semi with a sleeper and a flat bed to include a SubTypes LIKE '%;2;%' AND '%;3;%' clause to get only vehicles that include both features. I'm only thinking of this solution though right now because I've been on vacation for a year and my brain is stalling out on me :) I feel certain this is awful DB design! But for the life of me I can't think of a more proper way, and all my googling efforts keep turning up subtype examples that simply don't apply or I'm missing the similarity with (ie, people with multiple sets of contact information.. people should obviously be one table, contact info should obviously be another, they link by personID, etc etc)

EDIT/Conclusion:

Thanks to Bort for jarring my memory and pointing me toward a link table. I've now added a table, Link_VehicleToSubTypes:

linkID, VehicleID, SubTypeID
  1   ,   1      ,    2
  2   ,   2      ,    10     //10 = Cargo (Semi)
  3   ,   2      ,    15     //15 = No Sleeper

In addition, I've created the following stored procedure to return VehicleIDs for vehicles that match all parameters (up to 10) that I've passed to it - this way I can later join this info against results from searching my Vehicles table which includes vehicle-specific info such as Vehicle.Color, and thus I can filter a final result set:

ALTER PROCEDURE dbo.ReturnVehicleIDsMatchingSubTypes

    (
    @SubType1 int = NULL,
    @SubType2 int = NULL,
    @SubType3 int = NULL,
    @SubType4 int = NULL,
    @SubType5 int = NULL,
    @SubType6 int = NULL,
    @SubType7 int = NULL,
    @SubType8 int = NULL,
    @SubType9 int = NULL,
    @SubType10 int = NULL
    )

AS
    DECLARE @intNumberSubTypesToMatch int SET @intNumberSubTypesToMatch = 
    (SELECT COUNT(@SubType1) 
            + COUNT(@SubType2) 
            + COUNT(@SubType3) 
            + COUNT(@SubType4)
            + COUNT(@SubType5)
            + COUNT(@SubType6)
            + COUNT(@SubType7)
            + COUNT(@SubType8)
            + COUNT(@SubType9)
            + COUNT(@SubType10))

    SELECT  VehicleID
    FROM    Link_VehicleToSubTypes
    WHERE
            SubTypeID IN (@SubType1, @SubType2, @SubType3, @SubType4, @SubType5, @SubType6, @SubType7, @SubType8, @SubType9, @SubType10)
    GROUP BY VehicleID
    HAVING        (COUNT(*) = @intNumberSubTypesToMatch)

    RETURN

I've tested this out and it's working well. My implementation of the stored procedure may be a bit wonky (I've never had to count non null parameters before, this method is all that came to mind), but it works. Bort - I'll +1 you when I have enough points to do so! Your help is greatly appreciated!

Was it helpful?

Solution

Since storing multiple values in a single column like you are with SubTypes is almost always a bad idea (Needing to do LIKE '%;2;%' AND LIKE '%;3;%' is a huge red flag), if I've understood your requirements correctly this looks like a many to many relationship, which usually involves a cross table to link the two entities, in this case Vehicles and Subtypes.

If you removed SubTypeIDs from your Vehicle table, and created a link table Vehicle_Subtypes

VehicleID   SubTypeID
1           2
1           3

You would then be able to write queries to get the appropriate SubTypes for a given vehicle, something like

SELECT S.SubType FROM SubTypes S
    INNER JOIN Vehicle_Subtypes X ON X.SubTypeID = S.SubTypeID
WHERE X.VehicleID = @VehicleId

May need more logic in there to account for TypeID but this seems like the right design.

EDIT: I take it all back. Misunderstood the part about going from SubTypes to Vehicles, had it backwards in my head. Going the other way is more difficult, finding records that meet a variable number of conditions can be tricky. Given the same link table, (assuming you have a lot of control over the SQL that gets generated) you could write the somewhat hackish query

SELECT VehicleId FROM Vehicle_Subtypes 
WHERE SubTypeId IN (1, 2)
GROUP BY VehicleId
HAVING COUNT(*) = 2

You would have to ensure COUNT(*) = number of subtypes selected, this way the vehicle has all of them. Still, it feels like there should be a better way, I'll keep thinking on it.

OTHER TIPS

I'd do something like this:

  • A Vehicle Table with columns Vehicle ID, LicencePlate and TypeID (FK). Vehicle ID is PK.
  • A VehicleSubTypes table with columns TypeID, SubTypeID and SubType. TypeID and SubTypeID are PK's
  • A VehicleFeatures table with columns Vehicle ID (FK), TypeID and SubTypeID. TypeID and SubTypeID are FK to VehicleSubTypes. Your PK can be auto-generated.

With this design and some SQL you can get the data you want.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top