MySQL copy multiple row-entries to another position - mysql

I want to copy a big amount of values from a specific row and specific date to the same row, but another date. I have tried it with the SQL UPDATE SET function and two different tables, but the SET will only use the first value and copy it to the right date-columns.
At this picture you see the results:
And this is one of the queries I tried:
UPDATE Test_tab t
SET t.testValue =
(SELECT testValue
FROM Test_tab
WHERE testDate > '2015-10-01' AND testDate < '2015-10-31'
LIMIT 1)
WHERE t.testDate > '2015-11-01' AND t.testDate < '2015-11-31'

you may try like this
CREATE PROCEDURE your_proc()
BEGIN
DECLARE cont INTEGER;
SET cont = 0;
SET max = SELECT count(*) FROM test_tab;
WHILE cont < max DO
UPDATE Test_tab t SET t.testValue =(SELECT testValue FROM Test_tab WHERE testDate >'2015-10-01' AND testDate <'2015-10-31'
LIMIT cont, 1) WHERE t.testDate > '2015-11-01' AND t.testDate < '2015-11-31'
SET cont = cont + 1;
END WHILE;
END;

Related

How to increment using a SQL variable within an update statement

I am trying to increment a column using an #count variable in SQL. I have tried multiple attempts that I will list below that all result in:
Error Code: 1064
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ...
First was:
SET #count = 65;
UPDATE table t
SET t.Revision = CHAR(#count)
, #count = #count + 1
WHERE t.hidden = 0;
I am trying to increment every row currently as a proof of concept that this works.
Second was:
DECLARE t CURSOR FOR
SELECT * FROM table
WHERE t.hidden = 0;
OPEN t;
FETCH NEXT FROM t;
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE table t2 SET t2.Revision = 'D' WHERE t2.id1 = t.id1 AND t2.id2 = t.id2;
END;
END
CLOSE t;
DEALLOCATE t;
Once again I am just trying to see if I can set a standard variable using a while loop before I implement incrementing as a proof of concept that it works.
I am not sure why either of these attempts is failing but any help would be appreciated.
Here is how your first example should work(inside of some loop):
first you set your count value, then you update
SET #count = 65;
UPDATE CUSTOMER t
SET t.LName = CONVERT(#count, char)
where t.FName = 'a';
...and then increase that count and you update again...
set #count = #count + 1;
UPDATE CUSTOMER t
SET t.LName = CONVERT(#count, char)
where t.FName = 'a';
But that should be in a procedure for example.
Here is the DEMO. I it a small example and I hope you will find it helpful.
Cheers!
You can try the following solution:
SET #count = 64; -- so the first increment is 65 (starting on A).
UPDATE table_name t
SET t.Revision = CHAR(#count:=#count+1)
WHERE t.hidden = 0;
or (shorter):
UPDATE table_name t, (SELECT #count:=64) t2
SET t.Revision = CHAR(#count:=#count+1)
WHERE t.Hidden = 0;
demo on dbfiddle.uk

MySQL Variable Assignment via Procedure Not Working Correctly

In the code below, I'm trying go through the results of endDateTable row by row, comparing the current row's endDate to the previous row's endDate. If there has been any change since the previous, we increment #revisionNum. However, upon populating the new table, all of the #revisionNum entries are 0. What am I doing wrong?
NOTE: I'm using prepared statements in this manner since doing a straightforward SELECT into a variable gives a syntax error due to the LIMIT clause not allowing a variable in our version of MySQL.
BEGIN
DECLARE _currentEndDate DATETIME DEFAULT now();
DECLARE _priorEndDate DATETIME DEFAULT now();
SET #ResultsCount = (SELECT COUNT(*) FROM mainTable);
SET #j = 0;
WHILE #j < #ResultsCount DO
SET #revisionNum = 0;
/*CURRENT END DATE*/
SET #appResultQueryCurrent = CONCAT('
SELECT
end_date
INTO _currentEndDate
FROM endDateTable
LIMIT ', #j, ', 1'
);
PREPARE currentQueryStmt FROM #appResultQueryCurrent;
EXECUTE currentQueryStmt;
/*PREVIOUS END DATE*/
SET #appResultQueryPrior = CONCAT('
SELECT
end_date
INTO _priorAppEndDate
FROM endDateTable
LIMIT ', IF(#j = 0, 0, #j - 1), ', 1'
);
PREPARE priorQueryStmt FROM #appResultQueryPrior;
EXECUTE priorQueryStmt;
SET #revisionNum = IF(
#j = 0 OR (_currentEndDate = _priorEndDate),
#revisionNum,
IF(
_currentEndDate != _priorEndDate,
#revisionNum + 1,
#revisionNum
)
);
INSERT INTO finalTable (RevisionNum)
SELECT
#revisionNum AS RevisionNum
FROM endDateTable;
SET #j = #j +1;
END WHILE;
END $$
You don't need a loop, you can use INSERT INTO ... SELECT ..., incrementing the variable in the select query.
You also need an ORDER BY criteria to specify how to order the rows when comparing one row to the previous row.
INSERT INTO finalTable (RevisionNum, otherColumn)
SELECT revision, otherColumn
FROM (
SELECT IF(end_date = #prev_end_date, #revision, #revision := #revision + 1) AS revision,
#prev_end_date := end_date,
otherColumn
FROM endDateTable
CROSS JOIN (SELECT #prev_end_date := NULL, #revision := -1) AS vars
ORDER BY id) AS x
DEMO
The offset value in the LIMIT clause is tenuous without an ORDER BY.
Without an ORDER BY clause, MySQL is free to return results in any sequence.
There is no guarantee that LIMIT 41,1 will return the row before LIMIT 42,1, or that it won't return the exact same row as LIMIT 13,1 did.
(A table in a relational database represents an unordered set of tuples, there is no guaranteed "order" or rows in a table.)
But just adding ORDER BY to the queries isn't enough to fix the Rube-Goldberg-esque rigmarole.
In the code shown, it looks like each time through the loop, we're inserting a copy of endDateTable into finalTable. If that's 1,000 rows in endDateTable, we're going to get 1,000,000 rows (1,000 x 1,000) inserted into finalTable. Not at all clear why we need so many copies.
Given the code shown, it's not clear what the objective is. Looks like we are conditionally incrementing revisionNum, the end result of which is the highest revision num. Just guessing here.
If there is some kind of requirement to do this in a LOOP construct, within a procedure, I'd think we'd do a cursor loop. And we can use procedure variables vs user-defined variables.
Something along these lines:
BEGIN
DECLARE ld_current_end_date DATETIME;
DECLARE ld_prior_end_date DATETIME;
DECLARE li_done INT;
DECLARE li_revision_num INT;
DECLARE lcsr_end_date CURSOR FOR SELECT t.end_date FROM `endDateTable` t ORDER BY NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET li_done = TRUE;
SET li_done = FALSE;
SET li_revision_num = 0;
OPEN lcsr_end_date;
FETCH lcsr_end_date INTO ld_current_end_date;
SET ld_prior_end_date = ld_current_end_date;
WHILE NOT li_done DO
SET li_revision_num = li_revision_num + IF( ld_current_end_date <=> ld_prior_end_date ,0,1);
SET ld_prior_end_date := ld_current_end_date;
FETCH lcsr_end_date INTO ld_current_end_date;
END WHILE;
CLOSE lcsr_end_date;
INSERT INTO `finalTable` (revisionnum) VALUES (li_revision_num);
END $$
Note the "order by" clause on the SELECT, its not clear what the rows should be ordered on, so we're using a literal as a placeholder.
As the end result, we insert a single row into finalTable.
Again, it's not clear what the code in the question is supposed to achieve, but doing a cursor loop across ordered rows would be much more efficient than a bazillion dynamic SQL executions fetching individual rows.

MySQL Find how many days it needed to get the highest positive progression

Data set:
CREATE TABLE TEMPERATURES (
CREATED DATE,
ACTUAL_TEMPERATURE FLOAT
);
INSERT INTO TEMPERATURES VALUES
('20170101',12.2),
('20170103',10.1),
('20170112',14.2),
('20170115',12.5),
('20170205',20.8),
('20170122',16.7),
('20170123',7.8),
('20170130',12.5),
('20170201',13.7),
('20170302',14.8),
('20170313',11.1),
('20170414',12.0),
('20170525',10.4);
and SQL FIDDLE of same: http://sqlfiddle.com/#!9/9b11e
I just want to find how many days it needed to get the highest positive progression ?
The result I would like to have is :
20170103 10.1
20170205 20.8
Be careful, I don't want to have :
20170123 7.8
20170302 14.8
I tried many requests and many ways and no succeed.
Is it possible and if yes, how ?
Thanks for your help.
Based on your description of the problem, I think you want:
select grp, min(created), max(created),
min(actual_temperature), max(actual_temperature)
from (select t.*,
(#grp := #grp + coalesce(prev_at > actual_temperature, 0)) as grp
from (select t.*,
(select t2.actual_temperature
from temperatures t2
where t2.created < t.created
order by t2.created desc
limit 1
) as prev_at
from temperatures t
order by created
) t cross join
(select #grp := 0) params
) t
group by grp
order by max(actual_temperature) - min(actual_temperature) desc
limit 1;
This identifies streaks of increasing temperatures. It then returns the streak with the highest difference between the maximum and minimum.
This is how I interpret your question, although your results do not fully conform to this interpretation.
Maybe you can use a function, something like this (you still need to adjust the edge cases):
DELIMITER $$
CREATE FUNCTION find_progressive(startingDate DATE)
RETURNS VARCHAR(100) DETERMINISTIC
BEGIN
DECLARE risultato int default 0;
/* current cursor*/
DECLARE curr_data DATE;
DECLARE curr_temp FLOAT;
/*current best*/
DECLARE max_temp FLOAT;
DECLARE min_temp FLOAT;
DECLARE max_created DATE;
DECLARE min_created DATE;
/* absolute best*/
DECLARE best_max_created DATE;
DECLARE best_min_created DATE;
DECLARE best_max_temp FLOAT;
DECLARE best_min_temp FLOAT;
DECLARE temptemp FLOAT;
DECLARE tempcreated DATE;
DECLARE done INTEGER DEFAULT 0;
DECLARE cursore_temperature CURSOR FOR
SELECT
created,
ACTUAL_TEMPERATURE
FROM temperatures
WHERE created > startingDate
ORDER BY created;
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET done = 1;
SELECT
created,
ACTUAL_TEMPERATURE
INTO
min_created,
min_temp
FROM temperatures
ORDER BY created
LIMIT 1;
SELECT
created,
ACTUAL_TEMPERATURE
INTO
max_created,
max_temp
FROM temperatures
WHERE created > min_created
ORDER BY created
LIMIT 1;
IF min_temp > max_temp
THEN
set temptemp = min_temp;
set tempcreated =max_created;
set min_temp = max_temp;
set min_created = max_created;
set max_temp = temptemp;
set max_created = tempcreated;
END IF;
set best_max_temp = max_temp;
set best_min_temp = min_temp;
set best_max_created = max_created;
set best_min_created = min_created;
OPEN cursore_temperature;
read_loop: LOOP
FETCH cursore_temperature
INTO curr_data, curr_temp;
IF done = 1
THEN
LEAVE read_loop;
END IF;
IF curr_temp < min_temp
THEN
IF (max_temp - min_temp) > ( best_max_temp - best_min_temp)
THEN
set best_max_temp = max_temp;
set best_max_created = max_created;
set best_min_temp = min_temp;
set best_min_created = min_created;
END IF;
set min_temp = curr_temp;
set min_created = curr_data;
set max_temp = curr_temp;
set max_created = curr_data;
END IF;
IF curr_temp > max_temp
THEN
set max_temp = curr_temp;
set max_created = curr_data;
END IF;
END LOOP;
CLOSE cursore_temperature;
RETURN CONCAT_WS(' | ', best_min_created, best_min_temp, best_max_created, best_max_temp);
END$$
DELIMITER ;
and you call it like
select find_progressive('2017-01-03');
and the result is 2017-01-03 | 10.1 | 2017-02-05 | 20.8
anyway I guess it is not the best talking about optimization.

How to simplfy this query?

Query 1:
SET #count = 0;
UPDATE a_daily_copy_copy
SET a_daily_copy_copy.Cummulative_Target = #count:= target + #count
where a_daily_copy_copy.Site_id = 1
and a_daily_copy_copy.Year=4
and a_daily_copy_copy.Billing_cycle=1
ORDER BY date
Query 2: Modified the a_daily_copy_copy.Billing_cycle=2
SET #count = 0;
UPDATE a_daily_copy_copy
SET a_daily_copy_copy.Cummulative_Target = #count:= target + #count
where a_daily_copy_copy.Site_id = 1
and a_daily_copy_copy.Year=4
and a_daily_copy_copy.Billing_cycle=2
ORDER BY date
I'm a beginner and as of now I'm running the query every time manually by editing the query 1 , and I know both queries can be consolidated into a single query.
I tried solving with Group by function but couldnt come up with Please help me.
Have screened the table:
Looks to me that you can just do:
SET #count = 0;
UPDATE a_daily_copy_copy
SET a_daily_copy_copy.Cummulative_Target = #count:= target + #count where a_daily_copy_copy.Site_id = 1 and a_daily_copy_copy.Year=4 and a_daily_copy_copy.Billing_cycle IN (1, 2)
ORDER BY date
...unless I've missed a difference between the two queries other than the billing cycle.

Finding min and max value of the table in a constant time

I have a table which contains relative large data,
so that it takes too long for the statements below:
SELECT MIN(column) FROM table WHERE ...
SELECT MAX(column) FROM table WHERE ...
I tried index the column, but the performance still does not suffice my need.
I also thought of caching min and max value in another table by using trigger or event.
But my MySQL version is 5.0.51a which requires SUPER privilege for trigger and does not support event.
It is IMPOSSIBLE for me to have SUPER privilege or to upgrade MySQL.
(If possible, then no need to ask!)
How to solve this problem just inside MySQL?
That is, without the help of OS.
If your column is indexed, you should find min(column) near instantly, because that is the first value MySQL will find.
Same goes for max(column) on an indexed column.
If you cannot add an index for some reason the following triggers will cache the MIN and MAX value in a separate table.
Note that TRUE = 1 and FALSE = 0.
DELIMITER $$
CREATE TRIGGER ai_table1_each AFTER INSERT ON table1 FOR EACH ROW
BEGIN
UPDATE db_info i
SET i.minimum = LEAST(i.minimum, NEW.col)
,i.maximum = GREATEST(i.maximum, NEW.col)
,i.min_count = (i.min_count * (new.col < i.minumum))
+ (i.minimum = new.col) + (i.minimum < new.col)
,i.max_count = (i.max_count * (new.col > i.maximum))
+ (i.maximum = new.col) + (new.col > i.maximum)
WHERE i.tablename = 'table1';
END $$
CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
DECLARE new_min_count INTEGER;
DECLARE new_max_count INTEGER;
UPDATE db_info i
SET i.min_count = i.min_count - (i.minimum = old.col)
,i.max_count = i.max_count - (i.maximum = old.col)
WHERE i.tablename = 'table1';
SELECT i.min_count INTO new_min_count, i.max_count INTO new_max_count
FROM db_info i
WHERE i.tablename = 'table1';
IF new_max_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MAX(col) as new_max FROM table1) m
SET i.max_count = 1
,i.maximum = m.new_max;
END IF;
IF new_min_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MIN(col) as new_min FROM table1) m
SET i.min_count = 1
,i.minimum = m.new_min;
END IF;
END $$
DELIMITER ;
The after update trigger will be some mix of the insert and delete triggers.