I have and ORDERS table from a customer shopping cart. It's the typical customer name, address, etc..list of fields. The twist is that the cart will populate the shipping information into the appropriate fields (shipName, shipAddress, ShipState, etc) when the customer enters shipping information that is different then the billing information. However, when the customer ships to themselves, the cart does not move the shipping information to the proper fields for shipping (i.e., shipName, shipAddress, shipState). It keeps this information in the ordName, ordAddress, ordCity, etc fields and then keeps the shipping related fields empty.
Therefore, these shipping fields are then empty for orders that customers ship to themselves. I used IF commands to move the ordName, ordAddr, etc to alias names when no shipping information is provided so that the shipping information for all order types (whether the customer ships to themselves or to another address) is handled. That part is working fine in my query below.
One issue remains. My shipping program can't use long State name (e.g, Michigan, New York, etc). It needs the state to be the two character abbreviation (e.g, MI, NY). I have a look up table called *STATES that has a mapping between long state name and the two character abbreviation. I am trying to use the ShipState alias to look up the correct two character name for a given state. I tried to do this as a JOIN but keep getting errors. I removed the join I was using and am only showing the code that works correct right now but doesn't do the mapping for state abbreviation. Can someone please help?
SELECT
orders.ordDate AS `Date`,
orders.ordID AS Order_ID,
orders.ordEmail AS Email,
IF(ordShipName = ' ', ordName, ordShipName) AS Name,
IF(ordShipAddress = ' ', ordAddress, ordShipAddress) AS Address_1,
IF(ordShipAddress2 = ' ', ordAddress2, ordShipAddress2) AS Address_2,
IF(ordShipCity = ' ', ordCity, ordShipCity) AS City,
IF(ordShipState = ' ', ordState, ordShipState) AS ShipState,
IF(ordShipZip = ' ', ordZip, ordShipZip) AS Postal,
IF(ordShipCountry = ' ', ordCountry, ordShipCountry) AS Country,
IF(ordShipPhone = ' ', ordPhone, ordShipPhone) AS Phone,
FROM
orders
WHERE
orders.ordID > 21700
HAVING
Country = 'United States of America'
You can't use the column alias ShipState for the join to your lookup table. Instead, repeat your IF construct again:
SELECT
orders.ordDate AS `Date`,
orders.ordID AS Order_ID,
orders.ordEmail AS Email,
IF(ordShipName = ' ', ordName, ordShipName) AS Name,
IF(ordShipAddress = ' ', ordAddress, ordShipAddress) AS Address_1,
IF(ordShipAddress2 = ' ', ordAddress2, ordShipAddress2) AS Address_2,
IF(ordShipCity = ' ', ordCity, ordShipCity) AS City,
IF(ordShipState = ' ', ordState, ordShipState) AS ShipState,
IF(ordShipZip = ' ', ordZip, ordShipZip) AS Postal,
IF(ordShipCountry = ' ', ordCountry, ordShipCountry) AS Country,
IF(ordShipPhone = ' ', ordPhone, ordShipPhone) AS Phone,
STATE.StateAbbreviation
FROM
orders
INNER JOIN STATES
ON IF(orders.ordShipState = ' ', orders.ordState, orders.ordShipState) = STATES.State
WHERE
orders.ordID > 21700
HAVING
Country = 'United States of America'
You should put the code in where you try to join the two tables. You might just have a syntax error that someone could help you with. If you have a table called STATES with columns state and state_abrev, try this:
select ..., .., ..., STATES.state_abrev, ..., ..., from orders, STATES where STATES.state = orders.ordShipState AND ... ... HAVING ...
Since the origin of the data is a small set of transaction it will be very fast, I would have it do a left join to two instances of the states table respectively on their field and have your SELECT based on whichever was not empty... such as
select
...(other fields),
IF(orders.ordShipState = ' ', s1.StateName, s2.StateName ) as StateName
from
Orders
left outer join states s1
on orders.ordstate = s1.state
left outer join states s2
on orders.ordShipState = s2.state
where
orders.ordID > 21700
(etc with rest of query)...
This way, the join will always be having a join based on BOTH POSSIBLE states... So, whichever one exists, based on your preference, get THAT state first, otherwise get the other.
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;
database noob so please bear with me. Writing in Oracle MySql.
I have a block of code which notably is meant to add rows to DWCUST but change invalid gender values coming from a2custbris to valid ones by comparing them to the genderspelling table. I wrote the select code in the brackets to do this and it works. However, certain gender values are null in a2custbris, I want these to be written as 'U' in DWCUST. So how can I do both?
Here's the code:
INSERT INTO DWCUST (DWCUSTID, DWSOURCEIDBRIS, DWSOURCEIDMELB, FIRSTNAME, SURNAME, GENDER, PHONE, POSTCODE, CITY, STATE, CUSTCATNAME)
SELECT dwcustSeq.nextval, cb.custid, Null, cb.fname, cb.sname, (select trim(gs.NEW_VALUE) FROM genderspelling gs WHERE upper(gs.INVALID_VALUE) = upper(cb.GENDER)), cb.phone, cb.postcode, cb.city, cb.state, cc.custcatname
FROM a2custbris cb
NATURAL JOIN a2custcategory cc
WHERE cb.rowid IN (SELECT source_rowid FROM A2ERROREVENT where filterid = 7);
Any help is creating appreciated!
Use a CASE expression:
INSERT INTO DWCUST (DWCUSTID, DWSOURCEIDBRIS, DWSOURCEIDMELB, FIRSTNAME, SURNAME,
GENDER, PHONE, POSTCODE, CITY, STATE, CUSTCATNAME)
SELECT
dwcustSeq.nextval, cb.custid, Null, cb.fname, cb.sname,
CASE WHEN cb.gender IS NULL THEN 'U'
ELSE (select trim(gs.NEW_VALUE) FROM genderspelling gs
WHERE upper(gs.INVALID_VALUE) = upper(cb.GENDER))
END,
cb.phone, cb.postcode, cb.city, cb.state, cc.custcatname
FROM a2custbris cb
NATURAL JOIN a2custcategory cc
WHERE cb.rowid IN (SELECT source_rowid FROM A2ERROREVENT where filterid = 7);
If you want 'U' for every value that has no match in genderspelling, then use COALESCE instead:
INSERT INTO DWCUST (DWCUSTID, DWSOURCEIDBRIS, DWSOURCEIDMELB, FIRSTNAME, SURNAME,
GENDER, PHONE, POSTCODE, CITY, STATE, CUSTCATNAME)
SELECT
dwcustSeq.nextval, cb.custid, Null, cb.fname, cb.sname,
COALESCE(
(select trim(gs.NEW_VALUE) FROM genderspelling gs
WHERE upper(gs.INVALID_VALUE) = upper(cb.GENDER))
, 'U'),
cb.phone, cb.postcode, cb.city, cb.state, cc.custcatname
FROM a2custbris cb
NATURAL JOIN a2custcategory cc
WHERE cb.rowid IN (SELECT source_rowid FROM A2ERROREVENT where filterid = 7);
I am having this SQL command:
Select company, purchases.stock,SUM(ammount)*price from purchases INNER JOIN curstock ON purchases.stock = curstock.stock Group by company , purchases.stock;
Which returns following table :
Is it possible that it would print instead of table strings like:
"Company XXX owns YYY in ZZZ stock."
or SQL does not provide such formatting and it has to be done in code.
You can use CONCAT() function
SELECT CONCAT('Company ', company, ' owns ', SUM(ammount)*price, ' in ', purchases.stock, ' stock.') AS value
FROM purchases
INNER JOIN curstock
ON purchases.stock = curstock.stock
GROUP BY company , purchases.stock;
I am trying to solve an issue with joining last and first names that are identified using id's in another table. My code is producing the correct fields, but the Guide_Name and Guest_Name columns show all/only 0 (zero). Here is my code:
use www;
SELECT
d.destination_name,
tt.trip_type_name,
t.trip_number,
t.trip_date,
CONCAT(e.last_name + ', ' + e.first_name) AS guide_name,
CONCAT(g.last_name + ', ' + g.first_name) AS guest_name,
ex.exp_name AS guest_experience,
g.age AS guest_age,
g.weight AS guest_weight,
g.swimmer AS guest_is_swimmer,
g.mobile_phone AS guest_mobile_phone
FROM
trip_type tt
JOIN
trips t ON tt.trip_type_code = t.trip_type_code
JOIN
destination d ON t.destination_code = d.destination_code
JOIN
reservation r ON t.trip_number = r.trip_number
JOIN
guests g ON r.guest_id = g.guest_id
JOIN
experience ex ON ex.exp_code = g.exp_code
JOIN
employees e ON t.guide_employee_id = e.employee_id
ORDER BY d.destination_name , tt.trip_type_name , t.trip_date , g.last_name , e.employee_id
And here is the EER diagram:
CONCAT should just be a comma separated list of strings, so I would first change
CONCAT(e.last_name + ', ' + e.first_name)
to
CONCAT(e.last_name, ', ', e.first_name)
and see if that helps.
With + in the concat, mysql thinks you want them treated as numbers. Perhaps you're confusing it with javascript?
http://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_concat
dave has the heart of this problem, but I expect you also will want to deal with nulls. I think the final code you want will look like this:
COALESCE(CONCAT(e.last_name,', ',e.first_name),e.last_name,e.first_name,'') AS guide_name,
COALESCE(CONCAT(g.last_name,', ',g.first_name),g.last_name,g.first_name,'') AS guest_name,
I am trying to populate a table with phone number from a temp table. I have wrote the query with no problem. but my peoblem here is to know if the company already has a primary number or not
so I select 2 fields from my temp table called "cvsnumbers" 1) company_code (id) and the phone number.
I need to add a case statement to change the value of a main_number field. so if the number already has a number with main_number = 1 then I need to insert 0 for the new phone number but if there is no main_number then I need to insert 1 for the new phone number making it a primary phone number for the account.
this is my query
SELECT ac.account_id,
REPLACE(REPLACE(REPLACE(REPLACE(ta.phone_number, '-', ''), ' ', ''), ')', ''),'(','') AS Phone,
IFNULL(ta.ext, '') AS extention,
IFNULL(ta.main_number, 0) AS MainNumber,
ta.type AS contact_type,
'2' AS created_by
FROM cvsnumbers AS ta
INNER JOIN accounts AS ac ON ac.account_id = ta.company_code
WHERE LENGTH(REPLACE(REPLACE(REPLACE(REPLACE(ta.phone_number, '-', ''), ' ', ''), ')', ''),'(','') ) = 10
AND REPLACE(REPLACE(REPLACE(REPLACE(ta.phone_number, '-', ''), ' ', ''), ')', ''),'(','') NOT IN (SELECT contact_number FROM contact_numbers)
My issue is
`IFNULL(ta.main_number, 0) AS MainNumber,`
I want to change that to some what a case statment to check if a company_code already has a main_number or not.
How can I change this?
Thanks
I am still not sure exactly how your query should look like, but how about something along theese lines?
SELECT CASE
WHEN EXISTS (SELECT *
FROM contact_numbers
WHERE main_number = 1
AND contact_number =
<insert contact number from outer expression here>)
THEN 1
ELSE 0
END AS MainNumber
FROM ...