I have three tables which are joined by the following
FLIGHT F,
RESERVATION R,
AIRPLANE A
where F.AirplaneSerialNum = A.AirplaneSerialNum
and F.FlightCode = R.FlightCode
In the airplane table, there is a column to store the maximum capacity of any given plane.
In the reservation table, records of passengers are stored, and the flight they are embarking on is based on the FlightCode
In the flight table, there is a column to store the remaining capacity of any given plane, and each flight is uniquely determined by its FlightCode
Thus, I would like to find a way to update the remaining capacity by taking the values of the original maximum capacity, then get the remaining capacity by doing a COUNT() of the number of times the FlightCode appears in the reservation table
So far I've got the first half to work (setting RemCapacity as the original max capacity)
UPDATE FLIGHT F
SET F.RemCapacity = (SELECT Capacity FROM airplane
WHERE AIRPLANE.airplaneserialnum = F.airplaneserialnum);
However i'm stuck trying to minus the number of reservations
-- to get the count for number of times the FlightCode appears
SELECT COUNT(*) FROM reservation group by flightcode
UPDATE FLIGHT F
SET F.RemCapacity = F.RemCapacity -
(SELECT COUNT(*) FROM reservation group by flightcode ) WHERE F.FlightCode = R.FlightCode;
(returns %s invalid identifier SQL error)
And also if possible, how can I combine both halves into one query?
Not totally sure, but I think this might do the trick for you, doing all the work in one statement:
UPDATE FLIGHT F
SET F.RemCapacity = (SELECT Capacity FROM airplane
WHERE AIRPLANE.airplaneserialnum = F.airplaneserialnum) -
(SELECT COUNT(*) FROM reservation r WHERE F.FlightCode = R.FlightCode);
Related
I've got a database that holds information about volunteers and their participation in a range of events.
The following query gives me a list of their names and total attendances
SELECT
volunteers.last_name,
volunteers.first_name,
count (bookings.id)
FROM
volunteers,
bookings
WHERE
volunteers.id = bookings.volunteer_id
GROUP BY
volunteers.last_name,
volunteers.first_name
I want the result table to show the distinct number of attendances and how many there are of each; So if five people did one event it'd display 1 in the first column and 5 in the second and so on.
Thanks
If I understand correctly, you want what I call a "histogram of histograms" query:
select numvolunteers, count(*) as numevents, min(eventid), max(eventid)
from (select b.eventid, count(*) as numvolunteers
from bookings b
group by b.eventid
) b
group by numvolunteers
order by numvolunteers;
The first column is the number of volunteers booked for an "event". The second is the number of events where this occurs. The last two columns are just examples of events that have the given number of volunteers.
I can't think of a single T-SQL operation through the following problem can be solved. I can think only of record by record operation to solve the problem.
The problem is as follows:
For each village a number of shops are assigned ( from 1 to n).
Same shop can serve more than one village.
Each shop has different maximum capacity (that is given in a table)
Need to assign all members of a family (based on family id) to same shop in such a way that `'nearly' equal families are assigned to each FPS. As the number of families may not be equally divisible FPS number a few shops may get one additional Family. While assigning last family if the FPS max capacity exceeds by a few member that is acceptable. This however would not happen if last family has just one member.
Some families may remain unassigned if FPS max capacity exceeds for all FPS assigned to that village.
Available tables
Population: Uniqid, Familyid, name, shopcode, villagecode
Village: VillageId
Shop: ShopId, Name, MaxCapacity
VillageShopMap: VillageId, ShopId
My solution is as follows
Take each village
Get one Family for that village
Get a shop with minimum number of person allotted for that village , whose current capacity < max Capacity
Continue until that population from that village is exhausted, or Shop MaxCapacity is reached (in that case some people remain unassigned to shops, that is acceptable)
Loop
My solution is extremely slow. Looking for a better solution.
Thanks
Not much but could use this to fill a shop in one pass
In this case 20 is the shop capacity
The top 20 is just to not evaluate more than needed - a family will have at least one
This could leave some shops empty
You could scale capacity to a fraction of the actual capacity
with famA as
( select top 20 sParID as ID, count(*) as famSize
from docSVsys
group by sParID
)
, fam as
( select famA.*, ROW_NUMBER() over (order by ID) as rn
from famA
)
, famCum as
( select fam.ID, famSize, fam.rn,
(select sum(f.famSize) from fam f where f.rn <= fam.rn) as cum
from fam
)
select famCum.*
from famCum
where famCum.rn <= (select max(f.rn) from famCum f where f.cum <= 20) + 1
order by famCum.rn
Repeating shopcode and village code in Population is not 3NF
Should have a Family table and I would denormalize and put a famsize in the table so you are not calculating size over and over.
Or assume you have the above Family table and a ShopView with CurCapacity
Can assign a one family to all open shops in one pass
with ShopOne as
( select ShopId, min(VillageID) as VillageID
from ShopView
where CurCapacity < Max Capacity
)
, FamilyRn as
( select Family.*, row_number (over VillageID order by ID) as rn
from Family where ShopID is null
)
select Family.*, ShopOne.*
from ShopOne
join FamilyRn
on ShopOne.VillageID = Famility.VillageID
and FamilyRn = 1
So, I have a problem with a SQL Query.
It's about getting weather data for German cities. I have 4 tables: staedte (the cities with primary key loc_id), gehoert_zu (contains the city-key and the key of the weather station that is closest to this city (stations_id)), wettermessung (contains all the weather information and the station's key value) and wetterstation (contains the stations key and location). And I'm using PostgreSQL
Here is how the tables look like:
wetterstation
s_id[PK] standort lon lat hoehe
----------------------------------------
10224 Bremen 53.05 8.8 4
wettermessung
stations_id[PK] datum[PK] max_temp_2m ......
----------------------------------------------------
10224 2013-3-24 -0.4
staedte
loc_id[PK] name lat lon
-------------------------------
15 Asch 48.4 9.8
gehoert_zu
loc_id[PK] stations_id[PK]
-----------------------------
15 10224
What I'm trying to do is to get the name of the city with the (for example) highest temperature at a specified date (could be a whole month, or a day). Since the weather data is bound to a station, I actually need to get the station's ID and then just choose one of the corresponding to this station cities. A possible question would be: "In which city was it hottest in June ?" and, say, the highest measured temperature was in station number 10224. As a result I want to get the city Asch. What I got so far is this
SELECT name, MAX (max_temp_2m)
FROM wettermessung, staedte, gehoert_zu
WHERE wettermessung.stations_id = gehoert_zu.stations_id
AND gehoert_zu.loc_id = staedte.loc_id
AND wettermessung.datum BETWEEN '2012-8-1' AND '2012-12-1'
GROUP BY name
ORDER BY MAX (max_temp_2m) DESC
LIMIT 1
There are two problems with the results:
1) it's taking waaaay too long. The tables are not that big (cities has about 70k entries), but it needs between 1 and 7 minutes to get things done (depending on the time span)
2) it ALWAYS produces the same city and I'm pretty sure it's not the right one either.
I hope I managed to explain my problem clearly enough and I'd be happy for any kind of help. Thanks in advance ! :D
If you want to get the max temperature per city use this statement:
SELECT * FROM (
SELECT gz.loc_id, MAX(max_temp_2m) as temperature
FROM wettermessung as wm
INNER JOIN gehoert_zu as gz
ON wm.stations_id = gz.stations_id
WHERE wm.datum BETWEEN '2012-8-1' AND '2012-12-1'
GROUP BY gz.loc_id) as subselect
INNER JOIN staedte as std
ON std.loc_id = subselect.loc_id
ORDER BY subselect.temperature DESC
Use this statement to get the city with the highest temperature (only 1 city):
SELECT * FROM(
SELECT name, MAX(max_temp_2m) as temp
FROM wettermessung as wm
INNER JOIN gehoert_zu as gz
ON wm.stations_id = gz.stations_id
INNER JOIN staedte as std
ON gz.loc_id = std.loc_id
WHERE wm.datum BETWEEN '2012-8-1' AND '2012-12-1'
GROUP BY name
ORDER BY MAX(max_temp_2m) DESC
LIMIT 1) as subselect
ORDER BY temp desc
LIMIT 1
For performance reasons always use explicit joins as LEFT, RIGHT, INNER JOIN and avoid to use joins with separated table name, so your sql serevr has not to guess your table references.
This is a general example of how to get the item with the highest, lowest, biggest, smallest, whatever value. You can adjust it to your particular situation.
select fred, barney, wilma
from bedrock join
(select fred, max(dino) maxdino
from bedrock
where whatever
group by fred ) flinstone on bedrock.fred = flinstone.fred
where dino = maxdino
and other conditions
I propose you use a consistent naming convention. Singular terms for tables holding a single item per row is a good convention. You only table breaking this is staedte. Should be stadt.
And I suggest to use station_id consistently instead of either s_id and stations_id.
Building on these premises, for your question:
... get the name of the city with the ... highest temperature at a specified date
SELECT s.name, w.max_temp_2m
FROM (
SELECT station_id, max_temp_2m
FROM wettermessung
WHERE datum >= '2012-8-1'::date
AND datum < '2012-12-1'::date -- exclude upper border
ORDER BY max_temp_2m DESC, station_id -- id as tie breaker
LIMIT 1
) w
JOIN gehoert_zu g USING (station_id) -- assuming normalized names
JOIN stadt s USING (loc_id)
Use explicit JOIN conditions for better readability and maintenance.
Use table aliases to simplify your query.
Use x >= a AND x < b to include the lower border and exclude the upper border, which is the common use case.
Aggregate first and pick your station with the highest temperature, before you join to the other tables to retrieve the city name. Much simpler and faster.
You did not specify what to do when multiple "wettermessungen" tie on max_temp_2m in the given time frame. I added station_id as tiebreaker, meaning the station with the lowest id will be picked consistently if there are multiple qualifying stations.
I am building an application that shares some stuff...
Each Object can be rated has a rating 1..5 start. I keep for each the number of rates per star so can calculate the Avg rate.
So per Obj I have: Avg rate and total rate.
I need to get the top10 rated Obj - so can do it using AvgRate+TotalRate (those who has these values as top10).
I want to have in the server an sql table like this:
ObjId (index), totalRate, AvgRate...
If possible to have this table sorted so that can get the top10 as the first 10?
How can query the top10 with the calculation I want?
Also - I need to get the top10 per users. So per user I have all the Obj he shared so can have all of the rates of these Obj - with all info per Obj as mentioned before.
I need to know how to calculate a user rate, and also - how to fast get the top10.
Any ideas?
Later Edit: Sorry, didn't understand your question when writing this answer, gonna leave it still for others..
What's your formula for TotalRate ? And what do you mean by "so can do it using AvgRate+TotalRate" Why are you summing an average to TotalRate - whatever that is?
Best practice is to always compute the sums/averages incrementally.
I would model Obj like this:
A total number of rates received
B total sum of points received
C average (float: B/A )
D - foreign key to user (author/owner of Obj)
When object receives rate X, you then recompute A = A + 1, B = B + X, C = B/A.
In the same manner pre-compute aggregate sums/average. So if Obj belongs to user, create the same fields (A, B, C) to User model/table, and when Obj receives rate X, also update A, B, C values for user D (owner of Obj). Then, when selecting top 10 users, you do not need to join with Obj table (which may get huge), you only select users - descending by B or C column, limit 10.
We have a view that stores the history of membership plans held by our members and we have been running a half price direct debit offer for some time. We've been asked to report on whether people are allowing the direct debit to renew (at full price) but I'm no SQL expert!
The view in effect is
memberRef, historyRef, validFrom, validTo,MembershipType,PaymentType,totalAmount
Here
memberRef identifies the person (int)
historyRef identifies this row (int)
validFrom and validTo are the start and end of the plan (datetime)
MembershipType is the type of plan (int)
PaymentType is direct debit or credit card (a string - DD or EFT)
totalAmount is the price of the plan (decimal)
I'm wondering if there is a query as opposed to a cursor I can use to count the number of policies which are at half price and have another direct debit policy that follows on from it.
If we can also capture if that person first joined at half price or if there was a gap where membership had lapsed before they took the half price incentive that would be great.
Thanks in advance for any help!
For example
select count(MemberRef), max(vhOuter.validFrom) "most recent plan start",
(select top(1) vh2.validFrom
from v_Membershiphistory vh2
where (vh2.totalamount = 14.97 or vh2.totalamount = 25.50)
and vh2.memberref = vhOuter.memberref
order by createdat desc
) "half price plan start"
from v_membershiphistory vhOuter
where vhOuter.memberref in (select vh1.memberref from v_membershiphistory vh1 where vh1.totalamount = 14.97 or vh1.totalamount = 25.50)--have taken up offer
group by memberref
having max(vhOuter.validFrom) > (select top(1) vh2.validFrom
from v_Membershiphistory vh2
where (vh2.totalamount = 14.97 or vh2.totalamount = 25.50)
and vh2.memberref = vhOuter.memberref
order by createdat desc
)
This will display the members who have a half price plan and have a valid from date that is greater than the valid from date of that plan.
Not quite right as we should be testing that it is the same plan but...
if I change the select here to just count(memberRef) I get the count of memberRef for the member I'm grouping for each member I'm grouping i.e. for 5220 results I'd get 5220 rows returned each with in effect the number of plans I've selected
But I need to count the number of people taking the offer and proportion that renew. Also that renewal rate in the population that aren't taking the offer (which I'm guessing is a trivial change once I've got one set sorted)
I suppose I'm looking at how one operates on the set but compares multiple rows for each distinct person without using a cursor. But I might be wrong :)
try something like:
SELECT
a.*, b.*
FROM YourTable a
INNER JOIN YourTable b On a.memberRef=b.memberRef and a.validToDate<b.validFromDate
WHERE b.PaymentType='?direct debit?' and a.Cost='?half price?'
to get just counts use something like:
SELECT
COUNT(a.memberRef) AS TotalCount
FROM YourTable a
INNER JOIN YourTable b On a.memberRef=b.memberRef and a.validToDate<b.validFromDate
WHERE b.PaymentType='?direct debit?' and a.Cost='?half price?'