IF EXISTS in WHERE clause - sql

I ran into a problem this days. In my MS SQL Database I have a table of articles (details of them like insert date, insert user and other staffs) and one table with the body of articles in multiple languages. I want that the articles body to be in user preferred language. But not all the articles are in all languages. So, would be nice, first to search for the article in user's preferred language and, if not exists, to get the body in first language it is.
For this, I use a function. The WHERE clause is like this:
WHERE [tblBody].[Language] = #Language AND
[tblTitle].[Language] = #Language
but would be nice if it would be like this:
SELECT (if body is in my preferred language get that body; else give me one (maybe I understood it))
I hope you understood what I want to do and witch are my problems.
Thank you in advance!
UPDATE
This is my actual query which needs modifications:
ALTER FUNCTION [fx_GetNews]
(
#MinimumPermission INT,
#UserID UNIQUEIDENTIFIER,
#OwnerID UNIQUEIDENTIFIER = NULL,
#Title NVARCHAR(250) = NULL,
#Body VARCHAR(150) = NULL,
#InsertDateStart DATETIME = NULL,
#InsertDateEnd DATETIME = NULL,
#CreatedByUserID UNIQUEIDENTIFIER = NULL,
#ExpirationDate DATETIME = NULL,
#Language VARCHAR(150)
)
RETURNS #News TABLE
(
[Id] UNIQUEIDENTIFIER
,[OwnerID] UNIQUEIDENTIFIER
,[Title] NVARCHAR(250)
,[TitlePictureUrl] VARCHAR(150)
,[Body] NVARCHAR(max)
,[Visible] BIT
,[InsertDate] DATETIME
,[CreatedByUserId] UNIQUEIDENTIFIER
,[ModifiedDate] DATETIME
,[ModifiedByUserId] UNIQUEIDENTIFIER
,[ModifiedByPerson] VARCHAR(250)
,[ExpirationDate] DATETIME
,[CreatedByPerson] VARCHAR(250)
,[Permission] INT
)
AS
BEGIN
INSERT INTO #News
([Id]
,[OwnerID]
,[Title]
,[TitlePictureUrl]
,[Body]
,[Visible]
,[InsertDate]
,[CreatedByUserId]
,[ModifiedDate]
,[ModifiedByUserId]
,[CreatedByPerson]
,[ModifiedByPerson]
,[ExpirationDate]
,[Permission])
SELECT
[dbo].[tblNews].[Id]
,[dbo].[tblNews].[OwnerID]
,CAST([tblTitle].[Text] AS VARCHAR(150))
,[dbo].[tblNews].[TitlePictureUrl]
,[tblBody].[Text]
,[dbo].[tblNews].[Visible]
,[dbo].[tblNews].[InsertDate]
,[dbo].[tblNews].[CreatedByUserId]
,[dbo].[tblNews].[ModifiedDate]
,[dbo].[tblNews].[ModifiedByUserId]
,[dbo].[tblNews].[CreatedByPerson]
,[dbo].[tblNews].[ModifiedByPerson]
,[dbo].[tblNews].[ExpirationDate]
,[eportofolii].[fx_GetPermissionForObject] (#UserID, [dbo].[tblNews].[ID], 1, 1)
FROM [dbo].[tblNews]
INNER JOIN [dbo].[tblAdmTranslateText] AS [tblBody] ON
[tblBody].[OwnerID] = [dbo].[tblNews].[ID]
INNER JOIN [dbo].[tblAdmTranslateText] AS [tblTitle] ON
[tblTitle].[OwnerID] = [dbo].[tblNews].[ID]
WHERE
[eportofolii].[fx_GetPermissionForObject] (#UserID, [dbo].[tblNews].[ID], 1, 1) >= #MinimumPermission AND
([dbo].[tblNews].[OwnerID] = ISNULL(#OwnerID, [dbo].[tblNews].[OwnerID]) OR [dbo].[tblNews].[OwnerID] IS NULL) AND
[tblTitle].[Text] LIKE '%' + ISNULL(#Title, '') + '%' AND
[tblBody].[Text] LIKE '%' + ISNULL(#Body, '') + '%' AND
([dbo].[tblNews].[InsertDate] BETWEEN ISNULL(#InsertDateStart, ([dbo].[tblNews].[InsertDate] - 7)) AND ISNULL(#InsertDateEnd, [dbo].[tblNews].[InsertDate] + 1)) AND
[dbo].[tblNews].[CreatedByUserID] = ISNULL(#CreatedByUserID, [dbo].[tblNews].[CreatedByUserID]) AND
([dbo].[tblNews].[ExpirationDate] > ISNULL(#ExpirationDate, GETDATE() - 1) OR [dbo].[tblNews].[ExpirationDate] IS NULL) AND
[tblBody].[UDF_2] = 'Body' AND
[tblTitle].[UDF_2] = 'Title' AND
[tblBody].[UDF_1] = 'News' AND
[tblTitle].[UDF_1] = 'News' AND
[tblBody].[Language] = #Language AND
[tblTitle].[Language] = #Language
ORDER BY [InsertDate] DESC
RETURN
END
The problem is here at the end:
[tblBody].[Language] = #Language AND
[tblTitle].[Language] = #Language
Thank you!

;WITH x AS
(
SELECT b.[Language], /* other columns from b */
rn = ROW_NUMBER() OVER (PARTITION BY b.article_id_of_some_kind
ORDER BY CASE WHEN t.[Language] = #Language THEN 1 ELSE 2 END)
FROM dbo.tblBody AS b
INNER JOIN dbo.tblLanguage AS l
ON b.LanguageID = l.LanguageID -- guessing here
)
SELECT /* cols */ FROM x WHERE rn = 1;
This will return an arbitrary language if the preferred language is not available. You can further refine that by modifying the inner ORDER BY.

What you want to do is a bit complicated. You want to return articles in the desired language, if present, and in another language if not.
For this, a window function is quite useful:
select t.*
from (select blah,
max(case when [tblBody].[Language] = #Language then 1 else 0 end) over (partition by article) as HasLanguage
from whatever
where [tblTitle].[Language] = #Language
) t
where HasLanguage = 1 or [tblBody].[Language] <> #Language

select B.*
from bodies as B
where exists (
select 1 from (
select b.article, coalesce( u.language, b.language ) as language
from bodies as b
left join users as u
on b.language = u.language
and u.user = #user
where b.article = #article
and b.default_language = 'TRUE' -- or whatever
) as A
on A.article = B.Article
and A.language = B.language
)
You have to choose between two languages: the user's language and the article's language. you could use UNION or coalesce (because an outer join is a kind of a union), and the latter is simpler.

One option is to use the MSSQL "COALESCE" function:
http://msdn.microsoft.com/en-us/library/ms190349.aspx
Another might be to use a CASE block.

Related

Using multiple nested OR statements in WHERE clause makes query return incorrect results

I have a WHERE clause that has a nested OR statement, as seen here:
-- Declaration of variables
DECLARE
#PageSize INT,
#PageNumber INT,
#SearchPhraseOne VARCHAR(20),
#SearchPhraseTwo VARCHAR(20),
#FilterCategory VARCHAR(30),
#FilterStatus TINYINT,
#NeedsFollowUp TINYINT,
#NeedsTraining TINYINT,
#NeedsInitialVacc TINYINT;
SET #PageNumber = 1;
SET #PageSize = 100;
SET #SearchPhraseOne = null;
SET #SearchPhraseTwo = null;
SET #FilterCategory = 'High Exposure';
SET #FilterStatus = null;
SET #NeedsFollowUp = 1;
SET #NeedsTraining = null;
SET #NeedsInitialVacc = null;
select * from(
select
vel.fullName,
vel.EecEmpNo,
vel.EecLocation,
vel.EecDateOfLastHire,
job.JbcDesc,
vei.eiInitialBBPDate,
vei.eiVCGivenDate,
iif(jv.verTypeName is null, 'Low Risk', jv.verTypeName) as vaccineCategory,
vel.eecEmplStatus,
count(distinct vh.vhID) as vaccCount,
max(isnull(vh.vhNextDateScheduled, null)) as maxNextDateScheduled,
max(cast(vh.vhSeriesComplete as int)) as seriesComplete,
iif(vel.eecEmplStatus = 'T', null,
coalesce(iif(max(cast(vh.vhSeriesComplete as int)) = 1, null, max(isnull(vh.vhNextDateScheduled, null))), -- check if the vaccine items have a SeriesComplete of 1, otherwise use NextDateScheduled
iif(vei.eiInitialBBPDate is not null, null, vel.EecDateOfLastHire), -- check if the InitialBBPDate is not null, if it isn't use DateOfLastHire
iif(vei.eiVCGivenDate is not null, null, vel.EecDateOfLastHire), null)) as actionDate -- check if the OrientationDate is not null, if it isn't use DateOfLastHire
-- if all three of these values are null then there's no ActionDate
-- Terminated employees will not have an action date assigned even if there's a match
from dbo.vaccEmpList vel
left join dbo.vaccEmployeeInfo vei on vei.eiEmployeeNo = vel.EecEmpNo
left join dbo.vaccVaccinationHistory vh on vh.vhEmployeeNo = vel.EecEmpNo
left join dbo.vaccVaccineTypeLookup vt on vh.vhVaccinationTypeID = vt.vtlVaccineTypeID and vt.vtIsActive = 1 -- Only get active vaccination types
join dbo.U_JobCode job on vel.EecJobCode = job.JbcJobCode
left join dbo.JobVerficationXref jv on vel.EecJobCode = jv.JobCode and jv.verName = 'Vaccination Category'
group by vel.fullName, vel.EecEmpNo, job.JbcDesc, jv.verTypeName, vel.EecLocation, vel.eecEmplStatus, vei.eiInitialBBPDate, vei.eiVCGivenDate, vel.EecDateOfLastHire
) as searchResults
where (
(
#SearchPhraseOne is null
or searchResults.fullName like #SearchPhraseOne + '%'
or searchResults.EecEmpNo = #SearchPhraseOne
)
and (
#SearchPhraseTwo is null
or searchResults.fullName like #SearchPhraseTwo + '%'
or searchResults.EecEmpNo = #SearchPhraseTwo
) -- Employee Name/ID
and (
#FilterStatus is null
or (searchResults.eecEmplStatus = 'A' or searchResults.eecEmplStatus = 'L')
) -- Employee Status
and (
#FilterCategory is null
or searchResults.vaccineCategory = #FilterCategory
) -- Employee Vaccination Category
and ( -- ISSUES OCCUR HERE
(#NeedsTraining is null
or (searchResults.actionDate is not null
and (searchResults.eiInitialBBPDate is null or searchResults.eiVCGivenDate is null))
) -- Needs Training if either of these two date values are null
or (#NeedsInitialVacc is null
or (searchResults.actionDate is not null
and (searchResults.vaccCount = 0))
-- Needs Initial Vaccination if there are no vaccine records
)
or (#NeedsFollowUp is null
or (searchResults.actionDate is not null
and ((searchResults.seriesComplete is null or searchResults.seriesComplete = 0) and searchResults.maxNextDateScheduled is not null))
-- Needs a follow-up date if no series complete was detected
)
)
)
The #NeedsFollowUp, #NeedsInitialVacc, and #NeedsTraining variables are all set by the variables above. When one or more of these are set to "1", the query should return employee entries that match the criteria inside their related statements. For example, if the "NeedsFollowUp" and "NeedsTraining" values are set to "1" then the query should return employees that need a follow-up or employees that need training.
Right now, when I set all three to "1" I receive the combined results I'm looking for, but if any of them are set to null, then the query doesn't return the correct results.
EDIT: Here's a reproducible example of what I'm seeing.
I think the way the clauses are set up is causing an issue, but I'm not really sure how to fix this. How can I get the OR statements to work in the way I described above?
I was able to make the OR clauses work correcting by switching from is null to is not null in my where clauses. Using the minimal example, it would look like this:
select * from AGENTS
where (
(#NeedsName is not null and AGENTS.AGENT_NAME is null)
or
(#NeedsCountry is not null and AGENTS.COUNTRY is null)
or
(#NeedsCountry is null and #NeedsName is null)
)
Be sure to include an additional clause for when all options are NULL, so that you can return the appropriate number of rows.
Here's a working version.

SQL WHERE ... IN clause with possibly null parameter

I am having some problems with my WHERE clause (using SQL 2008) . I have to create a stored procedure that returns a list of results based on 7 parameters, some of which may be null. The ones which are problematic are #elements, #categories and #edu_id. They can be a list of ids, or they can be null. You can see in my where clause that my particular code works if the parameters are not null. I'm not sure how to code the sql if they are null. The fields are INT in the database.
I hope my question is clear enough. Here is my query below.
BEGIN
DECLARE #elements nvarchar(30)
DECLARE #jobtype_id INT
DECLARE #edu_id nvarchar(30)
DECLARE #categories nvarchar(30)
DECLARE #full_part bit
DECLARE #in_demand bit
DECLARE #lang char(2)
SET #jobtype_id = null
SET #lang = 'en'
SET #full_part = null -- full = 1, part = 0
SET #elements = '1,2,3'
SET #categories = '1,2,3'
SET #edu_id = '3,4,5'
select
jobs.name_en,
parttime.fulltime_only,
jc.cat_id category,
je.element_id elem,
jt.name_en jobtype,
jobs.edu_id minEdu,
education.name_en edu
from jobs
left join job_categories jc
on (jobs.job_id = jc.job_id)
left join job_elements je
on (jobs.job_id = je.job_id)
left join job_type jt
on (jobs.jobtype_id = jt.jobtype_id)
left join education
on (jobs.edu_id = education.edu_id)
left join
(select job_id, case when (jobs.parttime_en IS NULL OR jobs.parttime_en = '') then 1 else 0 end fulltime_only from jobs) as parttime
on jobs.job_id = parttime.job_id
where [disabled] = 0
and jobs.jobtype_id = isnull(#jobtype_id,jobs.jobtype_id)
and fulltime_only = isnull(#full_part,fulltime_only)
-- each of the following clauses should be validated to see if the parameter is null
-- if it is, the clause should not be used, or the SELECT * FROM ListToInt... should be replaced by
-- the field evaluated: ie if #elements is null, je.element_id in (je.element_id)
and je.element_id IN (SELECT * FROM ListToInt(#elements,','))
and jc.cat_id IN (SELECT * FROM ListToInt(#categories,','))
and education.edu_id IN (SELECT * FROM ListToInt(#edu_id,','))
order by case when #lang='fr' then jobs.name_fr else jobs.name_en end;
END
Something like
and (#elements IS NULL OR je.element_id IN
(SELECT * FROM ListToInt(#elements,',')))
and (#categories IS NULL OR
jc.cat_id IN (SELECT * FROM ListToInt(#categories,',')))
....
should do the trick
je.element_id IN (SELECT * FROM ListToInt(#elements,',')) OR #elements IS NULL
that way for each one
Have you tried explicitly comparing to NULL?
and (#elements is null or je.element_id IN (SELECT * FROM ListToInt(#elements,','))
And so on.

Stored procedure optimization

i have a stored procedure which takes lot of time to execure .Can any one suggest a better approch so that the same result set is achived.
ALTER PROCEDURE [dbo].[spFavoriteRecipesGET]
#USERID INT, #PAGENUMBER INT, #PAGESIZE INT, #SORTDIRECTION VARCHAR(4), #SORTORDER VARCHAR(4),#FILTERBY INT
AS
BEGIN
DECLARE
#ROW_START INT
DECLARE
#ROW_END INT
SET
#ROW_START = (#PageNumber-1)* #PageSize+1
SET
#ROW_END = #PageNumber*#PageSize
DECLARE
#RecipeCount INT
DECLARE
#RESULT_SET_TABLE
TABLE
(
Id INT NOT NULL IDENTITY(1,1),
FavoriteRecipeId INT,
RecipeId INT,
DateAdded DATETIME,
Title NVARCHAR(255),
UrlFriendlyTitle NVARCHAR(250),
[Description] NVARCHAR(MAX),
AverageRatingId FLOAT,
SubmittedById INT,
SubmittedBy VARCHAR(250),
RecipeStateId INT,
RecipeRatingId INT,
ReviewCount INT,
TweaksCount INT,
PhotoCount INT,
ImageName NVARCHAR(50)
)
INSERT INTO #RESULT_SET_TABLE
SELECT
FavoriteRecipes.FavoriteRecipeId,
Recipes.RecipeId,
FavoriteRecipes.DateAdded,
Recipes.Title,
Recipes.UrlFriendlyTitle,
Recipes.[Description],
Recipes.AverageRatingId,
Recipes.SubmittedById,
COALESCE(users.DisplayName,users.UserName,Recipes.SubmittedBy) As SubmittedBy,
Recipes.RecipeStateId,
RecipeReviews.RecipeRatingId,
COUNT(RecipeReviews.Review),
COUNT(RecipeTweaks.Tweak),
COUNT(Photos.PhotoId),
dbo.udfGetRecipePhoto(Recipes.RecipeId) AS ImageName
FROM
FavoriteRecipes
INNER JOIN Recipes ON FavoriteRecipes.RecipeId=Recipes.RecipeId AND Recipes.RecipeStateId <> 3
LEFT OUTER JOIN RecipeReviews ON RecipeReviews.RecipeId=Recipes.RecipeId AND RecipeReviews.ReviewedById=#UserId
AND RecipeReviews.RecipeRatingId= (
SELECT MAX(RecipeReviews.RecipeRatingId)
FROM RecipeReviews
WHERE RecipeReviews.ReviewedById=#UserId
AND RecipeReviews.RecipeId=FavoriteRecipes.RecipeId
)
OR RecipeReviews.RecipeRatingId IS NULL
LEFT OUTER JOIN RecipeTweaks ON RecipeTweaks.RecipeId = Recipes.RecipeId AND RecipeTweaks.TweakedById= #UserId
LEFT OUTER JOIN Photos ON Photos.RecipeId = Recipes.RecipeId
AND Photos.UploadedById = #UserId AND Photos.RecipeId = FavoriteRecipes.RecipeId
AND Photos.PhotoTypeId = 1
LEFT OUTER JOIN users ON Recipes.SubmittedById = users.UserId
WHERE
FavoriteRecipes.UserId=#UserId
GROUP BY
FavoriteRecipes.FavoriteRecipeId,
Recipes.RecipeId,
FavoriteRecipes.DateAdded,
Recipes.Title,
Recipes.UrlFriendlyTitle,
Recipes.[Description],
Recipes.AverageRatingId,
Recipes.SubmittedById,
Recipes.SubmittedBy,
Recipes.RecipeStateId,
RecipeReviews.RecipeRatingId,
users.DisplayName,
users.UserName,
Recipes.SubmittedBy;
WITH SortResults
AS
(
SELECT
ROW_NUMBER() OVER (
ORDER BY CASE WHEN #SORTDIRECTION = 't' AND #SORTORDER='a' THEN TITLE END ASC,
CASE WHEN #SORTDIRECTION = 't' AND #SORTORDER='d' THEN TITLE END DESC,
CASE WHEN #SORTDIRECTION = 'r' AND #SORTORDER='a' THEN AverageRatingId END ASC,
CASE WHEN #SORTDIRECTION = 'r' AND #SORTORDER='d' THEN AverageRatingId END DESC,
CASE WHEN #SORTDIRECTION = 'mr' AND #SORTORDER='a' THEN RecipeRatingId END ASC,
CASE WHEN #SORTDIRECTION = 'mr' AND #SORTORDER='d' THEN RecipeRatingId END DESC,
CASE WHEN #SORTDIRECTION = 'd' AND #SORTORDER='a' THEN DateAdded END ASC,
CASE WHEN #SORTDIRECTION = 'd' AND #SORTORDER='d' THEN DateAdded END DESC
) RowNumber,
FavoriteRecipeId,
RecipeId,
DateAdded,
Title,
UrlFriendlyTitle,
[Description],
AverageRatingId,
SubmittedById,
SubmittedBy,
RecipeStateId,
RecipeRatingId,
ReviewCount,
TweaksCount,
PhotoCount,
ImageName
FROM
#RESULT_SET_TABLE
WHERE
((#FILTERBY = 1 AND SubmittedById= #USERID)
OR ( #FILTERBY = 2 AND (SubmittedById <> #USERID OR SubmittedById IS NULL))
OR ( #FILTERBY <> 1 AND #FILTERBY <> 2))
)
SELECT
RowNumber,
FavoriteRecipeId,
RecipeId,
DateAdded,
Title,
UrlFriendlyTitle,
[Description],
AverageRatingId,
SubmittedById,
SubmittedBy,
RecipeStateId,
RecipeRatingId,
ReviewCount,
TweaksCount,
PhotoCount,
ImageName
FROM
SortResults
WHERE
RowNumber BETWEEN #ROW_START AND #ROW_END
print #ROW_START
print #ROW_END
SELECT
#RecipeCount=dbo.udfGetFavRecipesCount(#UserId)
SELECT
#RecipeCount AS RecipeCount
SELECT COUNT(Id) as FilterCount FROM #RESULT_SET_TABLE
WHERE
((#FILTERBY = 1 AND SubmittedById= #USERID)
OR (#FILTERBY = 2 AND (SubmittedById <> #USERID OR SubmittedById IS NULL))
OR (#FILTERBY <> 1 AND #FILTERBY <> 2))
END
You need to look at the execution plan to see where the time is going. It could be indexes, table-scans caused by your UDF, any number of things. As you anayze the plan, try to break up the query into smaller pieces to see if you can make a difference in them.
Then learn about ROW_NUMBER to see if you can do without the local table.
Couple notes
Indexing - often times when people create procedures which use temp table or table variable they fail to realize you can create indexes on those objects and this can have massive performance implications.
UDF - Sometimes the query processor will effectively inline UDF logic and sometimes not, look closely at your query plan an see how this is being handled. Often times if you manually inline this logic in something like a correlated sub-query you can boost performance a lot.
As others have said, the only way to know is to look at explain plans. Glancing over the code, this part looks kind of fishy:
AND RecipeReviews.RecipeRatingId= (
SELECT MAX(RecipeReviews.RecipeRatingId)
FROM RecipeReviews
WHERE RecipeReviews.ReviewedById=#UserId
AND RecipeReviews.RecipeId=FavoriteRecipes.RecipeId
)
In general, doing non-trivial stuff in join conditions is a Bad Idea. I would factor that out into a sub-select, and since it's an outer join, you'd probably have to combine that with RecipeReviews somehow.
BUT: All of this is speculation! Explain! Measure!
Well in addition to the possible poor performance of the UDF, this line of code concerns me
LEFT OUTER JOIN RecipeReviews
ON RecipeReviews.RecipeId=Recipes.RecipeId
AND RecipeReviews.ReviewedById=#UserId
AND RecipeReviews.RecipeRatingId=
(SELECT MAX(RecipeReviews.RecipeRatingId)
FROM RecipeReviews
WHERE RecipeReviews.ReviewedById=#UserId
AND RecipeReviews.RecipeId=FavoriteRecipes.RecipeId )
OR RecipeReviews.RecipeRatingId IS NULL
It is generally a poor practice to use a subquery as part of a join. I would strongly supect this is not using any indexes you may have. And the OR part doesn;t make sense to mea atll all, the left join shoudl get you this.
Rewrite it to make a derived table instead.
If you have a lot of records a temp table usually performs better than a table variable and can (and probably should) be indexed.
You need to add parentheses around your OR conditions.
LEFT OUTER JOIN RecipeReviews
ON RecipeReviews.RecipeId = Recipes.RecipeId
AND RecipeReviews.ReviewedById = #UserId
AND
-- insert open parenthesis here:
(
RecipeReviews.RecipeRatingId = (... subquery ...)
OR RecipeReviews.RecipeRatingId IS NULL
-- insert close parenthesis here:
)
the very first, simple thing i would do, is move all your declare statements to the top.
DECLARE #ROW_START INT,
#ROW_END INT,
#RecipeCount INT
DECLARE
#RESULT_SET_TABLE
TABLE
(
Id INT NOT NULL IDENTITY(1,1),
)
The next part, which is still rather simple, is stuff like this:
AND Recipes.RecipeStateId <> 3
AND RecipeTweaks.TweakedById= #UserId
This can be taken out of the join and move to the where clause. if you can, change the <> to an in statement so that it can utlize an index seek.
AND RecipeReviews.RecipeRatingId=
(
SELECT MAX(RecipeReviews.RecipeRatingId)
FROM RecipeReviews
WHERE RecipeReviews.ReviewedById=#UserId
AND RecipeReviews.RecipeId=FavoriteRecipes.RecipeId
)
that's jsut crazy looking and needs to be completely redone.

how do I remove nested select statement

I have a Name table with the columns
NameID
Name
TypeID
With the following SQL
SELECT[NameID]
FROM[Name]
WHERE[TypeID] = #TypeID
AND NameID >= (SELECT MIN([NameID])
FROM [Name]
WHERE [Name]='Billy' AND [TypeID]=#TypeID)
Ive been asked to convert this to an Inner Join without using any nested select but not sure how to.
thanks for your help!
Originally I didn't think you needed a join at all,
;WITH n AS
(
SELECT
NameID,
rn = ROW_NUMBER() OVER (ORDER BY NameID)
FROM [Name]
WHERE TypeID = #TypeID
AND [Name] = 'Billy'
)
SELECT NameID
FROM n
WHERE rn > 1;
Then again, maybe I do not have the requirements clear. What is the purpose of this query?
SELECT n1.NameID
FROM [Name] AS n1
INNER JOIN
(
SELECT NameID = MIN(NameID)
FROM [Name]
WHERE TypeID = #TypeID
AND [Name] = 'Billy'
) AS n2
ON n1.NameID >= n2.NameID
WHERE n1.TypeID = #TypeID;
I agree with Lukas, I am not sure why the person who is telling you to change this thinks an inner join will be better than your original.
You could remove the nested part via: -
declare #NameID int
select #NameID = (SELECT MIN([NameID])
FROM [Name] WHERE [Name]='Billy' AND
[TypeID]=#TypeID)
SELECT [NameID] FROM [Name] WHERE [TypeID]
= #TypeID AND NameID >= #NameID
But as stated already, this does not provide any performance benefit as the subquery would only be evaluated once in your version, the same as in this.
Well, it looks like just moving the condition [Name]='Billy' should produce the same result for this specific query. So convert your original:
SELECT[NameID]
FROM[Name]
WHERE[TypeID] = #TypeID
AND NameID >= (SELECT MIN([NameID])
FROM [Name]
WHERE [Name]='Billy' AND [TypeID]=#TypeID)
to:
SELECT[NameID]
FROM[Name]
WHERE[TypeID] = #TypeID
AND[Name]='Billy'

COALESCE... Can anybody help me to optimize this code?

Can anybody help me to optimize this code? At present it takes 17 seconds.
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
--SpResumeSearch NULL,null,null,null,null,null,null,null,null,null,null,NULL,null,null,null,null,null,null,null,0,10,NULL
ALTER PROCEDURE [dbo].[SpResumeSearch]
#Keyword varchar(50) = NULL,
#JobCategoryId int = NULL,
#NationalityId int = NULL,
#CountryId int = NULL,
#LocationId int = NULL,
#Email nvarchar(50) = NULL,
#Gender int = NULL,
#PassportNumber nvarchar(20) = NULL,
#VisaStatus int = NULL,
#PoBox nvarchar(10) = NULL,
#CareerLevelId int = NULL,
#KeySkills nvarchar(50) = NULL,
#ExpectedSalary int = NULL,
#Experience int = NULL,
#DOB varchar(20) = NULL,
#AppliedFrom datetime = NULL,
#AppliedTo datetime = NULL,
#MaritalStatusId int = NULL,
#LanguageId int = NULL,
#PageIndex int,
#NumRows int,
#SortCol varchar(20) = NULL
AS
BEGIN
DECLARE #startRowIndex INT;
SET #startRowIndex = (#PageIndex * #NumRows) + 1;
WITH ResumeListTemp AS
(SELECT DISTINCT M.MemberID, R.ResumeID, R.CreatedDate, R.ModifiedDate, R.CompletedDate, RP.FirstName, RP.LastName, G.Title AS Gender,
RP.DateOfBirth, C.NationalityTitle AS Nationality, RPD.KeySkills, RPD.ExperienceYear AS Experience, V.Title AS VisaStatus, RC.Phone, RC.Mobile,
ROW_NUMBER() OVER (ORDER BY
CASE WHEN #SortCol='FIRSTNAME' THEN FirstName END,
CASE WHEN #SortCol='LASTNAME' THEN RP.LastName END,
CASE WHEN #SortCol='GENDER' THEN G.Title END,
CASE WHEN #SortCol='DOB' THEN RP.DateOfBirth END,
CASE WHEN #SortCol='NATIONALITY' THEN C.NationalityTitle END,
CASE WHEN #SortCol='KEYSKILLS' THEN RPD.KeySkills END,
CASE WHEN #SortCol='EXPERIENCE' THEN RPD.ExperienceYear END,
CASE WHEN #SortCol='VISASTATUS' THEN V.Title END,
CASE WHEN #SortCol='CONTACTNO' THEN RC.Mobile END,
CASE WHEN #SortCol='UPDATEDATE' THEN R.ModifiedDate END,
CASE WHEN #SortCol IS NULL THEN R.CompletedDate END
) AS RowNum
FROM TblResume AS R
LEFT OUTER JOIN TblResumeContactInfo AS RC ON RC.FKResumeID = R.ResumeID
LEFT OUTER JOIN TblResumePersonalDetail AS RP ON RP.FKResumeID = R.ResumeID
LEFT OUTER JOIN TblResumeJobCategory AS RJC ON RJC.FKResumeID = R.ResumeID
LEFT OUTER JOIN TblResumeProfessionalDetail AS RPD ON RPD.FKResumeID = R.ResumeID
LEFT OUTER JOIN TblResumeWorkExperience AS RE ON RE.FKResumeID = R.ResumeID
LEFT OUTER JOIN TblResumeEducation AS RQ ON RQ.FKResumeID = R.ResumeID
LEFT OUTER JOIN TblResumeSkill AS RS ON RS.FKResumeID = R.ResumeID
LEFT OUTER JOIN TblMember AS M ON M.MemberID = R.FKMemberID
LEFT OUTER JOIN TblMasterGender AS G ON G.GenderID = RP.FKGenderID
LEFT OUTER JOIN TblMasterCountry AS C ON C.CountryID = RP.FKNationalityID
LEFT OUTER JOIN TblRisVisaStatus AS V ON V.VisaStatusID = RP.FKVisaStatusID
LEFT OUTER JOIN TblResumeLanguage AS L ON L.FKResumeID = R.ResumeID
WHERE (
-- RC.Address LIKE '%'+COALESCE(#Keyword,RC.Address)+'%' OR
-- RC.City LIKE '%'+COALESCE(#Keyword,RC.City)+'%' OR
R.ResumeID IN ( SELECT _RQ.FKResumeID FROM TblResumeEducation AS _RQ, TblResume AS _R WHERE _RQ.Specialization LIKE '%'+COALESCE(#Keyword, _RQ.Specialization)+'%' AND _RQ.FKResumeID=_R.ResumeID GROUP BY _RQ.FKResumeID) OR
R.ResumeID IN ( SELECT _RQ.FKResumeID FROM TblResumeEducation AS _RQ, TblResume AS _R WHERE _RQ.Institution LIKE '%'+COALESCE(#Keyword, _RQ.Institution)+'%' AND _RQ.FKResumeID=_R.ResumeID GROUP BY _RQ.FKResumeID) OR
RP.FirstName LIKE '%'+COALESCE(#Keyword,RP.FirstName)+'%' OR
RP.LastName LIKE '%'+COALESCE(#Keyword,RP.LastName)+'%' OR
--RP.PassportNumber LIKE '%'+COALESCE(#Keyword,RP.PassportNumber)+'%' OR
--(#Keyword IS NULL OR RP.PassportNumber LIKE '%' + #Keyword +'%') OR
RPD.Summary LIKE '%'+COALESCE(#Keyword,RPD.Summary)+'%' OR
-- R.ResumeID IN ( SELECT _RS.FKResumeID FROM TblResumeSkill AS _RS, TblResume AS _R WHERE _RS.Title LIKE '%'+COALESCE(#Keyword,_RS.Title)+'%' AND _RS.FKResumeID=_R.ResumeID GROUP BY _RS.FKResumeID) OR
-- R.ResumeID IN ( SELECT _RE.FKResumeID FROM TblResumeWorkExperience AS _RE, TblResume AS _R WHERE _RE.Employer LIKE '%'+COALESCE(#Keyword, _RE.Employer)+'%' AND _RE.FKResumeID=_R.ResumeID GROUP BY _RE.FKResumeID) OR
R.ResumeID IN ( SELECT _RE.FKResumeID FROM TblResumeWorkExperience AS _RE, TblResume AS _R WHERE _RE.Designation LIKE '%'+COALESCE(#Keyword, _RE.Designation)+'%' AND _RE.FKResumeID=_R.ResumeID GROUP BY _RE.FKResumeID) OR
R.ResumeID IN ( SELECT _RE.FKResumeID FROM TblResumeWorkExperience AS _RE, TblResume AS _R WHERE _RE.Responsibilities LIKE '%'+COALESCE(#Keyword, _RE.Responsibilities)+'%' AND _RE.FKResumeID=_R.ResumeID GROUP BY _RE.FKResumeID)) AND
R.ResumeID IN ( SELECT _RJC.FKResumeID FROM TblResumeJobCategory AS _RJC, TblResume AS _R WHERE _RJC.FKJobCategoryID = COALESCE(#JobCategoryId, _RJC.FKJobCategoryID) AND _RJC.FKResumeID=_R.ResumeID GROUP BY _RJC.FKResumeID ) AND
RP.FKNationalityID = COALESCE(#NationalityId, RP.FKNationalityID) AND
RC.FKCountryID = COALESCE(#CountryId, RC.FKCountryID) AND
-- RPD.FKJobLocationID = COALESCE(#LocationId, RPD.FKJobLocationID) AND
-- M.Email LIKE '%'+COALESCE(#Email, M.Email)+'%' AND
-- RP.FKGenderID = COALESCE(#Gender, RP.FKGenderID) AND
-- RP.PassportNumber LIKE '%'+COALESCE(#PassportNumber, RP.PassportNumber)+'%' AND
-- RP.FKVisaStatusID = COALESCE(#VisaStatus, RP.FKVisaStatusID) AND
-- COALESCE(RC.ZipCode,'0') LIKE '%'+COALESCE(#PoBox, COALESCE(RC.ZipCode,'0'))+'%' AND
RPD.FKExperienceLevelID = COALESCE(#CareerLevelId, RPD.FKExperienceLevelID) AND
-- RPD.KeySkills LIKE '%'+COALESCE(#KeySkills, RPD.KeySkills)+'%' AND
RPD.FKSalaryID = COALESCE(#ExpectedSalary, RPD.FKSalaryID) AND
RPD.ExperienceYear = COALESCE(#Experience, RPD.ExperienceYear) AND
RP.DateOfBirth = COALESCE(#DOB, RP.DateOfBirth) AND
R.CompletedDate = COALESCE(#AppliedFrom, R.CompletedDate) AND
R.CompletedDate = COALESCE(#AppliedTo, R.CompletedDate) AND
RP.FKMaritalStatusID = COALESCE(#MaritalStatusId, RP.FKMaritalStatusID) AND
R.ResumeID IN ( SELECT _L.FKResumeID FROM TblResumeLanguage AS _L, TblResume AS _R WHERE _L.FKLanguageID = COALESCE(#LanguageId, _L.FKLanguageID) AND _L.FKResumeID=_R.ResumeID GROUP BY _L.FKResumeID ) AND
R.IsCompleted = 1
)
SELECT ResumeListTemp.*, (SELECT COUNT(*) from ResumeListTemp) AS RecCount
FROM ResumeListTemp
WHERE RowNum BETWEEN #startRowIndex AND #StartRowIndex + #NumRows - 1
ORDER BY
CASE WHEN #SortCol='FIRSTNAME' THEN FirstName END,
CASE WHEN #SortCol='LASTNAME' THEN LastName END,
CASE WHEN #SortCol='GENDER' THEN Gender END,
CASE WHEN #SortCol='DOB' THEN DateOfBirth END,
CASE WHEN #SortCol='NATIONALITY' THEN Nationality END,
CASE WHEN #SortCol='KEYSKILLS' THEN KeySkills END,
CASE WHEN #SortCol='EXPERIENCE' THEN Experience END,
CASE WHEN #SortCol='VISASTATUS' THEN VisaStatus END,
CASE WHEN #SortCol='CONTACTNO' THEN Mobile END,
CASE WHEN #SortCol='UPDATEDATE' THEN ModifiedDate END,
CASE WHEN #SortCol IS NULL THEN CompletedDate END
END
try to replace * with the column names
In this case, I think that building your query dynamically and using sp_executesql will give you much better performance.
There's an MSDN article explaining the basics here, and there are some more in-depth articles by Erland Sommarskog, here and here.
Having '%' at the very start of a LIKE clause can cause a table scan, try to remove them if possible.
since there is only one column to be sorted in the result, you may try to replace the ORDER BY clause (twice!) as
CASE #SortCol
WHEN 'FIRSTNAME' THEN FirstName
WHEN 'LASTNAME' THEN LastName
etc
ELSE CompletedDate
END
(you probably need to CONVERT columns to NVARCHAR)
Optimization for WHERE section - seems to me, there is no need of using IN clause, thouse tables are already joined by ResumeID, all you need is to filter them out:
WHERE (
(RQ.Specialization IS NOT NULL AND (#Keyword IS NULL OR RQ.Specialization LIKE '%'+#Keyword+'%')) OR
(RQ.Institution IS NOT NULL AND (#Keyword IS NULL OR RQ.Institution LIKE '%'+#Keyword+'%')) OR
(RP.FirstName IS NOT NULL AND (#Keyword IS NULL OR RP.FirstName LIKE '%'+#Keyword+'%')) OR
(RP.LastName IS NOT NULL AND (#Keyword IS NULL OR RP.LastName LIKE '%'+#Keyword+'%')) OR
(RPD.Summary IS NOT NULL AND (#Keyword IS NULL OR RPD.Summary LIKE '%'+#Keyword+'%')) OR
(RE.Designation IS NOT NULL AND (#Keyword IS NULL OR RE.Designation LIKE '%'+#Keyword+'%')) OR
(RE.Responsibilities IS NOT NULL AND (#Keyword IS NULL OR RE.Responsibilities LIKE '%'+#Keyword+'%'))) AND
(RJC.FKJobCategoryID IS NOT NULL AND (#JobCategoryId IS NULL OR RE.Designation LIKE '%'+#JobCategoryId+'%')) AND
RP.FKNationalityID = COALESCE(#NationalityId, RP.FKNationalityID) AND
RC.FKCountryID = COALESCE(#CountryId, RC.FKCountryID) AND
RPD.FKExperienceLevelID = COALESCE(#CareerLevelId, RPD.FKExperienceLevelID) AND
RPD.FKSalaryID = COALESCE(#ExpectedSalary, RPD.FKSalaryID) AND
RPD.ExperienceYear = COALESCE(#Experience, RPD.ExperienceYear) AND
RP.DateOfBirth = COALESCE(#DOB, RP.DateOfBirth) AND
R.CompletedDate = COALESCE(#AppliedFrom, R.CompletedDate) AND
R.CompletedDate = COALESCE(#AppliedTo, R.CompletedDate) AND
RP.FKMaritalStatusID = COALESCE(#MaritalStatusId, RP.FKMaritalStatusID) AND
(L.FKLanguageID IS NOT NULL AND (#LanguageId IS NULL OR L.FKLanguageID LIKE '%'+#LanguageId+'%')) AND
R.IsCompleted = 1
)
Why are you starting each where clause with a wildcard? you can never optimize using that technique (even if you convert to dynamic SQL) as the database is not unable to use the indexes. Require your users to to at a minimum put in the first letter of what they are searching for.
Get rid of the subselects, they are performance killers. Since you are already joining to those tables, you shouldn't need them.