Complex SQL query assistance - sql

I have a DB that contains informations about buses arriving to/departing from a station. In my application, I need to display all departures from the station that the user selects.
That means that I have to identify each line that passes trough the selected station. I did that with the following query:
SELECT DISTINCT LinePaths.TimetableID
,Lines.LineName AS [Line]
,Timetable.Heading
,LinePaths.Departure
,Regime.Name AS [Regime]
FROM LinePaths
INNER JOIN Timetables ON Timetables.TimetableID = LinePaths.TimetableID
INNER JOIN Lines ON Timetable.LineID = Lines.LineID
INNER JOIN Stations ON LinePaths.Station = Station.StationID
INNER JOIN Regimes ON Timetables.Regime = Regimes.RegimeID
WHERE Station = #Station
The problem is that instead of Timetable.Heading I need to display the name of the final station that the bus is heading to. These are the schema's of the tables I have at my disposal:
Stations - ID, Name
Lines - LineID, LineName
Timetable - TimetableID, LineID, Heading, Regime (Regime defines on which days the bus drives)
LinePaths - ID, TimetableID, SN, Arrival, Departure, StationID (The SN is a serial number that defines the order of stations on a certain paths (the bus will arrive to those stations in that particular order).)
So, instead of the Timetable.Heading I need to have a Stations.Name, and here I come to my problem.
That column has to represent the final station to which the bus is going (for every line), so I have to somehow identify the final station (again for each line that passes trough the selected station). For that I have to use the LinePaths table. Inside that table the data is grouped by TimetableID, so that the entries with the same value for the TimetableID are ordered by the SN value that represents the order in which the bus arrives to the stations (like this: xxxxx, 1111, 3, 8:15, 8:20, 9999) - this means that the bus that drives according to the timetable 1111 will arrive at 8:15 to the station with StationID 9999, leave at 8:20, and that station is the 3rd one on this route (the xxxxx is the ID of the LinePath, and is not important for this query).
So, finally, my question is - how can I, using SQL, get the data in the following schema:
FinalStationName,
Departure(from selected station),
Regime.

The most efficient way would be to have a new table with a TimetableID and the name of the final station, and join this table with the other ones, but if you really need to obtain the value without additional tables, you new to calculate the final station for each timetable. This query obtains the id of each timetable and the name of the last station
SELECT li.TimetableID, s.Name
FROM LinePaths li INNER JOIN
(SELECT TimetableID, max(SN) as SN FROM LinePaths) as aux
ON aux.TimetableID = li.TimetableID AND aux.SN = li.SN
INNER JOIN Stations s
ON s.ID = li.StationID
and you could use it as a subquery and join it to your query via the TimetableID. Not the most efficient thing, but it should work.

Related

SQL query , group by only one column

i want to group this query by project only because there are two records of same project but i only want one.
But when i add group by clause it asks me to add other columns as well by which grouping does not work.
*
DECLARE #Year varchar(75) = '2018'
DECLARE #von DateTime = '1.09.2018'
DECLARE #bis DateTime = '30.09.2018'
select new_projekt ,new_geschftsartname, new_mitarbeitername, new_stundensatz
from Filterednew_projektkondition ps
left join Filterednew_fakturierungsplan fp on ps.new_projekt = fp.new_hauptprojekt1
where ps.statecodename = 'Aktiv'
and fp.new_startdatum >= #von +'00:00:00'
and fp.new_enddatum <= #bis +'23:59:59'
--and new_projekt= Filterednew_projekt.new_
--group by new_projekt
*
look at the column new_projekt . row 2 and 3 has same project, but i want it to appear only once. Due to different other columns this is not possible.
if its of interested , there is another coluim projectcondition id which is unique for both
You can't ask a database to decide arbitrarily for you, which records should be thrown away when doing a group. You have to be precise and specific
Example, here is some data about a person:
Name, AddressZipCode
John Doe, 90210
John Doe, 12345
SELECT name, addresszipcode FROM person INNER JOIN address on address.personid = person.id
There are two addresses stored for this one guy, the person data is repeated in the output!
"I don't want that. I only want to see one line for this guy, together with his address"
Which address?
That's what you have to tell the database
"Well, obviously his current address"
And how do you denote that an address is current?
"It's the one with the null enddate"
SELECT name, addresszipcode FROM person INNER JOIN address on address.personid = person.id WHERE address.enddate = null
If you still get two addresses out, there are two address records that are null - you have data that is in violation of your business data modelling principles ("a person's address history shall have at most one adddress that is current, denoted by a null end date") - fix the data
"Why can't i just group by name?"
You can, but if you do, you still have to tell the database how to accumulate the non-name data that it shows you. You want an address data out of it, it has 2 it wants to show you, you have to tell it which to discard. You could do this:
SELECT name, MAX(addresszipcode) FROM person INNER JOIN address on address.personid = person.id GROUP BY name
"But I don't want the max zipcode? That doesn't make sense"
OK, use the MIN, the SUM, the AVG, anything that makes sense. If none of these make sense, then use something that does, like the address line that has the highest end date, or the lowest end date that is a future end date. If you only want one address on show you must decide how to boil that data down to just one record - you have to write the rule for the database to follow and no question about it you have to create a rule so make it a rule that describes what you actually want
Ok, so you created a rule - you want only the rows with the minimum new_stundenstatz
DECLARE #Year varchar(75) = '2018'
DECLARE #von DateTime = '1.09.2018'
DECLARE #bis DateTime = '30.09.2018'
select new_projekt ,new_geschftsartname, new_mitarbeitername, new_stundensatz
from
(SELECT *, ROW_NUMBER() OVER(PARTITON BY new_projekt ORDER BY new_stundensatz) rown FROM Filterednew_projektkondition) ps
left join
Filterednew_fakturierungsplan fp on ps.new_projekt = fp.new_hauptprojekt1
where ps.statecodename = 'Aktiv'
and fp.new_startdatum >= #von +'00:00:00'
and fp.new_enddatum <= #bis +'23:59:59'
and ps.rown = 1
Here I've used an analytic operation to number the rows in your PS table. They're numbered in order of ascending new_stundensatz, starting with 1. The numbering restarts when the new_projekt changes, so each new_projekt will have a number 1 row.. and then we make that a condition of the where
(Helpful side note for applying this technique in future.. Ff it were the FP table we were adding a row number to, we would need to put AND fp.rown= 1 in the ON clause, not the WHERE clause, because putting it in the where would make the LEFT join behave like an INNER, hiding rows that don't have any FP matching record)

Access SQL Query: Find the most recent date entry for each player's test date with 2 joined tables

I have 1 table and 1 query that are joined by Player ID. I want to show only the latest test date result for height and weight columns in tblPlayerLogistics and Player Name and PlayerID from qryPlayersExtended
PlayerID is located in both table and query and they are joined.
I have playerID, height, weight,and testdate in tblplayerlogistics
I have PlayerID, Player Name in qryPlayersExtended
I would like a query that returns only one player record labeling the playerId and player name with the most current height and weight of each player determined by the testdate.
Query name: qryPlayersExtended
Table Name: tblPlayerLogistics
I have attached what I am trying to do in an image. This is an example of one player but I will have multiple players with multiple test dates
I have been struggling with this for weeks any help would be appreciated. I looked at this previous post but still couldnt figure it out
similar post
Assuming that there is not more than one test for a player on any one date. Warning, air code. Create a new query qryLastTestDate:
SELECT PlayerID, Max(TestDate) as LastTestDate FROM tblplayerlogistics Group By PlayerID Create a second query qryLastTest:
SELECT tblplayerlogistics.PlayerID, tblplayerlogistics.TestDate, tblplayerlogistics.height, tblplayerlogistics.weight FROM tblplayerlogistics INNER JOIN qryLastTestDate ON tblplayerlogistics.PlayerID = qryLastTestDate.PlayerID and tblplayerlogistics.TestDate = qryLastTestDate.LastTestDate
Your final query would be:
SELECT qryPlayersExtended.PlayerID, qryPlayersExtended.[Player Name], qryLastTest.TestDate, qryLastTest.Height, qryLastTest.Weight FROM qryPlayersExtended INNER JOIN qryLastTest ON qryPlayersExtended.PlayerID = qryLastTest.PlayerID; Add in additional fields as needed.
You will need two queries for this.
First, you need a query which gets the latest date per player. It will have two columns: PlayerId and Max(TestDate), Grouped by PlayerId. I've named this qryMaxPlayerTestDates. It will look something like this:
SELECT PlayerId, Max(TestDate) AS MaxDate
FROM tblPlayerLogistics
GROUP BY PlayerId;
Second, you join the PlayerId and Dates (MaxDate/TestDate) to get results limited by Max(TestDate). It will look something like this:
SELECT tblPlayerLogistics.PlayerId, tblPlayerLogistics.Height,
tblPlayerLogistics.Weight, tblPlayerLogistics.TestDate,
qryPlayersExtended.PlayerName
FROM qryPlayersExtended INNER JOIN (qryMaxPlayerTestDates
INNER JOIN tblPlayerLogistics
ON (qryMaxPlayerTestDates.MaxDate = tblPlayerLogistics.TestDate)
AND (qryMaxPlayerTestDates.PlayerId = tblPlayerLogistics.PlayerId))
ON qryPlayersExtended.PlayerId = qryMaxPlayerTestDates.PlayerId;
If your dates are not duplicated per Player (No Players have more than one test per date, or two tests on same date have different times), you will get one row per Player in the result with the Height/Weight for the latest test date.

Return data entered in column order by row

I am working on a simple timesheet module for a larger production system and need to display a table of information to the user. I have the following tables to work with:
TimeRecords
ID
WorkerID
AssyLineID
Station
Sequence
NbrHours
DateSubmitted
Workers
ID
Name
AssyLines
Name
During data entry, time is entered by AssyLine for each worker. A given worker may work on 2 or more different stations during the course of the day. The Sequence value is assigned based on the order of names as entered during data entry.
Now I want to return this data for all assembly lines and all workers in the following format:
ResultSet
Worker.ID
Worker.Name
AssyLine.Name - group returned rows by assembly line, in alphabetical order
Sequence - within each assembly line, group by sequence
NbrHours - total hours for worker for this assembly line, all stations
TotalHours - total hours for this worker across all assembly lines and stations
Other caveats:
1) The rows for a given worker should be grouped together, starting with the assembly line where they logged the most hours, in the sequence for that assembly line. I plan to consolidate all entries for a given worker into one row for display to the user and this is much easier if all rows for one user are grouped together. If that can't be done I will have to group and sort the row data in code...
Here is the query I have come up with so far:
SELECT
w.ID
,w.Name
,a.Name
,tr.NbrHours
,tr.Seq
FROM
TimeRecords tr
INNER JOIN
Workers w ON
w.ID = tr.WorkerId
INNER JOIN
AssyLines a ON
a.AssyLineID = tr.AssyLineId
WHERE
tr.DateSubmitted < '2000-01-01'
ORDER BY
w.Name
,a.Name
,tr.Seq
,NbrHours DESC
Obviously this leaves a lot to be desired. The worker entries are not grouped together and there is no overall total for the worker.
Can anyone help me get this right? I'm thinking I will need to do this with a Stored Proc rather than a view...
Thanks,
Dave
Most of this can be done with a simple group by clause; the messy part comes with your requirement for showing all hours, but I believe something like this should work depending on what DB you are using:
SELECT
w.ID
,w.Name
,a.Name
,tr.Seq
,SUM(tr.NbrHours) as nbrHours
(SELECT SUM(tr.NbrHours)
FROM TimeRecords tr2
WHERE tr2.WorkerId = w.id and tr2..DateSubmitted < '2000-01-01') as TotalHours
FROM
TimeRecords tr
INNER JOIN
Workers w ON
w.ID = tr.WorkerId
INNER JOIN
AssyLines a ON
a.AssyLineID = tr.AssyLineId
WHERE
tr.DateSubmitted < '2000-01-01'
GROUP BY
w.ID
,w.Name
,a.Name
,tr.Seq
ORDER BY
ReportName
,ShortName
,tr.Seq

Max of two columns from different tables

I need to get a max of the values from two columns from different tables.
eg the max of suburbs from schoolorder and platterorder. platterorder has clientnumbers that links to normalclient, and schoolorder has clientnumbers that links to school.
I have this:
SELECT MAX (NC.SUBURB) AS SUBURB
FROM normalClient NC
WHERE NC.CLIENTNO IN
(SELECT PO.CLIENTNO
FROM platterOrder PO
WHERE NC.CLIENTNO = PO.CLIENTNO)
GROUP BY NC.SUBURB
UNION
SELECT MAX (S.SUBURB) AS SCHOOLSUBURB
FROM school S
WHERE S.CLIENTNO IN
(SELECT S.CLIENTNO
FROM schoolOrder SO
WHERE S.CLIENTNO = SO.CLIENTNO)
GROUP BY S.SUBURB)
However that gets the max from platter order and joins it with the max of school. what I need is the max of both of them together.
=================================================
sorry for making this so confusing!
the output should only be one row.
it should be the suburb where the maxmimum orders have come from for both normal client and school clients. the orders are listed in platter order for normal clients, and school order for school clients. so it's the maximum value for two table's that don't have a direct relation.
hope that clears it up a bit !
If I'm understanding your question correctly, you don't need to use a GROUP BY since you're wanting the MAX of the field. I've also changed your syntax to use a JOIN instead of IN, but the IN should work just the same:
SELECT MAX (NC.SUBURB) AS SUBURB
FROM normalClient NC
JOIN platterOrder PO ON NC.ClientNo = PO.ClientNo
UNION
SELECT MAX (S.SUBURB) AS SCHOOLSUBURB
FROM school S
JOIN schoolOrder SO ON S.CLIENTNO = SO.CLIENTNO
Withouth knowing your table structures and seeing sample data, the best way I can recommend getting the MAX of results from the UNION is to use a subquery. There may be a better way with JOINs, but it's difficult to infer from your question:
SELECT MAX(Suburb)
FROM (
SELECT MAX (NC.SUBURB) AS SUBURB
FROM normalClient NC
JOIN platterOrder PO ON NC.ClientNo = PO.ClientNo
UNION
SELECT MAX (S.SUBURB)
FROM school S
JOIN schoolOrder SO ON S.CLIENTNO = SO.CLIENTNO
) T

sql queries to show the most popular record

I have four tables
Car (car_ registration_no, class, type_code)
Rental_history (rent_date, car_registration_no, rent_location_code, return_location_code)
Type (type_code, make, model)
Location (location_code, branch_name)
I need a query to show the most popular car rented by location.
I need a query to show the total rentals at each location for the previous month?
My code so far is as follows, but I couldn't complete it:
SELECT car.class, car.type_code , type.make, type.model
FROM car , type, rental_history
where rental_history.car_registration_no = car.car_registration_no
and car.type_code = type.type_code
You will need to join the tables and calculate the numbers. Let's start off with an easier query to point you in the right direction.
This will show you how many times a "type_code" car has been rented per location (untested, may contain errors)
SELECT
count(car.car_registration_no) as rental_num,
car.type_code,
rental_history.rent_location_code
FROM car
LEFT JOIN rental_history ON (rental_history.car_registration_no = car.car_registration_no)
GROUP BY car.type_code, rental_history.rent_location_code;
I'm using a left join here because there may be cars that have not been rented and won't have any history. Instead of not showing up, you will have a "0" for number of rentals.
Edit:
For the second query it's actually very straightforward. You need to group by location, filter on date and use COUNT (again, untested):
SELECT
count(rental_history.car_registration_no) as rental_num,
rental_history.rent_location_code
FROM rental_history
WHERE rent_date >= '2012-03-01' AND rent_date < '2012-04-01'
GROUP BY rental_history.rent_location_code;
Join All the table and use count..!