User-defined function sorting column problem - mysql

I have taken reference from the internet about one user-defined function to locate 'nth occurrence of a string to do the sort column name in the database. I am using MySQL 5.5 version, not the latest version. Here is my sample database link https://dbfiddle.uk/?rdbms=mysql_5.5&fiddle=bcb32a6b47d0d5b061fd401d0888bdc3
My problem is I want to sort column name in the database follow the prefix number, but I am using below the SQL query, it doesn't work.
select t.id,t.name
from
(
select t.*, cast((case when col1_col2_ref > 0
then
substring_index(modified_name,'-',1)
else
modified_name
end
) as unsigned) col1
, cast((case when col1_col2_ref > 0
and col3_ref > 0
then
substr(modified_name,(col1_col2_ref + 1),(col3_ref - (col1_col2_ref + 1)))
when col1_col2_ref > 0
then
substr(modified_name,(col1_col2_ref + 1))
end) as unsigned) col2
, cast((case when col3_ref > 0
and col4_ref > 0
then
substr(modified_name,(col3_ref + 1),(col4_ref - (col3_ref + 1)))
when col3_ref > 0
then
substr(modified_name,(col3_ref + 1))
end) as unsigned) col3
, cast((case when col4_ref > 0
then
substr(modified_name,(col4_ref + 1))
end) as unsigned) col4
from
(
select t.*,substring_index(name,' ',1) modified_name
,locate('-',name,1) col1_col2_ref
,locate('/',name,1) col3_ref
,locate('/',name,locate('/',name,1)+1) col4_ref
from filing_code_management t
) t
) t
order by col1,col2,col3,col4
It shows me below the result, it cannot sort properly.
Output 1
Actually I want the output sample like below:
Output 2
Output 3
This is before I can sort the column name link, https://dbfiddle.uk/?rdbms=mysql_5.5&fiddle=6b12a4d42359cb30f27a5bfb9d0c8210. After I am inserted into new data, it cannot work for me. Maybe an example in new data like this error (R)100-6-2-2 Mesyuarat Majlis Kerajaan Negeri (MMKN) JKK if I put () in front. Or in new data like this error 100-1-1 Penggubalan/Penyediaan/Pindaan Undang-Undang/Peraturan if I put / in between the word.
Hope someone can guide me to solve this problem. Thanks.

You should be able to adapt the following code to your needs (tested at your DB Fiddle!). I've used the file_name column instead of the name column to slightly simplify building the sort fields, as it seems the file name is always repeated in the first part of the name field anyway.
This would be quite a bit simpler using regular expression support, but I note that the version of MySQL you are using doesn't have this feature (I think it arrives in SQL 8.0, if I'm not mistaken).
SELECT id,
num_hyphens,
CAST(SUBSTRING_INDEX(CONCAT(file_name_adj,'-'), '-', 1) AS UNSIGNED) AS sort1,
CAST(CASE WHEN num_hyphens = 0
THEN '0'
ELSE SUBSTRING_INDEX(SUBSTRING_INDEX(file_name_adj,'-', 2), '-',-1)
END AS UNSIGNED) AS sort2,
CAST(CASE WHEN num_hyphens <= 1
THEN '0'
ELSE SUBSTRING_INDEX(SUBSTRING_INDEX(file_name_adj,'-', 3), '-',-1)
END AS UNSIGNED) AS sort3,
CAST(CASE WHEN num_hyphens <= 2
THEN '0'
ELSE SUBSTRING_INDEX(file_name_adj, '-', -1)
END AS UNSIGNED) AS sort4,
file_name,
name
FROM (
SELECT id, name, MID(file_name, instr(file_name, ')') + 1) AS file_name_adj, file_name,
LENGTH(file_name) - LENGTH(REPLACE(file_name, '-', '')) AS num_hyphens
FROM filing_code_management
) t1
ORDER BY sort1, sort2, sort3, sort4

Related

Convert a string with operation to int using sql query

persons is a tinytext field and can contain "4 + 2", "4+2", "4 +2", "5" or "" and so on.
I would like to select and int like 6, 6, 6, 5 and 0 from that MySQL 5.6 Table.
Tried this without success:
SELECT CAST(persons AS INT) FROM Table
SELECT CONVERT(INT, persons ) FROM Table
If + is the only operator and it appears once, then:
select (case when col like '%+%'
then substring_index(col, '+', 1) + substring_index(replace(col, ' ', ''), '+', -1)
else col + 0
end) as added_value
use SUBSTRING_INDEX
select SUBSTRING_INDEX(col , "+", 1)+ SUBSTRING_INDEX(col , "+", -1) as col1
from cte where col like '%+%'
union all
select SUBSTRING_INDEX(col , "+", 1) from cte where col not like '%+%'
output
col1
6
6
6
5
the upper solution will work only for your sample data
demo link
What database are you using? You may need to use something db specific. e.g. in oracle you can do:
select dbms_aw.eval_number ('4+2') from dual
It will return 6.
Generally speaking - using dynamic SQL you can easily achieve this.

Join SQL query doesn't work

I have a table app_form_data_audit_trail which I need to join to another table app_fd_listofcomponents.
-The foreign key needs first to be extracted from a column collating some other data before getting the 1st table joined with the 2nd one.
-Some records from the first table needs to be filtered out.
I made the following sql query but getting error. I created a sqlfiddle here for troubleshooting purpose..
Thanks for your help :-)
SELECT DATETIME
,username
,CASE
WHEN locate('operation', DATA) > 0
THEN substring_index(substring_index(DATA, '"operation":"', - 1), '"', 1)
ELSE 0
END AS operation
,CASE
WHEN locate('operation', DATA) > 0
THEN substring_index(substring_index(DATA, '"opdetails":"', - 1), '"', 1)
ELSE 0
END AS opdetails
,CASE
WHEN locate('operation', DATA) > 0
THEN substring_index(substring_index(DATA, '"componentName":"', - 1), '"', 1)
ELSE 0
END AS componentid
,CASE
WHEN locate('operation', DATA) > 0
THEN substring_index(substring_index(DATA, '"package":"', - 1), '"', 1)
ELSE 0
END AS package
FROM app_form_data_audit_trail
WHERE DATA LIKE "%Operation%"
INNER JOIN app_fd_listofcomponents ON app_form_data_audit_trail.componentid = app_fd_listofcomponents.id
FROM app_form_data_audit_trail
INNER JOIN app_fd_listofcomponents ON app_form_data_audit_trail.componentid = app_fd_listofcomponents.id
WHERE DATA LIKE "%Operation%"
Thanks to all, Finally I came to write the query as follows and it works !
SELECT
app_form_data_audit_trail.datetime,
app_form_data_audit_trail.username,
app_form_data_audit_trail.data,
app_fd_listofcomponents.c_component,
substring_index(substring_index(app_form_data_audit_trail.data,'"operation":"',-1),'"',1) AS operation,
substring_index(substring_index(app_form_data_audit_trail.data,'"opdetails":"',-1),'"',1) AS opdetails,
substring_index(substring_index(app_form_data_audit_trail.data,'"package":"',-1),'"',1) AS package
FROM app_form_data_audit_trail
JOIN app_fd_listofcomponents
ON app_form_data_audit_trail.data LIKE CONCAT ('%',app_fd_listofcomponents.id,'%')

mySQL extract number or string depending on value

Please take a look at a table below:
I need "the last part" of "what I have" to be number if it's all number and string if it contains character.
In Excel I've achieved this with the following function (as shown above):
=IFERROR(VALUE(TEXT(D2;"0"));TEXT(D2;"0"))
However I want to do this in mySQL in order to compute more effectively.
I've "floated" somwhere around CASE with CAST or CONVERT and also TRIM functions, but I haven't been able to put up something sensical.
A nice "bonus" would be to extract "the part part" by looking for the last "" character (so first "" from right of the string) but no idea at all how to achieve that.
Use SUBSTRING_INDEX.
Query
SELECT
CASE
WHEN SUBSTRING_INDEX(records.data, '_', -1) > 1 # is int check '00004949' returns 1
THEN SUBSTRING_INDEX(records.data, '_', -1) + 0 # converts '00004949' to 4949
ELSE SUBSTRING_INDEX(records.data, '_', -1)
END
AS word
FROM (
SELECT "TRA_PL_NWL_EMA_NWLY_DAI_000_20170610_IN1_01P002bc" AS DATA
UNION
SELECT "TRA_PL_NWL_EMA_NWLY_DAI_000_2017_0909_JET_00004949" AS DATA
) records
Result
word
----------
01P002bc
4949
Following query will somewhat achieve the task:
SELECT
case
when SUBSTRING_INDEX(value, "_", -1) REGEXP('(^[0-9]+$)')
then Trim(Leading 0 from SUBSTRING_INDEX(value, "_", -1))
else SUBSTRING_INDEX(value, "_", -1)
end as Value
From yourtable;
Click here for Demo
Hope it helps!
I hope this is what u want.
At least it does what you have asked for.
SELECT
CASE
WHEN (
CONVERT(
substring(
txt,
LENGTH(txt) - LOCATE('_', REVERSE(txt))+2,
length(txt)
)
, signed integer
)
) = 0
THEN substring(
txt,
LENGTH(txt) - LOCATE('_', REVERSE(txt))+2,
length(txt)
)
ELSE CONVERT(
substring(
txt,
LENGTH(txt) - LOCATE('_', REVERSE(txt))+2,
length(txt)
)
, signed integer
)
END as NUMBER
from test.test
This is my test Table and result of SQL:
txt NUMBER
DA_DA_ADAD_ADAD_ADAD_asd123 asd123
DA_DA_ADAD_ADAD_ADAD_000123 123
DA_DA_ADAD_ADAD_ADAD_444 444
DA_DA_ADAD_ADAD_ADAD_bsd123 bsd123
DA_DA_ADAD_ADAD_ADAD_csd123 csd123
DA_DA_ADAD_ADAD_ADAD_dsd123 dsd123

Ordering an existing database by numbers in a string

This one is a bit of a nightmare. I'm working on frontend for an existing database,and I'm having to jump through hoops to make sure that data is displayed in the correct order. It'd make my life a whole lot simpler if I could just order by Id, but the Ids have little or no correlation to the data.
Here's what I mean
ID DATA
357 "7-1-5: Sensitive Information I can't share"
2521 "30-2-8-17: Yet more sensitive Information"
6002 "9-30: There's a 10 behind the colon, because I hate you"
8999 "2-2-4: This was populated in no particular order"
9001 "30-3: More Info."
I'm trying to get it ordered like this
ID DATA
0001 "2-2-4: This was populated in no particular order"
0002 "7-1-5: Sensitive Information I can't share"
0003 "9-30: There's a 10 behind the colon, because I hate you"
0004 "30-2-8-17: Yet more sensitive Information"
0005 "30-3: More Info."
Basically, I need it to sort by each 1 to 2 digit number that's separated by dashes, again and again, so that 1-3 comes after 1-2-1, which comes after 1-1-50.
Like I said in the beginning, I'm a frontend guy, so executing stuff in MySql is more than I can do alone. Any help would be immensely appreciated.
Edit: I just realized there's foreign keys in a separate table pointing to this one, making things just that much worse.
Try this query:
SELECT col
FROM yourTable
ORDER BY SUBSTRING(col, INSTR(col, '"') + 1, INSTR(col, ':') - INSTR(col, '"') - 1)
The SUBSTRING(...) term in the ORDER BY clause extracts just the ids from the text. Presumably you want them to sorted numerically, from left to right. Even though they are varchar, numerical sorting should still work.
For your sample data, this produced the following output:
ID 8999 DATA "2-2-4: This was populated in no particular order"
ID 2521 DATA "30-2-8-17: Yet more sensitive Information"
ID 357 DATA "7-1-5: Sensitive Information I can't share"
ID 6002 DATA "9-30: There's a 10 behind the colon, because I hate you"
Fiddle is down as of the writing of this answer, but I tested the query in MySQL Workbench and it seems to work well.
Edit:
If you want to assign a new ID to each record, you create a new table (newTable) with an ID column which is auto increment. Then you can use INSERT INTO ... SELECT along with the above ORDER BY logic to populate the table. The ID field should be incremented automatically by MySQL.
INSERT INTO newTable (`id`, `col`)
SELECT NULL, col
FROM yourTable
ORDER BY SUBSTRING(col, INSTR(col, '"') + 1, INSTR(col, ':') - INSTR(col, '"') - 1)
Something like this should work, but it is very delicate; all the fields calculated in the outer SELECT (after the *) must be performed in that exact order. Note that the calculations aliased nl#, p#, and r# (except r0) repeat exactly... so the query is not as complicated as it initially appears.
SELECT *
, #r := dataOrd AS r0 -- #r is "remaining string"
, #nextSep := INSTR(#r, '-') AS nl1
, CAST(CASE #nextSep WHEN 0 THEN #r ELSE SUBSTR(#r, 1, #nextSep-1) END AS UNSIGNED) AS p1
, #r := CASE #nextSep WHEN 0 THEN '' ELSE SUBSTRING(#r, #nextSep+1) END AS r1
, #nextSep := INSTR(#r, '-') AS nl2
, CAST(CASE #nextSep WHEN 0 THEN #r ELSE SUBSTR(#r, 1, #nextSep-1) END AS UNSIGNED) AS p2
, #r := CASE #nextSep WHEN 0 THEN '' ELSE SUBSTRING(#r, #nextSep+1) END AS r2
, #nextSep := INSTR(#r, '-') AS nl3
, CAST(CASE #nextSep WHEN 0 THEN #r ELSE SUBSTR(#r, 1, #nextSep-1) END AS UNSIGNED) AS p3
, #r := CASE #nextSep WHEN 0 THEN '' ELSE SUBSTRING(#r, #nextSep+1) END AS r3
, #nextSep := INSTR(#r, '-') AS nl4
, CAST(CASE #nextSep WHEN 0 THEN #r ELSE SUBSTR(#r, 1, #nextSep-1) END AS UNSIGNED) AS p4
, #r := CASE #nextSep WHEN 0 THEN '' ELSE SUBSTRING(#r, #nextSep+1) END AS r4
FROM
(
SELECT *, SUBSTR(`DATA`, 1, INSTR(`DATA`, ':') - 1) AS dataOrd
FROM yourTable
) AS sepSubQ
ORDER BY p1, p2, p3, p4
;
Technically, the last #r assignment (aliased r4) is unnecessary, but it completes the pattern that will be need to be repeated if you need to handle more than 4 ordering "parts"; in which case you just need to repeat the last three field calculations (with incremented aliases).
If you want to be rid of the "working" fields, you can wrap this in another outer query only selects the fields from the original table you wanted and the pX fields from the above query; technically, you don't even need to select the pX fields as the order will already be performed by this query, or can be done in the wrapper without selecting them.
SELECT `ID`, `DATA`
FROM ([the query above]) AS subQ
ORDER BY p1, p2, p3, p4
;

How to check negative values in mysql and decide the output after that

I am using a mysql case for some calculation and my demand is that if the output is negative then i will show 0 in the place of the negative column.Otherwise the positive value.
Let me post the query:
select
concat(jo.title,' (', CCP.name, ')'), PL.analyst, PL.consultant,
PL.csm,
(CASE
WHEN PL.productType like 'Staffing' THEN (SELECT ((DATEDIFF(PL.dateEnd, PL.dateClientEffective)) -((WEEK(PL.dateEnd) - WEEK(PL.dateClientEffective)) * 2) - (case when weekday(PL.dateEnd) = 6 then 1 else 0 end) - (case when weekday(PL.dateClientEffective) = 5 then 1 else 0 end)) as DifD) * 8 * (PL.clientBillRate-PL.payRate) ELSE (PL.salary*PL.fee)END) Value
Now if the value is negative, then it should show 0 else will show the original value
The simplest way is simply to use greatest():
select greatest(<expression>, 0) as col
It is unclear what column/expression you want to do this for in your query. But, you can just plug it in.
The advantage of this method over other methods is:
The only needs to appear once in the query. Not having to duplicate code reduces the likelihood of errors.
There is no need for a subquery. This is an issue in MySQL only because MySQL materializes subqueries.
Simply use SIGN() in mysql
It returns 1 as a positive value and -1 is a negative value
Example
SELECT SIGN(500); -- (Output is 1)
SELECT SIGN(2500); -- (Output is 1)
SELECT SIGN(-999); -- (Output is -1)
SELECT SIGN(-5); -- (Output is -1)
please check this, haven't tested yet.
Note: this might not be the best solution
select concat(jo.title,' (', CCP.name, ')'), PL.analyst, PL.consultant, PL.csm,
if(
(CASE WHEN PL.productType like 'Staffing' THEN (SELECT ((DATEDIFF(PL.dateEnd, PL.dateClientEffective)) -((WEEK(PL.dateEnd) - WEEK(PL.dateClientEffective)) * 2) -
(case when weekday(PL.dateEnd) = 6 then 1 else 0 end) -
(case when weekday(PL.dateClientEffective) = 5 then 1 else 0 end)) as DifD) * 8 * (PL.clientBillRate-PL.payRate) ELSE (PL.salary*PL.fee)END)
< 0,0,
(CASE WHEN PL.productType like 'Staffing' THEN (SELECT ((DATEDIFF(PL.dateEnd, PL.dateClientEffective)) -((WEEK(PL.dateEnd) - WEEK(PL.dateClientEffective)) * 2) -
(case when weekday(PL.dateEnd) = 6 then 1 else 0 end) -
(case when weekday(PL.dateClientEffective) = 5 then 1 else 0 end)) as DifD) * 8 * (PL.clientBillRate-PL.payRate) ELSE (PL.salary*PL.fee)END)
) Value
Just do like this :
select temp.* , --all your columns
if(temp.value<0,0,temp.value) as Value
from
( your query mentioned above) temp;