Mysql Order by last name when full name for column - mysql

I have the following:
SELECT * FROM users LEFT JOIN user_info ON users.id=user_info.user_id
WHERE
((user_info.tester != 1) OR (user_info.tester is null)) AND
id in (SELECT explicituser_id FROM user_login WHERE (created < '2012-12-17' OR created >= date_add('2012-12-17', interval 1 day))) AND
id IN (SELECT participte_id FROM roster WHERE roster_id IN (6))
order by
substring_index(users.name, ' ', -1)
I'm simply trying to sort by the users' last name.
However, while it can sort by the first name, the last name is buggy. If the user has quotes around their name (ie. "Abigail Martinez" it will make the sorting incorrect. If the user provides only one name, and it's a nickname (ie. Juan), then it will also make it incorrect. And then there's middle initials (ie. Tiffany S Villa or Steve de la Makinov). Unfortunately, this uses only one column for the full name (users.name).
Any help is appreciated. Thanks!

substring_index(TRIM(users.name), ' ', -1) Adding TRIM will remove trailing spaces. After that, sorting occurs as expected.

if it is the case try to get all the rows and then may try with mysql_real_escape_string. so that you will not be having quotes. it'll be something like this.
$row['new_name'] = mysql_real_escape_string($row['name']);
I'm not too sure performance wise whether its good to go for it or not..
Hope this may helps

Related

Replace substrings using a reference table in MYSQL

I am trying to replace substrings within one text column in my table using a reference table.
To my knowledge, the replace(column, string1,string2) function will only work with strings as the second and third input.
Here is a visual of what I am trying to do. To be clear, the reference table I need to use is much larger - otherwise, I would use four replace functions.
EDIT: Thank you to everyone who has pointed out how bad this data model is built. Though I am not an expert on building efficient data models, I do know this one is built terribly. However, the structure of this model is completely out of my control. Apologies for not mentioning that from the get-go.
table1
Farms
Animals
Farm1
Cow, Pig
Farm2
Dog, Cow, Cat
Farm3
Dog
referenceTable
refColumn1
refColumn2
Cow
Moo
Pig
Oink
Dog
Bark
Cat
Meow
And here is what I would like the result column to be..
table1
Farms
Animals
Farm1
Moo, Oink
Farm2
Bark, Moo, Meow
Farm3
Bark
First question on stackoverflow so apologies if I missed anything.
Any help is appreciated! Thank you!
To loop over comma (or ', ' in this case) separated values, you can use a double substring_index and a join against a sequence table (where the sequence is <= the number of joined values in a given row, as determined with char_length/replace):
select t1.Farms, group_concat(rt.refColumn2 order by which.n separator ', ') Animals
from table1 t1
join (select 1 n union select 2 union select 3) which
on ((char_length(t1.Animals)-char_length(replace(t1.Animals,', ','')))/char_length(', '))+1 >= which.n
join referenceTable rt on rt.refColumn1=substring_index(substring_index(t1.Animals,', ',which.n),', ',-1)
group by t1.Farms
Here I use an ad hoc sequence table of 1 through 3, assuming no row will have more than 3 animals; expand as necessary or alternatively use a cte.
You have a really lousy data model and you should fix it. You should not be storing multiple values in a string column. Each value pair should be on its own row.
Let me assume that someone else created these tables and you have no choice. If that is the case, MySQL has a solution. I think I would suggest:
select t1.*, -- or whatever columns you want
(select group_concat(rt.refColumn2
order by find_in_set(rt.refColumn1, replace(t1.animals, ', ', ','))
separator ', '
)
from referenceTable rt
where find_in_set(rt.refColumn1, replace(t1.animals, ', ', ',')) > 0
)
from table1 t1
I'm more fluent in Sql Server than MySql, having got a solution working in Sql Server the real challenge was converting to a working MySql version!
See if this meets your needs. It works for your sample data, you may of course need to tweak if it doesn't fully represent your real world data.
with w as (
select *, case when animals like '%' || refcol1 || '%' then locate(refcol1,animals) end pos
from t1
join lateral (select * from t2)t2 on 1=1
)
select farms, group_concat(refcol2 order by pos separator ',') as Animals
from w
where pos>0
group by farms
order by farms
Working DB<>Fiddle

ORDER BY doesn't sort alphabetically

I use the following MySQL query to get results, sorted alphabetically by name:
SELECT * FROM `client` WHERE CONCAT(name, ', ', firstname) LIKE "%ti%" ORDER BY name
In some way the results get sorted, so the ORDER BY works. Unfortunately it works not as expected. The above query gives the following order for example:
Kostic (31)
Hatscher (30)
Why is Kostic listed first? Prove me wrong, but K comes after H in the alphabet... (If it helps - the name field is type of varchar.)
It shouldn't be possible for K to come before H with ORDER BY name. Maybe a blank or even some unprintable character in front?
Test:
Do you get 'Kostic (31)' when you add AND name like 'K%'?
Do you get 'Hatscher (30)' when you add AND name like 'H%' instead?
I know ASC is the default when not specified in the ORDER BY but have you tried putting ASC after.. ORDER BY Name ASC. It's worth a shot

Where statement that will use a concat

SELECT zlec_status.nazwa AS Status,
piorytet.nazwa AS Priorytet,
Concat(koord.imie, ' ', koord.nazwisko) AS `Koordynator`,
Concat(zlec_adresy.town, ' - ', zlec_adresy.street, ' ',
zlec_adresy.other)
AS `adres`,
zlec_z_dnia,zlec_id,
zlec_nr,
zlec_do,
zlec_ogran,
awizacje,
awizacja_na_dzien,
termin_zamkniecia,
tresc,
uwagi
FROM zlec
INNER JOIN koord
ON zlec.koord = koord.id
INNER JOIN zlec_adresy
ON zlec.zlec_addres = zlec_adresy.id
INNER JOIN piorytet
ON zlec.priorytet = piorytet.id
INNER JOIN zlec_status
ON zlec.status_zlecenia = zlec_status.id
WHERE `zlec_adresy`.`town` LIKE '%Sz%' LIMIT 0, 10
The table zlec_adresy is the following:
=============================
id | street | town | other
=============================
As you see there is a concat for the whole address part
Concat(zlec_adresy.town, ' - ', zlec_adresy.street, ' ',zlec_adresy.other)AS `adres`
Starting off - the user has a input field with the address lookup - so he can write either the town name or a street name or the other condition. In my code there will be only filtered through the WHERE clause of the .'town' LIKE userinput. But how can I make it working => Where ||Concat(zlec_adresy.town, ' - ', zlec_adresy.street, ' ',zlec_adresy.other)ASadres|| LIKE userinput
So basically it would filter the As adres for the where statement. Is that possible to do? Or there is another way to accomplish it.
Another, possibly more clean approach uses OR:
WHERE
town like concat('%',#userinput,'%')
OR
street like concat('%',#userinput,'%')
OR
other like concat('%',#userinput,'%')
WHERE
Concat(zlec_adresy.town, ' - ', zlec_adresy.street, ' ',
zlec_adresy.other) like concat('%',#variable,'%')
You can use HAVING to solve your issue. Something like :
SELECT
name, CONCAT(street, ' ', town) AS address
FROM
zlec
WHERE
zlec.town like '%abc%'
HAVING address like '%xyz%'
The query anatomy looks like this:
it handles the from, because all the other clauses are dependent on it
it handles the where, to filter out unneeded rows
it handles the select, so you will get the columns you want
Note, that I did not mention group by, having or order by, since they are not part of your query, so this anatomy is quite a simplified one. The purpose of this simplification was to achieve clarity.
Now, since the select runs after the where, you cannot use the renames from the select in the where, since when the where is executed, the selection was not yet executed.
So you have to do a work-around:
- you can define a temporary new relation by (select ...) mynewrelation and use the column renamings of the newrelation, but it is not advisable for this very situation. However, it is good to know about this option, it might be useful in the future
- you can use something like the one suggested by Madhivanan, but that solution is not performant, since you are doing very slow string operations to concatenate those fields. As a result, the code will be clear but slow
- Hanno Binder's solution is much better, since it uses the benefit of the or of not evaluating the second operand if the first was true. His code is fast, but you do not want to see it
My suggestion: You should define a stored function to make this calculation. That stored function should be equivalent to the solution suggested by Hanno Binder and you would just call that function in your query. So, your source-code will be clear, easy-to-read, correct and performant.

Query MySQL field for LIKE string

I have a field called 'areasCovered' in a MySQL database, which contains a string list of postcodes.
There are 2 rows that have similar data e.g:
Row 1: 'B*,PO*,WA*'
Row 2: 'BB*, SO*, DE*'
Note - The strings are not in any particular order and could change depending on the user
Now, if I was to use a query like:
SELECT * FROM technicians WHERE areasCovered LIKE '%B*%'
I'd like it to return JUST Row 1. However, it's returning Row 2 aswell, because of the BB* in the string.
How could I prevent it from doing this?
The key to using like in this case is to include delimiters, so you can look for delimited values:
SELECT *
FROM technicians
WHERE concat(', ', areasCovered, ', ') LIKE '%, B*, %'
In MySQL, you can also use find_in_set(), but the space can cause you problems so you need to get rid of it:
SELECT *
FROM technicians
WHERE find_in_set('B', replace(areasCovered, ', ', ',') > 0
Finally, though, you should not be storing these types of lists as strings. You should be storing them in a separate table, a junction table, with one row per technician and per area covered. That makes these types of queries easier to express and they have better performance.
You are searching wild cards at the start as well as end.
You need only at end.
SELECT * FROM technicians WHERE areasCovered LIKE 'B*%'
Reference:
Normally I hate REGEXP. But ho hum:
SELECT * FROM technicians
WHERE concat(",",replace(areasCovered,", ",",")) regexp ',B{1}\\*';
To explain a bit:
Get rid of the pesky space:
select replace("B*,PO*,WA*",", ",",");
Bolt a comma on the front
select concat(",",replace("B*,PO*,WA*",", ",","));
Use a REGEX to match "comma B once followed by an asterix":
select concat(",",replace("B*,PO*,WA*",", ",",")) regexp ',B{1}\\*';
I could not check it on my machine, but it's should work:
SELECT * FROM technicians WHERE areasCovered <> replace(areaCovered,',B*','whatever')
In case the 'B*' does not exist, the areasCovered will be equal to replace(areaCovered,',B*','whatever'), and it will reject that row.
In case the 'B*' exists, the areCovered will NOT be eqaul to replace(areaCovered,',B*','whatever'), and it will accept that row.
You can Do it the way Programming Student suggested
SELECT * FROM technicians WHERE areasCovered LIKE 'B*%'
Or you can also use limit on query
SELECT * FROM technicians WHERE areasCovered LIKE '%B*%' LIMIT 1
%B*% contains % on each side which makes it to return all the rows where value contains B* at any position of the text however your requirement is to find all the rows which contains values starting with B* so following query should do the work.
SELECT * FROM technicians WHERE areasCovered LIKE 'B*%'

Order by last 3 chars

I have a table like:
id name
--------
1 clark_009
2 clark_012
3 johny_002
4 johny_010
I need to get results in this order:
johny_002
clark_009
johny_010
clark_012
Do not ask me what I already tried, I have no idea how to do this.
This will do it, very simply selecting the right-most 3 characters and ordering by that value ascending.
SELECT *
FROM table_name
ORDER BY RIGHT(name, 3) ASC;
It should be added that as your data grows, this will become an inefficient solution. Eventually, you'll probably want to store the numeric appendix in a separate, indexed integer column, so that sorting will be optimally efficient.
you should try this.
SELECT * FROM Table order by SUBSTRING(name, -3);
good luck!
You may apply substring_index function to parse these values -
select * from table order by substring_index(name, '_', -1)
You can use MySQL SUBSTRING() function to sort by substring
Syntax : SUBSTRING(string,position,length)
Example : Sort by last 3 characters of a String
SELECT * FROM TableName ORDER BY SUBSTRING(FieldName, -3);
#OR
SELECT * FROM TableName ORDER BY SUBSTRING(FieldName, -3,3);
Example : Sort by first 3 characters of a String
SELECT * FROM TableName ORDER BY SUBSTRING(FieldName, 1,3);
Note : Positive Position/Index start from Left to Right and Negative Position/Index start from Right to Left of the String.
Here is the details about SUBSTRING() function.
If you want to order by the last three characters (from left to right) with variable name lengths, I propose this:
SELECT *
FROM TABLE
ORDER BY SUBSTRING (name, LEN(name)-2, 3)
The index starts at lenght of name -2 which is the third last character.
I'm a little late but just encountered the same problem and this helped me.