50% OFF!!!

Showing posts with label mysql. Show all posts
Showing posts with label mysql. Show all posts

Tuesday, December 6, 2022

MySQL: SUBSTRING_INDEX vs MID+INSTR (which is better)

Hello there.


SUBSTRING_INDEX vs MID+INSTR

I wanted to pull from domain name out of an email (or just remove the username part of an email).

There are two simple functions (and even more...) to do it:

1] SUBSTRING_INDEX('testuser@domain.com','@',-1)

use built in function SUBSTRING_INDEX to explode the string by '@' and get the last element.

 

2] MID('testuser@domain.com', INSTR('testuser@domain.com','@')+1, 999)

find '@' position using INSTR and get all string from that position until the end of string by MID function. 

 

But which one of them have better performance?

Using BENCHMARK function for Measuring the Speed of Expressions and Functions.

1] SELECT  BENCHMARK(100000000, SUBSTRING_INDEX('testuser@domain.com','@',-1))

/* Affected rows: 0  Found rows: 1  Warnings: 0  Duration for 1 query: 12.625 sec. */


2] SELECT  BENCHMARK(100000000, MID('testuser@domain.com', INSTR('testuser@domain.com','@')+1, 999))

/* Affected rows: 0  Found rows: 1  Warnings: 0  Duration for 1 query: 19.157 sec. */


BENCHMARK function results may vary between different devices, but on the same device can give quite clear results.

 

Summary:

SUBSTRING_INDEX won and have better results than MID + INSTR


Sunday, October 17, 2021

MySQL | join/combine 2 SMALLINT/TINYINT into 1 INT

Hi All.

Today we will try to combine two MYSQL small integer values into one integer value.
Obviously, there is limit for the size of integer.
We will discuss only UNSIGNED integer column types (non negative values).

2 Solutions are suggested below:
Solution-1: concatenate base-9 value of each number with '9' separator.
Solution-2: concatenate the original values with padding (left) zeros.
[Note: if the values allowed to be NULL, solution-2 is not supported!].

# LIMITS #

First, lets learn about the range limits of each data-type:
DatatypeTypeUNSIGNED RangeMax value (base9)
INTA normal-size integer0 to 4294967295-
SMALLINTA small integer0 to 65535CONV(65535, 10, 9) = 108806
TINYINTA very small integer0 to 255CONV(255, 10, 9) = 313

# SOLUTION-1 (Support NULL values) #

Combine and merge two small integers,
into 1 integer by converting each number to base-9 integer,
and then concatenate with the digit '9' as separator.

Two SMALLINTs cannot be saved inside INT value:
1088069108806 > 4294967295.
But combining SMALLINT with TINYINT is possible (and aswell two TINYINT).

MYSQL CODE:
############ TABLE DEFINITION:
CREATE TABLE `table_test` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`sint1` SMALLINT(5) UNSIGNED NULL DEFAULT NULL,
`tint2` TINYINT(3) UNSIGNED NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE);

############ SELECT:
SELECT  T.*, 
        CONV(SUBSTRING_INDEX(combined,'9',1), 9,10) as _sint1, 
        CONV(SUBSTRING_INDEX(combined,'9',-1),9,10) as _tint2
FROM (
	SELECT 	id, sint1, tint2, 
                CONCAT( IFNULL(CONV(sint1, 10, 9),''), 
                        '9', 
                        IFNULL(CONV(tint2, 10, 9),'')
                ) AS combined
	FROM table_test
) AS T

            

And output will be:
idsint1tint2>combined>_sint1>_tint2
1NULLNULL9NULLNULL
21234NULL162191234NULL
3NULL2559313NULL255
43123384250942312338
53410243460893003410243
62685207361392502685207
71327224173492681327224
82047102272491232047102
92953189404192302953189
103686117504591403686117
114455186100920445518
122482663357973248266
133072744183982307274
142036422712946203642
152047139272491642047139
161573236213792821573236
172464173333792122464173
183656155501291823656155
193305120447291433305120
205894121806891445894121

# SOLUTION-2 (NULLS not supported) #

Here we just merge the original values with LEFT-PADDING of zeros.

MYSQL CODE:
############ TABLE DEFINITION:
CREATE TABLE `table_test` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`sint1` SMALLINT(5) UNSIGNED NULL DEFAULT NULL,
`tint2` TINYINT(3) UNSIGNED NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE);

############ SELECT:
SELECT  T.*, 
        SUBSTR(combined,1,LENGTH(combined)-3)+0 AS _sint1,
        RIGHT(combined,3)+0 AS _tint2
FROM (
	SELECT 	id, sint1, tint2, 
                CONCAT(sint1, LPAD(tint2,3,0)) AS combined
	FROM table_test
) AS T
            
And output will be:
idsint1tint2>combined>_sint1>_tint2
43123383123038312338
5341024334102433410243
6268520726852072685207
7132722413272241327224
8204710220471022047102
9295318929531892953189
10368611736861173686117
114455184455018445518
122482662482066248266
133072743072074307274
142036422036042203642
15204713920471392047139
16157323615732361573236
17246417324641732464173
18365615536561553656155
19330512033051203305120
20589412158941215894121

This solution can work with two SMALLINT,
if one of them limited up to 42949,
and it will be the left operand.


Friday, June 19, 2015

MySQL | find median (Nth element) without join!

This is my suggestion to find on mysql select command
the Median (Nth element) without joining the table to itself (or to new custom one).

My method, uses string manipulation
so I'm not sure it is good for big tables.
(I tested it with medium size tables on Mysql 5.5.28)

The advantage of this method, 
that it works also if we need to find the MEDIAN for each group in the select query.

the code is using: SUBSTRING_INDEX and GROUP_CONCAT methods.


Here is test code for test table:
DROP TABLE test.test_median
CREATE TABLE test.test_median AS
SELECT 'book' AS grp, 4 AS val UNION ALL
SELECT 'book', 7 UNION ALL
SELECT 'book', 2 UNION ALL
SELECT 'book', 2 UNION ALL
SELECT 'book', 9 UNION ALL
SELECT 'book', 8 UNION ALL
SELECT 'book', 3 UNION ALL

SELECT 'note', 11 UNION ALL

SELECT 'bike', 22 UNION ALL
SELECT 'bike', 26 



and the code for finding the median for each group:
SELECT grp,
         SUBSTRING_INDEX( SUBSTRING_INDEX( GROUP_CONCAT(val ORDER BY val), ',', COUNT(*)/2 ), ',', -1) as the_median,
         GROUP_CONCAT(val ORDER BY val) as all_vals_for_debug
FROM test.test_median
GROUP BY grp

The output:

grp the_median all_vals_for_debug
bike 22 22,26
book 4 2,2,3,4,7,8,9
note 11 11


Hope it helps you there...


Sunday, February 10, 2013

MySQL xpath ExtractValue with delimiter

Hello,
I recently noticed the XPATH option inside MySQL.
What a wonderfull way for handling XML. (http://dev.mysql.com/doc/refman/5.1/en/xml-functions.html)
I used ExtractFunction, and noticed that this function is space delimited:
Because ExtractValue() returns multiple matches as a single space-delimited string

So I needed to create the same function with custom text delimted (comma, semi-colon or anything you want).

The function called: EXTRACTVALUE_ALL which is the same as EXTRACTVALUE just get a delimiter text to seperate between results. (seperated text).
Tested on MySQL 5.5.28.

 
Create Function CODE:
DROP FUNCTION IF EXISTS EXTRACTVALUE_ALL;

DELIMITER |
CREATE FUNCTION EXTRACTVALUE_ALL(p_xml TEXT, p_xpathExpr TEXT, p_delimiter TEXT) RETURNS TEXT
BEGIN
DECLARE total_elements INT;
DECLARE xpath_expression_count, xpath_expression_index  TEXT;
DECLARE single_tag, result  TEXT;

# calc TOTAL elements
SET xpath_expression_count = CONCAT('count(', p_xpathExpr, ')');
SELECT EXTRACTVALUE(p_xml, xpath_expression_count) INTO total_elements;

# run over elements (create long text)
SET result = '';
SET xpath_expression_index = CONCAT(p_xpathExpr, '[$@i]');
SET @i = 1;
WHILE @i <= total_elements DO
SET single_tag = EXTRACTVALUE(p_xml, xpath_expression_index);
SET result = IF(result='', single_tag, CONCAT(result, p_delimiter, single_tag));
SET @i = @i + 1;
END WHILE;

# return total result
RETURN result;
END |


Usage Code:
SET @XXXMMMMLLL = '      ';

SELECT EXTRACTVALUE_ALL(@XXXMMMMLLL, '/descendant-or-self::time', '  |  ');


Output:
time1  |  time2


Rememeber that you will need a MYSQL permission for creating new functions.

MDB-BLOG :)