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.
Related
I'm moving a few boolean columns from the 1st row of a generic settings table into the 1st row of a website_settings table across a few MYSQL databases. I've created the new columns in my new table with a default false value.
I have a working query to copy data from the old table:
UPDATE website_settings
SET
dark_mode_enabled = (SELECT dark_mode_enabled FROM settings ORDER BY id LIMIT 1),
header_enabled = (SELECT header_enabled FROM settings ORDER BY id LIMIT 1),
footer_enabled = (SELECT footer_enabled FROM settings ORDER BY id LIMIT 1)
LIMIT 1;
However for my own knowledge, I'm curious if there is a more cleaner way to write this, perhaps without the repetitive select queries to the same table?
You could use a join:
UPDATE website_settings ws CROSS JOIN
(SELECT s.*
FROM settings s
ORDER id DESC
LIMIT 1
) s
SET ws.dark_mode_enabled = s.dark_mode_enabled,
ws.header_enabled = s.header_enabled,
ws.footer_enabled = s.footer_enabled
LIMIT 1;
Here's a solution that does not use JOIN:
INSERT INTO website_settings (id, dark_mode_enabled, header_enabled, footer_enabled)
SELECT id, dark_mode_enabled, header_enabled, footer_enabled
FROM settings ORDER BY id LIMIT 1
ON DUPLICATE KEY UPDATE
dark_mode_enabled = VALUES(dark_mode_enabled),
header_enabled = VALUES(header_enabled),
footer_enabled = VALUES(footer_enabled);
I'm having problems with creating MySQL trigger - I want to update column temp of last row of table avg_temp with an average from the last 144 records from
the temperature_C column in stats table. I am doing this through phpmyadmin before INSERT happens.
My code, hope it helps to explain what I want the code to do:
UPDATE avg_temp(`temp`)
SET (
SELECT `id`, AVG(`temperature_C`)
FROM `stats`
GROUP by `id`
LIMIT 144
)
ORDER BY id DESC
LIMIT 1
this however throws a syntax error.
If anyone could help me then that would be wonderful.
Yyou could use some subselect
and for the right avg result you should use a subselect for get 144 rows
update avg_temp
set temp = ( select avg(t1.temperature_C)
from (
SELECT id, temperature_C
FROM stats
ORDER BY id
LIMIT 144
) t1
)
where id = your_id
This should be the right syntax for MySQL update to calculate the average of last 144 values, using a sublquery:
UPDATE avg_temp SET `temp` = AVG(
(
SELECT `temperature_C`
ORDER BY id DESC
LIMIT 144
)
)
ORDER BY id DESC
LIMIT 1
You appear to want:
UPDATE avg_temp
SET `temp` = (SELECT AVG(temperature_C)
FROM (SELECT s.temperature_C
FROM stats s
ORDER BY id DESC
LIMIT 144
) s
)
ORDER BY id DESC
LIMIT 1;
However, I would be very suspicious about your desire to do this, especially in a trigger. Normally, the effect that a trigger has depends on the data in the records passed to the trigger. I cannot think of a situation where I have ever written a trigger (other than for testing or informational purposes) that does not reference such data.
You appear to be using the insert as a timing mechanism. Instead, you might just want to create a view that returns the average from the last 144 rows (or from the last 144 rows but one).
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;
Are there any solutions on MySQL script to filter the results with specific interval number.
For example, if I have 100,000 records in database and I'd like to get only the record number 1000, 2000, 3000, etc. (step by 1000).
I could do this on server side script by getting the entire results (e.g. 100,000) and use syntax like:
for($i=0, $i <= 100,000, $i = $i+1000) $filterResult[] = $record[$i];
However, as you may see, it would pull stress to the system as 100,000 records will need to generated first.
Are there any solutions that could complete from database script? Please note that, primary key may not start with 1 - 100,000 as the results based on some condition in where clause.
Your help would be really appreciated.
You can do:
SELECT *
FROM tbl
WHERE id % 1000 = 0
But it seems like you don't want to rely on the primary key value, but rather the row ranking of a result set.
In that case, you can do:
SELECT *
FROM (
SELECT *, #rn:=#rn+1 AS rank
FROM tbl
CROSS JOIN (SELECT #rn:=0) var_init
WHERE column1 = value AND
column2 = value
) a
WHERE a.rank % 1000 = 0
Where column1 = value AND column2 = value is just a placeholder for whatever filtration you're doing in your query.
I have a table which has a field sort_id. In this field there are numbers from 1 to n, that define the order of the data sets.
Now I want to delete some elements and afterwards I want to reorder the table. Therefore I need a query that "finds" the gaps and changes the sort_id field according to the modifications.
Sure, I could do something like this:
SELECT sort_id FROM table WHERE id = 5
Then save the sort_id and afterwards:
DELETE FROM table WHERE id = 5
UPDATE table SET sort_id = sort_id - 1 WHERE sort_id > {id from above}
But I'd like to do the reordering process in one step.
Mladen and Arvo have good ideas, but unfortunately in MySQL you can't SELECT and UPDATE the same table in the same statement (even in a subquery). This is a known limitation of MySQL.
Here's a solution that uses MySQL user variables:
SET #i := 0;
UPDATE mytable
SET sort_id = (#i := #i + 1)
ORDER BY sort_id;
For what it's worth, I wouldn't bother doing this anyway. If your sort_id is used only for sorting and not as a kind of "row number," then the rows are still in sorted order after you delete the row where id=6. The values don't necessarily have to be consecutive for sorting.
for sql server 2005:
this is how you get the new sequence:
SELECT row_number() over(order by sort_id) as RN
FROM table
updating the table means you should join that select to your update:
update t1
set sort_id = t2.RN
FROM table t1
join (SELECT row_number() over(order by sort_id) as RN FROM table) t2
on t1.UniqueId = t2.UniqueId
I don't know MySQL syntax variations and cannot test query live, but something like next should give you at least an idea:
update table t1
set sort_id = (select count * from table t2 where t2.sort_id <= t1.sort_id)