Mysql replace function returns null - mysql

I have a table activity_types which contains the text for activities e.g.
New project has been assigned to {user} with name {project}
New discussion has been posted by {client} on {project}
Project {project} has been closed by {user}
A draft has been approved by {client} on {project}
and so on
I have another table in which the id of these texts has been populated.
Now my requirement is to fetch these by replacing the text within {} with their actual values like {client} should be replaced with client name, {project} should be replaced with project name.
I have tried using nested replace mysql function but it returns null string. Here is my query
SELECT REPLACE(REPLACE(REPLACE(att.type_text, '{project}', p.project_business_name), '{user}', u.user_fullname), '{client}', c.client_name) AS activity, a.*
FROM activities a
LEFT JOIN activity_types AS att ON att.type_id = a.activity_type
LEFT JOIN users u ON u.user_id = a.activity_user AND a.activity_user IS NOT NULL
LEFT JOIN projects p ON p.project_id = a.activity_project AND p.project_is_removed = '0'
LEFT JOIN clients c ON c.client_id = a.activity_client AND a.activity_client IS NOT NULL;
The problem is that if i replace the c.client_name with static text it works fine but when i replace the static string with c.client_name it return null in activities which does not have {client}. Any guesses where am i doing wrong?

try adding COALESCE on the column
...,COALESCE(c.client_name, ''),
basically if the column (c.client_name) is null, it will be replace with empty string.
what happens is that when part of the string is replace with null, the whole string becomes null.

SQL null is contagious. Once you get a null value somewhere and use that null value in functions and derived values, the whole result becomes null. As such, if any of your fields in the REPLACE() chain are null, the whole result becomes null. You can try something like
COALESCE(name_of_field, '')
or
IFNULL(name_of_field, '')
to convert those nulls into an empty string.

Related

Unable to write WHERE condition for a JSON array field with array values in MySQL

I have a JSON datatype with array values in a MySQL database as highlighted in the picture below:
I want to compare multiple values with this category field, Ex: ["49","27"].
How to write a MySQL query for this?
I tried this query:
SELECT l.*, pc.name as cat_name,u.name as uname
FROM listing l
LEFT OUTER JOIN package_purchased_history ph ON ph.user_id = l.user_id AND ph.expired_date >= 1656095400 AND ph.purchase_date <= 1656095400
LEFT OUTER JOIN user u ON u.id = l.user_id
INNER JOIN category pc ON JSON_SEARCH(l.categories, 'one', pc.id) AND pc.parent = 26
WHERE JSON_CONTAINS(l.categories,'["49"]','$[0]') IS NOT NULL
AND l.status = 'active'
GROUP BY l.id
Unfortunately it is not working, so please suggest me the a better approach.
$[0] is the first element of the array, not the whole array. So you're testing whether an array is contained in a single number, not whether the array is contained in the array.
The whole array is $, since that refers to the top-level element of the JSON value. But you don't need to specify the path when you're searching the whole value.
WHERE JSON_CONTAINS(l.categories, '[49]')
You don't need IS NOT NULL there, since JSON_CONTAINS() returns a boolean. The value will never be NULL unless l.categories is NULL.

how can i return a null value if the AND statement produces no results?

i have a pretty lengthy sql statement that pulls data based on a users input. one of the parameters is a date range and currently if the date does not exist then nothing is returned. im trying to figure out how to place the AND parameter within an if condition so that if the date does not exist, the data is still returned with a null. i have looked into IFNULL and CASE but cant seem to figure out a way to implement it properly
SELECT pName,pNum,pPhase,pStart,pEnd,pComp,pHoursBudgeted,Zee_Kray_A
FROM hourmap
JOIN projects ON projects.pID = hourmap.ProjectID
JOIN schedule ON schedule.id = hourmap.ScheduleID
WHERE (pManager LIKE '%' or pManager is Null)
AND (pNum LIKE '%90668%' or pNum is Null)
AND (year_week LIKE '2020-W01'or year_week is Null)
;
within the sql above, in the last parameter, if 2020-W01 is anything other than this, than there is nothing returned. how can i place this final parameter in an if statement or make it conditional?
use left join and move the left joined colunms condition in ON clause
SELECT pName,pNum,pPhase,pStart,pEnd,pComp,pHoursBudgeted,Zee_Kray_A
FROM hourmap
LEFT JOIN projects ON projects.pID = hourmap.ProjectID
LEFT JOIN schedule ON schedule.id = hourmap.ScheduleID
AND (pManager LIKE '%' or pManager is Null)
AND (pNum LIKE '%90668%' or pNum is Null)
AND (year_week LIKE '2020-W01'or year_week is Null)
;

MySQL LEFT JOIN with WHERE function-call produces wrong result

From MySQL 5.7 I am executing a LEFT JOIN, and the WHERE clause calls a user-defined function of mine. It fails to find a matching row which it should find.
[Originally I simplified my actual code a bit for the purpose of this post. However in view of a user's proposed response, I post the actual code as it may be relevant.]
My user function is:
CREATE FUNCTION `jfn_rent_valid_email`(
rent_mail_to varchar(1),
agent_email varchar(45),
contact_email varchar(60)
)
RETURNS varchar(60)
BEGIN
IF rent_mail_to = 'A' AND agent_email LIKE '%#%' THEN
RETURN agent_email;
ELSEIF contact_email LIKE '%#%' THEN
RETURN contact_email;
ELSE
RETURN NULL;
END IF
END
My query is:
SELECT r.RentCode, r.MailTo, a.AgentEmail, co.Email,
jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email)
AS ValidEmail
FROM rents r
LEFT JOIN contacts co ON r.RentCode = co.RentCode -- this produces one match
LEFT JOIN link l ON r.RentCode = l.RentCode -- there will be no match in `link` on this
LEFT JOIN agents a ON l.AgentCode = a.AgentCode -- there will be no match in `agents` on this
WHERE r.RentCode = 'ZAKC17' -- this produces one match
AND (jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email) IS NOT NULL)
This produces no rows.
However. When a.AgentEmail IS NULL if I only change from
AND (jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email) IS NOT NULL)
to
AND (jfn_rent_valid_email(r.MailTo, NULL, co.Email) IS NOT NULL)
it does correctly produce a matching row:
RentCode, MailTo, AgentEmail, Email, ValidEmail
ZAKC17, N, <NULL>, name#email, name#email
So, when a.AgentEmail is NULL (from non-matching LEFT JOINed row), why in the world does passing it to the function as a.AgentEmail act differently from passing it as a literal NULL?
[BTW: I believe I have used this kind of construct under MS SQL server in the past and it has worked as I would expect. Also, I can reverse the test of AND (jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email) IS NOT NULL) to AND (jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email) IS NULL) yet I still get no match. It's as though any reference to a.... as a parameter to the function causes no matching row...]
Most likely this is an issue with optimizer turning the LEFT JOIN into a INNER JOIN. The optimizer may do this when it believes that the WHERE-condition is always false for the generated NULL row (which it in this case is not).
You can take a look at the query plan with the EXPLAIN command, you will likely see different table order depending on the query variation.
If the actual logic of the function is to check all emails with one function call, you may have better luck with using a function that takes just one email address as parameter and use that for each email-column.
You can try without the function:
SELECT r.RentCode, r.MailTo, a.AgentEmail, co.Email,
jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email)
AS ValidEmail
FROM rents r
LEFT JOIN contacts co ON r.RentCode = co.RentCode -- this produces one match
LEFT JOIN link l ON r.RentCode = l.RentCode -- there will be no match in `link` on this
LEFT JOIN agents a ON l.AgentCode = a.AgentCode -- there will be no match in `agents` on this
WHERE r.RentCode = 'ZAKC17' -- this produces one match
AND ((r.MailTo='A' AND a.AgentEmail LIKE '%#%') OR co.Email LIKE '%#%' )
Or wrap the function in a subquery:
SELECT q.RentCode, q.MailTo, q.AgentEmail, q.Email, q.ValidEmail
FROM (
SELECT r.RentCode, r.MailTo, a.AgentEmail, co.Email,
jfn_rent_valid_email(r.MailTo, a.AgentEmail, co.Email) AS ValidEmail
FROM rents r
LEFT JOIN contacts co ON r.RentCode = co.RentCode -- this produces one match
LEFT JOIN link l ON r.RentCode = l.RentCode -- there will be no match in `link` on this
LEFT JOIN agents a ON l.AgentCode = a.AgentCode -- there will be no match in `agents` on this
WHERE r.RentCode = 'ZAKC17' -- this produces one match
) as q
WHERE q.ValidEmail IS NOT NULL
Changing the call to the function in the WHERE clause to read
jfn_rent_valid_email(r.MailTo, IFNULL(a.AgentEmail, NULL), IFNULL(co.Email, NULL)) IS NOT NULL
solves the issue.
It appears that the optimizer feels it can incorrectly guess that the function will return NULL in the non-match LEFT JOIN case if a plain reference to a.AgentEmail is passed as any parameter. But if the column reference is inside any kind of expression the optimizer ducks out. Wrapping it inside a "dummy", seemingly pointless IFNULL(column, NULL) is thus enough to restore correct behaviour.
I am marking this as the accepted solution because it is by far the simplest workaround, requiring the least code change/complete query rewrite.
However, full credit is due to #slaakso's post here in this topic for analysing the problem. Note that he states that the behaviour has been fixed/altered in MySQL 8 such that this workaround is unnecessary, so it may only be necessary in MySQL 5.7 or earlier.

mysql create a hardcoded value within select statement

I want to know if I can hardcode a value within my select statement. I have the following mysql query that I use to generate a list.
I use concat to build this string as part of creating the list.
However, now I need to generate an 'Unknown' record as part of the list.
For example:
20(ABC Object #20)
24(DEF Object #24)
I want to add a value of 'UKNOWN' to the top of the list:
--(UNKNOWN -- )
20(ABC Object #20)
24(DEF Object #24)
Here is what I have so far:
SELECT CAST(p.Serial AS UNSIGNED INTEGER) as Serial,
p.Object_ID,
p.Part_ID,
st.Description AS ObjectDesc,
s.Object_Num,
concat(Serial,' (',st.Desc,' #',s.Object_Num,')') as DropList
FROM Parts p LEFT JOIN Objects s ON p.Object_ID = s.Object_ID
LEFT JOIN ObjectTypes st ON s.ObjectType_ID = st.ObjectType_ID
Can I hardcode that string in my select statement? If so, how do I do that?
If I understand correctly, you want a new row. You can use UNION
SELECT "" as Serial,
"" as Object_ID,
"" as ObjectDEsc,
"" as Object_Num,
"--(UNKNOWN -- )" as DropList
UNION
SELECT CAST(p.Serial AS UNSIGNED INTEGER) as Serial,
p.Object_ID,
p.Part_ID,
st.Description AS ObjectDesc,
s.Object_Num,
concat(Serial,' (',st.Desc,' #',s.Object_Num,')') as DropList
FROM Parts p LEFT JOIN Objects s ON p.Object_ID = s.Object_ID
LEFT JOIN ObjectTypes st ON s.ObjectType_ID = st.ObjectType_ID
In a union query each part must select the same columns. Since your original query included Serial, Object_ID, Part_ID and ObjectDesc, the first part must include it as well.

MySQL CONCAT: Add a comma/separator only when field is NOT NULL

To summarize, I have an update query that will tack on a string of data (e.g. SAVE15) to an existing field. Currently, I anticipate this field to already have some information in it so my values are appending as ", SAVE15" which is a comma and space separator. This works for now but soon I anticipate the need to insert a comma and space only if the field is NOT NULL. If it is null, I need it to insert "SAVE15". Here is my current query:
UPDATE sales_flat_order sfo
INNER JOIN sales_flat_order_grid sfog
ON sfog.entity_id = sfo.entity_id
SET sfo.coupon_code = concat(IFNULL(sfo.coupon_code, ""),", SAVE15")
WHERE sfog.increment_id = "12345678";
Here is my attempt to use CONCAT_WS to add a separator ony when necessary:
UPDATE sales_flat_order sfo
INNER JOIN sales_flat_order_grid sfog
ON sfog.entity_id = sfo.entity_id
SET sfo.coupon_code = CONCAT_WS(',',IFNULL(sfo.coupon_code, ""),"SAVE15")
WHERE sfog.increment_id = "12345678";
I thought it was working at first but then it inserted ",SAVE15" onto a null field. I believe the second query is the correct method but I seem to be using it incorrectly.
The documentation for Concat_WS tell us that it will skip any Null field but not an empty string. You must therefore remove the call to IfNull(sfo.coupon_code, "") that convert the Null value into an empty string.