update columns based on query with select - mysql

I have a data base that I imported into a table called ip2city that contains 3 columns (startipint,endipint,country)
that is linking a given IP range (represented in integer) to a specific country
I have another table called Test2 that containing columns source and dest (INT representing IP) and empty columns countryS, countryD
I want to fill the empty columns based on the the data from the table ip2city
I am new to mysql
I tried something that looks like :
UPDATE Test2
SET CountryS = (SELECT Country FROM `ip2city` WHERE startipint <= Test2.`Source`
ORDER BY startipint DESC LIMIT 1);
where select... is suppose to return the value I want to insert to the table
but it dosnt seem to work
when I just use
SELECT * FROM `ip2city` WHERE startipint <= 3232235521 ORDER BY startipint DESC LIMIT 1
with specific ip, I get a good result so how can i use it on an entire table.. ?

Try this:
UPDATE Test2
JOIN ip2city s ON Test2.Source between s.startipint and s.endipint
JOIN ip2city d ON Test2.Dest between d.startipint and d.endipint
SET CountryS = s.country, CountryD = d.country
Note that this is a mysql-only solution, because this query uses mysql's multiple-table update syntax.

$result = mysql_query("SELECT * FROM ip2city WHERE startipint <= 3232235521 ORDER BY startipint DESC LIMIT 1");
while ($row = mysql_fetch_array($result) {
echo $row['columnname'];
Now you can use this in your UPDATE query like this:
UPDATE Test2
SET CountryS = $row['columnname']

Related

Update multiple columns in first row with data from other table without join

I'm moving a few boolean columns from the 1st row of a generic settings table into the 1st row of a website_settings table across a few MYSQL databases. I've created the new columns in my new table with a default false value.
I have a working query to copy data from the old table:
UPDATE website_settings
SET
dark_mode_enabled = (SELECT dark_mode_enabled FROM settings ORDER BY id LIMIT 1),
header_enabled = (SELECT header_enabled FROM settings ORDER BY id LIMIT 1),
footer_enabled = (SELECT footer_enabled FROM settings ORDER BY id LIMIT 1)
LIMIT 1;
However for my own knowledge, I'm curious if there is a more cleaner way to write this, perhaps without the repetitive select queries to the same table?
You could use a join:
UPDATE website_settings ws CROSS JOIN
(SELECT s.*
FROM settings s
ORDER id DESC
LIMIT 1
) s
SET ws.dark_mode_enabled = s.dark_mode_enabled,
ws.header_enabled = s.header_enabled,
ws.footer_enabled = s.footer_enabled
LIMIT 1;
Here's a solution that does not use JOIN:
INSERT INTO website_settings (id, dark_mode_enabled, header_enabled, footer_enabled)
SELECT id, dark_mode_enabled, header_enabled, footer_enabled
FROM settings ORDER BY id LIMIT 1
ON DUPLICATE KEY UPDATE
dark_mode_enabled = VALUES(dark_mode_enabled),
header_enabled = VALUES(header_enabled),
footer_enabled = VALUES(footer_enabled);

Update table with maximum result of function from another table

Suppose the following tables
source(
id INT,
name VARCAHR
)
dest(
id INT,
name VARCAHR
)
Where source table is filled with both id and name; and dest is filled with name only, having id set to null
It is necessary to update dest table and set id which is the id of most similar name (calculated by Levenstein distance) from source table
For better understanding this is how I planned it to be ideally (query has syntax error):
UPDATE dest
SET dest.id = (
SELECT source.id, levenstein_ratio(dest.name, source.name) as similarity
FROM source
GROUP BY similarity HAVING similarity > 50
ORDER BY similarity DESC
LIMIT 1
).id
I couldn't make any reasonable query without repeating function.
Is there a way to do it? Efficiency is not crucial but somewhat reasonable solution would be appreciated
You set dest.id require a single return value from select so you should refactor you query eg: this way
UPDATE dest
SET dest.id = (
SELECT source.id
FROM source
GROUP BY levenstein_ratio(dest.name, source.name)
HAVING levenstein_ratio(dest.name, source.name) > 50
ORDER BY levenstein_ratio(dest.name, source.name) DESC
LIMIT 1
)

MYSQL - ORDER BY & LIMIT: Different result when using variable or constant

Query 1:
set #userName = 'harry';
set #previousRegionId = (
select Region as RegionID
from log
where User = #userName
order by stamp desc
limit 1);
select #previousRegionId;
Query 2:
set #previousRegionId = (
select Region as RegionID
from log
where User = 'harry'
order by stamp desc
limit 1);
select #previousRegionId;
I expect the same result for both queries, but the result is different and it is reproduceable!
The log table contains same value in User column for all rows ('harry').
Changed the order by clause. Order by id works "better". Order by timestamp column is suspect ...
I am not 100% sure if this approach will work in all scenarios/aspects.

Using Limit random on mysql

I just used this query
SELECT * FROM questions
ORDER BY RAND()
LIMIT 20;
On mysql database I have a column called display (along with the columns of questions) where the values are equal to 1.So now as you can see in the query I have limited to 20.I want to change the value of the all the 20 to display=0.I know this is the query
update test3
set display=0
where id=11;
But this just sets the display of one row.How do I do it for the 20.
Thanks in advance.
you can do this
update test3
set display=0
where id in (select id from questions order by rand() limit 20);
Suppose you are using php.
$result = msyql_query("select id from questions order by rand() limit 20");
$ids = array();
while($row = msyql_fetch_assoc($result)){
$ids[] = $row['id'];
}
For your condition, first perform the first query and save the ids from the first query.
Suppose you are using php and you have saved the ids from the first query in $ids;
you can run the second query like this;
$idstring = implode(',',$ids);
$query = 'update test3 set display=0 where id in ('.$idstring.' )';
$result = mysql_query($query);
Use the WHERE clause to search a subquery
UPDATE test3
SET display = 0
WHERE id IN (
SELECT id FROM questions
ORDER BY RAND()
LIMIT 20)
If you want to perform something between the SELECT and the UPDATE try this:
CREATE TABLE #Temp (
division TINYINT
)
INSERT INTO #Temp
SELECT id FROM questions
ORDER BY RAND()
LIMIT 20
--SELECT * FROM #Temp
UPDATE test3
SET display = 0
WHERE id IN (
SELECT division FROM #Temp)

Update with SELECT and group without GROUP BY

I have a table like this (MySQL 5.0.x, MyISAM):
response{id, title, status, ...} (status: 1 new, 3 multi)
I would like to update the status from new (status=1) to multi (status=3) of all the responses if at least 20 have the same title.
I have this one, but it does not work :
UPDATE response SET status = 3 WHERE status = 1 AND title IN (
SELECT title FROM (
SELECT DISTINCT(r.title) FROM response r WHERE EXISTS (
SELECT 1 FROM response spam WHERE spam.title = r.title LIMIT 20, 1)
)
as u)
Please note:
I do the nested select to avoid the famous You can't specify target table 'response' for update in FROM clause
I cannot use GROUP BY for performance reasons. The query cost with a solution using LIMIT is way better (but it is less readable).
EDIT:
It is possible to do SELECT FROM an UPDATE target in MySQL. See solution here
The issue is on the data selected which is totaly wrong.
The only solution I found which works is with a GROUP BY:
UPDATE response SET status = 3
WHERE status = 1 AND title IN (SELECT title
FROM (SELECT title
FROM response
GROUP BY title
HAVING COUNT(1) >= 20)
as derived_response)
Thanks for your help! :)
MySQL doesn't like it when you try to UPDATE and SELECT from the same table in one query. It has to do with locking priorities, etc.
Here's how I would solve this problem:
SELECT CONCAT('UPDATE response SET status = 3 ',
'WHERE status = 1 AND title = ', QUOTE(title), ';') AS sql
FROM response
GROUP BY title
HAVING COUNT(*) >= 20;
This query produces a series of UPDATE statements, with the quoted titles that deserve to be updated embedded. Capture the result and run it as an SQL script.
I understand that GROUP BY in MySQL often incurs a temporary table, and this can be costly. But is that a deal-breaker? How frequently do you need to run this query? Besides, any other solutions are likely to require a temporary table too.
I can think of one way to solve this problem without using GROUP BY:
CREATE TEMPORARY TABLE titlecount (c INTEGER, title VARCHAR(100) PRIMARY KEY);
INSERT INTO titlecount (c, title)
SELECT 1, title FROM response
ON DUPLICATE KEY UPDATE c = c+1;
UPDATE response JOIN titlecount USING (title)
SET response.status = 3
WHERE response.status = 1 AND titlecount.c >= 20;
But this also uses a temporary table, which is why you try to avoid using GROUP BY in the first place.
I would write something straightforward like below
UPDATE `response`, (
SELECT title, count(title) as count from `response`
WHERE status = 1
GROUP BY title
) AS tmp
SET response.status = 3
WHERE status = 1 AND response.title = tmp.title AND count >= 20;
Is using GROUP BY really that slow ? The solution you tried to implement looks like requesting again and again on the same table and should be way slower than using GROUP BY if it worked.
This is a funny peculiarity with MySQL - I can't think of a way to do it in a single statement (GROUP BY or no GROUP BY).
You could select the appropriate response rows into a temporary table first then do the update by selecting from that temp table.
you'll have to use a temporary table:
create temporary table r_update (title varchar(10));
insert r_update
select title
from response
group
by title
having count(*) < 20;
update response r
left outer
join r_update ru
on ru.title = r.title
set status = case when ru.title is null then 3 else 1;