It is very difficult to accurately suggest a solution with no ability to run testing, or see how the DB is indexed etc. But I'm going to try anyway.
You ideally need to find a balance, if your query is likely to return a lot of data and run quickly then I would err towards running the query on the main table twice, if it is likely to take a long time and return a relatively small number of rows then I would stick with the temp table approach.
Given the information you have provided in the question it appears as though there is no problem with the speed of the select, in which case I would tend to agree with you, the additional cost of inserting into a temp table, then selecting from the temp table is as much overhead as performing the select query twice. The queries can be simplified as follows:
SELECT COUNT(*) [TotalHolidaysCount],
COUNT(DISTINCT PropertyID) [PropertyCount]
FROM dbo.Holiday
WHERE DepartureDate > GETDATE()
AND Country = 'FR'
SELECT *
FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY(CASE WHEN StartDate <= '01/Apr/2013' THEN 1 ELSE 0 END) ASC, [Price] ASC) [RowNumber]
FROM ( SELECT h.*, ROW_NUMBER(PARTITION BY PropertyID, ORDER BY Price ASC) [PropertyRow]
FROM dbo.Holiday
WHERE DepartureDate > GETDATE()
AND Country = 'FR'
) h
WHERE PropertyRow = 1
) h
WHERE Rownumber BETWEEN 1 AND 10
If you do find this is a performance hit and you would rather cache the results of the select I'd be inclined to use table variables instead of temp tables, and only store the Holiday ID primary key, and join back to holiday on the fast indexed join of primary key = primary key as follows:
DECLARE @Results TABLE (ID INT NOT NULL PRIMARY KEY)
INSERT @Results
SELECT ID
FROM dbo.Holiday
WHERE DepartureDate > GETDATE()
AND Country = 'FR'
SELECT COUNT(*) [TotalHolidaysCount],
COUNT(DISTINCT PropertyID) [PropertyCount]
FROM dbo.Holiday h
INNER JOIN @Results r
ON r.ID = h.ID
SELECT *
FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY(CASE WHEN StartDate <= '01/Apr/2013' THEN 1 ELSE 0 END) ASC, [Price] ASC) [RowNumber]
FROM ( SELECT h.*, ROW_NUMBER(PARTITION BY PropertyID, ORDER BY Price ASC) [PropertyRow]
FROM dbo.Holiday h
INNER JOIN @Results r
ON r.ID = h.ID
) h
WHERE PropertyRow = 1
) h
WHERE Rownumber BETWEEN 1 AND 10
This way you are caching as little data as possible (one column of integers), while still retaining enough data to do a fast indexed search on dbo.Holiday.
It ultimately has to come down to looking at your execution plans, creating proper indexes and testing various approaches to find the one that best suits you.
I think you need a track_segment
table to track the segments between stations. The problem with your Station
table is that you include distance there, but distance to/from what?
So let's try this:
Train
-----
ID
Train_number
Days_of_operation
other_details
Station
-------
ID
Name
other_data
Track_Segment
-------------
From_Station_ID
To_Station_ID
Length
Line_ID
Line
----
ID
Name
other_data
Train_Run
---------
ID
Train_ID
from_Station_ID
to_station_ID
Depart_datetime
Arrive_datetime
other_details
This allows a Train_run
to reference a specific train, as well as the two endpoints of the journey. Of course, there might be multiple segments of track between these two stations, so the total length of the journey could be had by looking at all the segments between from_station_id
and to_station_id
in track_segment
. I also included the line
table, since you had a refernce to it in your tables. I assume that a "Line" is just a descriptive term for a specific train journey that might have multiple stops along the way, like "Orient Express" or "Rocky Mountain Route".
Best Answer
Your bus schedule is actually much more complicated than just one many-to-many. There are actually at least a couple of many-to-many relationships needed to record the data in the schedule chart you've shown.
Consider this ERD:
Here is the type of data that would go in each table:
BUS_NUMBER
has a bus #1. Maybe this also has a name or other information.BUS_ROUTE_TYPE
is where you have (1)a, (1)b, (1)c. It is a child table ofBUS_NUMBER
. Maybe different bus numbers don't have a, b, and c. Some may only have a, some may have more than three, etc.STOP
is the list of places where busses go, like Noszlopy G. park or Cifra Palota, etc. Note that the route type has an original starting location (origin) and a finishing location (destination). It also has a list of stops along the way, which are inTRAVEL_TIME_OFFSET
(see next)TRAVEL_TIME_OFFSET
is a many-to-many intersection between a bus route type (e.g. 1b) and the places where the bus goes. For each stop along the way, the number of minutes of travel time to that stop from the origin is recorded. These are in your schedule chart as the numbers in circles on the left hand side. For example, for bus route 1b stop Piaristak tere is 0 minutes, Kodaly Iskola is 6 minutes, and Homokbanya kollegium is 15 minutes (including all the others along the way too)SCHEDULE_TYPE
is the list of different starting times - for example on school days, in summer workdays, free days, etc. These are the blocks of numbers on the right hand side of the chart (not the numbers in the blocks, the names of the blocks themselves)DEPARTURE
is also a many-to-many intersection. This is the list of times that a bus of a certain route type (e.g. 1c) leaves the origin stop, depending on what schedule type is in effect. For example, in Non-Working Days, Bus Route Type 1c departs at 20:40 and 22:30.SAMPLE DATA
Here is some sample table to illustrate how to use these tables. I'm using meaningless (auto incremented) Surrogate Keys for the tables because that's probably what you want to use. I'm picking numbers for these to help make it clearer how records in one table join to others using primary and foreign keys.