Plain parameters slower than creating table parameters - sql

I recently discovered the strange behavior, that a function (procedures possibly too) runs significantly slower, when I provide simple NCHAR parameters rather than table value parameters. After trying around a bit I also found, that the function with NCHAR parameters runs as fast as the table value parameter function if I select the NCHAR parameters into a table variable. Here is some code I've created with this.
Function with parameters:
ALTER FUNCTION [rpt].[ReportMaterial]
(
#dateto NCHAR(8),
#mfrnr NVARCHAR(10),
#vkorg NVARCHAR(4)
)
RETURNS #ReturnValue TABLE
(
MANDT NVARCHAR(3),
MATNR NVARCHAR(18),
MFRPN NVARCHAR(40),
MAKTX NVARCHAR(40),
WERKS NVARCHAR(4),
MTART NVARCHAR(4),
KBETR DECIMAL(11, 2),
KONWA NVARCHAR(5)
)
AS
BEGIN
-- FILL UP
SELECT #mfrnr = REPLICATE('0', 10 - LEN(#mfrnr)) + #mfrnr;
-- OUTPUT
WITH mat AS
(
-- direct assignment
SELECT
ma.MANDT,
ma.MATNR,
ma.MFRPN,
mc.WERKS,
ma.MTART
FROM sap.MARA ma
INNER JOIN sap.MARC mc ON ma.MATNR = mc.MATNR AND ma.MANDT = mc.MANDT AND mc.CDCDELETED = 0
INNER JOIN
(
-- better to use in subselect here
SELECT DISTINCT
WERKS
FROM [sap].[Werks] w
WHERE w.WERKSTYPE = 'D' AND w.VKORG = #vkorg
) w ON w.WERKS = mc.WERKS
WHERE ma.MANDT = N'100' AND ma.CDCDELETED = 0 AND ma.MFRNR = #mfrnr
)
INSERT INTO #ReturnValue (MANDT, MATNR, MFRPN, MAKTX, WERKS, MTART, KBETR, KONWA)
SELECT
t.MANDT,
t.MATNR,
t.MFRPN,
mk.MAKTX,
t.WERKS,
t.MTART,
COALESCE(pp.KBETR, 0) KBETR,
pp.KONWA
FROM
(
SELECT
m.MANDT,
m.MATNR,
m.MFRPN,
m.WERKS,
m.MTART
FROM mat m
UNION ALL
-- material selection (part of a hardbundle)
SELECT
ma.MANDT,
ma.MATNR,
ma.MFRPN,
ms.WERKS,
ma.MTART
FROM sap.MARA ma
INNER JOIN sap.MAST ms ON ms.MATNR = ma.MATNR AND ms.STLAN = N'3' AND ms.MANDT = ma.MANDT
INNER JOIN sap.STKO ko ON ko.STLNR = ms.STLNR AND ko.STLAL = ms.STLAL AND ko.MANDT = ms.MANDT
INNER JOIN sap.STPO po ON po.STLNR = ms.STLNR AND po.MANDT = ms.MANDT
INNER JOIN mat mr ON po.IDNRK = mr.MATNR AND mr.MANDT = po.MANDT -- use only relevant items...
INNER JOIN
(
-- better to use as subselect
SELECT DISTINCT
WERKS
FROM [sap].[Werks] w
WHERE w.WERKSTYPE = 'D' AND w.VKORG = #vkorg
) w ON w.WERKS = ms.WERKS
WHERE ma.MTART = N'ZFER' AND ma.MFRNR = N'0080001373' -- we use only dummy hard bundles (sign for mixed manufacturer bundles)
) t
INNER JOIN sap.MAKT mk ON mk.MATNR = t.MATNR AND mk.SPRAS = N'E' AND mk.MANDT = t.MANDT
LEFT JOIN sap.PurchasePrice pp ON pp.MATNR = t.MATNR AND pp.WERKS = t.WERKS AND pp.FLIFN = N'X' AND #dateto BETWEEN pp.VDATU AND pp.BDATU AND #dateto BETWEEN pp.DATAB AND pp.DATBI;
RETURN
END;
Funtion with parameters selected into table variables first:
ALTER FUNCTION [rpt].[ReportMaterial]
(
#dateto NCHAR(8),
#mfrnr NVARCHAR(10),
#vkorg NVARCHAR(4)
)
RETURNS #ReturnValue TABLE
(
MANDT NVARCHAR(3),
MATNR NVARCHAR(18),
MFRPN NVARCHAR(40),
MAKTX NVARCHAR(40),
WERKS NVARCHAR(4),
MTART NVARCHAR(4),
KBETR DECIMAL(11, 2),
KONWA NVARCHAR(5)
)
AS
BEGIN
-- FILL UP
SELECT #mfrnr = REPLICATE('0', 10 - LEN(#mfrnr)) + #mfrnr;
--> Better performance if using a table to join instead of directly using of parameters
-- CREATE TABLE
DECLARE #keyValue TABLE
(
MFRNR NVARCHAR(10),
VKORG NVARCHAR(4)
);
INSERT INTO #keyValue (MFRNR, VKORG) VALUES (#mfrnr, #vkorg);
-- OUTPUT
WITH mat AS
(
-- direct assignment
SELECT
ma.MANDT,
ma.MATNR,
ma.MFRPN,
mc.WERKS,
ma.MTART
FROM sap.MARA ma
INNER JOIN sap.MARC mc ON ma.MATNR = mc.MATNR AND ma.MANDT = mc.MANDT AND mc.CDCDELETED = 0
INNER JOIN
(
-- better to use in subselect here
SELECT DISTINCT
WERKS
FROM [sap].[Werks] w
INNER JOIN #keyValue kv1 ON kv1.VKORG = w.VKORG
WHERE w.WERKSTYPE = 'D'
) w ON w.WERKS = mc.WERKS
INNER JOIN #keyValue kv2 ON kv2.MFRNR = ma.MFRNR
WHERE ma.MANDT = N'100' AND ma.CDCDELETED = 0
)
INSERT INTO #ReturnValue (MANDT, MATNR, MFRPN, MAKTX, WERKS, MTART, KBETR, KONWA)
SELECT
t.MANDT,
t.MATNR,
t.MFRPN,
mk.MAKTX,
t.WERKS,
t.MTART,
COALESCE(pp.KBETR, 0) KBETR,
pp.KONWA
FROM
(
SELECT
m.MANDT,
m.MATNR,
m.MFRPN,
m.WERKS,
m.MTART
FROM mat m
UNION ALL
-- material selection (part of a hardbundle)
SELECT
ma.MANDT,
ma.MATNR,
ma.MFRPN,
ms.WERKS,
ma.MTART
FROM sap.MARA ma
INNER JOIN sap.MAST ms ON ms.MATNR = ma.MATNR AND ms.STLAN = N'3' AND ms.MANDT = ma.MANDT
INNER JOIN sap.STKO ko ON ko.STLNR = ms.STLNR AND ko.STLAL = ms.STLAL AND ko.MANDT = ms.MANDT
INNER JOIN sap.STPO po ON po.STLNR = ms.STLNR AND po.MANDT = ms.MANDT
INNER JOIN mat mr ON po.IDNRK = mr.MATNR AND mr.MANDT = po.MANDT -- use only relevant items...
INNER JOIN
(
-- better to use as subselect
SELECT DISTINCT
WERKS
FROM [sap].[Werks] w
INNER JOIN #keyValue kv1 ON kv1.VKORG = w.VKORG
WHERE w.WERKSTYPE = 'D'
) w ON w.WERKS = ms.WERKS
WHERE ma.MTART = N'ZFER' AND ma.MFRNR = N'00000000'
) t
INNER JOIN sap.MAKT mk ON mk.MATNR = t.MATNR AND mk.SPRAS = N'E' AND mk.MANDT = t.MANDT
LEFT JOIN sap.PurchasePrice pp ON pp.MATNR = t.MATNR AND pp.WERKS = t.WERKS AND pp.FLIFN = N'X' AND #dateto BETWEEN pp.VDATU AND pp.BDATU AND #dateto BETWEEN pp.DATAB AND pp.DATBI;
RETURN
END;
Does anybody have an Idea, why this is the case? Do I need to leverage this effect by allways selecting parameters into temporary tables/table variables?

Related

Insert if not exists doesn't work. I have 20000 records in JSON file. I want to do insert/update into table from json table accordingly using SP

I want to update/insert records from Json table ( which has 5000 records) to another table .
when I run for first time Insert works fine and when I update ,it updates accordingly.
But when I ran again the same file with additional records it doesn't insert the records.
can someone help me with this ?
Below is my code
ALTER PROCEDURE [dbo].[load_consultation_data]
#consultation_json NVARCHAR(MAX)
-- Add the parameters for the stored procedure here
AS
BEGIN
SET NOCOUNT ON;
DECLARE #JSON VARCHAR(MAX)
SET #JSON = #consultation_json
DECLARE #jsontable table
(
customerID NVARCHAR(50),
clinicID NVARCHAR(50),
birdId NVARCHAR(80),
consultationId NVARCHAR(80),
employeeName NVARCHAR(150),
totalPrice MONEY,
row_num int
)
/* Importing nested Json data */
INSERT INTO #jsontable
SELECT customer.customerID AS customerID
,customer.clinicID AS clinicID
,consultation.birdId AS birdId
,consultation.consultationId AS consultationId
,consultation.employeeName AS employeeName
,consultation.totalPrice AS totalPrice
,ROW_NUMBER() OVER (ORDER BY consultationId ) row_num
FROM OPENJSON (#JSON, '$')
WITH (customerID VARCHAR(50) '$.customerID',
clinicID VARCHAR(50) '$.clinic.clinicID') as customer
CROSS APPLY openjson(#json,'$.clinic.consultation')
WITH(
birdId NVARCHAR(80),
consultationId NVARCHAR(80),
employeeName NVARCHAR(150),
totalPrice MONEY
) as consultation
INNER JOIN dbo.clinic AS clinic_tab ON clinic_tab.external_id = customer.clinicID
INNER JOIN dbo.bird AS bird_tab ON bird_tab.external_bird_id = consultation.birdId
/** If consultation record doesn't exists then do insert else update **/
IF NOT EXISTS
(
SELECT 1
FROM dbo.consultation AS con
INNER JOIN dbo.bird AS al ON con.external_bird_id = al.external_bird_id AND al.clinic_id = con.clinic_id
INNER JOIN dbo.clinic AS pr ON con.clinic_id = pr.id
INNER JOIN #jsontable AS rjson ON rjson.consultationId = con.external_consultation_id
WHERE con.external_consultation_id = rjson.consultationId
AND pr.external_id = rjson.clinicID
AND al.external_bird_id = rjson.birdId )
BEGIN
/** Insert recored into consultation table **/
INSERT INTO dbo.[consultation]
(
[external_consultation_id]
,[external_bird_id]
,[clinic_id]
,[bird_id]
,[employee_name]
,[total_price]
)
SELECT rjson.consultationId AS [external_consultation_id]
,rjson.[birdId] AS [external_bird_id]
,bird_tab.clinic_id AS [clinic_id]
,bird_tab.[id] AS [bird_id]
,rjson.employeeName AS [employee_name]
,rjson.totalPrice AS [total_price]
FROM #jsontable as rjson
INNER JOIN dbo.bird AS bird_tab on bird_tab.external_bird_id = rjson.birdId
INNER JOIN dbo.clinic AS clinic_tab on clinic_tab.external_id = rjson.clinicID
WHERE rjson.consultationId = rjson.consultationId --#consultationID
and bird_tab.clinic_id = clinic_tab.id
END
ELSE
BEGIN
/* Update Records into consultation table */
UPDATE dbo.[consultation] SET
[external_bird_id] = rjson.[birdId]
,[clinic_id] = al.clinic_id
,[bird_id] = al.[id]
,[employee_name] = rjson.employeeName
,[total_price] = rjson.totalPrice
FROM dbo.consultation AS con
INNER JOIN dbo.bird AS al ON con.external_bird_id = al.external_bird_id AND al.clinic_id = con.clinic_id
INNER JOIN dbo.clinic AS pr ON con.clinic_id = pr.id
INNER JOIN #jsontable AS rjson ON rjson.consultationId = con.external_consultation_id
WHERE con.external_consultation_id = rjson.consultationId
AND pr.external_id = rjson.clinicID
AND al.external_bird_id = rjson.birdId
END
END

Avoid function in where clause,due to that its causing performance issue?

Whenever I'm executing the procedure I got performance issue, where do I need to change the procedure to increase the performance?
I have called table function in where clause I need to optimize this procedure without using string.
CREATE PROC proc_productwise_report #cmp_id VARCHAR(max), #unitcode VARCHAR(max), #gr_code VARCHAR(max), #store_code VARCHAR(max), #from_dt VARCHAR(20), #to_dt VARCHAR(20)
AS
BEGIN
SELECT sh.cmp_id, d.unitcode, d.store_code, st.item_code AS product, d.item_code, im.item_desc, SUM(charge_qty) AS challan_qty
FROM ps_invenstatic sh
INNER JOIN ps_invenstaticdet st ON sh.cmp_id = st.cmp_id
AND sh.sys_no_id = st.sys_no_id
AND sh.doc_id = st.doc_id
AND sys_doc_type = 'PSCH'
INNER JOIN ps_invenissu h ON sh.cmp_id = h.cmp_id
AND sh.doc_type = h.ref_doc_type
AND sh.doc_no = h.ref_doc_no
AND h.prod_code = st.item_code
INNER JOIN ps_invenissudet d ON h.cmp_id = d.cmp_id
AND h.sys_no_id = d.sys_no_id
AND h.doc_id = d.doc_id
INNER JOIN ps_itemmas im ON sh.cmp_id = im.cmp_id
AND im.item_code = d.item_code
WHERE sh.cmp_id IN (
SELECT *
FROM utilfn_split(#cmp_id, ',')
)
AND d.unitcode IN (
SELECT *
FROM utilfn_split(#unitcode, ',')
)
AND im.gr_code IN (
SELECT *
FROM utilfn_split(#gr_code, ',')
)
AND d.store_code IN (
SELECT *
FROM utilfn_split(#store_code, ',')
)
AND h.doc_dt BETWEEN convert(DATETIME, #from_dt, 103)
AND convert(DATETIME, #to_dt, 103)
AND sh.Stat_Code <> 'CA'
GROUP BY sh.cmp_id, d.unitcode, d.store_code, st.item_code, d.item_code, im.item_desc
END
I need to avoid function in where clause and resolve the performance issue.
You can build temporary tables in your stored procedure with the result of the SPLIT and INNER JOIN those temporary tables in your main query.
CREATE PROC proc_productwise_report #cmp_id VARCHAR(max), #unitcode VARCHAR(max),
#gr_code VARCHAR(max), #store_code VARCHAR(max), #from_dt VARCHAR(20), #to_dt VARCHAR(20)
AS
BEGIN
SELECT *
INTO #cmp_ids
FROM utilfn_split(#cmp_id, ',');
SELECT *
INTO #unitcodes
FROM utilfn_split(#unitcode, ',');
SELECT *
INTO #gr_codes
FROM utilfn_split(#gr_code, ',');
SELECT *
INTO #store_codes
FROM utilfn_split(#store_code, ',');
SELECT
sh.cmp_id
, d.unitcode
, d.store_code
, st.item_code AS product
, d.item_code
, im.item_desc
, SUM(charge_qty) AS challan_qty
FROM ps_invenstatic sh
INNER JOIN ps_invenstaticdet st
ON sh.cmp_id = st.cmp_id
AND sh.sys_no_id = st.sys_no_id
AND sh.doc_id = st.doc_id
AND sys_doc_type = 'PSCH'
INNER JOIN ps_invenissu h
ON sh.cmp_id = h.cmp_id
AND sh.doc_type = h.ref_doc_type
AND sh.doc_no = h.ref_doc_no
AND h.prod_code = st.item_code
INNER JOIN ps_invenissudet d
ON h.cmp_id = d.cmp_id
AND h.sys_no_id = d.sys_no_id
AND h.doc_id = d.doc_id
INNER JOIN ps_itemmas im
ON sh.cmp_id = im.cmp_id
AND im.item_code = d.item_code
INNER JOIN #cmp_ids tci on sh.cmp_id = tci.[value]
INNER JOIN #unitcodes tuc on d.unitcode = tuc.[value]
INNER JOIN #gr_codes tgr on im.gr_code = tgr.[value]
INNER JOIN #store_codes tsc on d.store_code = tsc.[value]
WHERE h.doc_dt BETWEEN convert(DATETIME, #from_dt, 103)
AND convert(DATETIME, #to_dt, 103)
AND sh.Stat_Code <> 'CA'
GROUP BY sh.cmp_id
, d.unitcode
, d.store_code
, st.item_code
, d.item_code
, im.item_desc
END
Use table-valued parameters instead of CSV strings. Alternatively in your proc
create temp tables (table variables) first.
declare #tunitcode table (id int); -- you type may be different
insert #tunitcode(id)
select *
from utilfn_split(#unitcode, ',');
.. AND d.unitcode IN (
SELECT * FROM #tunitcode)
..

Offer to increase performance in stored procedure

I write this query in SQL Server 2016:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[s2_GetReceivedDocumentsCount]
#_Username NVARCHAR(MAX),
#_SearchText NVARCHAR(MAX),
#_States NVARCHAR(MAX),
#_Senders NVARCHAR(MAX),
#_Date1 NVARCHAR(MAX),
#_Date2 NVARCHAR(MAX),
#_Filter1ID NVARCHAR(MAX),
#_Filter1Value NVARCHAR(MAX)
AS
BEGIN
--https://blogs.technet.microsoft.com/mdegre/2011/11/06/what-is-parameter-sniffing/
DECLARE #Username NVARCHAR(MAX)
DECLARE #Fild BIT
DECLARE #SearchText NVARCHAR(MAX)
DECLARE #States NVARCHAR(MAX)
DECLARE #Senders NVARCHAR(MAX)
DECLARE #Date1 NVARCHAR(MAX)
DECLARE #Date2 NVARCHAR(MAX)
DECLARE #Filter1ID NVARCHAR(MAX)
DECLARE #Filter1Value NVARCHAR(MAX)
SELECT
#Username = #_Username,
#SearchText = #_SearchText,
#States = #_States,
#Senders = #_Senders,
#Date1 = #_Date1,
#Date2 = #_Date2,
#Filter1ID = #_Filter1ID,
#Filter1Value = #_Filter1Value
SELECT #SearchText = LTRIM(RTRIM(IsNull(#SearchText, '')))
SELECT #Filter1ID = LTRIM(RTRIM(IsNull(#Filter1ID, '')))
SELECT #Filter1Value = LTRIM(RTRIM(IsNull(#Filter1Value, '')))
DECLARE #PersonalPostID INT = NULL
SELECT #PersonalPostID = p.PostID
FROM Person pr
JOIN PersonPost pp ON pp.PersonID = pr.PersonID
JOIN Post p ON p.PostID = pp.PostID
WHERE pr.Username = #Username
AND p.IsPersonalPost = 1
DECLARE #independentPostExists BIT = CASE
WHEN EXISTS (SELECT 1 FROM Post t
WHERE t.Independent = 1 AND t.PostID > 0)
THEN 1
ELSE 0
END
DECLARE #temp0 TABLE (
DocumentID int
, Likeness int
, ParentScrutinyID int
)
;With AllPost
As
(
Select pp.PostID
From
PersonPost pp
Join Person prs On prs.PersonID = pp.PersonID
Where prs.Username = #Username
Union
Select [type].PostID
From
Post [type]
Join Post p On p.TypeID = [type].PostID
Join PersonPost pp On pp.PostID = p.PostID
Join Person prs On prs.PersonID = pp.PersonID
Where prs.Username = #Username
)
,
SplitSearchText
AS
(
Select * From dbo._1001_Split(dbo.ReplaceYK(#SearchText),N'')
),
Temp0
AS
(
Select Distinct
s.DocumentID
, s.Code
, s2_Scrutiny.ParentScrutinyID
From
s2_Document s
Join s2_State state On state.StateID = s.StateID
Join Post sender On sender.PostID = s.SenderID
Join s2_Scrutiny On
s2_Scrutiny.DocumentID = s.DocumentID
And s2_Scrutiny.IsActive = 1
And s2_Scrutiny.ReferenceID not in (-10, -20)
Cross Join AllPost
Join s2_Producer On s2_Producer.DocumentID = s.DocumentID
Join PersonPost producerPost On producerPost.PostID = s2_Producer.PostID
Join Person producerPerson On producerPerson.PersonID = producerPost.PersonID
Where
1 = 1
And (#States = '' Or (N',' + #States + N',') Like (N'%,' + Cast(s.StateID as nvarchar(max)) + ',%'))
And (#Senders = '' Or (N',' + #Senders + N',') Like (N'%,' + Cast(s.SenderID as nvarchar(max)) + ',%'))
And (#Date1 = '' Or s.RegistrationDate >= #Date1)
And (#Date2 = '' Or s.RegistrationDate <= #Date2)
And (#Filter1ID = ''
Or Exists(
Select 1
From
s2_FieldValue fv
Join s2_Field f On f.FieldID = fv.FieldID
Where
fv.DocumentID = s.DocumentID
And fv.FieldID = #Filter1ID
And (
(f.FieldTypeID in (0, 5/*فیلد محاسباتی*/) And fv.[Value] = #Filter1Value)
Or (f.FieldTypeID = 3 And Cast(fv.[FieldOptionID] as nvarchar(max)) = #Filter1Value)
Or (f.FieldTypeID in(1,2,4))
)
))
--پیشنهاد به پست یا نوع پستی که این شخص حائز آن است، ارجاع شده است
And AllPost.PostID = s2_Scrutiny.ReferenceID
), Temp1
AS
(
Select Distinct
s.DocumentID
,Likeness = 99999999
From Temp0 s
Where #SearchText != '' And #SearchText = ISNULL(s.Code, s.DocumentID)
Union
Select Distinct
s.DocumentID
,Likeness = SUM(ts.[Length])
From
Temp0 s
Join s2_TextSegment ts On
ts.TableName = N's2_Document'
And ts.FieldName = N'Subject'
And ts.RecordID = s.DocumentID
Where #SearchText != '' And #SearchText != ISNULL(s.Code, s.DocumentID)
Group by s.DocumentID
Union
Select Distinct
s.DocumentID
,Likeness = 1
From Temp0 s
Where #SearchText = ''
)
, Temp2
AS
(
Select t0.*, t1.Likeness
From
Temp0 t0
Join Temp1 t1 On t0.DocumentID = t1.DocumentID
)
Insert Into #temp0 (DocumentID, Likeness, ParentScrutinyID)
Select DocumentID, Likeness, ParentScrutinyID From Temp2
DECLARE #temp1 TABLE (
DocumentID int
, Likeness int
)
If #independentPostExists = 0
Begin
Insert Into #temp1 (DocumentID, Likeness)
Select
t.DocumentID
, t.Likeness
From
#temp0 t
End
ELSE
Begin--حوزه مستقلی تعریف شده باشد
--انتقال حوزه فعال باشد
Insert Into #temp1 (DocumentID, Likeness)
Select
t.DocumentID
, t.Likeness
From
#temp0 t
Join s2_Scrutiny parentScrutiny On parentScrutiny.ScrutinyID = t.ParentScrutinyID
Join s2_ScrutinyItem sci On sci.ScrutinyItemID = parentScrutiny.ScrutinyItemID
Where
sci.TransferArea = 1
-- شخص جاری در حوزه ارجاع دهنده باشد
Insert Into #temp1 (DocumentID, Likeness)
Select
t.DocumentID
, t.Likeness
From
#temp0 t
Join s2_Scrutiny parentScrutiny On parentScrutiny.ScrutinyID = t.ParentScrutinyID
Join Temp_SubalternPost tsp1 On tsp1.Subaltern_PostID = parentScrutiny.ScrutinierID
Join Temp_SubalternPost tsp2 On tsp2.Superior_PostID = tsp1.Superior_PostID
Where
tsp1.Superior_NearestIndependent = 1
And tsp2.Subaltern_PostID = #PersonalPostID
--And Not Exists(Select 1 From #temp0 tt Where tt.DocumentID = t.DocumentID)
End
Select Count(Distinct t.DocumentID) From #temp1 t Where Likeness > 0
END--end procedure
GO
This code takes 26 seconds:
exec sp_executesql N'Exec [dbo].[s2_GetReceivedDocumentsCount] #username=N'admin', #Filter1ID=N'12',#Filter1Value=N'17658'
BUT :
I tested this code for another state but returned 22,000 records in 3 seconds
exec sp_executesql N'Exec [dbo].[s2_GetReceivedDocumentsCount] #username=N'admin'
In this code I removed the #Filter1ID=N'12',#Filter1Value=N'17658'
When I remove this #Filter1ID it not enter here:
And (#Filter1ID = ''
Or Exists(
Select 1
From
s2_FieldValue fv
Join s2_Field f On f.FieldID = fv.FieldID
Where
fv.DocumentID = s.DocumentID
And fv.FieldID = #Filter1ID
And (
(f.FieldTypeID in (0, 5/*فیلد محاسباتی*/) And fv.[Value] = #Filter1Value)
Or (f.FieldTypeID = 3 And Cast(fv.[FieldOptionID] as nvarchar(max)) = #Filter1Value)
Or (f.FieldTypeID in(1,2,4))
)
))
Now I'm sure problem is here. But where is it? Where is the problem?
While correlated EXISTS clauses can be a real problem, your overall SP is such a dogs breakfast that I think you should focus on other aspects first:
Do not use LIKE to match a list of numeric values:
(N',' + #States + N',') Like (N'%,' + Cast(s.StateID as nvarchar(max)) + ',%'))
Since you already utilise string splitting function, you should split input value into a table variable of stateIDs (make sure that data type is the same as s.StateID and join it. Do the same for #Senders.
Minimise the use of OR as this kills performance really quickly:
This And (#Date1 = '' Or s.RegistrationDate >= #Date1) should be replaced by:
SET #Date1 = CASE WHEN '' THEN '01-Jan-1753' ELSE #Date1 END and then in your query you can simply have And s.RegistrationDate >= #Date1.
For #Date2 use the maximum value as per DATETIME reference.
Get rid of NVARCHAR( MAX ). Unless you realistically expect input values to be more than 4000 characters, you should use NVARCHAR( 2000 ) or something smaller.
There is a major performance difference between UNION and UNION ALL. Make sure you use UNION ALL unless you need to remove duplicate records. See this
And lastly EXISTS: without fully knowing your table structure and being able to run the query myself I cannot see a way of removing it, such that it will definitely improve performance.

Returning column with count of 0

I have a query that looks up a list of documents depending on their department and their status.
DECLARE #StatusIds NVARCHAR(MAX) = '1,2,3,4,5';
DECLARE #DepartmentId NVARCHAR(2) = 'IT';
SELECT ILDPST.name,
COUNT(*) AS TodayCount
FROM dbo.TableA ILDP
LEFT JOIN dbo.TableB ILDPS ON ILDPS.IntranetLoanDealPreStateId = ILDP.IntranetLoanDealPreStateId
LEFT JOIN dbo.TableC ILDPST ON ILDPST.IntranetLoanDealPreStateTypeId = ILDPS.CurrentStateTypeId
WHERE (ILDP.CreatedByDepartmentId = #DepartmentId OR #DepartmentId IS NULL)
AND ILDPS.CurrentStateTypeId IN (
SELECT value
FROM dbo.StringAsIntTable(#StatusIds)
)
GROUP BY ILDPST.name;
This returns the results:
However, I'd also like to be able to return statuses where the TodayCount is equal to 0 (i.e. any status with an id included in #StatusIds should be returned, regardless of TodayCount).
I've tried messing with some unions / joins / ctes but I couldn't quite get it to work. I'm not much of an SQL person so not sure what else to provide that could be useful.
Thanks!
If you want to have all the records from TableC you need to left join all other tables to it, not left join it to the other tables. Also it's best to INNER JOIN the filtering table you create from #StatusIds rather then apply it through INclause. Try this:
DECLARE #StatusIds NVARCHAR(MAX) = '1,2,3,4,5';
DECLARE #DepartmentId NVARCHAR(2) = 'IT';
SELECT ILDPST.Name, COUNT(ILDP.IntranetLoanDealPreStateId) AS TodayCount
FROM (SELECT DISTINCT value FROM dbo.StringAsIntTable(#StatusIds)) StatusIds
INNER JOIN dbo.TableC ILDPST
ON ILDPST.IntranetLoanDealPreStateTypeId = StatusIds.value
LEFT JOIN dbo.TableB ILDPS
ON ILDPS.CurrentStateTypeId = ILDPST.IntranetLoanDealPreStateTypeId
LEFT JOIN dbo.TableA ILDP
ON ILDP.IntranetLoanDealPreStateId = ILDPS.IntranetLoanDealPreStateId
AND (ILDP.CreatedByDepartmentId = #DepartmentId OR #DepartmentId IS NULL)
GROUP BY ILDPST.Name;
Try this instead:
DECLARE #StatusIds NVARCHAR(MAX) = '1,2,3,4,5';
DECLARE #DepartmentId NVARCHAR(2) = 'IT';
SELECT ILDPST.name,
COUNT(ILDP.IntranetLoanDealPreStateId) AS TodayCount
FROM
dbo.TableC ILDPST
LEFT JOIN
dbo.TableB ILDPS ON ILDPST.IntranetLoanDealPreStateTypeId = ILDPS.CurrentStateTypeId
LEFT JOIN
dbo.TableA ILDP ON ILDPS.IntranetLoanDealPreStateId = ILDP.IntranetLoanDealPreStateId
AND (ILDP.CreatedByDepartmentId = #DepartmentId OR #DepartmentId IS NULL)
WHERE
ILDPST.IntranetLoanDealPreStateTypeId
IN (
SELECT value
FROM dbo.StringAsIntTable(#StatusIds)
)
GROUP BY ILDPST.name;
You could use the following function to create a table value for your status id's.
CREATE FUNCTION [dbo].[SplitString]
(
#myString varchar(max),
#deliminator varchar(2)
)
RETURNS
#ReturnTable TABLE
(
[Part] [varchar](max) NULL
)
AS
BEGIN
Declare #iSpaces int
Declare #part varchar(max)
--initialize spaces
Select #iSpaces = charindex(#deliminator,#myString,0)
While #iSpaces > 0
Begin
Select #part = substring(#myString,0,charindex(#deliminator,#myString,0))
Insert Into #ReturnTable(Part)
Select #part
Select #myString = substring(#mystring,charindex(#deliminator,#myString,0)+ len(#deliminator),len(#myString) - charindex(' ',#myString,0))
Select #iSpaces = charindex(#deliminator,#myString,0)
end
If len(#myString) > 0
Insert Into #ReturnTable
Select #myString
RETURN
END
This can now be used as a table that you can LEFT JOIN to.
DECLARE #StatusIds NVARCHAR(MAX) = '1,2,3,4,5';
SELECT * FROM dbo.SplitString(#StatusIds, ',')
It is not tested but give it a try:
;With Cte ( Value ) As
( Select Distinct Value From dbo.StringAsIntTable( #StatusIds ) )
Select
ILDPST.name,
COUNT(*) AS TodayCount
From
dbo.TableC As ILDPST
Inner Join Cte On ( ILDPST.IntranetLoanDealPreStateTypeId = Cte.Value )
Left Join dbo.TableB As ILDPS On ( ILDPST.IntranetLoanDealPreStateTypeId = ILDPS.CurrentStateTypeId )
Left Join dbo.TableA As ILDP On ( ILDPS.IntranetLoanDealPreStateId = ILDP.IntranetLoanDealPreStateId )
And ( ( ILDP.CreatedByDepartmentId = #DepartmentId ) Or ( #DepartmentId Is Null ) )
Group By
ILDPST.name

Changing SQL Stored Procedure for multiple search results

I have some inherited code I need to modify in order to accommodate multiple #ParentFolderID parameter. At present, one ID is passed in. However I will now need to account for several ID's being passed in and returning results from each. Below is the current code. I'm not quite sure what exactly where I would start.
declare #Values xml
declare #ValueAttributeID int
declare #YearAttributeID int
declare #CategoryID int
declare #year int
declare #ParentFolderID int
declare #DealerAttributeID int
set #ParentFolderID = 10646615
set #CategoryID = 10646175
set #YearAttributeID = 3
set #ValueAttributeID = 2
set #year = 2014
set #Values = '<values><value id=''1000104'' /></values>'
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
with Parents
(
dataid
)
as
(
select #ParentFolderID
Union
select child.dataid
from DTree parent (NOLOCK)
inner join DTree child (NOLOCK)
on parent.dataid = child.parentid
where parent.dataid = #ParentFolderID
and child.subtype = 0
)
select doc.name as '#name',
doc.dataId as '#id',
(
select allAtts.AttrID as '#id',
case when ((allAtts.ValInt is null) and (allAtts.ValStr is null))
then cast(allAtts.ValDate as nvarchar(255))
when (allAtts.ValInt is null and allAtts.ValDate is null)
then cast(allAtts.ValStr as nvarchar(255))
when (allAtts.ValDate is null and allAtts.ValStr is null)
then cast(allAtts.ValInt as nvarchar(255))
end as '#val'
from LLAttrData allAtts (NOLOCK)
where allAtts.id = doc.dataid
for xml path('attribute'), TYPE
)
from DTree category (NOLOCK)
inner join LLAttrData value (NOLOCK)
on category.dataid = value.defid
--Changes per environment (value attribute)
and value.AttrID = #ValueAttributeID
--Check for values
inner join #Values.nodes('//value') as A(att)
on A.att.value('#id', 'nvarchar(255)') = value.ValStr
--Changes per environment (year attribute)
inner join LLAttrData y (NOLOCK)
on category.dataid = y.defid
--Changes per environment (year attribute)
and y.AttrID = #YearAttributeID
--Check for year
and year(y.valDate) = #year
inner join DTree doc (NOLOCK)
on value.id = doc.dataid
and y.id = doc.dataid
inner join Parents parent
on parent.dataid = doc.parentid
--Must be associated to the category
where category.dataid = #CategoryID -- This is the hard coded category ID
order by doc.dataid --, allAtts.AttrID
FOR XML PATH('document'), Root('documents')`