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

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.

Related

CASE WHEN to assign a result set to a query

I've been trying to assign different resultsets to a query depending on an input
Depending on the company selected a set of filtered results should be displayed, here's my code.
DECLARE #COMPANY VARCHAR(10) = 'Company_name1'
SELECT
(CASE
WHEN #COMPANY = 'Company_name1'
THEN (SELECT INVENTLOCATIONID FROM INVENTDIM INV
WHERE (INV.DATAAREAID = #COMPANY)
AND (((INVENTLOCATIONID IS NOT NULL)
AND ((WMSLOCATIONID IS NOT NULL)
AND (WMSLOCATIONID <> '')))
AND (INV.INVENTLOCATIONID = 'x5'))
WHEN #COMPANY = 'Company_name2'
THEN (SELECT INVENTLOCATIONID FROM INVENTDIM INV
WHERE (INV.DATAAREAID = #COMPANY)
AND (((INVENTLOCATIONID IS NOT NULL)
AND ((WMSLOCATIONID IS NOT NULL)
AND (WMSLOCATIONID <> '')))
AND (INVENTLOCATIONID IN ('X0', 'X1', 'X2', 'X3', 'X5', 'X6', 'X8', 'P6', 'P8')))
ELSE (SELECT INVENTLOCATIONID FROM INVENTDIM INV
WHERE (DATAAREAID = #COMPANY)
AND ((INVENTLOCATIONID IS NOT NULL)
AND ((WMSLOCATIONID IS NOT NULL)
AND (WMSLOCATIONID <> '')))
END) AS WHAREWOUSE
FROM
INVENTDIM INV
ORDER BY
INV.INVENTLOCATIONID
Why am I not allowed to do this?
Here's one way you could do this using a CASE expression. Use a constant to reject entries where the company name matches the specific cases and the location is not in your list. If the company isn't one of the special cases, then accept it.
DECLARE #COMPANY VARCHAR(10) = 'Company_name1';
SELECT WHAREHOUSE = INVENTLOCATIONID
FROM dbo.INVENTDIM
WHERE DATAAREAID = #COMPANY
AND INVENTLOCATIONID IS NOT NULL
AND WMSLOCATIONID IS NOT NULL
AND WMSLOCATIONID <> ''
AND 1 =
(
CASE
WHEN DATAAREAID = 'Company_name1'
AND INVENTLOCATIONID <> 'x5'
THEN 0
WHEN DATAAREAID = 'Company_name2'
AND INVENTLOCATIONID NOT IN ('X0','X1','X2','X3','X5','X6','X8','P6','P8')
THEN 0
ELSE 1
END
)
ORDER BY INVENTLOCATIONID;
Potentially a disaster for query plan reuse, though, unless DATAAREAID is unique.
This is definitely a better approach:
Can you create a new cross reference table to allow the database to map company to the inventlocationid? – Zynon Putney II
So say you had a mapping table like:
CREATE TABLE dbo.LocationMap
(
DATAAREAID varchar(10),
INVENTLOCATIONID char(2),
PRIMARY KEY(DATAAREAID, INVENTLOCATIONID
);
INSERT dbo.LocationMap(DATAAREAID,INVENTLOCATIONID)
VALUES
('Company_Name1', 'x5'),
('Company_Name2', 'X0'), ('Company_Name2', 'X1') -- , ...
Then your query becomes:
DECLARE #COMPANY VARCHAR(10) = 'Company_name1';
SELECT WHAREHOUSE = INV.INVENTLOCATIONID
FROM dbo.INVENTDIM AS INV
LEFT OUTER JOIN dbo.LocationMap AS lm
ON INV.DATAAREAID = lm.DATAAREAID
WHERE INV.DATAAREAID = #COMPANY
AND INV.INVENTLOCATIONID IS NOT NULL
AND INV.WMSLOCATIONID IS NOT NULL
AND INV.WMSLOCATIONID <> ''
AND (lm.INVENTLOCATIONID = INV.INVENTLOCATIONID
OR lm.DATAAREAID IS NULL)
ORDER BY INV.INVENTLOCATIONID;
Written as a single query this is equivalent logic. Depending on the size of your data it's possible that the if..else branching would work better.
SELECT INVENTLOCATINID
FROM INVENTDIM INV
WHERE
INV.DATAAREAID = #COMPANY AND INVENTLOCATIONID IS NOT NULL WMSLOCATIONID <> '' AND
(
#COMPANY = 'Company name1' AND INV.INVENTLOCATIONID = 'x5' OR
#COMPANY = 'Company_name2' AND INVENTLOCATIONID IN
('X0', 'X1','X2','X3','X5','X6','X8','P6','P8') OR
#COMPANY NOT IN ('Company name1', 'Company name2')
);
Try this below query to get desired output
DECLARE #COMPANY VARCHAR(10) = 'Company_name1'
declare #tblIL as table
(
INVENTLOCATIONID int
)
IF(#COMPANY = 'Company_name1')
BEGIN
insert into #tblIL(INVENTLOCATIONID) values ('x5')
END
ELSE IF(#COMPANY = 'Company_name2')
BEGIN
insert into #tblIL(INVENTLOCATIONID) values ('X0', 'X1','X2','X3','X5','X6','X8','P6','P8')
END
ELSE
BEGIN
insert into #tblIL(INVENTLOCATIONID) values SELECT DISTINCT INVENTLOCATIONID FROM INVENTDIM
END
SELECT INVENTLOCATIONID FROM INVENTDIM INV
WHERE INV.DATAAREAID = #COMPANY AND INVENTLOCATIONID IS NOT NULL AND WMSLOCATIONID IS NOT NULL AND WMSLOCATIONID <> ''
AND INV.INVENTLOCATIONID in (select INVENTLOCATIONID from #tblIL)

how to make inner join query for getting multiple tables selected column

i just working out project school management. now i just made query for retrieve other table columns. here is my query :
ALTER PROCEDURE [dbo].[GetTeacherList]
(
#teacherid varchar(5) = null,
#classid varchar(5) = null,
#sectionid varchar(5) = null,
#subjectid varchar(5) = null
)
AS
BEGIN
SET NOCOUNT ON;
select *,right('00000' + CAST(t.[PK_PID] AS varchar(5)) ,5) as ID, c.ClassName, su.SubjectName, s.SectionName
from teachers t
inner join teacherclassassign tc on t.PK_PID = tc.TeacherID or (t.PK_PID is null and tc.TeacherID is null )
inner join classes c on c.PK_PID = tc.ClassID or (c.PK_PID is not null and tc.ClassID is null)
inner join sections s on s.PK_PID = tc.SectionID or (s.PK_PID is not null and tc.SectionID is null)
inner join subjects su on su.PK_PID = tc.SubjectID or (su.PK_PID is not null and tc.SubjectID is null)
where 1=1
and (#teacherid is null or tc.[TeacherID] = CONVERT(INT,#teacherid))
and (#classid is null or tc.[ClassID] = CONVERT(INT,#classid))
and (#sectionid is null or tc.[SectionID] = CONVERT(INT,#sectionid))
and (#subjectid is null or tc.[SubjectID] = CONVERT(INT,#subjectid))
order by t.PK_PID asc
END
how ever it's return me two rows how ever i want all rows from table teachers.
here is rows data i want :
and here is all data of teachers :
how i achieve this result..please guys help me.
You need to replace the first inner join with the LEFT Join as you want all teachers.
Below i have changed the query,See if this fixes the problem
SET NOCOUNT ON;
select *,right('00000' + CAST(t.[PK_PID] AS varchar(5)) ,5) as ID, c.ClassName, su.SubjectName, s.SectionName
from teachers t
LEFT join teacherclassassign tc on t.PK_PID = tc.TeacherID or (t.PK_PID is null and tc.TeacherID is null )
inner join classes c on c.PK_PID = tc.ClassID or (c.PK_PID is not null and tc.ClassID is null)
inner join sections s on s.PK_PID = tc.SectionID or (s.PK_PID is not null and tc.SectionID is null)
inner join subjects su on su.PK_PID = tc.SubjectID or (su.PK_PID is not null and tc.SubjectID is null)
where 1=1
and (#teacherid is null or tc.[TeacherID] = CONVERT(INT,#teacherid))
and (#classid is null or tc.[ClassID] = CONVERT(INT,#classid))
and (#sectionid is null or tc.[SectionID] = CONVERT(INT,#sectionid))
and (#subjectid is null or tc.[SubjectID] = CONVERT(INT,#subjectid))
order by t.PK_PID asc
END

IF EXISTS in WHERE clause

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.

Paging of records using sql server stored procedure

I have a stored procedure which returns result from two tables using outer join and where conditions. It has order by clause as well. I want to add paging to it so that only requested number of records are returned. How can I do it? I need to supply pagenumber, totalnumber of records, current page etc ? My stored procedure is:
CREATE PROCEDURE [dbo].[hr_SearchVacanciesForService]
#SearchText NVARCHAR(50) = NULL,
#DutyStationID INT = NULL,
#VacancyCategoryIDs VARCHAR(1000) = NULL,
#Language INT = 1
AS
SELECT *
FROM dbo.hr_Vacancies LEFT OUTER JOIN dbo.hr_DutyStations ON dbo.hr_Vacancies.DutyStationID = dbo.hr_DutyStations.DutyStationID
LEFT OUTER JOIN dbo.hr_Companies
ON dbo.hr_Vacancies.CompanyID = dbo.hr_Companies.CompanyID
WHERE dbo.hr_Vacancies.Deleted = 0
AND (dbo.hr_Vacancies.JobTitleLang1 LIKE #LoacalSeacrchText
OR dbo.hr_Vacancies.JobTitleLang2 LIKE #LoacalSeacrchText
OR dbo.hr_Vacancies.DescriptionLang1 LIKE #LoacalSeacrchText
OR dbo.hr_Vacancies.DescriptionLang2 LIKE #LoacalSeacrchText
AND (dbo.hr_Vacancies.DutyStationID = #DutyStationID OR #DutyStationID IS NULL OR #DutyStationID = 0)
ORDER BY HavePriority DESC, StartDate DESC, dbo.hr_Vacancies.VacancyID DESC
Use option with CTE and OVER() clause
CREATE PROCEDURE [dbo].[hr_SearchVacanciesForService]
#SearchText NVARCHAR(50) = NULL,
#DutyStationID INT = NULL,
#VacancyCategoryIDs VARCHAR(1000) = NULL,
#Language INT = 1,
#NumberOfPages int
AS
;WITH cte AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY HavePriority DESC, StartDate DESC, dbo.hr_Vacancies.VacancyID DESC) AS Pages
FROM dbo.hr_Vacancies LEFT OUTER JOIN dbo.hr_DutyStations ON dbo.hr_Vacancies.DutyStationID = dbo.hr_DutyStations.DutyStationID
LEFT OUTER JOIN dbo.hr_Companies ON dbo.hr_Vacancies.CompanyID = dbo.hr_Companies.CompanyID
WHERE dbo.hr_Vacancies.Deleted = 0
AND (dbo.hr_Vacancies.JobTitleLang1 LIKE #LoacalSeacrchText
OR dbo.hr_Vacancies.JobTitleLang2 LIKE #LoacalSeacrchText
OR dbo.hr_Vacancies.DescriptionLang1 LIKE #LoacalSeacrchText
OR dbo.hr_Vacancies.DescriptionLang2 LIKE #LoacalSeacrchText
AND (dbo.hr_Vacancies.DutyStationID = #DutyStationID OR #DutyStationID IS NULL OR #DutyStationID = 0)
)
SELECT *, COUNT(*) OVER() AS totalOfPages
FROM cte
WHERE Pages BETWEEN 1 AND ISNULL(#NumberOfPages, Pages)
Example using OVER() clause with expressions:
SELECT ... ROW_NUMBER() OVER (ORDER BY
CASE WHEN dbo.hr_Vacancies.Priority = 0
THEN 0 ELSE
CASE WHEN CONVERT(DATETIME,CONVERT(CHAR(10),dbo.hr_Vacancies.PriorityDateExpired,101)) > CONVERT(DATETIME,CONVERT(CHAR(10),GETDATE(),101)) OR dbo.hr_Vacancies.PriorityDateExpired IS NULL
THEN 1 ELSE 0 END END DESC, your_second_expression_StartDate DESC)
If you want to show records from 20 to 30:
CREATE PROCEDURE [dbo].[hr_SearchVacanciesForService]
#SearchText NVARCHAR(50) = NULL,
#DutyStationID INT = NULL,
#VacancyCategoryIDs VARCHAR(1000) = NULL,
#Language INT = 1,
#StartPage int = NULL,
#EndPage int = NULL
AS
;WITH cte AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY your_case_expressionForColumnHavePriority DESC, your_case_expressionForColumnStartDate DESC, dbo.hr_Vacancies.VacancyID DESC) AS Pages
FROM dbo.hr_Vacancies LEFT OUTER JOIN dbo.hr_DutyStations ON dbo.hr_Vacancies.DutyStationID = dbo.hr_DutyStations.DutyStationID
LEFT OUTER JOIN dbo.hr_Companies ON dbo.hr_Vacancies.CompanyID = dbo.hr_Companies.CompanyID
WHERE dbo.hr_Vacancies.Deleted = 0
AND (dbo.hr_Vacancies.JobTitleLang1 LIKE #LoacalSeacrchText
OR dbo.hr_Vacancies.JobTitleLang2 LIKE #LoacalSeacrchText
OR dbo.hr_Vacancies.DescriptionLang1 LIKE #LoacalSeacrchText
OR dbo.hr_Vacancies.DescriptionLang2 LIKE #LoacalSeacrchText
AND (dbo.hr_Vacancies.DutyStationID = #DutyStationID OR #DutyStationID IS NULL OR #DutyStationID = 0)
)
SELECT *, COUNT(*) OVER() AS totalOfPages
FROM cte
WHERE Pages BETWEEN ISNULL(#StartPage, Pages) AND ISNULL(#EndPage, Pages)
You can read the following papers :
SQL Server stored procedures to page large tables or queries
Or
Stored Procedure having Sorting, Paging and Filtering
OLD METHOD - Simple Pagination script with (limit,order direction,order column,start index)
#orderColumn int ,
#orderDir varchar(20),
#start int ,
#limit int,
#searchKey varchar(20)
declare #to as int = #start+#limit
select IDENTITY(int, 1, 1) AS SnoID,null as abc, make.link,make.manf,make.name
into #tempMake
from make where status=1 and UPPER(make.name) like upper('%'+#searchKey+'%')
select * from #tempMake where SnoID>#start and SnoID<=#to
order by
CASE WHEN #orderColumn = 1 AND #orderdir = 'desc' THEN #tempMake.[manf] END DESC,
CASE WHEN #orderColumn = 1 AND #orderdir = 'asc' THEN #tempMake.[manf] END ASC,
CASE WHEN #orderColumn = 2 AND #orderdir = 'desc' THEN #tempMake.[link] END DESC,
CASE WHEN #orderColumn = 2 AND #orderdir = 'asc' THEN #tempMake.[link] END ASC
select count(#tempMake.SnoID) as row_count from #tempMake
drop table #tempMake

Speeding up this query? Currently it takes 4 seconds on 100K rows

This is my main query:
exec sp_executesql N'set arithabort off;set transaction isolation level read uncommitted;Select COUNT(*) From ( Select ROW_NUMBER() OVER
(Order By Case When d.OldInstrumentID IS NULL THEN d.LastStatusChangedDateTime Else d.RecordingDateTime End desc ) peta_rn,
d.DocumentID
From Documents d
Inner Join Users u on d.UserID = u.UserID Inner Join IGroupes ig on ig.IGroupID = d.IGroupID
Inner Join ITypes it on it.ITypeID = d.ITypeID Where 1=1 ANd dbo.DoesNameExist(d.DocumentID, #0, #1, #2, #3) = 1 And (CreatedByAccountID = #4
Or DocumentStatusID = #5
Or DocumentStatusID = #6 ) )
v',N'#0 int,#1 varchar(4000),#2 varchar(4000),#3 nvarchar(4000),#4 int,#5 int,#6 int',#0=-999,#1='K',#2='Miller',
#3=NULL,#4=44,#5=5,#6=9
and this is my scalar function which is the culprit:
ALTER FUNCTION [dbo].[DoesNameExist]
(
#DocumentID int,
#PartyTypeID int = 0,
#FirstName varchar(30),
#LastName varchar(30),
#Business varchar(100)
)
RETURNS bit
AS
BEGIN
Declare #Found bit = 0
Set #FirstName = IsNull(#FirstName,'')
Set #LastName = IsNull(#LastName,'')
Set #Business = IsNull(#Business,'')
Select Top 1 #Found = 1
From DocumentNames
Where DocumentID = #DocumentID
And
Lower(IsNull(FirstName,'')) Like
Case When #FirstName = '' Then Lower(IsNull(FirstName,'')) + '%'
Else Lower(#FirstName) + '%' End
And
Lower(IsNull(LastName,'')) Like
Case When #LastName = '' Then Lower(IsNull(LastName,'')) + '%'
Else Lower(#LastName) + '%' End
And
Lower(IsNull(Business,'')) Like
Case When #Business = '' Then Lower(IsNull(Business,'')) + '%'
Else Lower(#Business) + '%' End
And
PartyTypeID = Case When #PartyTypeID IS NULL OR #PartyTypeID <= 0 Then PartyTypeID Else #PartyTypeID End
Return #Found
END
Basically one document has multiple DocumentNames and when the user types something in LastName or FirstName, I want to bring the count of all documents which have those matching names. Please note, I am querying on Documents table and then joining to DocumentNames. This is necessary.
Note: Indexes are created on all search columns which include FirstName, LastName etc.
Thanks!
First, check Execution plan. How may times IsNULL() is getting used?Check estimated and actual time taken for all IsNULL(). If it is more, use a subquery where you can just calculate IsNULL() and supply to your query.
I do not have your Exceution plan. You can also try this aswell :
ALTER FUNCTION [dbo].[DoesNameExist]
(
#DocumentID int,
#PartyTypeID int = 0,
#FirstName varchar(30),
#LastName varchar(30),
#Business varchar(100)
)
RETURNS bit
AS
BEGIN
Declare #Found bit = 0
Select Top 1 #Found = 1
From DocumentNames
Where DocumentID = #DocumentID
And
Lower(IsNull(FirstName,'')) Like COALESCE(#FirstName,firstname,'')+'%'
And
Lower(IsNull(LastName,'')) Like COALESCE(#LastName,LastName,'')+'%'
And
Lower(IsNull(Business,'')) Like COALESCE(#Business,Business,'')+'%'
And
PartyTypeID = Case When #PartyTypeID IS NULL OR #PartyTypeID <= 0 Then PartyTypeID Else #PartyTypeID End
Return #Found
END
Also if you are ok to discard this UDF, using this directly in your statement with EXISTS clause should do the trick.
Select COUNT(*) From ( Select ROW_NUMBER() OVER
(Order By Case When d.OldInstrumentID IS NULL THEN d.LastStatusChangedDateTime Else d.RecordingDateTime End desc ) peta_rn,
d.DocumentID
From Documents d
Inner Join Users u on d.UserID = u.UserID Inner Join IGroupes ig on ig.IGroupID = d.IGroupID
Inner Join ITypes it on it.ITypeID = d.ITypeID Where 1=1
ANd EXISTS (
Select 1
From DocumentNames
Where DocumentID = d.DocumentID
And
Lower(IsNull(FirstName,'')) Like COALESCE(#1,lower(firstname),'')+'%'
And
Lower(IsNull(LastName,'')) Like COALESCE(#2,lower(LastName),'')+'%'
And
Lower(IsNull(Business,'')) Like COALESCE(#3,lower(Business),'')+'%'
And
PartyTypeID = Case When #0 IS NULL OR #0 <= 0 Then PartyTypeID Else #0 End
)
Or DocumentStatusID = #5
Or DocumentStatusID = #6 ) )
v',N'#0 int,#1 varchar(4000),#2 varchar(4000),#3 nvarchar(4000),#4 int,#5 int,#6 int',#0=-999,#1='K',#2='Miller',
#3=NULL,#4=44,#5=5,#6=9