How to remove the joins in this slow SQL - sql

I have the following (obfuscated) SQL running on SQL Server 2012 and need to significantly improve its performance. It works, but sometimes takes more than 60s to return.
I would like to extract the JOINS but this post seems to indicate that this will not be possible (because of things like MIN and MAX) - so how can improve the performance and get these joins simplified/improved?
SELECT
wm.id, wm.uid, wm.em, wm.fn, wm.ln, c, y, RTRIM(LTRIM(yCode)) AS yCode, wm.d1, ISNULL(wm.ffn, wm.pp) as ffn, wm.ada,
case
when wm.mss & 2=2
then 'false'
else 'true'
end AS isa,
(
SELECT ', '+RTRIM(p1.cKey)
FROM profile p1
inner join loc stl on p1.cKey=stl.cKey
WHERE p1.id = wm.id and p1.s = 'A'
FOR XML PATH('')
) [lst],
lishc.[lstCount],
TotalCount = COUNT(*) OVER(),
la.lsa, wskp.cKey AS pid
FROM wmm wm
LEFT JOIN profile p1 ON wm.id = p1.id
LEFT JOIN (
SELECT UA.id, CONVERT(datetime, UA.ins, 1) As lsa
FROM actlog UA
INNER JOIN (
select id, max(ins) as laa
from actlog
group by id
) UAJ on UA.id=UAJ.id and UA.ins=UAJ.laa
) la on la.id=wm.id
LEFT JOIN (
SELECT id, cKey FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY d1 desc) AS ROWNUM
FROM keypro where sc = 'SAP' AND cKeyDesc = 'SAP Agent ID'
) x WHERE ROWNUM = 1
) wskp ON wskp.id = wm.id
LEFT JOIN (
(SELECT p1.id ,COUNT(p1.cKey) AS [lstCount]
FROM profile p1
inner join loc stl on p1.cKey=stl.cKey
where p1.s = 'A'
GROUP BY p1.id)
) lishc ON lishc.id = wm.id
WHERE (#id = 0 OR wm.id = #id)
AND (#uid IS NULL OR wm.uid LIKE '%' + #uid + '%')
AND (#c IS NULL OR wm.c LIKE '%' + #c + '%')
AND (#fn IS NULL OR wm.fn LIKE '%' + #fn + '%')
AND (#ln IS NULL OR wm.ln LIKE '%' + #ln + '%')
AND (#em IS NULL OR wm.em LIKE '%' + #em + '%')
AND (#ffn IS NULL OR (wm.ffn LIKE '%' + #ffn + '%' OR wm.pp LIKE '%' + #ffn + '%'))
AND (#pid IS NULL OR wskp.cKey LIKE '%' + #pid + '%' )
AND (#Date1 IS NULL OR (CAST(wm.d1 AS DATE) BETWEEN CAST(#Date1 AS DATE) AND CAST(#Date2 AS DATE)))
AND (#lsa1 IS NULL OR (CAST(la.lsa AS DATE) BETWEEN CAST(#lsa1 AS DATE) AND CAST(#lsa2 AS DATE)))
AND (#Active IS NULL OR (wm.mss & 2 != 2))
AND (#Inactive IS NULL OR (wm.mss & 2 = 2))
AND (#External IS NULL OR (wm.ada = 'biz'))
AND (#Internal IS NULL OR (wm.ada <> 'biz'))
AND (#ApplyyFilter =0 OR (wm.yCode IN (SELECT #yCode WHERE 1 = 0)))
AND (#ApplylstFilter = 0 OR(p1.cKey IN (SELECT #ShipToList WHERE 1 = 0)))
AND (#ApplylstFilter = 0 OR(p1.s = 'A'))
AND (#ApplyNoFilter = 0 OR (lishc.[lstCount] is null))
AND (#lstCount = 0 OR lishc.[lstCount] = #lstCount)
AND (#ApplyLimitedFilter = 0 OR (wm.id IN (0)))
AND (#ApplyMoreFilter = 0 OR (wm.id IN (SELECT #idss WHERE 1 = 0)))
GROUP BY wm.id, wm.uid, wm.em, wm.fn, wm.ln, y, yCode,c,wm.d1,wm.ffn,wm.mss,wm.ada, la.lsa, wskp.cKey, lishc.[lstCount], wm.pp
ORDER BY lsa DESC
OFFSET #PageOffset ROWS FETCH NEXT #PageSize ROWS ONLY

The quick hit here is to add OPTION (RECOMPILE) so SQL Server can eliminate the predicates that correspond to null parameters and create a new plan for each search.
And see, generally Dynamic Search Conditions in T‑SQL
The next thing to do is to get rid of the wildcard searches wherever possible.
And transform this
(CAST(la.lsa AS DATE) BETWEEN CAST(#lsa1 AS DATE) AND CAST(#lsa2 AS DATE)))
into a SARGable pattern like
la.lsa >= #lsa1 and la.lsa < #lsa2
Then start to pull this query apart, and hand-write separate queries for the most common or critical cases.

Related

How to increase speed of stored procedure when using order by statement?

I have a stored procedure that returns almost 16000 rows. It also has pagination and returns 10 rows on each page.
This procedure returns user's tickets; when I use order by for sorting them, its execution speed slows down. I checked the execution plan but I don't get useful information, and just I got that order by has warning that operator used tempdb to spill data during execution with spill level 1 and 1 spilled threads, but I don't know how can I solve it.
I would like to know, how can I make my procedure execution faster?
This is my procedure:
;WITH Tickets AS
(
SELECT
t.Id ,
t.[Subject] ,
t.UserId,
t.PriorityId,
u.UserName,
c.[Name] AS UserCompanyName,
t.[Description],
t.LastStatus AS StatusId,
s.[Name] AS [Status],
t.Number AS TicketNumber,
t.CreateOn,
t.CompanyProjectId AS ProjectId ,
(SELECT TOP 1 [Name] FROM CompanyProject
WHERE CrmProjectId = t.CompanyProjectId) AS ProjectName ,
t.[TempNumber],
t.TypeRequestId AS RequestTypeId,
rt.[Name] AS RequestTypeName,
psc.NewMessagesCount,
FORMAT(DATEADD(MINUTE, t.FirstResponsePace, t.CreateOn), 'yyyy/MM/dd HH:mm', 'fa') AS FirstResponsePace,
FORMAT(DATEADD(MINUTE, t.FinishedPace, t.CreateOn), 'yyyy/MM/dd HH:mm', 'fa') AS FinishedPace,
CASE
WHEN t.SlaType IS NULL THEN N'تعیین نشده'
WHEN t.SlaType = '' THEN N'تعیین نشده'
ELSE t.SlaType
END AS SlaType
FROM
Ticket t
LEFT OUTER JOIN
dbo.AspNetUsers u ON t.UserId = u.Id
INNER JOIN
#TempSubUsers tree ON tree.UserId = u.Id
LEFT OUTER JOIN
dbo.AssignmentPosition ap ON u.Id = ap.UserId
LEFT OUTER JOIN
dbo.Company c ON c.id = ap.CompanyId
LEFT OUTER JOIN
dbo.RequestType rt ON rt.Id = t.TypeRequestId
LEFT OUTER JOIN
dbo.[Status] s ON s.Id = t.LastStatus
OUTER APPLY
(SELECT COUNT(1) AS NewMessagesCount
FROM dbo.ProcessStepComment
WHERE EntityId = t.Id AND Seen = 0 AND UserId IS NULL
GROUP BY EntityId) psc
WHERE
(t.Id = #id OR ISNULL(#id, CAST(0x0 AS UNIQUEIDENTIFIER)) = CAST(0x0 AS UNIQUEIDENTIFIER))
AND (t.CreateOn >= #sdate OR ISNULL(#sdate, '') = '')
AND (t.CreateOn <= #edate OR ISNULL(#edate, '') = '')
AND ((t.[Subject] LIKE N'%' + #generalSearch + N'%'
OR t.Number LIKE N'%' + #generalSearch + N'%'
OR t.TempNumber LIKE N'%' + #generalSearch + N'%')
OR ISNULL(#generalSearch, '') = '')
AND (t.LastStatus IN (#allStatusIdListString)
OR (SELECT COALESCE(#allStatusIdListString, CAST(0x0 AS UNIQUEIDENTIFIER))) = CAST(0x0 AS UNIQUEIDENTIFIER))
AND (t.TypeRequestId = #requestTypeId OR
ISNULL(#requestTypeId, #UniqueIdentifierDefault) = #UniqueIdentifierDefault)
AND (t.UserId = #userId OR ISNULL(#userId, '') = '')
AND (t.CompanyProjectId = #companyProject
OR ISNULL(#companyProject, #UniqueIdentifierDefault) = #UniqueIdentifierDefault)
AND (c.Id = #companyId OR ISNULL(#companyId, #UniqueIdentifierDefault) = #UniqueIdentifierDefault)
AND t.[IsEnable] = 1
)
SELECT *
FROM Tickets
ORDER BY
NewMessagesCount DESC,
CASE StatusId
WHEN 'A4E6D31F-ECD1-4288-863F-D4D5449C19D3' THEN 1
WHEN 'EB1566A1-35FD-4C8F-AC27-DB7B02D68299' THEN 2
WHEN '14F7090A-412B-4467-966F-CFEA59F7C434' THEN 3
WHEN 'E9FFB96F-2770-4763-A58D-E363D0903FB4' THEN 4
WHEN 'D002E7AA-2599-4B3C-89E6-DE8A74023E6A' THEN 5
WHEN '1A20087B-D8F1-437F-BC60-4723E5AFEB1A' THEN 6
WHEN '04BE6210-CBF9-4158-A464-7973F4FFDD6F' THEN 7
WHEN 'DF0F6E06-2AEC-4731-86DC-DF25981B852E' THEN 8
WHEN '5635F7AA-37DF-4EFF-B0C7-24E0C3BF4564' THEN 9
ELSE 10
END ASC,
CreateOn DESC
OFFSET #PageSize * (#PageNumber - 1) rows fetch next #PageSize rows ONLY

Loop never ends in SQL Server using While Exists

While Exists (Select * From Col Where Depth Is Null)
Update T
Set T.Depth = P.Depth + 1,
T.Lineage = P.Lineage + LTrim(Str(T.ParentId, 6, 0)) + '/'
From Col T
Join Col P On T.ParentId = P.Id
Where
P.Depth >= 0
And P.Lineage Is Not Null
And T.Depth Is Null
It looks like you're trying to do a recursive query, and the standard way to do that is with a CTE:
;WITH rec AS(
SELECT c.Id, c.Depth, CAST(c.Id AS varchar(MAX) + '/' AS Lineage
FROM Col c
WHERE c.ParentId IS NULL
UNION ALL
SELECT c1.Id, rec.Depth + 1, rec.Lineage + CAST(c1.id as varchar(10)) + '/'
FROM rec
INNER JOIN Col c1 on rec.Id = c1.ParentId
WHERE rec.Depth < 100 --Limit the recursion, set this to what is appropriate for your data
)
--SELECT * FROM rec --Use this first to see if the data is correct, then:
UPDATE T
SET T.Depth = r.Depth, T.Lineage = r.Lineage
FROM Col T
INNER JOIN rec r ON T.Id = r.Id
For more information about CTE's, you can check this technet link.

SQL OpenQuery - Escaping Quotes

Been trying for some hours to convert this to a query I can use with OPENQUERY in SQL Server 2014 (to use with Progress OpenEdge 10.2B via ODBC). Can't seem to get the escaping of the quote right. Can anyone offer some assistance? Is there a tool to do it?
(There's a SQL table called #tAPBatches that is used in this, but I omitted it from this code)
DECLARE
#NoDays AS INT = 30
,#Prefix AS VARCHAR(5) = 'M_AP_'
SELECT
#Prefix + LTRIM(CAST(gh.[Batch-Number] AS VARCHAR(20))) AS BatchNo
,gh.[Batch-Number] AS BatchNo8
, aph.[Reference-number] AS InvoiceNo
,aph.[Voucher-Number] AS VoucherNo
,aph.[Amount] AS InvoiceTotal
,gh.[Journal-Number] AS JournalNo
,4 AS FacilityID
,CASE aph.[voucher-type]
WHEN 'DM' THEN 5
ELSE 1
END AS DocType
,apb.[Batch-Desc] AS BatchDesc
,apb.[Posting-Date] AS PostingDate
,apb.[Posting-Period]
,apb.[Posting-Fiscal-Year]
,apb.[Batch-Status]
,apb.[Expected-Count]
,apb.[Expected-Amount]
,apb.[Posted-To-GL-By]
,'Broadview' AS FacilityName
,apb.[Date-Closed] AS BatchDate
,gh.[Posted-by] AS PostUser
,gh.[Posted-Date] AS PostDT
,gh.[Created-Date] AS CreateDT
,gh.[Created-By] AS CreateUser
,aph.[Supplier-Key] AS VendorID
,sn.[Supplier-Name]
,aph.[Invoice-Date] AS InvoiceDate
,-1 AS Total
,-1 AS Discount
,gh.[Posted-by] AS Username
,CASE gt.[Credit-Debit]
WHEN 'CR' THEN LEFT(CAST(gacr.[GL-Acct] AS VARCHAR(20)), 2) + '.' + SUBSTRING(CAST(gacr.[GL-Acct] AS VARCHAR(20)), 3, 6) + '.'
+ RIGHT(CAST(gacr.[GL-Acct] AS VARCHAR(20)), 3)
ELSE NULL
END AS GLCreditAcct
,CASE gt.[Credit-Debit]
WHEN 'DR' THEN LEFT(CAST(gacr.[GL-Acct] AS VARCHAR(20)), 2) + '.' + SUBSTRING(CAST(gacr.[GL-Acct] AS VARCHAR(20)), 3, 6) + '.'
+ RIGHT(CAST(gacr.[GL-Acct] AS VARCHAR(20)), 3)
ELSE NULL
END AS GLDebitAcct
,CASE gt.[Credit-Debit]
WHEN 'CR' THEN gacr.[Report-Label]
ELSE NULL
END AS GLCreditDesc
,CASE gt.[Credit-Debit]
WHEN 'DR' THEN gacr.[Report-Label]
ELSE NULL
END AS GLDebitDesc
,'D' AS [Status]
,aph.[PO-Number] AS PoNo
,aph.[Terms-Code] AS TermsCode
,aph.[Due-Date] AS DueDate
,'' AS Comments
,aph.[Discount-Date] AS DiscountDate
,aph.[Discount-Amount] AS DiscountAmount
,aph.[Discount-Taken] AS DiscountTaken
,aph.[Amount] AS APAmount
,gt.[Amount]
,'BA REGULAR ' AS CheckBookID --ToDO
,0 AS Transferred
,aph.[voucher-type] AS VoucherType
,gt.[Credit-Debit]
,gacr.[Account-type]
,aph.[Freight-Ref-Num]
FROM
[Progress].[GAMS1].pub.[GL-Entry-Header] gh
INNER JOIN [Progress].[GAMS1].pub.[gl-entry-trailer] gt ON gt.[System-ID] = gh.[System-ID] AND gt.[Origin] = gh.[Origin] AND gt.[Journal-Number] = gh.[Journal-Number]
INNER JOIN [Progress].[GAMS1].pub.[apinvhdr] aph ON (gh.[Journal-Number] = aph.[Journal-Number]
OR (gh.[Journal-Num-Reversal-Of] = aph.[Journal-Number] AND aph.[Journal-Number] <> ' ' AND gh.[Journal-Num-Reversal-Of] <> ' '))
AND gh.[system-id] = aph.[system-id-gl]
AND gh.origin = 'inv'
AND gh.[system-id] = 'arcade'
INNER JOIN [Progress].[GAMS1].pub.[APInvoiceBatch] apb ON gh.[Batch-number] = apb.[Batch-number]
AND apb.[system-id] = 'lehigh'
AND apb.[Posted-To-GL] = 1
INNER JOIN [Progress].[GAMS1].pub.[GL-accts] gacr ON gacr.[system-id] = gt.[system-id]
AND gacr.[Gl-Acct-Ptr] = gt.[GL-Acct-Ptr]
INNER JOIN [Progress].[GAMS1].pub.[suppname] sn ON sn.[Supplier-Key] = aph.[Supplier-Key]
AND sn.[system-id] = 'arcade'
WHERE
gh.[Posted-Date] > CAST(DATEADD(DAY, -#NoDays, GETDATE()) AS DATE)
AND case
when CAST(gh."Posting-Period" as int) < 10 then gh."Posting-Year" + '0' + ltrim(gh."Posting-Period")
else gh."Posting-Year" + Ltrim(gh."Posting-Period")
end > '201501'
AND gh.[Batch-number] NOT IN (SELECT
BatchNo COLLATE SQL_Latin1_General_CP1_CI_AS
FROM
#tAPBatches)
TIA
MArk
Here's an example of what's giving me a syntax error. This works, but "M_AP_" is a parameter passed to SP
DECLARE
#NoDays AS INT = 5
,#Prefix AS VARCHAR(5) = 'M_AP_';
DECLARE
#InterestDate AS varchar(20)
SELECT #InterestDate = CAST(CAST(DATEADD(DAY, -#NoDays, GETDATE()) AS DATE) AS VARCHAR(20))
SELECT * FROM OPENQUERY(PROGRESS,
'SELECT TOP 100 ''M_AP_'' + LTRIM(CAST(gh."Batch-Number" AS VARCHAR(20))) AS BatchNo
, gh."Batch-Number"
This works, but when I try to swap in the variable I get Incorrect Syntax near '+'
DECLARE
#NoDays AS INT = 5
,#Prefix AS VARCHAR(5) = 'M_AP_';
DECLARE
#InterestDate AS varchar(20)
SELECT #InterestDate = CAST(CAST(DATEADD(DAY, -#NoDays, GETDATE()) AS DATE) AS VARCHAR(20))
SELECT * FROM OPENQUERY(PROGRESS,
'SELECT TOP 100 '' ' + #Prefix + ' '' + LTRIM(CAST(gh."Batch-Number" AS VARCHAR(20))) AS BatchNo
, gh."Batch-Number"
FROM
"GAMS1".pub."GL-Entry-Header" gh
OPENQUERY will only support a string literal query that is less than 8K. You might be running into that limit if you've got even more code that you're not showing here. Make sure that your query is less than 8000 bytes, or create procedures or views to reduce the size of your query.
It only accepts a single string literal... so if you are trying to concatenate strings and parameters together, it will not work. There are some ways to work around this by using dynamic SQL or creating supporting tables or views for filters.

Conversion failed when converting the nvarchar value '29449,29446,29450,29534' to data type int

I am create a stored procedure in SQL and I get the following error when I execute the query:
Conversion failed when converting the nvarchar value '11021,78542,12456,24521' to data type int.
Any idea why?
SELECT
A.Art_ID, A.Title
FROM
Art A
INNER JOIN
Iss I ON A.Iss_ID = I.Iss_ID
INNER JOIN
Sections S ON A.Section_ID = S.Section_ID
INNER JOIN
iPadSec IPS ON A.Sec_ID = IPS.Sec_ID
WHERE
A.Art_ID IN (SELECT CAST(Art_IDs AS int) /***error happens here***/
FROM Book_Art b
WHERE Sub_ID = 68)
AND I.Iss > dateadd(month, -13, getdate())
AND A.Active = 1
AND IPS.Active = 1
AND A.PDate <= getdate()
ORDER BY
PDate DESC, Art_ID DESC;
You cannot do what you want using in. First, it is a really bad idea to store ids in lists in strings. You should be using a junction table.
That said, sometimes this is necessary. You can rewrite this line of code as:
EXISTS (SELECT 1 /***error happens here***/
FROM Book_Art b
WHERE Sub_ID = 68 AND
',' + Art_IDs + ',' LIKE '%,' + cast(A.Art_ID as varchar(255)) + ',%'
)
However, the performance would generally be on the lousy side and there is little prospect of speeding this up without fixing the data structure. Use a junction table instead of a string to store lists.
Adding this line works for me.
declare #ids varchar(1000)
select #ids = art_ids from book_art where sub_id = #Sub_ID
EXECUTE ( 'SELECT A.Art_ID, A.Title'
+ ' FROM Art A'
+ ' INNER JOIN Iss I ON A.Iss_ID = I.Iss_ID'
+ ' INNER JOIN Sections S ON A.Section_ID = S.Section_ID'
+ ' INNER JOIN iPadSec IPS ON A.Sec_ID = IPS.Sec_ID'
+ ' WHERE A.Art_ID IN (' + #ids + ')'
+ ' AND I.Iss > dateadd(month, -13, getdate())'
+ ' AND A.Active = 1'
+ ' AND IPS.Active = 1'
+ ' AND A.PDate <= getdate()'
+ ' ORDER BY PDate DESC,'
+ ' Art_ID DESC;'
)
END
Thank you all for your help :)

Dynamic Multi-Column SQL

I have two tables with structures like this:
VelocityBase
Aisle | ItemId | ConfigId | InventSizeId | InventColorId | InventLocationId | DataAreaId | VelocityCategory
VelocitySalesCount
ItemId | ConfigId | InventSizeId | InventColorId | InventLocationId | DataAreaId | Sales
Every row in the Base table represents a SKU and the sum of the related SalesCount records' "Sales" fields determines the "Picks". This query works:
SELECT Aisle, COUNT(*) as '# SKUs',
SUM(Sales) as '# Picks',
SUM(CASE WHEN VelocityCategory = 'Hot' THEN 1 ELSE 0 END) as 'Hot SKUs',
SUM(CASE WHEN VelocityCategory = 'Hot' THEN SALES ELSE 0 END) as 'Hot Picks',
SUM(CASE WHEN VelocityCategory = 'Warm' THEN 1 ELSE 0 END) as 'Warm SKUs',
SUM(CASE WHEN VelocityCategory = 'Warm' THEN SALES ELSE 0 END) as 'Warm Picks',
SUM(CASE WHEN VelocityCategory = 'Cold' THEN 1 ELSE 0 END) as 'Cold SKUs',
SUM(CASE WHEN VelocityCategory = 'Cold' THEN SALES ELSE 0 END) as 'Cold Picks'
FROM [dbo].[VelocityBase] Base
LEFT OUTER JOIN [dbo].[VelocitySalesCount] SalesCount
ON Base.ItemId = SalesCount.ItemId
AND Base.ConfigId = SalesCount.ConfigId
AND Base.InventSizeId = SalesCount.InventSizeId
AND Base.InventColorId = SalesCount.InventColorId
AND Base.InventLocationId = SalesCount.InventLocationId
AND SalesCount.DataAreaId = Base.DataAreaId
GROUP BY Aisle
ORDER BY Aisle
However, the columns are hard coded. What I would like is that the "Hot", "Warm", "Cold", etc be generated based on what values are present in the database for this column. That way if a user added a row that had "Lukewarm" as the VelocityCategory, two new columns would appear with that data.
I'm not sure if something like SQL to generate SQL or maybe a PIVOT function would do the trick.
Thanks in advance!
EDIT:
I'm narrowing in. I've got the Sum of the Sales figures using this:
DECLARE #SQLStatement NVARCHAR(4000)
,#PivotValues NVARCHAR(4000);
SET #PivotValues = '';
SELECT #PivotValues = #PivotValues + ',' + QUOTENAME(VelocityCategory)
FROM
(
SELECT DISTINCT VelocityCategory
FROM dbo.VelocityBase
) src;
SET #PivotValues = SUBSTRING(#PivotValues,2,4000);
SELECT #SQLStatement =
'SELECT pvt.*
FROM
(
SELECT Aisle, VelocityCategory, Sales
FROM VelocityBase Base
LEFT OUTER JOIN [dbo].[VelocitySalesCount] SalesCount
ON Base.ItemId = SalesCount.ItemId
AND Base.ConfigId = SalesCount.ConfigId
AND Base.InventSizeId = SalesCount.InventSizeId
AND Base.InventColorId = SalesCount.InventColorId
AND Base.InventLocationId = SalesCount.InventLocationId
AND SalesCount.DataAreaId = Base.DataAreaId
) VelocityBase
PIVOT ( Sum(Sales) FOR VelocityCategory IN ('+#PivotValues+') ) pvt';
EXECUTE sp_executesql #SQLStatement;
Thanks for the link to the previous question which got me this far.
I usually do not use PIVOT, just "usual" dynamic SQL like this:
DECLARE #sSQL NVARCHAR(MAX)= '' ,
#sSQLSum NVARCHAR(MAX)= '' ,
#sSQlBegin NVARCHAR(MAX)= '
SELECT Aisle, COUNT(*) As ''# SKUs'',
SUM(Sales) As ''# Picks'',
' ,
#sSQLEnd NVARCHAR(MAX)= 'FROM [Dbo].[VelocityBase] Base
LEFT OUTER JOIN [Dbo].[VelocitySalesCount] SalesCount
ON Base.ItemId = SalesCount.ItemId
AND Base.ConfigId = SalesCount.ConfigId
AND Base.InventSizeId = SalesCount.InventSizeId
AND Base.InventColorId = SalesCount.InventColorId
AND Base.InventLocationId = SalesCount.InventLocationId
AND SalesCount.DataAreaId = Base.DataAreaId
GROUP BY Aisle
ORDER BY Aisle' ;
WITH c AS ( SELECT DISTINCT
VelocityCategory N
FROM Dbo.VelocityBase
)
SELECT #sSQLSum = #sSQLSum + 'SUM(CASE WHEN c.N=''' + c.N
+ ''' THEN 1 ELSE 0 END ) AS ''' + c.N + ' SKUs'',' + CHAR(13)
+ 'SUM(CASE WHEN c.N=''' + c.N
+ ''' THEN SALES ELSE 0 END ) AS ''' + c.N + ' Sales'',' + CHAR(13)
FROM c
IF(LEN(#sSQLSum))>0
SET #sSQLSum = LEFT(#sSQLSum, ( LEN(#sSQLsum) - 2 ))
SET #sSQL = #sSQlBegin + #sSQLSum + CHAR(13) + #sSQLEnd
EXEC (#sSQL)
Unless you generate the query dynamically, I don't think there's a way to generate what you want.
Your problem could be solved easily if your tables were normalized. For instance, the VelocityBase table should have a VelocityCategoryID column instead of a VelocityCategory column. This new column should be a foreign key to a new table called VelocityCategory (or something like that) then your query for this calculation becomes almost trivial.