So I have a few really slow queries in SP's I need to speed up, I have been using OPTIMIZE FOR UNKNOWN and seeing some dramatic increases in performance. I still have one query that really is slow and I want to apply this but it contains a UNION of 2 SQL Queries, so my question is do I apply the OPTIMIZE FOR UNKNOWN to both?
THIS IS A GREATLY SIMPLIFIED VERSION OF MY SP FOR CLARITY:
SELECT * FROM MyTable ManufacturerID=#ManufacturerID and tStamp > #tStamp
OPTION (OPTIMIZE FOR (#ManufacturerID UNKNOWN, #tStamp UNKNOWN))
UNION
SELECT * FROM MyTable ManufacturerID=#ManufacturerID
OPTION (OPTIMIZE FOR (#ManufacturerID UNKNOWN)
Here is the actual SP as it stand right now:
SELECT * FROM (
SELECT ROW_NUMBER() OVER(ORDER BY Products.ItemID) AS RowNum, *
FROM
(
SELECT Products.ProductID, Products.ItemID, Products.ManufacturerID,
CategoryID = NULL, CategoryName = NULL,
CategoryProductID = NULL, Products.ItemName, Products.Description, Products.Notes,
Products.Dimensions, Products.BasePrice, Products.OrderMinimumQuantity,
ContainerMinimumQuantity =
CASE COALESCE(Products.ContainerMinQty, 0)
WHEN 0 THEN Products.OrderMinimumQuantity
ELSE Products.ContainerMinQty
END
, Products.OrderMultipleQuantity, Products.OnHandQuantity,
Products.Category, Products.IntroDate, Products.BackOrderDate, Products.UPC, Products.PriceLevel1, Products.PriceLevel2, Products.PriceLevel3,
Products.PriceLevel4, Products.PriceLevel5, Products.PriceLevel6, Products.PriceLevel7, Products.PriceLevel8, Products.PriceLevel9, Products.PieceBox,
Products.Cubes, Products.UnitOfMeasure, Products.UDF1, Products.UDF2, Products.UDF3, Products.UDF4, Products.UDF5, Products.AdditionalImageCount,
PhotoName= LOWER(Products.PhotoName), Products.Discontinued, Products.ModifiedOn,
Products.IsDeleted, Products.PriceLevel10, Products.PriceLevel11, Products.PriceLevel12, Products.PriceLevel13,
Products.PriceLevel14, Products.PriceLevel15, Products.PriceLevel16, Products.PriceLevel17, Products.PriceLevel18, Products.PriceLevel19, Products.PriceLevel20,
Products.Weight, Products.DimensionsMetric, Products.Source, Products.InventoryStatus, Products.CatalogCode, Products.CatalogName,
SortOrder = NULL,
Products.reportCategory,Products.additionalPhotos,Products.udf6,Products.udf7,Products.udf8,
Products.udf9,Products.udf10,Products.udf11,Products.udf12,Products.udf13,Products.udf14,Products.udf15,Products.udf16,
Products.udf17,Products.udf18,Products.udf19,Products.udf20, Products.showRelatedFor,Products.showChildFor
FROM
CategoryProducts (nolock) RIGHT OUTER JOIN
Products (nolock) ON CategoryProducts.ManufacturerID = Products.ManufacturerID AND CategoryProducts.ItemID = Products.ItemID
WHERE (Products.ManufacturerID = #ManufacturerID)
AND
(Products.ModifiedOn > #tStamp ) AND ((CategoryProducts.IsDeleted = 1) OR (CategoryProducts.IsDeleted IS NULL)
)
UNION
SELECT Products.ProductID, Products.ItemID, Products.ManufacturerID, Categories.CategoryID, Categories.CategoryName, CategoryProducts.CategoryProductID, Products.ItemName, Products.Description, Products.Notes,
Products.Dimensions, Products.BasePrice, Products.OrderMinimumQuantity,
ContainerMinimumQuantity =
CASE COALESCE(Products.ContainerMinQty, 0)
WHEN 0 THEN Products.OrderMinimumQuantity
ELSE Products.ContainerMinQty
END
, Products.OrderMultipleQuantity, Products.OnHandQuantity,
Products.Category, Products.IntroDate, Products.BackOrderDate, Products.UPC, Products.PriceLevel1, Products.PriceLevel2, Products.PriceLevel3,
Products.PriceLevel4, Products.PriceLevel5, Products.PriceLevel6, Products.PriceLevel7, Products.PriceLevel8, Products.PriceLevel9, Products.PieceBox,
Products.Cubes, Products.UnitOfMeasure, Products.UDF1, Products.UDF2, Products.UDF3, Products.UDF4, Products.UDF5, Products.AdditionalImageCount,
PhotoName= LOWER(Products.PhotoName), Products.Discontinued, Products.ModifiedOn,
CategoryProducts.IsDeleted, Products.PriceLevel10, Products.PriceLevel11, Products.PriceLevel12, Products.PriceLevel13,
Products.PriceLevel14, Products.PriceLevel15, Products.PriceLevel16, Products.PriceLevel17, Products.PriceLevel18, Products.PriceLevel19, Products.PriceLevel20,
Products.Weight, Products.DimensionsMetric, Products.Source, Products.InventoryStatus, Products.CatalogCode, Products.CatalogName,
CategoryProducts.SortOrder,
Products.reportCategory,Products.additionalPhotos,Products.udf6,Products.udf7,Products.udf8,
Products.udf9,Products.udf10,Products.udf11,Products.udf12,Products.udf13,Products.udf14,Products.udf15,Products.udf16,
Products.udf17,Products.udf18,Products.udf19,Products.udf20, Products.showRelatedFor,Products.showChildFor
FROM Categories (nolock) INNER JOIN
CategoryProducts (nolock) ON Categories.CategoryID = CategoryProducts.CategoryID INNER JOIN
Products (nolock) ON CategoryProducts.ManufacturerID = Products.ManufacturerID AND CategoryProducts.ItemID = Products.ItemID
WHERE (Products.ManufacturerID = #ManufacturerID)
AND
(Products.ModifiedOn > #tStamp OR CategoryProducts.ModifiedOn > #tStamp)
) AS Products
) AS C WHERE RowNum >= #StartRow AND RowNum <= #EndRow ORDER BY ItemID, ManufacturerID
OPTION (OPTIMIZE FOR (#ManufacturerID UNKNOWN, #StartRow UNKNOWN, #EndRow UNKNOWN, #tStamp UNKNOWN))
From Books Online (Query Hints):
Query hints specify that the indicated hints should be used throughout
the query. They affect all operators in the statement. If UNION is
involved in the main query, only the last query involving a UNION
operation can have the OPTION clause.
So that's pretty explicit that you can only use OPTION once, not twice like in your example.
That makes sense, since even though there are two SELECT statements put together, SQL Server will still treat it as one overall statement for the purposes of compilation and optimization.
This does mean that if you want to use different hints in the different statements that make up the UNION you'll have to go about it a different way, e.g. using TEMP tables to store the interim results.
So, in your specific example, even though #tStamp is only in the first SELECT statement, you can still reference it in the one OPTION clause as required.
Try this one -
SELECT
p.ProductID
, RowNum = ROW_NUMBER() OVER(ORDER BY p.ItemID)
, p.ItemID
, p.ManufacturerID
, CategoryID = NULL
, CategoryName = NULL
, CategoryProductID = NULL
, p.ItemName
, p.[Description]
, p.Notes
, p.Dimensions
, p.BasePrice
, p.OrderMinimumQuantity
, ContainerMinimumQuantity =
CASE WHEN ISNULL(p.ContainerMinQty, 0) = 0
THEN p.OrderMinimumQuantity ELSE p.ContainerMinQty
END
, p.OrderMultipleQuantity
, p.OnHandQuantity
, p.category
, p.IntroDate
, p.BackOrderDate
, p.UPC
...
, p.PieceBox
, p.Cubes
, p.UnitOfMeasure
, p.UDF1, p.UDF2, p.UDF3
, p.UDF4, p.UDF5
, p.AdditionalImageCount
, PhotoName = LOWER(p.PhotoName)
, p.Discontinued
, p.ModifiedOn
, p.IsDeleted
...
, p.[Weight]
, p.DimensionsMetric
, p.[Source]
, p.InventoryStatus
, p.CatalogCode
, p.CatalogName
, SortOrder = NULL
, p.reportCategory
, p.additionalPhotos
....
, p.showRelatedFor
, p.showChildFor
FROM dbo.Products p WITH(NOLOCK)
--LEFT JOIN (
-- SELECT *
-- FROM dbo.CategoryProducts cp WITH(NOLOCK)
-- WHERE ISNULL(cp.IsDeleted, 1) = 1
--) cp ON cp.ManufacturerID = p.ManufacturerID AND cp.ItemID = p.ItemID
WHERE p.ManufacturerID = #ManufacturerID
AND p.ModifiedOn > #tStamp
Related
I have an inkling what's causing the slowdown. The issue is that I'm not sure what the correct solution.
I have the following underlying query in my view (named vMemberListByDate):
select
mpi.membermpi
, mpi.accountnumber
, mpi.MemberKey
, membergroupkey = m.groupkey
, mpi.accountkey
, cr.CreditScore
, m.HashedSSN
, OpenDate = mpi.AccountOpenDate
, Closedate = mpi.AccountCloseDate
, Tenure = DATEDIFF(MONTH, mpi.AccountOpenDate, mpi.SnapshotDate)
, AgeIndex = db.Sort
, mpi.SnapshotDate
, ShareBalanceAmt = fmad.TotalShareBalance
, cr.TotalUnsecuredBalance
, CreditLineBalance = fmad.CreditLineLoanBalance
, MortgageBalance = fmad.FirstMortgageLoanBalance + fmad.SecondMortgageLoanBalance
, cr.RevolvingBalance
, IsActive
, fmad.LastChargeOffDate
, fmad.TotalChargeOffCnt
into
#Results
from
EDW.Global.vFactMemberMPIDaily as mpi (nolock)
inner join
EDW.Global.DimMember as m (nolock) on m.MemberKey = mpi.MemberKey
inner join
EDW.Global.FactMemberAccountDaily as fmad (nolock) on fmad.AccountKey = mpi.AccountKey
and fmad.SnapshotDate = mpi.SnapshotDate
left join
EDW.Global.DimBand as db (nolock) on isnull(datediff(year, BirthDate, mpi.SnapshotDate), -1) between db.LowValue and db.HighValue
and db.GroupDescription = 'Age 2'
left join
EDW.Global.DimCredit cr (nolock) on cr.HashedSSN = mpi.HashedSSN
and mpi.SnapshotDate between cr.StartDate and cr.EndDate
I select from the view as follows:
select count(*)
from ML.vMemberListByDate
where SnapshotDate = '11/1/2022'
It runs excruciatingly slow at 26 minutes total.
However, if I apply this date filter directly in the view's query code, it runs in 10 seconds. My assumption for the slowdown is that the view returns every row possible before filtering, therefore causing the slowdown.
Is there anyway around this?
I am needing help converting a PostgreSQL query to MSSQ.
Below is what i have done so far but i am issuing with the function and array areas which i do not think are allowed in MS SQL.
Is there something that that i need to do change the function and looks the WHERE statement has an array in it too.
I have added the select statement for the #temp table but when i create the #temp table i am getting errors saying incorrect syntax
CREATE FUNCTION pm_aggregate_report
(
_facility_ids uuid[]
, _risk_ids uuid[] DEFAULT NULL::uuid[]
, _assignee_ids uuid[] DEFAULT NULL::uuid[]
, _start_date date DEFAULT NULL::date
, _end_date date DEFAULT NULL::date
)
RETURNS TABLE
(
facility character varying
, pm_id uuid, grouped_pm boolean
, risk_id uuid
, risk character varying
, pm_status_id uuid
, user_id uuid
, assignee text
, completed_by uuid
, total_labor bigint
)
CREATE TABLE #tmp_pm_aggregate
(
facility_id VARCHAR(126),
pm_id VARCHAR(126),
grouped_pm VARCHAR(126),
risk_id VARCHAR(126),
pm_status_id VARCHAR(126),
user_id VARCHAR(126),
completed_by VARCHAR(126)
)
SELECT DISTINCT
COALESCE(gp.facility_id, a.facility_id) as facility_id,
COALESCE(p.grouped_pm_id, p.id) as pm_id,
CASE WHEN p.grouped_pm_id IS NULL THEN false ELSE true END as grouped_pm,
COALESCE(gp.risk_id, a.risk_id) as risk_id,
COALESCE(gp.pm_status_id, p.pm_status_id) as pm_status_id,
COALESCE(gass.user_id, sass.user_id) as user_id,
COALESCE(gp.completed_by, p.completed_by) as completed_by
FROM pms p
JOIN assets a
ON p.asset_id = a.id
LEFT JOIN grouped_pms gp
ON p.grouped_pm_id = gp.id
LEFT JOIN assignees sass
ON p.id = sass.record_id
AND sass.type = 'single_pm'
LEFT JOIN assignees gass
ON p.grouped_pm_id = gass.record_id
AND gass.type = 'grouped_pm'
LEFT JOIN users u
ON (sass.user_id = u.id OR gass.user_id = u.id)
WHERE a.facility_id = ANY(_facility_ids)
AND NOT a.is_component
AND COALESCE(gp.pm_status_id, p.pm_status_id) in ('f9bdfc17-3bb5-4ec0-8477-24ef05ea3b9b', '06fc910c-3d07-4284-8f6e-8fb3873f5333')
AND COALESCE(gp.completion_date, p.completion_date) BETWEEN COALESCE(_start_date, '1/1/2000') AND COALESCE(_end_date, '1/1/3000')
AND COALESCE(gp.show_date, p.show_date) <= CURRENT_TIMESTAMP
AND COALESCE(gass.user_id, sass.user_id) IS NOT NULL
AND u.user_type_id != 'ec823d98-7023-4908-8006-2e33ddf2c11b'
AND (_risk_ids IS NULL OR COALESCE(gp.risk_id, a.risk_id) = ANY(_risk_ids)
AND (_assignee_ids IS NULL OR COALESCE(gass.user_id, sass.user_id) = ANY(_assignee_ids);
SELECT
f.name as facility,
t.pm_id,
t.grouped_pm,
t.risk_id,
r.name as risk,
t.pm_status_id,
t.user_id,
u.name_last + ', ' + u.name_first as assignee,
t.completed_by,
ISNULL(gwl.total_labor, swl.total_labor) as total_labor
FROM #tmp_pm_aggregate t
JOIN facilities f
ON t.facility_id = f.id
JOIN risks r
ON t.risk_id = r.id
JOIN users u
ON t.user_id = u.id
LEFT JOIN (SELECT wl.record_id, wl.user_id, SUM(wl.labor_time) as total_labor
FROM work_logs wl
WHERE wl.type = 'single_pm'
GROUP BY wl.record_id, wl.user_id) as swl
ON t.pm_id = swl.record_id
AND t.user_id = swl.user_id
AND t.grouped_pm = false
LEFT JOIN (SELECT wl.record_id, wl.user_id, SUM(wl.labor_time) as total_labor
FROM work_logs wl
WHERE wl.type = 'grouped_pm'
GROUP BY wl.record_id, wl.user_id) as gwl
ON t.pm_id = gwl.record_id
AND t.user_id = gwl.user_id
AND t.grouped_pm = true
ORDER BY facility,
assignee,
risk;
DROP TABLE #tmp_pm_aggregate;
You can create an inline Table Valued Function, and simply return a resultset from it. You do not need (and cannot use) a temp table, you do not declare the returned "rowset" shape.
For the array parameters, you can use a Table Type:
CREATE TYPE dbo.GuidList (value uniqueidentifier NOT NULL PRIMARY KEY);
Because the table parameters are actual tables, you must query them like this (NOT EXISTS (SELECT 1 FROM #risk_ids) OR ISNULL(gp.risk_id, a.risk_id) IN (SELECT r.value FROM #risk_ids))
The parameters must start with #
There is no boolean type, you must use bit
Always use deterministic date formats for literals. yyyymmdd works for dates. Do you need to take into account hours and minutes, because you haven't?
ISNULL generally performs better than COALESCE in SQL Server, as the compiler understands it better
You may want to pass a separate parameter showing whether you passed in anything for the optional table parameters
I suggest you look carefully at the actual query: why does it need DISTINCT? It performs poorly, and is usually a code-smell indicating poorly thought-out joins. Perhaps you need to combine the two joins on assignees, or perhaps you should use a row-numbering strategy somewhere.
CREATE FUNCTION dbo.pm_aggregate_report
(
#facility_ids dbo.GuidList
, #risk_ids dbo.GuidList
, #assignee_ids dbo.GuidList
, #start_date date
, #end_date date
)
RETURNS TABLE AS RETURN
SELECT DISTINCT -- why DISTINCT, perhaps rethink your joins
ISNULL(gp.facility_id, a.facility_id) as facility_id,
ISNULL(p.grouped_pm_id, p.id) as pm_id,
CASE WHEN p.grouped_pm_id IS NULL THEN CAST(0 AS bit) ELSE CAST(1 AS bit) END as grouped_pm,
ISNULL(gp.risk_id, a.risk_id) as risk_id,
ISNULL(gp.pm_status_id, p.pm_status_id) as pm_status_id,
ISNULL(gass.user_id, sass.user_id) as user_id,
ISNULL(gp.completed_by, p.completed_by) as completed_by
FROM pms p
JOIN assets a
ON p.asset_id = a.id
LEFT JOIN grouped_pms gp
ON p.grouped_pm_id = gp.id
LEFT JOIN assignees sass
ON p.id = sass.record_id
AND sass.type = 'single_pm'
LEFT JOIN assignees gass
ON p.grouped_pm_id = gass.record_id
AND gass.type = 'grouped_pm'
LEFT JOIN users u
ON (sass.user_id = u.id OR gass.user_id = u.id) -- is this doubling up your rows?
WHERE a.facility_id IN (SELECT f.value FROM #facility_ids f)
AND a.is_component = 0
AND ISNULL(gp.pm_status_id, p.pm_status_id) in ('f9bdfc17-3bb5-4ec0-8477-24ef05ea3b9b', '06fc910c-3d07-4284-8f6e-8fb3873f5333')
AND ISNULL(gp.completion_date, p.completion_date) BETWEEN ISNULL(#start_date, '20000101') AND ISNULL(#end_date, '30000101') -- perhaps use >= AND <
AND ISNULL(gp.show_date, p.show_date) <= CURRENT_TIMESTAMP
AND ISNULL(gass.user_id, sass.user_id) IS NOT NULL
AND u.user_type_id != 'ec823d98-7023-4908-8006-2e33ddf2c11b'
AND (NOT EXISTS (SELECT 1 FROM #risk_ids) OR ISNULL(gp.risk_id, a.risk_id) IN (SELECT r.value FROM #risk_ids))
AND (NOT EXISTS (SELECT 1 FROM #assignee_ids) OR ISNULL(gass.user_id, sass.user_id) IN (SELECT aid.value FROM #assignee_ids aid));
I'm using MS Visual Studio 2012. I'm having an issue with writing a subquery. This is the entire query as follows:
SELECT ACCOUNTNUM
, NAME
, ADDRESS
, PHONE
, TELEFAX
, INVOICEACCOUNT
, CUSTGROUP
, PAYMTERMID
, CURRENCY
, DIMENSION
, CELLULARPHONE
, STATISTICSGROUP
, PAYMMODE
, NAMEALIAS
, CONTACTPERSONID
, STREET
, PARTYID
, SEGMENTID
, TAXGROUP
, DATAAREAID
, ISNULL((
SELECT PERCENT1 AS DiscCount
FROM PRICEDISCTABLE
WHERE (DATAAREAID = CUSTTABLE.DATAAREAID)
AND (ACCOUNTRELATION = CUSTTABLE.ACCOUNTNUM)
), 0) / 100 AS DiscCount
, (
SELECT NAME
FROM CONTACTPERSON
WHERE (DATAAREAID = CUSTTABLE.DATAAREAID)
AND (CUSTACCOUNT = CUSTTABLE.ACCOUNTNUM)
AND (CONTACTPERSONID = CUSTTABLE.CONTACTPERSONID)
) AS ContactName
, (
SELECT PHONE
FROM CONTACTPERSON AS CONTACTPERSON_1
WHERE (DATAAREAID = CUSTTABLE.DATAAREAID)
AND (CUSTACCOUNT = CUSTTABLE.ACCOUNTNUM)
AND (CONTACTPERSONID = CUSTTABLE.CONTACTPERSONID)
) AS ContactPhone
, ISNULL((
SELECT STATGROUPNAME
FROM CUSTSTATISTICSGROUP
WHERE (DATAAREAID = CUSTTABLE.DATAAREAID)
AND (CUSTSTATISTICSGROUP = CUSTTABLE.STATISTICSGROUP)
), 0) AS StatisticsName
FROM CUSTTABLE
WHERE CUSTGROUP = #ty
AND DATAAREAID = N'OTN'
AND STATISTICSGROUP LIKE #ss;
and then I get the error
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
Where the issue comes from?
Issue is that one of your subqueries returns more than one value (HEY, ERROR DESCRIPTION SAYS THAT).
How to find which one?
Try commenting out each subquery in your select statement until your query works fine.
How to fix it?
That's probably the best case to use CROSS APPLY. That'd be your query:
SELECT ACCOUNTNUM
, NAME
, ADDRESS
, PHONE
, TELEFAX
, INVOICEACCOUNT
, CUSTGROUP
, PAYMTERMID
, CURRENCY
, DIMENSION
, CELLULARPHONE
, STATISTICSGROUP
, PAYMMODE
, NAMEALIAS
, CONTACTPERSONID
, STREET
, PARTYID
, SEGMENTID
, TAXGROUP
, DATAAREAID
, ISNULL(T1.DiscCount, 0) / 100 AS DiscCount
, T2.ContactName
, T3.ContactPhone
, ISNULL(T4, 0) AS StatisticsName
FROM CUSTTABLE
CROSS APPLY (
SELECT PERCENT1 AS DiscCount
FROM PRICEDISCTABLE
WHERE DATAAREAID = CUSTTABLE.DATAAREAID
AND ACCOUNTRELATION = CUSTTABLE.ACCOUNTNUM
) AS T1
CROSS APPLY (
SELECT NAME
FROM CONTACTPERSON
WHERE DATAAREAID = CUSTTABLE.DATAAREAID
AND CUSTACCOUNT = CUSTTABLE.ACCOUNTNUM
AND CONTACTPERSONID = CUSTTABLE.CONTACTPERSONID
) AS T2
CROSS APPLY (
SELECT PHONE
FROM CONTACTPERSON AS CONTACTPERSON_1
WHERE DATAAREAID = CUSTTABLE.DATAAREAID
AND CUSTACCOUNT = CUSTTABLE.ACCOUNTNUM
AND CONTACTPERSONID = CUSTTABLE.CONTACTPERSONID
) AS T3
CROSS APPLY (
SELECT STATGROUPNAME
FROM CUSTSTATISTICSGROUP
WHERE DATAAREAID = CUSTTABLE.DATAAREAID
AND CUSTSTATISTICSGROUP = CUSTTABLE.STATISTICSGROUP
) AS T4
WHERE CUSTGROUP = #ty
AND DATAAREAID = N'OTN'
AND STATISTICSGROUP LIKE #ss;
Also, these can be rewritten as simple INNER JOINS.
When encountering this error, try running the subqueries individually and if more than one record is returned then refine your conditions to ensure only one record is returned.
Of course, if more than one record is wanted then you need to change your query to use a join instead of a subquery.
I have a query in sql stored procedure. I want to get record from other query from its id how I do that.
SELECT t.Name ,t.CreatedDate ,t.CreatedBy , t.Amount
,t.Margin ,t.Probability ,t.Id
FROM (SELECT a = 1) a
CROSS JOIN
(SELECT
Name = HirschInternational_MSCRM.dbo.SalesOrderBase.Name
,CreatedDate=HirschInternational_MSCRM.dbo.SalesOrderBase.CreatedOn
,CreatedBy=HirschInternational_MSCRM.dbo.SystemUserBase.FullName
,Amount = totalamount
,Probability=CloseProbability
,Id=SalesOrderId
,Margin=(SELECT ( ISNULL( ((Sum(Price)-Sum(CurrentCost)) / NULLIF( Sum(Price), 0 ))*100, 0 ) )
FROM HirschInternational_MSCRM.dbo.ProductBase
JOIN HirschInternational_MSCRM.dbo.SalesOrderDetailBase
ON HirschInternational_MSCRM.dbo.SalesOrderDetailBase.ProductId = HirschInternational_MSCRM.dbo.ProductBase.ProductId
JOIN HirschInternational_MSCRM.dbo.SalesOrderBase
ON HirschInternational_MSCRM.dbo.SalesOrderBase.SalesOrderId = HirschInternational_MSCRM.dbo.SalesOrderDetailBase.SalesOrderId)
FROM HirschInternational_MSCRM.dbo.SalesOrderBase
JOIN HirschInternational_MSCRM.dbo.OpportunityBase
ON HirschInternational_MSCRM.dbo.SalesOrderBase.Opportunityid = HirschInternational_MSCRM.dbo.OpportunityBase.Opportunityid
JOIN HirschInternational_MSCRM.dbo.SystemUserBase
ON HirschInternational_MSCRM.dbo.SystemUserBase.SystemUserId = HirschInternational_MSCRM.dbo.SalesOrderBase.CreatedBy
WHERE YEAR(HirschInternational_MSCRM.dbo.SalesOrderBase.CreatedOn)=YEAR(GETDATE())
I want Margin from every record I want Output like
It's not entirely clear what you want, but you might be looking for something like
select *
from (your SQL SELECT statement goes here) t1
where id = ?;
I want to get margin of every record how I filter margin query for SalesOrderId
like
Margin=(SELECT ( ISNULL( ((Sum(Price)-Sum(CurrentCost)) / NULLIF( Sum(Price), 0 ))*100, 0 ) )
FROM HirschInternational_MSCRM.dbo.ProductBase
JOIN HirschInternational_MSCRM.dbo.SalesOrderDetailBase
ON HirschInternational_MSCRM.dbo.SalesOrderDetailBase.ProductId = HirschInternational_MSCRM.dbo.ProductBase.ProductId
JOIN HirschInternational_MSCRM.dbo.SalesOrderBase
ON HirschInternational_MSCRM.dbo.SalesOrderBase.SalesOrderId = HirschInternational_MSCRM.dbo.SalesOrderDetailBase.SalesOrderId
Where HirschInternational_MSCRM.dbo.SalesOrderBase.SalesOrderId= //SalesOrderId that I get in main query)
how I pass that SalesOrderId in this query
I have an SP that returns paged data from a query that contains a UNION. This is killing my DB and taking 30 seconds to run sometimes, am I missing something obvious here? What can I do to improve it's performance?
Tables Involved: Products, Categories, CategoryProducts
Goal:
Any Products that are not in a Category or have been deleted from a category UNION all Products currently in a category and page over them for a web service.
I have Indexes on all columns that I am joining on and there are 427,996 Products, 6148 Categories and 409,691 CategoryProducts in the database.
Here is my query that is taking between 6, and 30 seconds to run:
SELECT * FROM (
SELECT ROW_NUMBER() OVER(ORDER BY Products.ItemID, Products.ManufacturerID) AS RowNum, *
FROM
(
SELECT Products.*,
CategoryID = NULL, CategoryName = NULL,
CategoryProductID = NULL,
ContainerMinimumQuantity =
CASE COALESCE(Products.ContainerMinQty, 0)
WHEN 0 THEN Products.OrderMinimumQuantity
ELSE Products.ContainerMinQty
END
Products.IsDeleted,
SortOrder = NULL
FROM CategoryProducts RIGHT OUTER JOIN Products
ON CategoryProducts.ManufacturerID = Products.ManufacturerID
AND CategoryProducts.ItemID = Products.ItemID
WHERE (Products.ManufacturerID = #ManufacturerID)
AND (Products.ModifiedOn > #tStamp )
AND ((CategoryProducts.IsDeleted = 1) OR (CategoryProducts.IsDeleted IS NULL))
UNION
SELECT Products.*,
CategoryProducts.CategoryID , CategoryProducts.CategoryName,
CategoryProducts.CategoryProductID ,
ContainerMinimumQuantity =
CASE COALESCE(Products.ContainerMinQty, 0)
WHEN 0 THEN Products.OrderMinimumQuantity
ELSE Products.ContainerMinQty
END
CategoryProducts.IsDeleted,
CategoryProducts.SortOrder
FROM Categories INNER JOIN
CategoryProducts ON Categories.CategoryID = CategoryProducts.CategoryID INNER JOIN
Products ON CategoryProducts.ManufacturerID = Products.ManufacturerID
AND CategoryProducts.ItemID = Products.ItemID
WHERE (Products.ManufacturerID = #ManufacturerID)
AND (Products.ModifiedOn > #tStamp OR CategoryProducts.ModifiedOn > #tStamp))
AS Products) AS C
WHERE RowNum >= #StartRow AND RowNum <= #EndRow
Any insight would be greatly appreciated.
If I read your situation correctly, the only reason for having two distinct queries is treatment of missing/deleted CategoryProducts. I tried to address this issue by left join with IsDeleted = 0 to bring all deleted CategoryProducts to nulls, so I don't have to test them again. ModifiedOn part got another test for null for missing/deleted Categoryproducts you wish to retrieve.
select *
from (
SELECT
Products.*,
-- Following three columns will be null for deleted/missing categories
CategoryProducts.CategoryID,
CategoryProducts.CategoryName,
CategoryProducts.CategoryProductID ,
ContainerMinimumQuantity = COALESCE(nullif(Products.ContainerMinQty, 0),
Products.OrderMinimumQuantity),
CategoryProducts.IsDeleted,
CategoryProducts.SortOrder,
ROW_NUMBER() OVER(ORDER BY Products.ItemID,
Products.ManufacturerID) AS RowNum
FROM Products
LEFT JOIN CategoryProducts
ON CategoryProducts.ManufacturerID = Products.ManufacturerID
AND CategoryProducts.ItemID = Products.ItemID
-- Filter IsDeleted in join so we get nulls for deleted categories
-- And treat them the same as missing ones
AND CategoryProducts.IsDeleted = 0
LEFT JOIN Categories
ON Categories.CategoryID = CategoryProducts.CategoryID
WHERE Products.ManufacturerID = #ManufacturerID
AND (Products.ModifiedOn > #tStamp
-- Deleted/missing categories
OR CategoryProducts.ModifiedOn is null
OR CategoryProducts.ModifiedOn > #tStamp)
) C
WHERE RowNum >= #StartRow AND RowNum <= #EndRow
On a third look I don't see that Category is used at all except as a filter to CategoryProducts. If this is the case second LEFT JOIN should be changed to INNER JOIN and this section should be enclosed in parenthessis.