SQL query is loading for long period, how it could be optimized? - sql

This is the query:
SELECT
[Code]
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY [OrderNo], [ProductNo] ORDER BY [Quantity] DESC) AS [RowNumber],
SUBSTRING(P.[ProductNo], 1, 2) AS [Code]
FROM [LESMESPRD].[FlexNet_prd].[dbo].[ORDER_DETAIL] AS OD
INNER JOIN [LESMESPRD].[FlexNet_prd].[dbo].[WIP_COMPONENT] AS WC ON [WC].[WiporderNo] = OD.[OrderNo]
AND WC.[WipOrderType] = OD.[OrderType]
AND WC.[Active] = 1
INNER JOIN [LESMESPRD].[FlexNet_prd].[dbo].[COMPONENT] AS C ON C.[ID] = WC.[ComponentID]
INNER JOIN [LESMESPRD].[FlexNet_prd].[dbo].[PRODUCT] AS P ON P.[ID] = C.[ProductID]
WHERE SUBSTRING(P.[ProductNo], 1, 2) IN ('43', '72')
) AS OrderBrandComponents
WHERE [RowNumber] = 1
Executing time is 1 minute and 16 seconds, maybe you can help me optimize it somehow? This query is just small piece of the code, but I found that exactly this part is slowing the process.
I tried to think that maybe problem is in sub select when I try to get my rownumber, from these tables that are linked servers data is executing in seconds, I think problem is with the functions. I hope that this query could be optimized.

I believe the delay is because your query is not sargable based on the SUBSTRING( P.[ProductNo], 1,2 ). The engine can not utilize an index on a function call. But by using the full column and using LIKE based on the first 2 characters Plus wild-card anything after, you get the same records, but able to use an index.
Now, because you are looking for 2 specific product type codes (43 and 72), I reversed the query to START with that table, then find orders the products were used. This may help optimize speed, especially if you have 100 orders with these products, but 1000s of orders otherwise. Thus, starting with a smaller set to begin with.
Also, you dont need all the square brackets all over. Typically, those are only used if you have a column name based on a "reserved" keyword, such as naming a column "from" which is an obvious keyword in a SQL statement. Or things that are known data types, function names, etc.
Finally indexes to help optimize this. I would ensure you have indexes on the following tables
table index
Product ( ProductNo, Id ) -- specifically this order
Component ( ProductID, Id
WIP_COMPONENT ( ComponentId, Active, WipOrderNo, WipOrderType )
ORDER_DETAIL ( OrderNo, OrderType )
SELECT
Code
FROM
(SELECT
ROW_NUMBER() OVER
(PARTITION BY OrderNo, ProductNo
ORDER BY Quantity DESC) AS RowNumber,
SUBSTRING(P.ProductNo, 1, 2) Code
FROM
LESMESPRD.FlexNet_prd.dbo.PRODUCT P
JOIN LESMESPRD.FlexNet_prd.dbo.COMPONENT C
ON P.ID = C.ProductID
JOIN LESMESPRD.FlexNet_prd.dbo.WIP_COMPONENT WC
ON C.ID = WC.ComponentID
AND WC.Active = 1
JOIN LESMESPRD.FlexNet_prd.dbo.ORDER_DETAIL OD
ON WC.WiporderNo = OD.OrderNo
AND WC.WipOrderType = OD.OrderType
WHERE
P.ProductNo like '43%'
OR P.ProductNo like '72%' ) AS OrderBrandComponents
WHERE
OrderBrandComponents.RowNumber = 1

Related

SQL Most Recent Register FROM Second Table by Id

I have 2 tables (Opportunity and Stage). I need to get each opportunity with the most recent stage by StageTypeId.
Opportunity: Id, etc
Stage: Id, CreatedOn, OpportunityId, StageTypeId.
Let's suppose I have "opportunity1" and "opportunity2" each one with many Stages added.
By passing the StageTypeId I need to get the opportunity which has this StageTypeId as most recent.
I'm trying the following query but it´s replicating the same Stage for all the Opportunities.
It seems that it's ignoring this line: "AND {Stage}.[OpportunityId] = ID"
SELECT {Opportunity}.[Id] ID,
{Opportunity}.[Name],
{Opportunity}.[PotentialAmount],
{Contact}.[FirstName],
{Contact}.[LastName],
(SELECT * FROM
(
SELECT {Stage}.[StageTypeId]
FROM {Stage}
WHERE {Stage}.[StageTypeId] = #StageTypeId
AND {Stage}.[OpportunityId] = ID
ORDER BY {Stage}.[CreatedOn] DESC
)
WHERE ROWNUM = 1) AS StageTypeId
FROM {Opportunity}
LEFT JOIN {Contact}
ON {Opportunity}.[ContactId] = {Contact}.[Id]
Thank you
Most of DBMS support fetch first clause So, you can do :
select o.*
from Opportunity o
where o.StageTypeId = (select s.StageTypeId
from Stage s
where s.OpportunityId = o.id
order by s.CreatedOn desc
fetch first 1 rows only
);
you can try below way all dbms will support
select TT*. ,o*. from
(
select s1.OpportunityId,t.StageTypeId from Stage s1 inner join
(select StageTypeId,max(CreatedOn) as createdate Stage s
group by StageTypeId
) t
on s1.StageTypeId=t.StageTypeId and s1.CreatedOn=t.createdate
) as TT inner join Opportunity o on TT.OpportunityId=o.id

How to speed up a slow update query in SQL Server 2012

I have an update query that works fine, but it is way too slow and takes over 2 minutes to complete. Is there another way I can write this query to speed it up? Here is my code thanks:
UPDATE #tmpIMDS
SET
ModelFileName = b.ModelFileName,
SendEMail = b.SendEMail
FROM
(
SELECT DISTINCT
IMDSConversionReportData.ModelNumber,
ModelFileName,
'Send Email' AS SendEmail
FROM
IMDSConversionReportData,
(
SELECT DISTINCT
ModelNumber,
Max(DateAdded) AS DateAdded
FROM
IMDSConversionReportData
GROUP BY
ModelNumber) a
WHERE
IMDSConversionReportData.ModelNumber = a.ModelNumber
AND IMDSConversionReportData.DateAdded = a.DateAdded
) b
WHERE ModelID = b.ModelNumber
Instead of hitting IMDSConversionReportData table twice to get the maximum DateAdded per ModelNumber you can generate row_number to identify maximum DateAdded per ModelNumbercolumn.
Also remove distinct when you are selecting only one non aggregate column with group by which is meaningless
Try this
;WITH cte
AS (SELECT *,
'Send Email' AS SendEmail,
Row_number()OVER(partition BY ModelNumber ORDER BY DateAdded DESC) AS rn
FROM IMDSConversionReportData)
UPDATE t
SET ModelFileName = c.ModelFileName,
SendEMail = c.SendEMail
FROM #tmpIMDS t
INNER JOIN cte c
ON t.ModelID = c.ModelNumber
Where Rn = 1
Note : Always use proper INNER JOIN syntax to join two tables instead of Old style comma separated join. We always find INNER Join syntax is more readable. Keep the filters alone in Where clause

INNER JOIN on a Sub Query

I have a list of tasks in a table called dbo.Task
In the database, each Task can have 1 or more rows in the TaskLine table.
TaskLine has a TaskID to related the Tasklines to the Task.
A TaskLine has a column called TaskHeadingTypeID
I need to return all the tasks, joined to the LAST TaskLine for that Task.
In english, I need to display a task, with the latest TaskLine heading. So, I basically need to join to the TaskLine table, like this (which, is incorrect and maybe inefficient, but hopefully shows what I am trying to do)
SELECT *
FROM #Results r
INNER JOIN (
SELECT TOP 1 TaskID, TaskHeadingTypeID FROM dbo.TaskLine
ORDER BY TaskLineID DESC
) tl
ON tl.TaskID = r.TaskID
However, the issue is, the sub query only brings back the last TaskLine row, which is incorrect.
Edit:
At the moment, it's 'Working' like the code below, but it seems highly inefficient, because for each task row, it has to run two extra queries. And they're both on the same table, just slightly different columns in that table:
(An extract of the columns in the SELECT cause)
SELECT TaskStatusID,
TaskStatus,
(SELECT TOP 1 TaskHeadingTypeID FROM dbo.TaskLine
WHERE TaskID = r.TaskID
ORDER BY TaskLineID DESC) AS TaskHeadingID,
(SELECT TOP 1 LongName FROM dbo.TaskLine tl
INNER JOIN ref.TaskHeadingType tht
ON tht.TaskHeadingTypeID = tl.TaskHeadingTypeID
WHERE TaskID = r.TaskID
ORDER BY TaskLineID DESC) AS TaskHeading,
PersonInCareID,
ICMSPartyID,
CarerID.... FROM...
EDIT:
Thanks to the ideas and comments below, I have ended up with this, using CTE:
;WITH ValidTaskLines (RowNumber, TaskID, TaskHeadingTypeID, TaskHeadingType)
AS
(SELECT
ROW_NUMBER()OVER(PARTITION BY tl.TaskID, tl.TaskHeadingTypeID ORDER BY tl.TaskLineID) AS RowNumber,
tl.TaskID,
tl.TaskHeadingTypeID,
LongName AS TaskHeadingType
FROM dbo.TaskLine tl
INNER JOIN ref.TaskHeadingType tht
ON tht.TaskHeadingTypeID = tl.TaskHeadingTypeID
)
SELECT AssignedByBusinessUserID,
BusinessUserID,
LoginName,
Comments,
r.CreateDate,
r.CreateUser,
r.Deleted,
r.Version,
IcmsBusinessUserID,
r.LastUpdateDate,
r.LastUpdateUser,
OverrrideApprovalBusinessUserID,
PlacementID,
r.TaskID,
TaskPriorityTypeID,
TaskPriorityCode,
TaskPriorityType,
TaskStatusID,
TaskStatus,
vtl.TaskHeadingTypeID AS TaskHeadingID,
vtl.TaskHeadingType AS TaskHeading,
PersonInCareID,
ICMSPartyID,
CarerID,
ICMSCarerEntityID,
StartDate,
EndDate
FROM #Results r
INNER JOIN ValidTaskLines vtl
ON vtl.TaskID = r.TaskID
AND vtl.RowNumber = 1
You could use the ROW_NUMBER() function for this:
SELECT *
FROM #Results r
INNER JOIN (SELECT TaskID
, TaskHeadingTypeID
, ROW_NUMBER()OVER(PARTITION BY TaskID, TaskHeadingTypeID ORDER BY TAskLineID DESC) RN
FROM dbo.TaskLine
) tl
ON tl.TaskID = r.TaskID
AND t1.RN = 1
The ROW_NUMBER() function assigns a number to each row. PARTITION BY is optional, but used to start the numbering over for each value in that group, ie: if you PARTITION BY Some_Date then for each unique date value the numbering would start over at 1. ORDER BY of course is used to define how the counting should go, and is required in the ROW_NUMBER() function.
You may need to adjust the PARTITION BY to suit your query, run the subquery by itself to get an idea of how the ROW_NUMBER() works.

SQL Group By Clause and Empty Entries

I have a SQL Server 2005 query that I'm trying to assemble right now but I am having some difficulties.
I have a group by clause based on 5 columns: Project, Area, Name, User, Engineer.
Engineer is coming from another table and is a one to many relationship
WITH TempCTE
AS (
SELECT htce.HardwareProjectID AS ProjectId
,area.AreaId AS Area
,hs.NAME AS 'Status'
,COUNT(*) AS Amount
,MAX(htce.DateEdited) AS DateModified
,UserEditing AS LastModifiedName
,Engineer
,ROW_NUMBER() OVER (
PARTITION BY htce.HardwareProjectID
,area.AreaId
,hs.NAME
,htce.UserEditing ORDER BY htce.HardwareProjectID
,Engineer DESC
) AS row
FROM HardwareTestCase_Execution AS htce
INNER JOIN HardwareTestCase AS htc ON htce.HardwareTestCaseID = htc.HardwareTestCaseID
INNER JOIN HardwareTestGroup AS htg ON htc.HardwareTestGroupID = htg.HardwareTestGroupId
INNER JOIN Block AS b ON b.BlockId = htg.BlockId
INNER JOIN Area ON b.AreaId = Area.AreaId
INNER JOIN HardwareStatus AS hs ON htce.HardwareStatusID = hs.HardwareStatusId
INNER JOIN j_Project_Testcase AS jptc ON htce.HardwareProjectID = jptc.HardwareProjectId AND htce.HardwareTestCaseID = jptc.TestcaseId
WHERE (htce.DateEdited > #LastDateModified)
GROUP BY htce.HardwareProjectID
,area.AreaId
,hs.NAME
,htce.UserEditing
,jptc.Engineer
)
The gist of what I want is to be able to deal with empty Engineer columns. I don't want this column to have a blank second entry (where row=2).
What I want to do:
Group the items with "row" value of 1 & 2 together.
Select the Engineer that isn't empty.
Do not deselect engineers where there is not a matching row=2.
I've tried a series of joins to try and make things work. No luck so far.
Use j_Project_Testcase PIVOT( MAX(Engineer) for Row in ( [1], [2] ) then select ISNULL( [1],[2]) to select the Engineer value
I can give you a more robust example if you set up a SQL fiddle
Try reading this: PIVOT and UNPIVOT

Pagination help in SQL

The below inner SELECT returns huge amount of rows (1000000+) and the outer SELECTs(alpha BETWEEN #startRec# AND #endRec#) is used for PAGINATION
to display data with 25 in each page.
Issue is:-This PAGINATION done below is very slow and slows the entire display of data.So could all please help me on doing this below
pagination in a BETTER WAY? COde about pagination would be best.
**I am very sorry to put in this way but i am very new to Pagination concepts and so need your help.
/*********ORIGINAL QUERY ****/
SELECT
*
FROM
(
SELECT
beta.*, rownum as alpha
FROM
(
SELECT
p.lastname, p.firstname, porg.DEPARTMENT,
porg.org_relationship,
porg.enterprise_name,
(
SELECT
count(*)
FROM
test_person p, test_contact c1, test_org_person porg
WHERE
p.p_id = c1.ref_id(+)
AND p.p_id = porg.o_p_id
$where_clause$
) AS results
FROM
test_person p, test_contact c1, test_org_person porg
WHERE
p.p_id = c1.ref_id(+)
AND p.p_id = porg.o_p_id
$where_clause$
ORDER BY
upper(p.lastname), upper(p.firstname)
) beta
)
WHERE
alpha BETWEEN #startRec# AND #endRec#
My tried implementation below
(1)The inner most query..is the 1st QUERY fetching the data.
(2)Then,we do a total COUNT on the above data.
Now,main issue is running the query goes on forever....and finally i have to forcibly cancel it.
I feel there is something missing in the below query for which it hangs off.
Also,I came to know doing the COUNT outside is the best approach for performance.So,could you please correct the query below so that I am able return the COUNT
*** DATA using Pagination,rownum etc.Mainly with the aliases below,rownum and getting data.
select * from
( select x.* ,rownum rnum
from ( SELECT
count(*) as results /****2nd QUERY is OUTSIDE to get total count**/
Question is here,how do i access the data selected inside the 1st query below
from ( /****1st query to SELECT data***/
SELECT
p.lastname, p.firstname, porg.DEPARTMENT,
porg.org_relationship,
porg.enterprise_name
FROM
t_person p, t_contact c1, t_o_person porg
WHERE rownum <10
and
p.person_id = c1.ref_id(+)
AND p.person_id = porg.o_person_id
ORDER BY
upper(p.lastname), upper(p.firstname)
) y ------------------>alias defined Y from data of the 1st query
)x ------------------>alias defined X
where rownum <= 20 )
where rnum >= 1
To do pagination quickly, you need to limit the query results returned. eg. in mysql you can use limit and calc_rows.
You'd have to check your DB, however it'd be easier to break those 2 into separate queries if you don't have those helper functions.
Maybe I've missed something, but have you looked into use the LIMIT and OFFSET clauses?
http://www.sql.org/sql-database/postgresql/manual/queries-limit.html
I usually do this as two separate queries, e.g.,:
-- get page of data
SELECT *
FROM
(
SELECT
p.lastname, p.firstname, porg.DEPARTMENT,
porg.org_relationship,
porg.enterprise_name
FROM
test_person p, test_contact c1, test_org_person porg
WHERE
p.p_id = c1.ref_id(+)
AND p.p_id = porg.o_p_id
$where_clause$
ORDER BY
upper(p.lastname), upper(p.firstname)
) beta
WHERE
rownum BETWEEN #startRec# AND #endRec#
--get total count
SELECT count(*) as Count
FROM
test_person p, test_contact c1, test_org_person porg
WHERE
p.p_id = c1.ref_id(+)
AND p.p_id = porg.o_p_id
$where_clause$
You could also return the total count in the first row of data in your results, like this:
SELECT null, null, null, null, null, count(*) as Count
FROM
test_person p, test_contact c1, test_org_person porg
WHERE
p.p_id = c1.ref_id(+)
AND p.p_id = porg.o_p_id
$where_clause$
UNION ALL
SELECT *
FROM
(
SELECT
p.lastname, p.firstname, porg.DEPARTMENT,
porg.org_relationship,
porg.enterprise_name, null
FROM
test_person p, test_contact c1, test_org_person porg
WHERE
p.p_id = c1.ref_id(+)
AND p.p_id = porg.o_p_id
$where_clause$
ORDER BY
upper(p.lastname), upper(p.firstname)
) beta
WHERE
rownum BETWEEN #startRec# AND #endRec#
What database are you using? If Oracle the ideas suggested by others will not work, Oracle does not support the LIMIT syntax for SQL.
For Oracle you wrap your query in this syntax:
SELECT *
FROM (SELECT a.*,
ROWNUM rnum
FROM ( [your query] ) a
WHERE ROWNUM <= [endRow] )
WHERE rnum >= [startRow]
These are specifically intended for ASP, but can be adapted without much trouble:
http://databases.aspfaq.com/database/how-do-i-page-through-a-recordset.html
Personally, I implemented the "#Temp table" stored procedure method when I recently needed a paging solution.
My suggestion is :
Create an index on test_person by lastname + firstname (in this order)
If possible, remove the upper functions (some DBs allow creating indexes using functions)
Remove the external SELECT and do the pagination in the client (not in DB)
I suspect that the internal subquery must be resolved first, and that's costly if there are no proper indexes. Usually ordering by computed columns do not use indexes, temporal tables are created etcetera.
Cheers
In Oracle there are a couple of options:
Using ROWNUM in an inner query with a wrapping to get the pagination (as you've tried)
Using analytic functions.
Both approaches have been described well by Tom Kyte:
http://www.oracle.com/technology/oramag/oracle/07-jan/o17asktom.html
Hope this helps.