mysql query updating with subquery - mysql

Hello i need to perform the following query
Query
update Taxonomy
set sourceId = (
select id from TaxonomyMapping a where a.oldId =
(
select cm.en_ID
from TaxonomyMapping ta
join CategoryMapping cm on ta.oldId = cm.language_ID where ta.oldId = oldId limit 1
)
) where id > -1;
The tables are as following:
Taxonomy(id, oldId, sourceId, name, language)
TaxonomyMapping(id, oldId, sourceId, name, language) Exact copy of Taxonomy
CategoryMapping(en_ID, language_ID)
What im trying to accomplish
The original taxonomy table has got categories in a certain language there are translations of it, but the need they are known in Category mapping, now i need to add the english translation to the sourceId, Can somebody help me with this? at the moment it fills one id for all im suspecting that the oldId(ta.oldId = oldId) i use is not the oldId of the table to update. Is it possible to do a query like this or should i search for another solution?

I have placed the same question in dba.stackexchange.com the answer given to me was the right one this was the query i was looking for:
UPDATE Taxonomy t
JOIN CategoryMapping cm ON cm.language_ID = t.oldID
JOIN (SELECT * FROM Taxonomy) x ON x.oldID = cm.en_ID
SET t.sourceId = x.id
WHERE t.id > -1;
source: https://dba.stackexchange.com/questions/50981/mysql-update-query-with-subquery

Related

INNER JOIN query

I'm having trouble optimising a simple SQL query but having serious issue with timing. I've written it three times and none of them work. Here is the original one I was hoping to work:
SELECT RSKADDR.*
FROM EDW_BASE.RCI_RISK_ADDRESS RSKADDR
INNER JOIN (
SELECT DISTINCT COVER_RISK_ID
FROM EDW_BASE.RCI_COVER_RISK_MASTER RSKMASTER
INNER JOIN
(SELECT DISTINCT CONTACT_ID, FOLLOW_UP_DATE
FROM EDW_STG.STG_CIM_SVOM03
WHERE OUTSTANDING = 1 AND QUEUE = 'CIM Update for Contact Address') ADDR_WF
ON RSKMASTER.CONTACT_CODE = ADDR_WF.CONTACT_ID
WHERE RSKMASTER.IS_STORNO != 1
AND RSKMASTER.PRODUCT_CODE = 'HOME'
AND ADDR_WF.FOLLOW_UP_DATE >= RSKMASTER.COVER_EFF_START_DATE
AND RSKMASTER.POLICY_STATUS_CODE = 'POLICY'
AND ADDR_WF.FOLLOW_UP_DATE <= RSKMASTER.COVER_EFF_END_DATE
) ACTVRSK
ON ACTVRSK.COVER_RISK_ID = RSKADDR.RISK_ID
The code in the first inner join works fast all the way to the end. That is, the second SELECT query (within the INNER JOIN query of the first and main SELECT query) works fast without a problem. The problem arises when I integrate the second SELECT query inside the INNER JOIN of the main SELECT query (select RSKADDR.*).
Then it seems the execution is never ending!
I tried other ways and same result:
SELECT RSKADDR.*
FROM EDW_BASE.RCI_RISK_ADDRESS RSKADDR
INNER JOIN EDW_BASE.RCI_COVER_RISK_MASTER RSKMASTER
ON RSKMASTER.COVER_RISK_ID = RSKADDR.RISK_ID
AND RSKMASTER.IS_STORNO != 1
AND RSKMASTER.PRODUCT_CODE = 'HOME'
AND RSKMASTER.POLICY_STATUS_CODE = 'POLICY'
INNER JOIN EDW_STG.STG_CIM_SVOM03 ADDR_WF
ON OUTSTANDING = 1 AND QUEUE = 'CIM Update for Contact Address'
AND RSKMASTER.CONTACT_CODE = ADDR_WF.CONTACT_ID
AND ADDR_WF.FOLLOW_UP_DATE >= RSKMASTER.COVER_EFF_START_DATE
AND ADDR_WF.FOLLOW_UP_DATE <= RSKMASTER.COVER_EFF_END_DATE
It's such an easy query and can't get it to work. How can I do this?
DISTINCT is a costly operation and seldom needed. It often indicates a bad database design or a poorly written query. In your query you are even doing this repeatedly; that doesn't look good.
The second query looks much better. As you say you get the same result, DISTINCT in the first query was superfluous obviously.
I see you doing joins, but all you select is data from one table. So why join then? Select from the table you want data from and put your criteria in WHERE where it belongs.
The following query may be faster, because it plainly shows that we are simply checking whether we find matches in the other tables or not. But then, MySQL was known for not performing too well with IN clauses, so that may depend on the Version you are using.
select *
from edw_base.rci_risk_address
where risk_id in
(
select rm.cover_risk_id
from edw_base.rci_cover_risk_master rm
where rm.is_storno <> 1
and rm.product_code = 'HOME'
and rm.policy_status_code = 'POLICY'
and exists
(
select *
from edw_stg.stg_cim_svom03 adr
where adr.contact_id = rm.contact_code
and adr.follow_up_date >= rm.cover_eff_start_date
and adr.follow_up_date <= rm.cover_eff_end_date
and adr.outstanding = 1
and adr.queue = 'CIM Update for Contact Address'
)
);
Anyway, with your second query or with mine, I suppose the following indexes would help:
create index idx1 on rci_cover_risk_master
(
product_code,
policy_status_code,
is_storno,
contact_code,
cover_eff_start_date,
cover_eff_end_date,
cover_risk_id
);
create index idx2 on stg_cim_svom03
(
contact_id,
follow_up_date,
outstanding,
queue
);
create index idx3 on rci_risk_address(risk_id);
From the query, you only need RSKADDR data, so no need for an INNER JOIN. You can do the same with EXISTS keyword. Try the below query
SELECT RSKADDR.*
FROM EDW_BASE.RCI_RISK_ADDRESS RSKADDR
WHERE EXISTS (
SELECT 1
FROM EDW_BASE.RCI_COVER_RISK_MASTER RSKMASTER
WHERE EXISTS
(SELECT 1
FROM EDW_STG.STG_CIM_SVOM03
WHERE OUTSTANDING = 1 AND QUEUE = 'CIM Update for Contact Address') ADDR_WF
AND RSKMASTER.CONTACT_CODE = ADDR_WF.CONTACT_ID
AND RSKMASTER.IS_STORNO != 1
AND RSKMASTER.PRODUCT_CODE = 'HOME'
AND ADDR_WF.FOLLOW_UP_DATE >= RSKMASTER.COVER_EFF_START_DATE
AND RSKMASTER.POLICY_STATUS_CODE = 'POLICY'
AND ADDR_WF.FOLLOW_UP_DATE <= RSKMASTER.COVER_EFF_END_DATE
)
AND RSKMASTER.COVER_RISK_ID = RSKADDR.RISK_ID
)
Note : I have not tested query as no schema available.

mySQL update a value

Modified some stuff from my pic so you guys can understand it
I have this database. I am trying to update a value from a table based on another value from an another table.
I want to update the SUM from salary like this :
( sum = presence * 5 )
This is what I've been trying to use ( unsuccessful )
update table salary
set suma.salary = users.presence * 5
FROM salary INNER JOIN users1 INNER JOIN presence on id_salary = id_presence
I am not sure what to do, I'd appreciate some help, Thanks
In MySQL to UPDATE tables with a join you use this syntax:
UPDATE table1, table2
SET table1.column = some expression
WHERE table1.column = table2.column
That said, even with the updated picture, in your SQL you are mentioning columns that I cannot understand in which table are to be found. You also have an inner join between salariu and users1, with no join condition. Could you please clean up the question and make everything clear?
Assuming you are making the updates to the db structure you were talking about, then you can start working on this one maybe:
UPDATE salary, presence
SET salary.sum = SUM(presence.hours) * 5
WHERE presence.id = salary.id
AND <some filter on the month that depends on salary.date>
Another way, but I'm not sure it is supported in all RDBMS, would be something like this:
UPDATE salary
SET sum = (
SELECT SUM(presence.hours) * 5
FROM user, presence
WHERE presence.id = salary.id
AND <some filter on the month that depends on salary.date>
)

Right way to do this query

I have a table
form (
int id )
webformsystemflags ( int id, int formid int sysflagid )
sysflag ( int id, name char(10) )
form table is a table which has all the forms
webform is a table which has the forms which have flags applied to it. It has a foreign key formid which is id to the form table and sysflagid which is foreign key to the sys flag table
sys flag is the table which contains the flags. Lets say I have flags defined as 1,2,3
I can have forms which don't have all the flags applied to it, some may have 1, some may have 2 or some may have 3 applied to it or some may have none.
How can I find all the forms which have either flag 1 or flag 2 or flag 3 applied to it ?
This is a common trick to find EXCLUSION. The value I have below of "FlagYouAreExpectingTo_NOT_Exist" is explicitly the one you expect NOT to be there. Here's how it works.
Get every form and LEFT JOIN to the Web System Flags table WITH FINDING the matching form, and flag setting you DO NOT want. If it finds a valid entry for the form and flag, the "formid" in the (wsf) table will exist. So, we want all that DON'T exist, hence the closing WHERE wsf.formid is null.
It will be NULL for those where it is NOT already flagged.
select
f.ID
from
forms f
left join webformsystemflags wsf
on f.id = wsf.formid
AND wsf.sysflagid = FlagYouAreExpectingTo__NOT__Exist
where
wsf.formid is null
You could use a subquery:
SELECT * FROM `form` WHERE `id` IN (SELECT `formid` FROM `webformsystemflags`)
Careful with subqueries on huge databases though. You could do the same thing with joins but this is an easy solution that will get you going.
Or for all results that DO NOT have a certain flag:
SELECT * FROM `form` WHERE `id` IN (SELECT `formid` FROM `webformsystemflags` WHERE `sysflagid` != 1 OR `sysflagid` != 2)
or a join method:
SELECT f.*, r.`sysflagid` FROM `form` f LEFT JOIN `webformsystemflags` r ON r.`formid` = f.`id` WHERE r.`sysflagid` != null
will get you the forms and the related flags. However, it will not get ALL flags in one row if the form has multiple flags on it. That one you may need to do a concat on the flags, but this answer is already growing unnecessarily complex.
*LAST EDIT *
Ok nutsandbolts - You need to update your question cause the two of us have overshot ourselves in a number of different queries and it isn't really helping to come back saying it doesnt give the right results. The right results can easily be reached by simply examining the queries we have provided and using the general logic behind them to compose the query that is right for you.
So my last suggestion - you say you want a query that will return a form IF it has a certain flag applied to it AND that is does NOT have other flags applied to it.
Here it is supposing you wanted all forms with a flag of 1 AND NOT 2 or 3 or none:
SELECT f.*, r.`sysflagid` FROM `form` f LEFT JOIN `webformsystemflags` r ON r.`formid` = f.`id` WHERE r.`sysflagid` =1 AND r.`formid` NOT IN (SELECT `formid` FROM `webformsystemflags` WHERE `sysflagid` = 2 OR `sysflagid` = 3)
Because your webformsystemflags is relational this query will NOT return any forms that do not exist in the webformsystemflags table - so you don't need to consider null.
If this is not what you're looking for I strongly suggest you rewrite your question with absolute and perfect clarity on your needs cause after this one I'm out of this conversation. Much luck to you though. Have fun.
You can use an exists clause to pull records like this:
select a.*
from form a
where exists (select 1
from webformsystemflags
where formid = a.id
and sysflagid IN (1,2,3))
This won't give you the associated flag. If you want that:
select a.*, b.sysflagid
from form a
join (select formid, sysflagid
from webformsystemflags
where sysflagid in (1,2,3)) b
on a.id = b.formid
There are many different ways to solve this.
EDIT: By reading a comment on the other answer it seems the question was unclear. You want the result forms that only have ONE flag? i.e. the form has flag 1 but not 2 or 3?
edit2: if you really just want a true/false query pulling only the true (has a flag):
select a.*, b.sysflagid
from form a
join webformsystemflags b on a.id = b.formid
If you want forms without flags:
select a.*
from form a
left join webformsystemflags b on a.id = b.formid
where b.formid is null
edit3: Based on comment, forms with one flag and not one of the others:
select a.*
from form a
where exists (select 1 from webformsystemflags where formid = a.id and sysflagid = 1)
and (
not exists (select 1 from webformsystemflags where formid = a.id and sysflagid = 2)
or
not exists (select 1 from webformsystemflags where formid = a.id and sysflagid = 3)
)

How to efficiently code a query in mysql to AND tags?

I have a common problem to be sure. I'd like to make a query that finds an entity that has "n" tags. So in the simplest case, we find all the entities that have the tag "hey". In a more complex case, we find all the entities that have all the tags "hey", "hi" and "howdy".
It seems that I have to join to the tag table 3 times, and and thus create 3 different aliases. In the abstract case, I will have to make N different aliases. Is there a simpler way to achieve this?
The reason I am asking is that I need to write a query that not only does this for tags, but for a variety of things. So I am basically going to join NxM aliases... which is going to suck to write (and tune) the query.
Help?
EDIT:
Nevermind. I found the solution:
select distinct g.id, g.description
FROM gallery g
inner join gallery_to_tag g2t_0
on g2t_0.gallery_id = g.id
inner join tag t_0
on t_0.id = g2t_0.tag_id and t_0.term = 'hi'
inner join gallery_to_tag g2t_1
on g2t_1.gallery_id = g.id
inner join tag t_1
on t_1.id = g2t_1.tag_id and t_1.term = 'hey'
Is this what you might be looking for. Its hard to tell without out seeing your schema
CREATE TEMPORARY TABLE `checktags` (
`checkme` varchar(100) NOT NULL,
KEY `checkme` (`checkme`)
) ENGINE=MyISAM;
//INSERT ALL TAGS TO CHECK
SELECT * FROM `table`
LEFT JOIN `tags` ON `table_id` = `tag_table_id`
WHERE `tag` IN (SELECT `checkme` FROM `checktags`);
It is difficult to provide a precise answer without the schema, however, I believe you are asking how to determine that a given item has all the tags being queried. You could do something like
Select ..
From MainTable
Where Exists (
Select 1
From TagsTable
Where TagsTable.FK = MainTable.PK
And TagsTable.Tag In('hey','hi','howdy')
Having Count(*) = 3
)
If you want to avoid joining tables, you will need some subqueries. So for N tags, you would need N subqueries.
SELECT * FROM Entities
WHERE id IN (SELECT entity_id FROM tags WHERE tag = 'Hey')
AND id IN (SELECT entity_id FROM tags WHERE tag = 'Hello')
AND ...
And the same way, you can select entities that have any of some desired tags:
SELECT * FROM Entities
WHERE id IN (SELECT entity_id FROM tags WHERE tag = 'Hey')
OR id IN (SELECT entity_id FROM tags WHERE tag = 'Hello')
OR ...

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;