MySQL doesn't match varchar keys ending with a number - mysql

I've defined a table like
CREATE TABLE `mytable` (
`identifier` varchar(45) NOT NULL,
`f1` char(1) NOT NULL,
KEY `identifier` (`identifier`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
and then added primary key and index as
ALTER TABLE `mytable` ADD PRIMARY KEY ( `identifier` )
ALTER TABLE `mytable` ADD INDEX ( `identifier` )
In the identifier field my table is populated with values like (about 800,000 records)
USER01-TESTXXY-CAD-10172
USER01-TESTXXY-CAD-1020
USER01-TESTXXY-CAD-10245
USER02-TEST-003-SUBA
USER02-TEST-002-SUBB
I've discovered that queries where the identifier ends with a number aren't matched:
SELECT *
FROM identifier
WHERE identifier = 'USER01-TESTXXY-CAD-10245';
but queries matching an identifier which ends with letters are matched successfully
SELECT *
FROM identifier
WHERE identifier = 'USER02-TEST-003-SUBA';
My queries are exact, I don't need to compare with LIKE because my users provide me exact strings. Besides varchar(45) is more than enough space for my identifiers.
What I did wrong? What could be the reason or solution?

I think you have made a typo in both the queries. Your table name should be mytable instead of identifier.
SELECT *
FROM mytable
WHERE identifier = 'USER01-TESTXXY-CAD-10245';
Is it a typo ?
Is there another table in your database that is called as identifier?

Related

Field 'category_id' doesn't have a default value MySQL

I'm learning SQL.
I'm trying to insert data. My MySQL database looks like this.
CREATE TABLE category (
category_id CHAR(100),
category_name VARCHAR(120) NOT NULL,
PRIMARY KEY (category_id)
)
I ran this command
INSERT INTO category (category_name) VALUES ("test");
But I got this error
ERROR 1364 (HY000): Field 'category_id' doesn't have a default value
Thank you in advance.
If you want to have an incrementing ID it would need to be an int. You Generally want to make ID's integers not chars to speed up lookup regardless.
CREATE TABLE IF NOT EXISTS category (
`category_id` INT NOT NULL AUTO_INCREMENT,
`category_name` VARCHAR(120) NOT NULL,
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_unicode_ci;
That will let you insert without adding your own ID, and automatically generate unique ID's for the records.
Issue was you set your category_id field to not have a default value and also not allow null, which means you -have- to set a value for it in the insert. If you wanted to use your existing table you would need to do this:
INSERT INTO category (category_id, category_name) VALUES ("someid", "test");

Optimize speed of Mysql JOIN query

I have 2 tables called T1 made of 1.6mln of rows and T2 made of 4.6mln of rows with with one-to-many relationship.
The CREATE STMT of T1 is:
CREATE TABLE `T1` (
`field_1` text,
`field_2` text,
`field_3` decimal(10,6) DEFAULT NULL,
`field_4` decimal(10,6) DEFAULT NULL,
`field_4` decimal(10,6) DEFAULT NULL,
`field_5` text,
`field_6` text,
`field_7` text,
`field_8` double DEFAULT NULL,
`field_9` text,
`field_10` text,
`field_11` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
The CREATE STMT of T2 is:
CREATE TABLE `T2` (
`field_1` int(11) DEFAULT NULL,
`field_2` text,
`field_3` text,
`field_4` text,
`field_5` text,
`field_6` text,
`field_7` text,
`field_8` text,
`field_9` text,
`field_10` text,
`field_11` text,
`field_12` text,
`field_13` text
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I don't have set any kind of indexes or any particular constraints for now, but the T1.field_1 should be my ideal key and can be joined with T2.field_2 field.
If I decide to make a JOIN like:
SELECT * FROM T1
JOIN T2
ON T1.field_1=T2.field_2
WHERE T1.=2130100;
The benchmark is really high.
This is the EXPLAIN:
So I'm just trying to understand what could be some possibile improvements:
Add some index
Change the type of the input fields?
Maybe add a primary key?
In you where condition you missed the column name i assume the columns is named your_col
Starting form mysql 5.0.3 varchar can be up 65,535 so you could try using varchar instead of text when possibile
for indexing there are limitation on the size of the index max key length is 767 byte ( assuming 3 bytes for each utf8 character. so about 250 utf8 char )
the column candidate for indexing must respected these limit
if this is possible then you could
add index on
table t2 colums fiedl_2
and on
table t1 a composite index on column (Your_col, field_1)
these are the columns involved in where and ON clause
SELECT * FROM T1
JOIN T2
ON T1.field_1=T2.field_2
WHERE T1.Your_col=2130100;
Since you are using latin1, switch t1.field_1 and t2.field_2 to VARCHAR of no more than 767. Use the shortest value that is not likely to be exceeded. Do likewise for all the other TEXT columns. (If you need >767, stick with TEXT.)
Then add two indexes:
T1: INDEX(??) -- whatever column you are using in the `WHERE`
T2: INDEX(field_2)
If the column in T1 is an INT, then 2130100 is OK. But if it is TEXT (or soon to be VARCHAR(..), then quote it: "2130100". The should prevent a surprising and unnecessary table scan of T1.

MySQL: Enforce an unique column without using an unique key

I have a column with data that exceeds MySQL's index length limit. Therefore, I can't use an unique key.
There's a solution here to the problem without using an unique key: MySQL: Insert record if not exists in table
However, in the comments, people are having issues with inserting the same value into multiple columns. In my case, a lot of my values are 0, so I'll get duplicate values very often.
I'm using Node and node-mysql to access the database. I'm thinking I can have a variable that keeps track of all values that are currently being inserted. Before inserting, I check if the value is currently being inserting. If so, I'll wait until it finishes inserting, then continue execution as if the value was originally inserted. However, I feel like this will be very error prone.
Here's part of my table schema:
CREATE TABLE `links` (
`id` int(10) UNSIGNED NOT NULL,
`url` varchar(2083) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
`likes` int(10) UNSIGNED NOT NULL,
`tweets` int(10) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `links`
ADD PRIMARY KEY (`id`),
ADD KEY `url` (`url`(50));
I cannot put an unique key on url because it can be 2083 bytes, which is over MySQL's key size limit. likes and tweets will often be 0, so the linked solution will not work.
Is there another possible solution?
If you phrase your INSERT in a certain way, you can make use of WHERE NOT EXISTS to check first if the URL does not exist before completing the insert:
INSERT INTO links (`url`, `likes`, `tweets`)
SELECT 'http://www.google.com', 10, 15 FROM DUAL
WHERE NOT EXISTS
(SELECT 1 FROM links WHERE url='http://www.google.com');
This assumes that the id column is a primary key/auto increment, and MySQL will automatically assign a value to it.

Let field value set sort order direction in Mysql

Given the following table:
CREATE TABLE `example` (
`Identifier` int(11) NOT NULL AUTO_INCREMENT,
`FieldValue` varchar(255) DEFAULT NULL,
`FieldOrder` enum('asc','desc') DEFAULT 'asc',
PRIMARY KEY (`Identifier`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I want to run a query that sorts the FieldValue field based on the value in the FieldOrder field. E.g.
Select * from example order by FieldValue [here should the FieldOrder value be placed]
Is it possible to make a reference to the FieldOrder field in the sort by part of the query?
One way to approach this is to treat FieldValue as positive or negative for FieldOrder values of "asc" and "desc" respectively. This can be expressed by a case expression:
SELECT *
FROM example
ORDER BY CASE FieldOrder WHEN 'asc' THEN 1 ELSE -1 END * FieldValue
Not that I know of ...
Please note that every row has a FieldOrder so what you seek to achieve seems to be questionable. What if one row says asc and another row say desc? How should the order of the two rows be shown then?
If you want to have parameterized order by action, you can consider using the following two methods:
Use a stored procedure that takes an argument for, say,
sortingOrder
Use a programming language (e.g. Java) to construct a query string and
inject the sorting order dynamically, and
then execute the query string to MySQL
You need to normalize your data and store it in a better schema. Do not store multiple values in a single field, only store one value per row. What you can do is store the multiple data in its own table, where each piece of data is in its own row.
Try a schema like this:
CREATE TABLE `example` (
`Identifier` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`Identifier`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `exampleData` (
`RowID` int(11) NOT NULL AUTO_INCREMENT,
`Identifier` int(11) NOT NULL,
`FieldValue` varchar(255) DEFAULT NULL,
PRIMARY KEY (`RowID`),
INDEX `Identifier` (`Identifier`),
FOREIGN KEY (Identifier) REFERENCES example(Identifier)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Now you have two tables, and in your exampleData table, you have one row per each piece of data in FieldValue.
Now you can query like so:
SELECT Identifier, FieldValue
FROM example
JOIN exampleData USING(Identifier)
WHERE Identifier = 3
ORDER BY FieldValue ASC

Hash of two columns in mysql

I have a MYSQL table, with 5 columns in it:
id bigint
name varchar
description varchar
slug
Can I get MySQL to automatically generate the value of slug as a 256 Bit Hash of name+description?
I am now using PHP to generate an SHA256 value of the slug prior to saving it.
Edit:
By automatic, I mean see if it's possible to change the default value of the slug field, to be a computed field that's the sha256 of name+description.
I already know how to create it as part of an insert operation.
MySQL 5.7 supports generated columns so you can define an expression, and it will be updated automatically for every row you insert or update.
CREATE TABLE IF NOT EXISTS MyTable (
id int NOT NULL AUTO_INCREMENT,
name varchar(50) NOT NULL,
description varchar(50) NOT NULL,
slug varchar(64) AS (SHA2(CONCAT(name, description), 256)) STORED NOT NULL,
PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;
If you use an earlier version of MySQL, you could do this with TRIGGERs:
CREATE TRIGGER MySlugIns BEFORE INSERT ON MyTable
FOR EACH ROW SET slug = SHA2(CONCAT(name, description));
CREATE TRIGGER MySlugUpd BEFORE UPDATE ON MyTable
FOR EACH ROW SET slug = SHA2(CONCAT(name, description), 256);
Beware that concat returns NULL if any one column in the input is NULL. So, to hash in a null-safe way, use concat_ws. For example:
select md5(concat_ws('', col_1, .. , col_n));
Use MySQL's CONCAT() to combine the two values and SHA2() to generate a 256 bit hash.
CREATE TABLE IF NOT EXISTS `mytable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`description` varchar(50) NOT NULL,
`slug` varchar(64) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `mytable` (`name`,`description`,`slug`)
VALUES ('Fred','A Person',SHA2(CONCAT(`name`,`description`),256));
SELECT * FROM `mytable`
OUTPUT:
COLUMN VALUE
id 1
name Fred
description A Person
slug ea76b5b09b0e004781b569f88fc8434fe25ae3ad17807904cfb975a3be71bd89
Try it on SQLfiddle.