Extract a specific value from string (MYSQL) - mysql

I have the MISC column in a MYSQL table with the following value:
'PrimeCC_Stripe/XX_582130/PMethod=VISA/CardType=VISA/489930******8888/12/2020/TraceId=7182992'
another example:
'-1/error/PMethod=VISA/CardType=VISA/489930******8888/12/2020/TraceId=714291'
or
'Cancelled by PendingDepositCleanerJob. User didn't finish the payment process properly.'
Im am trying to extract the CARD number as another column in my query, here it should be: '489930******8888' or nothing if no card number is included in the MISC column.
What is the best option to extract this information?

A bit of string manipulation
drop table if exists t;
create table t (str varchar(100));
insert into t values
('PrimeCC_Stripe/XX_582130/PMethod=VISA/CardType=VISA/489930******8888/12/2020/TraceId=7182992'),
('Cancelled by PendingDepositCleanerJob. User didnt finish the payment process properly.'),
('123456******7891')
;
select str,
case when instr(str,'******') > 0 then
concat(
substring(str, instr(str,'******') - 6, 6),
'******',
substring(str, instr(str,'******') + 6, 4)
)
end
from t;
+----------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+
| PrimeCC_Stripe/XX_582130/PMethod=VISA/CardType=VISA/489930******8888/12/2020/TraceId=7182992 | 489930******8888 |
| Cancelled by PendingDepositCleanerJob. User didnt finish the payment process properly. | NULL |
| 123456******7891 | 123456******7891 |
+----------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+
3 rows in set (0.00 sec)
But it won't work if you have more than 1 occurrance of ****** or the number format differs (or is only a partial)

MySQL supports regular expressions, which we can use as a last resort.
SELECT REGEXP_SUBSTR(misc, '489930******8888') as CARD
The default value returned will be null. Hope this sorts you.

Related

Update Column by Parsing String from Other Column in MYSQL

I would like to create a new column in a MYSQL table based on the string values in an existing column.
My strategy is to first create an empty column and then update the values in the new column based on values in the existing column. However, I am stumbling on how to parse the string in order to extract the correct values.
The string is of the form 1.1.25. I want to extract the value before the first period and the value between the two periods and put these in new columns.
mytable
id|actsceneline|text
1 |1.1.1 |How are you.
1 |1.1.2 |Not bad. You?
To create the new empty column
ALTER TABLE mytable
ADD COLUMN act VARCHAR(6) NOT NULL,
ADD COLUMN scene VARCHAR(6) NOT NULL
To change the values in the new columns, I imagine I would do something like:
UPDATE mytable SET act = '1',scene = 1
And then use MYSQL string functions such as instr or substr or regex to extract the values and update the new columns as in.
UPDATE mytable SET act =
SELECT SUBSTR(actsceneline, 1, LOCATE('.', text)) FROM mytable
However, I'm struggling with how to extract the values from the string.
Thanks for any suggestions.
Try using SUBSTRING_INDEX():
UPDATE mytable
SET act = SUBSTRING_INDEX(actsceneline, '.', 1),
scene = SUBSTRING_INDEX(SUBSTRING_INDEX(actsceneline, '.', 2), '.', -1);
Result given your data:
mysql> select * from mytable;
+----+--------------+---------------+-----+-------+
| id | actsceneline | text | act | scene |
+----+--------------+---------------+-----+-------+
| 1 | 1.1.1 | How are you. | 1 | 1 |
| 2 | 1.1.2 | Not bad. You? | 1 | 1 |
+----+--------------+---------------+-----+-------+
Best way to create a select and what you want to update.
create a new table from your existing table.
"create table destinationtablename
select * from sourcetable;"
then work on your destinationtablename.
All work finished then check twice before update to original table or you can also take backup of your data by creating new table.

Process TEXT BLOBs fields in MySQL line by line

I have a MEDIUMTEXT blob in a table, which contains paths, separated by new line characters. I'd like to add a "/" to the begging of each line if it is not already there. Is there a way to write a query to do this with built-in procedures?
I suppose an alternative would be to write a Python script to get the field, convert to a List, process each line and update the record. There aren't that many records in the DB, so I can take the processing delay (if it doesn't lock the entire DB or table). About 8K+ rows.
Either way would be fine. If second option is recommended, do I need to know of specific locking schematics before getting into this -- as this would be run on a live prod DB (of course, I'd take a DB snapshot). But in place updates would be best to not have downtime.
Demo:
mysql> create table mytable (id int primary key, t text );
mysql> insert into mytable values (1, 'path1\npath2\npath3');
mysql> select * from mytable;
+----+-------------------+
| id | t |
+----+-------------------+
| 1 | path1
path2
path3 |
+----+-------------------+
1 row in set (0.00 sec)
mysql> update mytable set t = concat('/', replace(t, '\n', '\n/'));
mysql> select * from mytable;
+----+----------------------+
| id | t |
+----+----------------------+
| 1 | /path1
/path2
/path3 |
+----+----------------------+
However, I would strongly recommend to store each path on its own row, so you don't have to think about this. In SQL, each column should store one value per row, not a set of values.

Limiting a field's value in MySQL?

I am developing a program that uses a MySQL database to store data. I have a table (simplified here):
+---------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+------------------+------+-----+---------+----------------+
| dataId | int(10) unsigned | NO | PRI | NULL | auto_increment |
| someNum | tinyint(4) | NO | | 0 | |
+---------+------------------+------+-----+---------+----------------+
Now, I update the table with a query like this one.
UPDATE table SET someNum=someNum+2 WHERE dataId=78225;
The ID and the amount someNum changes come dynamically from code.
Now, what I'd like to do is limit someNum's value to between -3 and 3, particularly in that UPDATE. It isn't required, my software can handle it being outside that range, but the data would be clearer with that limit. If someNum+2 would be over 3, I'd just like to set it to 3.
Numeric MIN/MAX would make it easy:
UPDATE table SET someNum=MAX(-3,MIN(3,someNum+2)) WHERE dataId=78225;
I looked at the docs here, but there seems to be no MIN/MAX for numbers. MIN and MAX are found here, but they don't seem to be the right ones for this.
What would be the best way (if any) to implement such a limit in MySQL (not the code that calls MySQL)?
First way: use LEAST() and GREATEST():
UPDATE t SET someNum=GREATEST(-3,LEAST(3,someNum+2)) WHERE dataId=78225;
This is the most easy way because you'll store all the logic inside one UPDATE query.
Second way: create trigger:
DELIMITER //
CREATE TRIGGER catCheck BEFORE UPDATE ON t
FOR EACH ROW
BEGIN
IF NEW.someNum<-3 THEN
NEW.someNum=-3;
END IF;
IF NEW.someNum>3 THEN
NEW.someNum=3;
END IF;
END;//
DELIMITER ;
you can also replace IF with CASE - but I left that two separate constraints for -3 and 3. The benefits here is - that DBMS will handle your data by itself - and you'll be able to pass data as it is and do not worry about ranges. But - there's weakness too: while in first case you can just change query text to adjust desired range, in second case you'll have to re-create trigger again if you'll want to change that constraints (so, less flexibility).
Also you may want to check your data not only on UPDATE statements, but on INSERT too.
The appropriate functions in MySQL are greatest() and least(), not max()/min(). But, I think it is clearer with just a case statement:
UPDATE table
SET someNum = (case when someNum + 2 < -3 then -3
when someNum + 2 > 3 then 3
else someNum + 2
end)
WHERE dataId=78225;
use GREATEST and LEAST instead of MAX and MIN
You could also use a CASE WHEN
update table
set someNum = CASE WHEN SomeNum +2 > 3 THEN 3
WHEN SomeNum +2 < -3 THEN -3
ELSE someNum + 2
END)

mysql strange behavior when inserting data

When inserting data to mysql via the phpmyadmin page, or via python I've seen something I can't explain:
cur.execute("INSERT INTO 28AA507A0500009E (timestamp, temp) VALUES ('2014-01-04 15:36:30',24.44)")
cur.execute("INSERT INTO 28D91F7A050000D9 (timestamp, temp) VALUES ('2014-01-04 15:36:30',24.44)")
cur.execute("INSERT INTO `28012E7A050000F5` (timestamp, temp) VALUES ('2014-01-04 15:36:30',24.44)")
Notice the last entry with the ` around the table name.
The first 2 entry's work fine without the apostrophe.
I can also put the apostrophes around all the table names and it still works.
Why can I remote the apostrophes from the first 2 lines, and not the 3rd one?
The tables are all created equally.
Edit 1:
In due respect to the following comments:
Your explanation is not entirely accurate. There is no alias in
the INSERT statement. I think that the part of the identifier after
28012E7 is just discarded as MySQL tries convert the identifier to
an integer value! – ypercube
these are table names not column names. – Sly Raskal
Well, MySQL sure have discarded the part of the table name identifier. My intention was to bring forward how a identifier name was interpreted when the system could not find it in the list of accessible table names ( I chose column/expression names in my examples ). As the engine interpreted it as a valid number but not as an identifier to represent a table, it threw an exception.
And I chose SELECT to clarify, why the table identifier was rejected for not putting in back quotes. Because it represents a number, it can't be used as an identifier directly, but should be surrounded with back quotes.
MySQL allows to suffix aliases just after numerics, numeric expressions surrounded by braces or literals. To one's surprise, a space between them is optional.
In your case, 28012E7A050000F5 is a valid exponent form ( 28012E7 ) of number 280120000000 suffixed with alias A050000F5. And hence 28012E7A050000F5 can't be used as a column name without back quotes. See following observations:
mysql> -- select 28012E7 as A050000F5;
mysql> select 28012E7A050000F5;
+--------------+
| A050000F5 |
+--------------+
| 280120000000 |
+--------------+
1 row in set (0.00 sec)
Following are some valid examples:
mysql> -- select ( item_count * price ) as v from orders;
mysql> select ( item_count * price )v from orders;
+-----+
| v |
+-----+
| 999 |
+-----+
1 rows in set (0.30 sec)
mysql> -- select ( 3 * 2 ) as a, 'Ravinder' as name;
mysql> select ( 3 * 2 )a, 'Ravinder'name;
+---+----------+
| a | name |
+---+----------+
| 6 | Ravinder |
+---+----------+
1 row in set (0.00 sec)

Iterating through MySQL rows

I have a simple MySQL table made up of words and an associated number. The numbers are unique for each word. I want to find the first word whose index is larger than a given number. As an example:
-----------------------
| WORD: | F_INDEX: |
|---------------------|
| a | 5 |
| cat | 12 |
| bat | 4002 |
-----------------------
If I was given the number "9" I would want "cat" returned, as it is the first word whose index is larger than 9.
I know that I can get a full list of sorted rows by querying:
SELECT * FROM table_name ORDER BY f_index;
But would, instead, like to make a MySQL query that does this. (The confusion lies in the fact that I'm unsure as to how to keep track of the current row in my query). I know can loop with something like this:
CREATE PROCEDURE looper(desired_index INT)
BEGIN
DECLARE current_index int DEFAULT 0
// Loop here, setting current_index to whatever the next rows index is,
// then do a comparison to check it to our desired_index, breaking out
// if it is greater.
END;
Any help would be greatly appreciated.
Try this:
SELECT t.word
, t.f_index
FROM table_name t
WHERE t.f_index > 9
ORDER
BY t.f_index
LIMIT 1
It is much more efficient to have the database return the row you need, than it is to pull a whole bunch of rows and figure out which one you need.
For best performance of this query, you will want an index ON table_name (f_index,word).
Why don't you just use MYSQL statement to retrieve the first item you found from f_index where the f_index is greater than the value your pass in.
For example :
select word from table_name
where f_index > desired_index
order by f_index
limit 1