Find entries in same table WITHOUT similar location - sql

Imagine I have a table of fast food restaurants (FASTFOOD). Each of them has geo coordinates set in columns GEO_X and GEO_Y, as well as a column FRANCHISE. Franchise may be MCDONALDS or BURGERKING.
I want to select all Burger Kings which do NOT have a McDonalds within a specific distance, measured in geo coordinate degrees.
How do I do this?
I AM able to list the Burger Kings that DO have a McDonalds within a certain distance:
select t.*
from FASTFOOD t
INNER JOIN FASTFOOD s ON (ABS(t.geo_x - s.geo_x) < 0.01 AND ABS(t.geo_y - s.geo_y) < 0.01)
WHERE t.FRANCHISE= 'BURGERKING'
AND s.FRANCHISE = 'MCDONALDS';
But I have no idea how to find the "opposite".
The result sets of my query are the same whether I use an INNER JOIN, LEFT JOIN, RIGHT JOIN, or FULL OUTER JOIN, as all entries do have set geo coordinates.

I AM able to list the Burger Kings that DO have a McDonalds within a certain distance
use
[all burgerkings] except [burgerkings that have a mcd nearby]
that should only leave those without one nearby

Try this
--below mentioned 'geo_z' is specified distance between two FRANCHISE'S
select *
from (
select t.*
from FASTFOOD t
inner join FASTFOOD s on t.PrimaryKey=s.PrimaryKey
where ABS(t.geo_x-t.geo_y) > geo_z
) d
where d.FRANCHISE='BURGERKING'

Related

Use of USING in SQL

A restaurant provides wine pairings for most food items on its menu. The structure of two of the tables containing this information is shown below
Join these two tables by their id columns to find the country that the recommended wine is produced in.
Here is the code I have tried:
SELECT country, item
FROM regions
INNER JOIN pairing
regions.id = pairing.id
ORDER BY item
LIMIT 5;
But the compiler gives the solution as:
SELECT country, item
FROM regions
INNER JOIN pairing
USING(id)
ORDER BY item
LIMIT 5;
OUTPUT:
country
item
France
caviar
Italy
curry
Italy
grilled vegetables
Argentina
lamb
Germany
roast duck
Doubt:
I want to clear if there is any difference bwtween USING and equal statement on id or they are same?

SQLite query to get corresponding data

I have some database tables that holds rooms and another table that holds exits of that rooms:
rooms Table:
+---------------+----------------+----------------+
| uid (integer) | name (varchar) | area (varchar) |
+---------------+----------------+----------------+
exits Table:
+------------+---------+-------+
| direction | fromuid | touid |
+------------+---------+-------+
there is 6 possible direction for each room: north, south, west, east, up, down
I want to get all uids from current uid to all directions with 3 depth.
What I mean is: for example for north room's north room and that one's north room and that one's north room also.
|n3| |u3|
|n2| |u2|
|n1| |u1|
|w3|w2|w1| |e1|e2|e3| and
|s1| |d1|
|s2| |d2|
|s3| |d3|
Like this I want to get all rooms uids with keys like n1, n2, n3 etc.
Is that possible with one query?
Also note that some directions are not have 3 rooms. For example east direction can have only 1 room or other directions cant have any room at all
Probably this is what you want:
select rooms.uid as origin,
exits1.direction as direction1,
exits1.touid as room1,
exits2.direction as direction2,
exits2.touid as room2,
exits3.direction as direction3,
exits3.touid as room3
from rooms
left outer join exits exits1
on rooms.uid = exits1.fromuid
left outer join exits exits2
on exits1.touid = exits2.fromuid
left outer join exits exits3
on exits2.touid = exits3.fromuid
This is not filtering on starting direction so if the 2nd north room connects to an up room, you will get that. Or if a room has more than one connection. To filter on the starting direction, use this one:
select rooms.uid as origin,
exits1.direction as direction1,
exits1.touid as room1,
exits2.direction as direction2,
exits2.touid as room2,
exits3.direction as direction3,
exits3.touid as room3
from rooms
left outer join exits exits1
on rooms.uid = exits1.fromuid
left outer join exits exits2
on exits1.touid = exits2.fromuid
and exits1.direction = exits2.direction
left outer join exits exits3
on exits2.touid = exits3.fromuid
and exits2.direction = exits3.direction

SQL JOINing on max value, even if it is 0

I have two tables that look roughly like this:
Airports
uniqueID | Name
0001 | Dallas
Runways
uniqueID | AirportID | Length
000101 | 0001 | 8000
I'm doing a join that looks like this:
SELECT Airports.Name, Runways.Length FROM Airports, Runways
WHERE Airports.uniqueID==Runways.AirportID
Obviously, each runway has exactly one airport, and each airport has 1..n runways.
For an airport with multiple runways, this gives me several rows, one for each runway at that airport.
I want a result set that contains ONLY the row for the longest runway, i.e. MAX(Length).
Sometimes, the Length is 0 for several runways in the database, because the source data is missing. In that case I only want one row with the Length = 0 obviously.
I've tried the approach laid out here: Inner Join table with respect to a maximum value but that's actually not helpful because that's like searching for the longest runway of all, not for the longest at one particular airport.
This seems to simple to be what you want but it seems to meet all the cases you've described...
SELECT A.Name, Max(R.Length)
FROM Airports A
INNER JOIN Runways R
on A.uniqueID=R.AirportID
Group by A.Name
This should give you the max runway for each airport.
If you need additional data elements then use the above as a inline view (Subquery within the joins) to limit the results sets to just those airports and their max runway.

SQL SUM with Repeating Sub Entries - Best Practice?

I hit this issue regularly but here is an example....
I have a Order and Delivery Tables. Each order can have one to many Deliveries.
I need to report totals based on the Order Table but also show deliveries line by line.
I can write the SQL and associated Access Report for this with ease ....
SELECT xxx
FROM
Order
LEFT OUTER JOIN
Delivery on Delivery.OrderNO = Order.OrderNo
until I get to the summing element. I obviously only want to sum each Order once, not the 1-many times there are deliveries for that order.
e.g. The SQL might return the following based on 2 Orders (ignore the banalness of the report, this is very much simplified)
Region OrderNo Value Delivery Date
North 1 £100 12-04-2012
North 1 £100 14-04-2012
North 2 £73 01-05-2012
North 2 £73 03-05-2012
North 2 £73 07-05-2012
South 3 £50 23-04-2012
I would want to report:
Total Sales North - £173
Delivery 12-04-2012
Delivery 14-04-2012
Delivery 01-05-2012
Delivery 03-05-2012
Delivery 07-05-2012
Total Sales South - £50
Delivery 23-04-2012
The bit I'm referring to is the calculation of the £173 and £50 which the first of which obviously shouldn't be £419!
In the past I've used things like MAX (for a given Order) but that seems like a fudge.
Surely there must be a regular answer to this seemingly common problem but I can't find one.
I don't necessarily need the code - just a helpful point in the right direction.
Many thanks,
Chris.
A roll up operator may not look pretty. However, it would do the regular aggregates that you see now, and it show the subtotals of the order. This is what you're looking for.
SELECT xxx
FROM
Order
LEFT OUTER JOIN
Delivery on Delivery.OrderNO = Order.OrderNo
GROUP BY xxx
WITH ROLLUP;
I'm not exactly sure how the rest of your query is set up, but it would look something like this:
Region OrderNo Value Delivery Date
North 1 £100 12-04-2012
North 1 £100 14-04-2012
North 2 £73 01-05-2012
North 2 £73 03-05-2012
North 2 £73 07-05-2012
NULL NULL f419 NULL
I believe what you want is called a windowing function for your aggregate operation. It looks like the following:
SELECT xxx, SUM(Value) OVER (PARTITION BY Order.Region) as OrderTotal
FROM
Order
LEFT OUTER JOIN
Delivery on Delivery.OrderNO = Order.OrderNo
Here's the MSDN article. The PARTITION BY tells the SUM to be done separately for each distinct Order.Region.
Edit: I just noticed that I missed what you said about orders being counted multiple times. One thing you could do is SUM() the values before joining, as a CTE (guessing at your schema a bit):
WITH RegionOrders AS (
SELECT Region, OrderNo, SUM(Value) OVER (PARTITION BY Region) AS RegionTotal
FROM Order
)
SELECT Region, OrderNo, Value, DeliveryDate, RegionTotal
FROM RegionOrders RO
INNER JOIN Delivery D on D.OrderNo = RO.OrderNo

Modelling country adjacency in SQL

I'm trying to model which countries border each other in MySQL. I have three tables:
nodes
-----
node_id MEDIUMINT
countries
---------
country_id MEDIUMINT (used as a foreign key for nodes.node_id)
country CHAR(64)
iso_code CHAR(2)
node_adjacency
--------------
node_id_1 MEDIUMINT (used as a foreign key for nodes.node_id)
node_id_2 MEDIUMINT (used as a foreign key for nodes.node_id)
I appreciate the nodes table is redundant in this example, but this is part of a larger architecture where nodes can represent many other items other than countries.
Here's some data (IDs (which appear in all three tables) and countries)
59 Bosnia and Herzegovina
86 Croatia
130 Hungary
178 Montenegro
227 Serbia
232 Slovenia
Croatia is bordered by all the other countries, and this is represented in the node_adjacency table as:
59 86
86 130
86 178
86 227
86 232
So Serbia's ID may appear as a node_id_1 or a node_id_2. The data in this table is essentially non directed graph data.
Questions:
Given the name 'Croatia', what SQL should I use to retrieve its neighbours?
Bosnia and Herzegovina
Hungary
Montenegro
Serbia
Slovenia
Would there be any retrieval efficiency gains in storing the adjacency information as directed graph data? E.g. Croatia borders Hungary, and Hungary borders Croatia, essentially duplicating storage of the relationships:
86 130
130 86
This is just off the top of my head, so I don't know if it's the most performant solution and it may need a tweak, but I think it should work:
SELECT
BORDER.country
FROM
Countries AS C
LEFT OUTER JOIN Node_Adjacency NA1 ON
NA1.node_id_1 = C.country_id OR
NA1.node_id_2 = C.country_id
INNER JOIN Countries AS BORDER ON
(
BORDER.country_id = NA1.node_id_1 OR
BORDER.country_id = NA1.node_id_2
) AND
BORDER.country_id <> C.country_id
WHERE
C.country = 'CROATIA'
Since your graph is not directed, I don't think that it makes sense to store it as a directed graph. You might also want to Google "Celko SQL Graph" as he has done a lot of advanced work on trees, graphs, and hierarchies in SQL and has an excellent book devoted to the subject.
I would store both relations (Hungary borders Croatia, Croatia borders Hungary) so that you only ever need to query one column.
SELECT c.country FROM countries AS c
INNER JOIN node_adjacency AS n
ON n.node_id_1 = c.countryID
WHERE c.countryID = 86
To do both columns, simply union two queries together (borrowing from Matthew Jones):
SELECT c.country FROM countries AS c
INNER JOIN node_adjacency AS n
ON n.node_id_1 = c.countryID
WHERE c.countryID = 86
UNION
SELECT c.country FROM countries AS c
INNER JOIN node_adjacency AS n
ON n.node_id_2 = c.countryID
WHERE c.countryID = 86
If you do it this way, you duplicate your query instead of your data (use 50% less space), and it's still really simple.
You can create a union view to avoid duplication:
CREATE VIEW adjacency_view (node_id_1, node_id_2)
AS
SELECT node_id_1, node_id_2 FROM node_adjacency
UNION ALL
SELECT node_id_2, node_id_1 FROM node_adjacency
So your query becomes quite straightforward:
SELECT c1.country
FROM adjacency_view
INNER JOIN countries AS c1 on c1.country_id = adjacency_view.node_id_1
INNER JOIN countries AS c2 on c2.country_id = adjacency_view.node_id_2
WHERE c2.country = 'CROATIA'
If you are duplicating relationships (i.e. country A shares border with B, and B shares border with A) you can get a way with a simple select. If you store only one relationship per pair of countries you will need to search by both columns in node_adjacency table (running two select statements and performing a union).