LPAD without trimming in mysql - mysql

I am trying to find a way to add 0 at the beginning of an ID without trimming it :
I am currently using LPAD(id,4,"0") witch works fine until 9999
Request : SELECT LPAD(12345,4,"0");
Excepted result : 12345
Result : 1234
I am Looking for a Request who does LPAD(id, MAX(LENGTH(id),4), "0")
I found SELECT IF(LENGTH(12345)>4, 12345, LPAD(12345, 4, "0")); but i would prefer if the 4 was in a single place (to make it easier use it elsewhere).
Is there a build-in function that does what i'm looking For ?
Or is there an alternative to the function MAX() that would work in that situation ?
Or should I stick with the IF(LENGTH) solution and it's drawbacks ?
Edit :
ZEROFILL doesn't fit my needs because I also need the id without 0s.
I mainly use the ID without the LPAD(), but when I do, I use it with a prefix : CONCAT("PFX", LPAD(id,4,"0"))
Thanks for your Help
PS: please tell me if i do anything wrong, it's my first time asking here.

Well I had similar problem with LPAD, it was truncating number to its pad length. According to https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_lpad it is expected result.
As far as I can see, nobody mentioned answer that solved my same problem:
LPAD(id, GREATEST(LENGTH(id), 4), "0")
It works as expected. Pads any id's shorter than 4 characters with 0, and returns unchanged id's that are longer than 4 characters.
I'm leaving my answer here for other people, that will find this question in the future.

You can cat the length of a int field and also set zerofill like this:
CREATE TABLE `num` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`Numbers` int(5) unsigned zerofill DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Insert values:
INSERT INTO `num` (`id`, `Numbers`)
VALUES
(1, 1),
(2, 22),
(3, 123456);
See the result:
MariaDB []> select * from num;
+----+---------+
| id | Numbers |
+----+---------+
| 1 | 00001 |
| 2 | 00022 |
| 3 | 123456 |
+----+---------+
3 rows in set (0.000 sec)
MariaDB []>
without zerofill you can calc it like:
SELECT
id,
SUBSTRING( CONCAT('000000',Numbers),- GREATEST(LENGTH(Numbers),5))
FROM num;
Result:
MariaDB []> SELECT
-> id,
-> SUBSTRING( CONCAT('000000',Numbers),- GREATEST(LENGTH(Numbers),5))
-> FROM num;
+----+--------------------------------------------------------------------+
| id | SUBSTRING( CONCAT('000000',Numbers),- GREATEST(LENGTH(Numbers),5)) |
+----+--------------------------------------------------------------------+
| 1 | 00001 |
| 2 | 00022 |
| 3 | 123456 |
+----+--------------------------------------------------------------------+
3 rows in set (0.001 sec)
MariaDB []>

Ask yourself, why a number would need leading zeroes at all. No number in practical and theoretical math needs leading zeroes.
Maybe you want to visuals something, and not work with the result as a real number.
In that case, you can either declare the field in the table ZEROFILL and select it converted to char: SELECT CONVERT(id, CHAR);.
Other than that, the only way is a mix of CONCAT() and LENGTH(), that way you avoid stripping the ID to the value in LPAD().

LPAD
You can achieve this with an REGEXP_REPLACE to do a LPAD equivalent but without truncating the result. If you want a minimum length of 4 and padding with 0, you can do:
REGEXP_REPLACE(CONCAT('0000', id), '^0{0,4}(.{4,})$', '\\1')
You can adapt this by replacing '0000' with REPEAT('0', 4) to do it programmatically. The main advantage in this approach, is to not repeating the padded thing. In my use cases, I can have very long and complex things that I don't want to duplicate to only pad them, so this code fragment do its job.
RPAD
For those who have the problem for RPAD, you need a very close pattern:
REGEXP_REPLACE(CONCAT(id, '0000'), '^(.{4,}?)0{0,4}$', '\\1')
The two blocks in the pattern are essentially inverted but you can see .{4,}? instead of .{4,}. It's for set the greediness to lazy for the {4,} quantifier. It's needed here.

Related

Mysql query like number greater than x

I have a field for comments used to store the title of the item sold on the site as well as the bid number (bid_id). Unfortunately, the bid_id is not stored on its own in that table.
I want to query items that have a number (the bid_id) greater than 4,000 for example.
So, what I have is:
select * from mysql_table_name where comment like '< 4000'
I know this won't work, but I need something similar that works.
Thanks a lot!
Just get your bid_id column cleaned up. Then index is.
create table `prior`
( id int auto_increment primary key,
comments text not null
);
insert `prior` (comments) values ('asdfasdf adfas d d 93827363'),('mouse cat 12345678');
alter table `prior` add column bid_id int; -- add a nullable int column
select * from `prior`; -- bid_id is null atm btw
update `prior` set bid_id=right(comments,8); -- this will auto-cast to an int
select * from `prior`;
+----+-----------------------------+----------+
| id | comments | bid_id |
+----+-----------------------------+----------+
| 1 | asdfasdf adfas d d 93827363 | 93827363 |
| 2 | mouse cat 12345678 | 12345678 |
+----+-----------------------------+----------+
Create the index:
CREATE INDEX `idxBidId` ON `prior` (bid_id); -- or unique index
select * from mysql_table_name where substring(comment,start,length, signed integer) < 4000
This will work, but I suggest create new column and put the bid value in it then compare.
To update value in new column you can use
update table set newcol = substring(comment,start,length)
Hope this will help
There is nothing ready that works like that.
You could write a custom function or loadable UDF, but it would be a significant work, with significant impact on the database. Then you could run WHERE GET_BID_ID(comment) < 4000.
What you can do more easily is devise some way of extracting the bid_id using available string functions.
For example if the bid_id is always in the last ten characters, you can extract those, and replace all characters that are not digits with nil. What is left is the bid_id, and that you can compare.
Of course you need a complex expression with LENGTH(), SUBSTRING(), and REPLACE(). If the bid_id is between easily recognizable delimiters, then perhaps SUBSTRING_INDEX() is more your friend.
But better still... add an INTEGER column, initialize it to null, then store there the extracted bid_id. Or zero if you're positive there's no bid_id. Having data stored in mixed contexts is evil (and a known SQL antipattern to boot). Once you have the column available, you can select every few seconds a small number of items with new_bid_id still NULL and subject those to extraction, thereby gradually amending the database without overloading the system.
In practice
This is the same approach one would use with more complicated cases. We start by checking what we have (this is a test table)
SELECT commento FROM arti LIMIT 3;
+-----------------------------------------+
| commento |
+-----------------------------------------+
| This is the first comment 100 200 42500 |
| Another 7 Q 32768 |
| And yet another 200 15 55332 |
+-----------------------------------------+
So we need the last characters:
SELECT SUBSTRING(commento, LENGTH(commento)-5) FROM arti LIMIT 3;
+-----------------------------------------+
| SUBSTRING(commento, LENGTH(commento)-5) |
+-----------------------------------------+
| 42500 |
| 32768 |
| 55332 |
+-----------------------------------------+
This looks good but it is not; there's an extra space left before the ID. So 5 doesn't work, SUBSTRING is 1-based. No matter; we just use 4.
...and we're done.
mysql> SELECT commento FROM arti WHERE SUBSTRING(commento, LENGTH(commento)-4) < 40000;
+-------------------+
| commento |
+-------------------+
| Another 7 Q 32768 |
+-------------------+
mysql> SELECT commento FROM arti WHERE SUBSTRING(commento, LENGTH(commento)-4) BETWEEN 35000 AND 55000;
+-----------------------------------------+
| commento |
+-----------------------------------------+
| This is the first comment 100 200 42500 |
+-----------------------------------------+
The problem is if you have a number not of the same length (e.g. 300 and 131072). Then you need to take a slice large enough for the larger number, and if the number is short, you will get maybe "1 5 300" in your slice. That's where SUBSTRING_INDEX comes to the rescue: by capturing seven characters, from " 131072" to "1 5 300", the ID will always be in the last space separated token of the slice.
IN THIS LAST CASE, when numbers are not of the same length, you will find a problem. The extracted IDs are not numbers at all - to MySQL, they are strings. Which means that they are compared in lexicographic, not numerical, order; and "17534" is considered smaller than "202", just like "Alice" comes before "Bob". To overcome this you need to cast the string as unsigned integer, which further slows down the operations.
WHERE CAST( SUBSTRING(...) AS UNSIGNED) < 4000

MySql how to make an ID with 6 nubers

Simple question: how to make id when creating table in mySql go like this 0001 0002 and so on instead of 1 2 3..
Sorry but don't know how to ask this in a correct way for Google to understand :)
And I am beginner so please make answer simple
Thanks
Edit: got answer
Create the Field with Zerofill. So MySQL fill it with 0 to the lenght you specified
CREATE TABLE `num` (
`id` int(4) unsigned zerofill NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
MariaDB [test]> select * from num;
+------+
| id |
+------+
| 0001 |
| 0002 |
| 0003 |
+------+
3 rows in set (0.00 sec)
The simple answer is... you don't.
Numeric data isn't stored in a formatted way. It's stored as numeric values. You can, however, display that data however you like when you query it.
For example, to take a number that's 4 digits or fewer and pad leading zeroes, you might do something like this:
SELECT LPAD(CONVERT(`ID`, VARCHAR(4)), 4, '0') FROM Table
This would convert the ID column to VARCHAR(4) in the resulting data, and left-pad the values with '0' characters up to a length of 4.
You can add the zeros when you select the values:
SELECT LPAD(id, 4, '0') as id FROM your_table;

mysql sort by number with dots

i have a column with name title with rows :
1.8.8
1.8.9
1.9.1
1.9.2
1.8.10
and I need sort like this
1.8.8
1.8.9
1.8.10
1.9.1
1.9.2
is any way how to make it? (type of column is varchar)
Clunky, but should work provided all of your entries are in the format x.x.x:
select yourColumn
from yourTable
order by
cast(substring_index(yourColumn,'.',1) as unsigned),
cast(substring_index(substring_index(yourColumn,'.',2),'.',-1) as unsigned),
cast(substring_index(substring_index(yourColumn,'.',3),'.',-1) as unsigned)
;
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table(legal VARCHAR(12) NOT NULL PRIMARY KEY);
INSERT INTO my_table VALUES('1.8.8'),('1.8.9'),('1.9.1'),('1.9.2'),('1.8.10');
SELECT * FROM my_table ORDER BY INET_ATON(legal);
+--------+
| legal |
+--------+
| 1.8.8 |
| 1.8.9 |
| 1.8.10 |
| 1.9.1 |
| 1.9.2 |
+--------+
Note that this is a hack. In consequence, it has certain limitations. That said, there's a high likelihood that it will work fine for your purposes.
It may help, Simple and Exact Answer:
SELECT `COLUMN_NAME` FROM `TABLENAME` WHERE 1 ORDER BY `COLUMN_NAME` *1
This is called "natural sort" and there is no way to perform natural sort in MySQL: Natural Sort in MySQL
You can solve it by using special sorting field. With values like:
10808
10809
10901
10902
10910
(leading zeros)
PS as a bonus - sorting integers works muuuuch faster than sorting strings (especially strings with some magic rules). Especially if you create sorting index.
none of the answer quite were practical(either wrong or not performant).
Let's think your varchar column with dots is productId then you must use:
SELECT * FROM PRODUCT_TABLE ORDER BY productId * 1,length(productId)
productId * 1 gives you the same result as INET_ATON(productId) and they both are incomplete.
0.0.0
0.0
0
I got a result like that so to fix that just added length(productId).
0
0.0
0.0.0
and now everything is fine.

In MySQL, should I quote numbers or not?

For example - I create database and a table from cli and insert some data:
CREATE DATABASE testdb CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
USE testdb;
CREATE TABLE test (id INT, str VARCHAR(100)) TYPE=innodb CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
INSERT INTO test VALUES (9, 'some string');
Now I can do this and these examples do work (so - quotes don't affect anything it seems):
SELECT * FROM test WHERE id = '9';
INSERT INTO test VALUES ('11', 'some string');
So - in these examples I've selected a row by a string that actually stored as INT in mysql and then I inserted a string in a column that is INT.
I don't quite get why this works the way it works here. Why is string allowed to be inserted in an INT column?
Can I insert all MySQL data types as strings?
Is this behavior standard across different RDBMS?
MySQL is a lot like PHP, and will auto-convert data types as best it can. Since you're working with an int field (left-hand side), it'll try to transparently convert the right-hand-side of the argument into an int as well, so '9' just becomes 9.
Strictly speaking, the quotes are unnecessary, and force MySQL to do a typecasting/conversion, so it wastes a bit of CPU time. In practice, unless you're running a Google-sized operation, such conversion overhead is going to be microscopically small.
You should never put quotes around numbers. There is a valid reason for this.
The real issue comes down to type casting. When you put numbers inside quotes, it is treated as a string and MySQL must convert it to a number before it can execute the query. While this may take a small amount of time, the real problems start to occur when MySQL doesn't do a good job of converting your string. For example, MySQL will convert basic strings like '123' to the integer 123, but will convert some larger numbers, like '18015376320243459', to floating point. Since floating point can be rounded, your queries may return inconsistent results. Learn more about type casting here. Depending on your server hardware and software, these results will vary. MySQL explains this.
If you are worried about SQL injections, always check the value first and use PHP to strip out any non numbers. You can use preg_replace for this: preg_replace("/[^0-9]/", "", $string)
In addition, if you write your SQL queries with quotes they will not work on databases like PostgreSQL or Oracle.
Check this, you can understand better ...
mysql> EXPLAIN SELECT COUNT(1) FROM test_no WHERE varchar_num=0000194701461220130201115347;
+----+-------------+------------------------+-------+-------------------+-------------------+---------+------+---------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------------------+-------+-------------------+-------------------+---------+------+---------+--------------------------+
| 1 | SIMPLE | test_no | index | Uniq_idx_varchar_num | Uniq_idx_varchar_num | 63 | NULL | 3126240 | Using where; Using index |
+----+-------------+------------------------+-------+-------------------+-------------------+---------+------+---------+--------------------------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT COUNT(1) FROM test_no WHERE varchar_num='0000194701461220130201115347';
+----+-------------+------------------------+-------+-------------------+-------------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------------------+-------+-------------------+-------------------+---------+-------+------+-------------+
| 1 | SIMPLE | test_no | const | Uniq_idx_varchar_num | Uniq_idx_varchar_num | 63 | const | 1 | Using index |
+----+-------------+------------------------+-------+-------------------+-------------------+---------+-------+------+-------------+
1 row in set (0.00 sec)
mysql>
mysql>
mysql> SELECT COUNT(1) FROM test_no WHERE varchar_num=0000194701461220130201115347;
+----------+
| COUNT(1) |
+----------+
| 1 |
+----------+
1 row in set, 1 warning (7.94 sec)
mysql> SELECT COUNT(1) FROM test_no WHERE varchar_num='0000194701461220130201115347';
+----------+
| COUNT(1) |
+----------+
| 1 |
+----------+
1 row in set (0.00 sec)
AFAIK it is standard, but it is considered bad practice because
- using it in a WHERE clause will prevent the optimizer from using indices (explain plan should show that)
- the database has to do additional work to convert the string to a number
- if you're using this for floating-point numbers ('9.4'), you'll run into trouble if client and server use different language settings (9.4 vs 9,4)
In short: don't do it (but YMMV)
This is not standard behavior.
For MySQL 5.5. this is the default SQL Mode
mysql> select ##sql_mode;
+------------+
| ##sql_mode |
+------------+
| |
+------------+
1 row in set (0.00 sec)
ANSI and TRADITIONAL are used more rigorously by Oracle and PostgreSQL. The SQL Modes MySQL permits must be set IF AND ONLY IF you want to make the SQL more ANSI-compliant. Otherwise, you don't have to touch a thing. I've never done so.
It depends on the column type!
if you run
SELECT * FROM `users` WHERE `username` = 0;
in mysql/maria-db you will get all the records where username IS NOT NULL.
Always quote values if the column is of type string (char, varchar,...) otherwise you'll get unexpected results!
You don't need to quote the numbers but it is always a good habit if you do as it is consistent.
The issue is, let's say that we have a table called users, which has a column called current_balance of type FLOAT, if you run this query:
UPDATE `users` SET `current_balance`='231608.09' WHERE `user_id`=9;
The current_balance field will be updated to 231608, because MySQL made a rounding, similarly if you try this query:
UPDATE `users` SET `current_balance`='231608.55' WHERE `user_id`=9;
The current_balance field will be updated to 231609

What is the benefit of zerofill in MySQL?

I just want to know what is the benefit/usage of defining ZEROFILL for INT DataType in MySQL?
`id` INT UNSIGNED ZEROFILL NOT NULL
When you select a column with type ZEROFILL it pads the displayed value of the field with zeros up to the display width specified in the column definition. Values longer than the display width are not truncated. Note that usage of ZEROFILL also implies UNSIGNED.
Using ZEROFILL and a display width has no effect on how the data is stored. It affects only how it is displayed.
Here is some example SQL that demonstrates the use of ZEROFILL:
CREATE TABLE yourtable (x INT(8) ZEROFILL NOT NULL, y INT(8) NOT NULL);
INSERT INTO yourtable (x,y) VALUES
(1, 1),
(12, 12),
(123, 123),
(123456789, 123456789);
SELECT x, y FROM yourtable;
Result:
x y
00000001 1
00000012 12
00000123 123
123456789 123456789
One example in order to understand, where the usage of ZEROFILL might be interesting:
In Germany, we have 5 digit zipcodes. However, those Codes may start with a Zero, so 80337 is a valid zipcode for munic, 01067 is a zipcode of Berlin.
As you see, any German citizen expects the zipcodes to be displayed as a 5 digit code, so 1067 looks strange.
In order to store those data, you could use a VARCHAR(5) or INT(5) ZEROFILL whereas the zerofilled integer has two big advantages:
Lot lesser storage space on hard disk
If you insert 1067, you still get 01067 back
Maybe this example helps understanding the use of ZEROFILL.
It's a feature for disturbed personalities who like square boxes.
You insert
1
23
123
but when you select, it pads the values
000001
000023
000123
It helps in correct sorting in the case that you will need to concatenate this "integer" with something else (another number or text) which will require to be sorted as a "text" then.
for example,
if you will need to use the integer field numbers (let's say 5) concatenated as A-005 or 10/0005
I know I'm late to the party but I find the zerofill is helpful for boolean representations of TINYINT(1). Null doesn't always mean False, sometimes you don't want it to. By zerofilling a tinyint, you're effectively converting those values to INT and removing any confusion ur application may have upon interaction. Your application can then treat those values in a manner similar to the primitive datatype True = Not(0)
mysql> CREATE TABLE tin3(id int PRIMARY KEY,val TINYINT(10) ZEROFILL);
Query OK, 0 rows affected (0.04 sec)
mysql> INSERT INTO tin3 VALUES(1,12),(2,7),(4,101);
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM tin3;
+----+------------+
| id | val |
+----+------------+
| 1 | 0000000012 |
| 2 | 0000000007 |
| 4 | 0000000101 |
+----+------------+
3 rows in set (0.00 sec)
mysql>
mysql> SELECT LENGTH(val) FROM tin3 WHERE id=2;
+-------------+
| LENGTH(val) |
+-------------+
| 10 |
+-------------+
1 row in set (0.01 sec)
mysql> SELECT val+1 FROM tin3 WHERE id=2;
+-------+
| val+1 |
+-------+
| 8 |
+-------+
1 row in set (0.00 sec)
ZEROFILL
This essentially means that if the integer value 23 is inserted into an INT column with the width of 8 then the rest of the available position will be automatically padded with zeros.
Hence
23
becomes:
00000023
When used in conjunction with the
optional (nonstandard) attribute
ZEROFILL, the default padding of
spaces is replaced with zeros. For
example, for a column declared as
INT(4) ZEROFILL, a value of 5 is
retrieved as 0005.
http://dev.mysql.com/doc/refman/5.0/en/numeric-types.html
If you specify ZEROFILL for a numeric column, MySQL automatically adds the UNSIGNED attribute to the column.
Numeric data types that permit the UNSIGNED attribute also permit SIGNED. However, these data types are signed by default, so the SIGNED attribute has no effect.
Above description is taken from MYSQL official website.