Hey guys I've written this MS SQL script with my awesome MS SQL skills, how can I get it to work in MySQL? It's failing on some of the string functions :( Don't know MySQL too well!
SELECT i.id as ID, e.code as EMPCODE, lower(e.email_address) as EMAIL
from load_incidents i , dim_employee e
WHERE
( (i.involvedemail = e.email_address)
or (replace(lower(e.preferred_name+e.last_name),' ', '') = replace(lower(i.involvedname),' ', ''))
or (replace(lower(e.preferred_name+e.last_name),' ', '') like replace(lower(i.involvedname),' ', '')+'%')
or (replace(lower(e.first_name+e.last_name),' ', '') = replace(lower(i.involvedname),' ', ''))
or (replace(lower(e.first_name+e.last_name),' ', '') like replace(lower(i.involvedname),' ', '')+'%')
or (replace(lower(e.employee_name),' ','') = replace(lower(i.involvedname),' ',''))
)
and charindex(' ',e.first_name) = 0
and (i.involvedemail= 'none available' or i.involvedemail is null or i.involvedemail = '')
UNION
SELECT i.id as ID, e.code as EMPCODE, lower(e.email_address) as EMAIL
from load_incidents i , dim_employee e
WHERE
( (i.involvedemail = e.email_address)
or (replace(lower(e.preferred_name+e.last_name),' ', '') = replace(lower(i.involvedname),' ', ''))
or (replace(lower(e.preferred_name+e.last_name),' ', '') like replace(lower(i.involvedname),' ', '')+'%')
or (replace(lower(left(e.first_name,charindex(' ',e.first_name)-1) + e.last_name),' ', '') = replace(lower(i.involvedname),' ',''))
or (replace(lower(e.employee_name),' ','') = replace(lower(i.involvedname),' ',''))
)
and charindex(' ',e.first_name) > 1
and (i.involvedemail= 'none available' or i.involvedemail is null or i.involvedemail = '')
YOUR problem is the charindex function, use locate (in place of charindex) for that purpose :
LOCATE(' ',e.first_name)
A lot of the functions are the same. Here's a list of MySQL string functions that you can use.
I tried Full Convert Enterprise with quite big success. Fast, easy and it did my job. Give it a try it may help you.
Also, You're probably best off doing this yourself to ensure everything is correct rather than relying on a third party tool (Also the additional benefit of understanding the differences between the two pieces of code). However you could use SQL to MySQL tool available here.
Related
I have a multi-join query that targeting the hospital's chart database.
this takes 5~10 seconds or more.
This is the visual expain using mysql workbench.
The query is below.
select sc.CLIENT_ID as 'guardianId', sp.PET_ID as 'patientId', sp.NAME as 'petName'
, (select BW from syn_vital where HOSPITAL_ID = sp.HOSPITAL_ID and PET_ID = sp.PET_ID order by DATE, TIME desc limit 1) as 'weight'
, sp.BIRTH as 'birth', sp.RFID as 'regNo', sp.BREED as 'vName'
, (case when ss.NAME like '%fel%' or ss.NAME like '%cat%' or ss.NAME like '%pawpaw%' or ss.NAME like '%f' then '002'
when ss.NAME like '%canine%' or ss.NAME like '%dog%' or ss.NAME like '%can%' then '001' else '007' end) as 'sCode'
, (case when LOWER(replace(sp.SEX, ' ', '')) like 'male%' then 'M'
when LOWER(replace(sp.SEX, ' ', '')) like 'female%' or LOWER(replace(sp.SEX, ' ', '')) like 'fam%' or LOWER(replace(sp.SEX, ' ', '')) like 'woman%' then 'F'
when LOWER(replace(sp.SEX, ' ', '')) like 'c.m%' or LOWER(replace(sp.SEX, ' ', '')) like 'castratedmale' or LOWER(replace(sp.SEX, ' ', '')) like 'neutered%' or LOWER(replace(sp.SEX, ' ', '')) like 'neutrality%man%' or LOWER(replace(sp.SEX, ' ', '')) like 'M.N%' then 'MN'
when LOWER(replace(sp.SEX, ' ', '')) like 'woman%' or LOWER(replace(sp.SEX, ' ', '')) like 'f.s%' or LOWER(replace(sp.SEX, ' ', '')) like 'S%' or LOWER(replace(sp.SEX, ' ', '')) like 'neutrality%%' then 'FS' else 'NONE' end) as 'sex'
from syn_client sc
left join syn_tel st on sc.HOSPITAL_ID = st.HOSPITAL_ID and sc.CLIENT_ID = st.CLIENT_ID
inner join syn_pet sp on sc.HOSPITAL_ID = sp.HOSPITAL_ID and sc.FAMILY_ID = sp.FAMILY_ID and sp.STATE = 0
inner join syn_species ss on sp.HOSPITAL_ID = ss.HOSPITAL_ID and sp.SPECIES_ID = ss.SPECIES_ID
WHERE
trim(replace(st.NUMBER, '-','')) = '01099999999'
and trim(sc.NAME) = 'johndoe'
and sp.HOSPITAL_ID = 'HOSPITALID999999'
order by TEL_DEFAULT desc
I would like to know how to improve the performance of this complex query.
The most obvious performance killers in your query are the non-sargable criteria in your where clause.
trim(replace(st.NUMBER, '-','')) = '01099999999'
This cannot use any available index as you have applied a function to the column, which needs to be evaluated before the comparison can be made.
As suggested by Pham, you could change your criterion to -
st.number IN ('01099999999', '01-099-999-999', 'ALL_OTHERS_FORMAT_YOU_ACCEPTS...')
or better still would be to normalize the numbers before you store them (you can always apply formatting for display purposes), that way you know how to search the stored data. Strip all the hyphens and spaces from the existings numbers -
UPDATE syn_tel
SET number = REPLACE(REPLACE(number, '-',''), ' ', '')
WHERE number LIKE '% %' OR number LIKE '%-%';
Similarly for the next criterion -
trim(sc.NAME) = 'johndoe'
The name should be trimmed before being stored in the database so there is no need to trim it when searching it. Update already stored names to trim whitespace -
UPDATE syn_client
SET NAME = TRIM(NAME)
WHERE NAME LIKE ' %' OR NAME LIKE '% ';
Changing sp.HOSPITAL_ID = 'HOSPITALID999999' to sc.HOSPITAL_ID = 'HOSPITALID999999' will allow for the use of a composite index on syn_client (HOSPITAL_ID, name) assuming you drop the TRIM() from the previously discussed criterion.
The sorting in your sub-query for weight might be wrong -
order by DATE, TIME desc limit 1
presumably you want the most recent weight -
order by `DATE` desc, `TIME` desc limit 1
/* OR */
order by CONCAT(`DATE`, ' ', `TIME`) desc limit 1
order by DATE, TIME desc -- really? That's equivalent to date ASC, time DESC. If you want "newest first", then ORDER BY date DESC, time DESC. Furthermore, it is usually bad practice and clumsy to code when you have DATE and TIME in separate columns. Is there a strong reason for storing them separately? It is reasonably easy to split them apart in a SELECT.
Similarly, cleanse NUMBER and NAME when inserting.
This will make the first subquery much faster:
syn_vital needs INDEX(hostital_id, pet_id, date, time, BW)
LIKE with a leading wildcard (%) is slow, but you probably cannot avoid it in this case.
LOWER(replace(sp.SEX, ' ', '')) -- Cleanse the input during INSERT, not on output!.
LOWER(...) -- With a suitable COLLATION (eg, the default), calling LOWER is unnecessary.
Some of these 'composite' INDEXes may be useful:
ss: INDEX(HOSPITAL_ID, SPECIES_ID, NAME)
st: INDEX(HOSPITAL_ID, CLIENT_ID, NUMBER)
sp: INDEX(HOSPITAL_ID, PET_ID)
What table is TEL_DEFAULT in?
You may want to:
Create index on syn_client(hospital_id, name --,tel_default?)
Create index on syn_tel(hospital_id, client_id, number)
Create index on syn_pet(hospital_id, family_id, state)
Create index on syn_species(hospital_id, species_id)
Change your query to:
SELECT ...
FROM syn_client sc
INNER JOIN syn_tel st ON sc.hospital_id = st.hospital_id AND sc.client_id = st.client_id
INNER JOIN syn_pet sp ON sc.hospital_id = sp.hospital_id AND sc.family_id = sp.family_id AND sp.state = 0
INNER JOIN syn_species ss ON sp.hospital_id = ss.hospital_id AND sp.species_id = ss.species_id
WHERE st.number IN ('01099999999', '01-099-999-999', 'ALL_OTHERS_FORMAT_YOU_ACCEPTS...')
AND trim(sc.name) = 'johndoe' --sc.name = 'johndoe' with standardize data input
AND sc.hospital_id = 'HOSPITALID999999' --not sp.hospital_id
ORDER BY tel_default DESC;
I wrote a query which is this:
SELECT mrt_name as MRT ,
operation_alpha_numeric_codes as Original,
SUBSTRING_INDEX(operation_alpha_numeric_codes,' ', 1) as First_code,
SUBSTRING_INDEX(operation_alpha_numeric_codes,' ', -1) as Second_Code,
SUBSTRING_INDEX(operation_alpha_numeric_codes,' ', -2) as Third_Code
FROM scraping.xp_pn_mrt;
I got result like this
As you can see the second_code copies the value of the original or the first_code if the value doesn't have a corresponding space or data. Also, the third code gets the second_code in the records that have a third code in them. How do I prevent the data being copied or set it to blank when the code doesn't have a corresponding value in it and how can I achieve getting the third code without copying the second one? Can someone help me with my query and what's wrong with it? Thanks a lot.
Since you're using MariaDB, you can use REGEXP_REPLACE to extract the parts of the code that you want:
SELECT
operation_alpha_numeric_codes as Original,
REGEXP_REPLACE(operation_alpha_numeric_codes, '^([^ ]+)(?: ([^ ]+))?(?: ([^ ]+))?$', '\\1') as First_code,
REGEXP_REPLACE(operation_alpha_numeric_codes, '^([^ ]+)(?: ([^ ]+))?(?: ([^ ]+))?$', '\\2') as Second_code,
REGEXP_REPLACE(operation_alpha_numeric_codes, '^([^ ]+)(?: ([^ ]+))?(?: ([^ ]+))?$', '\\3') as Third_code
FROM data
Output for (part of) your sample data
Original First_code Second_code Third_code
NS23 NS23
NS24 NE6 CC1 NS24 NE6 CC1
NS25 EW13 NS25 EW13
Demo on dbfiddle
Here's a version that will also work on MySQL 5.7, using RLIKE to check if the input matches given patterns:
SELECT
operation_alpha_numeric_codes as Original,
SUBSTRING_INDEX(operation_alpha_numeric_codes, ' ', 1) AS First_code,
CASE WHEN operation_alpha_numeric_codes RLIKE '^([^ ]+)$' THEN ''
WHEN operation_alpha_numeric_codes RLIKE '^([^ ]+)( ([^ ]+))?$' THEN SUBSTRING_INDEX(operation_alpha_numeric_codes, ' ', -1)
ELSE SUBSTRING_INDEX(SUBSTRING_INDEX(operation_alpha_numeric_codes, ' ', 2), ' ', -1)
END AS Second_code,
CASE WHEN operation_alpha_numeric_codes RLIKE '^([^ ]+)( ([^ ]+)){2}$' THEN SUBSTRING_INDEX(operation_alpha_numeric_codes, ' ', -1)
ELSE ''
END AS Third_code
FROM data
Demo on dbfiddle
You can try the following:
SELECT
mrt_name as MRT ,
operation_alpha_numeric_codes as Original,
SUBSTRING_INDEX(SUBSTRING_INDEX(operation_alpha_numeric_codes , ' ', 1), ' ', -1) AS First_code,
If( length(operation_alpha_numeric_codes ) - length(replace(operation_alpha_numeric_codes , ' ', ''))>=1,
SUBSTRING_INDEX(SUBSTRING_INDEX(operation_alpha_numeric_codes , ' ', 2), ' ', -1) , '')
as Second_code,
If( length(operation_alpha_numeric_codes ) - length(replace(operation_alpha_numeric_codes , ' ', ''))>=2,
SUBSTRING_INDEX(SUBSTRING_INDEX(operation_alpha_numeric_codes , ' ', 3), ' ', -1), '')
AS Third_code
FROM scraping.xp_pn_mrt;
[EDIT]
For double spaces between each value, This will work:
SELECT
mrt_name as MRT ,
operation_alpha_numeric_codes as Original,
If( length(operation_alpha_numeric_codes ) - length(replace(operation_alpha_numeric_codes , ' ', ''))>=2,
SUBSTRING_INDEX(SUBSTRING_INDEX(operation_alpha_numeric_codes , ' ', 3), ' ', -1) , '')
as Second_code,
If( length(operation_alpha_numeric_codes ) - length(replace(operation_alpha_numeric_codes , ' ', ''))>=4,
SUBSTRING_INDEX(SUBSTRING_INDEX(operation_alpha_numeric_codes , ' ', 3), ' ', -1), '') AS Third_code
FROM scraping.xp_pn_mrt;
I have used CASE WHEN clause with LENGTH function.
I used LENGTH to calculate number of occurences of your separator ' ' in the string. CASE WHEN it is ONE occurances then there are TWO "results". CASE WHEN it is TWO occurances then there are THREE "results".
Here is the DEMO that will show the correct results for your two problematic data.
SELECT mrt_name as MRT
, operation_alpha_numeric_codes as Original
, SUBSTRING_INDEX(operation_alpha_numeric_codes,' ', 1) as First_code
, CASE WHEN LENGTH(operation_alpha_numeric_codes) - LENGTH(REPLACE(operation_alpha_numeric_codes, ' ', '')) = 1
THEN SUBSTRING_INDEX(operation_alpha_numeric_codes,' ', -1)
WHEN LENGTH(operation_alpha_numeric_codes) - LENGTH(REPLACE(operation_alpha_numeric_codes, ' ', '')) = 2
THEN SUBSTRING_INDEX(SUBSTRING_INDEX(operation_alpha_numeric_codes,' ', 2), ' ', -1)
ELSE NULL
END Second_Code
, CASE WHEN LENGTH(operation_alpha_numeric_codes) - LENGTH(REPLACE(operation_alpha_numeric_codes, ' ', '')) = 2
THEN SUBSTRING_INDEX(SUBSTRING_INDEX(operation_alpha_numeric_codes,' ', -2), ' ', -1)
ELSE NULL
END Third_Code
FROM scraping.xp_pn_mrt;
I have a vendors table in my database that am experimenting with, as shown below
And when i run the sql command below
SELECT vendor_name
FROM vendors
ORDER BY vendor_name
LIMIT 10
I get the output below
My issue is am trying to extract the second word from each vendor_name and when the second word doesn't exist it's supposed to return a blank cell.
And below is the sql query i have written to do just that
SELECT vendor_name,
SUBSTRING(
SUBSTRING( vendor_name, LOCATE(' ', vendor_name) + 1),
1,
LOCATE( ' ', SUBSTRING( vendor_name, LOCATE(' ', vendor_name) + 1) ) - 1
) AS second_word
FROM vendors
ORDER BY vendor_name
LIMIT 10
And here is the output of that sql query
If you notice from the output above, when the words in the vendor_name are more than two, it returns the second word just fine and when the vendor_name contains one word it returns a blank cell as expected.
Problem comes when the vendor_name contains exactly two words, instead of returning the second word it is returning a blank cell for example in the case of American Express and ASC Signs.
How can i better improve my query so that even when the vendor_name does contain two words, it does return the second word instead of a blank cell?
Thank you.
That's because there is no space after the second word, if the text ends there, the locate() has no space to find.
Quick hack: Add a space at the end.
LOCATE( ' ', CONCAT(SUBSTRING( vendor_name, LOCATE(' ', vendor_name) + 1), ' ') ) - 1
SELECT vendor_name , substr(vendor_name , instr(vendor_name, " ") ,
case when LOCATE (' ', vendor_name,instr(vendor_name, " ") ) > 0 then LOCATE (' ',
vendor_name,instr(vendor_name, " ") ) else CHAR_LENGTH (vendor_name) end )
from vendors ;
I took tips from both #stick bit and #kiran gadhe and i came up with this sql query and it's working just fine
SELECT vendor_name,
CASE
WHEN INSTR( vendor_name, ' ' ) = 0
THEN
''
ELSE
SUBSTRING(
SUBSTRING( vendor_name, LOCATE(' ', vendor_name) + 1),
1,
LOCATE( ' ', CONCAT(SUBSTRING( vendor_name, LOCATE(' ', vendor_name) + 1), ' ') ) - 1
)
END AS second_word
FROM vendors
ORDER BY vendor_name
LIMIT 10
I am doing a GROUP_CONCAT to display names in the format of
Lastname1, Firstname1; Lastname2, Firstname2
There are occasions where the lastname also contains strings enclosed in curved brackets - ( and ). Since these will get displayed in the middle of the concatenated string I'm trying to move it to the end.
My solution so far is this:
GROUP_CONCAT(
DISTINCT
CASE
WHEN UPPER(psn.surname) LIKE '%INACTIVE%' THEN CONCAT(TRIM(REPLACE(psn.surname, '(Inactive)', '')), ', ', psn.firstname, ' (Inactive)')
ELSE CONCAT(psn.surname, ', ', psn.firstname)
END
ORDER BY
CASE
WHEN UPPER(psn.surname) LIKE '%INACTIVE%' THEN CONCAT(TRIM(REPLACE(psn.surname, '(Inactive)', '')), ', ', psn.firstname, ' (Inactive)')
ELSE CONCAT(psn.surname, ', ', psn.firstname)
END
ASC
SEPARATOR '; '
) AS contacts
So far this works but it only looks for a specific string, there are also cases when the string within the curved brackets isn't Inactive and I don't want to hard code all of those.
So basically how do I move a string enclosed in curved brackets to the end of a string. I imagine regex is the best solution to this problem but I don't know how to use regex.
Hmmm . . . something like this might work:
GROUP_CONCAT(DISTINCT (CASE WHEN psn.surname LIKE '%(%'
THEN CONCAT(TRIM(SUBSTRING_INDEX(psn.surname, '(', 1)), ', ',
psn.firstname, '('
SUBSTRING_INDEX(psn.surname, '(', -1)
)
ELSE CONCAT(psn.surname, ', ', psn.firstname)
END)
ORDER BY psn.surname, psn.firstname ASC SEPARATOR '; '
) AS contacts
I didn't repeat the expression for the order by. That seems like overkill.
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?