sql query times out...case / if / what to do - sql-server

I have the following sql server sproc:
PROCEDURE [dbo].[GetSoftwareProgramsGrid]
#SoftwareTitle varchar(1000)='All',
#CategoryID varchar(100)='All',
#ManufacturerID varchar(50)='All',
#ModelID int=0, -- 0 means all
#AssetID int=0, -- 0 means all
#AssetStatus int=0, --0 is active, 1 is inactive, and 2 is all
#Status int=0, --0 is active, 1 is inactive, and 2 is all
#Type varchar(100)='All',
#Site varchar(100)='All',
#Department varchar(100)='All',
#Manager varchar(100)='All',
#Employee varchar(100)='All',
#SortExpression varchar(100)='Software',
#SortOrder int=0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
*
FROM
(
SELECT DISTINCT
Program AS Software
FROM
AssetProgram ap
LEFT JOIN
AssetAssignment aa
ON
aa.AssetID = ap.AssetID
LEFT JOIN
[MyLinkedServer].MyDB.dbo.Login l
ON
l.LoginID = aa.LoginID
LEFT JOIN
Asset a
ON
a.AssetID = ap.AssetID
INNER JOIN Model m
ON
a.ModelID = m.ModelID
INNER JOIN
Category c
ON
c.CategoryID = m.CategoryID
INNER JOIN Manufacturer ma
ON
ma.ManufacturerID = m.ManufacturerID
WHERE
(
--Software filters
(ap.Program = #SoftwareTitle OR #SoftwareTitle='All')
--Asset filters
AND (c.CategoryID = #CategoryID OR #CategoryID='All') --filter category
AND (ma.ManufacturerID = #ManufacturerID OR #ManufacturerID='All') --filter manufacturer
AND (m.ModelID = #ModelID OR #ModelID = 0) --filter model
AND (a.AssetID = #AssetID OR #AssetID = 0) --filter by asset name (the actual asset id)
AND (((a.Inactive=#AssetStatus) OR (#AssetStatus=2)))
AND (aa.Inactive=0)
AND (ap.Inactive=0)
--Employee filters
/*AND ((l.Inactive=#Status) OR (#Status=2)) --status of employee 2 is all, 1 is inactive, and 0 is active
AND (#Type='All' OR (#Type='Contractor' AND l.IsContractor=1) OR (#Type='Regular' AND l.IsContractor=0)) --contractor or regular employee
AND (#Site='All' OR #Site=l.ClientID) --the site
AND (#Department='All' OR #Department=l.FunctionalGroupID) --the department
AND ((l.Manager = #Manager OR l.FullName=#Manager) OR #Manager='All') --the manager
AND (l.FullName = #Employee OR #Employee='All') --the employee
*/
)) ttt
ORDER BY
CASE WHEN #SortExpression='Software' AND #SortOrder=0 THEN Software END ASC,
CASE WHEN #SortExpression='Software' AND #SortOrder=1 THEN Software END DESC
This query has to include a linked server, due to our setup. The query runs fine and is fast as long as I comment out my employee parameters, namely this section:
--Employee filters
/*AND ((l.Inactive=#Status) OR (#Status=2)) --status of employee 2 is all, 1 is inactive, and 0 is active
AND (#Type='All' OR (#Type='Contractor' AND l.IsContractor=1) OR (#Type='Regular' AND l.IsContractor=0)) --contractor or regular employee
AND (#Site='All' OR #Site=l.ClientID) --the site
AND (#Department='All' OR #Department=l.FunctionalGroupID) --the department
AND ((l.Manager = #Manager OR l.FullName=#Manager) OR #Manager='All') --the manager
AND (l.FullName = #Employee OR #Employee='All') --the employee
*/
The minute I bring even the first line of that section in, for example just this one:
AND ((l.Inactive=#Status) OR (#Status=2))
The entire sproc hangs (times out)....I've properly indexed my tables and I even have an index on the Inactive field within my linked table...If I take that same line above and just say:
AND (l.Inactive=0)
It runs fine, so the OR condition is causing it (boolean). However, I need this condition as a parameter is passed that needs to be satisfied. What are my other options, do I have to IF BEGIN... using all these parameters? It seems cumbersome...For anyones information the AssetProgram table has a total of 50k rows, so that isn't too much.

yeah your where clause is crazy, specially with all the "OR"s
try to load your "users" into a temp table (or var table) based on the #Status variable (if it is 1 you load only inactive, if it is 0 you load only actives and so on...) and use the temp table on the join, that will drastically decrease the number of comparisons.

Related

Add second database with website pullng information from both databases?

So my website is currently pulling stock information from "J" warehouse in a database called InvWarehouse, now we want to add "JT", so that it pulls from both "J" and "JT"
We have a scheduled stored procedure called "[spc_Schedule_Update_Qty_Stock]"that runs script in the back ground updating new information to the website. When I executed the stored procedure it returned a value of 0, I'm not exactly sure what that means.
ALTER PROCEDURE [dbo].[spc_Schedule_Update_Qty_Stock]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
UPDATE item
SET QtyOnHand = CASE WHEN (inv.QtyOnHand - inv.QtyAllocated) < 0 THEN 0 ELSE (inv.QtyOnHand - inv.QtyAllocated) END--inv.QtyOnHand
FROM tb_Item item WITH (NOLOCK)
INNER JOIN [105.255.132.000].SysproCompanyA.dbo.InvWarehouse inv WITH (NOLOCK)
ON inv.StockCode = item.sCode COLLATE SQL_Latin1_General_CP1_CI_AS
WHERE inv.Warehouse in ('J','JT')
END
I expect the output of the following to be 9, if "J" has 7 belts and "JT" has 2, with what I did so far there's no change.
You have to do something like below. Your original query is doing calculation at individual warehouse level only. You have to aggregate the stocks at two warehouses and then you have to apply that for UPDATE operation.
;WITH WarehousesAvailability AS
(
SELECT inv.StockCode, Sum(inv.QtyOnHand) AS QtyOnHand, Sum(QtyAllocated) AS QtyAllocated
FROM [105.255.132.248].SysproCompanyA.dbo.InvWarehouse inv
WHERE inv.Warehouse in ('J','JT')
GROUP BY inv.StockCode
)
UPDATE item
SET QtyOnHand = CASE WHEN (inv.QtyOnHand - inv.QtyAllocated) < 0 THEN 0 ELSE (inv.QtyOnHand - inv.QtyAllocated) END--inv.QtyOnHand
FROM tb_Item item
INNER JOIN WarehousesAvailability inv
ON inv.StockCode = item.sCode COLLATE SQL_Latin1_General_CP1_CI_AS

Update row with values from select on condition, else insert new row

I'm need to run a calculation for month every day. If the month period, exists already, I need to update it, else I need to create a new row for the new month.
Currently, I've written
declare #period varchar(4) = '0218'
DECLARE #Timestamp date = GetDate()
IF EXISTS(select * from #output where period=#period)
/* UPDATE #output SET --- same calculation as below ---*/
ELSE
SELECT
#period AS period,
SUM(timecard.tworkdol) AS dol_local,
SUM(timecard.tworkdol/currates.cdrate) AS dol_USD,
SUM(timecard.tworkhrs) AS hrs,
#Timestamp AS timestamp
FROM dbo.timecard AS timecard
INNER JOIN dbo.timekeep ON timecard.ttk = timekeep.tkinit
INNER JOIN dbo.matter with (nolock) on timecard.tmatter = matter.mmatter
LEFT JOIN dbo.currates with (nolock) on matter.mcurrency = currates.curcode
AND currates.trtype = 'A'
AND timecard.tworkdt BETWEEN currates.cddate1
AND currates.cddate2
WHERE timekeep.tkloc IN('06','07') AND
timecard.twoper = #period
SELECT * FROM #output;
How can simply update my row with the new data from my select.
Not sure what RDBMS are you using, but in SQL Server something like this would update the #output table with the results of the SELECT that you placed in the ELSE part:
UPDATE o
SET o.dol_local = SUM(timecard.tworkdol),
SET o.dol_USD = SUM(timecard.tworkdol/currates.cdrate),
SET o.hrs = SUM(timecard.tworkhrs),
set o.timestamp = #Timestamp
FROM #output o
INNER JOIN dbo.timecard AS timecard ON o.period = timecard.twoper
INNER JOIN dbo.timekeep ON timecard.ttk = timekeep.tkinit
INNER JOIN dbo.matter with (nolock) on timecard.tmatter = matter.mmatter
LEFT JOIN dbo.currates with (nolock) on matter.mcurrency = currates.curcode
AND currates.trtype = 'A'
AND timecard.tworkdt BETWEEN currates.cddate1
AND currates.cddate2
WHERE timekeep.tkloc IN('06','07') AND
timecard.twoper = #period
Also, I think you want to do an INSERT in the ELSE part, but you are doing just a SELECT, so I guess you should fix that too
The answer to this will vary by SQL dialect, but the two main approaches are:
1. Upsert (if your DBMS supports it), for example using a MERGE statement in SQL Server.
2. Base your SQL on an IF:
IF NOT EXISTS (criteria for dupes)
INSERT INTO (logic to insert)
ELSE
UPDATE (logic to update)

Performance issue with larger resultsets MSSQL

I currently have a stored procedure in MSSQL where I execute a SELECT-statement multiple times based on the variables I give the stored procedure. The stored procedure counts how many results are going to be returned for every filter a user can enable.
The stored procedure isn't the issue, I transformed the select statement from te stored procedure to a regular select statement which looks like:
DECLARE #contentRootId int = 900589
DECLARE #RealtorIdList varchar(2000) = ';880;884;1000;881;885;'
DECLARE #publishSoldOrRentedSinceDate int = 8
DECLARE #isForSale BIT= 1
DECLARE #isForRent BIT= 0
DECLARE #isResidential BIT= 1
--...(another 55 variables)...
--Table to be returned
DECLARE #resultTable TABLE
(
variableName varchar(100),
[value] varchar(200)
)
-- Create table based of inputvariable. Example: turns ';18;118;' to a table containing two ints 18 AND 118
DECLARE #RealtorIdTable table(RealtorId int)
INSERT INTO #RealtorIdTable SELECT * FROM dbo.Split(#RealtorIdList,';') option (maxrecursion 150)
INSERT INTO #resultTable ([value], variableName)
SELECT [Value], VariableName FROM(
Select count(*) as TotalCount,
ISNULL(SUM(CASE WHEN reps.ForRecreation = 1 THEN 1 else 0 end), 0) as ForRecreation,
ISNULL(SUM(CASE WHEN reps.IsQualifiedForSeniors = 1 THEN 1 else 0 end), 0) as IsQualifiedForSeniors,
--...(A whole bunch more SUM(CASE)...
FROM TABLE1 reps
LEFT JOIN temp t on
t.ContentRootID = #contentRootId
AND t.RealEstatePropertyID = reps.ID
WHERE
(EXISTS(select 1 from #RealtorIdTable where RealtorId = reps.RealtorID))
AND (#SelectedGroupIds IS NULL OR EXISTS(select 1 from #SelectedGroupIdtable where GroupId = t.RealEstatePropertyGroupID))
AND (ISNULL(reps.IsForSale,0) = ISNULL(#isForSale,0))
AND (ISNULL(reps.IsForRent, 0) = ISNULL(#isForRent,0))
AND (ISNULL(reps.IsResidential, 0) = ISNULL(#isResidential,0))
AND (ISNULL(reps.IsCommercial, 0) = ISNULL(#isCommercial,0))
AND (ISNULL(reps.IsInvestment, 0) = ISNULL(#isInvestment,0))
AND (ISNULL(reps.IsAgricultural, 0) = ISNULL(#isAgricultural,0))
--...(Around 50 more of these WHERE-statements)...
) as tbl
UNPIVOT (
[Value]
FOR [VariableName] IN(
[TotalCount],
[ForRecreation],
[IsQualifiedForSeniors],
--...(All the other things i selected in above query)...
)
) as d
select * from #resultTable
The combination of a Realtor- and contentID gives me a set default set of X amount of records. When I choose a Combination which gives me ~4600 records, the execution time is around 250ms. When I execute the sattement with a combination that gives me ~600 record, the execution time is about 20ms.
I would like to know why this is happening. I tried removing all SUM(CASE in the select, I tried removing almost everything from the WHERE-clause, and I tried removing the JOIN. But I keep seeing the huge difference between the resultset of 4600 and 600.
Table variables can perform worse when the number of records is large. Consider using a temporary table instead. See When should I use a table variable vs temporary table in sql server?
Also, consider replacing the UNPIVOT by alternative SQL code. Writing your own TSQL code will give you more control and even increase performance. See for example PIVOT, UNPIVOT and performance

Does a WHERE clause that matches a column value to itself get optimized out in SQL Server 2008+?

I'm trying to clean up some stored procedures, and was curious about the following. I did a search, but couldn't find anything that really talked about performance.
Explanation
Imagine a stored procedure that has the following parameters defined:
#EntryId uniqueidentifier,
#UserId int = NULL
I have the following table:
tbl_Entry
-------------------------------------------------------------------------------------
| EntryId PK, uniqueidentifier | Name nvarchar(140) | Created datetime | UserId int |
-------------------------------------------------------------------------------------
All columns are NOT NULL.
The idea behind this stored procedure is that you can get an Entry by its uniqueidentifier PK and, optionally, you can validate that it has the given UserId assigned by passing that as the second parameter. Imagine administrators who can view all entries versus a user who can only view their own entries.
Option 1 (current)
DECLARE #sql nvarchar(3000);
SET #sql = N'
SELECT
a.EntryId,
a.Name,
a.UserId,
b.UserName
FROM
tbl_Entry a,
tbl_User b
WHERE
a.EntryId = #EntryId
AND b.UserId = a.UserId';
IF #UserId IS NOT NULL
SET #sql = #sql + N' AND a.UserId = #UserId';
EXECUTE sp_executesql #sql;
Option 2 (what I thought would be better)
SELECT
a.EntryId,
a.Name,
a.UserId,
b.UserName
FROM
tbl_Entry a,
tbl_User b
WHERE
a.EntryId = #EntryId
AND a.UserId = COALESCE(#UserId, a.UserId)
AND b.UserId = a.UserId;
I realize this case is fairly, simple, and could likely be optimized by a single IF statement that separates two queries. I wrote a simple case to try and concisely explain the issue. The actual stored procedure has 6 nullable parameters. There are others that have even more nullable parameters. Using IF blocks would be very complicated.
Question
Will SQL Server still check a.UserId = a.UserId on every row even though that condition will always be true, or will that condition be optimized out when it sees that #UserId is NULL?
If it would check a.UserId = a.UserId on every row, would it be more efficient to build a string like in option 1, or would it still be faster to do the a.UserId = a.UserId condition? Is that something that would depend on how many rows are in the tables?
Is there another option here that I should be considering? I wouldn't call myself a database expert by any means.
You will get the best performance (and the lowest query cost) if you replace the COALESCE with a compound predicate as follows:
(#UserId IS NULL OR a.UserId = #UserId)
I would also suggest when writing T-SQL that you utilize the join syntax rather than the antiquated ANSI-89 coding style. The revised query will look something like this:
SELECT a.EntryId, a.Name, a.UserId, b.UserName
FROM tblEntry a
INNER JOIN tblUser b ON a.UserId = b.UserId
WHERE a.EntryId = #EntryId
AND (#UserId IS NULL OR a.UserId = #UserId);

SQL SP with multiple values for an input

I've inherited a db etc from another developer and need some help.
I have the following stored procedure:
CREATE PROCEDURE [dbo].[TESTgetSearchResults]
(
#ids varchar(100),
#Date DateTime = Null,
#Date2 DATETIME = Null,
#Sort VARCHAR(5), /* ASC or DESC */
#SortBy VARCHAR(10), /* Sorting criteria */
#Location VARCHAR(40)
)
AS
SELECT #Date = GetDate()
SELECT #Date2 = DATEADD(day,-14,GETDATE())
BEGIN
SELECT Aircraft.Id AS AircraftID, AircraftManufacturers.Name, AircraftModels.ModelName,
Aircraft.ModelSuffix, Aircraft.ImageFileName, Aircraft.Year, Aircraft.SerialNo,
Locations.DescriptionForSite, Aircraft.Description, Aircraft.Description2,
Aircraft.InfoWebAddress, Aircraft.ImageDescription, Advertisers.Id AS AdvertisersID,
Advertisers.Name AS AdvertisersName, Aircraft.AircraftDataId, Aircraft.ForSale, Aircraft.ForLease,
Aircraft.TTAF, Aircraft.ReSend, Aircraft.ReSendReason, Aircraft.Registration, Aircraft.AdType,
Aircraft.HasAlternateImage, Aircraft.AlternateImageDescription,
Aircraft.Price, AircraftModels.AircraftType, Advertisers.CurrentEMagLink, Aircraft.CurrentEMagLink,
Aircraft.Email, Aircraft.IsSold, Aircraft.SoldDate, Aircraft.DateAdded, Aircraft.ExtendedDetails,
Aircraft.LastUpdateDate, Aircraft.ImageCount, Aircraft.ContactTelephone, AircraftModels.id, Advertisers.IsPremiumAdvertiser,
Aircraft.Location, Advertisers.ContactTelephone As AdvertisersTelephone, Aircraft.EndDate, Aircraft.VideoLink,
Aircraft.Contact, Advertisers.WASSalesEmail, Advertisers.WASSalesEmail2, Aircraft.PriceNumeric,
Aircraft.PriceQualifier, Aircraft.Currency, Aircraft.AircraftDescription
FROM (((Aircraft
INNER JOIN Advertisers ON Aircraft.AdvertiserId = Advertisers.Id)
INNER JOIN AircraftModels ON Aircraft.AircraftModelId = AircraftModels.Id)
INNER JOIN AircraftManufacturers ON AircraftModels.ManufacturerId = AircraftManufacturers.Id)
INNER JOIN Locations ON Aircraft.LocationId = Locations.Id
JOIN iter$simple_intlist_to_tbles(#ids) i ON AircraftModels.id = i.number
WHERE (Aircraft.IsActive=1 AND Advertisers.IsActive=1 AND Aircraft.IsSold=0 AND (Aircraft.EndDate>=#Date OR Aircraft.EndDate Is Null) AND Locations.Id = #Location)
OR (Aircraft.IsActive=1 AND Advertisers.IsActive=1 AND Aircraft.IsSold=1 AND Aircraft.SoldDate>=#Date2 AND Locations.Id = #Location)
ORDER BY Advertisers.IsPremiumAdvertiser ASC, Aircraft.DateAdded DESC, Aircraft.ListPosition DESC,
Aircraft.LastUpdateDate, AircraftManufacturers.Name, AircraftModels.ModelName, Aircraft.ModelSuffix,
Aircraft.Id DESC
END
iter$simple_intlist_to_tbles(#ids) simple builds a table from the #ids input. This input comes in the form of a strings of id numbers seperated by a ',' eg ,1,,2,,3,,4, etc...
Now I need to replace the #Location with a string of location IDs formatted in this same fashion eg ,1,,2,,3,,4, etc....
So my problem is this... How do I adapt the above sql/ stored procedure so that the two 'WHERE' clauses which filter based on a single location, are now able to take multiple location IDs ??????
Any help would be really appreciated.
Thanks.
To solve your problem, simply retrieve the values from iter$simple_intlist_to_tbles(#Location) in a subquery, and check for them with IN:
AND Locations.Id IN (SELECT * FROM iter$simple_intlist_to_tbles(#Location))
Your where clause is also more complex that it needs to be. There are identical AND requirements in each OR, so you can move them outside the OR. It simplifies to:
WHERE Aircraft.IsActive=1
AND Advertisers.IsActive=1
AND ((Aircraft.IsSold=0 AND (Aircraft.EndDate>=#Date OR Aircraft.EndDate Is Null))
OR (Aircraft.IsSold=1 AND Aircraft.SoldDate>=#Date2))
AND Locations.Id IN (SELECT * FROM iter$simple_intlist_to_tbles(#Location))
By using a subquery like:
FROM blah ... AND Locations.Id IN (SELECT number FROM iter$simple_intlist_to_tbles(#locations))

Resources