Related
Here is my sql query. I don't want to write the "replace" 3 times. How can I optimize it ?
select * from table1 where col1='blah' AND
(
replace(replace(col2,'_',' '),'-',' ') LIKE ? OR
replace(replace(col2,'_',' '),'-',' ') LIKE ? OR
replace(replace(col2,'_',' '),'-',' ') LIKE ?
)
You could use subquery:
SELECT *
FROM (
select *, replace(replace(col2,'_',' '),'-',' ') AS r
from table1
where col1='blah'
) s
WHERE r LIKE ? OR r LIKE ? OR r LIKE ?
Or LATERAL:
select *
from table1
,LATERAL(SELECT replace(replace(col2,'_',' '),'-',' ') AS r) s
where col1='blah'
and (s.r LIKE ? OR s.r LIKE ? OR s.r LIKE ?)
db<>fiddle demo
I prefer the second approach because there is no need for introducing outer query. This feature was added in version 8.0.14.
Related:
PostgreSQL: using a calculated column in the same query
CROSS/OUTER APPLY in MySQL
In MySQL you can use a column alias in the HAVING clause even without any aggregation:
select *, replace(replace(col2,'_',' '),'-',' ') as col2_replace
from table1
where col1='blah'
having col2_replace like ?
or col2_replace like ?
MySQL has a tendency to materialize subqueries -- not only is this overhead for reading and writing a temporary table but it can also affect the use of indexes in a more complicated query.
Here are three alternative solutions that do not require subqueries.
If ? does not contain wildcards, then the simplest method is:
replace(replace(col2, '_', ' '), '-', ' ') in (?, ?, ?)
If it does, then change logic to use a single regular expression pattern:
replace(replace(col2, '_', ' '), '-', ' ') regexp ?
You can also explicitly adjust the pattern in the query:
replace(replace(col2, '_', ' '), '-', ' ') regexp
concat('(',
replace(replace(?, '_', '.'), '%', '.*'), ')|(',
replace(replace(?, '_', '.'), '%', '.*'), ')|(',
replace(replace(?, '_', '.'), '%', '.*'), ')'
)
I want to validate the data in MYSQL table. Table has 4 fields :
Firstname
Middlename
Lastname
Fullname
I want to compare if CONCAT(firstname, ' ', LASTNAME), matches Fullname
Here is the command I am using :
select * from user_info where CONCAT(firstname, ' ', lastname)
like CONCAT('%', fullname, '%')
However, this is not working. But the following command works :
select * from user_info where CONCAT(firstname, ' ', lastname)
like '%JOHN DOE%'
What could be the issue with the MySQL command ?
Your newly attached data confirms what I suspected, namely that the full name is not necessarily composes simply of the first and last name, but include the middle name, or might even be missing any of the three components. One option here would be to assert that each component present does appear somewhere inside the full name, in the correct order, e.g.
SELECT *
FROM yourTable
WHERE
fullname REGEXP
CONCAT(
CASE WHEN firstname IS NOT NULL
THEN CONCAT('[[:<:]]', COALESCE(firstname, ''), '[[:>:]]')
ELSE '' END,
'.*',
CASE WHEN middlename IS NOT NULL
THEN CONCAT('[[:<:]]', COALESCE(middlename, ''), '[[:>:]]')
ELSE '' END,
'.*',
CASE WHEN lastname IS NOT NULL
THEN CONCAT('[[:<:]]', COALESCE(lastname, ''), '[[:>:]]')
ELSE '' END);
Demo
You need to change order of condition in your code :-
select * from table1 where fullname
like CONCAT('%', trim(CONCAT(firstname,' ',middlename,' ',lastname)) , '%')
SQL Fiddle
I have the following query:
SELECT
title_imdb_id, person_imdb_id, role_type_id, role_name
FROM
mturk_imdbcredit
WHERE
title_imdb_id=1
Which gives me:
title_imdb_id person_imdb_id role_type_id role_name
1 1588970 Actor Herself
1 5690 Director NULL
I would like to group by the title_imdb_id and concatenate the three fields separated by a : and the rows separated by a ,. The end result should be:
1588970:Actor:Herself, 5690:Director:
Is this possible to do in SQL ?
You can use a field concat in addition to a GROUP_CONCAT:
SELECT
GROUP_CONCAT(
CONCAT (
person_imdb_id, ':', role_type_id, ':', IFNULL(role_name, '')
) SEPARATOR ', '
)
FROM
mturk_imdbcredit
GROUP BY
title_imdb_id
Which gives me:
1588970:Actor:Herself, 5690:Director:
I discovered a couple of hours ago that CASE WHEN doesn't work on access queries. I have a complex CONCAT on my SELECT statement but it is written in MySQL:
SELECT CONCAT(
'www.mywebsite.com/catalogsearchtool?title='
, REPLACE(tblTitles.Content_Name, '&', '%26')
, '&contributor=ln:'
, CASE WHEN LOCATE('; ',tblTitles.Contributors) > 0
THEN REPLACE(REPLACE(REPLACE(
CASE WHEN RIGHT(TRIM(tblTitles.Contributors), 1) = ',' THEN LEFT(TRIM(tblTitles.Contributors), CHAR_LENGTH(TRIM(tblTitles.Contributors)) - 1) ELSE TRIM(tblTitles.Contributors) END
, '&', '%26'), '; ', ',rl:Author&contributor=ln:'), ', ', ',fn:')
ELSE REPLACE(REPLACE(
CASE WHEN RIGHT(TRIM(tblTitles.Contributors), 1) = ',' THEN LEFT(TRIM(tblTitles.Contributors), CHAR_LENGTH(TRIM(tblTitles.Contributors)) - 1) ELSE TRIM(tblTitles.Contributors) END
, '&', '%26'), ', ', ',fn:')
END
, ',rl:Author&language='
, LOWER(tblTitles.language)
) AS 'link'
Basically what that does is that it generates a link based on names that can be found in a column called Contributors. That column has the following order:
last_name, first_name
or
last_name, first_name; last_name, first_name
For reasons out of my control, there can be many names in that column. So for the query above a couple of results would be (for 2 names):
www.mywebsite.com/catalogsearchtool?title=tile123&contributor=ln:smith,fn:john,rl:Author&contributor=ln:smith,fn:jane,rl:Author&language=english
And (for 1 name):
www.mywebsite.com/catalogsearchtool?title=title5678&contributor=ln:smith,fn:john,rl:Author&language=english
Can anyone please help me translating this logic to Access SQL?
Currently I'm working on a database redesign project. A large bulk of this project is pulling data from the old database and importing it into the new one.
One of the columns in a table from the old database is called 'name'. It contains a forename and a surname all in one field (ugh). The new table has two columns; forenames and surname. I need to come up with a clean, efficient way to split this single column into two.
For now I'd like to do everything in the same table and then I can easily transfer it across.
3 columns:
Name (the forename and surname)
Forename (currently empty, first half of name should go here)
Surname (currently empty, second half of name should go here)
What I need to do: Split name in half and place into forename and surname
If anyone could shed some light on how to do this kind of thing I would really appreciate it as I haven't done anything like this in SQL before.
Database engine: MySQL
Storage engine: InnoDB
A quick solution is to use SUBSTRING_INDEX to get everything at the left of the first space, and everything past the first space:
UPDATE tablename
SET
Forename = SUBSTRING_INDEX(Name, ' ', 1),
Surname = SUBSTRING_INDEX(Name, ' ', -1)
Please see fiddle here. It is not perfect, as a name could have multiple spaces, but it can be a good query to start with and it works for most names.
Try this:
insert into new_table (forename, lastName, ...)
select
substring_index(name, ' ', 1),
substring(name from instr(name, ' ') + 1),
...
from old_table
This assumes the first word is the forename, and the rest the is lastname, which correctly handles multi-word last names like "John De Lacey"
For the people who wants to handle fullname: John -> firstname: John, lastname: null
SELECT
if( INSTR(`name`, ' ')=0,
TRIM(SUBSTRING(`name`, INSTR(`name`, ' ')+1)),
TRIM(SUBSTRING(`name`, 1, INSTR(`name`, ' ')-1)) ) first_name,
if( INSTR(`name`, ' ')=0,
null,
TRIM(SUBSTRING(`name`, INSTR(`name`, ' ')+1)) ) last_name
It works fine with John Doe. However if user just fill in John with no last name, SUBSTRING(name, INSTR(name, ' ')+1)) as lastname will return John instead of null and firstname will be null with SUBSTRING(name, 1, INSTR(name, ' ')-1).
In my case I added if condition check to correctly determine lastname and trim to prevent multiple spaces between them.
This improves upon the answer given, consider entry like this "Jack Smith Smithson", if you need just first and last name, and you want first name to be "Jack Smith" and last name "Smithson", then you need query like this:
-- MySQL
SELECT
SUBSTR(full_name, 1, length(full_name) - length(SUBSTRING_INDEX(full_name, ' ', -1)) - 1) as first_name,
SUBSTRING_INDEX(full_name, ' ', -1) as last_name
FROM yourtable
Just wanted to share my solution. It also works with middle names. The middle name will be added to the first name.
SELECT
TRIM(SUBSTRING(name,1, LENGTH(name)- LENGTH(SUBSTRING_INDEX(name, ' ', -1)))) AS firstname,
SUBSTRING_INDEX(name, ' ', -1) AS lastname
I had a similar problem but with Names containing multiple names, eg. "FirstName MiddleNames LastName" and it should be "MiddleNames" and not "MiddleName".
So I used a combo of substring() and reverse() to solve my problem:
select
SystemUser.Email,
SystemUser.Name,
Substring(SystemUser.Name, 1, instr(SystemUser.Name, ' ')) as 'First Name',
reverse(Substring(reverse(SystemUser.Name), 1, instr(reverse(SystemUser.Name), ' '))) as 'Last Name',
I do not need the "MiddleNames" part and maybe this is not the most efficient way to solve it, but it works for me.
Got here from google, and came up with a slightly different solution that does handle names with more than two parts (up to 5 name parts, as would be created by space character). This sets the last_name column to everything to the right of the 'first name' (first space), it also sets full_name to the first name part. Perhaps backup your DB before running this :-) but here it is it worked for me:
UPDATE users SET
name_last =
CASE
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 1)) = LENGTH(full_name) THEN ''
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 2)) = LENGTH(full_name) THEN SUBSTRING_INDEX(del_name, ' ', -1)
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 3)) = LENGTH(full_name) THEN SUBSTRING_INDEX(del_name, ' ', -2)
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 4)) = LENGTH(full_name) THEN SUBSTRING_INDEX(del_name, ' ', -3)
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 5)) = LENGTH(full_name) THEN SUBSTRING_INDEX(del_name, ' ', -4)
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 6)) = LENGTH(full_name) THEN SUBSTRING_INDEX(del_name, ' ', -5)
ELSE ''
END,
full_name = SUBSTRING_INDEX(full_name, ' ', 1)
WHERE LENGTH(name_last) = 0 or LENGTH(name_last) is null or name_last = ''
SUBSTRING_INDEX didn't work for me in SQL 2018, so I used this:
declare #fullName varchar(50) = 'First Last1 Last2'
declare #first varchar(50)
declare #last varchar(50)
select #last = right(#fullName, len(#fullName)-charindex(' ',#fullName, 1)), #first = left(#fullName, (charindex(' ', #fullName, 1))-1);
Yields #first = 'First', #last = 'Last1 Last2'