PostGIS distance query between 2 selected points from same table - sql

This is probably a really easy query, but for some reason, I just can't figure it out..
I have a table with cities (cities) and I want to get the distance between London (cities.city_name='London') and Dundee. This is my query:
SELECT
cities.city_name, ST_Distance(cities.the_geom,cities.the_geom)
from cities
WHERE cities.city_name in ('London','Dundee')
The result is in the ST_Distance field of the output is 0 for both cities.
I know that I'm missing something, but I don't know what. I assume I have to set aliases for the 2 cities, but I don't know how.
Can anybody help me out with this?
Thanks a ton in advance!!

You are comparing distance between the same point in your query:
ST_Distance(cities.the_geom,cities.the_geom) --compares the same column in the list of the two cities:
CITY | GEOM | DISTANCE
-----------------------------------
London | GEOM1 | DISTANCE (GEOM1, GEOM1)
Dundee | GEOM2 | DISTANCE(GEOM2, GEOM2)
Hope you can see my point from table above
Maybe something like this:
SELECT ST_Distance(
(SELECT the_geom FROM cities WHERE city_name ='London'),
(SELECT the_geom FROM cities WHERE city_name ='Dundee')
)

Related

When a statement contains an item in a list, show it in a new column

I would appreciate a little help on some script in sql. So I have a list like the one below and a database table -Table1 with statement as a colum name, and I will like to create a column called location, where the script can search in the statement column and once it finds any of the items in the list in any row it states that in the location column
(Tema, london, Sydney, Germany, China, Africa,)
Statement
-------------------
Going to london
Apples in Tema
Sydney is a city
China is a country
Africa is a continent
In the end I hope to see a table like this :
Statement
location
Going to london
London
Apples in Tema
Tema
Sydney is a city
Sydney
china is a country
China
Africa is a continent
Africa
By using this script,
SELECT Statement,
Case
WHEN Statement::text ~~* '%london%'::character varying::text
THEN 'london'::character varying
ELSE NULL::character varying
END AS location
FROM Table1
I think I would have to write a very tall script, but I was wondering if I could get help with something efficient and quite simple to achieve this
If you have a list of places, you can use that:
select t1.*, v.place
from table1 t1 cross join
(values ('tema'), ('london'), ('sydney'), ('germany'), ('china'), ('africa')
) v(place)
on Statement::text ilike '%' || v.place || '%';
Note: You might want to use regular expressions so you can include work boundaries but your example code doesn't do tis.

Get row number of a row that meets an x condition in Postgresql

Hello Stack Overflow community, I wanted to get the row number of the first element that meets the condition specified in the query. I am making the queries using Postgresql and I send them from Python.
Here is an outline of what I want:
+--------+---------------+
|name |country |
|--------|---------------|
|Juan | United States |
|--------|---------------|
|Carlos | France |
|--------|---------------|
|Lucy | UK |
+--------+---------------+
In this case I would like to make a query where I can get the row number of the first person whose country is France (that is, row 2). Thank you. Anything that is not clear you can ask me.
I guess I rushed a little. Sorry.
I already found the solution. The following query worked for me:
SELECT num FROM (SELECT Row_Number() OVER () AS num, country FROM people ORDER BY country) AS tabla WHERE country='France'
with rownum as (
select *,
row_number() over() as rnum
from mytable
)
select min(rnum) as first_instance
from rownum
where country = 'France'
You can play around with this code on db-fiddle

Select longest string for each user

I have a table like this :
Clients Cities
1 NY
1 NY | WDC | LA
1 NY | WDC
2 LA
So, I have duplicate clients with different cities (not in order, but with different length at each line). What I want is to display for each user the longest cities string. So, I should get something like this :
Clients Cities
1 NY | WDC | LA
2 LA
I am a beginner in SQL (I use Spark SQL but it's mainly the same thing), so can you please how can I fix this problem please ??
Thanks !
You can use max():
select client, max(cities)
from t
group by client;
Then you should fix your data model, so you are not storing lists of cities in a string. That is not a good way to store the data in a relational database.
I think you should handle that query (in MYSQL) by using SELECT DISTINCT statement,
As inside a table contains many duplicate values, I hope it will make it work!
For instance,
SELECT DISTINCT city_name FROM cities;
And continue.... this is my hint to lead you to the desired and great answer

SQL (COUNT(*) / locations.area)

We are learning SQL at school, and my professor has this sql code in his documents.
SELECT wp.city, (COUNT(*) / locations.area) AS population_density
FROM world_poulation AS wp
INNER JOIN location
ON wp.city = locations.city
WHERE locations.state = “Hessen”
GROUP BY wp.city, locations.area
Everything is almost clear for me, just the aggregate function with /locations.area doesn't make any sense to me. Can anybody help?
Thank you in advance!
Look at what the query is grouped on, that tells you what each group consists of. In this case, each group is a city, and contains all the rows that have the same value for wp.city (and as the location table is joined on that value too, the locations.area is only included in the grouping so that it can be used in the result).
So each group has a number of rows, and the COUNT(*) aggregate will contain the number of rows for each group. The value of (COUNT(*) / locations.area) will be the number of rows in the group divided by the value of locations.area for that group.
If you would have data like this:
world_population
name city
--------- ---------
John London
Peter London
Sarah London
Malcolm London
Ian Cardiff
Johanna Stockholm
Sven Stockholm
Egil Stockholm
locations
city state area
----------- -------------- ---------
London Hessen 2
Cardiff Somehere else 14
Stockholm Hessen 1
Then you would get a result with two groups (as Cardiff is not in the state Hessen). One group has four people from London which has the area 2, so the population density would be 2. The other group has three people from Stockholm which has the area 1, so the population density would be 3.
Side note: There is a typo in the query, as it joins in the table location but refers to it as locations everywhere else.
Try writing it like:
SELECT wp.city,
locations.area,
COUNT(*) AS population,
(COUNT(*) / locations.area) AS population_density
FROM world_poulation AS wp
INNER JOIN location
ON wp.city = locations.city
WHERE locations.state = “Hessen”
GROUP BY wp.city, locations.area
The key is the GROUP BY statement. You are showing pairs of cities and areas. The COUNT(*) is the number of times a given pair shows up in the table you created by joining world population and location. The area is just a number, so you can divide the area by the COUNT.

How to do an exact match followed by ORDER BY in PostgreSQL

I'm trying to write a query that puts some results (in my case a single result) at the top, and then sorts the rest. I have yet to find a PostgreSQL solution.
Say I have a table called airports like so.
id | code | display_name
----+------+----------------------------
1 | SDF | International
2 | INT | International Airport
3 | TES | Test
4 | APP | Airport Place International
In short, I have a query in a controller method that gets called asynchronously when a user text searches for an airport either by code or display_name. However, when a user types in an input that matches a code exactly (airport code is unique), I want that result to appear first, and all airports that also have int in their display_name to be displayed afterwards in ascending order. If there is no exact match, it should return any wildcard matches sorted by display_name ascending. So if a user types in INT, The row (2, INT, International Airport) should be returned first followed by the others:
Results:
1. INT | International Airport
2. APP | Airport Place International
3. SDF | International
Here's the kind of query I was tinkering with that is slightly simplified to make sense outside the context of my application but same concept nonetheless.
SELECT * FROM airports
WHERE display_name LIKE 'somesearchtext%'
ORDER BY (CASE WHEN a.code = 'somesearchtext` THEN a.code ELSE a.display_name END)
Right now the results if I type INT I'm getting
Results:
1. APP | Airport Place International
2. INT | International Airport
3. SDF | International
My ORDER BY must be incorrect but I can't seem to get it
Any help would be greatly appreciated :)
If you want an exact match on code to return first, then I think this does the trick:
SELECT a.*
FROM airports a
WHERE a.display_name LIKE 'somesearchtext%'
ORDER BY (CASE WHEN a.code = 'somesearchtext' THEN 1 ELSE 2 END),
a.display_name
You could also write this as:
ORDER BY (a.code = 'somesearchtext') DESC, a.display_name
This isn't standard SQL, but it is quite readable.
I think you can achieve your goal by using a UNION.
First get an exact match and then add that result to rest of the data as you which.
e.g.. (you will need to work in this a bit)
SELECT * FROM airports
WHERE code == 'somesearchtext'
ORDER BY display_name
UNION
SELECT * FROM airports
WHERE code != 'somesearchtext' AND display_name LIKE 'somesearchtext%'
ORDER BY display_name