MySQL UPDATE with multiple tables and derived calculation - mysql

I'm in need of assistance with a MySQL UPDATE involving two tables and a calculated value. The biggest issue seems to be getting the derived value in place.
I need to calculate the DENS column based on the number of IDs in Table 2 (Total ID by FIPS / Table.POP)
Table 1
---------------------
| FIPS | POP | DENS |
---------------------
| 0001 | 100 | |
| 0002 | 25 | |
| 0003 | 500 | |
---------------------
Table 2
-------------
| ID | FIPS |
-------------
| 01 | 0001 |
| 02 | 0001 |
| 03 | 0002 |
| 04 | 0003 |
| 05 | 0003 |
| 06 | 0003 |
-------------
I can't figure out the syntax for the UPDATE statement to properly associate the count and subsequent calculation with the FIPS value.
I thought the following might work but it has not:
UPDATE Table1
SET Table1.DENS = (
SELECT (COUNT(DISTINCT Table2.id) / Table1.POP )
FROM Table2
WHERE Table2.FIPS = Table1.FIPS
)
Any help is appreciated!
EDIT: Desired result would look like:
----------------------
| FIPS | POP | DENS |
----------------------
| 0001 | 100 | 0.020 |
| 0002 | 25 | 0.040 |
| 0003 | 500 | 0.006 |
----------------------
The DENS is calulated from Table 2 and Table 1 (using POP: total IDs/POP = DENS) in that there are 2 IDs for FIPS 0001 (2/100 = 0.0200), 1 ID For FIPS 0002 (1/25 = 0.0400), and 3 IDs for FIPS 0003 (3/500 = 0.0060)

Try
UPDATE table1 t1
JOIN (
SELECT FIPS, count( distinct ID ) As DENS
FROM table2
GROUP BY FIPS
) t2
ON t1.FIPS = t2.FIPS
SET t1.DENS = t2.DENS
;

DROP TABLE IF EXISTS table1;
CREATE TABLE table1
(fips INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,pop INT NOT NULL
,dens DECIMAL(5,3) NULL
);
INSERT INTO table1 VALUES
(1,100,NULL),
(2,25,NULL),
(3,500,NULL);
DROP TABLE IF EXISTS table2;
CREATE TABLE table2
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,fips INT NOT NULL
);
INSERT INTO table2 VALUES
(1,1),
(2,1),
(3,2),
(4,3),
(5,3),
(6,3);
UPDATE table1 x
JOIN
( SELECT t1.fips,COUNT(t2.fips)/SUM(DISTINCT t1.pop) n FROM table1 t1 JOIN table2 t2 ON t2.fips = t1.fips GROUP BY t1.fips ) y
ON y.fips = x.fips
SET x.dens = n;
SELECT * FROM table1;
+------+-----+-------+
| fips | pop | dens |
+------+-----+-------+
| 1 | 100 | 0.020 |
| 2 | 25 | 0.040 |
| 3 | 500 | 0.006 |
+------+-----+-------+
...or something like that

Correct answer ended up being combination of both with the addition of the reset of innodb_lock_wait_timeout (set to 600)
SET SESSION innodb_lock_wait_timeout = 600
Final update code was:
UPDATE Table1 x
JOIN (SELECT t1.FIPS, COUNT(DISTINCT t2.ID)/t1.POP n
FROM Table1 t1
JOIN Table2 t2 ON t2.FIPS = t1.FIPS
GROUP BY t1.FIPS) y
ON y.fips = x.fips
SET x.DENS = n
Thanks to both Kordirko and Strawberry for the assist on this!

Related

A join or function in Mysql to include all unique items of one column and all unique items of second column

I need to combine the unique values of one column and the unique values of another column as one new column. As shown in Table example 2
Union to append all DISTINCT return dates
drop table if exists t;
create table t
(cid int, bid int,cdate varchar(6),rdate varchar(6));
insert into t values
(103,23,'15-mar','26-jan'),
(103,23,'14-apr','26-jan'),
(103,23,'18-may','26-jan');
select cid,bid,cdate,rdate,cdate as newdate from t
union all
(select distinct cid,bid,null,null,rdate from t)
;
+------+------+--------+--------+---------+
| cid | bid | cdate | rdate | newdate |
+------+------+--------+--------+---------+
| 103 | 23 | 15-mar | 26-jan | 15-mar |
| 103 | 23 | 14-apr | 26-jan | 14-apr |
| 103 | 23 | 18-may | 26-jan | 18-may |
| 103 | 23 | NULL | NULL | 26-jan |
+------+------+--------+--------+---------+
4 rows in set (0.002 sec)
select column1,column2,count(column1) as c1 ,count(column2) as c2 from MyTable having c1 =1 OR c2 =1

Deleting table entries based on criteria in another table

We want to be able to delete entries from a MySQL table, based on deletion criteria set in another table. Let me explain with an example.
I have two tables defined as follows:
CREATE TABLE base_tbl
(
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
f1 VARCHAR(8),
f2 VARCHAR(8),
PRIMARY KEY (id)
);
CREATE TABLE del_criteria_tbl
(
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
f3 VARCHAR(8),
f4 VARCHAR(8),
PRIMARY KEY (id)
);
base_tbl has the data and del_criteria_tbl has the criteria for deleting entries from base_tbl.
I populate the tables as follows:
INSERT INTO base_tbl(f1, f2) VALUES ('ABC', '123C57'),
('ABC', '532B49'), ('DEF', '397F45'),
('DEF', '684G65'), ('GHI', '793A86'),
('GHI', '541H32');
and
INSERT INTO del_criteria_tbl(f3, f4) VALUES ('ABC', '532B49'),
('ABC', '813E89'), ('DEF', '397F45'),
('GHI', '541H32');
Obviously:
mysql>SELECT * FROM base_tbl;
+----+------+--------+
| id | f1 | f2 |
+----+------+--------+
| 1 | ABC | 123C57 |
| 2 | ABC | 532B49 |
| 3 | DEF | 397F45 |
| 4 | DEF | 684G65 |
| 5 | GHI | 793A86 |
| 6 | GHI | 541H32 |
+----+------+--------+
mysql>SELECT * FROM del_criteria_tbl;
+----+------+--------+
| id | f3 | f4 |
+----+------+--------+
| 1 | ABC | 532B49 |
| 2 | ABC | 813E89 |
| 3 | DEF | 397F45 |
| 4 | GHI | 541H32 |
+----+------+--------+
I would like to define a succinct and efficient SQL operation that executes the following pseudo-SQL logic:
DELETE FROM base_tbl WHERE base_tbl.f1 = del_criteria_tbl.f3 AND base_tbl.f2 = del_criteria_tbl.f4
After the operation is executed, SELECT * FROM base_tbl should yield:
+----+------+--------+
| id | f1 | f2 |
+----+------+--------+
| 1 | ABC | 123C57 |
| 4 | DEF | 684G65 |
| 5 | GHI | 793A86 |
+----+------+--------+
A simple method is IN:
DELETE b FROM base_tbl b
WHERE (b.f1, b.f2) IN (SELECT dc.f3, dc.f4
FROM del_criteria_tbl dc
);
With indexes on (f1, f2), you might find a JOIN has better performance:
DELETE b
FROM base_tbl b JOIN
del_criteria_tbl dc
ON b.f1 = dc.f3 AND b.f2 = c.f4;
I would recommend exists:
delete b
from base_tbl b
where exists (
select 1
from del_criteria_tbl dc
where dc.f1 = b.f1 and dc.f2 = b.f2
)
This seems like the most natural way to phrase what you ask for. exists usually scales better than in over large datasets. For performance, you want an index on del_criteria_tbl(f1, f2).

Multiple times values are coming in mysql

I have two tables
tbl1 and tbl2
tbl1 table contains 5 columns name id(pk), email , address ,pid(INDEX),status(ENUM Y,N)
tbl2 table contains 3 columns id(pk) ,pid(INDEX),domain
When i am running this query
SELECT *
FROM tbl1 as l
LEFT JOIN tbl2 as m on l.pid=m.pid
WHERE l.status='Y';
It is giving multiple records . Please note we are making join in pid both pid are not primary key. Please help to get only unique values from both table.
enter image description here
You seem to want to join on the basis of relative position in the tables.A way to do this is row_number simulation using variables.
drop table if exists t1,t2;
create table t1(id int, email varchar(5),address varchar(10),pid int,status varchar(1));
create table t2(id int, pid int, domain varchar(5));
insert into t1 values (1,'aa#aa', 'aaaaa',428,'Y'), (2,'bb#bb', 'bbbbb',428,'n'),(3,'cc#cc', 'ccccc',428,'Y') ;
insert into t2 values (1,428,'mmm'),(2,428,'zzz');
select t1.*,t2.*
from
(
select t1.*,
if(t1.pid <> #pid1, #bn1:=#bn1+1,#bn1:=#bn1) BlockNo1,
if(t1.id <> #id1, #rn1:=#rn1+1, #rn1:=1) rowno1,
#pid1:=t1.pid pid1,
#id1:=t1.id p1
from t1
cross join (select #bn1:=0,#rn1:=0, #pid1:=0 ,#id1:=0) r
where status = 'y'
order by t1.pid,t1.id
) t1
join
(
select t2.id t2id,t2.pid t2pid, t2.domain t2domain,
if(t2.pid <> #pid2, #bn2:=#bn2+1,#bn2:=#bn2) BlockNo2,
if(t2.id <> #id2, #rn2:=#rn2+1, #rn2:=1) rowno2,
#pid2:=t2.pid pid2,
#id2:=t2.id p2
from t2
cross join (select #bn2:=0,#rn2:=0, #pid2:=0 ,#id2:=0) r
order by t2.pid,t2.id
) t2 on (t1.blockno1 = t2.blockno2) and (t1.rowno1 = t2.rowno2)
+------+-------+---------+------+--------+----------+--------+------+------+------+-------+----------+----------+--------+------+------+
| id | email | address | pid | status | BlockNo1 | rowno1 | pid1 | p1 | t2id | t2pid | t2domain | BlockNo2 | rowno2 | pid2 | p2 |
+------+-------+---------+------+--------+----------+--------+------+------+------+-------+----------+----------+--------+------+------+
| 1 | aa#aa | aaaaa | 428 | Y | 1 | 1 | 428 | 1 | 1 | 428 | mmm | 1 | 1 | 428 | 1 |
| 3 | cc#cc | ccccc | 428 | Y | 1 | 2 | 428 | 3 | 2 | 428 | zzz | 1 | 2 | 428 | 2 |
+------+-------+---------+------+--------+----------+--------+------+------+------+-------+----------+----------+--------+------+------+
2 rows in set (0.04 sec)

MySQL Inner Join changes the order of records

I have a table Table1 which has 5 columns like this
| ID | Name | V1 | V2 | V3 |
| 1 | A | 103 | 507 | 603 |
| 2 | B | 514 | 415 | 117 |
and another table Table2 which has values like this
| Values | Rooms |
| 103 | ABC |
| 507 | DEF |
| 603 | GHI |
| 514 | JKL |
| 415 | MNO |
| 117 | PQR |
I am running a join query to get rooms from Table2 joined by Table1 as
SELECT t2.values, t2.rooms, t1.Name FROM Table2 t2
INNER JOIN Table1 t1 ON t1.V1 = t2.Values
OR t1.V2 = t2.Values
OR t1.V3 = t2.Values;
this query gets the result but in ascending order of t2.values. I do not want to change any order. I just want to get result in whatever the Table1 has values.
| Values | Rooms | Names |
| 103 | ABC | A |
| 117 | PQR | B |
| 415 | MNO | B |
| 507 | DEF | A |
| 514 | JKL | B |
| 603 | GHI | A |
The above result is ordered according to T2.Values and these values come form t1.V1, t1.V2, T1.V3. I do not want the order result. I want the result to be according the t1.V1, t1.V2, T1.V3 values. If we see at Table1 the values would be 103, 507, 603, 514, 415, 117 and therefore the result should be
| Values | Rooms | Names |
| 103 | ABC | A |
| 507 | DEF | A |
| 603 | GHI | A |
| 415 | MNO | B |
| 514 | JKL | B |
| 117 | PQR | B |
I hope I made my explaination somehow better. Please If it still doesnt clear let me allow to edit it more.
As paxdiablo suggested, I tried adding ORDER BY t1.name but that is not sorting and result is same. Why?
I just want to get result in whatever the Table1 has values.
This is where you've made your mistake. Table1, at least as far as SQL is concerned, doesn't have an order. Tables are unordered sets to which you impose order when extracting the data (if you wish).
SQL select statements make absolutely no guarantee on the order in which results are returned, unless you specifically use order by or group by. Even select * from table1 can return the rows in whatever order the DBMS sees fit to give them to you.
If you want a specific ordering, you need to ask for it explicitly. For example, if you want them ordered by the room name, whack an order by t1.name at the end of your query. Though I'd probably go the whole hog and use a secondary sort order as well, with order by t1.name, t2.rooms.
Or, to sort on the values, add order by t2.values.
For example, punching this schema/data into SQLFiddle:
create table table1(
id integer,
name varchar(10),
v1 integer,
v2 integer,
v3 integer);
insert into table1 (id,name,v1,v2,v3) values (1,'a',103,507,603);
insert into table1 (id,name,v1,v2,v3) values (2,'b',514,415,117);
create table table2 (
val integer,
room varchar(10));
insert into table2(val,room) values (103,'abc');
insert into table2(val,room) values (507,'def');
insert into table2(val,room) values (603,'ghi');
insert into table2(val,room) values (514,'jkl');
insert into table2(val,room) values (415,'mno');
insert into table2(val,room) values (117,'pqr');
and then executing:
select t2.val, t2.room, t1.name from table2 t2
inner join table1 t1 on t1.v1 = t2.val
or t1.v2 = t2.val
or t1.v3 = t2.val
gives us an arbitrary ordering (it may look likes it's ordering by rooms within name but that's not guaranteed):
| val | room | name |
|-----|------|------|
| 103 | abc | a |
| 507 | def | a |
| 603 | ghi | a |
| 514 | jkl | b |
| 415 | mno | b |
| 117 | pqr | b |
When we change that to sort on two descending keys order by t1.name desc, t2.room desc, we can see it re-orders based on that:
| val | room | name |
|-----|------|------|
| 117 | pqr | b |
| 415 | mno | b |
| 514 | jkl | b |
| 603 | ghi | a |
| 507 | def | a |
| 103 | abc | a |
And, finally, changing the ordering clause to order by t2.val asc, we get it in value order:
| val | room | name |
|-----|------|------|
| 103 | abc | a |
| 117 | pqr | b |
| 415 | mno | b |
| 507 | def | a |
| 514 | jkl | b |
| 603 | ghi | a |
Finally, if your intent is to order it by the order of columns in each row of table1 (so the order is left to right v1, v2, v3, you can introduce an artificial sort key, either by using a case statement to select based on which column matched, or by running multiple queries which may be more efficient since:
you're not executing per-row functions, which tend not to scale very well; and
in larger DBMS', they can be parallelised.
The multiple query option would go something like:
select 1 as minor, t2.val as val, t2.room as room, t1.name as name from table2 t2
inner join table1 t1 on t1.v1 = t2.val
union all select 2 as minor, t2.val as val, t2.room as room, t1.name as name from table2 t2
inner join table1 t1 on t1.v2 = t2.val
union all select 3 as minor, t2.val as val, t2.room as room, t1.name as name from table2 t2
inner join table1 t1 on t1.v3 = t2.val
order by name, minor
and generates:
| minor | val | room | name |
|-------|-----|------|------|
| 1 | 103 | abc | a |
| 2 | 507 | def | a |
| 3 | 603 | ghi | a |
| 1 | 514 | jkl | b |
| 2 | 415 | mno | b |
| 3 | 117 | pqr | b |
You can see there that it uses name as the primary key and the position of the value in the row as the minor key.
Now some people may think it an ugly approach to introduce a fake column for sorting but it's a tried and tested method for increasing performance. However, you shouldn't trust me (or anyone) on that. My primary mantra for optimisation is measure, don't guess.
I know you've already accepted an answer, but it looks to me like you want them sorted by the order of ID in table1, and then order of the column (v1, v2, v3) that you've matched on. In which case, something like this should work:
SELECT t2.`values`, t2.rooms, t1.Name FROM Table2 t2
INNER JOIN Table1 t1 ON t1.V1 = t2.`values`
OR t1.V2 = t2.`values`
OR t1.V3 = t2.`values`
ORDER BY
t1.id,
CASE
WHEN t1.v1 = t2.`values` THEN 1
WHEN t1.v2 = t2.`values` THEN 2
WHEN t1.V3 = t2.`values` THEN 3
END
(Note I'm quoting values because it's a keyword in SQL...)
What I'm doing here is:
First, I'm ordering by t1.id, which gets you the rough sort order based on the rows in the t1 tables.
Then I'm adding a secondary sort based on which Values column was matched in the result row, using a CASE statement. For each row of your query results, if the result was produced by a match between t1.v1 and t2.values, then the CASE statement evaluates to 1. If the result was because of a match between t1.v2 and t2.values, then we get 2. If the result was because of a match between t1.v3 and t2.values, then we get 3.
So the overall sort order is based first on the order of the rows in t1, and then within that on the order of which column got matched between t1 and t2 for each row in your results, which seems to be the requirement (though it's hard to put into words!)
well the query is sorting the table using Values in ascending order - like "103 < 117 < 415 and so on..." but you want them to take the order in which they are sorted in actual table i.e. "103 than 507 than 603 and so on" which is sorted the way they have been inserted and you just want to retain this order of sorting.. one possible way you can achieve that is using an extra timestamp field in second table that can track the time insertion is done and thus you can use that timestamp like " ORDER BY timestamp " in your query..

Merging Corresponding MySQL Records

I have a MySQL table called "objecttable" that has the following structure and data in it. (The data is just a sequence, there is a whole lot more).
ID | Name | posX | posY | posZ |rotX | rotY | rotZ | rotW |
3562 | LODpmedhos1_LAe | 2062 | -1703 | 16 | 0 | 45 | 22 | 1 |
3559 | LODpmedhos5_LAe | 2021 | -1717 | 15 | 0 | 45 | 34 | 1 |
3561 | LODpmedhos3_LAe | 2021 | -1717 | 15 | 0 | 45 | 34 | 1 |
I want to figure out which records have the same posX, posY, posZ, rotX, rotY and rotZ values and insert them into a table called "matchtable", and in the end I want it to look like this (I have the table structure ready)
ID1 | Name | ID2 | Name |
3559 | LODpmedhos5_LAe | 3561 | LODpmedhos3_LAe|
I'd appreciate if someone could give me the correct SQL query for it. I don't have more than two matching coordinates and not all coordinates match.
Sorry if the table representations suck, I'll try to make a HTML table if necessary.
Thanks!
This query will do the trick, but the number of results might be a LOT more than required. For example, if there are 5 rows satisfying your query, then the results will be 20( = n*(n-1) ) in number.
SELECT ot.ID AS ID1, ot.Name AS Name1, ot2.ID AS ID2, ot2.Name AS Name
FROM objecttable ot
JOIN objecttable ot2
ON ot.ID > ot2.ID
AND ot.posX = ot2.posX
AND ot.posY = ot2.posY
AND ot.posZ = ot2.posZ
AND ot.rotX = ot2.rotX
AND ot.rotY = ot2.rotY
AND ot.rotZ = ot2.rotZ
EDIT
In reply to lserni's comment:
ON ot.ID <> ot2.ID
The above condition is there to remove the result like:
ID1 | Name | ID2 | Name |
3559 | LODpmedhos5_LAe | 3559 | LODpmedhos5_LAe|
try this:
-- insert into matchtable -- uncomment to insert the data
select alias1.Id,
alias1.Name,
alias2.Id
alias2.Name
from objecttable as alias1
join objecttable as alias2
on alias1.posx = alias2.posx
and alias1.posy = alias2.posy
and alias1.posz = alias2.posz
and alias1.roty = alias2.roty
and alias1.roty = alias2.roty
and alias1.rotz = alias2.rotz
and alias1.Id > alias2.Id