Mysql update null values with value got by comparing another column - mysql

Consider a table "users" as below:
id, add_id, add
1, 1, abc
2, null, abc
3, null, xyz
4, 2, xyz
Expected output:
id, add_id, add
1, 1, abc
2, 1, abc
3, 2, xyz
4, 2, xyz
Please suggest a MySQL query to get the desired result.

A simple method uses window functions:
select id, max(add_id) over (partition by add), add
from t;
If you want to change the value, then the update would be:
update t join
(select add, max(add_id) as add_id
from t
group by add
) tt
on t.add = tt.add
set t.add_id = tt.add_id
where t.add_id is null;

You can select the id From the table.
if you have more than 1 row with and aff_id and add, then you must LIMIT the inner SELECT
CREATE TABLE table1 (
`id` INTEGER,
`add_id` VARCHAR(4),
`add` VARCHAR(3)
);
INSERT INTO table1
(`id`, `add_id`, `add`)
VALUES
('1', '1', 'abc'),
('2', null, 'abc'),
('3', null, 'xyz'),
('4', '2', 'xyz');
UPDATE table1 t2
SET `add_id` = (SELECT `add_id` FROM (SELECT * FROM table1) t1 WHERE t1. `add` = t2.`add` AND `add_id` IS NOT NULL)
WHERE `add_id` IS NULL
SELECT * FROM table1
id | add_id | add
-: | :----- | :--
1 | 1 | abc
2 | 1 | abc
3 | 2 | xyz
4 | 2 | xyz
db<>fiddle here

Related

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.

select parent row where ALL children rows meet criterias

I have two table, rates and criterias. parent_id in criterias refers to id in rates.
I need to select the rates where ALL children rows in table criterias WHERE criteria_1 AND criteria_2 equal to NULL.
In the example below, only flat rate should be selected
rates
id | name
--------------------
1 | summer rate
2 | flat rate
3 | student rate
conditions
id | parent_id | criteria_1 | criteria_2
------------------------------------------------------
1 | 1 | 523 | 563
2 | 1 | null | null
3 | 2 | null | null
4 | 2 | null | null
5 | 3 | 777 | null
I tried NOT EXIST, but it return it return any rate where one children have two null criteria
try using this subquery with inner join.
select * from
(select * from rates where name = 'flat rate') t1
inner join
(select * from criterias where coalesce(criteria_1, 0) = 0 and coalesce(criteria_2, 0) = 0) t2
on t2.parent_id = t1.id
Please see the following query it should work. You need to compare 2 result set to find rate with ALL null childrens.
SELECT
a.parent_id
FROM(
SELECT
parent_id,
COUNT(*) AS total_count
FROM criterias c
WHERE c. criteria_1 IS NULL AND c.criteria_2 IS NULL
GROUP BY 1
) a
INNER JOIN (
SELECT
parent_id,
COUNT(*) AS total_count
FROM criterias c
GROUP BY 1
)b ON a.parent_id = b.parent_id AND a.total_count = b.total_count
I would use some aggregate function with an having clause grouped by parent_id.
Using a min or max would return a numerical value if there is at least one non-null value per parent_id but will be null if there are only null. So just need to use an having min(<field>) is null to find a parent_id with only null value.
select *
from rates r
where id in(
select parent_id
from criterias
group by parent_id
having min(criteria_1) is null
and min(criteria_2) is null
);
or With an inner join (if you prefer)
select *
from rates r
inner join (
select parent_id
from criterias
group by parent_id
having min(criteria_1) is null
and min(criteria_2) is null
) c ON c.parent_id = r.id;
Validated with :
create table rates(
id int,
name varchar(20)
);
create table criterias (
id int,
parent_id int,
criteria_1 int null,
criteria_2 int null
);
insert into rates values (1, 'summer rate');
insert into rates values (2, 'flate rate');
insert into rates values (3, 'student rate');
insert into rates values (4, 'old rate');
insert into rates values (5, 'any rate');
insert into criterias values (1, 1, 523, 563);
insert into criterias values (2, 1, null, null);
insert into criterias values (3, 2, null, null);
insert into criterias values (4, 2, null, null);
insert into criterias values (5, 1, 777, null);
insert into criterias values (6, 4, null, null);
insert into criterias values (7, 5, null, null);
insert into criterias values (8, 5, null, null);
/*insert into criterias values (9, 5, 1, null);*/
select *
from rates r
where id in(
select parent_id
from criterias
group by parent_id
having min(criteria_1) is null
and min(criteria_2) is null
);
Result:
id name
2 flate rate
4 old rate
5 any rate

How do I select the max(timestamp) from a relational mysql table fast

We are developing a ticket system and for the dashboard we want to show the tickets with it's latest status. We have two tables. The first one for the ticket itself and a second table for the individual edits.
The system is running already, but the performance for the dashboard is very bad (6 seconds for ~1300 tickets). At first we used a statemant which selected 'where timestamp = (select max(Timestamp))' for every ticket. In the second step we created a view which only includes the latest timestamp for every ticket, but we are not able to also include the correct status into this view.
So the main Problem might be, that we can't build a table in which for every ticket the lastest ins_date and also the latest status is selected.
Simplyfied database looks like:
CREATE TABLE `ticket` (
`id` int(10) NOT NULL,
`betreff` varchar(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ticket_relation` (
`id` int(11) NOT NULL,
`ticket` int(10) NOT NULL,
`info` varchar(10000) DEFAULT NULL,
`status` int(1) NOT NULL DEFAULT '0',
`ins_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`ins_user` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `ticket` (`id`, `betreff`) VALUES
(1, 'Technische Frage'),
(2, 'Ticket 2'),
(3, 'Weitere Fragen');
INSERT INTO `ticket_relation` (`id`, `ticket`, `info`, `status`, `ins_date`, `ins_user`) VALUES
(1, 1, 'Betreff 1', 0, '2019-05-28 11:02:18', 123),
(2, 1, 'Betreff 2', 3, '2019-05-28 12:07:36', 123),
(3, 2, 'Betreff 3', 0, '2019-05-29 06:49:32', 123),
(4, 3, 'Betreff 4', 1, '2019-05-29 07:44:07', 123),
(5, 2, 'Betreff 5', 1, '2019-05-29 07:49:32', 123),
(6, 2, 'Betreff 6', 3, '2019-05-29 08:49:32', 123),
(7, 3, 'Betreff 7', 2, '2019-05-29 09:49:32', 123),
(8, 2, 'Betreff 8', 1, '2019-05-29 10:49:32', 123),
(9, 3, 'Betreff 9', 2, '2019-05-29 11:49:32', 123),
(10, 3, 'Betreff 10', 3, '2019-05-29 12:49:32', 123);
I have created a SQL Fiddle: http://sqlfiddle.com/#!9/a873b6/3
The first three Statements are attempts that won't work correct or way too slow. The last one is the key I think, but I don't understand, why this gets the status wrong.
The attempt to create the table with latest ins_date AND status for each ticket:
SELECT
ticket, status, MAX(ins_date) as max_date
FROM
ticket_relation
GROUP BY
ticket
ORDER BY
ins_date DESC;
This query gets the correct (latest) ins_date for every ticket, but not the latest status:
+--------+--------+----------------------+
| ticket | status | max_date |
+--------+--------+----------------------+
| 3 | 1 | 2019-05-29T12:49:32Z |
+--------+--------+----------------------+
| 2 | 0 | 2019-05-29T10:49:32Z |
+--------+--------+----------------------+
| 1 | 0 | 2019-05-28T12:07:36Z |
+--------+--------+----------------------+
Expected output would be this:
+--------+--------+----------------------+
| ticket | status | max_date |
+--------+--------+----------------------+
| 3 | 3 | 2019-05-29T12:49:32Z |
+--------+--------+----------------------+
| 2 | 1 | 2019-05-29T10:49:32Z |
+--------+--------+----------------------+
| 1 | 3 | 2019-05-28T12:07:36Z |
+--------+--------+----------------------+
Is there a efficient way to select the latest timestamp and status for every ticket in the tiket-table?
Other approach is to think filtering not GROUPing..
Query
SELECT
ticket_relation_1.ticket
, ticket_relation_1.status
, ticket_relation_1.ins_date
FROM
ticket_relation AS ticket_relation_1
LEFT JOIN
ticket_relation AS ticket_relation_2
ON
ticket_relation_1.ticket = ticket_relation_2.ticket
AND
ticket_relation_1.ins_date < ticket_relation_2.ins_date
WHERE
ticket_relation_2.id IS NULL
ORDER BY
ticket_relation_1.id DESC
Result
| ticket | status | ins_date |
| ------ | ------ | ------------------- |
| 3 | 3 | 2019-05-29 12:49:32 |
| 2 | 1 | 2019-05-29 10:49:32 |
| 1 | 3 | 2019-05-28 12:07:36 |
see demo
This query would require a index KEY(ticket, ins_date, id) to get max performance..
One solution would be to use a subquery to compute the latest insert date for each ticket, and then to join the results with the original table, like:
SELECT t.ticket, t.status, t.ins_date
FROM ticket_relation t
INNER JOIN (
SELECT ticket, max(ins_date) max_ins_date
FROM ticket_relation
GROUP BY ticket
) x ON t.ticket = x.ticket AND t.ins_date = x.max_ins_date
For better performance with this query, you want an index on (ticket, ins_date).
Anoter option would be to use a NOT EXISTS condition to ensure that only the latest record is selected, like:
SELECT t.ticket, t.status, t.ins_date
FROM ticket_relation t
WHERE NOT EXISTS (
SELECT 1
FROM ticket_relation t1
WHERE t1.ticket = t.ticket AND t1.ins_date > t.ins_date)
)
NB: when dealing with GROUP BY, all non-aggregated columns must appear in the GROUP BY clause. Else, you will get either an error or unprectictable results (depending on whether server option ONLY_FULL_GROUP_BY is, respectively, enabled or disabled).
If you are able to upgrade to a recent version of mysql (8.0), then window functions can be used to simplify the query and possibly increase its performance, like:
SELECT ticket, status, ins_date
FROM (
SELECT
ticket,
status,
ins_date,
row_number() over(partition by ticket order by ins_date desc) rn
FROM ticket_relation
) x WHERE rn = 1
You can try below query -
SELECT
ticket, status, ins_date as max_date
FROM ticket_relation a
where ins_date in (select max(ins_date) from ticket_relation b where a.ticket=b.ticket)

MySQL NOT IN from another column in the same table

I want to run a mysql query to select all rows from a table films where the value of the title column does not exist anywhere in all the values of another column (collection).
Here is a simplified version of my table with content:
mysql> select * from films;
+----+--------------+--------------+
| id | title | collection |
+----+--------------+--------------+
| 1 | Collection 1 | NULL |
| 2 | Film 1 | NULL |
| 3 | Film 2 | Collection 1 |
+----+--------------+--------------+
Here is my query:
mysql> SELECT * FROM films WHERE title NOT IN (SELECT collection FROM films);
Empty set (0.00 sec)
In this example, I would want to select the rows with titles Film 1 and Film 2, but my query is returning no rows.
Here is the table structure:
CREATE TABLE `films` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(200) NOT NULL DEFAULT '',
`collection` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
SELECT *
FROM films
WHERE title NOT IN (SELECT collection FROM films where collection is not null);
SQLFiddle: http://sqlfiddle.com/#!2/76278/1
Have you tried using NOT EXISTS:
SELECT *
FROM films f1
WHERE NOT EXISTS (SELECT collection
FROM films f2
WHERE f1.title = f2.collection);
See SQL Fiddle with Demo
If you want to use IN then you will want to look for values that are NOT NULL:
SELECT *
FROM films
WHERE title NOT IN (SELECT collection
FROM films
WHERE collection is not null);
See SQL Fiddle with Demo
The result for both is:
| ID | TITLE | COLLECTION |
------------------------------
| 2 | Film 1 | (null) |
| 3 | Film 2 | Collection 1 |
The problem with your current query is that -- stealing from #Quassnoi's answer here:
Both IN and NOT IN return NULL which is not an acceptable condition for WHERE clause.
Since the null value is being returned by your subquery you want to specifically exclude it.
Another option using an outer join
SELECT f.*
FROM films f LEFT OUTER JOIN films ff
ON f.title = ff.collection
WHERE ff.collection IS NULL
Try this please:
SQLFIDDLE DEMO
Query:
select a.id, a.planid
from one a
left join one b
on a.planid <> b.iid
where not (b.iid is null)
group by b.id
;
Results: based on the sample table I used.
ID PLANID
t15 1
j18 2
EDIT TO ADD : HERE WITH OP SCHEMA
select b.id, b.title
from opschema b
inner join opschema a
on b.title <> a.collection
or b.collection <> a.title
group by b.id
;
OP SHCEMA SQLFIDDLE DEMO
ID TITLE
2 Film 1
3 Film 2
CREATE TABLE IF NOT EXISTS `reservation_tables` (
`res_table_id` int(10) NOT NULL AUTO_INCREMENT,
`res_table_name` int(10) NOT NULL,
`date_time` varchar(20) NOT NULL,
`partyhall_id` int(10) NOT NULL,
`flag` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`res_table_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
INSERT INTO `reservation_tables` (`res_table_id`, `res_table_name`, `date_time`, `partyhall_id`, `flag`) VALUES
(1, 1, '2014-08-17 12:00 am', 7, '1'),
(2, 2, '2014-08-17 12:00 am', 7, '1'),
(3, 3, '2014-08-18 12:00 am', 8, '1'),
(4, 4, '2014-08-18 12:00 am', 8, '1'),
(5, 1, '2014-08-25 12:00 am', 12, '1'),
(6, 2, '2014-08-25 12:00 am', 12, '1'),
(7, 3, '2014-08-20 12:00 am', 23, '1'),
(8, 4, '2014-08-20 12:00 am', 23, '1');
Ι had to select available table name for matching date_time
Example select available table_name where date_time = 2014-08-18 12:00 am.
solution query is:
im sure this works well
SELECT distinct res_table_name FROM reservation_tables WHERE `res_table_name` NOT IN
(SELECT `res_table_name` FROM reservation_tables where `date_time` = '2014-08-17 12:00 am')