MySQL IF returning nothing - mysql

I am trying to do some optimisation, currently post mysql work is done on the results to set a new paramter $class_subject... so i am trying get this already calculated in mysql...
SELECT
class_grade.results as results,
subjects.subject as subject,
subjects_pseudonyms.pseudonym as pseudonym,
IF( subjects_pseudonyms.pseudonym = null, subjects.subject, subjects_pseudonyms.pseudonym ) as class_subject
FROM
class_grade
INNER JOIN class ON class_grade.class_ID = class.class_ID
INNER JOIN subjects ON class.subject_ID = subjects.a_ID
LEFT JOIN subjects_pseudonyms ON class.subject_pseudonym_ID = subjects_pseudonyms.a_ID
WHERE
class_grade.teacher_ID = :teacher_id AND
class_grade.class_ID = :current_class_ID AND
class_grade.report_set_ID = :report_set_ID AND
class_grade.student_ID = :current_student_ID
In the above query the pseudonym might be null, if so I am attempting to set a new variable class_subject to be either subject or pseudonym...
The query runs fine, a results example is:
[results] => 71
[subject] => Law
[pseudonym] =>
[class_subject] =>
The problem is, the class_subject is not being populated..
Is there something wrong with my IF() cond?
Thanks,
John

You need to use IS NULL instead of = NULL or ISNULL()
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_isnull
ISNULL() can be used instead of = to test whether a value is NULL.
(Comparing a value to NULL using = always yields false.)

Related

How to implement where condition only if we are passing values in MySQL

This is what I'm calling and passing from service:
List<String> projectOwners = filterComponent.getProjectOwner();
List<String> dataProviderCodes = filterComponent.getDataProvider();
custEnrollmentMgmts = customerEnrollmentManagementRepository.findActiveByPage(getArchiveDate(),
completedEnrollmentStatusCodes, projectOwners, dataProviderCodes, getNoSearchCriteria(projectOwners, dataProviderCodes), pageRequest);
This is the query:
select distinct a.*
from cust_enrollment_mgmt a
inner join cust_enrollment_rel_mgmt r
on a.cust_enrollment_proj_id=r.cust_enrollment_proj_id
left join cust_prime p
on p.cust_prime_id = r.cust_prime_id
where a.cust_enrollment_proj_id NOT IN
(
select ce.cust_enrollment_proj_id
from cust_enrollment_mgmt ce
inner join enrollment_status_avt es
on ce.enrollment_status_id = es.enrollment_status_id
where ce.sys_update_ts < "2022-06-06 00:00:00"
and es.enrollment_status_desc in ("Complete", "Inactive")
)
and
(
if ((:code) is not null and (:name) is not null ,
a.data_provider_cd in (:code) and p.cust_prime_nm in (:name),0)
or
if ((:code) is not null and (:name) is null , a.data_provider_cd in (:code),0)
or
if ((:code) is null and (:name) is not null , p.cust_prime_nm in (:name),0)
or 1=:nosearchcriteria
);
If and only if the dataProviderCodes or projectOwners is not empty
then only it should go to that where condition.
If I pass dataProviderCodes and pass projectOwners then it should
fetch only projectOwner data If I'm not passing anything then it has
to stop ("Complete", "Inactive") and fetch up to that.
After that ("Complete", "Inactive") I need to fetch as per this
condition ☝☝☝.
The main thing is that it has to fetch only if data is present for the param we are passing.
This is working but if I pass more than 1 data It fails because I'm passing a list it may contain 1 or more than 1 or empty. For example: for dataProviderCodes if I pass ["aa","bb"] it fails.
I need to perform fetching if and only if the values are not null or present.
The whole implementation is regarding filtering.

Doctrine DBAL query buider joins and where clause

To generate a report I (i.e. that has 0 whenever a value is nonexistent for a certain date) I use a calendar table.
The problem is that any extra conditions in my query need to go into the join clause. That makes the otherwise so flexible QB very unflexible.
$this->query->select('dt AS date, count o.orderId) as orders');
$this->query->from('calendar_table', 'ct');
$this->query->leftJoin('ct', 'orders', 'o',
'o.orderDate = ct.dt AND o.SOME_EXTRA_CONDITION = VALUE');
$this->query->groupBy('dt');
The problem is the o.SOME_EXTRA_CONDITION = VALUE part. Is there any way I can get this condition out of the join clause and in an extra QB call like
$this->query->where(o.SOME_EXTRA_CONDITION = VALUE)?
Of course putting the condition in a normal where clause does noet yield the same result, as this happens after the join on the final result but what I need is a where clause on the right table only before the join.
An example for leftJoin from the official doctrine documentation
// Example - $qb->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, $qb->expr()->eq('p.area_code', 55))
// Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55')
// Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55', 'p.id')
public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null);
Try changing your leftJoin accordingly
$this->query->leftJoin('ct.orders', 'o', 'WITH','o.orderDate = ct.dt AND o.SOME_EXTRA_CONDITION = VALUE');

NULL != NULL in mysql query

I am trying to do a query that sees if fields are equivalent. However, whenever the field is NULL it returns a false result. I even tried doing the same thing with the column itself:
SELECT * FROM `mturk_completion` WHERE (`mturk_completion`.`imdb_url` =
`mturk_completion`.`imdb_url` AND `mturk_completion`.`worker_id` = 'A3NF84Q37D7F35' )
And it only returns results where the column is not NULL. Why is this so, and how do I get around it?
Your title is absolutely correct for any SQL implementation (not just MySQL). NULL is not equal to anything (including another NULL).
You need to use explicit IS NULL check or COALESCE() function (or its RDBMS-dependent alternatives) to set some default value in case of NULL.
Your comparison of mturk_completion.imdb_url to itself is redundant and should always return True, except when mturk_completion.imdb_urlis Null, in which case it will return Null.
That's because the operator = returns either True, False when comparisons can be made or Null, when either of the two operators is Null
Try this to illustrate the situation.
SELECT 1 = NULL; -- returns NULL
SELECT 1 != NULL; -- also return NULL
SELECT ISNULL(1 = NULL); -- returns 1
SELECT ISNULL(1 != NULL); -- returns 1
If you rewrite your query like below, your problems with ignoring NULLs will go away:
SELECT * FROM `mturk_completion` WHERE worker_id = 'A3NF84Q37D7F35'
I think you can use
(table.Field = table2.Field OR COALESCE(table.Field, table2.Field) IS NULL)

MySQL Left Join, SELECT a field either from one or the other if null

I am trying to LEFT JOIN 2 tables. which is working out fine. But i am getting back two sets of fields named setting_value. iam trying to get tblSettings.setting_value only if tblAgencySettings.setting_value is NULL. How would i go about doing this? I know i can rename the fields, then in PHP i can check the tblAgencySettings.setting_value and if NULL then grab the tblSettings.setting_value but i prefer to keep this at MySQL.
SELECT `tblSettings`.`id`, `tblSettings`.`setting_name`,
`tblSettings`.`setting_value`, `tblAgencySettings`.`setting_value`
FROM `tblSettings` LEFT JOIN `tblAgencySettings`
ON `tblSettings`.`id` = `tblAgencySettings`.`setting_id`
AND `tblAgencySettings`.`agency_id` = '1'
WHERE `tblSettings`.`changeable` = '1'
slight issue i just noticed. i failed to mention this. if tblAgencySettings.setting_value does have a value. but changeable is not 1 then just select tblSettings.setting_value
Just add a COALESCE:
SELECT `tblSettings`.`id`, `tblSettings`.`setting_name`,
COALESCE(`tblAgencySettings`.`setting_value`, `tblSettings`.`setting_value`)
FROM `tblSettings` LEFT JOIN `tblAgencySettings`
ON `tblSettings`.`id` = `tblAgencySettings`.`setting_id`
AND `tblAgencySettings`.`agency_id` = '1'
WHERE `tblSettings`.`changeable` = '1'
The COALESCE function returns the first non-NULL value you give it so this:
COALESCE(`tblAgencySettings`.`setting_value`, `tblSettings`.`setting_value`)
Will be tblAgencySettings.setting_value if that's not NULL and tblSettings.setting_value if tblAgencySettings.setting_value is NULL.
If tblAgencySettings.setting_value can also be zero and you want to ignore that as well as NULL, then you could use this instead of the COALESCE above:
COALESCE(
IF(`tblAgencySettings`.`setting_value` = 0, NULL, `tblAgencySettings`.`setting_value`),
`tblSettings`.`setting_value`
)
The IF returns the second argument if the first is true and the third if the first argument is false so the above use converts zero to NULL. Or, you could go all the way to a CASE statement:
case
when `tblAgencySettings`.`setting_value` = 0 then `tblSettings`.`setting_value`
when `tblAgencySettings`.`setting_value` IS NULL then `tblSettings`.`setting_value`
else `tblSettings`.`setting_value`
end
Change your SQL Statement to this:
SELECT `tblSettings`.`id`, `tblSettings`.`setting_name`,
CASE WHEN `tblSettings`.`setting_value` IS NULL THEN `tblAgencySettings`.`setting_value`
ELSE `tblSettings`.`setting_value` END AS `setting_value`
FROM `tblSettings` LEFT JOIN `tblAgencySettings`
ON `tblSettings`.`id` = `tblAgencySettings`.`setting_id`
AND `tblAgencySettings`.`agency_id` = '1'
WHERE `tblSettings`.`changeable` = '1'
Here's a link to MYSQL CASE Statement for your reference.

How can I speed up this linq to sql function?

I have a function (called "powersearch", the irony!) that searches for a set of strings across a bunch(~ 5) of fields.
The words come in as one string and are separated by spaces.
Some fields can have exact matches, others should have "contains".
(Snipped for brevety)
//Start with all colors
IQueryable<Color> q = db.Colors;
//Filter by powersearch
if (!string.IsNullOrEmpty(searchBag.PowerSearchKeys)){
foreach (string key in searchBag.SplitSearchKeys(searchBag.PowerSearchKeys)
.Where(k=> !string.IsNullOrEmpty(k))){
//Make a local copy of the var, otherwise it gets overwritten
string myKey = key;
int year;
if (int.TryParse(myKey, out year) && year > 999){
q = q.Where(c => c.Company.Name.Contains(myKey)
|| c.StockCode.Contains(myKey)
|| c.PaintCodes.Any(p => p.Code.Equals(myKey))
|| c.Names.Any(n => n.Label.Contains(myKey))
|| c.Company.CompanyModels.Any(m => m.Model.Name.Contains(myKey))
|| c.UseYears.Any(y => y.Year.Equals(year))
);
}
else{
q = q.Where(c => c.Company.Name.Contains(myKey)
|| c.StockCode.Contains(myKey)
|| c.PaintCodes.Any(p => p.Code.Contains(myKey))
|| c.Names.Any(n => n.Label.Contains(myKey))
|| c.Company.CompanyModels.Any(m => m.Model.Name.Equals(myKey))
);
}
}
}
Because the useYear count is rather large, I tried to check for it as little as possible by outruling all numbers that can never be a number that makes sence in this case. Similar checks are not possible on the other fields since they can pretty much contain any thinkable string.
Currently this query takes about 15 secs for a single, non-year string. That's too much.
Anything I can do to improve this?
--Edit--
Profiler shows me the following info for the part where the string is not a year:
exec sp_reset_connection
Audit login
exec sp_executesql N'
SELECT COUNT(*) AS [value]
FROM [dbo].[CLR] AS [t0]
INNER JOIN [dbo].[CO] AS [t1] ON [t1].[CO_ID] = [t0].[CO_ID]
WHERE
([t1].[LONG_NM] LIKE #p0)
OR ([t0].[EUR_STK_CD] LIKE #p1)
OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[PAINT_CD] AS [t2]
WHERE ([t2].[PAINT_CD] LIKE #p2)
AND ([t2].[CLR_ID] = [t0].[CLR_ID])
AND ([t2].[CUSTOM_ID] = [t0].[CUSTOM_ID])
)
)OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[CLR_NM] AS [t3]
WHERE ([t3].[CLR_NM] LIKE #p3)
AND ([t3].[CLR_ID] = [t0].[CLR_ID])
AND ([t3].[CUSTOM_ID] = [t0].[CUSTOM_ID])
)
) OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[CO_MODL] AS [t4]
INNER JOIN [dbo].[MODL] AS [t5] ON [t5].[MODL_ID] = [t4].[MODL_ID]
WHERE ([t5].[MODL_NM] = #p4)
AND ([t4].[CO_ID] = [t1].[CO_ID])
)
)
',N'#p0 varchar(10),#p1 varchar(10),#p2 varchar(10),#p3 varchar(10),#p4 varchar(8)',#p0='%mercedes%',#p1='%mercedes%',#p2='%mercedes%',#p3='%mercedes%',#p4='mercedes'
(took 3626 msecs)
Audit Logout (3673 msecs)
exec sp_reset_connection (0msecs)
Audit login
exec sp_executesql N'
SELECT TOP (30)
[t0].[CLR_ID] AS [Id],
[t0].[CUSTOM_ID] AS [CustomId],
[t0].[CO_ID] AS [CompanyId],
[t0].[EUR_STK_CD] AS [StockCode],
[t0].[SPCL_USE_CD] AS [UseCode],
[t0].[EFF_IND] AS [EffectIndicator]
FROM [dbo].[CLR] AS [t0]
INNER JOIN [dbo].[CO] AS [t1] ON [t1].[CO_ID] = [t0].[CO_ID]
WHERE
([t1].[LONG_NM] LIKE #p0)
OR ([t0].[EUR_STK_CD] LIKE #p1)
OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[PAINT_CD] AS [t2]
WHERE ([t2].[PAINT_CD] LIKE #p2)
AND ([t2].[CLR_ID] = [t0].[CLR_ID])
AND ([t2].[CUSTOM_ID] = [t0].[CUSTOM_ID])
)
)
OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[CLR_NM] AS [t3]
WHERE ([t3].[CLR_NM] LIKE #p3)
AND ([t3].[CLR_ID] = [t0].[CLR_ID])
AND ([t3].[CUSTOM_ID] = [t0].[CUSTOM_ID])
)
)
OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[CO_MODL] AS [t4]
INNER JOIN [dbo].[MODL] AS [t5] ON [t5].[MODL_ID] = [t4].[MODL_ID]
WHERE ([t5].[MODL_NM] = #p4)
AND ([t4].[CO_ID] = [t1].[CO_ID])
)
)'
,N'#p0 varchar(10),#p1 varchar(10),#p2 varchar(10),#p3 varchar(10),#p4 varchar(8)',#p0='%mercedes%',#p1='%mercedes%',#p2='%mercedes%',#p3='%mercedes%',#p4='mercedes'
(took 3368 msecs)
The database structure, sadly, is not under my control. It comes from the US and has to stay in the exact same format for compatibility reasons. Although most of the important fields are indeed indexed, they are indexed in (unnecessary) clustered primary keys. There's verry little I can do about that.
Okay, let's break this down - the test case you're interested in first is a single non-year, so all we've got is this:
q = q.Where(c => c.Company.Name.Contains(myKey)
|| c.StockCode.Contains(myKey)
|| c.PaintCodes.Any(p => p.Code.Contains(myKey))
|| c.Names.Any(n => n.Label.Contains(myKey))
|| c.Company.CompanyModels.Any(m => m.Model.Name.Equals(myKey))
Am I right? If so, what does the SQL look like? How long does it take just to execute the SQL statement in SQL Profiler? What does the profiler say the execution plan looks like? Have you got indexes on all of the appropriate columns?
Use compiled queries.
If you don't, you will lose up to 5-10x times performance, as LINQ-to-SQL will have to generate SQL from query every time you call it.
Things become worse when you use non-constants in LINQ-to-SQL as getting their values is really slow.
This assumes that you already have indexes and sane DB schema.
BTW, I am not kidding about 5-10x part.