Related
**N** is a prositive number
Need list of scenarios which have sum equal N
For example if N=4
ScenarioId Value
---------- -----
1 1
1 1
1 1
1 1
2 2
2 1
2 1
3 2
3 2
4 3
4 1
5 4
above list is required. If you sum by ScenarioId all sum must equal to N
UPDATE
Here is my own solution. however, I am not sure about the multiplication of two different number sets would not be equal at any time.
My current question is
Is there any possibilities a + b + c = d + e + f and a * b * c = d * e * f
Test link is here
DECLARE #N int = 4;
SELECT
[Value] = CAST(number + 1 as tinyint)
INTO #Values
FROM master.dbo.spt_values
WHERE number < #N
AND [Type] = 'p'
;WITH COMBINATIONS AS(
SELECT ScenarioKey = CAST(NULL AS nvarchar(MAX)), [Value], Total = 0, Multipication = 1, MemeberCount = 0
FROM #Values
UNION ALL
SELECT ScenarioKey = ISNULL(S.ScenarioKey, '') + IIF(S.ScenarioKey IS NULL, '', N'-') + CAST(P.[Value] AS nvarchar(10)), S.[Value], Total = S.Total + P.[Value], Multipication = S.Multipication * P.[Value], MemeberCount = MemeberCount + 1
FROM #Values P
JOIN COMBINATIONS AS S ON S.Total < S.[Value]
),
SCENARIOS AS(
SELECT
ScenarioKey
,ScenarioId = ROW_NUMBER() OVER(ORDER BY ScenarioKey)
,[Value]
FROM
(
SELECT
ScenarioKey
,[Value]
,Multipication
,MemeberCount
-- this will prevent dublications. because 1 * 2 * 3 = 3 * 2 * 1
-- however, I am not sure about multipication of two different number sets would not be equal any time
,RowNo = ROW_NUMBER() OVER(PARTITION BY [Value],Multipication,MemeberCount ORDER BY [Value],ScenarioKey)
FROM COMBINATIONS
WHERE Total = #N
) X
WHERE RowNo = 1 AND [Value] = #N
)
SELECT
R.ScenarioId
,[Value] = S.[value]
FROM SCENARIOS R
CROSS APPLY (SELECT [value] FROM STRING_SPLIT(R.ScenarioKey, '-')) S
DROP TABLE #Values
It's too long for comment, so I post this as an answer. I want to note, that this is a static example, but I hope it can be easily translated as a dynamic statement.
Steps are written as comments in the statement:
WITH rcte AS
(
-- Recursive query to generate all numbers from 1 to 4
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM rcte
WHERE Number < 4
), permutations AS (
-- All possible permutations with sum equal to 4
-- There is additional column DuplicateMarker.
-- It will be used later, because 0,0,0,4 and 0,4,0,0 are the same
SELECT
t1.Number AS Number1,
t2.Number AS Number2,
t3.Number AS Number3,
t4.Number AS Number4,
CONCAT(LTRIM(STR(t1.Number)), '.', LTRIM(STR(t2.Number)), '.', LTRIM(STR(t3.Number)), '.', LTRIM(STR(t4.Number))) AS DuplicateMarker
FROM rcte t1, rcte t2, rcte t3, rcte t4
WHERE (t1.Number + t2.Number + t3.Number + t4.Number) = 4
), duplicates AS (
-- Get data with splitted DuplicateMarker column
SELECT *
FROM permutations
CROSS APPLY (SELECT [value] FROM STRING_SPLIT(DuplicateMarker, '.')) t
), results AS (
-- Get unique combinations
-- WITHIN GROUP (ORDER BY) will order strings and 0.0.0.4 and 0.4.0.0 will be the same
SELECT DISTINCT STRING_AGG([value], '.') WITHIN GROUP (ORDER BY [value]) AS ScenarioValue
FROM duplicates
GROUP BY Number1, Number2, Number3, Number4
)
SELECT
DENSE_RANK() OVER (ORDER BY r.ScenarioValue) AS ScenarioID,
s.[value]
FROM results r
CROSS APPLY (SELECT [value] FROM STRING_SPLIT(r.ScenarioValue, '.')) s
WHERE [value] <> '0'
Output:
ScenarioID value
1 4
2 1
2 3
3 2
3 2
4 1
4 1
4 2
5 1
5 1
5 1
5 1
Update:
Thanks to #AndriyM's comment, I've made some changes and now you can eliminate string manipulations:
WITH rcte AS
(
-- Recursive query to generate all numbers from 0 to 4
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM rcte
WHERE Number < 4
), combinations AS (
-- All different combinations with sum equal to 4
SELECT
t1.Number AS Number1,
t2.Number AS Number2,
t3.Number AS Number3,
t4.Number AS Number4,
ROW_NUMBER() OVER (ORDER BY t1.Number, t2.Number, t3.Number, t4.NUmber) AS ScenarioID
FROM rcte t1, rcte t2, rcte t3, rcte t4
WHERE
((t1.Number + t2.Number + t3.Number + t4.Number) = 4) AND
(t1.Number <= t2.Number) AND
(t2.Number <= t3.Number) AND
(t3.Number <= t4.Number)
)
SELECT c.ScenarioID, v.[value]
FROM combinations c
CROSS APPLY (VALUES (c.NUmber1), (c.Number2), (c.Number3), (c.Number4)) AS v ([value])
WHERE v.[value] > 0
Update 2:
Approach using dynamic statement - probably not the best approach, but is based on statement from first update:
-- Set your #n value
DECLARE #n int
SET #n = 4
-- Declarations
DECLARE #combinationsSelect nvarchar(max)
DECLARE #combinationsRowNumber nvarchar(max)
DECLARE #combinationsFrom nvarchar(max)
DECLARE #combinationsWhere1 nvarchar(max)
DECLARE #combinationsWhere2 nvarchar(max)
DECLARE #combinationsValues nvarchar(max)
SET #combinationsSelect = N''
SET #combinationsRowNumber = N''
SET #combinationsFrom = N''
SET #combinationsValues = N''
SET #combinationsWhere1 = N''
SET #combinationsWhere2 = N''
-- Generate dynamic parts of the statement
;WITH numbers AS
(
SELECT 1 AS Number
UNION ALL
SELECT Number + 1
FROM Numbers
WHERE Number < #n
)
SELECT
#combinationsSelect = #combinationsSelect + N', t' + LTRIM(STR(Number)) + N'.Number AS Number' + LTRIM(STR(Number)),
#combinationsRowNumber = #combinationsRowNumber + N', t' + LTRIM(STR(Number)) + N'.Number',
#combinationsValues = #combinationsValues + N', (c.Number' + LTRIM(STR(Number)) + N')',
#combinationsFrom = #combinationsFrom + N', rcte t' + LTRIM(STR(Number)),
#combinationsWhere1 = #combinationsWhere1 + N'+ t' + LTRIM(STR(Number)) + N'.Number ',
#combinationsWhere2 = #combinationsWhere2 +
CASE
WHEN Number = 1 THEN N''
ELSE N'AND (t' + LTRIM(STR(Number-1)) + N'.Number <= t' + + LTRIM(STR(Number)) + N'.Number) '
END
FROM
numbers
SET #combinationsSelect = STUFF(#combinationsSelect, 1, 2, N'')
SET #combinationsRowNumber = STUFF(#combinationsRowNumber, 1, 2, N'')
SET #combinationsValues = STUFF(#combinationsValues, 1, 2, N'')
SET #combinationsFrom = STUFF(#combinationsFrom, 1, 2, N'')
SET #combinationsWhere1 = STUFF(#combinationsWhere1, 1, 2, N'')
SET #combinationsWhere2 = STUFF(#combinationsWhere2, 1, 4, N'')
-- Dynamic statement
DECLARE #stm nvarchar(max)
SET #stm =
N'WITH rcte AS (
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM rcte
WHERE Number < ' + LTRIM(STR(#n)) +
N'), combinations AS (
SELECT ' +
#combinationsSelect +
N', ROW_NUMBER() OVER (ORDER BY ' + #combinationsRowNumber + N') AS ScenarioID
FROM ' + #combinationsFrom +
N' WHERE ((' + #combinationsWhere1 + N') = ' + LTRIM(STR(#n)) + ') AND ' + #combinationsWhere2 +
N')
SELECT c.ScenarioID, v.[value]
FROM combinations c
CROSS APPLY (VALUES ' + #combinationsValues + N') AS v ([value])
WHERE v.[value] > 0'
-- Execute dynamic statement
EXEC (#stm)
If you have sample data like below
You can write query like below
Declare #N int =4
Select T.*
From #T T
cross apply (
select S, SUM(V) Total
From #T
Group By S) Totals
Where Totals.Total=#N and T.S = Totals.S
I have the following tables
BATCH
BatchID Name CustomerID DateCreated Status
12 A 1 01/01/2013 Active
13 B 12 01/01/2013 Inactive
14 C 245 01/01/2013 Complete
BATCHDETAIL
BatchDetailID BatchID Weight Price DestinationCode
1 12 55 500.00 99
2 12 119 1500.00 55
3 13 12 133 1212
A batch record can have many batch detail records linked via the FK BatchDetail.BatchID
I want to write a query to select a single row back to the user which combines the information in the BATCH record and the Weight,Price and DestinationCode from both BATCHDETAIL records for BatchID = 12
So the output would be :
BatchID Name CustomerID DateCreated Status WeightA PriceA DestinationCodeA WeightB PriceB DestinationCodeB
12 A 1 01/01/2013 Active 55 500.00 99 119 1500 55
So you can see I want to have 1 row with all information combined in the one row and differentiate each detail record with A or B ( Lets assume a maximum of 2 detail records is only allowed )
I have thought of creating a table with these fields and then building up the information in a series of select statements and finally doing a select on the temp table but getting the query into a single block of SQL would be ideal.
Here is a solution using dynamic SQL:
-- Get the MAX total number of records per BatchID (how many sets of columns do we need?)
DECLARE #requiredLevels int = (SELECT MAX(C) FROM (SELECT COUNT(*) C FROM BATCHDETAIL GROUP BY BatchID) Q)
;
-- Build a dynamic statement for the final SELECT fields
DECLARE
#finalFieldsSQL varchar(1000) = ''
, #finalFieldsN int = 1
;
WHILE #finalFieldsN <= #requiredLevels
BEGIN
SET #finalFieldsSQL = #finalFieldsSQL + ', Weight' + CHAR(64 + #finalFieldsN) + ', Price' + CHAR(64 + #finalFieldsN) + ', DestinationCode' + CHAR(64 + #finalFieldsN)
SET #finalFieldsN = #finalFieldsN + 1
END
-- Build a dynamic statement for the subquery SELECT fields
DECLARE
#subqueryFieldsSQL varchar(1000) = ''
, #subqueryFieldsN int = 1
;
WHILE #subqueryFieldsN <= #requiredLevels
BEGIN
SET #subqueryFieldsSQL = #subqueryFieldsSQL + ', MAX([' + CAST(#subqueryFieldsN AS varchar) + ']) ColumnName' + CHAR(64 + #subqueryFieldsN)
SET #subqueryFieldsN = #subqueryFieldsN + 1
END
-- Build a dynamic statement for the PIVOT fields
DECLARE
#pivotFieldsSQL varchar(1000) = ''
, #pivotFieldsN int = 1
;
WHILE #pivotFieldsN <= #requiredLevels
BEGIN
SET #pivotFieldsSQL = #pivotFieldsSQL + ', [' + CAST(#pivotFieldsN AS varchar) + ']'
SET #pivotFieldsN = #pivotFieldsN + 1
END
SET #pivotFieldsSQL = SUBSTRING(#pivotFieldsSQL, 3, LEN(#pivotFieldsSQL) - 2)
-- Build the final SQL statement and execute
DECLARE #SQL varchar(8000) =
'
SELECT
B.BatchID, B.Name, B.CustomerID, B.DateCreated, [Status]' + #finalFieldsSQL + '
FROM
BATCH B
LEFT JOIN
(
SELECT
BatchID' + REPLACE(#subqueryFieldsSQL, 'ColumnName', 'Weight') + '
FROM
(
SELECT BD.BatchID, [Weight], ROW_NUMBER() OVER (PARTITION BY B.BatchID ORDER BY BatchDetailID) R
FROM
BATCH B
JOIN BATCHDETAIL BD ON B.BatchID = BD.BatchID
) Q
PIVOT
(
MAX([Weight])
FOR R IN (' + #pivotFieldsSQL + ')
) P
GROUP BY BatchID
) W
ON B.BatchID = W.BatchID
LEFT JOIN
(
SELECT
BatchID' + REPLACE(#subqueryFieldsSQL, 'ColumnName', 'Price') + '
FROM
(
SELECT BD.BatchID, Price, ROW_NUMBER() OVER (PARTITION BY B.BatchID ORDER BY BatchDetailID) R
FROM
BATCH B
JOIN BATCHDETAIL BD ON B.BatchID = BD.BatchID
) Q
PIVOT
(
MAX(Price)
FOR R IN (' + #pivotFieldsSQL + ')
) P
GROUP BY BatchID
) P
ON B.BatchID = P.BatchID
LEFT JOIN
(
SELECT
BatchID' + REPLACE(#subqueryFieldsSQL, 'ColumnName', 'DestinationCode') + '
FROM
(
SELECT BD.BatchID, DestinationCode, ROW_NUMBER() OVER (PARTITION BY B.BatchID ORDER BY BatchDetailID) R
FROM
BATCH B
JOIN BATCHDETAIL BD ON B.BatchID = BD.BatchID
) Q
PIVOT
(
MAX(DestinationCode)
FOR R IN (' + #pivotFieldsSQL + ')
) P
GROUP BY BatchID
) D
ON B.BatchID = D.BatchID
'
EXEC (#SQL)
If you don't want to show empty records, replace LEFT JOIN with JOIN in the final statement (3 occurences).
you can use Pivot and UnPivot to achieve this result. Try Something like this:
SELECT
BatchID,[Name],[CustomerID],[DateCreated],[Status],
MAX(Weight1) as WeightA,
MAX(Price1) as PriceA,
MAX(DestinationCode1) as DestinationCodeA,
MAX(Weight2) as WeightB,
MAX(Price2) as PriceB,
MAX(DestinationCode2) as DestinationCodeB
FROM (
SELECT *,COL + CAST(DENSE_RANK() OVER (PARTITION BY Batchid ORDER BY BatchDetailID ASC) AS VARCHAR) AS BATCHPIVOT
FROM
(
SELECT b.*,cast(d.Weight as varchar(255)) as Weight, cast(d.Price as varchar(255)) as Price, cast(d.DestinationCode as varchar(255)) as DestinationCode,d.BatchDetailID
FROM #Batch B
INNER JOIN #BATCHDETAIL D on b.BatchID = d.BatchID
) AS cp
UNPIVOT
(
Val FOR Col IN ([Weight], [Price], [DestinationCode])
) AS up
) AS query
PIVOT (MAX(Val)
FOR BATCHPIVOT IN (Weight1,Price1,DestinationCode1, Weight2, Price2, DestinationCode2)) AS Pivot1
GROUP BY BatchID,[Name],[CustomerID],[DateCreated],[Status]
This is just standard query. you can make this script Dynamic according to your liking:
Complete Script:
Create table #Batch
(BatchID int,
[Name] char(1),
[CustomerID] int,
[DateCreated] date,
[Status] varchar(50)
)
Create table #BATCHDETAIL
(BatchDetailID int,
BatchID int,
[Weight] int,
Price money,
DestinationCode int
)
INSERT INTO #Batch
VALUES(12,'A',1,'01/01/2013','Active')
,(13,'B',12,'01/01/2013','Inactive')
,(14,'C',245,'01/01/2013','Complete')
INSERT INTO #BATCHDETAIL
VALUES(1,12,55,500.00,99)
,(2,12,119,1500.00,55)
,(3,13,12,133,1212)
SELECT
BatchID,[Name],[CustomerID],[DateCreated],[Status],
MAX(Weight1) as WeightA,
MAX(Price1) as PriceA,
MAX(DestinationCode1) as DestinationCodeA,
MAX(Weight2) as WeightB,
MAX(Price2) as PriceB,
MAX(DestinationCode2) as DestinationCodeB
FROM (
SELECT *,COL + CAST(DENSE_RANK() OVER (PARTITION BY Batchid ORDER BY BatchDetailID ASC) AS VARCHAR) AS BATCHPIVOT
FROM
(
SELECT b.*,cast(d.Weight as varchar(255)) as Weight, cast(d.Price as varchar(255)) as Price, cast(d.DestinationCode as varchar(255)) as DestinationCode,d.BatchDetailID
FROM #Batch B
INNER JOIN #BATCHDETAIL D on b.BatchID = d.BatchID
) AS cp
UNPIVOT
(
Val FOR Col IN ([Weight], [Price], [DestinationCode])
) AS up
) AS query
PIVOT (MAX(Val)
FOR BATCHPIVOT IN (Weight1,Price1,DestinationCode1, Weight2, Price2, DestinationCode2)) AS Pivot1
GROUP BY BatchID,[Name],[CustomerID],[DateCreated],[Status]
if I have a result returned as follows:
pkTestInstanceID Percent1 Count1 Percent2 Count2
1 25 1 75 3
2 50 2 50 2
Is there a way so it pivots in such format:
pkTestInstanceID Percent Count
1 25 1
1 75 3
2 50 2
2 50 2
Sorry if this question is totally misguided. I'm not super clear on the pivoting process. Thanks for any help.
EDIT I should probably have noted that the Percent1, Count1, Percent2 etc columns are created based off of another column (stackposition). So if stackposition has 4 rows then the percent and count will go up to percent4 count4. Is a pivot or union still possible without the knowledge of the exact number of percent and count columns in the result set.
EDIT 2: It gets a bit more complicated now...
I now realize that I have to include another item in my select statement (fkBandID). For each bandID there is a stackposition as stated above, so for bandID 96 the stackposition is 4, for 97 the stackposition is 3, for 98 the stackposition is 2 etc. so I want the result set to look as follows:
fkBandID pkTestInstanceID Band_Percent Band_Count StackPosition (not included but there for for visual example)
96 265 2 1 4
97 265 4 2 3
98 265 34 17 2
99 265 59 29 1
Here is what the creation of my second query looks like after the initial result set is brought back and with the bandID being selected including the new bandID. This is from Pradeep's answer.
http://gyazo.com/091ece1a4a1334c0f2546bccb8a6b8da
This is what the result set looks like, so as you can see there are 4 rows being created for each bandID. Is there anyway to fix this and make it look as I displayed above in the cross apply that Pradeep helped me with? Or any other solution?
http://gyazo.com/cd19634a1201362ac3aa4546f15373c9
Sorry I'm super nooby with SQL. Let me know if more info is needed.
EDIT 3
(N'DECLARE #strYearIds nvarchar(100)
SET #strYearIds = ''' + #strYearIds + N'''
DECLARE #strDemoCodeIds nvarchar(100)
SET #strDemoCodeIds = ''' + #strDemoCodeIds + N'''
DECLARE #intRosterSetId int
SET #intRosterSetId = ' + CONVERT(nvarchar, #intRosterSetId) + N'
DECLARE #intSchoolId int
SET #intSchoolId = ' + CONVERT(nvarchar, #intSchoolId) + N'
DECLARE #intTeachId int
SET #intTeachId = ' + CONVERT(nvarchar, #intTeachId) + N'
DECLARE #intGradeId int
SET #intGradeId = ' + CONVERT(nvarchar, #intGradeId) + N'
DECLARE #intDeptId int
SET #intDeptId = ' + CONVERT(nvarchar, #intDeptId) + N'
DECLARE #intCourseId int
SET #intCourseId = ' + CONVERT(nvarchar, #intCourseId) + N'
DECLARE #intPeriodId int
SET #intPeriodId = ' + CONVERT(nvarchar, #intPeriodId) + N'
DECLARE #strTestInstId nvarchar(100)
SET #strTestInstId = ''' + #strTestInstId + N'''
DECLARE #intTestTypeId int
SET #intTestTypeId = ' + CONVERT(nvarchar, #intTestTypeId) + N'
DECLARE #strSubIds nvarchar(100)
SET #strSubIds = ''' + #strSubIds + N'''
DECLARE #bitIsStrand bit
SET #bitIsStrand = ' + CONVERT(nvarchar, #bitIsStrand) + N'
DECLARE #intPerfLevelReportId int
SET #intPerfLevelReportId = ' + CONVERT(nvarchar, #intPerfLevelReportId) +
N' DECLARE #tempTests TABLE (id int)
INSERT INTO #tempTests
exec SPGetStudentTests_Local_MTI #strDemoCodeIds, #strYearIds, #intSchoolId, #intTeachId, #intGradeId,
#intRosterSetId, #intPeriodId, #intDeptId, #intCourseId, #strTestInstId, #intTestTypeId
DECLARE #tempSubs TABLE (id int)
IF #bitIsStrand = 1
BEGIN
INSERT INTO #tempSubs
SELECT pkTestSubjectID FROM MM_Test_Subjects WHERE fkCSTStrandID /*= #intSubID*/ IN (SELECT number FROM itot(#strSubIds, N'','')) AND fkTestTypeID = #intTestTypeId
END
ELSE
BEGIN
INSERT INTO #tempSubs
SELECT number FROM itot(#strSubIds, N'','')--VALUES (#intSubId)
END
SELECT bands.pkPerformanceLevelReportBandID AS ''fkBandID'', TestInstances.pkTestInstanceID AS ''TestInstanceID'', StudentScores_Subject.fkTest_SubjectID AS ''TestSubjectID'', '
+ #cols +
N'INTO ##tempTable FROM StudentScores_Subject
INNER JOIN StudentTests ON StudentScores_Subject.fkStudentTestID = StudentTests.pkStudentTestID
INNER JOIN TestInstances ON TestInstances.pkTestInstanceID = StudentTests.fkTestInstanceID
INNER JOIN CAHSEE_TestPeriods ON CAHSEE_TestPeriods.pkTestPeriodID = TestInstances.fkTestPeriodID
INNER JOIN PerformanceLevelReportBands bands ON bands.fkPerformanceLevelReportID = #intPerfLevelReportId
LEFT JOIN MMARS_Web_TestInfo_California.dbo.PerfLevelReportBandCutScores cutScores ON cutScores.fkPerformanceLevelReportBandID = bands.pkPerformanceLevelReportBandID
AND cutScores.fkGradeID = #intGradeId
AND cutScores.fkTestSubjectID IN (SELECT id FROM #tempSubs)
INNER JOIN PerfLevelReportBandComponents bandComponents ON bandComponents.fkPerformanceLevelReportBandID = bands.pkPerformanceLevelReportBandID
AND((bandComponents.ScoreValue = StudentScores_Subject.ScoreValue) OR
((CAST(StudentScores_Subject.ScoreValue AS INT) BETWEEN bandComponents.minScore and bandComponents.maxScore)
OR
(CAST(StudentScores_Subject.ScoreValue AS INT) BETWEEN cutScores.minScore and cutScores.maxScore))
)
RIGHT JOIN MM_SchoolYears ON MM_SchoolYears.pkSchoolYearID = TestInstances.fkSchoolYearID
WHERE MM_SchoolYears.pkSchoolYearID IN (SELECT number FROM itot(#strYearIds, N'',''))
AND bands.fkPerformanceLevelReportID = #intPerfLevelReportId
AND StudentScores_Subject.fkStudentTestID IN (SELECT id FROM #tempTests)
AND StudentScores_Subject.fkScoreTypeID = bandComponents.fkScoreTypeID
AND StudentScores_Subject.fkTest_SubjectID IN (SELECT id FROM #tempSubs)
--AND((bandComponents.ScoreValue = StudentScores_Subject.ScoreValue) OR
--(StudentScores_Subject.ScoreValue BETWEEN bandComponents.minScore and bandComponents.maxScore) OR
--(StudentScores_Subject.ScoreValue BETWEEN cutScores.minScore and cutScores.maxScore))
GROUP BY bands.pkPerformanceLevelReportBandID, TestInstances.pkTestInstanceID, StudentScores_Subject.fkTest_SubjectID
ORDER BY bands.pkPerformanceLevelReportBandID, TestInstances.pkTestInstanceID, StudentScores_Subject.fkTest_SubjectID')
The #cols variable is as follows:
DECLARE #cols NVARCHAR(MAX)
SELECT #cols = STUFF(( SELECT DISTINCT TOP 100 PERCENT ', SUM(CASE WHEN bands.StackPosition = ''' + STR(b.StackPosition, 1) + ''' THEN 1 ELSE 0 END) * 100.0/ CASE WHEN COUNT(pkStudentScoreID) = 0 THEN 1 ELSE COUNT(pkStudentScoreID) END AS ''Percent_' + STR(b.StackPosition, 1) + ''', SUM(CASE WHEN bands.StackPosition = ''' + STR(b.StackPosition, 1) + ''' THEN 1 ELSE 0 END) AS ''Count_' + STR(b.StackPosition, 1) + ''''
FROM PerformanceLevelReportBands AS b
WHERE b.fkPerformanceLevelReportID = #intPerfLevelReportId
ORDER BY ', SUM(CASE WHEN bands.StackPosition = ''' + STR(b.StackPosition, 1) + ''' THEN 1 ELSE 0 END) * 100.0/ CASE WHEN COUNT(pkStudentScoreID) = 0 THEN 1 ELSE COUNT(pkStudentScoreID) END AS ''Percent_' + STR(b.StackPosition, 1) + ''', SUM(CASE WHEN bands.StackPosition = ''' + STR(b.StackPosition, 1) + ''' THEN 1 ELSE 0 END) AS ''Count_' + STR(b.StackPosition, 1) + ''''
FOR XML PATH('')
), 1, 2, '')
what you are looking for is Unpivot not pivot
CREATE TABLE #piv
(
pkTestInstanceID INT,
Percent1 INT,
Count1 INT,
Percent2 INT,
Count2 INT
)
INSERT INTO #piv
VALUES ( 1,25,1,75,3),
(2,50,2,50,2)
SELECT pkTestInstanceID,
[percent],
[count]
FROM #piv AS p
CROSS APPLY ( VALUES (Percent1,Count1),
(Percent2,Count2))
AS x([percent], [count]);
If you want this to work dynamically then below code should help you.
For example i have kept no. of stackposition rows as 2 u can change it and check
DECLARE #stackposition INT=2,
#sql NVARCHAR(max),
#cnt INT=1
SET #sql =' SELECT pkTestInstanceID,
[percent],
[count]
FROM #piv AS p
CROSS APPLY ( VALUES '
WHILE #cnt <= #stackposition
BEGIN
SET #sql+='([Percent' + CONVERT(VARCHAR(10), #cnt)+ '],[Count' + CONVERT(VARCHAR(10), #cnt) + ']),'
SET #cnt+=1
END
SET #sql= LEFT(#sql, Len(#sql) - 1)
SET #sql+=') AS x([percent], [count])'
EXEC Sp_executesql
#sql
OUTPUT
pkTestInstanceID percent count
---------------- ------- -----
1 25 1
1 75 3
2 50 2
2 50 2
You don't really need to pivot here. You can do a UNION on the resultset as suggested by #bksi like below
select pkTestInstanceID, percent1 as [percent], count1 as count
from (
inner result set
) tab
UNION
select pkTestInstanceID, percent2, count2
from (
inner result set
) tab1
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.
I have an SQL CREATE PROCEDURE statement that runs perfectly in my local SQL Server, but cannot be recreated in production environment. The error message I get in production is Msg 102, Level 15, State 1, Incorrect syntax near '='.
It is a pretty big query and I don't want to annoy StackOverflow users, but I simply can't find a solution. If only you could point me out what settings I could check in the production server in order to enable running the code... I must be using some kind of syntax or something that is conflicting with some setting in production. This PROCEDURE was already registered in production before, but when I ran a DROP - CREATE PROCEDURE today, the server was able to drop the procedure, but not to recreate it.
I will paste the code below. Thank you!
===============
USE [Enorway]
GO
/****** Object: StoredProcedure [dbo].[Spel_CM_ChartsUsersTotals] Script Date: 03/17/2010 11:59:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[Spel_CM_ChartsUsersTotals]
#IdGroup int,
#IdAssessment int,
#UserId int
AS
SET NOCOUNT ON
DECLARE #RequiredColor varchar(6)
SET #RequiredColor = '3333cc'
DECLARE #ManagersColor varchar(6)
SET #ManagersColor = '993300'
DECLARE #GroupColor varchar(6)
SET #GroupColor = 'ff0000'
DECLARE #SelfColor varchar(6)
SET #SelfColor = '336600'
DECLARE #TeamColor varchar(6)
SET #TeamColor = '993399'
DECLARE #intMyCounter tinyint
DECLARE #intManagersPosition tinyint
DECLARE #intGroupPosition tinyint
DECLARE #intSelfPosition tinyint
DECLARE #intTeamPosition tinyint
SET #intMyCounter = 1
-- Table that will hold the subtotals...
DECLARE #tblTotalsSource table
(
IdCompetency int,
CompetencyName nvarchar(200),
FunctionRequiredLevel float,
ManagersAverageAssessment float,
SelfAssessment float,
GroupAverageAssessment float,
TeamAverageAssessment float
)
INSERT INTO #tblTotalsSource
(
IdCompetency,
CompetencyName,
FunctionRequiredLevel,
ManagersAverageAssessment,
SelfAssessment,
GroupAverageAssessment,
TeamAverageAssessment
)
SELECT
e.[IdCompetency],
dbo.replaceAccentChar(e.[Name]) AS CompetencyName,
(i.[LevelNumber]) AS FunctionRequiredLevel,
(
SELECT
ROUND(avg(CAST(ac.[LevelNumber] AS float)),0)
FROM
Spel_CM_AssessmentsData aa
INNER JOIN Spel_CM_CompetenciesLevels ab ON aa.[IdCompetencyLevel] = ab.[IdCompetencyLevel]
INNER JOIN Spel_CM_Levels ac ON ab.[IdLevel] = ac.[IdLevel]
INNER JOIN Spel_CM_AssessmentsEvents ad ON aa.[IdAssessmentEvent] = ad.[IdAssessmentEvent]
WHERE
aa.[EvaluatedUserId] = #UserId AND
aa.[AssessmentType] = 't' AND
aa.[IdGroup] = #IdGroup AND
ab.[IdCompetency] = e.[IdCompetency] AND
ad.[IdAssessment] = #IdAssessment
) AS ManagersAverageAssessment,
(
SELECT
bc.[LevelNumber]
FROM
Spel_CM_AssessmentsData ba
INNER JOIN Spel_CM_CompetenciesLevels bb ON ba.[IdCompetencyLevel] = bb.[IdCompetencyLevel]
INNER JOIN Spel_CM_Levels bc ON bb.[IdLevel] = bc.[IdLevel]
INNER JOIN Spel_CM_AssessmentsEvents bd ON ba.[IdAssessmentEvent] = bd.[IdAssessmentEvent]
WHERE
ba.[EvaluatedUserId] = #UserId AND
ba.[AssessmentType] = 's' AND
ba.[IdGroup] = #IdGroup AND
bb.[IdCompetency] = e.[IdCompetency] AND
bd.[IdAssessment] = #IdAssessment
) AS SelfAssessment,
(
SELECT
ROUND(avg(CAST(cc.[LevelNumber] AS float)),0)
FROM
Spel_CM_AssessmentsData ca
INNER JOIN Spel_CM_CompetenciesLevels cb ON ca.[IdCompetencyLevel] = cb.[IdCompetencyLevel]
INNER JOIN Spel_CM_Levels cc ON cb.[IdLevel] = cc.[IdLevel]
INNER JOIN Spel_CM_AssessmentsEvents cd ON ca.[IdAssessmentEvent] = cd.[IdAssessmentEvent]
WHERE
ca.[EvaluatedUserId] = #UserId AND
ca.[AssessmentType] = 'g' AND
ca.[IdGroup] = #IdGroup AND
cb.[IdCompetency] = e.[IdCompetency] AND
cd.[IdAssessment] = #IdAssessment
) AS GroupAverageAssessment,
(
SELECT
ROUND(avg(CAST(dc.[LevelNumber] AS float)),0)
FROM
Spel_CM_AssessmentsData da
INNER JOIN Spel_CM_CompetenciesLevels db ON da.[IdCompetencyLevel] = db.[IdCompetencyLevel]
INNER JOIN Spel_CM_Levels dc ON db.[IdLevel] = dc.[IdLevel]
INNER JOIN Spel_CM_AssessmentsEvents dd ON da.[IdAssessmentEvent] = dd.[IdAssessmentEvent]
WHERE
da.[EvaluatedUserId] = #UserId AND
da.[AssessmentType] = 'm' AND
da.[IdGroup] = #IdGroup AND
db.[IdCompetency] = e.[IdCompetency] AND
dd.[IdAssessment] = #IdAssessment
) AS TeamAverageAssessment
FROM
Spel_CM_AssessmentsData a
INNER JOIN Spel_CM_AssessmentsEvents c ON a.[IdAssessmentEvent] = c.[IdAssessmentEvent]
INNER JOIN Spel_CM_CompetenciesLevels d ON a.[IdCompetencyLevel] = d.[IdCompetencyLevel]
INNER JOIN Spel_CM_Competencies e ON d.[IdCompetency] = e.[IdCompetency]
INNER JOIN Spel_CM_Levels f ON d.[IdLevel] = f.[IdLevel]
-- This will link with user's assigned functions
INNER JOIN Spel_CM_FunctionsCompetenciesLevels g ON a.[IdFunction] = g.[IdFunction]
INNER JOIN Spel_CM_CompetenciesLevels h ON g.[IdCompetencyLevel] = h.[IdCompetencyLevel] AND e.[IdCompetency] = h.[IdCompetency]
INNER JOIN Spel_CM_Levels i ON h.[IdLevel] = i.[IdLevel]
WHERE
(NOT c.[EndDate] IS NULL) AND
a.[EvaluatedUserId] = #UserId AND
c.[IdAssessment] = #IdAssessment AND
a.[IdGroup] = #IdGroup
GROUP BY
e.[IdCompetency],
e.[Name],
i.[LevelNumber]
ORDER BY
e.[Name] ASC
-- This will define the position of each element (managers, group, self and team)
SELECT #intManagersPosition = #intMyCounter FROM #tblTotalsSource WHERE NOT ManagersAverageAssessment IS NULL
IF IsNumeric(#intManagersPosition) = 1 BEGIN SELECT #intMyCounter += 1 END
SELECT #intGroupPosition = #intMyCounter FROM #tblTotalsSource WHERE NOT GroupAverageAssessment IS NULL
IF IsNumeric(#intGroupPosition) = 1 BEGIN SELECT #intMyCounter += 1 END
SELECT #intSelfPosition = #intMyCounter FROM #tblTotalsSource WHERE NOT SelfAssessment IS NULL
IF IsNumeric(#intSelfPosition) = 1 BEGIN SELECT #intMyCounter += 1 END
SELECT #intTeamPosition = #intMyCounter FROM #tblTotalsSource WHERE NOT TeamAverageAssessment IS NULL
-- This will render the final table for the end user. The tabe will flatten some of the numbers to allow them to be prepared for Google Graphics.
SELECT
SUBSTRING(
(
SELECT
( '|' + REPLACE(ma.[CompetencyName],' ','+'))
FROM
#tblTotalsSource ma
ORDER BY
ma.[CompetencyName] DESC
FOR XML PATH('')
), 2, 1000) AS 'CompetenciesNames',
SUBSTRING(
(
SELECT
( ',' + REPLACE(ra.[FunctionRequiredLevel]*10,' ','+'))
FROM
#tblTotalsSource ra
FOR XML PATH('')
), 2, 1000) AS 'FunctionRequiredLevel',
SUBSTRING(
(
SELECT
( ',' + CAST(na.[ManagersAverageAssessment]*10 AS nvarchar(10)))
FROM
#tblTotalsSource na
FOR XML PATH('')
), 2, 1000) AS 'ManagersAverageAssessment',
SUBSTRING(
(
SELECT
( ',' + CAST(oa.[GroupAverageAssessment]*10 AS nvarchar(10)))
FROM
#tblTotalsSource oa
FOR XML PATH('')
), 2, 1000) AS 'GroupAverageAssessment',
SUBSTRING(
(
SELECT
( ',' + CAST(pa.[SelfAssessment]*10 AS nvarchar(10)))
FROM
#tblTotalsSource pa
FOR XML PATH('')
), 2, 1000) AS 'SelfAssessment',
SUBSTRING(
(
SELECT
( ',' + CAST(qa.[TeamAverageAssessment]*10 AS nvarchar(10)))
FROM
#tblTotalsSource qa
FOR XML PATH('')
), 2, 1000) AS 'TeamAverageAssessment',
SUBSTRING(
(
SELECT
( '|t++' + CAST([FunctionRequiredLevel] AS varchar(10)) + ',' + #RequiredColor + ',0,' + CAST(ROW_NUMBER() OVER(ORDER BY CompetencyName) - 1 AS varchar(2)) + ',9')
FROM
#tblTotalsSource
FOR XML PATH('')
), 2, 1000) AS 'FunctionRequiredAverageLabel',
SUBSTRING(
(
SELECT
( '|t++' + CAST([ManagersAverageAssessment] AS varchar(10)) + ',' + #ManagersColor + ',' + CAST(#intManagersPosition AS varchar(2)) + ',' + CAST(ROW_NUMBER() OVER(ORDER BY CompetencyName) - 1 AS varchar(2)) + ',9')
FROM
#tblTotalsSource
FOR XML PATH('')
), 2, 1000) AS 'ManagersLabel',
SUBSTRING(
(
SELECT
( '|t++' + CAST([GroupAverageAssessment] AS varchar(10)) + ',' + #GroupColor + ',' + CAST(#intGroupPosition AS varchar(2)) + ',' + CAST(ROW_NUMBER() OVER(ORDER BY CompetencyName) - 1 AS varchar(2)) + ',9')
FROM
#tblTotalsSource
FOR XML PATH('')
), 2, 1000) AS 'GroupLabel',
SUBSTRING(
(
SELECT
( '|t++' + CAST([SelfAssessment] AS varchar(10)) + ',' + #SelfColor + ',' + CAST(#intSelfPosition AS varchar(2)) + ',' + CAST(ROW_NUMBER() OVER(ORDER BY CompetencyName) - 1 AS varchar(2)) + ',9')
FROM
#tblTotalsSource
FOR XML PATH('')
), 2, 1000) AS 'SelfLabel',
SUBSTRING(
(
SELECT
( '|t++' + CAST([TeamAverageAssessment] AS varchar(10)) + ',' + #TeamColor + ',' + CAST(#intTeamPosition AS varchar(2)) + ',' + CAST(ROW_NUMBER() OVER(ORDER BY CompetencyName) - 1 AS varchar(2)) + ',10')
FROM
#tblTotalsSource
FOR XML PATH('')
), 2, 1000) AS 'TeamLabel',
(Count(src.[IdCompetency]) * 30) + 100 AS 'ControlHeight'
FROM
#tblTotalsSource src
SET NOCOUNT OFF
GO
Search your stored procedure for instances of this string: += (you have a few of them). This combined operator is not supported by T-SQL,
So instead of this:
SELECT #intMyCounter += 1
you will need to do this instead:
SELECT #intMyCounter = #intMyCounter + 1
when you run your create procedure script in sql server management studio and you get an error, double click on the error message and it usually takes you to the line where the error occurs. Sometimes this is just the first line of a long SELECT query, but in this case it is fairly obvious, because it is a one line IF
you can not use += in the line:
IF IsNumeric(#intManagersPosition) = 1 BEGIN SELECT #intMyCounter += 1 END
should be:
IF IsNumeric(#intManagersPosition) = 1 BEGIN SELECT #intMyCounter = #intMyCounter+1 END
you do this three times, fix then and you are good to go.
don't you miss BEGIN and END in the beginnning and eding of procedure, respectively?
(create procedure blablabla as begin .... end)
If SQL isn't giving you a line number, try commenting out different statements and re-running the command