I'm designing a database for a Cab/Taxi service. There's a table for taxi service details.
*cab_services*
+---------------------+
| SID | Name |
|---------------------|
| S001 | ABC Taxi |
| S002 | XYZ Cabs |
| S003 | MN Taxi |
| S004 | OP Cabs |
|_______|_____________|
And there's another table for locations.
locations
+-----------------------------------+
| LID | Code | Location |
|----------------|------------------|
| L001 | CO | Akarawita |
| L002 | CO | Angamuwa |
| L003 | CO | Batawala |
| L004 | CO | Avissawella |
| L005 | CO | Battaramulla |
| L006 | GQ | Ambepussa |
| L007 | GQ | Bemmulla |
| L008 | GQ | Biyagama |
| L008 | GQ | Alawala |
| L010 | GQ | Andiambalama |
| L011 | GQ | Biyagama IPZ |
| L012 | KT | Bellana |
| L013 | KT | Bolossagama |
| L014 | KT | Bombuwala |
| L015 | KT | Alutgama |
| L016 | KT | Alubomulla |
|_______|________|__________________|
Note that they are categorized according to districts. (CO, GQ, KT) Each district has multiple towns/cities.
A taxi service may be providing their service in multiple districts. And one district may have multiple taxi services. Its sort of a many to many scenario.
I'm trying to connect the cab_services table with the locations table. But I can't figure out how to.
I would have done something like this if only one service was in on district.
+-------+-------------+---------+
| SID | Name | Locs |
|-------+-------------+---------|
| S001 | ABC Taxi | CO |
|_______|_____________|_________|
But like I said before, a service can have many districts.
+-------+-------------+---------+
| SID | Name | Locs |
|-------+-------------+---------|
| S001 | ABC Taxi | CO, KT |
|_______|_____________|_________|
This would violate the 1NF.
I would want to be able to get results in a situation like say, if a user search using a Location name, he should get the cab services in that area.
What changes do I have to do in my database, table structure to accomplish this?
Please let me know if some part is confusing, I'll try my best to clarify it further. I'm pretty bad at explaining things. Thank you.
It seems your connecting table only needs to define the FK columns for the cab_services table and the locations table (i.e., Note what Oded states about duplication). So for example, if "ABC Taxi" is available in ALL "CO" locations then the connecting table would have the following records:
SID | LID
-----------
S001 | L001
S001 | L002
S001 | L003
S001 | L004
S001 | L005
You will have multiple entries on the connecting table.
SID Name Locs
-----------------------
S001 ABC Taxi CO
S001 ABC Taxi KT
This is still in 1NF, though you are duplicating data by having SID and Name in the table.
Related
I have a table of Vendors (Vendors):
+-----+-------------+--+
| ID | Vendor | |
+-----+-------------+--+
| 1 | ABC Company | |
| 2 | DEF Company | |
| 3 | GHI Company | |
| ... | ... | |
+-----+-------------+--+
and a table of services (AllServices):
+-----+------------+--+
| ID | Service | |
+-----+------------+--+
| 1 | Automotive | |
| 2 | Medical | |
| 3 | Financial | |
| ... | ... | |
+-----+------------+--+
and a table that links the two (VendorServices):
+-----------+-----------+
| Vendor ID | ServiceID |
+-----------+-----------+
| 1 | 1 |
| 1 | 3 |
| 3 | 2 |
| ... | ... |
+-----------+-----------+
Note that one company may provide multiple services while some companies may not provide any of the listed services.
The query results I want would be, for a given Vendor:
+------------+----------+
| Service ID | Provided |
+------------+----------+
| 1 | 0 |
| 2 | 0 |
| 3 | 1 |
| ... | ... |
+------------+----------+
Where ALL of the services are listed and the ones that the given vendor provides would have a 1 in the Provided column, otherwise a zero.
Here's what I've got so far:
SELECT
VendorServices.ServiceID,
<Some Function> AS Provided
FROM
AllServices LEFT JOIN VendorServices ON AllServices.ID = VendorServices.ServiceID
WHERE
VendorServices.VendorID = #VendorID
ORDER BY
Service
I have two unknowns:
The above query does not return every entry in the AllServices table; and
I don't know how to write the function for the Preovided column.
You need a LEFT join of AllServices to VendorServices and a case expression to get the column provided:
select s.id,
case when v.serviceid is null then 0 else 1 end provided
from AllServices s left join VendorServices v
on v.serviceid = s.id and v.vendorid = #VendorID
See the demo.
I need to track chages made in a record and able to get the any version of the record at given point of time.
Consider, I have the following contact table.
--------------------------
| contact |
--------------------------
| id |
| name |
| phone |
| email |
| city |
| country |
| create_by |
| created_at |
--------------------------
I want to achieve following things on the above table.
I want to track all the changes made in the records over the time.
I want to find the version of the record at the given point of time.
The original record should stay as it is (without any update).
For example,
So, I have created following table to track all the changes.
--------------------------
| contact_history |
--------------------------
| contact_id |
| field_name |
| old_value |
| new_value |
| modified_by |
| modified_at |
--------------------------
With this data model,
To create a new contact record, we will create the record in the contact table.
To Update a record, we will create a new record in the contact_history record with the field name, new value, current time and who modified the record.
In this way I can able to track, what change was made and who made the change and the same time the original record will stay as it is without any change.
What I am trying to achieve is to find the version of all the contact records at a given point of time.
For example, consider the following record.
Contact table
------------------------------------------------------------------------------------------------
| id | name | phone | email | city | country | created_by | created_at |
------------------------------------------------------------------------------------------------
| 1 | Steve | 1111111 | steve#abc.com | NY | USA | John | 2019-04-01 13:17:49.417 |
------------------------------------------------------------------------------------------------
Each update will be created as a history record in the contact_history table.
contact_history
-------------------------------------------------------------------------------------------------------
| contact_id | field_name | old_value | new_value | modified_at | modified_by |
-------------------------------------------------------------------------------------------------------
| 1 | email | steve#abc.com | steve#changed.com | 2019-04-02 08:19:49.213 | Arnold |
| 2 | city | NY | LA | 2019-04-03 12:48:37.568 | John |
| 3 | city | LA | SF | 2019-04-04 25:25:19.715 | John |
-------------------------------------------------------------------------------------------------------
When I need the version of the contact record before 2019-04-02 I should able to get the following,
------------------------------------------------------------------------------------------------
| id | name | phone | email | city | country | created_by | created_at |
------------------------------------------------------------------------------------------------
| 1 | Steve | 1111111 | steve#abc.com | NY | USA | John | 2019-04-01 13:17:49.417 |
------------------------------------------------------------------------------------------------
When I need the version of the contact record before 2019-04-03 I should able to get the following,
----------------------------------------------------------------------------------------------------
| id | name | phone | email | city | country | created_by | created_at |
----------------------------------------------------------------------------------------------------
| 1 | Steve | 1111111 | steve#changed.com | NY | USA | John | 2019-04-01 13:17:49.417 |
----------------------------------------------------------------------------------------------------
When I wanted the latest version of the same record I should able to get,
----------------------------------------------------------------------------------------------------
| id | name | phone | email | city | country | created_by | created_at |
----------------------------------------------------------------------------------------------------
| 1 | Steve | 1111111 | steve#changed.com | SF | USA | John | 2019-04-01 13:17:49.417 |
----------------------------------------------------------------------------------------------------
The example is given for only one record. It can be applicable for multiple contact records.
How can I write a efficient query to achieve this in SQL Server.
Simple. Use a TOP 1 query to get the most recent record before the date you want to filter on.
Unfortunately, due to the data model you've chosen to use, you'll need to do a TOP 1 subquery for every field in your result set that can be in your history table.
SELECT id,
(SELECT TOP 1 new_value ... WHERE field_name='Name' ... ) AS Name,
etc...
I was provided 2 files, as two tables: 'VoterData' and 'VoterHistory' - What is the best way to accomplish my expected display?
EXPECTED DISPLAY
ID | First Name | Last Name | Election1 | Election2 | Election3
--------+------------+-------------+-----------+-----------+----------
2155077 | Camille | Bocchicchio | 2016June7 | 2016Nov8 | 2018June5
2155079 | Manabu | Lonny | 2016June7 | 2016Nov8 |
2155083 | Scott | Bosomworth | 2016June7 | | 2018June5
ONE- 'VoterData'
lVoterUniqueID | szNameFirst | szNameLast
---------------+-------------+------------
2155077 | Camille | Bocchicchio
2155079 | Manabu | Lonny
2155083 | Scott | Bosomworth
MANY- 'VoterHistory'
lVoterUniqueID | sElectionAbbr
---------------+---------------
2155077 | 2016June7
2155077 | 2016Nov8
2155077 | 2018June5
2155079 | 2016June7
2155079 | 2016Nov8
2155083 | 2016June7
2155083 | 2018June5
Using Crosstab query
TRANSFORM First(H.sElectionAbbr) AS FirstOfsElectionAbbr
SELECT H.lVoterUniqueID AS ID, D.szNameFirst AS [First Name], D.szNameLast AS [Last Name]
FROM VoterData AS D INNER JOIN VoterHistory AS H ON D.lVoterUniqueID = H.lVoterUniqueID
GROUP BY H.lVoterUniqueID, D.szNameFirst, D.szNameLast
PIVOT H.sElectionAbbr;
I've been playing around and just not coming up with anything workable :( I have a simple query:
SELECT DISTINCT IP.DeviceUID, IP.DeviceName, d.NodeName from Devices d
inner join IPSCHEMA IP on IP.PLCIP=d.CommunicationAddress
This brings me to the data I want to Pivot (Subset of that data below):
|---------------------|------------------|------------------|
| DeviceUID | DeviceName | NodeName |
|---------------------|------------------|------------------|
| 226 | Boiler | BOILER |
|---------------------|------------------|------------------|
| 226 | Boiler | AMMONIA |
|---------------------|------------------|------------------|
| 226 | Boiler | CHILLER |
|---------------------|------------------|------------------|
| 230 | SSilo | SSUG |
|---------------------|------------------|------------------|
| 230 | SSilo | WALKER |
|---------------------|------------------|------------------|
| 29 | Cooling | AMMONIA |
|---------------------|------------------|------------------|
| 29 | Cooling | BOILER |
|---------------------|------------------|------------------|
| 29 | Cooling | CAR_A |
|---------------------|------------------|------------------|
| 29 | Cooling | CAR_B |
|---------------------|------------------|------------------|
| 29 | Cooling | LINE1 |
|---------------------|------------------|------------------|
I need it to look like the following:
|---------------------|------------------|------------------|------------------|------------------|------------------|------------------|
| DeviceUID | DeviceName | Node1 | Node2 | Node3 | Node 4 | Node5 |
|---------------------|------------------|------------------|------------------|------------------|------------------|------------------|
| 226 | Boiler | BOILER | AMMONIA | CHILLER | | |
|---------------------|------------------|------------------|------------------|------------------|------------------|------------------|
| 230 | SSilo | SSUG | WALKER | | | |
|---------------------|------------------|------------------|------------------|------------------|------------------|------------------|
| 29 | Cooling | AMMONIA | BOILER | CAR_A | CAR_B | LINE1 |
|---------------------|------------------|------------------|------------------|------------------|------------------|------------------|
I'm sure I can export to Excel, modify, make it look like that. But I would like this to be a repeatable Stored Procedure I can use for the current dataset.
Thanks in advance.
Untested, but we just add a PIVOT to your initial query.
This also assumes you have a known or maximum number of nodes. If not, you would have to go DYNAMIC.
Select *
From (
Select *
,Item = concat('Node',Row_Number() over (Partition By DeviceUID,DeviceName Order by (Select null))
From (
SELECT DISTINCT IP.DeviceUID, IP.DeviceName, d.NodeName from Devices d
inner join IPSCHEMA IP on IP.PLCIP=d.CommunicationAddress
) A
) src
Pivot (max(NodeName) for Item in ([Node1],[Node2],[Node3],[Node4],[Node5]) ) pvt
I have following entities, EntityA, EntityB, EntityC and EntityD:
+----------+ +----------+ +----------+ +----------+
| EntityA | | EntityB | | EntityC | | EntityD |
+----------+ +----------+ +----------+ +----------+
| FC1 | | FC1 | | FC1 | | FC1 |
| FC2 | | FC2 | | FC2 | | FC2 |
| FC3 | | FC3 | | FC3 | | FC3 |
| FC4 | | FC4 | | FC4 | | FC4 |
| EA1 | | EB1 | | EC1 | | ED1 |
| EA2 | | EB2 | | EC2 | | ED2 |
| EA3 | | | | EC3 | | ED3 |
| EA4 | | | | | | ED4 |
+----------+ +----------+ +----------+ +----------+
Each entity has properties FC1, FC2, FC3 and FC4 that are common across all the entities; and some properties are specific to the entity. Also each entity references every other entity in the domain. There is a many-to-many relation between entities.
Which of the following DB designs is better? Or is there any other better approach than the two described below?
1)
+-------------+
| Link |
+-------------+
+---| id_T1(FK) |
+---| id_T2(FK) |
+---------------+ | +-------------+
| TableCommon | |
+---------------+ |
+-->| id(PK) |<----+-------+------------------+------------------+
| | FC1 | | | |
| | FC2 | | | |
| | FC3 | | | |
| | FC4 | | | |
| +---------------+ | | |
| | | |
| +----------+ +----------+ | +----------+ | +----------+ |
| | TableA | | TableB | | | TableC | | | TableD | |
| +----------+ +----------+ | +----------+ | +----------+ |
+---| id(FK) | | id(FK) |--+ | id(FK) |--+ | id(FK) |--+
| EA1 | | EB1 | | EC1 | | ED1 |
| EA2 | | EB2 | | EC2 | | ED2 |
| EA3 | | | | EC3 | | ED3 |
| EA4 | | | | | | ED4 |
+----------+ +----------+ +----------+ +----------+
In this design, the common properties of the entities above are stored in a separate table, the TableCommon; this table can be thought as the base table from which every other tables are derived. The Link table above stores the reference of one entity to other entity in the domain representing many-to-many relationship between entities.
2)
+----------+ +----------+ +----------+ +----------+
| TableA | | TableB | | TableC | | TableD |
+----------+ +----------+ +----------+ +----------+
| id(PK) |<--+ +-->| id(PK) | | id(PK) | | id(PK) |
| FC1 | | | | FC1 | | FC1 | | FC1 |
| FC2 | | | | FC2 | | FC2 | | FC2 |
| FC3 | | | | FC3 | | FC3 | | FC3 |
| FC4 | | | | FC4 | | FC4 | | FC4 |
| EA1 | | | | EB1 | | EC1 | | ED1 |
| EA2 | | | | EB2 | | EC2 | | ED2 |
| EA3 | | | | | | EC3 | | ED3 |
| EA4 | | | | | | | | ED4 |
+----------+ | | +----------+ +----------+ +----------+
| |
+----------+ | | +----------+ +----------+ +----------+
| TableAB | | | | TableAC | | TableAD | | TableBC |
+----------+ | | +----------+ +----------+ +----------+
| id_1(FK) |---+ | | id_1(FK) | | id_1(FK) | | id_1(FK) | ...
| id_2(FK) |------+ | id_2(FK) | | id_2(FK) | | id_2(FK) |
+----------+ +----------+ +----------+ +----------+
In this design each entity is represented by it's own table. The common properties of the entities are not extracted into a separate table. But separate tables are created to represent many-to-many relations between each entity e.g. TableAB represents link between TableA and TableB, similarly TableBC represents link between TableB and TableC and so on. In this case there will be total 6 tables, TableAB, TableAC, TableAD, TableBC, TableBD and TableCD representing many-to-many relations between all the 4 entity tables, TableA, TableB, TableC and TableD.
From the above two designs I can think of following pros and cons for each:
First design:
Pros:
There are less tables created in the design.
Any change in the common properties of the entities has to be made in only one table, the TableCommon.
Adding new entity in the design is easy.
Cons:
All the addition, update and deletion has to be made through a single table, the TableCommon, to maintain referential integrity. That could be a bottleneck.
Adding entries into an entity table has to be done in two tables.
Second Design:
Pros:
Each entity is represented by a separate table hence there is no bottleneck while addition, update and deletion.
Adding entries in an entity table has to be done in a single table.
Cons:
Too many tables are created for storing references between entities.
Adding new entity is cumbersome.
Changing common properties of entities has to be done in all the entity tables.
Which of the above designs is better, or is there some other even better approach? Here better is in terms of performance, storage space, maintainance and scalability.
I think that you answered many part of your question very well. I just notify some other points.
Note 1: about TableCommon strategy
I strongly recommend to use TableCommon to save common fields. It has not many side effects on your evaluation parameters (Performance, Redundancy, Scalability, Maintenance and etc.).
Note 2: about Link table strategy
The parameters that are important here:
The number of records in Entities A,B,C and D
The number of record in many-to-many relationships among them
The number of CRUDs from these many-to-many relationships
If you have a lot of records in them and you have a lot of CRUDs and the performance of them is vital, you should not use Link table strategy.
However, If you have only two or more tables (for example EntityA and EntityB) that have a lot of records in many-to-many relationship, you can use EntityAB strategy only for them and use Link table strategy for the others.
Note 3: using Fact Table between Entity A,B,C and D
I know that it is very bad design at the first look.
But, based on the evaluation parameters, it can be useful in some cases.
Using Fact Table like this:
gathering F.Ks of all Entities A,B,C and D in one table.
First Cons:
If the many-to-many relationships have many other fields, we cannot use this strategy.
There are many bad Nullification in that Fact Table.
Pros:
You can fetch all EntityA relationships in one record.
Reduce the number of entities.
Reduce the number of records.