MySQL select to find similar lat/lng with matching name column - mysql

I am trying to find rows in a single table of locations that have the same latitude/longitude when rounded to 2 decimal places as well as the same name. Here is my table (for example):
+---------------------------------------+
| ID | lat | lng | name |
+---------------------------------------+
| 11 | -11.119 | 13.891 | Smith's Place |
| 81 | -11.121 | 13.893 | Smith's Place |
+---------------------------------------+
What SELECT statement would find instances (like the one above) where the lat/lng match when rounded to 2 decimal places...and the names are the same?
I am looking for something similar to this query that obviously doesn't work (but is asking for what I am after):
SELECT * FROM pb_locations GROUP BY ROUND(lat,2),ROUND(lng,2) WHERE name = name HAVING count(ID) > 1

WHERE name = name is always true, since it's just comparing within the same row, not across different rows.
You need to put all 3 columns in the GROUP BY clause.
SELECT *
FROM pb_locations
GROUP BY ROUND(lat, 2), ROUND(lng, 2), name
HAVING COUNT(*) > 1

Related

MySQL: select all rows where just the name is distinct [duplicate]

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 4 years ago.
I'm currently trying to select unique entries in only the name column. I have tried using this query but it will not return prices that are the same as well. I've tried other variations with no success either.
SELECT DISTINCT name, price from table;
Here's the table I'm working with:
+----+-------------------+
| id | name | price |
+----+-----------+-------+
| 1 | Henry | 20 |
| 2 | Henry | 30 |
| 3 | Robert | 20 |
| 4 | Joshua | 10 |
| 5 | Alexander | 30 |
+----+-----------+-------+
The output that I'm seeking is:
+----+-------------------+
| id | name | price |
+----+-----------+-------+
| 1 | Henry | 20 |
| 3 | Robert | 20 |
| 4 | Joshua | 10 |
| 5 | Alexander | 30 |
+----+-----------+-------+
The desired output as you can tell only removed the duplicate name and none of the prices. Is there something I can add to my query above to only select unique entries in the name column? Any help is really appreciated as I have tried to find a solution on here, Google, DuckDuckGo, etc. with no luck.
From your sample data, this should work.
SELECT MIN(Id) AS Id, name, MIN(price) AS price
FROM table
GROUP BY name;
This is what GROUP BY is for:
SELECT * FROM `table` GROUP BY `name`
Usually people run into trouble because they will now get an arbitrarily-chosen row when more than one matches for a given name — you have to use aggregate functions to pick a specific one, e.g. "the one with the maximum price".
But in your case, since you don't seem to care which row is returned, this is perfect as-is.
So you want to select distinct list of rows AND then select that given entire row from the table? Try this query where temporary query is just a list of uniqueid then that row is linked back to the table.
Select n.*
From nameprices n
Join (Select MIN(id) as id
From nameprices
Group by name
Order By id) aTemp On (aTemp.id=n.id);
This is a common problem in SQL queries where we want to use that given fully row data but filter was using a distinct/groupby formula.

SQL operator IN returns only DISTINCT

I have the following query:
SELECT class, subclass ,weight
FROM classes
WHERE classes.term in ('this','paper','present','this','and','this','this')
The above query returns only distinct values. For example I have the following table:
+-----------------------------------+
|class | subclass | term | weight |
+-----------------------------------+
| a | b | this | 3 |
| c | d | paper | 2 |
| e | f | sth | 1 |
+-----------------------------------+
the result I will get is
+-----------------------------------+
|class | subclass | term | weight |
+-----------------------------------+
| a | b | this | 3 |
| c | d | paper | 1 |
+-----------------------------------+
what I actually wanted is the following
+-----------------------------------+
|class | subclass | term | weight |
+-----------------------------------+
| a | b | this | 3 |
| a | b | this | 3 |
| a | b | this | 3 |
| a | b | this | 3 |
| c | d | paper | 2 |
+-----------------------------------+
I there any other way to get all the results without IN "cutting" only distinct values?
The problem is that I cannot change that part: ('this','paper','present','this','and','this','this')
because it is not created by a query. It is a string of words I want to search.
Edit:
- In the original scenario the table contains more than 3000 different words and the actual string is generated by a function I do not have
rights to access and contains 300+ words with many duplicates.
- In the original scenario I want to add the weight of the word every
time it appears in the string
Edit2:
The result I expect is to sum the weights every time a term appears in string.
Expecting results like the following:
+-----------------------------------+
|class | subclass | term | weight |
+-----------------------------------+
| a | b | this | 12 |
| c | d | paper | 2 |
+-----------------------------------+
Is there any other solution?
Use a join:
select c.*
from (select 'this' as term union all
select 'paper' as term union all
select 'present' as term union all
select 'this' as term union all
select 'and' as term union all
select 'this' as term union all
select 'this' as term
) terms left join
classes c
on c.term = terms.term;
This will work in both MySQL and SQLite.
For reference, see this question on how to count the number of occurrences in a substring:
SELECT m.*, (LENGTH('this paper present this and this this') - LENGTH(REPLACE('this paper present this and this this', term, ''))) / LENGTH(term) AS count
FROM myTable;
Once you have the number of occurrences for each string, you can multiply that value by the weight to get the total, like this:
SELECT term, weight * (LENGTH('this paper present this and this this') - LENGTH(REPLACE('this paper present this and this this', term, ''))) / LENGTH(term) AS totalWeight
FROM myTable m;
Note that this solution does not take a separated list of words, but concatenates that list into one string.
Here is an SQL Fiddle example for you.
EDIT
If you want the sum of weights for all terms in the string, without regard to the terms themselves, you can just adjust the query to use the SUM() function, and don't use GROUP BY because you want to sum for the whole table:
SELECT SUM(weight * (LENGTH('this paper present this and this this') - LENGTH(REPLACE('this paper present this and this this', term, ''))) / LENGTH(term)) AS totalWeight
FROM myTable m;
EDIT 2
A little more explanation for the query based on lengths. You can break it up into multiple parts:
LENGTH('this paper present this and this this') returns the number of characters in the string you are searching
LENGTH(REPLACE(myString, term)) is the length of the string above, with your term removed. (So, for example of 'this', it's going to be total length 37, subtracting 16 (4 for each occurrence) which will give you 21.
By subtracting the second value from the first, you'll get the number of characters in the overall string that are as a result of your value (37 - 21 = 16).
Then, it divides it by the length of 'term' to get the number of occurrences. 16 characters, divided by 4 characters in each occurrence means the substring occured 4 times. (16 / 4 = 4). Try these steps again with 'paper' and you will see.
The above procedure is illustrated step by step in this SQL Fiddle.

How to optimize search in list in SQL

I have to make a SQL query in Mysql to search a string list (for ex: 1,2,3) in a columns (for ex: list_id), which also have string value list (1,2,3).
For more detail, my_table is
+-----------+----------+
| id | list_id |
+-----------+----------+
| 1 | 29 |
| 2 | 30 |
| 3 | 31 |
| 4 | 4,5,6,7 |
| 5 | 8,9,10,11|
| 6 | 4,5,8,9 |
| 7 | 1,2,3,6 |
+-----------+----------+
The search value is 1,5,8 and I need get the rows have list_id have 1 or 5 or 8 in it's list. Therefore, the result wil be:
+-----------+----------+
| id | list_id |
+-----------+----------+
| 4 | 4,5,6,7 |
| 5 | 8,9,10,11|
| 6 | 4,5,8,9 |
| 7 | 1,2,3,6 |
+-----------+----------+
My query string is:
SELECT * FROM my_table
WHERE list_id LIKE '%,1,%'
OR list_id LIKE '1,%'
OR list_id LIKE '%,1'
OR list_id LIKE '%,5,%'
OR list_id LIKE '5,%'
OR list_id LIKE '%,5'
OR list_id LIKE '%,8,%'
OR list_id LIKE '8,%'
OR list_id LIKE '%,8'
It is match correct what I want. However, the length of query is in proportion to length of list.
Does REGEXP is better than LIKE in this circumstance?
Does anyone have experience to make another solution better?
You may try to concatenate commas to your field (or use SET in MySQL or make a better database structure - in which you join on tables in which the related data is stored).
SELECT * FROM yourtable WHERE CONCAT(',', fieldname, ',') like '%,1,%';
Yes, regular expressions will work for this. Here is what you can do:
SELECT * FROM junk
WHERE CONCAT(',', list_id, ',') REGEXP CONCAT(',(', REPLACE('1,3,8',',','|'), '),');
Results:
ID | LIST_ID
5 | 8,9,10,11
6 | 4,5,8,9
7 | 1,2,3,6
Please see SQL Fiddle demo here.
We turn the query list 1,3,8 into an alternating group 1|3|8. You might be able to do this in your application code to avoid using the REPLACE() function above.
UPDATE Apologies, I mistakenly used 1,3,8 as the query parameter instead of 1,5,8. But it should still work.
I am going to strongly suggest that you change the design of the database (I am assuming you have some control or influence over it).
You should make the id column non-unique and then the list_id column should contain a single value. You can then search as follows:
SELECT id WHERE list_id IN (1,5,8)
If it is a big table and there are a lot of list_id values, put an index on the list_id column.
If you need the output in a comma-separated list, then you will need to use an aggregating concatenation function with GROUP BY (e.g., GROUP_CONCAT() in MySQL).
If you cannot change the design of the schema, then use one of the other suggestions here.

Mysql where in multiple rows

I have this problem.
One table with.
id | routename | usersid |
1 | route 1 | 1,2,3,5 2 |
2 | route 2 | 5,20,15 3 |
4 | route 4 | 10,15,7,5 |
I need, search ej. userid 5 in colum usersid... but I have no idea how to do, because there are multiple rows.
If you cannot change the schema then you will have to use the REGEXP operator to match on a regular expression. For example
where column REGEXP '(^|,)5(,|$)'
This matches the number 5 either at the beginning or end of the field or surrounded by commas (or any combination thereof), to avoid matching other numbers like 15, 55 or 1234567890.
If the table is large this will perform very slowly as it will require a full table scan
You might be looking for FIND_IN_SET().
select * from Table1
WHERE FIND_IN_SET(5,usersid)
SAMPLE FIDDLE

Combine count rows in MySQL

I've got a table in MySQL that looks roughly like:
value | count
-------------
Fred | 7
FRED | 1
Roger | 3
roger | 1
That is, it was created with string ops outside of MySQL, so the values are case- and trailing-whitespace-sensitive.
I want it to look like:
value | count
-------------
Fred | 8
Roger | 4
That is, managed by MySQL, with value a primary key. It's not important which one (of "Fred" or "FRED") is kept.
I know how to do this in code. I also know how to generate a list of problem values (with a self-join). But I'd like to come up with a SQL update/delete to migrate my table, and I can't think of anything.
If I knew that no pair of records had variants of one value, with the same count (like ("Fred",4) and ("FRED",4)), then I think I can do it with a self-join to copy the counts, and then an update to remove the zeros. But I have no such guarantee.
Is there something simple I'm missing, or is this one of those cases where you just write a short function outside of the database?
Thanks!
As an example of how to obtain the results you are looking for with a SQL query alone:
SELECT UPPER(value) AS name, SUM(count) AS qty FROM table GROUP BY name;
If you make a new table to hold the correct values, you INSERT the above query to populate the new table as so:
INSERT INTO newtable (SELECT UPPER(value) AS name, SUM(count) AS qty FROM table GROUP BY name);
Strangely, MySQL seems to do this for you. I just tested this in MySQL 5.1.47:
create table c (value varchar(10), count int);
insert into c values ('Fred',7), ('FRED',1), ('Roger',3), ('roger',1);
select * from c;
+-------+-------+
| value | count |
+-------+-------+
| Fred | 7 |
| FRED | 1 |
| Roger | 3 |
| roger | 1 |
+-------+-------+
select value, sum(count) from c group by value;
+-------+------------+
| value | sum(count) |
+-------+------------+
| Fred | 8 |
| Roger | 4 |
+-------+------------+
I was surprised to see MySQL transform the strings like that, and I'm not sure I can explain why it did that. I was expecting to have to get four distinct rows, and to have to use some string functions to map the values to a canonical form.