Complex update query - mysql

Table 1
|id|name|address|car_id |
|1 |Alex|US |NULL |
|2 |Jaso|Canada |1 |
Table 2
|car_id|color|
|1 |red |
|2 |blue |
I am trying to update Alex's car_id with the car_id of the blue car in the second table. Is it possible to do it in one query?
I tried writing the query below, but couldn't make it work. Any ideas?
UPDATE table1
SET table1.car_id = table2.car_id
FROM table1
JOIN table2
ON table2.color = "blue"
WHERE table1.name = "Alex"
Desired result:
|id|name|address|car_id |
|1 |Alex|US |2 |
|2 |Jaso|Canada |1 |

I suspect it does not work in one query. [Edit: Wrong, see other answer]
You have 2 different requirements for 2 different tables which do not have a common column.
You need to find the blue car_id independently from Alexes row as they are not linked in any way by now.
I would do it as follows:
UPDATE
table1
SET
table.car_id = (
SELECT
car_id
FROM
table2
WHERE
color LIKE 'blue'
)
WHERE
name LIKE 'Alex'

update firstTable join secondTable on secondTable.color = 'blue' set firstTable.car_id = secondTable.car_id where firstTable.car_id is null;

I have a solution for a similar problem with more people and more cars.
Maybe it helps DBfiddle example
The base datatabke are as follows
CREATE TABLE Table1
(`id` int, `name` varchar(4), `address` varchar(7), `car_id` varchar(4))
;
INSERT INTO Table1
(`id`, `name`, `address`, `car_id`)
VALUES
(1, 'Alex', 'US', NULL),
(2, 'Jaso', 'Canad a', '1'),
(3, 'Paso', 'Canad a', NULL),
(4, 'Paso', 'danad a', NULL)
;
CREATE TABLE Table2
(`car_id` int, `color` varchar(7))
;
INSERT INTO Table2
(`car_id`, `color`)
VALUES
(1, 'red'),
(2, 'blue'),
(3, 'green') ,
(4, 'purple')
;
The sql statement for mysql till 5.7
Update Table1 t1a inner Join
(Select tab1.id, tab2.car_id From
( Select
`id`,
#curRank := #curRank + 1 AS rank
FROM Table1, (SELECT #curRank := 0) r
WHERE car_id IS NULL ) tab1
inner join
( Select
car_id,
#curRank2 := #curRank2 + 1 AS rank
From Table2 , (SELECT #curRank2 := 0) r Where car_id NOT IN
(
Select Car_id From
(Select GROUP_CONCAT(t1.car_id) car_id FROM Table1 as t1) t1e
WHERE car_id IS NOT NULL GROUP BY car_id
)
) tab2
ON tab1.rank = tab2.rank) t2a
on t1a.id = t2a.id
SET t1a.car_id = t2a.car_ID
;
The Problem that we face here is that we need actually the car_ids from table taht are already taken. And then select only rest of the car_id, that are not selected yet.
After that we have to make the statement so that ver person gets one color ann that every colour is selected only once. that takes a little code because we have no relationship betwenn tabel1 car_ids thta are NULL and table2.
So result is
id name address car_id
1 Alex US 2
2 Jaso Canad a 1
3 Paso Canad a 3
4 Paso danad a 4
With mysql 8 you use window function instead of the vehicle rank.

Related

Is there a way to perform multiple lookups across different columns in two tables in mysql?

I have a table 1 as below (Simplified versions show here as the actual one has over 60+ columns)
ID
Description
From_Index_Code
To_Index_Code
Cost
PARAM1
PARAM2
A
Something.
A001
B001
500
abc.
xyz.
B
Something2.
B001.
C001
1000
abc.
xyz.
I have a master table that is of the following structure:
ID
Code.
Value
1
A001.
100.
2
B001.
200.
3
C001.
300.
Now I have an input that has the following values:
PARAM1=abc,PARAM2=xyz and Index value as 150. I need to check if the index value is between A001 and B001, if yes, return 500. If it is between B001 and C001 then I return 1000.
I tried doing
WITH
src_1 as (select id,s.description,g.value
from table1 s left outer join table 2 g on s.from_index_code=g.code),
src_2 as (select id,s.description,g.value
from table1 s left outer join table 2 g on s.to_index_code=g.code)
select src_1.id, src_1.description,src_1.value as 'from_value',src_2.value as 'to_value' from src_1 ,src_2 where src_1.id=src_2.id.
I expect the resulting set to be something like:
ID
Description
From_Value
To_Value
A
Something.
100.
200.
B
Somethng2.
200.
300.
It should have the same number of rows as Table 1. But the resulting output has far too many rows.
Table 1 has 8497 rows.Table 2 has 121 rows. However the resulting output has over 14 million rows.
What is the best way to accomplish this?
Any help would be much appreciated.
Thanks.
your problem was, that you select id in the subselect and so mysql chooses wrong, adding a s to the ids columns clears everything up and gets the correct result
CREATE TABLE table1
(`id` varchar(1), `Description` varchar(11), `From_Index_Code` varchar(5), `To_Index_Code` varchar(4), `Cost` int, `PARAM1` varchar(4), `PARAM2` varchar(4))
;
INSERT INTO table1
(`id`, `Description`, `From_Index_Code`, `To_Index_Code`, `Cost`, `PARAM1`, `PARAM2`)
VALUES
('A', 'Something.', 'A001', 'B001', 500, 'abc.', 'xyz.'),
('B', 'Something2.', 'B001.', 'C001', 1000, 'abc.', 'xyz.')
;
CREATE TABLE table2
(`id` int, `Code` varchar(4), `Value` int)
;
INSERT INTO table2
(`id`, `Code`, `Value`)
VALUES
(1, 'A001', 100.),
(2, 'B001', 200.),
(3, 'C001', 300.)
;
WITH
src_1 as (select s.id,s.description,g.value
from table1 s left outer join table2 g on s.from_index_code=g.code),
src_2 as (select s.id,s.description,g.value
from table1 s left outer join table2 g on s.to_index_code=g.code)
select src_1.id, src_1.description,src_1.value as 'from_value',src_2.value as 'to_value' from src_1 ,src_2 where src_1.id=src_2.id
id | description | from_value | to_value
:- | :---------- | ---------: | -------:
A | Something. | 100 | 200
B | Something2. | null | 300
db<>fiddle here
Hmmm . . . I am thinking you want joins and conditions like this:
select t1.*
from table1 t1 join
table2 t2f
on t2f.code = t1.From_Index_Code join
table2 t2t
on t2t.code = t1.to_index_code
where t1.param1 = #param1 and t2.param2 = #param2 and
#index_value between t2f.value and t2t.value;
For performance, you would want indexes on table1(param1, param2, from_index_code, to_index_code) and on table2(code) (if it is not already the primary key).

Get a name from other tables when making a sql query, if the id fields are the same

I have three tables. FirstTable is like this:
id
id2
id3
message
info
1
0
2
hello!
none
2
1
0
hi there
none
3
0
3
hi man
none
SecondTable is:
id2
name
1
Alex
2
Bob
ThirdTable is:
id3
name
1
Rob
2
Tom
3
Joe
As you can see, in FirstTable always only one of id2 and id3 column's values is not zero. So, I want to get the result as below:
1 - Tom - hello!
or
2 - Alex - hi there
or
3 - Joe - hi man
I cannot use a query like
SELECT
FirstTable.id AS id, FirstTable.message AS message,
FirstTable.info AS info,
SecondTable.name AS name1,
ThirdTable.name AS name2,
FROM
FirstTable, SecondTable, ThirdTable
WHERE
FirstTable.id = 1
AND FirstTable.id2 = SecondTable.id2
AND FirstTable.id3 = ThirdTable.id3
ORDER BY
FirstTable.id DESC
because I do not have 0 only one of id2/id3 in FirstTable. What is the right query to get something like
1 - Tom - hello!
? Thanks
You can accoumplish that by using two LEFT JOINs
CREATE TABLE table1
(`id` int, `id2` int, `id3` int, `message` varchar(8), `info` varchar(4))
;
INSERT INTO table1
(`id`, `id2`, `id3`, `message`, `info`)
VALUES
(1, 0, 2, 'hello!', 'none'),
(2, 1, 0, 'hi there', 'none'),
(3, 0, 3, 'hi man', 'none')
;
CREATE TABLE table2
(`id2` int, `name` varchar(4))
;
INSERT INTO table2
(`id2`, `name`)
VALUES
(1, 'Alex'),
(2, 'Bob')
;
CREATE TABLE table3
(`id3` int, `name` varchar(3))
;
INSERT INTO table3
(`id3`, `name`)
VALUES
(1, 'Rob'),
(2, 'Tom'),
(3, 'Joe')
;
SELECT t1.`id`,
CASE WHEN t1.id2 = 0 THEN t3.`name`
ELSE t2.`name`END name,
t1.message
FROM table1 t1 LEFT JOIN table2 t2 ON t1.id2=t2.id2 LEFT JOIN table3 t3 ON t1.id3 = t3.id3
id | name | message
-: | :--- | :-------
1 | Tom | hello!
2 | Alex | hi there
3 | Joe | hi man
db<>fiddle here

MySQL turn JSON_ARRAY of ids into JSON_ARRAY of values [MySQL 8]

I have a JSON_ARRAY of ids in the form of [1,3,...]. Each value represents an id to a value in another table.
Table: pets
id | value
1 | cat
2 | dog
3 | hamster
Table: pet_owner
id | pets_array
1 | [1, 3]
2 | [2]
3 | []
What I want to get when I query pet_owners is the following result:
Table: pet_owner
id | pets_array
1 | ["cat", "hamster"]
2 | ["dog"]
3 | []
How do I run a sub-select on each array element to get its value?
As JSON goes, it is always a pain to handle
When you need also all that have no pets, you must left Join the owner table
CREATE TABLE pet_owner (
`id` INTEGER,
`pets_array` JSON
);
INSERT INTO pet_owner
(`id`, `pets_array`)
VALUES
('1', '[1, 3]'),
('2', '[2]'),
('3', '[]');
CREATE TABLE pets (
`id` INTEGER,
`value` VARCHAR(7)
);
INSERT INTO pets
(`id`, `value`)
VALUES
('1', 'cat'),
('2', 'dog'),
('3', 'hamster');
SELECT
t1.id,
JSON_ARRAYAGG(
p.`value`
) AS pets_array
FROM(
SELECT *
FROM pet_owner ,
JSON_TABLE(
pet_owner.pets_array , "$[*]"
COLUMNS(IDs int PATH "$" NULL ON ERROR DEFAULT '0' ON EMPTY )
) AS J_LINK ) t1
LEFT JOIN pets p ON p.id =t1.IDs
GROUP BY
t1.id
;
id | pets_array
-: | :-----------------
1 | ["cat", "hamster"]
2 | ["dog"]
db<>fiddle here
A normalized Table would spare you to convert the data into usable columns.
You can join on json_contains(), then re-aggregate:
select po.id, json_arrayagg(p.value) as owners
from pet_owner po
left join pets p on json_contains(po.pets_array, cast(p.id as char))
group by po.id
Note that, unlike most (if not all!) other databases, MySQL does not guarantee the ordering of elements in an array generated by json_arrayagg(): that's just a fact we have to live with as of the current version.

MySQL count occurrences of data displayed as list in cells

Good day all.
Lets say that I've a table organized like this:
|ColA |
---------------
|AAA |
|AAA#!#BBB |
|BBB#!#CCC#!#DDD|
|AAA#!#DDD |
|DDD |
What I would like to achieve is to count occurrences of every string, considering the simbols "#!#" as a separator.
The best should be having a MySQL Count() result style:
|count| |
|AAA |3 |
|BBB |2 |
|CCC |1 |
|DDD |3 |
Is it possible to combine a replace with a count in a SQL statement? is it possible to use the replace result as a table to apply the count on?
Something like this should do the trick:
SCHEMA
CREATE TABLE TableA
(`id` int, `ColA` varchar(255));
INSERT INTO TableA
(`id`, `ColA`)
VALUES
(1, 'AAA'),
(2, 'AAA#!#BBB'),
(3, 'BBB#!#CCC#!#DDD'),
(4, 'AAA#!#DDD'),
(5, 'DDD');
CREATE TABLE TableB
(`id` int, `Name` varchar(255));
INSERT INTO TableB
(`id`, `Name`)
VALUES
(1, 'AAA'),
(2, 'BBB'),
(3, 'CCC'),
(4, 'DDD');
QUERY
SELECT col, SUM(count) as count
FROM
(SELECT
t2.Name as col,
ROUND (
(
CHAR_LENGTH(t1.ColA)
- CHAR_LENGTH( REPLACE (t1.ColA, t2.Name, "") )
) / CHAR_LENGTH(t2.Name)
) as count
FROM TableA as t1
INNER JOIN TableB as t2) as SubB
GROUP BY col, count
HAVING count > 0;
SQL FIDDLE

Get last state of item

In MySQL, I have two tables with a 1:n relationship.
Table items has products, whose state is kept in another table, like so :
items:
id |ref_num|name |...
1 |0001 |product1|...
2 |0002 |product2|...
items_states :
id|product_id|state_id|date
1 |1 |5 |2010-05-05 10:25:20
2 |1 |9 |2010-05-08 12:38:00
3 |1 |6 |2010-05-10 20:45:12
...
The states table is not relevant and only relates the state_id to the state name and so on.
How can I get products where the latest state is the one I specify, one item per row?
Thank you
You may want to try the following:
SELECT i.ref_num, i.name, s.latest_date
FROM items i
JOIN (
SELECT product_id, MAX(date) as latest_date
FROM items_states
GROUP BY product_id
) s ON (s.product_id = i.id);
If you want to return just one item, simply add a WHERE i.id = ? to the query.
Test case:
CREATE TABLE items (id int, ref_num varchar(10), name varchar(10));
CREATE TABLE items_states (id int, product_id int, state_id int, date datetime);
INSERT INTO items VALUES (1, '0001', 'product1');
INSERT INTO items VALUES (2, '0002', 'product2');
INSERT INTO items_states VALUES (1, 1, 5, '2010-05-05 10:25:20');
INSERT INTO items_states VALUES (2, 1, 9, '2010-05-08 12:38:00');
INSERT INTO items_states VALUES (3, 1, 6, '2010-05-10 20:45:12');
Result:
+---------+----------+---------------------+
| ref_num | name | latest_date |
+---------+----------+---------------------+
| 0001 | product1 | 2010-05-10 20:45:12 |
+---------+----------+---------------------+
1 row in set (0.02 sec)
Either LEFT JOIN the items_states table to itself, requiring a second.date > first.date, and put a WHERE second.id IS NULL clause in it:
SELECT a.*
FROM item_states a
LEFT JOIN item_states b
ON a.product_id = b.product_id
AND b.product_id > a.product_id
WHERE b.id IS NULL AND a.state_id = <desired state>
Or make a row based query: see Mark Byers' example.