Check for interchangable column values in SQL - sql

I have 2 tables. The first one contains IDs of certain airports, the second contains flights from one airport to another.
ID Airport
---- ----
12 NYC
23 LOS
21 AMS
54 SFR
33 LSA
from to cost
---- ---- ----
12 23 500
12 23 250
23 12 200
21 23 100
54 12 400
33 21 700
I'd like to return a table where it contains ONLY airports that are interchangeable (NYC -LOS) in that case, with a total cost.
please note that there're identical (from , to) rows with different costs and the desired output needs to aggregate all costs for each unique combination
Desired Output :
airport_1 airport_2 total_cost
---- ---- ----
NYC LOS 950

You can get the result without need of a subquery by using LEAST() and GREATEST() functions along with HAVING clause such as
SELECT MIN(airport) AS airport_1, MAX(airport) AS airport_2, SUM(cost)/2 AS total_cost
FROM flights
JOIN airports
ON id IN ("from" , "to")
GROUP BY LEAST("from","to"), GREATEST("from","to")
HAVING COUNT(DISTINCT "from")*COUNT(DISTINCT "to")=4
where each pair(2) is counted twice(2) -> (2*2=4)
Demo

Related

Replace Id of one column by a name from another table while using the count statement?

I am trying to get the count of patients by province for my school project, I have managed to get the count and the Id of the province in a table but since I am using the count statement it will not let me use join to show the ProvinceName instead of the Id (it says it's not numerical).
Here is the schema of the two tables I am talking about
The content of the Province table is as follow:
ProvinceId
ProvinceName
ProvinceShortName
1
Terre-Neuve-et-Labrador
NL
2
Île-du-Prince-Édouard
PE
3
Nouvelle-Écosse
NS
4
Nouveau-Brunswick
NB
5
Québec
QC
6
Ontario
ON
7
Manitoba
MB
8
Saskatchewan
SK
9
Alberta
AB
10
Colombie-Britannique
BC
11
Yukon
YT
12
Territoires du Nord-Ouest
NT
13
Nunavut
NU
And here is n sample data from the Patient table (don't worry it's fake data!):
SS
FirstName
LastName
InsuranceNumber
InsuranceProvince
DateOfBirth
Sex
PhoneNumber
2
Doris
Patel
PATD778276
5
1977-08-02
F
514-754-6488
3
Judith
Doe
DOEJ7712917
5
1977-12-09
F
418-267-2263
4
Rosemary
Barrett
BARR05122566
6
2005-12-25
F
905-638-5062
5
Cody
Kennedy
KENC047167
10
2004-07-01
M
604-833-7712
I managed to get the patient count by province using the following statement:
select count(SS),InsuranceProvince
from Patient
full JOIN Province ON Patient.InsuranceProvince = Province.ProvinceId
group by InsuranceProvince
which gives me the following table:
PatientCount
InsuranceProvince
13
1
33
2
54
3
4
4
608
5
1778
6
25
7
209
8
547
9
649
10
6
11
35
12
24
13
How can I replace the id's with the correct ProvinceShortName to get the following final result?
ProvinceName
PatientCount
NL
13
PE
33
NS
54
NB
4
QC
608
ON
1778
MB
25
SK
209
AB
547
BC
649
YT
6
NT
35
NU
24
Thanks in advance!
So you can actually just specify that in the select. Note that it's best practise to include the thing you group by in the select, but since your question is so specific then...
SELECT ProvinceShortName, COUNT(SS) AS PatientsInProvince
FROM Patient
JOIN Province ON Patient.InsuranceProvince=Province.ProvinceId
GROUP BY InsuranceProvince;
I would suggest:
select pr.ProvinceShortName, count(*)
from Patient p join
Province pr
on p.InsuranceProvince = pr.ProvinceId
group by pr.ProvinceShortName
order by min(pr.ProvinceId);
Notes:
The key is including the columns you want in the select and group by.
You seem to want the results in province number order, so I included an order by.
There is no need to count the non-NULL values of SS. You might as well use count(*).
Table aliases make the query easier to write and to read.
I assume that you need to show the patient count by province.
SELECT
Province.ProvinceShortName AS [ProvinceName]
,COUNT(1) as [PatinetCount]
FROM Patient
RIGHT JOIN Province ON Patient.InsuranceProvince = Province.ProvinceId
GROUP BY ProvinceShortName
Just altering your query to
select ProvinceShortName As PatientCount,count(InsuranceProvince) As PatientCount
from Patient
full JOIN Province ON Patient.InsuranceProvince = Province.ProvinceId
group by ProvinceShortName

Join on second table if value not found in first table

I would like to join on a second table only if the results of the first join are blank. Below is a subsection of Table A data:
ID Metro Submarket
1 NYC Manhattan
2 NYC Brooklyn
3 NYC Queens
4 NYC Bronx
5 NYC Newark
The tables I'm using for the joins are:
Table B Table C
Metro Submarket A.Price B.Price C.Price Metro A.Price B.Price C.Price
NYC Manhattan 54 32 48 NYC 50 49 69
NYC Queens 35 39 59 Philly 49 48 37
NYC Brooklyn 20 49 58 Chicago 20 48 36
NYC Bronx 49 30 20
NYC Newark 49 50 -
I'm adding the Price columns from Table B to Table A based on a Metro and Submarket match. However, Table B doesn't have all the prices. If I can't find a match in Table B then I want to look into Table C for a match only on Metro.
For ID 5, we can find the A and B prices in Table B. However, the C price is blank. In that case, I want it to retrieve the C price from Table C (69 is what it would choose).
I'm using SAS 9.4. SQL, macros, or anything else SAS can handle is welcome!
You can left join both tables to the main table and simply use COALESCE(). This will give you the value if present in Table B, otherwise it will give you the value in Table C:
PROC SQL;
CREATE TABLE Output AS
SELECT
ta.ID,
ta.Metro,
ta.Submarket,
COALESCE(tb.A_Price,tc.A_Price) AS A_Price,
COALESCE(tb.B_Price,tc.B_Price) AS B_Price,
COALESCE(tb.C_Price,tc.C_Price) AS C_Price
FROM
tablea ta
LEFT JOIN
tableb tb
ON (tb.Metro = ta.Metro)
AND (tb.Submarket = ta.Submarket)
LEFT JOIN
tablec tc
ON (tc.Metro = ta.Metro);
QUIT;

Oracle - Converting a column to rows and getting a sum

Im trying to convert a column to rows and get a sum of the items ordered. Below is how the data is currently in the database. Notice how the DollarDays store gives the indication that not all stores have ordered all items.
Store ItemNumber Description Ordered
---- ---------- ---------- ---------
WallyMart 10021 Corn 10
J-Mart 10021 Corn 4
Big-H Foods 10021 Corn 32
WallyMart 20055 Beans 11
J-Mart 20055 Beans 3
Big-H Foods 20055 Beans 21
DollarDays 50277 Onions 48
This is my goal below. It looks to be pretty much grouping by ItemNumber
ItemNumber Description WallyMart J-Mart Big-H Foods DollarDays TotalOrdered
---------- ----------- --------- ------ ----------- ---------- ------------
10021 Corn 10 4 32 0 46
20055 Beans 11 3 21 0 35
50277 Onions 0 0 0 48 48
When I try PIVOT this is shortened example of what I get. Im totally lost.
ItemNumber Description WallyMart J-Mart Big-H Foods DollarDays
---------- ----------- --------- ------ ----------- ----------
10021 Corn 10 Null Null Null
10021 Corn Null 4 Null Null
10021 Corn Null Null 32 Null
10021 Corn Null Null Null 0
FYI I am of course a beginner and a student so please forgive me I haven't posted exactly right.
Any help is appreciated.
Try this:
select
itemnumber, description,
coalesce("WallyMart",0) as "WallyMart",
coalesce("J-Mart",0) as "J-Mart",
coalesce("Big-H Foods",0) as "Big-H Foods",
coalesce("DollarDays",0) as "DollarDays",
coalesce("WallyMart",0) + coalesce("J-Mart",0) + coalesce("Big-H Foods",0) + coalesce("DollarDays",0) as "Total"
from
(select * from stores) s
pivot
(max(ordered)
for store in
('WallyMart' as "WallyMart",
'J-Mart' as "J-Mart",
'Big-H Foods' as "Big-H Foods",
'DollarDays' as "DollarDays")) p
To explain a bit, the PIVOT clause consists of 2 parts - the aggregation and the list of values used for this aggregation. For your case, the aggregation used is MAX, since it looks like one store can have only one record for a particular product. If that is not so, SUM would be the right function to use. Likewise, since we want store-wise details, and not product-wise, we specify the distinct values of the store column in the list.
COALESCE is used to default null values in the ordered column to 0. Finally, we add the 4 derived columns (after coalescing to 0) to get the total value.
SQLFiddle

Access SQL - Select only the last sequence

I have a table with an ID and multiple informative columns. Sometimes however, I can have multiple data for an ID, so I added a column called "Sequence". Here is a shortened example:
ID Sequence Name Tel Date Amount
124 1 Bob 873-4356 2001-02-03 10
124 2 Bob 873-4356 2002-03-12 7
124 3 Bob 873-4351 2006-07-08 24
125 1 John 983-4568 2007-02-01 3
125 2 John 983-4568 2008-02-08 13
126 1 Eric 345-9845 2010-01-01 18
So, I would like to obtain only these lines:
124 3 Bob 873-4351 2006-07-08 24
125 2 John 983-4568 2008-02-08 13
126 1 Eric 345-9845 2010-01-01 18
Anyone could give me a hand on how I could build a SQL query to do this ?
Thanks !
You can calculate the maximum sequence using group by. Then you can use join to get only the maximum in the original data.
Assuming your table is called t:
select t.*
from t join
(select id, MAX(sequence) as maxs
from t
group by id
) tmax
on t.id = tmax.id and
t.sequence = tmax.maxs

How do I loop through a table until condition reached

I have a table product, pick_qty, shortfall, location, loc_qty
Product Picked Qty Shortfall Location Location Qty
1742 4 58 1 15
1742 4 58 2 20
1742 4 58 3 15
1742 4 58 4 20
1742 4 58 5 20
1742 4 58 6 20
1742 4 58 7 15
1742 4 58 8 15
1742 4 58 9 15
1742 4 58 10 20
I want a report to loop around and show the number of locations and the quantity I need to drop to fulfil the shortfall for replenishment. So the report would look like this.
Product Picked Qty Shortfall Location Location Qty
1742 4 58 1 15
1742 4 58 2 20
1742 4 58 3 15
1742 4 58 4 20
Note that it is best not to think about SQL "looping through a table" and instead to think about it as operating on some subset of the rows in a table.
What it sounds like you need to do is create a running total that tells how many of the item you would have if you were to take all of them from a location and all of the locations that came before the current location and then check to see if that would give you enough of the item to fulfill the shortfall.
Based on your example data, the following query would work, though if Locations aren't actually numerics then you would need to add a row number column and tweak the query a bit to use the row number instead of the Location Number; It would still be very similar to the query below.
SELECT
Totals.Product, Totals.PickedQty, Totals.ShortFall, Totals.Location, Totals.LocationQty
FROM (
SELECT
TheTable.Product, TheTable.PickedQty, TheTable.ShortFall,
TheTable.Location, TheTable.LocationQty, SUM(ForRunningTotal.LocationQty) AS RunningTotal
FROM TheTable
JOIN TheTable ForRunningTotal ON TheTable.Product = ForRunningTotal.Product
AND TheTable.Location >= ForRunningTotal.Location
GROUP BY TheTable.Product, TheTable.PickedQty, TheTable.ShortFall, TheTable.Location, TheTable.LocationQty
) Totals
-- Note you could also change the join above so the running total is actually the count of only the rows above,
-- not including the current row; Then the WHERE clause below could be "Totals.RunningTotal < Totals.ShortFall".
-- I liked RunningTotal as the sum of this row and all prior, it seems more appropriate to me.
WHERE Totals.RunningTotal - Totals.LocationQty <= Totals.ShortFall
AND Totals.LocationQty > 0
Also - as long as you are reading my answer, an unrelated side-note: Based on the data you showed above, your database schema isn't normalized as far as it could be. It seems like the Picked Quantity and the ShortFall actually depend only on the Product, so that would be a table of its own, and then the Location Quantity depends on the Product and Location, so that would be a table of its own. I'm pointing it out because if your data contained different Picked Quantities/ShortFall for a single product, then the above query would break; This situation would be impossible with the normalized tables I mentioned.