Can anyone please shorten this query? - mysql

This query generates next datafile name for a existing schema.
For e.g. id last datafile name is test_schema_05.dbf
then this query gives test_schema_06.dbf
I need to shorten this query .
Is this possible?
SELECT CONCAT
(SUBSTR
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/'))) + 1
)
),
1,
INSTR
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/'))) + 1
)
),
'_',
1,
(LENGTH
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/'))) + 1
)
)
) - LENGTH
(REPLACE
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/'))) + 1
)
),
'_'
)
)
)
)
),
CONCAT
('0',
SUBSTR
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/' ))) + 1
)
),
INSTR
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/' ))) + 1
)
),
'_',
1,
(LENGTH
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/'))) + 1
)
)
) - LENGTH
(REPLACE
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/'))) + 1
)
),
'_'
)
)
)
) + 1,
INSTR
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/'))) + 1
)
),
'.',
1
) - INSTR
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/' ))) + 1
)
),
'_',
1,
(LENGTH
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/' ))) + 1
)
)
) - LENGTH
(REPLACE
(MAX
(SUBSTR
(file_name,
INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/' ))) + 1
)
),
'_'
)
)
)
) - 1
) + 1
)
)
|| '.dbf' AS data_file_name
FROM dba_data_files
WHERE tablespace_name =
(SELECT default_tablespace
FROM dba_users
WHERE username = 'schema_name'
);

this almost made me cry. Also, it looks like your query is just somewhat wrong, as your MAX functions appear to only contain a single value, which is a string
You have SO MANY repeated lines that are clogging up your query, and your brain when you try to read it. Extract that out to a variable! Assuming this is all inside a sproc somewhere, you end up with something like this:
DECLARE i_intLocation INTEGER;
SET i_intLocation = INSTR (file_name, '/', 1, LENGTH (file_name) - LENGTH (REPLACE (file_name, '/'))) + 1;
DECLARE i_strSubstr VARCHAR;
SET i_strSubstr = (SUBSTR (file_name, i_intLocation));
SELECT
CONCAT(
SUBSTR(
MAX(i_strSubstr),
1,
INSTR(
MAX(i_strSubstr),
'_',
1,
(
LENGTH(MAX(i_strSubstr))
- LENGTH(
REPLACE(
MAX(i_strSubstr),
'_'
)
)
)
)
),
CONCAT(
'0',
SUBSTR(
MAX(i_strSubstr),
INSTR(
MAX(i_strSubstr),
'_',
1,
(
LENGTH(MAX(i_strSubstr))
- LENGTH(
REPLACE(
MAX(i_strSubstr),
'_'
)
)
)
) + 1,
INSTR(
MAX(i_strSubstr),
'.',
1
)
- INSTR(
MAX(i_strSubstr),
'_',
1,
(
LENGTH(MAX(i_strSubstr))
- LENGTH(
REPLACE(
MAX(i_strSubstr),
'_'
)
)
)
) - 1
) + 1
)
)
|| '.dbf' AS data_file_name
FROM dba_data_files
WHERE tablespace_name =
(SELECT default_tablespace
FROM dba_users
WHERE username = 'schema_name'
);

Using a sub query to get the highest file name to shorten the code it would come out something like this, but I suspect that this is no more efficient (and might well be less efficient).
SELECT CONCAT(SUBSTR(sub1.derived_bit,1,INSTR(sub1.derived_bit,'_',1,(LENGTH(sub1.derived_bit)- LENGTH(REPLACE(sub1.derived_bit,'_'))))),
CONCAT('0',SUBSTR(sub1.derived_bit,INSTR(sub1.derived_bit,'_',1,(LENGTH(sub1.derived_bit)- LENGTH(REPLACE(sub1.derived_bit,'_'))))+ 1,INSTR
(sub1.derived_bit,'.',1)- INSTR(sub1.derived_bit,'_',1,(LENGTH(sub1.derived_bit)- LENGTH(REPLACE(sub1.derived_bit, '_' ))))- 1)+ 1), '.dbf') AS data_file_name
FROM
(
SELECT MAX(SUBSTR(file_name, INSTR(file_name, '/', 1, LENGTH(file_name) - LENGTH(REPLACE(file_name, '/'))) + 1)) as derived_bit
FROM dba_data_files
INNER JOIN dba_users
ON dba_data_files.tablespace_name = dba_users.default_tablespace AND username = 'schema_name'
) sub1
However this is just based on your code which has syntax errors (which I have just repeated in my code above). For example your code is using INSTR with 4 parameters (should only have 2). Similarly you are using REPLACE with only 2 parameters. Your functions are looking for '/' while the file names you give as example do not contain any '/' characters.
How fixed is the format of the file name? Is there only ever 1 . in the file name? Is the number to increment always immediatly before the .dbf?

Related

MySql function: Hexadecimal conversation to Float

I have a bit of a challenge in converting a hexadecimal string into a float.
Here is an example:
Hex:
3F62 0C3C
Binary: 00111111011000100000110000111100
Conversion result (float big endian):
0.8829992
Can this conversion be achieved with MySql functions and, if possible, how?
Thank you for your help.
So, I assume you have your hexadecimal string in some sort of TEXT type:
SELECT #hexstring := '3F62 0C3C'
The first thing you need to do is to convert it to a decimal. You can do this using the CONV function. For that, first remove the whitespace from the string with the REPLACE function. Depending on the parameters of the CONV function, you can e.g. get a binary or a decimal representation:
SELECT #binvalue := CONV(REPLACE(#hexstring, ' ', ''), 16, 2) -- 111111011000100000110000111100
SELECT #decvalue := CONV(REPLACE(#hexstring, ' ', ''), 16, 10) -- 1063390268
From the decimal representation, you can calculate your float (based on this SO answer):
SELECT SIGN(#decvalue) * (1.0 + (#decvalue & 0x007FFFFF) * POWER(2.0, -23))
* POWER(2.0, (#decvalue & 0x7f800000) / 0x00800000 - 127)
Result:
0.8829991817474365
Or all in one:
SELECT SIGN(CONV(REPLACE(#hexstring, ' ', ''), 16, 10))
* (1.0 + (CONV(REPLACE(#hexstring, ' ', ''), 16, 10) & 0x007FFFFF)
* POWER(2.0, -23))
* POWER(2.0, (CONV(REPLACE(#hexstring, ' ', ''), 16, 10) & 0x7f800000) / 0x00800000 - 127)
Test all of it together in this db<>fiddle.

Informatica column concatenation

I have 2 columns in a single my input as given below :
Input :
Column1
A|B|C|D
Column2
1|2|3|4
And I need to get the single Output as shown below :
Column1
A.1 || B.2 || C.3 || D.4
Can anyone help me to get the output ?
You can split the columns into multiple parts based on delimitter and then concat.
1 find position of | in first column like this -
v_pos1 = InStr( col1, '|', 1, 1)
v_pos2 = InStr( col1, '|', 1, 2)
v_pos3 = InStr( col1, '|', 1, 3)
...
2 Create bunch of variable ports to capture actual values-
v_val1 = iif(v_pos1 =0,col1, SubStr( col1, 1, v_pos1 - 1))
v_val2 = iif(v_pos2 =0,'', SubStr( col1, v_pos1 + 1), SubStr( col1, v_pos1 + 1, v_pos2 - v_pos1 - 1))
v_val2 = iif(v_pos2 =0,'', SubStr( col1, v_pos2 + 1), SubStr( col1, v_pos2 + 1, v_pos3 - v_pos2 - 1))
...
3 Repeat above steps for column2 as well. Lets asseme they are v_val_col21,v_val_col22...
4 Once you have both set of columns, then concat both to get final values.
o_final = v_val1 ||'.' || v_val_col21 ||'||'|| v_val2 ||'.' || v_val_col22...

How to auto increment a string with sql query

I am stuck at a point where i have to increment a string, and my strings are of type C001,SC001,B001
in my data base they are defined like
what i am trying to do do is write a query which check the previous highest code present into my db and the incriment it to +1
for example C001 -> C002,C009->C010,C099`->C100 and so on
Similarly for SC001->SC002,SC009->SC010,SC099->SC100 and so on
Similarly fro B001 -> B002,B009->B010,B099`->B100 and so on
I have a query which my friend has suggested me to use but that query only incriminating AAAA->AAAA01 , AAAA09->AAAA10
query is
SELECT id AS PrevID, CONCAT(
SUBSTRING(id, 1, 4),
IF(CAST(SUBSTRING(id, 5) AS UNSIGNED) <= 9, '0', ''),
CAST(SUBSTRING(id, 5) AS UNSIGNED) + 1
) AS NextID
FROM (
-- since you allow strings such as AAAA20 and AAAA100 you can no longer use MAX
SELECT id
FROM t
ORDER BY SUBSTRING(id, 1, 4) DESC, CAST(SUBSTRING(id, 5) AS UNSIGNED) DESC
LIMIT 1
) x
when i am replacing ID with CategoryCode it is giving me PrevID-C004 NextID-C00401 which is not my requirement i want PrevID-C004 and NextID->C005
NOTE i am using my sqlServer 5.1
Just try this one ,
SELECT
CategoryCode,CAST(CONCAT(LPAD(CategoryCode,1,0),LPAD(MAX(RIGHT(CategoryCode,
3)) + 1, 3, 0) ) AS CHAR),
FROM test
SELECT
SubCategoryCode,CAST(CONCAT(LPAD(SubCategoryCode,2,0),
LPAD(MAX(RIGHT(CategoryCode, 3)) + 1, 3, 0) ) AS CHAR),
FROM test
SELECT
BrandCode,CAST(CONCAT(LPAD(BrandCode,1,0), LPAD(MAX(RIGHT(BrandCode, 3)) +
1, 3, 0)) AS CHAR) FROM test

Ordering by inches value in MYSQL

I've searched for the answer to this but can't quite work it out. I think I'm close but need your help please.
I have the following mysql query:
SELECT `size`
FROM `table`
GROUP BY `size`
ORDER BY CONVERT(SUBSTR(size, 1, POSITION('/' IN size) - 1), UNSIGNED INTEGER) ASC,
SUBSTRING_INDEX(size,'/',1)/SUBSTRING_INDEX(size,'/',-1) ASC
Running this gives the following result:
1"
2"
4"
3"
1/2"
3/8"
3/4"
11/4"
11/2"
21/2"
I need the sizes to come out smallest to largest. Any help on this would be massively appreciated. Thank you.
Note, I have also tried removing the " from the sizes in the database and the result was exactly the same.
An example of the dataset:
1 1/2"
1 1/4"
1"
1/2"
1/4"
1/8"
10"
11/2"
11/4"
11/4""
12"
14"
16"
2 1/2"
2"
21/2"
3"
3/4"
3/8"
4"
5"
6"
8"
Lovely data format. I think you are going to have to do the division. The calculation is something like this:
order by (case when size like '%/%'
then (substring_index(size, '/', 1) + 0) / (substring_index(size, '/', 2) + 0)
else size + 0
end)
Even if the " is part of the field, this will still work. The + 0 does "silent" conversion. That is, it converts the string up to the first non-numeric character.
EDIT:
If you could have spaces with whole numbers first, you would do:
order by (case when size like '% %/%'
then (substring_index(size, 1, ' ') + 0) +
((substring_index(substring_index(size, ' ', 2), '/', 1) + 0) /
(substring_index(size, '/', -1) + 0)
)
when size like '%/%'
then (substring_index(size, '/', 1) + 0) / (substring_index(size, '/', 2) + 0)
else size + 0
end)
You query almost work. However in first part of the order by you should substract the position from the string length.
SELECT `size`
FROM `size`
GROUP BY `size`
ORDER BY CONVERT(SUBSTR(size, 1, LENGTH(size) - POSITION('/' IN size) - 1), UNSIGNED INTEGER) ASC,
SUBSTRING_INDEX(size,'/',1)/SUBSTRING_INDEX(size,'/',-1) ASC
http://sqlfiddle.com/#!2/cd0658/6

MySQL Convert Bytes to Kilobytes, Megabytes, Gigabytes

I have a logs table that stores various file sizes in bytes. I want to be able to query the database and return the smallest possible float which has been converted to MB GB etc. At present I can return the value in MB but how do I continue to divide further to smallest value and append the unit?
SELECT CONCAT( ROUND( SUM( data_transferred ) /1048576, 2 ) , ' MB' )
FROM `logs`
Any help would be appreciated.
UPDATE:
Based on the link voodoo417 provided I updated my query to the following, which will output the most relevant file size to two decimal places and append the unit (1000 Bytes, 1 KB, 500 MB, 2 GB, etc):
SET #bytes := (SELECT SUM(data_transferred) FROM wp_ddownload_statistics);
SELECT
CASE
WHEN ABS(#bytes) < 1024 THEN CONCAT( ROUND( #bytes, 2 ), ' Bytes')
WHEN ABS(#bytes) < 1048576 THEN CONCAT( ROUND( (#bytes/1024), 2 ), ' KB')
WHEN ABS(#bytes) < 1073741824 THEN CONCAT( ROUND( (#bytes/1048576), 2 ), ' MB')
WHEN ABS(#bytes) < 1099511627776 THEN CONCAT( ROUND( (#bytes/1073741824), 2 ), ' GB' )
WHEN ABS(#bytes) < 1125899906842624 THEN CONCAT( ROUND( (#bytes/1099511627776), 2 ), ' TB')
WHEN ABS(#bytes) < 1152921504606846976 THEN CONCAT( ROUND( (#bytes/1125899906842624), 2 ), ' PB' )
WHEN ABS(#bytes) < 1180591620717411303424 THEN CONCAT( ROUND( (#bytes/1152921504606846976) ,2), ' EB' )
WHEN ABS(#bytes) < 1208925819614629174706176 THEN CONCAT( ROUND( (#bytes/1180591620717411303424), 2), ' ZB' )
WHEN ABS(#bytes) < 1237940039285380274899124224 THEN CONCAT( ROUND( (#bytes/1208925819614629174706176), 2), ' YB' )
WHEN ABS(#bytes) < 1267650600228229401496703205376 THEN CONCAT( ROUND( (#bytes/1237940039285380274899124224), 2), ' BB' )
END
I know this is an old question, but I was looking for the same thing recently, and found out that MySQL 5.7 added format_bytes function exactly for this purpose:
mysql> SELECT format_bytes(512), format_bytes(18446644073709551615);
+-------------------+------------------------------------+
| format_bytes(512) | format_bytes(18446644073709551615) |
+-------------------+------------------------------------+
| 512 bytes | 16383.91 PiB |
+-------------------+------------------------------------+
I have a more elegant solution (also using a user defined function):
CREATE FUNCTION `format_filesize`(filesize FLOAT) RETURNS varchar(20) CHARSET utf8
BEGIN
DECLARE n INT DEFAULT 1;
LOOP
IF filesize < 1024 THEN
RETURN concat(round(filesize, 2), ' ', elt(n, 'Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'));
END IF;
SET filesize = filesize / 1024;
SET n = n + 1;
END LOOP;
END
UPDATE:
Even better, and can be used outside procedures:
SET #filesize = 536870912;
SET #log = IFNULL(TRUNCATE(LOG(1024, #filesize), 0),0);
SELECT CONCAT(ROUND(#filesize / POW(1024, #log), 2), ' ',
ELT(#log + 1, 'Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'));
Renaat's code is failing when filesize is 0 (obviously you can't do LOG from zero). Therefore #log is filled with null and CONCAT produce null as well.
Correct fix is:
SET #filesize = 536870912;
SET #log = IFNULL(TRUNCATE(LOG(1024, #filesize), 0),0);
SELECT CONCAT(ROUND(#filesize / POW(1024, #log), 2), ' ',
ELT(#log + 1, 'Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'));
select concat(round(data*1048576/1073741824,2),' GB')
for example: 1024 = 1gb
select concat(round(1024*1048576/1073741824,2),' GB')
1gb
The top answer about using format_bytes, despite being answered in 2016, is sadly still not found in MariaDB.
I adapted the query from here to make a function I could use.
FUNCTION `format_bytes`(val float) RETURNS varchar(20) CHARSET latin1
BEGIN
DECLARE pw smallint;
IF val < 1024 THEN
return CONCAT(val, ' B');
END IF;
SET pw = LEAST(7, FLOOR(LOG(val) / LOG(1024)));
RETURN CONCAT(ROUND(val / POW(1024, pw), 2), ' ', SUBSTR('KMGTPEZY', pw, 1), 'B');
END
>>> SELECT format_bytes(512), format_bytes(18446644073709551615);
+-------------------+------------------------------------+
| format_bytes(512) | format_bytes(18446644073709551615) |
+-------------------+------------------------------------+
| 512 B | 16.00 EB |
+-------------------+------------------------------------+
A few things could be tweaked to more closely mimic the MySQL format_bytes, but it's not what I was aiming for.