Mysql Update Table with Multiple Sub-Queries within same table - mysql

I am converting a column datetime format within the same table using multiple sub-queries. I am getting an error that my table does not exist.
Here is my query:
update mytable as t
set t.PO_Date = (
select DATE_FORMAT(STR_TO_DATE(PO_Date, '%m/%d/%Y'), '%Y-%m-%d')
from mytable as i
where i.Pri_ID = (select MAX(Pri_ID) from mytable)
);
I have tried changing the table alias multiple times, consistently get the error that the alias t does not exist.

If anyone stumbles across this in the future, here is what corrected the issue.
update mytable as t
set t.PO_Date = DATE_FORMAT(STR_TO_DATE(PO_Date, '%m/%d/%Y'), '%Y-%m-%d')
ORDER BY t.Pri_ID DESC LIMIT 1;

Related

SQL: How to update column with unique values

Below is a MySQL query:
SELECT
COUNT(*) AS counted, employer_group
FROM
employer_survey
GROUP BY employer_group
HAVING counted > 1;
Before I alter table definition for employer_group to unique, I need to create an UPDATE statement to CONCAT() the value of created_dt to employer_group so the alter table will not fail because of values.
How do I do this? I am unable to return id column because I am using GROUP BY and HAVING.
I should mention that I want the id column returned so I may use the above SELECT with an IN clause in my UPDATE statement. This may not be the best approach.
You can do this with join:
update employer_survey es join
(select es2.employer_group
from employer_survey es2
group by es2.employer_group
having count(*) > 1
) eg
on es.employer_group = eg.employer_group
set es.employer_group = concat_ws(' ', es.employer_group, es.created_dt);

Sql Server default value average of x rows

I have the following trigger running on MySQL:
CREATE DEFINER=`root`#`%` TRIGGER `before_insert` BEFORE INSERT ON `table` FOR EACH ROW SET
new.AVG_COLUMN1 = (SELECT avg(COLUMN1) FROM (SELECT COLUMN1 from table ORDER BY DateTimeCol DESC LIMIT 20) as COLUMN1_A),
new.AVG_COLUMN2 = (SELECT avg(COLUMN2) FROM (SELECT COLUMN2 from table ORDER BY DateTimeCol DESC LIMIT 20) as COLUMN2_A),
new.AVG_COLUMN3 = (SELECT avg(COLUMN3) FROM (SELECT COLUMN3 from table ORDER BY DateTimeCol DESC LIMIT 20) as COLUMN3_A)
Basically my goal here is to set a automatic, default value in the AVG_COLUMNx column, based on the last 20 entries in COLUMNx, whenever a new row is inserted. This is working fine in MySQL using the mentioned trigger.
I am in the process of migrating my project to Sql Server Express from MS, and I'm trying to do the same there. Does anyone have any good pointers as to how I could accomplish this? Using triggers, computed columns, etc?
Thanks for any input!
The logic would be different in SQL Server because it would be using inserted rather than new. Basically:
update t
set AVG_ROW1 = tt.avg1,
AVG_ROW2 = tt.avg2,
AVG_ROW3 = tt.avg3
from table t join
inserted i
on i.pk = t.pk outer apply
(select avg(Row1) as avg1, avg(Row2) as avg2, avg(Row3) as avg3
from (select top 20 t.*
from table t
order by DateTimeRow desc
) t
) tt;
You need some identifier(s) in the row to match the table to inserted. That is what pk stands for.

fixing duplicate fields in mysql table with update statement

I have inherited a table with a field "sku" with should be unique, but thanks to a failing sku-generating method is now littered with dozens of duplicates all around.
I need to quickly fix these duplicates (other parts of the application are failing when encountering these duplicate records) by running an update and appending the record ID to the SKU (which is a valid solution for the time being for this application).
I'm trying to run:
UPDATE
main_product_table
SET sku = CONCAT(sku, '-', CAST(product_id as CHAR) )
WHERE sku IN (
SELECT sku FROM main_product_table
GROUP BY sku
HAVING COUNT(*) > 1
);
But I receive:
You can't specify target table 'main_product_table' for update in FROM clause
Is there a way to accomplish the same? Is mysql complaining about me having main_product_table both in the update and in the subquery to get the duplicates?
Thanks!
Try this:
UPDATE
main_product_table
SET sku = CONCAT(sku, '-', CAST(product_id as CHAR) )
WHERE sku IN (
select * from ( SELECT sku FROM main_product_table
GROUP BY sku
HAVING COUNT(*) > 1) as p
);
Added table alias in inner query.

update with select and case statement in mysql

can anyone help me with this query what is going wrong in this query.
Query
update vcd_deals
set del_date='2015-09-17'
where case
when (
(select count(del_id)
from vcd_deals
where del_date=curdate()+1
and del_superSaver_deal=1)=0) then 1
else 0
end
order by del_date asc limit 3
I am getting the following error:
Error
You can't specify target table 'vcd_deals' for update in FROM clause.
Actually I want to update first three rows of vcd_deals table only when
(select count(del_id) from vcd_deals where del_date=curdate()+1 and del_superSaver_deal=1)=0.
please suggest is there any other way to do this.
Thanks
That is the problem with MySQL Parser. You can try Inner Join instead.
Otherwise you can enclose your subselect into a separate table like below:(not tested)
update vcd_deals
set del_date='2015-09-17'
where 0 = ( select Del_id_count
from (select count(del_id) as Del_id_count
from vcd_deals
where del_date=curdate()+1
and del_superSaver_deal=1) temp
)
order by del_date asc limit 3
You can try as per below-
UPDATE vcd_deals
SET del_date='2015-09-17'
WHERE del_date<>ADDDATE(CURDATE(), INTERVAL 1 DAY)
AND del_superSaver_deal<>1
ORDER BY del_date LIMIT 3;

how to set an array as a mysql user variable

I didn't expect to find this so difficult, but I'm trying to set a user variable in MySQL to contain an array of values. I have no clue how to do this so tried doing some research and was quite suprised to find no answer. I have tried:
SET #billable_types = ['client1','client2','client3'];
The reason being I would like to use the variable in the following statement later on:
SELECT sum((time_to_sec(timediff(tlg.time_stop, tlg.time_start))/3600)) as billable_hours
from mod_tmlog_time_log tlg, mod_tmlog_task_list mttl
where date(tlg.time_start) >= #time_start
and date(tlg.time_stop) <= #time_stop
and mttl.type IN (#billable_types)
and tlg.task_id = mttl.id
group by start_date
order by start_date desc;
Would be very grateful for help.
Fast forward a while, I ended up with the following quick and dirty solution which doesn't give me the flexibility of re-using the array elsewhere in the code but hey it's an unchargeable admin task so I don't want to spend any more time on it.
SELECT WEEKDAY(tlg.time_start) AS day_of_week, date(tlg.time_start) as start_date,
sum((time_to_sec(timediff(tlg.time_stop, tlg.time_start))/3600)) as billable_hours
from mod_tmlog_time_log tlg, mod_tmlog_task_list mttl
where date(tlg.time_start) >= #time_start
and date(tlg.time_stop) <= #time_stop
and mttl.type IN ('c1','c2','c3')
and tlg.task_id = mttl.id
group by start_date
order by start_date desc;
joostschouten seems to have found the most elegant solution (not tested it myself yet) but next time I'm writing something which calls for this I will remember to test it!
Just found the answer here: How to cycle with an array in MySQL?
set #billable_types = 'client1,client2,client3';
select * from mttl where find_in_set(mttl.type, #billable_types);
As Marc B mentioned, there is no array variable in MYSQL.
The alternative to find_in_set solution is to use SELECT with UNION to simulate the array:
SELECT billable_type FROM (
SELECT 'client1' AS billable_type UNION
SELECT 'client2' AS billable_type UNION
SELECT 'client3' AS billable_type) AS t
So your query will looks like that:
SELECT sum((time_to_sec(timediff(tlg.time_stop, tlg.time_start))/3600)) as billable_hours
from mod_tmlog_time_log tlg, mod_tmlog_task_list mttl
where date(tlg.time_start) >= #time_start
and date(tlg.time_stop) <= #time_stop
and mttl.type IN (
SELECT billable_type FROM (
SELECT 'client1' AS billable_type UNION
SELECT 'client2' AS billable_type UNION
SELECT 'client3' AS billable_type) AS t
)
and tlg.task_id = mttl.id
group by start_date
order by start_date desc;
If the user has the CREATE TABLE privilege, an array can be simulated by creating a temporary, single-column table. A value or values in the table can be retrieved with a SELECT statement. Temporary tables are dropped at the end of the session, but it's a good idea to explicitly drop them once they're no longer needed.
CREATE TEMPORARY TABLE billable_types (c VARCHAR(16));
INSERT INTO billable_types VALUES ('client1'), ('client2'), ('client3');
SELECT sum((time_to_sec(timediff(tlg.time_stop, tlg.time_start))/3600)) as billable_hours
from mod_tmlog_time_log tlg, mod_tmlog_task_list mttl
where date(tlg.time_start) >= #time_start
and date(tlg.time_stop) <= #time_stop
and mttl.type IN (SELECT * FROM billable_types)
and tlg.task_id = mttl.id
group by start_date
order by start_date desc;
DROP TABLE billable_types;