I have a sequence of 20 numbers from 0 to 2, I want to compare this string with other sequences saved in my database, the problem is that the lenght of the strings saved on the database fluctuates.Also the comparison needs to be done from the end to the start.
Example of what I want:
20 digits string:
'1,1,2,1,2,1,0,1,2,1,2,1,0,1,2,1,1,1,2,1'
couple of strings saved in the database:
1 - '1,1,2,1'
2 - '2,1,2,2,2,2'
3 - '2,1'
4 - '1,1,2,1,2,1'
In this case the query would return the 1 and 3 only
create table mytable ( s varchar(60) );
insert into mytable values
('1,1,2,1'),
('2,1,2,2,2,2'),
('2,1'),
('1,1,2,1,2,1');
set #x = '1,1,2,1,2,1,0,1,2,1,2,1,0,1,2,1,1,1,2,1';
select s from mytable
where right(#x, length(s)) = s;
Output:
s
1,1,2,1
2,1
Fiddle: https://www.db-fiddle.com/f/r5m2hPbnmUu5VQfYvMVtir/0
You could use a LIKE trick here. For example, to check for the first string 1,1,2,1:
SELECT *
FROM yourTable
WHERE ',1,1,2,1,2,1,0,1,2,1,2,1,0,1,2,1,1,1,2,1,' LIKE '%,1,1,2,1,%';
The question was asked earlier but it appears from the discussion that the question had insufficient input to determine output. I have a similar problem. I will try to come up with some spec/logic.
I have a table with timestamp data that I have converted to unix_timestamp.
id
p_value
ceil(unix_timestamp(updated_at))
3
300
1653549602
7
300
1653549902
11
300
1653550202
15
300
1653550502
19
300
1653550802
23
1200
1653551102
27
1300
1653551402
31
1300
1653551402
35
1300
1653551702
39
1300
1653551702
These are 10 rows with roughly equidistant times. And suppose I want N roughly equidistant rows. So I follow these steps for N = 3,
divide the set by N - 1 i.e. (max - min)/(N - 1). I get 2100/2 = 1050
pick first row (with timestamp 1653549602) save as last
then pick (the first row with updated_at > (last + 1050)) i.e. with timestamp 1653550802 and save as last.
repeat step 3 until it crosses max; use max as last sample. i.e. with timestamp 1653551702.
I have this rough algorithm but how to write this in SQL.
Sample output:
id
p_value
ceil(unix_timestamp(updated_at))
3
300
1653549602
19
300
1653550802
39
1300
1653551702
I just given a try. Check this can help you. Just try the function that i given.
Your '1653549602' is not the last. It is the first record that saved to table.
1653549602 = 2022-05-26 07:20:02 <-- first record 7:20
and 1653551702 = 2022-05-26 07:55:02. <-- last record at 7:55
Also i feel there is a logic issue in your described scenario while selecting the last record. Because 1653550802 + 1050 mean real time is --> "2022-05-26 07:57:32". So you cannot select "1653551702" as the record through this condition updated_at > (last + 1050)). 1653551702 = "2022-05-26 07:55:02". So your condition not valid with it.
1653550802 + 1050 = 1653551852 which is "2022-05-26 07:57:32"
So this condition is not working [ "2022-05-26 07:55:02" > "2022-05-26 07:57:32" ]
[Start from here]
Anyway i did a procedure for you. It give you a some idea to your requirement and also it will help you to go forward.
I used the same table structure as
create table `equidistants` (
`pid` int (11),
`id` int (11),
`p_value` int (11),
`unix_time` bigint (20)
);
pid is a column that i created as PK for me
Table name i used : equidistants
Created Below function
DROP PROCEDURE IF EXISTS my_proc_equidistant;
DELIMITER $$
CREATE PROCEDURE my_proc_equidistant(IN n_value INT)
BEGIN
DECLARE i_val INT; -- Variable for (max - min)/(N - 1)
DECLARE i_loop INT DEFAULT 0;
DECLARE i_Selected_unixTime INT;
SET n_value = n_value -1;
-- Handle the devided by 0 error
IF n_value = 0 THEN
SET n_value = 1 ;
END IF;
-- (max - min)/(N - 1) calculate here
SELECT (MAX(unix_time) - MIN(unix_time))/(n_value)
INTO i_val FROM `equidistants` ;
-- Get the first updated value. Not the last one
SELECT unix_time INTO i_Selected_unixTime
FROM `equidistants` ORDER BY unix_time ASC LIMIT 1;
-- Temporary table to keep your Data
DROP TABLE IF EXISTS temp_equidistants;
-- Inser the latest record from the data set.
CREATE TEMPORARY TABLE temp_equidistants
SELECT * FROM equidistants ORDER BY unix_time ASC LIMIT 1;
-- Start the loop based on the given N value
WHILE i_loop < n_value DO
-- Insert the next selected record into the temp table base on the [last selected unix time + i_val]
INSERT INTO temp_equidistants
SELECT * FROM equidistants WHERE unix_time > i_Selected_unixTime + i_val ORDER BY unix_time ASC LIMIT 1;
-- identify the next unix time
SELECT unix_time INTO i_Selected_unixTime FROM equidistants WHERE unix_time > i_Selected_unixTime + i_val ORDER BY unix_time ASC LIMIT 1;
SET i_loop=i_loop+1;
END WHILE;
-- Execute the result you need
SELECT * FROM temp_equidistants;
-- Drop the Temp table
DROP TABLE IF EXISTS temp_equidistants;
END$$
DELIMITER ;
Hope you can do something with this function by modifying some areas.
Result that i got
Note: 3rd record missing due to the condition miss match that i explain at the top
Here i used "ASC" for ther order by clause. You can change it to descending and you can run it other way-around.
My problem is that I try to calculate a moving average over some values from my table (one avg value for each row). It actually works but if it comes to gaps such as id[20,18,17] or date[2018-05-11,2018-05-9,2018-05-8] the calculation becomes wrong. I´m looking for a way to use a specific number of next rows to prevent this to happen.
The table contains id (auto_increment), date and close (Float).
This is my code:
CREATE DEFINER=`root`#`localhost` PROCEDURE `moving_avg`(IN periode INT)
NO SQL
BEGIN
select hist_ask.id, hist_ask.date, hist_ask.close, round(avg(past.close),2) as mavg
from hist_ask
join hist_ask as past
on past.id between hist_ask.id - (periode-1) and hist_ask.id
group by hist_ask.id, hist_ask.close
ORDER BY hist_ask.id DESC
LIMIT 10;
END
The table I use looks like this
id , date , close
20 , 2018-10-13 , 12086.5
19 , 2018-10-12 , 12002.2
17 , 2018-10-11 , 12007.0
and so on
The output looks like this:
The output I get from the query
Thanks in advance!
I finaly make it work using a temporary table.
I can now give two parameters to the procedure:
periode: the periode the moving average is calculated with
_limit: limits the result set
Important for performance is the
ALTER TABLE temp
ENGINE=MyISAM;
statement because it reduces the execution time significantly. For example when proccessing 2000 rows it needs about 0.5 seconds, before adding it it needed about 6 seconds
Thats the code:
CREATE DEFINER=`root`#`localhost` PROCEDURE `moving_avg`(IN periode INT, IN _limit INT)
NO SQL
BEGIN
DECLARE a FLOAT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE count_limit INT DEFAULT 0;
SET #rn=0;
CREATE TEMPORARY TABLE IF NOT EXISTS temp (
SELECT
#rn:=#rn+1 AS pri_id,
date,
close , a AS
mavg
FROM hist_ask);
ALTER TABLE temp
ENGINE=MyISAM;
SET i=(SELECT pri_id FROM temp ORDER by pri_id DESC LIMIT 1);
SET count_limit= (i-_limit)-periode;
WHILE i>count_limit DO
SET a= (SELECT avg(close) FROM temp WHERE pri_id BETWEEN i-(periode-1) AND i);
UPDATE temp SET mavg=a WHERE pri_id=i;
SET i=i-1;
END WHILE;
SELECT pri_id,date,close,round(mavg,2) AS mavg FROM temp ORDER BY pri_id DESC LIMIT _limit;
DROP TABLE temp;
END
The result looks like that:
CALL `moving_avg`(3,5)
pri_id, date, close, mavg
1999 2018-09-13 12086.6 12032.03
1998 2018-09-11 12002.2 11983.47
1997 2018-09-10 12007.3 11976.53
1996 2018-09-07 11940.9 11993.80
1995 2018-09-06 11981.4 12089.23
5 row(s) returned 0.047 sec / 0.000 sec
How can I add a column to a table with the number 1 to 4 based on each row meeting certain criteria?
Suppose I have a table:
Like this
with two columns (ID) and (RESULTS) and that I want to add a third column called (SCORE).
I want to give a score (between 1 and 4) to each row in my column based on whether the numbers in column (RESULTS) meet certain criteria.
If the RESULT is negative, I want to give it a score of 1,
If the RESULT is less than 30, a score of 2,
less than 100 a score of 3
and greater than 100 a score of 4
I have tried using the CASE statement but cannot seem to get it to work;
I searched on the topics about constraints but they always seem to have two arguments - I need 4
I have updated the answer, as more details were given in the question (and to fix some errors)
Please remember to change table_name and trigger_name to appropriate names :).
SOLUTION:
You should first of all add the third column to the table
ALTER TABLE table_name ADD COLUMN SCORE INT;
You should add the trigger to set the SCORE for the new rows:
DELIMITER $$
CREATE TRIGGER trigger_name BEFORE INSERT ON table_name
FOR EACH ROW
BEGIN
SET NEW.SCORE = CASE WHEN NEW.RESULTS < 0 THEN 1 WHEN NEW.RESULTS < 30 THEN 2 WHEN NEW.RESULTS < 100 THEN 3 ELSE 4 END;
END$$
DELIMITER ;
And you should initialize SCORE values for rows existing in the table
UPDATE table_name t
SET t.SCORE = CASE WHEN t.RESULTS < 0 THEN 1 WHEN t.RESULTS < 30 THEN 2 WHEN t.RESULTS < 100 THEN 3 ELSE 4 END;
Hope it helps.
Im looking to run a query in phpmyadmin (mysql) that wil check a table for a specific value at a specific postion in a string, i'm new to mysql and this is what i've tried but there's a syntax issue. I'm looking to find the value "1" at position 5 and display all those users that possess this.
SELECT*
FROM`user`
WHERE`options`LOCATE(`options`,1,5)
LIMIT 0 , 30
regards,
Silo
Locate is for finding a value ANYWHERE in a string. You want a specific location only, so use substr() instead:
SELECT *
FROM user
WHERE substr(options, 5, 1) = '1'
You could try
SELECT*
FROM`user`
WHERE options LIKE '____1%'
LIMIT 0 , 30
Another ;)
SELECT *
FROM user
WHERE instr(options, '1') = 5
Duh .. Well won't work since it only returns the first occurnace :$ But CHECK the reference,
SQLFIDDLE using Locate()
Sample data:
COL1 COL2
G11 112
G11-1 0
G11-2 2
G12-2 111
Query1:
-- to check on varchar columns
SELECT *
FROM tablex
where locate('1',col1,5)
;
Results on varchar:
COL1 COL2
G11-1 0
Query 2:
-- to check on int columns
SELECT *
FROM tablex
where locate(1,col2,2)
;
Results on int:
COL1 COL2
G11 112
G12-2 111