Filtering by alphanumeric - mysql

I want to sort a table by a column named 'generic_location'.
generic location data is conformed by numbers, letters and then numbers like these:
'4A1', '5AW89', '7AA89', ETC
I trying to split the data using regexp_substr and order by CAST(generic_location AS UNSIGNED) ASC but it doesn't show as I want.
this is what I get.
4A14
4A15
4AW39
4AW70
4A75
4AW83
This is my current query:
SELECT gen_loc,
regexp_substr(generic_location, '^[0-9]*') AS `A`,
regexp_substr(generic_location, '[A-Z]+') AS `B`,
REGEXP_REPLACE(generic_location, '([0-9]+[A-Z]+)+', '') AS `C`
FROM boms ORDER BY CAST(A AS UNSIGNED) ASC, CAST(B AS UNSIGNED), CAST(C AS UNSIGNED);
I want the column order shows like:
4A14
4A15
4A75
4AW39
4AW70
4AW83
Is there any way to order the generic location column like this?
Thank you!

The problem you are facing is casting varchar to int perhaps hoping to obtain ASCII code sum.
The correct query is the following where MySQL is using inbuilt alphabetical sorting
SELECT gen_loc,
regexp_substr(generic_location, '^[0-9]*') AS `A`,
regexp_substr(generic_location, '[A-Z]+') AS `B`,
REGEXP_REPLACE(generic_location, '([0-9]+[A-Z]+)+', '') AS `C`
FROM boms ORDER BY CAST(A AS UNSIGNED) ASC, B, CAST(C AS UNSIGNED);
Notice B is not being cast.
Also, not sure why you had to use substrings, the following query achieves the same result
SELECT gen_loc, generic_location FROM boms ORDER BY generic_location ASC;

Related

Finding the null rate of the column device_id

I'm trying to write a code that will display the null count for a column in a table
I wrote the code below but i've been running into a syntax issue
SELECT device_id AS FIELD, COUNT (*)
FROM (SELECT *
FROM TABLE
WHERE TRIM(UPPER(device_id) IN ('','NULL',) OR device_id IS NULL OR SUBSTRING(TRIM(UPPER(device_id)), 1,1) = '-')
AND (event_date between '2019-05-20' AND '2019-05-27')
);
I've been experiencing a syntax error
cannot recognize input near ')' 'OR' 'device_id' in expression
specification:17:16,
In MySQL at least, subqueries require an alias. But you don't need a subquery. So I think you want:
SELECT COUNT(*)
FROM <TABLE> t
WHERE (TRIM(UPPER(device_id)) IN ('', 'NULL') OR
device_id IS NULL OR
TRIM(UPPER(device_id)) LIKE '-%')
) AND
event_date between '2019-05-20' AND '2019-05-27';
Notes:
You had a trailing comma in the `IN list.
I don't think you had the write parentheses before the event_date comparison.
You cannot include device_id in the SELECT unless you have GROUP BY, but I think you just want the count.
LIKE is much easier than SUBSTRING() for your third comparison.
All the TRIM()s and UPPER()s suggest that you should maintain better control of your data. These should not be necessary, particularly in an "id" column.

mysql get max number from a string field

I need to get maximum number from a part of the value that generally start with year followed by slash(/). So I need a maximum number after the slash(/) but year should be 2016
2016/422
2016/423
2016/469
2016/0470
2014/777
2015/123
2015/989
I tried this query
SELECT columname FROM tablename WHERE columname LIKE '2016/%' ORDER BY id DESC
the above query always giving '2016/469' as first record, how to get '2016/0470' as the maximum number?
any help will be much appreciated.
Thank you.
If columname follows that pattern YEAR/0000, you can use SUBSTRING function from MySQL to remove the part of the string you don't want.
SELECT value FROM (
SELECT CAST(SUBSTRING(columname, 0, 4) AS UNSIGNED) as year, CAST(SUBSTRING(columname FROM 6) AS UNSIGNED) as value FROM tablename
) total
ORDER BY year DESC, value DESC
LIMIT 1;
You need to split the string into 2 parts and evaluate them as numbers, instead of strings. The following formula will return the number after the / in the fieldname. All functions used below are described in the string functions section of the MySQL documentation. This way you can get the number after the / character, even if it is not year before the /, but sg else. The + 0 converts the string to a number, eliminating any leading 0.
select right(columnname, char_length(columnname)-locate('/',columnname)) + 0
from tablename
Just take the max() of the above expression to get the expected results.
UPDATE:
If you need the original number and the result has to be restricted to a specific year, then you need to join back the results to the original table:
select columnname
from tablename t1
inner join (select max(right(t.columnname, char_length(t.columnname)-locate('/',t.columnname)) + 0) as max_num
from tablename t
where left(t.columnname,4)='2016'
) t2
on right(t1.columnname, char_length(1t.columnname)-locate('/',t1.columnname)) + 0 = t2.max_num
where left(t1.columnname,4)='2016'
There are lots of suggestions given as answers already. But some of those seem overkill to me.
Seems like the only change needed to the OP query is the expression in the ORDER BY clause.
Instead of:
ORDER BY id
We just need to order by the numeric value following the slash. And there are several approaches, several expressions, that will get that from the example data.
Since the query already includes a condition columname LIKE '2016/%'
We can get the characters after the first five characters, and then convert that string to a numeric value by adding zero.
ORDER BY SUBSTRING(columname,6) + 0 DESC
If we only want to return one row, add
LIMIT 1
http://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring
If we only want to return the numeric value, we could use the same expression in the SELECT list, in addition columnname.
This isn't the only approach. There are lots of other approaches that will work, and don't use SUBSTRING.
Try like this:
SELECT
MAX(CAST(SUBSTRING(t.name,
LOCATE('/', t.name) + 1)
AS UNSIGNED)) AS max_value
FROM
tablename AS t;
You can try with this little uggly approach:
SELECT t.id, t2.secondNumber FROM table AS t
JOIN (SELECT id,
CONCAT(SUBSTRING(field,1,5),
if(SUBSTRING(SUBSTRING(field, 6),1,1)='0',
SUBSTRING(field, 6),
SUBSTRING(field,7)
)
) as secondNumber FROM table ) AS t2 ON t2.id=t.id
ORDER BY t2.secondNumber DESC
Would be valid only if the 0 (zeroes) before the second number (after the slash) are no more than 1.
Or if the year doesn`t matter you can try to order them only by the second number if it is ok:
SELECT t.id, t2.secondNumber FROM table AS t
JOIN (SELECT id,
if(SUBSTRING(SUBSTRING(field, 6),1,1)='0',
SUBSTRING(field, 6),
SUBSTRING(field,7)
) as secondNumber FROM table ) AS t2 ON t2.id=t.id
ORDER BY t2.secondNumber DESC

order by numbers where can be hyphens - mysql

I have a query say
select * from table order by numbers asc
numbers are: 32,-,11,76,-
What I get is -,-,11,32,76 (hyphens at the start)
what do I change in query so I get the hyphens at the end ?
Here First I tried sort using conditional flow with type casting
SELECT * FROM `table` ORDER BY numbers = '-', numbers+0 asc
If you want the reverse negate the conditional flow
SELECT * FROM `table` ORDER BY numbers != '-', numbers+0 asc
You are storing numbers as varchar() and you will have more trouble while you have data like 1,11,9,5... etc and ordering will have 1,11,5,9...
Here is how you can manage it
select * from table_name
order by case when numbers ='-' then 2 else 1 end,numbers+0 ;

MySQL sort varchar column numeric, numbers first

I've got a varchar column that I want to sort numeric, which works great when using this trick: https://stackoverflow.com/a/5418033/1005334 (in short: ...ORDER BY Result * 1).
However, the table concerned contains results. So something like this occurs:
Result
------
DNS
DNF
1
2
3
The numbers are correctly ordered, but the DNF comes above the numbers when sorting like this. What I'd like is to have the numeric sort, but with non-numbers sorted alphabetically below the numbers. Like so:
Result
------
1
2
3
DNF
DNS
In what way can I modify the query (preferably only the ORDER BY clause) to get this result?
use LPAD
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_lpad
LPAD(yourField, 20, '0');
this will manage correct order for "varchar numeric fields" (10 will be after 2) and put strings at the end.
SqlFiddle
The second argument (20) is quite arbitrary. It should be equivalent to (or bigger then) the length of the longest string in your field.
SELECT *, (Result REGEXP '^[0-9]+$') AS is_numeric
FROM table_name
ORDER BY is_numeric DESC,
CASE WHEN is_numeric THEN (Result + 0) ELSE Result END ASC
You can do that by using MySQL's REGEXP. Try this one,
SELECT *
FROM tablea
ORDER BY IF(`Result` REGEXP '^-?[0-9]+$', 0, 1) ASC,
`Result` ASC
SQLFiddle Demo
try this:
Please change your ORDER BY Clause with this:
ORDER BY
CASE WHEN Result REGEXP '^[0-9]+$' THEN Result*1 else 999999 END,
Result
This will order the numeric values first then the rest
ORDER BY CAST(`Result` AS SIGNED) DESC
this should work.

How to fetch the first and last record of a grouped record in a MySQL query with aggregate functions?

I am trying to fetch the first and the last record of a 'grouped' record.
More precisely, I am doing a query like this
SELECT MIN(low_price), MAX(high_price), open, close
FROM symbols
WHERE date BETWEEN(.. ..)
GROUP BY YEARWEEK(date)
but I'd like to get the first and the last record of the group. It could by done by doing tons of requests but I have a quite large table.
Is there a (low processing time if possible) way to do this with MySQL?
You want to use GROUP_CONCAT and SUBSTRING_INDEX:
SUBSTRING_INDEX( GROUP_CONCAT(CAST(open AS CHAR) ORDER BY datetime), ',', 1 ) AS open
SUBSTRING_INDEX( GROUP_CONCAT(CAST(close AS CHAR) ORDER BY datetime DESC), ',', 1 ) AS close
This avoids expensive sub queries and I find it generally more efficient for this particular problem.
Check out the manual pages for both functions to understand their arguments, or visit this article which includes an example of how to do timeframe conversion in MySQL for more explanations.
Try This to start with... :
Select YearWeek, Date, Min(Low_Price), Max(High_Price)
From
(Select YEARWEEK(date) YearWeek, Date, LowPrice, High_Price
From Symbols S
Where Date BETWEEN(.. ..)
GROUP BY YEARWEEK(date)) Z
Group By YearWeek, Date
Here is a great specific solution to this specific problem:
http://topwebguy.com/first-and-last-in-mysql-a-working-solution/
It's almost as simple as using FIRST and LAST in MySQL.
I will include the code that actually provides the solution but you can look upi the whole text:
SELECT
word ,
(SELECT a.ip_addr FROM article a
WHERE a.word = article.word
ORDER BY a.updated LIMIT 1) AS first_ip,
(SELECT a.ip_addr FROM article a
WHERE a.word = article.word
ORDER BY a.updated DESC LIMIT 1) AS last_ip
FROM notfound GROUP BY word;
Assuming that you want the ids of the records with the lowest low_price and the highest high_price you could add these two columns to your query,
SELECT
(SELECT id ORDER BY low_price ASC LIMIT 1) low_price_id,
(SELECT id ORDER BY high_price DESC LIMIT 1) high_price_id,
MIN(low_price), MAX(high_price), open, close
FROM symbols
WHERE date BETWEEN(.. ..)
GROUP BY YEARWEEK(date)
If efficiency is an issue you should add a column for 'year_week', add some covering indexes, and split the query in two.
The 'year_week' column is just an INT set to the value of YEARWEEK(date) and updated whenever the 'date' column is updated. This way you don't have to recalculate it for each query and you can index it.
The new covering indexes should look like this. The ordering is important.
KEY yw_lp_id (year_week, low_price, id),
KEY yw_hp_id (year_week, high_price, id)
You should then use these two queries
SELECT
(SELECT id ORDER BY low_price ASC LIMIT 1) low_price_id,
MIN(low_price), open, close
FROM symbols
WHERE year_week BETWEEN(.. ..)
GROUP BY year_week
and
SELECT
(SELECT id ORDER BY high_price DESC LIMIT 1) high_price_id,
MAX(high_price), open, close
FROM symbols
WHERE year_week BETWEEN(.. ..)
GROUP BY year_week
Covering indexes are pretty useful. Check this out for more details.