How to prevent duplicate entry key when update - mysql

Problem explain
I won't update the last primary key of the 3 primary key concatenate. But the problem is sometimes the first and second primary key was the same for multiple records. And in this case, when I set my new value I have a duplicate entry key even I use sub-request to avoid that problem.
Some Code
Schemas
create table llx_element_contact
(
rowid int auto_increment
primary key,
datecreate datetime null,
statut smallint default 5 null,
element_id int not null,
fk_c_type_contact int not null,
fk_socpeople int not null,
constraint idx_element_contact_idx1
unique (element_id, fk_c_type_contact, fk_socpeople)
)
Update request
this request return duplicate key error
update llx_element_contact lec
set lec.fk_socpeople = 64
where
-- Try to avoid the error by non including the values that are the same
(select count(*)
from llx_element_contact ec
where ec.fk_socpeople = 64
and ec.element_id = lec.element_id
and ec.fk_c_type_contact = lec.fk_c_type_contact) = 0
Test data
rowid, datecreate, statut, element_id, fk_c_type_contact, fk_sockpeople
65,2015-08-31 18:59:18,4,65,160,30
66,2015-08-31 18:59:18,4,66,159,12
67,2015-08-31 18:59:18,4,67,160,12
15283,2016-03-23 11:47:15,4,6404,160,39
15284,2016-03-23 11:51:30,4,6404,160,58

You should check only two other members of unique constraint as you're trying to assign the same value to the 3d member. No more then one row with the same two members must exist.
update llx_element_contact lec
set lec.fk_socpeople = 64
where
-- Try to avoid the error by non including the values that are the same
(select count(*)
from llx_element_contact ec
where ec.element_id = lec.element_id
and ec.fk_c_type_contact = lec.fk_c_type_contact) <=1
or
update llx_element_contact lec
set lec.fk_socpeople = 64
where
-- Try to avoid the error by non including the values that are the same
not exists (select 1
from llx_element_contact ec
where ec.element_id = lec.element_id
and ec.fk_c_type_contact = lec.fk_c_type_contact
and lec.fk_socpeople != ec.fk_socpeople)

You can use:
You can prevent the unique conflict using left join to check that the corresponding row doesn't already exist:
update llx_element_contact lec left join
(select element_id, fk_c_type_contact
from llx_element_contact lec2
where lec2.fk_socpeople = 64
group by element_id, fk_c_type_contact
) lec2
using (element_id, fk_c_type_contact)
set lec.fk_socpeople = 64
where lec2.element_id is null;
Your query has additional logic in it that is not explained. It is not necessary for what you are asking.

WITH cte AS (
SELECT rowid,
SUM(fk_socpeople = 64) OVER (PARTITION BY element_id, fk_c_type_contact) u_flag,
ROW_NUMBER() OVER (PARTITION BY element_id, fk_c_type_contact ORDER BY datecreate DESC) u_rn
FROM llx_element_contact
)
update llx_element_contact lec
JOIN cte USING (rowid)
set lec.fk_socpeople = 64
where cte.u_flag = 0
AND cte.u_rn = 1
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=08e20328ccc6187716084ce9d78816b0

Related

Get value of mySQL query grouped by id

I want to know an efficient way to solve the following query. Essentially I have the following two classes
CREATE TABLE `example`.`doc` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` INT NOT NULL,
`creator` VARCHAR(32) NOT NULL,
PRIMARY KEY (`id`);
CREATE TABLE `example`.`pic` (
`id` INT NOT NULL AUTO_INCREMENT,
`docuuid` INT NOT NULL,
`taken_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`lat` DECIMAL(3,10),
`lon` DECIMAL(3,10),
PRIMARY KEY (`id`);
this two tables are related by uuid (I know this is not the best idea to link two tables, but the table is like this). uuid is unique per doc. With the following query
SELECT
`doc`.`id` AS `docId`,
`doc`.`uuid` AS `uuid`,
`doc`.`creator` AS `creator`,
COUNT(`pic`.`id`) AS `pics`,
MIN(`pic`.`taken_at`) AS `min_date`,
MAX(`pic`.`taken_at`) AS `max_date`
FROM
`doc` INNER JOIN
`pic` ON (`doc`.`uuid` = `pic`.`docuuid`)
WHERE (
`pic`.`docuuid` IS NOT NULL AND
`doc`.`uuid` IS NOT NULL)
GROUP BY `doc`.`uuid`
I get, for each doc, the date at witch the first and last pics was taken. Additionally I want to get in this query, for each doc, the latitude and longitude of the first and last pic taken at that doc.
For example, If I have
doc1 = (id=0, uuid=1)
doc2 = (id=1, uuid=2)
pic1 = (taken_at=2021-01-01, docuuid=1, lat=1, lon=2)
pic2 = (taken_at=2021-01-02, docuuid=1, lat=3, lon=4)
pic3 = (taken_at=2021-01-03, docuuid=2, lat=5, lon=6)
pic4 = (taken_at=2021-01-04, docuuid=2, lat=7, lon=8)
pic5 = (taken_at=2021-01-05, docuuid=2, lat=9, lon=10)
then I want to get for doc1 minLat=1, minLon=2, maxLat=3, maxLon=4 and for doc2 minLat=5, minLon=6, maxLat=9, maxLon=10
You mat continue by joining the results of your current query with the pics table and extracting the desired details of each pic, I've included a sample below:
SELECT
t.*,
earliest_pic.lat as minLat,
earliest_pic.lon as minLon,
latest_pic.lat as maxLat,
latest_pic.lon as maxLon
FROM
(
SELECT
`doc`.`id` AS `docId`,
`doc`.`uuid` AS `uuid`,
`doc`.`creator` AS `creator`,
COUNT(`pic`.`id`) AS `pics`,
MIN(`pic`.`taken_at`) AS `min_date`,
MAX(`pic`.`taken_at`) AS `max_date`
FROM
`doc` INNER JOIN
`pic` ON (`doc`.`uuid` = `pic`.`docuuid`)
GROUP BY `doc`.`uuid`
) AS t
INNER JOIN `pic` as earliest_pic ON earliest_pic.taken_at = t.min_date AND
earliest_pic.docuuid = t.docId
INNER JOIN `pic` as latest_pic ON latest_pic.taken_at = t.max_date AND
latest_pic.docuuid = t.docId
Let me know if this works for you

Is this query optimised? - Mutual follows

Is this query optimised?
I'm trying to get the list of people personA follows who follow personB.
Few thousand of rows right now in the table but growing fast.
Want to make sure the query is performant enough for mysql.
Query:
select
*
from
(
select
*
from
creator_followers cf
where
cf.follower_user_id = 'personA'
and cf.current_active = 1
and cf.current_following = 1
) as fo
join creator_followers cf
where
fo.creator_user_id = cf.follower_user_id
and cf.creator_user_id = 'personB'
and cf.current_following = 1
and cf.current_active = 1
order by
cf.created_at desc
limit
10 offset 0;
Schema:
create table social.creator_followers
(
creator_user_id varchar(16) charset utf8 null,
follower_user_id varchar(16) charset utf8 null,
current_following bit null,
current_active bit null,
created_at bigint null,
id bigint auto_increment
primary key
)
It seems that it must be
SELECT *
FROM creator_followers cf1
join creator_followers cf2 ON cf1.creator_user_id = cf2.follower_user_id
where cf1.follower_user_id = 'personA'
and cf2.creator_user_id = 'personB'
and cf1.current_active = 1
and cf1.current_following = 1
and cf2.current_following = 1
and cf2.current_active = 1
order by cf2.created_at desc
limit 10 offset 0;
Based on a Comment, this might provide another speedup. Replace
id bigint auto_increment
primary key
with
PRIMARY KEY(creator_user_id, follower_user_id)

Conversion of merge statement to MYSQL using on duplicate key

Please suggest how to convert this teradata statement in MYSQL. As we know mysql doesn't support merge statement. Below 2 tables are also being used in select query and we have multiple primary key in each table.
MERGE INTO XYZ USING (
SELECT
ITRR.WORKFLOW_NAME WORKFLOW_NAME
, ITRR.INSTANCE_NAME INSTANCE_NAME
, MIN(ITRR.START_TIME) EARLIEST_START_TIME
, ITRR.SUBJECT_AREA SUBJECT_AREA
, 'INFORMATICA' PLATFORM_NAME
FROM
ABC IWRR
, DEF ITRR
WHERE
IWRR.WORKFLOW_RUN_ID = ITRR.WORKFLOW_RUN_ID
AND IWRR.USER_NAME IN ('xyz')
AND ITRR.RUN_STATUS_CODE <> 2
GROUP BY
ITRR.WORKFLOW_NAME
, ITRR.INSTANCE_NAME
, ITRR.SUBJECT_AREA
) SRC
ON
XYZ.PARENT_JOB_NAME = SRC.WORKFLOW_NAME
AND XYZ.CHILD_JOB_NAME = SRC.INSTANCE_NAME
AND XYZ.SANDBOX = SRC.SUBJECT_AREA
WHEN MATCHED THEN UPDATE SET FIRST_EXECUTION = SRC.EARLIEST_START_TIME
WHEN NOT MATCHED THEN INSERT
(
PARENT_JOB_NAME
, CHILD_JOB_NAME
, FIRST_EXECUTION
, SANDBOX
, PLATFORM_NAME
)VALUES
(
SRC.WORKFLOW_NAME
, SRC.INSTANCE_NAME
, SRC.EARLIEST_START_TIME
, SRC.SUBJECT_AREA
, SRC.PLATFORM_NAME
);
I am trying below query but it is not working.
INSERT INTO XYZ (
PARENT_JOB_NAME
, CHILD_JOB_NAME
, FIRST_EXECUTION
, SANDBOX
, PLATFORM_NAME
)
(SELECT
ITRR.WORKFLOW_NAME WORKFLOW_NAME
, ITRR.INSTANCE_NAME INSTANCE_NAME
, MIN(ITRR.START_TIME) EARLIEST_START_TIME
, ITRR.SUBJECT_AREA SUBJECT_AREA
, 'INFORMATICA' PLATFORM_NAME
FROM
ABC IWRR
, DEF ITRR
WHERE
IWRR.WORKFLOW_RUN_ID = ITRR.WORKFLOW_RUN_ID
AND IWRR.USER_NAME IN ('XYZ')
AND ITRR.RUN_STATUS_CODE <> 2
GROUP BY
ITRR.WORKFLOW_NAME
, ITRR.INSTANCE_NAME
, ITRR.SUBJECT_AREA
) SRC
ON DUPLICATE KEY UPDATE
FIRST_EXECUTION = SRC.EARLIEST_START_TIME
Primary key of XYZ = PARENT_JOB_NAME
Primary key of ABC= SUBJECT_ID
Primary key of DEF= SUBJECT_ID,WORKFLOW_ID,WORKFLOW_RUN_ID,WORKLET_RUN_ID,INSTANCE_ID,TASK_ID,START_TIME
The correct syntax in MySQL is:
INSERT INTO XYZ (PARENT_JOB_NAME, CHILD_JOB_NAME, FIRST_EXECUTION, SANDBOX, PLATFORM_NAME)
SELECT ITRR.WORKFLOW_NAME, ITRR.INSTANCE_NAME,
MIN(ITRR.START_TIME), ITRR.SUBJECT_AREA, 'INFORMATICA'
FROM ABC IWRR JOIN
DEF ITRR
ON IWRR.WORKFLOW_RUN_ID = ITRR.WORKFLOW_RUN_ID
WHERE IWRR.USER_NAME IN ('XYZ') AND
ITRR.RUN_STATUS_CODE <> 2
GROUP BY ITRR.WORKFLOW_NAME, ITRR.INSTANCE_NAME, ITRR.SUBJECT_AREA
ON DUPLICATE KEY UPDATE FIRST_EXECUTION = VALUES(FIRST_EXECUTION);
Note the use of proper, explicit, standard, readable JOIN syntax. Use it.
The major changes are
Fixing the archaic syntax.
Removing the parentheses are not needed for the select in an insert . . . select (although they are probably allowed).
Removing the table alias, which is definitely not allowed.
Fixing the on duplicate key statement.
Believe the comment from #Akina is correct and that the primary key on the table XYZ is just incorrect.
The primary key on the XYZ table need to include these columns PARENT_JOB_NAME, CHILD_JOB_NAME and SANDBOX for the mysql INSERT ... ON DUPLICATE KEY statement to work correctly.

MYSQL - INSERT INTO using WHERE condition of Table 1

after reading all the "INSERT INTO" posts as well as the documentation, I am still unsure whether what I want is feasible or not.
I want to change the UPDATE below into an INSERT INTO, because I have many 100-thousands of them (speed issues):
UPDATE city c
SET
c.g17h = '3196504',
c.g17q = '2593487',
c.g17k = '0',
c.g17w = '0',
c.g17s = '0'
WHERE
p17t = 30 AND p17l = '30';
or
UPDATE city c
SET
c.g1h = '0',
c.g1q = '0',
c.g1k = '0',
c.g1w = '0',
c.g1s = '0'
WHERE
p1t = 1
AND p1l = '1';
However, my best solutions do not work:
INSERT INTO city (g17h, g17q, g17k, g17w, g17s)
SELECT
'3196504',
'2593487',
'0',
'0',
'0'
FROM valuestoretab
WHERE
p17t = 30
AND p17l = '30';
This is of course because my WHERE condition can only be satisfied in the first table (city) but not in the second one which is just a table of values, whereas the city table is a data set where each id has particular values for p17t, p17l and so on.
For clarification:
The first table (city) looks like
cityid1, ownerid1, islandid1, p17t, p17l
cityid2, ownerid2, islandid2, p17t, p17l
with different values for each row for p17t and p17l.
So, my questions would be:
1. Is it at all possible to write a INSERT-Query with a WHERE condition for the table that is being inserted into?
2. If no, do I have to stick to my UPDATE or is there another (fast!) solution?
Thanks to the community!
litotes
To give a quick answer insert with a select statement is possible
Insert INTO MyTable (Val1, Val2)
Select
SomeValue,
AnotherValue
From MyOtherTable
Where Date = Getdate()
On the other hand, you can also update many records like in the following example:
Update t1
Set
t1.Val1 = t2.SomeValue,
t1.Val2 = t2.AnotherValue
From MyTable t1
Inner join MyOtherTable t2 ON t1.PK = t2.FK
where t2.Date = getdate()
EDIT:
When i read the following query, I presume p17t and p17l are from the valuestoretab.
UPDATE city c
SET
c.g17h = '3196504',
c.g17q = '2593487',
c.g17k = '0',
c.g17w = '0',
c.g17s = '0'
WHERE
p17t = 30 AND p17l = '30';
==> changed this into:
UPDATE c
SET
c.g17h = '3196504',
c.g17q = '2593487',
c.g17k = '0',
c.g17w = '0',
c.g17s = '0'
FROM City c, ValueStoreTab v
WHERE
c.SomeCol = v.SomeCol -- Here, your relation must exist!
AND v.p17t = 30 AND v.p17l = '30';

mysql add key does not work

I have this query:
SELECT adressid, adressname FROM kino_adressen WHERE city ='Seattle'
I wanted to create an index like this
ALTER TABLE <tablename> ADD KEY index_abc(adressid, adressname(40))
But when I then check it by using:
EXPLAIN SELECT adressid, adressname FROM kino_adressen WHERE city ='Seattle'
It says
type = ALL
possible keys = NULL
key = NULL
...rows = 106
Can anyone give some piece of advice how to do this properly ?
// edit:
Another problem I do not understand:
SELECT DISTINCT
titel,
regie,
darsteller,
filmbild,
kino_filme.filmid,
kino_filme.beschreibung,
fsk,
filmlaenge,
verleih,
sprachfassung
FROM
kino_filme
LEFT JOIN kino_terminefilme ON (
kino_terminefilme.filmid = kino_filme.filmid
)
LEFT JOIN kino_termine ON (
kino_terminefilme.terminid = kino_termine.terminid
)
LEFT JOIN kino_kinos ON (
kino_kinos.kinoid = kino_termine.kinoid
)
LEFT JOIN kino_adressen ON (
kino_adressen.adressid = kino_kinos.adressid
)
WHERE
kino_adressen.adressid = 32038
And the result is like:
Why is kino_termine not using any index ?
I set it to PK while creating and even added an index afterwards, but none of those helped.
You added an index on the address but use the city in the where clause. Add an index on the city then it will be used.