My slow T-SQL query needs a complete rethink - sql

Good afternoon all. I'm going to post the stored procedure in it's entire glory. Feel free to rip it to shreds. The author won't mind.
DECLARE #itemTypeID INT
SELECT #itemTypeID=ItemTypeID FROM dbo.ItemTypes WHERE ItemTypeName = 'Advert'
BEGIN
SELECT a.Active,
a.ParentClass,
a.Classification,
a.Variant,
FV."Full Views",
PV."Print Views",
EE."Email Enquiries",
a.ItemRef,
a.SiteID
FROM
(
SELECT DISTINCT i.ItemID,
i.ItemRef,
i.SiteID,
i.ParentClass,
i.Classification,
i.Summary AS "Variant",
i.Active
FROM Items i
JOIN Actions a
ON a.ItemID = i.ItemID
JOIN ActionTypes at
ON a.ActionTypeID = at.ActionTypeID
WHERE i.ItemTypeID = 1
AND a.DateAndTime BETWEEN #startDate AND #endDate
AND at.ActionTypeName IN ('Full view', 'Print view', 'Email enquiry')
AND ((#siteID = -1) OR (i.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
) a LEFT JOIN
(
SELECT i.ItemID,
COUNT(*) AS "Full Views"
FROM CustomerSites cs JOIN Items i
ON cs.SiteID = i.SiteID
JOIN Actions a
ON a.ItemID = i.ItemID
JOIN ActionTypes at
ON a.ActionTypeID = at.ActionTypeID
JOIN Sites s
ON cs.SiteID = s.SiteID
WHERE a.DateAndTime BETWEEN #startDate AND #endDate
AND i.ItemTypeID = #itemTypeID
AND at.ActionTypeName = 'Full view'
AND ((#customerID IS NULL) OR (cs.CustomerID = #customerID))
AND ((#siteID = -1) OR (cs.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
GROUP BY
i.ItemID
) FV
ON a.ItemID = FV.ItemID
LEFT JOIN
(
SELECT i.ItemID,
COUNT(*) AS "Print Views"
FROM CustomerSites cs JOIN Items i
ON cs.SiteID = i.SiteID
JOIN Actions a
ON a.ItemID = i.ItemID
JOIN ActionTypes at
ON a.ActionTypeID = at.ActionTypeID
JOIN Sites s
ON cs.SiteID = s.SiteID
WHERE a.DateAndTime BETWEEN #startDate AND #endDate
AND i.ItemTypeID = #itemTypeID
AND at.ActionTypeName = 'Print view'
AND ((#customerID IS NULL) OR (cs.CustomerID = #customerID))
AND ((#siteID = -1) OR (cs.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
GROUP BY
i.ItemID
) PV
ON a.ItemID = PV.ItemID
LEFT JOIN
(
SELECT i.ItemID,
COUNT(*) AS "Email Enquiries"
FROM CustomerSites cs JOIN Items i
ON cs.SiteID = i.SiteID
JOIN Actions a
ON a.ItemID = i.ItemID
JOIN ActionTypes at
ON a.ActionTypeID = at.ActionTypeID
JOIN Sites s
ON cs.SiteID = s.SiteID
WHERE a.DateAndTime BETWEEN #startDate AND #endDate
AND i.ItemTypeID = #itemTypeID
AND at.ActionTypeName = 'Email enquiry'
AND ((#customerID IS NULL) OR (cs.CustomerID = #customerID))
AND ((#siteID = -1) OR (cs.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
GROUP BY
i.ItemID
) EE
ON a.ItemID = EE.ItemID
UNION
SELECT '0','','','','','','','',''
Now ultimately all this does is return some records and the number of times a particular action has occured against them.
A small subset would look like.
Item Description Views Printed Emails
Item1 Desc1 12 NULL 1
Item2 Desc2 NULL NULL 3
Item3 Desc3 5 6 2
Hopefully you can see what's going on.
I want a list of items who have had actions against them for a particular date range for a particular customer for a particular site and the query should be filterable on parent class and classification. Nice
The first select returns all distinct items that fall within the selection criteria.
The other 3 queries all simply returning counts of 1 type of action against each item. The query is pants slow even against a small amount of data. This is never going to go live it just won't work.
Hopefully you can see the error of the 'authors' ways and correct him/her.

here is the original query with my formatting style:
SELECT
a.Active
,a.ParentClass
,a.Classification
,a.Variant
,FV."Full Views"
,PV."Print Views"
,EE."Email Enquiries"
,a.ItemRef
,a.SiteID
FROM (SELECT DISTINCT
i.ItemID,
,i.ItemRef
,i.SiteID
,i.ParentClass
,i.Classification
,i.Summary AS "Variant"
,i.Active
FROM Items i
JOIN Actions a ON a.ItemID = i.ItemID
JOIN ActionTypes at ON a.ActionTypeID = at.ActionTypeID
WHERE i.ItemTypeID = 1
AND a.DateAndTime BETWEEN #startDate AND #endDate
AND at.ActionTypeName IN ('Full view', 'Print view', 'Email enquiry')
AND ((#siteID = -1) OR (i.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
) a
LEFT JOIN (SELECT
i.ItemID
,COUNT(*) AS "Full Views"
FROM CustomerSites cs
JOIN Items i ON cs.SiteID = i.SiteID
JOIN Actions a ON a.ItemID = i.ItemID
JOIN ActionTypes at ON a.ActionTypeID = at.ActionTypeID
JOIN Sites s ON cs.SiteID = s.SiteID
WHERE a.DateAndTime BETWEEN #startDate AND #endDate
AND i.ItemTypeID = #itemTypeID
AND at.ActionTypeName = 'Full view'
AND ((#customerID IS NULL) OR (cs.CustomerID = #customerID))
AND ((#siteID = -1) OR (cs.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
GROUP BY i.ItemID
) FV ON a.ItemID = FV.ItemID
LEFT JOIN (SELECT
i.ItemID
,COUNT(*) AS "Print Views"
FROM CustomerSites cs
JOIN Items i ON cs.SiteID = i.SiteID
JOIN Actions a ON a.ItemID = i.ItemID
JOIN ActionTypes at ON a.ActionTypeID = at.ActionTypeID
JOIN Sites s ON cs.SiteID = s.SiteID
WHERE a.DateAndTime BETWEEN #startDate AND #endDate
AND i.ItemTypeID = #itemTypeID
AND at.ActionTypeName = 'Print view'
AND ((#customerID IS NULL) OR (cs.CustomerID = #customerID))
AND ((#siteID = -1) OR (cs.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
GROUP BY i.ItemID
) PV ON a.ItemID = PV.ItemID
LEFT JOIN (SELECT
i.ItemID
,COUNT(*) AS "Email Enquiries"
FROM CustomerSites cs
JOIN Items i ON cs.SiteID = i.SiteID
JOIN Actions a ON a.ItemID = i.ItemID
JOIN ActionTypes at ON a.ActionTypeID = at.ActionTypeID
JOIN Sites s ON cs.SiteID = s.SiteID
WHERE a.DateAndTime BETWEEN #startDate AND #endDate
AND i.ItemTypeID = #itemTypeID
AND at.ActionTypeName = 'Email enquiry'
AND ((#customerID IS NULL) OR (cs.CustomerID = #customerID))
AND ((#siteID = -1) OR (cs.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
GROUP BY i.ItemID
) EE ON a.ItemID = EE.ItemID
UNION
SELECT '0','','','','','','','',''
this should help a little:
;WITH CustomerSitesCounts AS
(
SELECT
at.ActionTypeName
,i.ItemID
,COUNT(*) AS "Print Views"
FROM CustomerSites cs
JOIN Items i ON cs.SiteID = i.SiteID
JOIN Actions a ON a.ItemID = i.ItemID
JOIN ActionTypes at ON a.ActionTypeID = at.ActionTypeID
JOIN Sites s ON cs.SiteID = s.SiteID
WHERE a.DateAndTime BETWEEN #startDate AND #endDate
AND i.ItemTypeID = #itemTypeID
AND at.ActionTypeName IN ( 'Print view','Full view','Email enquiry')
AND ((#customerID IS NULL) OR (cs.CustomerID = #customerID))
AND ((#siteID = -1) OR (cs.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
GROUP BY at.ActionTypeName,i.ItemID
)
SELECT
a.Active
,a.ParentClass
,a.Classification
,a.Variant
,FV."Full Views"
,PV."Print Views"
,EE."Email Enquiries"
,a.ItemRef
,a.SiteID
FROM (SELECT DISTINCT
i.ItemID,
,i.ItemRef
,i.SiteID
,i.ParentClass
,i.Classification
,i.Summary AS "Variant"
,i.Active
FROM Items i
JOIN Actions a ON a.ItemID = i.ItemID
JOIN ActionTypes at ON a.ActionTypeID = at.ActionTypeID
WHERE i.ItemTypeID = 1
AND a.DateAndTime BETWEEN #startDate AND #endDate
AND at.ActionTypeName IN ('Full view', 'Print view', 'Email enquiry')
AND ((#siteID = -1) OR (i.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
) a
LEFT JOIN CustomerSitesCounts FV ON a.ItemID = FV.ItemID AND FV.ActionTypeName='Full view'
LEFT JOIN CustomerSitesCounts PV ON a.ItemID = PV.ItemID AND PV.ActionTypeName='Print view'
LEFT JOIN CustomerSitesCounts EE ON a.ItemID = EE.ItemID AND EE.ActionTypeName='Email enquiry'
UNION
SELECT '0','','','','','','','',''

Problems:
"catch-all" type queries cannot be optimized. Solution: parametrized dynamic SQL.
UNION forces sorting. Solution: use UNION ALL instead (unless you really need the implicit DISTINCT that it forces).
This should be the fastest solution:
DECLARE #sql AS VARCHAR(MAX)
SELECT #sql = '
;WITH cteCommon as (
SELECT
i.ItemID,
,i.ItemRef
,i.SiteID
,i.ParentClass
,i.Classification
,i.Summary
,i.Active
,at.ActionTypeName
FROM Items i
JOIN Actions a ON a.ItemID = i.ItemID
JOIN ActionTypes at ON a.ActionTypeID = at.ActionTypeID
WHERE at.ActionTypeName IN (''Full view'', ''Print view'', ''Email enquiry'')
--NOTE: if you max-out this date range, then you mant want to exclude it also, RBarryYoung
AND a.DateAndTime BETWEEN #startDate AND #endDate
'
+ CASE #siteid WHEN -1 THEN '' ELSE 'AND (i.SiteID = #siteID)
' END
+ CASE #parentClass WHEN '%' THEN '' ELSE 'AND (i.ParentClass = #parentClass)
' END
+ CASE #class WHEN '%' THEN '' ELSE 'AND (i.classification = #class)
' END
+ '
)
, cteA as (
SELECT DISTINCT
i.ItemID,
,i.ItemRef
,i.SiteID
,i.ParentClass
,i.Classification
,i.Summary AS "Variant"
,i.Active
FROM cteA as i
WHERE i.ItemTypeID = 1
)
, cteCountViews AS (
SELECT
i.ItemID
,i.ActionType
,COUNT(*) AS "ViewCount"
FROM cteCommon i
JOIN CustomerSites cs ON cs.SiteID = i.SiteID
JOIN Sites s ON cs.SiteID = s.SiteID
WHERE i.ItemTypeID = #itemTypeID
'
+ CASE WHEN #customerid IS NULL THEN '' ELSE '(cs.CustomerID = #customerID)' END
+ '
GROUP BY i.ItemID
,i.ActionType
)
SELECT
a.Active
,a.ParentClass
,a.Classification
,a.Variant
,FV."Full Views"
,PV."Print Views"
,EE."Email Enquiries"
,a.ItemRef
,a.SiteID
FROM cteA AS a
LEFT JOIN (
SELECT i.ItemID, ViewCount AS "Full Views"
FROM cteCountViews i
WHERE i.ActionTypeName = ''Full view''
) FV ON a.ItemID = FV.ItemID
LEFT JOIN (
SELECT i.ItemID, ViewCount AS "Print Views"
FROM cteCountViews i
WHERE i.ActionTypeName = ''Print view''
) PV ON a.ItemID = PV.ItemID
LEFT JOIN (
SELECT i.ItemID, ViewCount AS "Email Enquiries"
FROM cteCountViews i
WHERE i.ActionTypeName = ''Email enquiry''
) EE ON a.ItemID = EE.ItemID
UNION ALL
SELECT ''0'','''','''','''','''','''','''','''',''''
'
EXEC sp_ExecuteSQL #sql
,'#startdate DATETIME,#enddate DATETIME,#siteid INT,#parentclass VARCHAR(MAX),#class VARCHAR(MAX),#itemtypeid INT,#customerid INT'
, #startdate, #enddate, #siteid, #parentclass, #class, #itemtypeid, #customerid
Note: your use of wildcards on some of thes (class, sites, etc.) with JOINs is likely to cause cross-multiplication of your source rows and enormous result sets.

Another option is to setup either table variable or a temporary #Table to hold the results. Add a unique constraint on the ItemID.
Break apart the sub-equeries into separate steps. First INSERT the records you want to count into this table. Run separate UPDATEs on the data for each view type-- UPDATE the Print View count, UPDATE the Full View count, UPDATE the Email Enquiry count. Then return the results. If necessary, split apart the OR conditions into separate queries.
This approach runs through your data several times, but it avoids LEFT JOINs and sub-queries that aren't indexed.
In our app, having multiple steps seems to perform better than one very complex query. Your results may vary.

First of all, you can avoid having a copy of 3 absolutely the same sub-queries buy having a generic one and use WITH statement so that you can reuse it.
Then, why doing a subselect when not really required.
Then again, remove some joins (and therefore DISTINCTs) that you do not need.
And you get something along these lines (not tested, naturally):
DECLARE #itemTypeID INT
SELECT #itemTypeID=ItemTypeID FROM dbo.ItemTypes WHERE ItemTypeName = 'Advert'
BEGIN
WITH ItemTypeSummary
AS (SELECT i.ItemID,
at.ActionTypeName,
COUNT(*) AS CNT
FROM Items i
JOIN Actions a
ON a.ItemID = i.ItemID
JOIN ActionTypes at
ON a.ActionTypeID = at.ActionTypeID
AND at.ActionTypeName IN ('Full view', 'Print view', 'Email enquiry')
WHERE a.DateAndTime BETWEEN #startDate AND #endDate
--// not sure you need those below as they are all part of Items filter anyways in the main query
/*
AND i.ItemTypeID = #itemTypeID
AND ((#customerID IS NULL) OR (cs.CustomerID = #customerID))
AND ((#siteID = -1) OR (cs.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
*/
GROUP BY i.ItemID,
at.ActionTypeName
)
SELECT DISTINCT i.ItemID,
i.ItemRef,
i.SiteID,
i.ParentClass,
i.Classification,
i.Summary AS "Variant",
i.Active,
FV.CNT AS "Full views",
PV.CNT AS "Print views",
EE.CNT AS "Email enquiries"
FROM Items i
JOIN CustomerSites cs
ON cs.SiteID = i.SiteID
LEFT JOIN ItemTypeSummary FV
ON i.ItemID = FV.ItemID
AND FV.ActionTypeName = 'Full view'
LEFT JOIN ItemTypeSummary PV
ON i.ItemID = PV.ItemID
AND PV.ActionTypeName = 'Print view'
LEFT JOIN ItemTypeSummary EE
ON i.ItemID = EE.ItemID
AND EE.ActionTypeName = 'Email enquiry'
WHERE i.ItemTypeID = #itemTypeID
AND ((#customerID IS NULL) OR (cs.CustomerID = #customerID))
AND ((#siteID = -1) OR (i.SiteID = #siteID))
AND ((#parentClass = '%') OR (i.ParentClass = #parentClass))
AND ((#class = '%') OR (i.classification = #class))
END
Also keep in mind that doing a trick of filter ALL or specific item that you have with those OR statements is not really cool if you can avoid them, because SQLServer will not be able to generate optimal execution plan.

Rewritten:
WITH base AS (
SELECT i.ItemID,
i.ItemRef,
i.SiteID,
i.ParentClass,
i.Classification,
i.Summary,
i.Active,
i.ItemTypeID,
at.ActionTypeName
FROM ITEMS i
JOIN ACTIONS a ON a.ItemID = i.ItemID
JOIN ACTIONTYPES at ON at.ActionTypeID = a.ActionTypeID
WHERE a.DateAndTime BETWEEN #startDate AND #endDate
AND (#siteID = -1 OR i.SiteID = #siteID)
AND (#parentClass = '%' OR i.ParentClass = #parentClass)
AND (#class = '%' OR i.classification = #class)),
items AS (
SELECT b.ItemID,
b.ItemRef,
b.SiteID,
b.ParentClass,
b.Classification,
b.Summary AS "Variant",
b.Active
FROM base b
WHERE b.itemtypeid = 1
AND b.actiontypename IN ('Full view', 'Print view', 'Email enquiry')
GROUP BY i.ItemID, i.ItemRef, i.SiteID, i.ParentClass, i.Classification, i.Summary, i.Active),
full_views AS (
SELECT b.ItemID,
COUNT(*) AS num_full_Views
FROM base b
JOIN CUSTOMERSITES cs ON cs.siteid = b.siteid
JOIN SITES s ON s.siteid = b.siteid
WHERE b.itemtypeid = #itemTypeID
AND b.ActionTypeName = 'Full view'
AND (#customerID IS NULL OR cs.CustomerID = #customerID)
GROUP BY b.itemid),
print_views AS (
SELECT b.ItemID,
COUNT(*) AS num_print_views
FROM base b
JOIN CUSTOMERSITES cs ON cs.siteid = b.siteid
JOIN SITES s ON s.siteid = b.siteid
WHERE b.itemtypeid = #itemTypeID
AND b.ActionTypeName = 'Print view'
AND (#customerID IS NULL OR cs.CustomerID = #customerID)
GROUP BY b.itemid),
email_queries AS (
SELECT b.ItemID,
COUNT(*) AS num_email_enquiries
FROM base b
JOIN CUSTOMERSITES cs ON cs.siteid = b.siteid
JOIN SITES s ON s.siteid = b.siteid
WHERE b.itemtypeid = #itemTypeID
AND b.ActionTypeName = 'Email enquiry'
AND (#customerID IS NULL OR cs.CustomerID = #customerID)
GROUP BY b.itemid)
SELECT a.Active,
a.ParentClass,
a.Classification,
a.Variant,
ISNULL(fv.num_full_Views, 0) AS "Full Views",
ISNULL(pv.num_print_views, 0) AS "Print Views",
ISNULL(ee.num_email_enquiries, 0) AS "Email Enquiries",
a.ItemRef,
a.SiteID
FROM items a
LEFT JOIN full_views fv ON fv.itemid = a.itemid
LEFT JOIN print_views pv ON pv.itemid = a.itemid
LEFT JOIN email_queries ee ON ee.itemid = a.itemid
To get better performance, I'd convert this to dynamic SQL in order to remove the parameter checks like these:
AND (#siteID = -1 OR i.SiteID = #siteID)
...because of the negative impact on sargability.

Related

SQL Query to provide a count of rows on sales order

Hoping you can help.
We have an app that displays a grid. We can add custom fields to the grid using subqueries with which I am struggling. The main grid query looks like this.
SELECT TOP 300000
'' AS alloc_status
,'' AS stock_status
,wo_description
,wo_quantity
,wo_number
,wo_pwos_id
,vad_variant_code
,oh_order_number
,oh_datetime
,ohd_dm_id
,ohd_customer_name
,vad_description AS vad_description_Condition
,vad_variant_code AS vad_variant_code_Condition
,wo_id AS key_id
FROM works_order
LEFT OUTER JOIN works_order_analysis
ON works_order_analysis.woa_wo_id = works_order.wo_id
LEFT OUTER JOIN works_order_process
ON works_order_process.wop_id = works_order.wo_current_wop_id
LEFT OUTER JOIN works_order_process_analysis
ON works_order_process_analysis.wopa_wop_id = works_order_process.wop_id
INNER JOIN variant_detail
ON variant_detail.vad_id = works_order.wo_vad_id
LEFT OUTER JOIN works_order_total
ON works_order.wo_id = works_order_total.wot_wo_id
LEFT JOIN order_line_item
ON oli_id = wo_oli_id
LEFT JOIN order_header
ON oh_id = oli_oh_id
LEFT JOIN job_number
ON jn_id = wo_jn_id
LEFT JOIN works_order_process_subcontract_analysis
ON wopsa_wop_id = wop_id
LEFT JOIN order_header_detail
ON ohd_oh_id = oh_id
LEFT JOIN customer_detail
ON cd_id = oh_cd_id
WHERE ((cd_ow_account = 'NOTHS')
AND (wo_pwos_id = 1)
OR (cd_ow_account = 'EBAY')
AND (wo_pwos_id = 1)
OR (cd_ow_account = '4008')
AND (wo_pwos_id = 1)
OR (cd_ow_account = 'TRUSCA')
AND (wo_pwos_id = 1))
AND ((wo_required_datetime <= '2016-12-24 23:59:59'
OR wo_required_datetime IS NULL)
AND (wo_wos_id <> 4)
AND (wo_kit = 0))
The subquery is to basically provide a column that looks at the sales order and returns a count of row within that order. When I do this I get the following:
There was a problem retrieving the data:
subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, < etc
or when the subquery is used as an expression
Below is the query complete with subquery. If anyone can help that would be amazing
SELECT TOP 300000
'' AS alloc_status
,'' AS stock_status
,wo_description
,wo_quantity
,wo_number
,wo_pwos_id
,vad_variant_code
,oh_order_number
,oh_datetime
,ohd_dm_id
,ohd_customer_name
,(SELECT
COUNT(order_line_item.oli_id) AS 'Count'
FROM dbo.order_line_item
INNER JOIN dbo.order_header
ON order_line_item.oli_oh_id = order_header.oh_id
INNER JOIN dbo.variant_detail
ON order_line_item.oli_vad_id = variant_detail.vad_id
INNER JOIN dbo.variant_setting
ON variant_setting.vas_vad_id = variant_detail.vad_id
WHERE variant_setting.vas_manufactured_variant = 1
GROUP BY order_header.oh_order_number)
AS CustomLineCount
,vad_description AS vad_description_Condition
,vad_variant_code AS vad_variant_code_Condition
,wo_id AS key_id
FROM works_order
LEFT OUTER JOIN works_order_analysis
ON works_order_analysis.woa_wo_id = works_order.wo_id
LEFT OUTER JOIN works_order_process
ON works_order_process.wop_id = works_order.wo_current_wop_id
LEFT OUTER JOIN works_order_process_analysis
ON works_order_process_analysis.wopa_wop_id = works_order_process.wop_id
INNER JOIN variant_detail
ON variant_detail.vad_id = works_order.wo_vad_id
LEFT OUTER JOIN works_order_total
ON works_order.wo_id = works_order_total.wot_wo_id
LEFT JOIN order_line_item
ON oli_id = wo_oli_id
LEFT JOIN order_header
ON oh_id = oli_oh_id
LEFT JOIN job_number
ON jn_id = wo_jn_id
LEFT JOIN works_order_process_subcontract_analysis
ON wopsa_wop_id = wop_id
LEFT JOIN order_header_detail
ON ohd_oh_id = oh_id
LEFT JOIN customer_detail
ON cd_id = oh_cd_id
WHERE ((cd_ow_account = 'NOTHS')
AND (wo_pwos_id = 1)
OR (cd_ow_account = 'EBAY')
AND (wo_pwos_id = 1)
OR (cd_ow_account = '4008')
AND (wo_pwos_id = 1)
OR (cd_ow_account = 'TRUSCA')
AND (wo_pwos_id = 1))
AND ((wo_required_datetime <= '2016-12-24 23:59:59'
OR wo_required_datetime IS NULL)
AND (wo_wos_id <> 4)
AND (wo_kit = 0))
Try the below query, it may helps you
SELECT TOP 300000
'' AS alloc_status
,'' AS stock_status
,wo_description
,wo_quantity
,wo_number
,wo_pwos_id
,vad_variant_code
,oh_order_number
,oh_datetime
,ohd_dm_id
,ohd_customer_name
,(SELECT
COUNT(order_line_item.oli_id) AS 'Count'
FROM dbo.order_line_item
INNER JOIN dbo.order_header
ON order_line_item.oli_oh_id = order_header.oh_id
INNER JOIN dbo.variant_detail
ON order_line_item.oli_vad_id = variant_detail.vad_id
INNER JOIN dbo.variant_setting
ON variant_setting.vas_vad_id = variant_detail.vad_id
WHERE variant_setting.vas_manufactured_variant = 1
and order_header.oh_order_number=ordhead.oh_order_number)
AS CustomLineCount
,vad_description AS vad_description_Condition
,vad_variant_code AS vad_variant_code_Condition
,wo_id AS key_id
FROM works_order
LEFT OUTER JOIN works_order_analysis
ON works_order_analysis.woa_wo_id = works_order.wo_id
LEFT OUTER JOIN works_order_process
ON works_order_process.wop_id = works_order.wo_current_wop_id
LEFT OUTER JOIN works_order_process_analysis
ON works_order_process_analysis.wopa_wop_id = works_order_process.wop_id
INNER JOIN variant_detail
ON variant_detail.vad_id = works_order.wo_vad_id
LEFT OUTER JOIN works_order_total
ON works_order.wo_id = works_order_total.wot_wo_id
LEFT JOIN order_line_item
ON oli_id = wo_oli_id
LEFT JOIN order_header ordhead
ON oh_id = oli_oh_id
LEFT JOIN job_number
ON jn_id = wo_jn_id
LEFT JOIN works_order_process_subcontract_analysis
ON wopsa_wop_id = wop_id
LEFT JOIN order_header_detail
ON ohd_oh_id = oh_id
LEFT JOIN customer_detail
ON cd_id = oh_cd_id
WHERE ((cd_ow_account = 'NOTHS')
AND (wo_pwos_id = 1)
OR (cd_ow_account = 'EBAY')
AND (wo_pwos_id = 1)
OR (cd_ow_account = '4008')
AND (wo_pwos_id = 1)
OR (cd_ow_account = 'TRUSCA')
AND (wo_pwos_id = 1))
AND ((wo_required_datetime <= '2016-12-24 23:59:59'
OR wo_required_datetime IS NULL)
AND (wo_wos_id <> 4)
AND (wo_kit = 0))
Change your sub query to this:
(SELECT
COUNT(order_line_item.oli_id) AS 'Count'
FROM dbo.order_line_item
INNER JOIN dbo.order_header
ON order_line_item.oli_oh_id = order_header.oh_id
INNER JOIN dbo.variant_detail
ON order_line_item.oli_vad_id = variant_detail.vad_id
INNER JOIN dbo.variant_setting
ON variant_setting.vas_vad_id = variant_detail.vad_id
WHERE variant_setting.vas_manufactured_variant = 1
AND order_header.oh_order_number = OUTER_QUERY_TABLE.SAME_FIELD )
I've added this line:
AND order_header.oh_order_number = OUTER_QUERY_TABLE.SAME_FIELD
You have to make this query correlated , so add the condition to match the record to the outer query . I didn't know which field it is.
You could also optimize your WHERE and make it more readable with IN() :
WHERE wo_pwos_id = 1
AND cd_ow_account IN('NOTHS','EBAY','4008','TRUSCA')
AND (wo_required_datetime <= '2016-12-24 23:59:59' OR wo_required_datetime IS NULL)
AND wo_wos_id <> 4
AND wo_kit = 0

SQL Server Left Join issue

When I execute this:
SELECT i.descricao,
sii.quantidade quantidade_inicial,
sii.quantidade * i.valor valor_inicial
FROM itensvalor i
LEFT JOIN saldoinicialprodutositens sii
ON i.id = sii.iditemvalor
AND Isnull(sii.datahoraexclusao, '') = ''
WHERE sii.idSaldoInicialProduto IN (SELECT id FROM saldoInicialProdutos WHERE idmovimentodiario = 76)
It returns this:
And when I execute this:
SELECT i.descricao,
sfi.quantidade quantidade_final,
sfi.quantidade * i.valor valor_final
FROM itensvalor i
LEFT JOIN saldofinalprodutositens sfi
ON i.id = sfi.iditemvalor
AND Isnull(sfi.datahoraexclusao, '') = ''
WHERE sfi.idSaldoFinalProduto IN (SELECT id FROM saldoFinalProdutos WHERE idmovimentodiario = 76)
It returns this: (with the R$100,00)
But, finally, when I try to join the 2 tables, the value that refers to R$100,00 not appears.
I'm trying to join using this query:
SELECT i.descricao,
sii.quantidade quantidade_inicial,
sii.quantidade * i.valor valor_inicial,
sfi.quantidade quantidade_final,
sfi.quantidade * i.valor valor_final
FROM itensvalor i
LEFT JOIN saldofinalprodutositens sfi
ON i.id = sfi.iditemvalor
AND Isnull(sfi.datahoraexclusao, '') = ''
LEFT JOIN saldoinicialprodutositens sii
ON i.id = sii.iditemvalor
AND Isnull(sii.datahoraexclusao, '') = ''
WHERE sii.idSaldoInicialProduto IN (SELECT id FROM saldoInicialProdutos WHERE idmovimentodiario = 76)
AND sfi.idSaldoFinalProduto IN (SELECT id FROM saldoFinalProdutos WHERE idmovimentodiario = 76)
You need to move your WHERE inside the ON clause for that to work:
SELECT i.descricao,
sii.quantidade quantidade_inicial,
sii.quantidade * i.valor valor_inicial,
sfi.quantidade quantidade_final,
sfi.quantidade * i.valor valor_final
FROM itensvalor i
LEFT JOIN saldofinalprodutositens sfi
ON i.id = sfi.iditemvalor
AND Isnull(sfi.datahoraexclusao, '') = ''
and sfi.idSaldoFinalProduto IN (SELECT id FROM saldoFinalProdutos WHERE idmovimentodiario = 76)
LEFT JOIN saldoinicialprodutositens sii
ON i.id = sii.iditemvalor
AND Isnull(sii.datahoraexclusao, '') = ''
and sii.idSaldoInicialProduto IN (SELECT id FROM saldoInicialProdutos WHERE idmovimentodiario = 76)
Otherwise, your sfi.idSaldoFinalProduto is null, and the row is omited.

SQL Not In sub Query

I have a situation where I need to select prospects (relationship of 'P') from our MEMBERS table for a particular site. However, when I select these people, I need to exclude two groups of those prospects. Each of these groups have entries in a separate table named MEMBERUDFS. Now not all entries in the MEMBERS table will have an entry in the MEMBERUDFS table. Therefore, I tried to exclude those groups from the main query by using two "NOT IN" subqueries but that does not seem to work. I am able to isolate the exclusion group with the following queries:
SELECT MEMBERS_1.memid
FROM MEMBERS AS MEMBERS_1
JOIN SITES AS SITES_1 ON MEMBERS_1.siteid = SITES_1.siteid
LEFT JOIN MEMBERUDFS AS MEMBERUDFS_1 ON MEMBERS_1.memid = MEMBERUDFS_1.memid
JOIN MEMBERUDFSETUPS AS MEMBERUDFSETUPS_1 ON MEMBERUDFS_1.udfid = MEMBERUDFSETUPS_1.udfid
WHERE (MEMBERS_1.relationship = 'P')
AND (MEMBERS_1.email <> '')
AND (MEMBERUDFS_1.udfid = '26')
AND (MEMBERUDFS_1.udfvalue = 'No')
AND (MEMBERS_1.entrydate < DATEADD(DAY, -6, GETDATE()))
AND (MEMBERS_1.siteid = #rvSite)
And This query:
SELECT MEMBERS_2.memid
FROM MEMBERS AS MEMBERS_2
JOIN SITES AS SITES_2 ON MEMBERS_2.siteid = SITES_2.siteid
LEFT JOIN MEMBERUDFS AS MEMBERUDFS_2 ON MEMBERS_2.memid = MEMBERUDFS_2.memid
JOIN MEMBERUDFSETUPS AS MEMBERUDFSETUPS_2 ON MEMBERUDFS_2.udfid = MEMBERUDFSETUPS_2.udfid
WHERE (MEMBERS_2.relationship = 'P')
AND (MEMBERS_2.email <> '')
AND (MEMBERUDFS_2.udfid = '25')
AND (MEMBERUDFS_2.udfvalue = 'Yes')
AND (MEMBERS_2.entrydate < DATEADD(DAY, -21, GETDATE()))
AND (MEMBERS_2.siteid = #rvSite)
SO THIS IS WHAT I HAD PUT TOGETHER, BUT IT DOES NOT ELIMINATE THE TWO SUBQUERY GROUPS:
SELECT MEMBERS.scancode,
MEMBERS.memid,
MEMBERS.fname,
MEMBERS.lname,
MEMBERS.relationship,
MEMBERS.status,
MEMBERS.email,
MEMBERS.entrydate,
SITES.sitename
FROM MEMBERS
JOIN SITES ON MEMBERS.siteid = SITES.siteid
WHERE (MEMBERS.relationship = 'P')
AND (MEMBERS.email <> '')
AND (MEMBERS.siteid = #rvSite)
AND (MEMBERS.memid NOT IN (
SELECT MEMBERS_1.memid
FROM MEMBERS AS MEMBERS_1
JOIN SITES AS SITES_1 ON MEMBERS_1.siteid = SITES_1.siteid
LEFT JOIN MEMBERUDFS AS MEMBERUDFS_1 ON MEMBERS_1.memid = MEMBERUDFS_1.memid
JOIN MEMBERUDFSETUPS AS MEMBERUDFSETUPS_1 ON MEMBERUDFS_1.udfid = MEMBERUDFSETUPS_1.udfid
WHERE (MEMBERS_1.relationship = 'P')
AND (MEMBERS_1.email <> '')
AND (MEMBERUDFS_1.udfid = '26')
AND (MEMBERUDFS_1.udfvalue = 'No')
AND (MEMBERS_1.entrydate < DATEADD(DAY, -6, GETDATE()))
AND (MEMBERS_1.siteid = #rvSite))
)
AND (MEMBERS.memid NOT IN (
SELECT MEMBERS_2.memid
FROM MEMBERS AS MEMBERS_2
JOIN SITES AS SITES_2 ON MEMBERS_2.siteid = SITES_2.siteid
LEFT JOIN MEMBERUDFS AS MEMBERUDFS_2 ON MEMBERS_2.memid = MEMBERUDFS_2.memid
JOIN MEMBERUDFSETUPS AS MEMBERUDFSETUPS_2 ON MEMBERUDFS_2.udfid = MEMBERUDFSETUPS_2.udfid
WHERE (MEMBERS_2.relationship = 'P')
AND (MEMBERS_2.email <> '')
AND (MEMBERUDFS_2.udfid = '25')
AND (MEMBERUDFS_2.udfvalue = 'Yes')
AND (MEMBERS_2.entrydate < DATEADD(DAY, -21, GETDATE()))
AND (MEMBERS_2.siteid = #rvSite))
)
Any help would be appreciated
You just need to play with your where clause...
AND (MEMBERS.memid NOT IN (
SELECT MEMBERS_1.memid
FROM MEMBERS AS MEMBERS_1
JOIN SITES AS SITES_1 ON MEMBERS_1.siteid = SITES_1.siteid
LEFT JOIN MEMBERUDFS AS MEMBERUDFS_1 ON MEMBERS_1.memid = MEMBERUDFS_1.memid
JOIN MEMBERUDFSETUPS AS MEMBERUDFSETUPS_1 ON MEMBERUDFS_1.udfid = MEMBERUDFSETUPS_1.udfid
WHERE (MEMBERS_1.relationship = 'P')
AND (MEMBERS_1.email <> '')
AND (MEMBERS_1.siteid = #rvSite)
AND (((MEMBERUDFS_1.udfid = '26') AND (MEMBERUDFS_1.udfvalue = 'No') AND (MEMBERS_1.entrydate < DATEADD(DAY, -6, GETDATE()))) OR
((MEMBERUDFS_1.udfid = '25') AND (MEMBERUDFS_1.udfvalue = 'Yes') AND (MEMBERS_1.entrydate < DATEADD(DAY, -21, GETDATE()))))

Optimize query with sql server

I have the following query .its execution took 15 min .It is too much slowly .
Is there a way to optimize it ?
Query
SELECT
Id,Fees,WeekOfMonth,CONVERT(NVARCHAR(MAX), StartDate, 103) AS StartDate,CONVERT(NVARCHAR(MAX), EndDate, 103) AS EndDate,'Temp Fees ' AS FeesName
,#MonthName AS [MonthName]
INTO ##TempFeesMonthly
from
(
SELECT DISTINCT
1 as Id,sum(((CNTI_THPAYE *(CNTI_THFACT / CNTI_THPAYE)) *
(CASE WHEN CNTI_DURHEBDO IS NULL THEN 1 ELSE CNTI_DURHEBDO/5 END)*#NumberOfDays)) AS Fees,WeekOfMonth,StartDate,EndDate
FROM SCHHAYS.dbo.WTVTAT TAT
LEFT JOIN
SCHHAYS.dbo.WTTIEINT INT
ON (
TAT.TIE_ID = INT.TIE_ID
)
AND (
TAT.VTAT_IORDRE = INT.TIEI_ORDRE
)
LEFT JOIN
SCHHAYS.dbo.PYCONTRAT CC
ON TAT.PER_ID = CC.PER_ID
AND TAT.CNT_ID = CC.CNT_ID
LEFT JOIN
SCHHAYS.dbo.CMTIERS T
ON TAT.TIE_ID = T.TIE_ID
LEFT JOIN
SCHHAYS.dbo.WTMISS M
ON CC.PER_ID = M.PER_ID
AND CC.CNT_ID = M.CNT_ID
LEFT JOIN
##WTCNTIWeek COT1
ON M.PER_ID = COT1.PER_ID
AND M.CNT_ID = COT1.CNT_ID
INNER JOIN
SCHHAYS.dbo.WTPRH AS PRH
ON M.PER_ID = PRH.PER_ID
AND M.CNT_ID = PRH.CNT_ID
AND M.TIE_ID = PRH.TIE_ID
INNER JOIN
##tempStartEndWeekDates AS Tsed
ON PRH_DTEDEBSEM>=Tsed.StartDate
AND PRH_DTEFINSEM<=Tsed.EndDate
LEFT JOIN
SCHHAYS.dbo.WTSCCT C
ON CC.RGPCNT_ID = C.RGPCNT_ID
AND CC.PER_ID = C.PER_ID
AND CC.CNT_ID = C.CNT_ID
INNER JOIN
##TempHaysStaffWeek HF
ON C.VAPO_CODE = HF.onetouch COLLATE Latin1_General_CI_AS
group by
WeekOfMonth,StartDate,EndDate)t
--CREATE INDEX IDX_TempFeesMonthly ON ##TempFeesMonthly(WeekOfMonth)
--Calcul Temp Margin
UNION ALL
--INSERT INTO ##TempFeesMonthly(Id,Fees,WeekOfMonth,StartDate,EndDate,FeesName,[MonthName])
SELECT
2, sum(Fees) AS Fees ,WeekOfMonth,CONVERT(NVARCHAR(MAX), StartDate, 103) AS StartDate,CONVERT(NVARCHAR(MAX), EndDate, 103) AS EndDate,'Temp Margin ' AS FeesName
,#MonthName AS [MonthName]
from
(
SELECT DISTINCT
sum((CASE WHEN CNTI_DURHEBDO IS NULL THEN 1 ELSE CNTI_DURHEBDO/5 END)*#NumberOfDays)-(CNTI_THPAYE *(CASE WHEN CNTI_DURHEBDO IS NULL THEN 1 ELSE CNTI_DURHEBDO/5 END)*1.453*1.21*#NumberOfDays) AS Fees,WeekOfMonth,StartDate,EndDate
FROM SCHHAYS.dbo.WTVTAT TAT
LEFT JOIN
SCHHAYS.dbo.WTTIEINT INT
ON (
TAT.TIE_ID = INT.TIE_ID
)
AND (
TAT.VTAT_IORDRE = INT.TIEI_ORDRE
)
LEFT JOIN
SCHHAYS.dbo.PYCONTRAT CC
ON TAT.PER_ID = CC.PER_ID
AND TAT.CNT_ID = CC.CNT_ID
LEFT JOIN
SCHHAYS.dbo.CMTIERS T
ON TAT.TIE_ID = T.TIE_ID
LEFT JOIN
SCHHAYS.dbo.WTMISS M
ON CC.PER_ID = M.PER_ID
AND CC.CNT_ID = M.CNT_ID
LEFT JOIN
##WTCNTIWeek COT1
ON M.PER_ID = COT1.PER_ID
AND M.CNT_ID = COT1.CNT_ID
INNER JOIN
SCHHAYS.dbo.WTPRH AS PRH
ON M.PER_ID = PRH.PER_ID
AND M.CNT_ID = PRH.CNT_ID
AND M.TIE_ID = PRH.TIE_ID
INNER JOIN
##tempStartEndWeekDates AS Tsed
ON PRH_DTEDEBSEM>=Tsed.StartDate
AND PRH_DTEFINSEM<=Tsed.EndDate
LEFT JOIN
SCHHAYS.dbo.WTSCCT C
ON CC.RGPCNT_ID = C.RGPCNT_ID
AND CC.PER_ID = C.PER_ID
AND CC.CNT_ID = C.CNT_ID
INNER JOIN
##TempHaysStaffWeek HF
ON C.VAPO_CODE = HF.onetouch COLLATE Latin1_General_CI_AS
group by
WeekOfMonth,StartDate,EndDate,CNTI_THPAYE,CNTI_DURHEBDO)t
GROUP BY WeekOfMonth,StartDate,EndDate

CURSOR is not displaying data/result set

I've tried everything I could and i still can't get my cursor to display data. here is my code:
DECLARE
CURSOR SL_Cur IS
select *
from (
select
cdav.bank_id,
ent.bank_desc Bank_Description,
cdav.sol_id,
sol.sol_desc SOL_Description,
cdav.gl_sub_head_code GLSH_Code,
decode(cdav.gl_sub_head_code,
'10301',1,
'10403',2, '60403',2, '10501',2, '60501',2, '10502',2,
'10503',2, '10504',2, '10505',2, '10507',2, '10509',2,
'60509',2, '10511',2, '10518',2, '60518',2, '10523',2,
'60523',2, '10551',2, '10552',2, '10553',2, '10554',2,
'10555',2, '10557',2, '10559',2, '10561',2, '10568',2,
'10573',2,
'12336',3, '62336',3, '10401',3, '60402',3, 4 ) GLSH_SET ,
gsh.gl_sub_head_desc GLSH_Name,
case
when (cast(substr(cdav.gl_sub_head_code,0,1) as int) >= 1
and cast(substr(cdav.gl_sub_head_code,0,1) as int) <= 5)
then 'R'
when cast(substr(cdav.gl_sub_head_code,0,1) as int) = 0
or (cast(substr(cdav.gl_sub_head_code,0,1) as int) >= 6
and cast(substr(cdav.gl_sub_head_code,0,1) as int) <= 9)
then 'F'
end book_type,
gam.foracid account_number,
gam.acct_name,
cdav.tran_crncy_code Tran_Currency,
cdav.value_date,
cdav.tran_date Transaction_Date,
cdav.gl_date,
cdav.tran_particular,
rank() over(partition by gam.foracid order by eab.eod_date desc) eod_date_rank,
eab.eod_date,
case when
(select tran_date_bal from tbaadm.eab
where eab.eod_date = (select max(eab.eod_date) from tbaadm.eab
where cdav.acid = eab.acid and eab.eod_date < '28-MAY-2013')
and cdav.acid = eab.acid
and cdav.bank_id = eab.bank_id) is not null
then (select tran_date_bal from tbaadm.eab
where eab.eod_date = (select max(eab.eod_date) from tbaadm.eab
where cdav.acid = eab.acid and eab.eod_date < '28-MAY-2013')
and cdav.acid = eab.acid
and cdav.bank_id = eab.bank_id)
else 0
end beg_tran_date_bal,
(select tran_date_bal from tbaadm.eab eab
where eod_date = (select max(eab.eod_date) from tbaadm.eab eab
where cdav.acid = eab.acid and eab.eod_date <= '28-MAY-2013')
and cdav.acid = eab.acid
and cdav.bank_id = eab.bank_id) end_tran_date_bal,
ott.ref_num OAP_Ref_No,
trim(cdav.tran_id) Transaction_ID,
--cdav.dth_init_sol_id Initiating_SOL_ID,
'PCC_Code',
cdav.tran_rmks Tran_Remarks,
case
when (cdav.part_tran_type = 'D')
then (cdav.tran_amt)
end dr_amount,
case
when (cdav.part_tran_type = 'C')
then (cdav.tran_amt)
end cr_amount
from tbaadm.ctd_dtd_acli_view cdav
left outer join tbaadm.gam
on cdav.bank_id = gam.bank_id and cdav.acid = gam.acid
left outer join tbaadm.gsh
on gam.bank_id = gsh.bank_id and gam.sol_id = gsh.sol_id
and gam.gl_sub_head_code = gsh.gl_sub_head_code
and gam.acct_crncy_code = gsh.crncy_code
left outer join tbaadm.sol
on cdav.bank_id = sol.bank_id and cdav.sol_id = sol.sol_id
left outer join tbaadm.eab
on gam.bank_id = eab.bank_id and gam.acid = eab.acid
left outer join tbaadm.cnc
on gam.bank_id = cnc.bank_id and gam.acct_crncy_code = cnc.crncy_code
left outer join crmuser.end ent
on cdav.bank_id = ent.bank_id
left outer join tbaadm.gct
on cdav.bank_id = gct.bank_id
left outer join tbaadm.ott
on cdav.tran_id = ott.tran_id and cdav.tran_date = ott.tran_date
and cdav.part_tran_srl_num = ott.part_tran_srl_num
and cdav.bank_id = ott.bank_id and cdav.acid = ott.acid
where
gam.acct_ownership = 'O'
and cdav.bank_id = 'CBC01'
and cdav.gl_date = '28-MAY-2013'
and (gam.gl_sub_head_code in ('10301','10403','60403',
'10501','60501','10502','10503','10504','10505',
'10507','10509','60509','10511','10518','60518',
'10523','60523','10551','10552','10553','10554',
'10555','10557','10559','10561','10568','10573',
'12336','62336','10401','60402')
or gam.acct_classification_flg in ('I','E')
)
and trim(cdav.del_flg) in ('N', null)
and trim(gam.del_flg) in ('N', null)
and trim(gsh.del_flg) in ('N', null)
and trim(sol.del_flg) in ('N', null)
and trim(cnc.del_flg) in ('N', null)
and trim(gct.del_flg) in ('N', null)
)
where
eod_date_rank = 1
and GLSH_SET = 1
order by
bank_id,
sol_id,
tran_currency,
glsh_code,
book_type desc,
account_number,
transaction_date;
in_sl_rec SL_Cur%ROWTYPE;
BEGIN
open SL_Cur;
LOOP
FETCH SL_Cur INTO in_sl_rec;
exit when SL_Cur%notfound;
END LOOP;
close SL_Cur;
END;
At first, I just need to make the CURSOR display the result set that I want to see. Next, I want to make the cursor into a sort of FOR LOOP, because there would be input parameters involved when executing the code in iReport, mainly a date range.
Cursors don't just "display" when you execute them - you have to write a little code to do that. Try adding the following lines in your loop after the exit when SL_Cur%notfound; and just before the END LOOP;
DBMS_OUTPUT.PUT_LINE('BANK_ID=' || in_sl_rec.BANK_ID ||
'Bank_Description=' || in_sl_rec.Bank_Description ||
'sol_id=' || in_sl_rec.sol_id ||
'SOL_Description=' || in_sl_rec.SOL_Description);
You can add more fields as you need to.
Share and enjoy.