I have a scenerio. I have a field called card_no. Some entries are blank. We don't want to handle that. Some have 16 digit integers and some have the proper data I need. What I need is that I want to select all the records that are 16 digits integer and replace them with xxxxxxxxxxxxLAST4DIGITS (The credit card format). Can i do that via mysql?
You don't have to SELECT the rows.
You can apply an UPDATE to a subset of rows matching a condition.
UPDATE scenario
SET card_no = CONCAT('xxxxxxxxxxxx', RIGHT(card_no, 4))
WHERE LENGTH(card_no) = 16
Yes. But you have to have this field as char(4) as you might want to store a number 0002 as 0002 not just 2. So I would first update the datatype of the field and then update the field entries as desired.
-- update datatype
alter table `cards`
modify column `card_no` char(4);
-- update values in card_no
update `cards`
set `card_no` = LPAD(if(length(`card_no`) > 4, substr(`card_no`, -4, 4), `card_no`), 4, '0')
where `card_no` is not null;
The 'xxx' append operation you can do at application code side or else it will take extra memory in db.
Related
It's been my first question to this website, I'm sorry if I used any wrong keywords. I have been with one problem from quite a few days.
The Problem is, I have a MYSQL table named property where I wanted to add a ref number which will be a unique 6 digit non incremental number so I alter the table to add a new column named property_ref which has default value as 1.
ALTER TABLE property ADD uniqueIdentifier INT DEFAULT (1) ;
Then I write a script to first generate a number then checking it to db if exist or not and If not exist then update the row with the random number
Here is the snippet I tried,
with cte as (
select subIdentifier, id from (
SELECT id, LPAD(FLOOR(RAND() * (999999 - 100000) + 100000), 6, 0) AS subIdentifier
FROM property as p1
WHERE "subIdentifier" NOT IN (SELECT uniqueIdentifier FROM property as p2)
) as innerTable group by subIdentifier
)
UPDATE property SET uniqueIdentifier = (
select subIdentifier from cte as c where c.id = property.id
) where property.id != ''
this query returns a set of record for almost all the rows but I have a table of entries of total 20000,
but this query fills up for ~19000 and rest of the rows are null.
here is a current output
[current result picture]
If anyone can help, I am extremely thanks for that.
Thanks
Instead of trying to randomly generate unique numbers that do not exist in the table, I would try the approach of randomly generating numbers using the ID column as a seed; as long as the ID number is unique, the new number will be unique as well. This is not technically fully "random" but it may be sufficient for your needs.
https://www.db-fiddle.com/f/iqMPDK8AmdvAoTbon1Yn6J/1
update Property set
UniqueIdentifier = round(rand(id)*1000000)
where UniqueIdentifier is null
SELECT id, round(rand(id)*1000000) as UniqueIdentifier FROM test;
I have a table someTable with a column bin of type VARCHAR(4). Whenever I insert to this table, bin should be a unique combination of characters and numbers. Unique in this sense meaning has not appeared before in the table in another row.
bin is in the form of AA00, where A is a character A-F and 0 is a number 0-9.
Say I insert to this table once: it should come up with a bin value which doesn't appear before. Assuming the table was empty, the first bin could be AA11. On second insertion, it should be AA12, and then AA13, etc.
AA00, AA01, ... AA09, AA10, AA11, ... AA99, AB00, AB01, ... AF99, BA00, BA01, ... FF99
It doesn't matter this table can contain only 3,600 possible rows. How do I create this code, specifically finding a bin that doesn't already exist in someTable? It can be in order as I've described or a random bin, as long as it doesn't appear twice.
CREATE TABLE someTable (
bin VARCHAR(4),
someText VARCHAR(32),
PRIMARY KEY(bin)
);
INSERT INTO someTable
VALUES('?', 'a');
INSERT INTO someTable
VALUES('?', 'b');
INSERT INTO someTable
VALUES('?', 'c');
INSERT INTO someTable
VALUES('?', 'd');
Alternatively, I can use the below procedure to insert instead:
CREATE PROCEDURE insert_someTable(tsomeText VARCHAR(32))
BEGIN
DECLARE var (VARCHAR(4) DEFAULT (
-- some code to find unique bin
);
INSERT INTO someTable
VALUES(var, tsomeText);
END
A possible outcome is:
+------+----------+
| bin | someText |
+------+----------+
| AB31 | a |
| FC10 | b |
| BB22 | c |
| AF92 | d |
+------+----------+
As Gordon said, you will have to use a trigger because it is too complex to do as a simple formula in a default. Should be fairly simple, you just get the last value (order by descending, limit 1) and increment it. Writing the incrementor will be somewhat complicated because of the alpha characters. It would be much easier in an application language, but then you run into issues of table locking and the possibility of two users creating the same value.
A better method would be to use a normal auto-increment primary key and translate it to your binary value. Consider your bin value as two base 6 characters followed by two base 10 values. You then take the id generated by MySQL which is guaranteed to be unique and convert to your special number system. Calculate the bin and store it in the bin column.
To calculate the bin:
Step one would be to get the lower 100 value of the decimal number (mod 100) - that gives you the last two digits. Convert to varchar with a leading zero.
Subtract that from the id, and divide by 100 to get the value for the first two digits.
Get the mod 6 value to determine the 3rd (from the right) digit. Convert to A-F by index.
Subtract this from what's left of the ID, and divide by 6 to get the 4th (from the right) digit. Convert to A-F by index.
Concat the three results together to form the value for the bin.
You may need to edit the following to match your table name and column names, but it should so what you are asking. One possible improvement would be to have it cancel any inserts past the 3600 limit. If you insert the 3600th record, it will duplicate previous bin values. Also, it won't insert AA00 (id=1 = 'AA01'), so it's not perfect. Lastly, you could put a unique index on bin, and that would prevent duplicates.
DELIMITER $$
CREATE TRIGGER `fix_bin`
BEFORE INSERT ON `so_temp`
FOR EACH ROW
BEGIN
DECLARE next_id INT;
SET next_id = (SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='so_temp');
SET #id = next_id;
SET #Part1 = MOD(#id,100);
SET #Temp1 = FLOOR((#id - #Part1) / 100);
SET #Part2 = MOD(#Temp1,6);
SET #Temp2 = FLOOR((#Temp1 - #Part2) / 6);
SET #Part3 = MOD(#Temp2,6);
SET #DIGIT12 = RIGHT(CONCAT("00",#Part1),2);
SET #DIGIT3 = SUBSTR("ABCDEF",#Part2 + 1,1);
SET #DIGIT4 = SUBSTR("ABCDEF",#Part3 + 1,1);
SET NEW.`bin` = CONCAT(#DIGIT4,#DIGIT3,#DIGIT12);
END;
$$
DELIMITER ;
I'm trying to update a certain column of certain row WHERE id is certain value. The thing is, the number/names of columns are variable, and so are their respective ids.
For example:
UPDATE table SET column1="hello" WHERE id = 5
UPDATE table SET column2="cucumber" WHERE id = 6
How can I do a single mysql query in PDO to do this?
First thing I tried is...
UPDATE table SET column1="hello", column4="bye" WHERE id IN(5, 6)
But that query will update BOTH of those columns in rows where it finds BOTH of those ids, and that's not what I'm looking for. Is it only possible to do this query by query?
Keep in mind that the argument after SET is variable, so the columns to be updated, their values and their respective ids are also variable.
A solution where you can just purely bind values would be great, but if I have to build the query string with escaped variables, then that's OK too.
Thank you.
You can do this
UPDATE table t1 JOIN table t2
ON t1.id= 5 AND t2.id= 6
SET t1.column1= 'hello',
t2.column2 = 'cucumber';
Or if you want to do this on a single column
UPDATE table
SET column2 = CASE id
WHEN 5 THEN 'hello'
WHEN 6 THEN ''
END
WHERE id IN(5, 6);
To be more clear:
The table thetable (id int, username varchar(30), password varchar(30), last_successful_login timestamp, last_unsuccessful_login timestamp, another_variable varchar(30)) has the following row: (1, "tgh", "pass", 0, 0, "another")
1) Wrong User/Pass Pair, but there is a row with the username
I want select id from thetable where username="tgh" and password="wrongpass" and another_variable="another"; to update the last_unsuccessful_login columns of all the rows with username="tgh" AND another_variable="another" (which is unique, there can't be two rows with ("tgh", "another") pair. There can be ("tgh", "another2") though.) to CURRENT_TIMESTAMP.
So the example row would be (1, "tgh", "pass", 0, CURRENT_TIMESTAMP, "another"), after the "select" query that does not completely match.
To be even more clear, I am trying to avoid running an extra update with only username="tgh" and another_variable="another" on the table, i.e. update thetable set last_unsuccessful_login=CURRENT_TIMESTAMP where username="tgh" and another_variable="another";, according to the result of the select.
2) Correct User/Pass Pair
Also, if all three username and password and another_variable matches, this time I want to set the last_successful_login to CURRENT_TIMESTAMP.
That would make the example row `(1, "tgh", "pass", CURRENT_TIMESTAMP, 0, "another")
What is the most efficient way to do this?
The short answer to your question is no, it is not possible for a SELECT statement to cause or trigger an update. (The caveat here is that a SELECT statement can call a FUNCTION (MySQL stored program) which can perform an UPDATE.)
You can't get around issuing an UPDATE statement; an UPDATE statement has to be issued from somewhere, and a SELECT statement cannot "trigger" it.
It is possible to have a single UPDATE statement do the check of the supplied password against the current value in the password column, and set both the last_successful_login and last_unsuccessful_login columns, e.g.:
UPDATE thetable
SET last_successful_login =
IF(IFNULL(password,'')='wrongpass',CURRENT_TIMESTAMP,0)
, last_unsuccessful_login =
IF(IFNULL(password,'')='wrongpass',0,CURRENT_TIMESTAMP)
WHERE username='tgh'
AND another_variable='another'
So, you could issue an UPDATE statement first; and then issue a SELECT statement.
If you want to minimize the number of "roundtrips" to the database, at the cost of additional complexity (making it harder for someone else to figure out what is going on) you could put the UPDATE statement into a stored program. If you put this into a function, you could set the return value to indicate whether the login was successful.
SELECT udf_login('username','wrongpass','another')
So, from your application, it looks like you are doing a login check, but the called function can perform the UPDATE.
CREATE FUNCTION `udf_login`
( as_username VARCHAR(30)
, as_password VARCHAR(30)
, as_another_variable VARCHAR(30)
) RETURNS INT
READS SQL DATA
BEGIN
UPDATE `thetable`
SET `last_successful_login` =
IF(IFNULL(`password`,'')=IFNULL(as_password,''),CURRENT_TIMESTAMP,0)
, `last_unsuccessful_login` =
IF(IFNULL(`password`,'')=IFNULL(as_password,''),0,CURRENT_TIMESTAMP)
WHERE `username` = as_username
AND `another_variable` = as_another_variable;
-- then perform whatever checks you need to (e.g)
-- SELECT IFNULL(t.password,'')=IFNULL(as_password,'') AS password_match
-- FROM `thetable` t
-- WHERE t.username = as_username
-- AND t.another_variable = as_another_variable
-- and conditionally return a 0 or 1
RETURN 0;
END$$
I have a table which goes somewhat as follows:
Name1 (Key)|Name2(Key)|Total|LastUpdate
There will be hundreds of thousands of such records.
Now very frequently, my main program will query a source to get updated values. It will then update the total and last update. It may have to update hundreds of such rows.
Currently I have this:
Update mytable SET Total=[...] AND LastUpdate=[....] WHERE Name1='x' AND Name2='y';
Update mytable SET .....
I'm wondering if there is a faster way of updating the rows (similar to how you can Insert multiple rows at the same time).
The Totals will be completely different, but the LastUpdate time will be the same for each Update.
Update to Clarify:
The changes to total aren't just an increment, and don't depend on the current value - so its not deterministic in that regard. The source provides me a new value every second or so, and I have to put in a new one.
if its to slow to do the updates separatly, mayby you can do somthing like this
CREATE TABLE mytable_updates
(
Name1 VARCHAR(50) NOT NULL,
Name2 VARCHAR(50) NOT NULL,
total_increase INT NOT NULL
PRIMARY KEY(Name1, Name2)
)
INSERT INTO mytable_updates VALUES ('x', 'y', 5), ('x', 'z', 3);
...
UPDATE mytable_updates
LEFT JOIN mytable USING (Name1, Name2)
SET
Total = Total + total_increase,
LastUpdate = NOW();