Find the nearest point from a given point in MySQL - mysql

I have a set of points from which I created a multipoint.
SET #multi_point = ST_GeomFromText('MULTIPOINT(-118.2845938 34.0252385, -118.2867610 34.0221188, -118.2905912 34.0227248, -118.284119 34.021846, -118.2864676 34.0186438, -118.2886342 34.0203211, -118.2907290 34.0193680, -118.2831326 34.0192874, -118.2828242 34.0205473)');
I also have a single other point
SET #home = ST_GeomFromText('POINT(-118.2819136 34.0261177)')
From #home I want to find the nearest 3 points. that are a part of the multi-point #multi_point. I thought of first determining the distance from this point #home to every point in the multipoint using ST_DISTANCE. But I get the following:
SELECT ST_DISTANCE(#home, #multi_point)
+----------------------------------+
| ST_DISTANCE(#home, #multi_point) |
+----------------------------------+
| 0.0028207205958764945 |
+----------------------------------+
It looks like I only get the shortest distance of the point to the multipoint. Questions:
Is there a way to get the shortest distance to every point, and determine the points themselves?
Is there a better way to determine the nearest neighbors from one point #home to the rest in #multi_point?
EDIT I tried Paul Spiegel's of storing these points in a table. But when I store a Point type, it is stored as a garbage value:
+-----------------+---------------------------+
| Name | Coordinates |
+-----------------+---------------------------+
| P1 | [2]pxA# |
| p2 | +1Z]:PA# |
| p3 | a]KA# |
| p4 | X ]qgpA# |
| p5 | EW3|U]A# |
| p6 | M]bKzA# |
| p7 | Ku/]2ƇA# |
| p9 | =];A# |
| p8 | u`x]A# |
+-----------------+---------------------------+
Also, When I query it, the distances are null.
SELECT P.Name, ST_DISTANCE(#home, P.Coordinates) AS dist
FROM Placemark P ORDER BY dist LIMIT 3;
+--------------+------+
| Name | dist |
+--------------+------+
| p1 | NULL |
| p2 | NULL |
| p3 | NULL |
+--------------+------+
How do I fix this?

Here is an example using a temporary table:
drop temporary table if exists Placemark;
create temporary table Placemark(
Name varchar(50),
Coordinates point
);
insert into Placemark(Name, Coordinates) values
('p1', POINT(-118.2845938, 34.0252385)),
('p2', POINT(-118.2867610, 34.0221188)),
('p3', POINT(-118.2905912, 34.0227248)),
('p4', POINT(-118.284119 , 34.021846 )),
('p5', POINT(-118.2864676, 34.0186438)),
('p6', POINT(-118.2886342, 34.0203211)),
('p7', POINT(-118.2907290, 34.0193680)),
('p8', POINT(-118.2831326, 34.0192874)),
('p9', POINT(-118.2828242, 34.0205473));
SET #home = ST_GeomFromText('POINT(-118.2819136 34.0261177)');
SELECT
P.Name,
ST_AsText(P.Coordinates) as Coordinates,
ST_DISTANCE(#home, P.Coordinates) AS dist
FROM Placemark P
ORDER BY dist
LIMIT 3
The result:
Name | Coordinates | dist
p1 | POINT(-118.2845938 34.0252385) | 0.00282072059587649
p4 | POINT(-118.284119 34.021846) | 0.00480741199088132
p9 | POINT(-118.2828242 34.0205473) | 0.00564433773972052
Demo: http://rextester.com/GMPK57301
To define a POINT use either POINT(-118.2845938, 34.0252385) or ST_GeomFromText('POINT(-118.2845938 34.0252385)'). To see the value in a readable form use ST_AsText().

Related

Selecting IDs linked with CPC codes in the same column

I am using the PATSTAT database to select the APPLN_ID of patent applications that have a cpc classification symbol but not another. I need to do this in order to retrieve a control dataset of patents to verify my hypothesis.
PATSTAT is a relational database where each patent application has a set of attributes. The TLS224 table contains multiple rows with the same APPLN_ID and different CPC symbols. I want to retrieve the APPLN_IDs that have a set of symbols A but that do not have a set of symbols B.
From this example data
| APPLN_ID | CPC_CLASS_SYMBOL |
| 2345 | C07K 16/26 |
| 2345 | C07K2317/34 |
| 2345 | C07K2317/76 |
| 2345 | G01N 33/74 |
| 2345 | B01L 9/527 |
| 1000 | C07K2317/34 |
| 1000 | C07K 16/26 |
| 1000 | C07K2317/76 |
| 1000 | B01L 3/5025 |
| 9999 | B01L 3/5025 |
| 9999 | G01N2333/47 |
| 9999 | G01N2333/4727 |
I want to obtain this as a result.
| APPLN_ID |
| 1000 |
Here, the set of values A that must be included are 'C07K 16/26' ,'C07K2317/34', 'C07K2317/76', while the value B that must NOT be present is G01N 33/74.
How can I do that?
This is what I came out with so far (I know that the WHERE IN and NOT IN clauses nullify each other, but it is just to show an example).
SELECT DISTINCT p2.APPLN_ID
FROM (SELECT p1.APPLN_ID, p1.PUBLN_AUTH, YEAR(p1.PUBLN_DATE)
FROM TLS211_PAT_PUBLN p1
WHERE YEAR(p1.PUBLN_DATE) = 2008
AND PUBLN_AUTH = 'WO') p2
JOIN (SELECT DISTINCT cpc3.APPLN_ID
FROM TLS224_APPLN_CPC cpc3
WHERE cpc3.APPLN_ID IN
(SELECT APPLN_ID
FROM TLS224_APPLN_CPC
WHERE CPC_CLASS_SYMBOL NOT IN ('G01N 33/74'))
AND cpc3.APPLN_ID IN
(SELECT APPLN_ID
FROM TLS224_APPLN_CPC
WHERE CPC_CLASS_SYMBOL IN ('C07K 16/26', 'C07K2317/34', 'C07K2317/76'))
) cpc1
ON cpc1.APPLN_ID = p2.APPLN_ID
I am still a newbie to SQL so any help is appreciated!
Thank you
your IN and NOT IN doesn't make sense.
if CPC_CLASS_SYMBOL are in the first Group they are automatocally NOT IN your second
Your WHERE clause would only give you APPLN_ID (and some more) the have these symbols and everything else is excluded.

MySql: How to add a column from one table to that of another

I have a table of locations that are verified, like so:
+--------+----------+----------+
| idW | lat | lon |
+--------+----------+----------+
| 111650 | 47.20000 | 14.75000 |
| 111810 | 47.96412 | 16.25498 |
| 111820 | 47.83234 | 16.23143 |
+--------+----------+----------+
I also have a table of "all locations", whether verified or not. It looks like this (with lots of other columns I'm leaving out)
+--------+--------+----------+----------+
| id | idW | lat | lon |
+--------+--------+----------+----------+
| 100000 | 111650 | 47.20000 | 14.75000 |
| 100001 | 111712 | 42.96412 | 19.25498 |
| 100002 | 111820 | 47.83234 | 16.23143 |
+--------+--------+----------+----------+
What I would like to do is, for each verified location, find its "id" in the table of "all locations", and attach those as a new first column on the verified table (remembering that not all verified locations exist in all locations, so it's not as easy as copy and paste I don't think). Any ideas?
edit: The expected output from my example above would be
+--------+--------+----------+----------+
| id | idW | lat | lon |
+--------+--------+----------+----------+
| 100000 | 111650 | 47.20000 | 14.75000 |
| 100002 | 111820 | 47.83234 | 16.23143 |
| x | 111810 | 47.96412 | 16.25498 |
+--------+--------+----------+----------+
where x would be whatever value that 111810 had as its id in the all locations table.
The better option would be to only display the additional data when you query the database using in joins either in a normal query or in a view.
select t1.*, t2.field1, t2.field2 from t1 inner join t2 on t1.idW=t2.idW
You can copy the data over to your 1st table (there are valid reasons to do it, eg. optimalization of selects, but itás a rare case). You need to add the extra columns to your first table using alter table add column ... commands (or just use an sql editor app).
Then to copy the data over:
update t1, t2 set t1.fieldname=t2.fieldname where t1.idW=t2.idW
Since adding columns to a table is not really efficient, you may choose to create a 3rd table from the existing ones and copy the data over:
create table t3 as select t1.*, t2.fieldname1, t2.fieldname2
from t1 inner join t2 on t1.idW=t2.idW
If I understand correctly you want to add a new column to your original table. This can be done as
ALTER TABLE locations ADD COLUMN `id` INTEGER NULL DEFAULT NULL FIRST;
and afterwards you can populate it by getting the values from the verified locations table as
SET SQL_SAFE_UPDATES = 0;
UPDATE locations a SET id =
(SELECT id FROM verified_locations b
WHERE a.idW = b.idW AND a.lat = b.lat AND a.`long` = B.`long`
LIMIT 1);

MySQL query to JOIN tables based on column values

I've got two tables. One with part numbers, hardware names, and type and other with the locations of the hardware that also has locations of specific bins that contain the hardware. The bins don't have a specific number but have unique names. The second table also has the location of the hardware and bin which may change over time. And I'm trying to create a MySQL query that will combine the data in a new table that will be outputted as a comma separated file.
Table 1 Contents
Part Number | Name | Type
------------+---------------+---------------
0 | None | Not Applicable
25 | name1 | type1
150 | name2 | type2
Table 2 Contents
Date | Bin | Part Number | Event | To Location | From Location
---------+------+--------------+----------+-------------+---------------
1/1/2013 | bin1 | 0 | arrive | location1 | none
1/2/2013 | none | 25 | arrive | location2 | none
1/2/2013 | none | 150 | relocate | location3 | location2
The final output of the query should look something like:
Date | Bin | Part Number | Part Name | Type | Event | To Location | From Location
---------+------+-------------+-----------+----------------+----------+-------------+--------------
1/1/2013 | bin1 | 0 | None | Not Applicable | arrive | location1 | none
1/2/2013 | none | 25 | name1 | type1 | arrive | location2 | none
1/2/2013 | none | 150 | name2 | type2 | relocate | location2 | location2
Try this:
SELECT
*
FROM
`Table1`
INNER JOIN `Table2` ON (`Table1`.`Part Number`=`Table2`.`Part Number`)
To make the query better, you would want to define all the columns that you wanted returned instead of *
may be this help full
select Name, Type, t2.Date, t2.Bin, t2.Part Number, t2.Event, t2.To Location, t2.From Location
FROM table1
JOIN table2 as t2 ON (table1.Part Number=t2.Part Number);
concept
SELECT column_name(s)
FROM table1
JOIN table2
ON table1.column_name=table2.column_name;

MySql complex query - SUM on multiple and variable columns

I have the following table structure (simplified version)
+----------------+ +-----------------+ +------+
| fee_definition | | user_fee | | user |
+----------------+ +-----------------+ +------+
| id | | user_id | | id |
| label | | fee_id | | ... |
| case1 | | case | +------+
| case2 | | manual_override |
| case3 | +-----------------+
| case4 |
| case5 |
+----------------+
Base on a pretty simple algorithm id determine which case fits the user to determine the amount of money they have to pay. A user_fee can be base on 1 to no limit number of fees definitions. which mean i can have the following content in the intersection table
+-----------+----------+--------+-------------------+
| user_id | fee_id | case | manual_override |
+-----------+----------+--------+-------------------+
| 1 | 1 | case1 | |
| 1 | 3 | case1 | |
| 1 | 5 | case1 | 50.22 |
| 2 | 1 | case5 | |
| 3 | 1 | case2 | |
| 3 | 2 | case2 | 18.50 |
+-----------+----------+--------+-------------------+
If a user is setted to have the case 1, all the fees listed under the case 1 where the value is different from 0 get picked. Same goes for the four other cases.
Just for reference on how i did things here is the actual query that I execute which is written in french (sorry for that but since we are a team of french speaking developpers, we mostly write in our code and queries in french).:
SELECT
`etudiant_etu`.*,
`session_etudiant_set`.*,
SUM(ROUND(frais_session_etudiant.fse_frais_manuel*100)/100) AS `fse_frais_manuel`,
`frais_session_etudiant`.`des_colonne`,
SUM(ROUND(definition_frais_des.des_quebecCanada*100)/100) AS `des_quebecCanada`,
SUM(ROUND(definition_frais_des.des_etranger*100)/100) AS `des_etranger`,
SUM(ROUND(definition_frais_des.des_non_credite*100)/100) AS `des_non_credite`,
SUM(ROUND(definition_frais_des.des_visiteur*100)/100) AS `des_visiteur`,
SUM(ROUND(definition_frais_des.des_explore*100)/100) AS `des_explore`,
`type_etudiant_tye`.*,
`type_formation_tyf`.*,
`pays_pys`.*,
`province_prc`.*
FROM `etudiant_etu`
INNER JOIN `session_etudiant_set`
ON session_etudiant_set.etu_id = etudiant_etu.etu_id
INNER JOIN `frais_session_etudiant`
ON frais_session_etudiant.set_id = session_etudiant_set.set_id
INNER JOIN `definition_frais_des`
ON definition_frais_des.des_id = frais_session_etudiant.des_id
LEFT JOIN `type_etudiant_tye`
ON type_etudiant_tye.tye_id = session_etudiant_set.tye_id
LEFT JOIN `type_formation_tyf`
ON type_formation_tyf.tyf_id = session_etudiant_set.tyf_id
LEFT JOIN `pays_pys`
ON pays_pys.pys_code = etudiant_etu.pys_adresse_permanente_code
LEFT JOIN `province_prc`
ON province_prc.prc_code = etudiant_etu.prc_adresse_permanente_code
WHERE (set_session = 'P11')
GROUP BY `session_etudiant_set`.`set_id`
ORDER BY `etu_nom` asc, `etu_prenom` ASC
as for reference from the actual query with the simplified version:
simplified version actual version
fee_definition.id definition_frais_des.des_id
fee_definition.case1 definition_frais_des.des_quebecCanada
fee_definition.case2 definition_frais_des.des_etranger
fee_definition.case3 definition_frais_des.des_non_credite
fee_definition.case4 definition_frais_des.des_visiteur
fee_definition.case5 definition_frais_des.des_explore
user_fee.user_id frais_session_etudiant.set_id
user_fee.fee_id frais_session_etudiant.des_id
user_fee.case frais_session_etudiant.des_colonne
user_fee.manual_override frais_session_etudiant.fes_frais_manuel
user.id session_etudiant_set.set_id
The problem I have is when it comes to handling the manual override setting. What would be the best way of doing this?
I would rather this to be handled in the query itself than in the programmation.
the logic behind what I am looking for goes as follow
get the SUM of the fees to be charged for a user and if an override value as been set, use that value instead of the actual value setted in the fee_definition, else use the value in the fee_definition.
I don't mind to loose the 4 not used cases and only keep the right column
Edited to display final result
This is the query I ended with, five levels of IF's
'IF(`frais_session_etudiant`.des_colonne= "des_quebec_canada",
SUM(IF(`frais_session_etudiant`.fse_frais_manuel > 0,
ROUND(`frais_session_etudiant`.fse_frais_manuel*100)/100,
ROUND(definition_frais_des.des_quebec_canada*100)/100)
),
IF(`frais_session_etudiant`.des_colonne= "des_etranger",
SUM(IF(`frais_session_etudiant`.fse_frais_manuel > 0,
ROUND(`frais_session_etudiant`.fse_frais_manuel*100)/100,
ROUND(definition_frais_des.des_etranger*100)/100)
),
IF(`frais_session_etudiant`.des_colonne= "des_non_credite",
SUM(IF(`frais_session_etudiant`.fse_frais_manuel > 0,
ROUND(`frais_session_etudiant`.fse_frais_manuel*100)/100,
ROUND(definition_frais_des.des_non_credite*100)/100)
),
IF(`frais_session_etudiant`.des_colonne= "des_visiteur",
SUM(IF(`frais_session_etudiant`.fse_frais_manuel > 0,
ROUND(`frais_session_etudiant`.fse_frais_manuel*100)/100,
ROUND(definition_frais_des.des_visiteur*100)/100)
),
IF(`frais_session_etudiant`.des_colonne= "des_explore",
SUM(IF(`frais_session_etudiant`.fse_frais_manuel > 0,
ROUND(`frais_session_etudiant`.fse_frais_manuel*100)/100,
ROUND(definition_frais_des.des_explore*100)/100)
),
0
)
)
)
)
) as frais'
That's a monster! as said by Ted Hopp :D
You can use IFNULL(manual_override,non-override-value)

Order by in mysql using second table

I have two tables, one is a list os stores and attributes, the second is a list of allocationsa based on these attributes.
The attribute table (stores_metadata)
| key | store_key | field | value
| 1 | 1 | size | Large
| 2 | 1 | dist | Midlands
| 3 | 2 | size | Medium
| 4 | 3 | dist | South
The allocation table (allocation)
| key | ticket_key | field | value | count
| 1 | 1 | size | Large | 10
| 2 | 1 | size | Medium| 5
I've managed to get the allocations working using the code:
SELECT store_key, quantity FROM
allocation
INNER JOIN store_metadata
ON allocation.`field` = store_metadata.`field`
AND allocation.`value` = store_metadata.`value`
This returns a list of the stores and how many items they should recieve, what I now need to do it order the stores by the distribution attribute.
Any help would be greatly appreciated.
The question isn't asked very well.
To perform ordering by any column in your result set add ORDER BY [column] to the end of the query. E.g.
SELECT store_key, quantity FROM
allocation
INNER JOIN store_metadata
ON allocation.`field` = store_metadata.`field`
AND allocation.`value` = store_metadata.`value`
ORDER BY allocation.`field`;