I have found out that my SQL 2008 R2 database is really struggling with COALESCE function if used within search.
CODE:
where
i.id_categ = COALESCE(#id_categ, i.id_categ )
and i.id_brand = COALESCE(#id_brand , i.id_brand )
and i.id_model = COALESCE(#id_model , i.id_model )
and i.id_type = COALESCE(#id_karoseria, i.id_type )
and i.id_fuel = COALESCE(#id_palivo, i.id_fuel )
and (i.year between #year_from and #year_to)
and (i.price between #price_from and #price_to)
DYNAMIC variables:
ALTER PROCEDURE [dbo].[spInzeratSelect]
#id_categ int = null,
#id_brand int = null,
#id_model int = null,
#id_fuel int = null,
#id_type int = null,
Search should work with or without these variables.
Benchmark:
with COALESCE = Total Execution Time: 3582
without COALESCE conditions = Total Execution Time: 13
You get the difference ...
Is there a nice solution how to ignore COALESCE and create dynamic SQL select with different approch ?
Thanks.
tIn your very specific case you should replace all your COALESCE search parameters with the following pattern:
AND ( (#id_brand IS NULL) OR (i.id_brand = #id_brand) )
etc
The parameter is evaluated as a literal before execution, so doing it this way makes the condition sargable.
This is functionally equivalent to your query except you're first checking against the literal value, which can be optimized away if it is, in fact, null.
EDIT: Apparently this is the approach recommended in #Joe Stefanelli's link as well. I originally poached it from Erland Sommerskog.
EDIT2: And I always forget to mention OPTION (RECOMPILE) too.
Related
I'm using SQL Server. I'm trying to run the following SQL script and I'm getting this error:
Error converting data type varchar to bigint.
This is the script:
with T as
(
select
sp.ProfileId
,sp.ProfileHandle
,sp.[Description]
from
[SocialProfile] sp
inner join
Entity e on sp.EntityId = e.EntityId
where
e.EntityStatusId != 3 and e.EntityStatusId != 4
and sp.SocialProfileTypeId in (1, 2, 10)
and (ISNUMERIC(sp.ProfileHandle) = 1)
and IsRemoved = 0
)
update T
set ProfileHandle = NULL
where ProfileHandle = ProfileId
I tried to use cast function but it doesn't work. Can someone tell me what I'm doing wrong?
The isnumeric() doesn't give the protection you want. Use try_convert() for the comparison:
with T as (
select sp.ProfileId, sp.ProfileHandle, sp.[Description]
from [SocialProfile] sp inner join
Entity e
on sp.EntityId = e.EntityId
where e.EntityStatusId not in (3, 4) and
sp.SocialProfileTypeId in (1, 2, 10) and
ISNUMERIC(sp.ProfileHandle) = 1 and -- you can leave it in
IsRemoved = 0
)
update T
set ProfileHandle = NULL
where try_convert(int, ProfileHandle) = ProfileId;
SQL Server has a "feature" where it will rearrange the conditions in the query. The CTE does not get executed "first", so the isnumeric() is not necessarily run before the conversion in the where. I consider this a bug. Microsoft considers this a feature (because it provides more options for optimization).
The only ordering guaranteed in a query is through case expressions. The simplest work-around is try_convert().
In addition, I strongly recommend never relying on implicit conversions. Always explicitly convert. I have spent many hours debugging code for problems caused by implicit conversion.
I am learning SQL so be gentle. If I have designated a specific role in my where clause it is only pulling those cases where that role is populated. How can I also include the NULL values or those roles that are blank?
Here is the where clause now:
WHERE (dbo.vcases.lawtype = 'My Cases') AND
(dbo.vcase_parties_people.role_sk = 4001) AND
**(v1.role_sk = 3940) AND
(v1.report_ind = 'Y') AND
(v2.role_sk = 3939) AND
(v2.report_ind = 'Y')** AND
(dbo.vcases.case_type NOT IN ('Case type 1', 'Case type 2'))
The COALESCE() expression in SQL is useful for substituting a default value when NULL is encountered for a given column or expression. When the query optimizer encounters a COALESCE() call, it will internally rewrite that expression to an equivalent CASE...WHEN expression. In your sample query, WHERE (COALESCE(v1.role_sk, 3940) = 3940) would operate (and optimize) the same as WHERE (CASE WHEN v1.role_sk IS NOT NULL THEN v1.role_sk ELSE 3940 END = 3940).
Since your example specifically involves a condition in the WHERE clause, you may want to use an OR operation, which could optimize better than a COALESCE() expression: WHERE (v1.role_sk = 3940 OR v1.role_sk IS NULL).
This is also assuming that any joins in your query aren't filtering out rows whose role_sk column is NULL.
You might edit your code as follows:
WHERE (dbo.vcases.lawtype = 'My Cases') AND
(dbo.vcase_parties_people.role_sk = 4001) AND
(v1.role_sk = 3940 OR v1.role_sk IS NULL) AND
(v1.report_ind = 'Y') AND
(v2.role_sk = 3939) AND
(v2.report_ind = 'Y') AND
(dbo.vcases.case_type NOT IN ('Case type 1', 'Case type 2'))
The use of the Coalesce function has been suggested but a good rule of thumb in SQL is to avoid the use of functions in the WHERE clause because it reduces the efficiency of the table's indexes. Functions in WHERE clause often cause Index-Scans instead of the more efficient Index-Seeks.
I have a bunch of report parameters and as a result my criteria checking first checks if parameter value is null and if not compares it with a column value .
(#dateStart IS NULL OR #dateStart <= BELGE.AccDate)
AND (#dateEnd IS NULL OR #dateEnd >= BELGE.AccDate)
AND (#CompanyId IS NULL OR #CompanyId = hrktlr.CompanyId)
AND ((#onKayitlarDahil = 1 and hrktlr.StatusCode in ('M', 'O'))
OR (#onKayitlarDahil = 0 AND hrktlr.StatusCode = 'M'))
AND (#BizPartnerId IS NULL or CK.BizPartnerId = #BizPartnerId)
AND (#BizPartnerKodStart is null or #BizPartnerKodStart = '' or #BizPartnerKodStart <= CK.BizPartnerKod)
AND (#BizPartnerKodEnd is null or #BizPartnerKodEnd = '' or #BizPartnerKodEnd >= CK.BizPartnerKod)
AND (#BizPartnerType is null or #BizPartnerType=CK.BizPartnerType)
this is great for a maintainable sql query, but the problem is that Sql Query Optimizer prepares itself for the worst case I guess, and index usage is bad. For example when I pass in BizPartnerId and thus avoid BizPartnerId is null check, query runs a 100 times faster.
So if I keep going with this approach are there any pointers that you can recommend for Query Planner to help increase query performance.
Any viable alternatives to optional parameter checking?
To stop sql server form saving a sub optimal query plan you can use the option WITH RECOMPILE. The query plan will be recalculated each time you run the query.
The following SQL should demonstrate what I am trying to do. Unfortunately, when I try to run it, I get an error saying "Null or empty full-text predicate".
DECLARE
#essaySearchTerm varchar(100),
#game_id varchar(100)
SET #essaySearchTerm = NULL;
SET #game_id = 2;
SELECT * FROM tblImageInfo i
WHERE i.game_id = #game_id
AND (#essaySearchTerm IS NULL OR CONTAINS(i.essay, #essaySearchTerm))
What does this error mean?
How can I fix this?
This is because SQL Server operators are not guaranteed to be short-circuited, so if you have an expression 'A OR B', sometimes both A and B will be evaluated regardless of logical value of A. See this post or this one for more details.
In your case, when #essaySearchTerm is null, it tries to evaluate CONTAINS(i.essay, NULL), which is illegal. After changing your code to:
IF ISNULL(#essaySearchTerm, '') = ''
SET #essaySearchTerm = '""';
SELECT * FROM tblImageInfo i
WHERE i.game_id = #game_id
AND (#essaySearchTerm = '""' OR CONTAINS(i.essay, #essaySearchTerm))
the right side of the OR will be a legal expression, as CONTAINS(x, '""') returns an empty set.
Set the value of #essaySearchTerm to "" (two double quotes) if its null or empty:
if #essaySearchTerm is null
set #essaySearchTerm='""';
In MySQL, is there a way to set the "total" fields to zero if they are NULL?
Here is what I have:
SELECT uo.order_id, uo.order_total, uo.order_status,
(SELECT SUM(uop.price * uop.qty)
FROM uc_order_products uop
WHERE uo.order_id = uop.order_id
) AS products_subtotal,
(SELECT SUM(upr.amount)
FROM uc_payment_receipts upr
WHERE uo.order_id = upr.order_id
) AS payment_received,
(SELECT SUM(uoli.amount)
FROM uc_order_line_items uoli
WHERE uo.order_id = uoli.order_id
) AS line_item_subtotal
FROM uc_orders uo
WHERE uo.order_status NOT IN ("future", "canceled")
AND uo.uid = 4172;
The data comes out fine, except the NULL fields should be 0.
How can I return 0 for NULL in MySQL?
Use IFNULL:
IFNULL(expr1, 0)
From the documentation:
If expr1 is not NULL, IFNULL() returns expr1; otherwise it returns expr2. IFNULL() returns a numeric or string value, depending on the context in which it is used.
You can use coalesce(column_name,0) instead of just column_name. The coalesce function returns the first non-NULL value in the list.
I should mention that per-row functions like this are usually problematic for scalability. If you think your database may get to be a decent size, it's often better to use extra columns and triggers to move the cost from the select to the insert/update.
This amortises the cost assuming your database is read more often than written (and most of them are).
None of the above answers were complete for me.
If your field is named field, so the selector should be the following one:
IFNULL(`field`,0) AS field
For example in a SELECT query:
SELECT IFNULL(`field`,0) AS field, `otherfield` FROM `mytable`
Hope this can help someone to not waste time.
You can try something like this
IFNULL(NULLIF(X, '' ), 0)
Attribute X is assumed to be empty if it is an empty String, so after that you can declare as a zero instead of last value. In another case, it would remain its original value.
Anyway, just to give another way to do that.
Yes IFNULL function will be working to achieve your desired result.
SELECT uo.order_id, uo.order_total, uo.order_status,
(SELECT IFNULL(SUM(uop.price * uop.qty),0)
FROM uc_order_products uop
WHERE uo.order_id = uop.order_id
) AS products_subtotal,
(SELECT IFNULL(SUM(upr.amount),0)
FROM uc_payment_receipts upr
WHERE uo.order_id = upr.order_id
) AS payment_received,
(SELECT IFNULL(SUM(uoli.amount),0)
FROM uc_order_line_items uoli
WHERE uo.order_id = uoli.order_id
) AS line_item_subtotal
FROM uc_orders uo
WHERE uo.order_status NOT IN ("future", "canceled")
AND uo.uid = 4172;